Skip to content

Commit

Permalink
Merge remote-tracking branch 'refs/remotes/origin/release/2'
Browse files Browse the repository at this point in the history
  • Loading branch information
gmazzap committed Mar 12, 2017
2 parents 5ab6340 + d48ac59 commit 33045bb
Show file tree
Hide file tree
Showing 21 changed files with 445 additions and 225 deletions.
4 changes: 2 additions & 2 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Auto detect text files and perform LF normalization
*.* text=auto
*.* text=lf

tests/ export-ignore
.travis.yml export-ignore
.phpunit.xml.dist export-ignore
README.md export-ignore
4 changes: 1 addition & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
language: php

php:
- 5.4
- 5.5
- 5.6
- 7
- hhvm
- 7.1

before_script:
- curl -s http://getcomposer.org/installer | php
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2015 Giuseppe Mazzapica
Copyright (c) 2017 Giuseppe Mazzapica

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
62 changes: 40 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
-------

[![travis-ci status](https://img.shields.io/travis/Giuseppe-Mazzapica/Andrew.svg?style=flat-square)](https://travis-ci.org/Giuseppe-Mazzapica/Andrew)
[![codecov.io](https://img.shields.io/codecov/c/github/Giuseppe-Mazzapica/Andrew.svg?style=flat-square)](http://codecov.io/github/Giuseppe-Mazzapica/Andrew?branch=master)
[![license](https://img.shields.io/github/license/Giuseppe-Mazzapica/Andrew.svg?style=flat-square)](http://opensource.org/licenses/MIT)
[![travis-ci status](https://img.shields.io/travis/gmazzap/Andrew.svg?style=flat-square)](https://travis-ci.org/Giuseppe-Mazzapica/Andrew)
[![codecov.io](https://img.shields.io/codecov/c/github/gmazzap/Andrew.svg?style=flat-square)](http://codecov.io/github/Giuseppe-Mazzapica/Andrew?branch=master)
[![license](https://img.shields.io/github/license/gmazzap/Andrew.svg?style=flat-square)](http://opensource.org/licenses/MIT)

-------

Expand All @@ -22,66 +22,84 @@ It provides 2 "proxy" objects, one for dynamic properties and methods, the other
Let's assume following class

```php
class Foo
class Subject
{
private $var = 'I am private';

public function getVar() {
return $this->var;
}

public function hasVar() {
return isset($this->var);
}

private function testMe() {
return 'I am private';
return 'I am private';
}
}
```

With *Andrew* is possible to:

```php
$proxy = new Andrew\Proxy(new Foo());
$subject = new Subject();

// getter
$proxy = new Andrew\Proxy($subject);

// get subject private var via proxy
echo $proxy->var; // 'I am private'

// setter
// set subject private var via proxy
$proxy->var = 'I WAS private';
echo $proxy->var; // 'I WAS private'
echo $subject->getVar(); // 'I WAS private'

// isser
// check subject private var is set via proxy
isset($proxy->var); // true

// unsetter
// unset subject private var via proxy
unset($proxy->var);
isset($proxy->var); // false
$subject->hasVar(); // false

// caller
// call subject private method via proxy
echo $proxy->testMe() // 'I am private'
```

The `Subject` class example has public getter and isser for its private variable, that we added for
test purposes, bu Andrew proxy is, of course, more useful when classes does not provide that
possibility.


# Static Proxy

Let's assume following class

```php
class Foo
class Subject
{
private static $var = 'I am private static';
private static $var = 'I am private static';

private static function testMe() {
return 'I am private static';
}
public static function getVar() {
return self::$var;
}

private static function testMe() {
return 'I am private static';
}
}
```

With *Andrew* is possible to:

```php
$proxy = new Andrew\StaticProxy('Foo'); // we pass class name, not object
$proxy = new Andrew\StaticProxy(Subject::class); // we pass class name, not object

// getter
echo $proxy->var; // 'I am private static'

// setter
$proxy->var = 'I WAS private static';
echo $proxy->var; // 'I WAS private static'
echo Subject::getVar(); // 'I WAS private static'

// isser
isset($proxy->var); // true
Expand All @@ -90,7 +108,7 @@ isset($proxy->var); // true
echo $proxy->testMe() // 'I am private static'
```

Note that `StaticProxy` has **not** unsetter, because PHP [does not allow unsett static variables](http://3v4l.org/91GTk).
Note that `StaticProxy` has **not** unsetter, because PHP [does not allow to unset static variables](http://3v4l.org/91GTk).

If you try to unset anything on a `StaticProxy` object, *Andrew* will throw an Exception.

Expand Down Expand Up @@ -119,7 +137,7 @@ No. But may be quite useful in unit tests.

# Requirements

*Andrew* has no dependencies. Requires PHP 5.4+, works with PHP 7 and HHVM.
*Andrew* has no dependencies. Requires PHP 5.6+, works with PHP 7 and 7.1.

# License

Expand Down
5 changes: 2 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
{
"name": "Giuseppe Mazzapica",
"email": "[email protected]",
"homepage": "http://gm.zoomlab.it",
"role": "Developer"
}
],
Expand All @@ -28,10 +27,10 @@
},
"minimum-stability": "stable",
"require": {
"php": ">=5.4"
"php": ">=5.6"
},
"require-dev": {
"phpunit/phpunit": "~4.4"
"phpunit/phpunit": "5.7.*"
},
"config": {
"optimize-autoloader": true
Expand Down
2 changes: 1 addition & 1 deletion phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.4/phpunit.xsd"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/5.7/phpunit.xsd"
bootstrap="./tests/boot.php"
colors="true"
convertErrorsToExceptions="true"
Expand Down
6 changes: 6 additions & 0 deletions src/Callbacks/CallbacksInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
*/
interface CallbacksInterface
{
const GETTER = 'getter';
const SETTER = 'setter';
const ISSER = 'isser';
const UNSETTER = 'unsetter';
const CALLER = 'caller';

/**
* @return callable
*/
Expand Down
110 changes: 71 additions & 39 deletions src/Callbacks/DynamicCallbacks.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
namespace Andrew\Callbacks;

use Andrew\Checker\Checker;
use Andrew\Exception\ClassException;
use Closure;

/**
Expand All @@ -20,13 +21,21 @@
*/
final class DynamicCallbacks implements CallbacksInterface
{
const TYPE_CHECKS_MAP = [
self::GETTER => ['assertProperty', 'Undeclared properties can not be retrieved.'],
self::SETTER => ['assertProperty', 'Undeclared properties can not be set.'],
self::ISSER => ['assertProperty', 'Undeclared properties can not be checked.'],
self::UNSETTER => ['assertProperty', 'Undeclared properties can not be unset.'],
self::CALLER => ['assertMethod', 'Undeclared methods can not be called.'],
];

/**
* @var string
*/
private $class;

/**
* @var object
* @var mixed
*/
private $object;

Expand All @@ -36,88 +45,111 @@ final class DynamicCallbacks implements CallbacksInterface
private $checker;

/**
* @param object $object
* @var \Closure[]
*/
private $callbacks = [];

/**
* @param mixed $object
* @param \Andrew\Checker\Checker $checker
* @throws \Andrew\Exception\ClassException
* @throws \Andrew\Exception\ArgumentException
*/
public function __construct($object, Checker $checker = null)
{
is_null($checker) and $checker = new Checker();
$checker or $checker = new Checker();
$checker->assertObject($object, __CLASS__.' expects an object.');
$class = get_class($object);
if (ltrim($class, '\\') === 'stdClass') {
throw new ClassException('It is not possible to use proxy with stdClass.');
}
$this->checker = $checker;
$this->object = $object;
$this->class = get_class($object);
$this->class = $class;
}

/**
* @return \Closure
*/
public function getter()
{
$checker = $this->checker;
$getter = function ($var) use ($checker) {
$checker->assertProperty($this, $var, 'Undeclared properties can not be retrieved.');

return $this->$var;
};

return Closure::bind($getter, $this->object, $this->class);
return $this->createCallback(self::GETTER);
}

/**
* @return \Closure
*/
public function setter()
{
$checker = $this->checker;
$setter = function ($var, $value) use ($checker) {
$checker->assertProperty($this, $var, 'Undeclared properties can not be set.');
$this->$var = $value;
};

return Closure::bind($setter, $this->object, $this->class);
return $this->createCallback(self::SETTER);
}

/**
* @return \Closure
*/
public function isser()
{
$checker = $this->checker;
$isser = function ($var) use ($checker) {
$checker->assertProperty($this, $var, 'Undeclared properties can not be checked.');

return isset($this->$var);
};

return Closure::bind($isser, $this->object, $this->class);
return $this->createCallback(self::ISSER);
}

/**
* @return \Closure
*/
public function unsetter()
{
$checker = $this->checker;
$unsetter = function ($var) use ($checker) {
$checker->assertProperty($this, $var, 'Undeclared properties can not be unset.');
unset($this->$var);
};

return Closure::bind($unsetter, $this->object, $this->class);
return $this->createCallback(self::UNSETTER);
}

/**
* @return \Closure
*/
public function caller()
{
$checker = $this->checker;
$caller = function ($method, $args) use ($checker) {
$checker->assertMethod($this, $method, 'Undeclared methods can not be called.');
return $this->createCallback(self::CALLER);
}

return call_user_func_array([$this, $method], $args);
/**
* @param string $which
* @return \Closure
*/
private function createCallback($which)
{
if (isset($this->callbacks[$which])) {
// @codeCoverageIgnoreStart
return $this->callbacks[$which];
// @codeCoverageIgnoreEnd
}

$checker = $this->checker;
$object = $this->object;

$caller = function ($key, $value = null) use ($which, $checker, $object) {
list($checkMethod, $checkMessage) = DynamicCallbacks::TYPE_CHECKS_MAP[$which];
/** @var callable $checkerCallback */
$checkerCallback = [$checker, $checkMethod];
$checkerCallback($object, $key, $checkMessage);

switch ($which) {
case DynamicCallbacks::CALLER:
/** @var callable $method */
$method = [$this, $key];

return $value ? $method(...$value) : $method();
case DynamicCallbacks::GETTER:
return $this->{$key};
case DynamicCallbacks::SETTER:
$this->{$key} = $value;
break;
case DynamicCallbacks::ISSER:
return isset($this->{$key});
case DynamicCallbacks::UNSETTER:
unset($this->{$key});
break;
}
};

return Closure::bind($caller, $this->object, $this->class);
$this->callbacks[$which] = Closure::bind($caller, $this->object, $this->class);

return $this->callbacks[$which];
}
}
Loading

0 comments on commit 33045bb

Please sign in to comment.