.phpstorm.meta.php 0000644 00000002154 13645114176 0010146 0 ustar 00 generateProxy(
new ReflectionClass(SayHello::class),
new ClassGenerator('ProxyClassName')
);
eval('generate());
$proxyName = $proxyClass->getName();
$object = new ProxyClassName(); // echoes "Hello!"
var_dump($object); // a proxy object
```
If you still want to manually build a proxy (without factories), a
`public static staticProxyConstructor` method is added to the generated proxy classes.
#### Friend classes support
You can now access state of "friend objects" at any time.
```php
class EmailAddress
{
private $address;
public function __construct(string $address)
{
assertEmail($address);
$this->address = $address;
}
public function equalsTo(EmailAddress $other)
{
return $this->address === $other->address;
}
}
```
When using lazy-loading or access-interceptors, the `equalsTo` method will
properly work, as even `protected` and `private` access are now correctly proxied.
#### Ghost objects now only lazy-load on state-access
Lazy loading ghost objects now trigger lazy-loading only when their state is accessed.
This also implies that lazy loading ghost objects cannot be used with interfaces anymore.
```php
class AccessPolicy
{
private $policyName;
/**
* Calling this method WILL cause lazy-loading, when using a ghost object,
* as the method is accessing the object's state
*/
public function getPolicyName() : string
{
return $this->policyName;
}
/**
* Calling this method WILL NOT cause lazy-loading, when using a ghost object,
* as the method is not reading any from the object.
*/
public function allowAccess() : bool
{
return false;
}
}
```
#### Faster ghost object state initialization
Lazy loading ghost objects can now be initialized in a more efficient way, by avoiding
reflection or setters:
```php
class Foo
{
private $a;
protected $b;
public $c;
}
$factory = new \ProxyManager\Factory\LazyLoadingGhostFactory();
$proxy = $factory-createProxy(
Foo::class,
function (
GhostObjectInterface $proxy,
string $method,
array $parameters,
& $initializer,
array $properties
) {
$initializer = null;
$properties["\0Foo\0a"] = 'abc';
$properties["\0*\0b"] = 'def';
$properties['c'] = 'ghi';
return true;
}
);
$reflectionA = new ReflectionProperty(Foo::class, 'a');
$reflectionA->setAccessible(true);
var_dump($reflectionA->getValue($proxy)); // dumps "abc"
$reflectionB = new ReflectionProperty(Foo::class, 'b');
$reflectionB->setAccessible(true);
var_dump($reflectionB->getValue($proxy)); // dumps "def"
var_dump($proxy->c); // dumps "ghi"
```
#### Skipping lazy-loaded properties in generated proxies
Lazy loading ghost objects can now skip lazy-loading for certain properties.
This is especially useful when you have properties that are always available,
such as identifiers of entities:
```php
class User
{
private $id;
private $username;
public function getId() : int
{
return $this->id;
}
public function getUsername() : string
{
return $this->username;
}
}
/* @var $proxy User */
$proxy = (new \ProxyManager\Factory\LazyLoadingGhostFactory())->createProxy(
User::class,
function (
GhostObjectInterface $proxy,
string $method,
array $parameters,
& $initializer,
array $properties
) {
$initializer = null;
var_dump('Triggered lazy-loading!');
$properties["\0User\0username"] = 'Ocramius';
return true;
},
[
'skippedProperties' => [
"\0User\0id",
],
]
);
$idReflection = new \ReflectionProperty(User::class, 'id');
$idReflection->setAccessible(true);
$idReflection->setValue($proxy, 123);
var_dump($proxy->getId()); // 123
var_dump($proxy->getUsername()); // "Triggered lazy-loading!", then "Ocramius"
```
#### Proxies are now always generated on-the-fly by default
Proxies are now automatically generated any time you require them: no configuration
needed. If you want to gain better performance, you may still want to read
the [tuning for production docs](docs/tuning-for-production.md).
#### Proxy names are now hashed, simplified signature is attached to them
Proxy classes now have shorter names, as the parameters used to generate them are
hashed into their name. A signature is attached to proxy classes (as a private static
property) so that proxy classes aren't re-used across library updates.
Upgrading ProxyManager will now cause all proxies to be re-generated automatically,
while the old proxy files are going to be ignored.
LICENSE 0000644 00000002041 13645114176 0005556 0 ustar 00 Copyright (c) 2013 Marco Pivetta
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
README.md 0000644 00000005755 13645114176 0006047 0 ustar 00 # Proxy Manager
This library aims to provide abstraction for generating various kinds of
[proxy classes](http://ocramius.github.io/presentations/proxy-pattern-in-php/).
![ProxyManager](https://raw.githubusercontent.com/Ocramius/ProxyManager/917bf1698243a1079aaa27ed8ea08c2aef09f4cb/proxy-manager.png)
[![Build Status](https://travis-ci.org/Ocramius/ProxyManager.png?branch=master)](https://travis-ci.org/Ocramius/ProxyManager)
[![Code Coverage](https://scrutinizer-ci.com/g/Ocramius/ProxyManager/badges/coverage.png?s=ca3b9ceb9e36aeec0e57569cc8983394b7d2a59e)](https://scrutinizer-ci.com/g/Ocramius/ProxyManager/)
[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/Ocramius/ProxyManager/badges/quality-score.png?s=eaa858f876137ed281141b1d1e98acfa739729ed)](https://scrutinizer-ci.com/g/Ocramius/ProxyManager/)
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/69fe5f97-b1c8-4ddd-93ce-900b8b788cf2/mini.png)](https://insight.sensiolabs.com/projects/69fe5f97-b1c8-4ddd-93ce-900b8b788cf2)
[![Total Downloads](https://poser.pugx.org/ocramius/proxy-manager/downloads.png)](https://packagist.org/packages/ocramius/proxy-manager)
[![Latest Stable Version](https://poser.pugx.org/ocramius/proxy-manager/v/stable.png)](https://packagist.org/packages/ocramius/proxy-manager)
[![Latest Unstable Version](https://poser.pugx.org/ocramius/proxy-manager/v/unstable.png)](https://packagist.org/packages/ocramius/proxy-manager)
## Documentation
You can learn about the proxy pattern and how to use the **ProxyManager** in the [docs](docs).
## ocramius/proxy-manager for enterprise
Available as part of the Tidelift Subscription.
The maintainer of ocramius/proxy-manager and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-ocramius-proxy-manager?utm_source=packagist-ocramius-proxy-manager&utm_medium=referral&utm_campaign=enterprise&utm_term=repo).
You can also contact the maintainer at ocramius@gmail.com for looking into issues related to this package
in your private projects.
## Installation
The suggested installation method is via [composer](https://getcomposer.org/):
```sh
php composer.phar require ocramius/proxy-manager
```
## Proxy example
Here's how you build a lazy loadable object with ProxyManager using a *Virtual Proxy*
```php
$factory = new \ProxyManager\Factory\LazyLoadingValueHolderFactory();
$proxy = $factory->createProxy(
\MyApp\HeavyComplexObject::class,
function (& $wrappedObject, $proxy, $method, $parameters, & $initializer) {
$wrappedObject = new \MyApp\HeavyComplexObject(); // instantiation logic here
$initializer = null; // turning off further lazy initialization
}
);
$proxy->doFoo();
```
See the [documentation](docs) for more supported proxy types and examples.
SECURITY.md 0000644 00000000301 13645114176 0006337 0 ustar 00 ## Security contact information
To report a security vulnerability, please use the
[Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure.
STABILITY.md 0000644 00000000646 13645114176 0006450 0 ustar 00 ---
title: Stability
---
This is a list of supported versions, with their expected release/support time-frames:
# 2.0.x
* Release date: 2016-01-30
* Bug fixes: till 2017-12-31
* Security fixes: till 2018-12-31
# 1.0.x
* Release date: 2014-12-12
* Bug fixes: till 2015-12-11
* Security fixes: till 2016-12-11
# 0.5.x
* Release date: 2013-12-01
* Bug fixes: till 2015-03-11
* Security fixes: till 2015-12-11
UPGRADE.md 0000644 00000014376 13645114176 0006200 0 ustar 00 ---
title: Upgrade
---
This is a list of backwards compatibility (BC) breaks introduced in ProxyManager:
# 2.0.0
* PHP `~7.0` is now required to use ProxyManager
* HHVM compatibility is not guaranteed, as HHVM is not yet PHP 7 compliant
* All classes and interfaces now use [strict scalar type hints](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration).
If you extended or implemented anything from the `ProxyManager\` namespace, you probably need to change
that code to adapt it to the new signature.
* All classes and interfaces now use [return type declarations](http://php.net/manual/en/functions.returning-values.php#functions.returning-values.type-declaration).
If you extended or implemented anything from the `ProxyManager\` namespace, you probably need to change
that code to adapt it to the new signature.
* ProxyManager will no longer write proxies to disk by default:
the [`EvaluatingGeneratorStrategy`](src/GeneratorStrategy/EvaluatingGeneratorStrategy.php) is used instead.
If you still want ProxyManager to write files to disk, please refer to the [tuning for production docs](docs/tuning-for-production.md)
* Ghost objects were entirely rewritten, for better support and improved performance. Lazy-loading is not
triggered by public API access, but by property access (private and public). While this is not really a BC
break, you are encouraged to check your applications if you rely on [ghost objects](docs/lazy-loading-ghost-object.md).
* If ProxyManager can't find a proxy, it will now automatically attempt to auto-generate it, regardless of
the settings passed to it.
* `ProxyManager\Configuration#setAutoGenerateProxies()` was removed. Please look for calls to this method and
remove them.
* Private properties are now also correctly handled by ProxyManager: accessing proxy state via friend classes
(protected or private scope) does not require any particular workarounds anymore.
* `ProxyManager\Version::VERSION` was removed. Please use `ProxyManager\Version::getVersion()` instead.
* PHP 4 style constructors are no longer supported
# 1.0.0
`1.0.0` is be fully compatible with `0.5.0`.
# 0.5.0
* The Generated Hydrator has been removed - it is now available as a separate project
at [Ocramius/GeneratedHydrator](https://github.com/Ocramius/GeneratedHydrator) [#65](https://github.com/Ocramius/ProxyManager/pull/65)
* When having a `public function __get($name)` defined (by-val) and public properties, it won't be possible to get public
properties by-ref while initializing the object. Either drop `__get()` or implement
a by-ref `& __get()` [#126](https://github.com/Ocramius/ProxyManager/pull/126)
* Proxies are now being always auto-generated if they could not be autoloaded by a factory. The methods
[`ProxyManager\Configuration#setAutoGenerateProxies()`](https://github.com/Ocramius/ProxyManager/blob/0.5.0-BETA2/src/ProxyManager/Configuration.php#L67)
and [`ProxyManager\Configuration#doesAutoGenerateProxies()`](https://github.com/Ocramius/ProxyManager/blob/0.5.0-BETA2/src/ProxyManager/Configuration.php#L75)
are now no-op and deprecated, and will be removed in the next minor
version [#87](https://github.com/Ocramius/ProxyManager/pull/87) [#90](https://github.com/Ocramius/ProxyManager/pull/90)
* Proxy public properties defaults are now set before initialization [#116](https://github.com/Ocramius/ProxyManager/pull/116) [#122](https://github.com/Ocramius/ProxyManager/pull/122)
# 0.4.0
* An optional parameter `$options` was introduced
in [`ProxyManager\Inflector\ClassNameInflectorInterface#getProxyClassName($className, array $options = array())`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Inflector/ClassNameInflectorInterface.php)
parametrize the generated class name as of [#10](https://github.com/Ocramius/ProxyManager/pull/10)
and [#59](https://github.com/Ocramius/ProxyManager/pull/59)
* Generated hydrators no longer have constructor arguments. Any required reflection instantiation is now dealt with
in the hydrator internally as of [#63](https://github.com/Ocramius/ProxyManager/pull/63)
# 0.3.4
* Interface names are also supported for proxy generation as of [#40](https://github.com/Ocramius/ProxyManager/pull/40)
# 0.3.3
* [Generated hydrators](https://github.com/Ocramius/ProxyManager/tree/master/docs/generated-hydrator.md) were introduced
# 0.3.2
* An additional (optional) [by-ref parameter was added](https://github.com/Ocramius/ProxyManager/pull/31)
to the lazy loading proxies' initializer to allow unsetting the initializer with less overhead.
# 0.3.0
* Dependency to [jms/cg](https://github.com/schmittjoh/cg-library) removed
* Moved code generation logic to [`Zend\Code`](https://github.com/zendframework/zf2)
* Added method [`ProxyManager\Inflector\ClassNameInflectorInterface#isProxyClassName($className)`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Inflector/ClassNameInflectorInterface.php)
* The constructor of [`ProxyManager\Autoloader\Autoloader`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Autoloader/Autoloader.php)
changed from `__construct(\ProxyManager\FileLocator\FileLocatorInterface $fileLocator)` to
`__construct(\ProxyManager\FileLocator\FileLocatorInterface $fileLocator, \ProxyManager\Inflector\ClassNameInflectorInterface $classNameInflector)`
* Classes implementing `CG\Core\GeneratorStrategyInterface` now implement
[`ProxyManager\GeneratorStrategy\GeneratorStrategyInterface`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/GeneratorStrategy/GeneratorStrategyInterface.php)
instead
* All code generation logic has been replaced - If you wrote any logic based on `ProxyManager\ProxyGenerator`, you will
have to rewrite it
# 0.2.0
* The signature of initializers to be used with proxies implementing
[`ProxyManager\Proxy\LazyLoadingInterface`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Proxy/LazyLoadingInterface.php)
changed from:
```php
$initializer = function ($proxy, & $wrappedObject, $method, $parameters) {};
```
to
```php
$initializer = function (& $wrappedObject, $proxy, $method, $parameters) {};
```
Only the order of parameters passed to the closures has been changed.
composer.json 0000644 00000004565 13645114176 0007310 0 ustar 00 {
"name": "ocramius/proxy-manager",
"description": "A library providing utilities to generate, instantiate and generally operate with Object Proxies",
"type": "library",
"license": "MIT",
"homepage": "https://github.com/Ocramius/ProxyManager",
"keywords": [
"proxy",
"proxy pattern",
"service proxies",
"lazy loading",
"aop"
],
"authors": [
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com",
"homepage": "http://ocramius.github.io/"
}
],
"require": {
"php": "~7.4.1",
"laminas/laminas-code": "^3.4.1",
"ocramius/package-versions": "^1.8.0",
"webimpress/safe-writer": "^2.0.1"
},
"conflict": {
"zendframework/zend-stdlib": "<3.2.1",
"laminas/laminas-stdlib": "<3.2.1",
"doctrine/annotations": "<1.6.1"
},
"require-dev": {
"ext-phar": "*",
"phpunit/phpunit": "^9.1.1",
"squizlabs/php_codesniffer": "^3.5.4",
"slevomat/coding-standard": "^5.0.4",
"doctrine/coding-standard": "^6.0.0",
"nikic/php-parser": "^4.4.0",
"phpbench/phpbench": "^0.17.0",
"infection/infection": "^0.16.2",
"vimeo/psalm": "^3.11.1"
},
"suggest": {
"ocramius/generated-hydrator": "To have very fast object to array to object conversion for ghost objects",
"laminas/laminas-xmlrpc": "To have the XmlRpc adapter (Remote Object feature)",
"laminas/laminas-json": "To have the JsonRpc adapter (Remote Object feature)",
"laminas/laminas-soap": "To have the Soap adapter (Remote Object feature)"
},
"autoload": {
"psr-4": {
"ProxyManager\\": "src/ProxyManager"
}
},
"autoload-dev": {
"psr-4": {
"ProxyManagerBench\\": "tests/ProxyManagerBench",
"ProxyManagerTest\\": "tests/ProxyManagerTest",
"ProxyManagerTestAsset\\": "tests/ProxyManagerTestAsset",
"Laminas\\Server\\": "tests/Stubbed/Laminas/Server"
}
},
"extra": {
"branch-alias": {
"dev-master": "3.0.x-dev"
}
}
}
docs/access-interceptor-scope-localizer.md 0000644 00000006607 13645114176 0014725 0 ustar 00 ---
title: Access Interceptor Scope Localizer Proxy
---
# Access Interceptor Scope Localizer Proxy
An access interceptor scope localizer is a smart reference proxy that allows you to dynamically define logic to be executed
before or after any of the proxied object methods' logic.
It works exactly like the [access interceptor value holder](access-interceptor-value-holder.md), with some minor differences
in behaviour.
The working concept of an access interceptor scope localizer is to localize scope of a proxied object:
```php
class Example
{
protected $foo;
protected $bar;
protected $baz;
public function doFoo()
{
// ...
}
}
class ExampleProxy extends Example
{
public function __construct(Example $example)
{
$this->foo = & $example->foo;
$this->bar = & $example->bar;
$this->baz = & $example->baz;
}
public function doFoo()
{
return parent::doFoo();
}
}
```
This allows creating a mirror copy of the real instance, where any change in the proxy or the real instance is reflected in
both objects.
The main advantage of this approach is that the proxy is now safe against fluent interfaces, which would break an
[access interceptor value holder](access-interceptor-value-holder.md) instead.
## Differences with [access interceptor value holder](access-interceptor-value-holder.md):
* It does **NOT** implement the `ProxyManager\Proxy\ValueHolderInterface`, since the proxy itself does not keep a reference
to the original object being proxied
* In all interceptor methods (see [access interceptor value holder](access-interceptor-value-holder.md)), the `$instance`
passed in is the proxy itself. There is no way to gather a reference to the original object right now, and that is mainly
to protect from misuse.
## Known limitations
* It is **NOT** possible to intercept access to public properties
* It is **NOT** possible to proxy interfaces, since this proxy relies on `parent::method()` calls. Interfaces don't provide
a parent method implementation.
* calling `unset` on a property of an access interceptor scope localizer (or the real instance) will cause the two objects
to be un-synchronized, with possible unexpected behaviour.
* serializing or un-serializing an access interceptor scope localizer (or the real instance) will not cause the real
instance (or the proxy) to be serialized or un-serialized
* methods using `func_get_args()`, `func_get_arg()` and `func_num_arg()` will not function properly for parameters that are
not part of the proxied object interface: use
[variadic arguments](http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list) instead.
## Example
Here's an example of how you can create and use an access interceptor scope localizer:
```php
createProxy(
new Foo(),
['doFoo' => function () { echo "PreFoo!\n"; }],
['doFoo' => function () { echo "PostFoo!\n"; }]
);
$proxy->doFoo();
```
This sends something like following to your output:
```
PreFoo!
Foo!
PostFoo!
```
This is similar logic to what you can find in [access interceptor value holder](access-interceptor-value-holder.md).
docs/access-interceptor-value-holder.md 0000644 00000007615 13645114176 0014221 0 ustar 00 ---
title: Access Interceptor Value Holder Proxy
---
# Access Interceptor Value Holder Proxy
An *access interceptor value holder* is a smart reference proxy that allows you to dynamically define the logic that will be executed before or after any of the wrapped object's methods logic.
It wraps around a real instance of the object to be proxied and can be useful for things like:
* caching execution of slow and heavy methods
* log method calls
* debugging
* event triggering
* handling of orthogonal logic, and [AOP](http://en.wikipedia.org/wiki/Aspect-oriented_programming) in general
## Example
Here's an example of how you can create and use an access interceptor value holder:
```php
createProxy(
new Foo(),
['doFoo' => function () { echo "PreFoo!\n"; }],
['doFoo' => function () { echo "PostFoo!\n"; }]
);
$proxy->doFoo();
```
This sends something like following to your output:
```
PreFoo!
Foo!
PostFoo!
```
## Implementing pre- and post- access interceptors
A proxy produced by the
[`ProxyManager\Factory\AccessInterceptorValueHolderFactory`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Factory/AccessInterceptorValueHolderFactory.php)
implements the [`ProxyManager\Proxy\AccessInterceptorValueHolderInterface`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Proxy/AccessInterceptorValueHolderInterface.php).
Therefore, you can set an access interceptor callback by calling:
```php
$proxy->setMethodPrefixInterceptor('methodName', function () { echo 'pre'; });
$proxy->setMethodSuffixInterceptor('methodName', function () { echo 'post'; });
```
You can also listen to public properties access by attaching interceptors to `__get`, `__set`, `__isset` and `__unset`.
A prefix interceptor (executed before method logic) should have the following signature:
```php
/**
* @var object $proxy the proxy that intercepted the method call
* @var object $instance the wrapped instance within the proxy
* @var string $method name of the called method
* @var array $params sorted array of parameters passed to the intercepted
* method, indexed by parameter name
* @var bool $returnEarly flag to tell the interceptor proxy to return early, returning
* the interceptor's return value instead of executing the method logic
*
* @return mixed
*/
$prefixInterceptor = function ($proxy, $instance, $method, $params, & $returnEarly) {};
```
A suffix interceptor (executed after method logic) should have the following signature:
```php
/**
* @var object $proxy the proxy that intercepted the method call
* @var object $instance the wrapped instance within the proxy
* @var string $method name of the called method
* @var array $params sorted array of parameters passed to the intercepted
* method, indexed by parameter name
* @var mixed $returnValue the return value of the intercepted method
* @var bool $returnEarly flag to tell the proxy to return early, returning the interceptor's
* return value instead of the value produced by the method
*
* @return mixed
*/
$suffixInterceptor = function ($proxy, $instance, $method, $params, $returnValue, & $returnEarly) {};
```
## Known limitations
* methods using `func_get_args()`, `func_get_arg()` and `func_num_arg()` will not function properly for parameters that are
not part of the proxied object interface: use
[variadic arguments](http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list) instead.
## Tuning performance for production
See [Tuning ProxyManager for Production](tuning-for-production.md).
docs/copyright.md 0000644 00000002111 13645114176 0010031 0 ustar 00 ---
title: License
---
# License
Copyright (c) 2013 Marco Pivetta
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
---
docs/credits.md 0000644 00000002011 13645114176 0007455 0 ustar 00 ---
title: Credits
---
# Credits
The idea was originated by a [talk about Proxies in PHP OOP](https://ocramius.github.io/presentations/proxy-pattern-in-php/) that I gave at
the [@phpugffm](https://twitter.com/phpugffm) in January 2013.
---
### Contributors
- [Marco Pivetta](https://github.com/Ocramius)
- [Jefersson Nathan](https://github.com/malukenho)
- [Blanchon Vincent](https://github.com/blanchonvincent)
- [Markus Staab](https://github.com/staabm)
- [Gordon Stratton](https://github.com/gws)
- [Prolic](https://github.com/prolic)
- [Guillaume Royer](https://github.com/guilro)
- [Robert Reiz](https://github.com/reiz)
- [Lee Davis](https://github.com/leedavis81)
- [flip111](https://github.com/flip111)
- [Krzysztof Menzyk](https://github.com/krymen)
- [Aleksey Khudyakov](https://github.com/Xerkus)
- [Alexander](https://github.com/asm89)
- [Raul Fraile](https://github.com/raulfraile)
### Documentation
[Gary Hockin](https://twitter.com/GeeH) is amazing and spent literally 20 minutes tidying up the documentation.
docs/download.md 0000644 00000000325 13645114176 0007635 0 ustar 00 ---
title: Download / Installation
---
## Download / Installation
The suggested installation method is via [composer](https://getcomposer.org/).
```sh
php composer.phar require ocramius/proxy-manager:1.0.*
```
docs/generator-strategies.md 0000644 00000002737 13645114176 0012175 0 ustar 00 ---
title: Generator strategies
---
# Generator strategies
ProxyManager allows you to generate classes based on generator strategies and a given `Laminas\Code\Generator\ClassGenerator` as of the [interface of a generator strategy](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/GeneratorStrategy/GeneratorStrategyInterface.php).
Currently, 3 generator strategies are shipped with ProxyManager:
* [`ProxyManager\GeneratorStrategy\BaseGeneratorStrategy`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/GeneratorStrategy/BaseGeneratorStrategy.php)
which retrieves the string representation of the class from `ClassGenerator`
* [`ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/GeneratorStrategy/EvaluatingGeneratorStrategy.php)
which calls `eval()` upon the generated class code before returning it. This is useful in cases where you want to generate
multiple classes at runtime
* [`ProxyManager\GeneratorStrategy\FileWriterGeneratorStrategy`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/GeneratorStrategy/FileWriterGeneratorStrategy.php)
which accepts a
[`ProxyManager\FileLocator\FileLocatorInterface`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/FileLocator/FileLocatorInterface.php)
instance as constructor parameter, and based on it, writes the generated class to a file before returning its code.
docs/lazy-loading-ghost-object.md 0000644 00000030772 13645114176 0013017 0 ustar 00 ---
title: Lazy Loading Ghost Object Proxies
---
# Lazy Loading Ghost Object Proxies
A Lazy Loading Ghost is a type of proxy object.
More specifically, it is a fake object that looks exactly like an object that you want to interact with but is just an empty
instance that gets all properties populated as soon as they are needed.
Those properties do not actually exist until the ghost object is initialized.
## Lazy loading with the Ghost Object
In pseudo-code, in userland, [lazy loading](http://www.martinfowler.com/eaaCatalog/lazyLoad.html) in a ghost object looks
like following:
```php
class MyObjectProxy
{
private $initialized = false;
private $name;
private $surname;
public function doFoo()
{
$this->init();
// Perform doFoo routine using loaded variables
}
private function init()
{
if (! $this->initialized) {
$data = some_logic_that_loads_data();
$this->name = $data['name'];
$this->surname = $data['surname'];
$this->initialized = true;
}
}
}
```
Ghost objects work similarly to virtual proxies, but since they do not wrap around a "real" instance of the proxied subject,
they are better suited for representing dataset rows.
## When do I use a ghost object?
You usually need a ghost object in cases where the following applies:
* you are building a small data-mapper and want to lazily load data across associations in your object graph
* you want to initialize objects representing rows in a large dataset
* you want to compare instances of lazily initialized objects without the risk of comparing a proxy with a real subject
* you are aware of the internal state of the object and are confident in working with its internals via reflection or direct
property access
## Usage examples
[ProxyManager](https://github.com/Ocramius/ProxyManager) provides a factory that creates lazy loading ghost objects.
To use it, follow these steps:
First, define your object's logic without taking care of lazy loading:
```php
namespace MyApp;
class Customer
{
private $name;
private $surname;
// just write your business logic or generally logic
// don't worry about how complex this object will be!
// don't code lazy-loading oriented optimizations in here!
public function getName() { return $this->name; }
public function setName($name) { $this->name = (string) $name; }
public function getSurname() { return $this->surname; }
public function setSurname($surname) { $this->surname = (string) $surname; }
}
```
Then, use the proxy manager to create a ghost object of it. You will be responsible for setting its state during lazy loading:
```php
namespace MyApp;
use ProxyManager\Factory\LazyLoadingGhostFactory;
use ProxyManager\Proxy\GhostObjectInterface;
require_once __DIR__ . '/vendor/autoload.php';
$factory = new LazyLoadingGhostFactory();
$initializer = function (
GhostObjectInterface $ghostObject,
string $method,
array $parameters,
& $initializer,
array $properties
) {
$initializer = null; // disable initialization
// load data and modify the object here
$properties["\0MyApp\\Customer\0name"] = 'Agent';
$properties["\0MyApp\\Customer\0surname"] = 'Smith';
// you may also call methods on the object, but remember that
// the constructor was not called yet:
$ghostObject->setSurname('Smith');
return true; // confirm that initialization occurred correctly
};
$ghostObject = $factory->createProxy(\MyApp\Customer::class, $initializer);
```
You can now use your object as before:
```php
// this will work as before
echo $ghostObject->getName() . ' ' . $ghostObject->getSurname(); // Agent Smith
```
## Lazy Initialization
We use a closure to handle lazy initialization of the proxy instance at runtime.
The initializer closure signature for ghost objects is:
```php
/**
* @var object $ghostObject The instance of the ghost object proxy that is being initialized.
* @var string $method The name of the method that triggered lazy initialization.
* @var array $parameters An ordered list of parameters passed to the method that
* triggered initialization, indexed by parameter name.
* @var Closure $initializer A reference to the property that is the initializer for the
* proxy. Set it to null to disable further initialization.
* @var array $properties By-ref array with the properties defined in the object, with their
* default values pre-assigned. Keys are in the same format that
* an (array) cast of an object would provide:
* - `"\0Ns\\ClassName\0propertyName"` for `private $propertyName`
* defined on `Ns\ClassName`
* - `"\0Ns\\ClassName\0propertyName"` for `protected $propertyName`
* defined in any level of the hierarchy
* - `"propertyName"` for `public $propertyName`
* defined in any level of the hierarchy
*
* @return bool true on success
*/
$initializer = function (
\ProxyManager\Proxy\GhostObjectInterface $ghostObject,
string $method,
array $parameters,
& $initializer,
array $properties
) {};
```
The initializer closure should usually look like:
```php
$initializer = function (
\ProxyManager\Proxy\GhostObjectInterface $ghostObject,
string $method,
array $parameters,
& $initializer,
array $properties
) {
$initializer = null; // disable initializer for this proxy instance
// initialize properties (please read further on)
$properties["\0ClassName\0foo"] = 'foo';
$properties["\0ClassName\0bar"] = 'bar';
return true; // report success
};
```
### Lazy initialization `$properties` explained
The assignments to properties in this closure use unusual `"\0"` sequences. This is to be consistent with how PHP represents
private and protected properties when casting an object to an array. `ProxyManager` copies a reference to the properties into
the `$properties` array passed to the initializer, which allows you to set the state of the object without accessing any of
its public API. (This is a significant detail for mapper implementations!)
Specifically:
* `"\0Ns\\ClassName\0propertyName"` means `private $propertyName` defined in `Ns\ClassName`;
* `"\0*\0propertyName"` means `protected $propertyName` defined in any level of the class
hierarchy;
* `"propertyName"` means `public $propertyName` defined in any level of the class hierarchy.
Therefore, given this class:
```php
namespace MyNamespace;
class MyClass
{
private $property1;
protected $property2;
public $property3;
}
```
Its appropriate initialization code would be:
```php
namespace MyApp;
use ProxyManager\Factory\LazyLoadingGhostFactory;
use ProxyManager\Proxy\GhostObjectInterface;
require_once __DIR__ . '/vendor/autoload.php';
$factory = new LazyLoadingGhostFactory();
$initializer = function (
GhostObjectInterface $ghostObject,
string $method,
array $parameters,
& $initializer,
array $properties
) {
$initializer = null;
$properties["\0MyNamespace\\MyClass\0property1"] = 'foo'; //private property of MyNamespace\MyClass
$properties["\0*\0property2"] = 'bar'; //protected property in MyClass's hierarchy
$properties["property3"] = 'baz'; //public property in MyClass's hierarchy
return true;
};
$instance = $factory->createProxy(\MyNamespace\MyClass::class, $initializer);
```
This code would initialize `$property1`, `$property2` and `$property3` respectively to `"foo"`, `"bar"` and `"baz"`.
You may read the default values for those properties by reading the respective array keys.
Although it is possible to initialize the object by interacting with its public API, it is not safe to do so, because the
object only contains default property values as its constructor was not called.
## Proxy implementation
The
[`ProxyManager\Factory\LazyLoadingGhostFactory`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Factory/LazyLoadingGhostFactory.php)
produces proxies that implement the
[`ProxyManager\Proxy\GhostObjectInterface`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Proxy/GhostObjectInterface.php).
At any point in time, you can set a new initializer for the proxy:
```php
$ghostObject->setProxyInitializer($initializer);
```
In your initializer, you **MUST** turn off any further initialization:
```php
$ghostObject->setProxyInitializer(null);
```
or
```php
$initializer = null; // if you use the initializer passed by reference to the closure
```
Remember to call `$ghostObject->setProxyInitializer(null);`, or to set `$initializer = null` inside your initializer closure
to disable initialization of your proxy, otherwise initialization will trigger more than once.
## Triggering Initialization
A lazy loading ghost object is initialized whenever you access any of its properties.
Any of the following interactions would trigger lazy initialization:
```php
// calling a method (only if the method accesses internal state)
$ghostObject->someMethod();
// reading a property
echo $ghostObject->someProperty;
// writing a property
$ghostObject->someProperty = 'foo';
// checking for existence of a property
isset($ghostObject->someProperty);
// removing a property
unset($ghostObject->someProperty);
// accessing a property via reflection
$reflection = new \ReflectionProperty($ghostObject, 'someProperty');
$reflection->setAccessible(true);
$reflection->getValue($ghostObject);
// cloning the entire proxy
clone $ghostObject;
// serializing the proxy
$unserialized = unserialize(serialize($ghostObject));
```
A method like following would never trigger lazy loading, in the context of a ghost object:
```php
public function sayHello() : string
{
return 'Look ma! No property accessed!';
}
```
## Skipping properties (properties that should not be initialized)
In some contexts, you may want some properties to be ignored entirely by the lazy-loading system.
An example for that (in data mappers) is entities with identifiers: an identifier is usually:
* lightweight
* known at all times
This means that it can be set in our object at all times, and we never need to lazy-load it. Here is a typical example:
```php
namespace MyApp;
class User
{
private $id;
private $username;
private $passwordHash;
private $email;
private $address;
// ...
public function getId() : int
{
return $this->id;
}
}
```
If we want to skip the property `$id` from lazy-loading, we might want to tell that to the `LazyLoadingGhostFactory`. Here is
a longer example, with a more real-world scenario:
```php
namespace MyApp;
use ProxyManager\Factory\LazyLoadingGhostFactory;
use ProxyManager\Proxy\GhostObjectInterface;
require_once __DIR__ . '/vendor/autoload.php';
$factory = new LazyLoadingGhostFactory();
$initializer = function (
GhostObjectInterface $ghostObject,
string $method,
array $parameters,
& $initializer,
array $properties
) {
$initializer = null;
// note that `getId` won't initialize our proxy here
$properties["\0MyApp\\User\0username"] = $db->fetchField('users', 'username', $ghostObject->getId();
$properties["\0MyApp\\User\0passwordHash"] = $db->fetchField('users', 'passwordHash', $ghostObject->getId();
$properties["\0MyApp\\User\0email"] = $db->fetchField('users', 'email', $ghostObject->getId();
$properties["\0MyApp\\User\0address"] = $db->fetchField('users', 'address', $ghostObject->getId();
return true;
};
$proxyOptions = [
'skippedProperties' => [
"\0MyApp\\User\0id",
],
];
$instance = $factory->createProxy(User::class, $initializer, $proxyOptions);
$idReflection = new \ReflectionProperty(User::class, 'id');
$idReflection->setAccessible(true);
// write the identifier into our ghost object (assuming `setId` doesn't exist)
$idReflection->setValue($instance, 1234);
```
In this example, we pass a `skippedProperties` array to our proxy factory. Note the use of the `"\0"` parameter syntax as
described above.
## Proxying interfaces
A lazy loading ghost object cannot proxy an interface directly, as it operates directly around the state of an object. Use a
[Virtual Proxy](lazy-loading-value-holder.md) for that instead.
## Tuning performance for production
See [Tuning ProxyManager for Production](tuning-for-production.md).
docs/lazy-loading-value-holder.md 0000644 00000015217 13645114176 0013013 0 ustar 00 ---
title: Lazy Loading Value Holder Proxy
---
# Lazy Loading Value Holder Proxy
A lazy loading value holder proxy is a virtual proxy that wraps and lazily initializes a "real" instance of the proxied class.
## What is lazy loading?
In pseudo-code, [lazy loading](http://www.martinfowler.com/eaaCatalog/lazyLoad.html) looks like the following:
```php
class MyObjectProxy
{
private $wrapped;
public function doFoo()
{
$this->init();
return $this->wrapped->doFoo();
}
private function init()
{
if (null === $this->wrapped) {
$this->wrapped = new MyObject();
}
}
}
```
This code is problematic and adds complexity that makes your unit tests' code even worse.
Also, this kind of usage often ends up in coupling your code with a particular
[Dependency Injection Container](http://martinfowler.com/articles/injection.html) or a framework that fetches dependencies
for you. That way, further complexity is introduced, and some problems related with service location raise, as explained
[in this article](http://ocramius.github.com/blog/zf2-and-symfony-service-proxies-with-doctrine-proxies/).
Lazy loading value holders abstract this logic for you, hiding your complex, slow, performance-impacting objects behind tiny
wrappers that have their same API, and that get initialized at first usage.
## When do I use a lazy value holder?
You usually need a lazy value holder in cases where the following applies:
* your object takes a lot of time and memory to be initialized (with all dependencies)
* your object is not always used, and the instantiation overhead is avoidable
## Usage examples
*ProxyManager* provides a factory that eases instantiation of lazy loading
value holders. To use it, follow these steps:
Firstly, define your object's logic without taking care of lazy loading:
```php
namespace MyApp;
class HeavyComplexObject
{
public function __construct()
{
// just write your business logic
// don't worry about how heavy initialization of this will be!
}
public function doFoo() {
echo 'OK!';
}
}
```
Then use the proxy manager to create a lazy version of the object (as a proxy):
```php
namespace MyApp;
use ProxyManager\Factory\LazyLoadingValueHolderFactory;
use ProxyManager\Proxy\LazyLoadingInterface;
require_once __DIR__ . '/vendor/autoload.php';
$factory = new LazyLoadingValueHolderFactory();
$initializer = function (& $wrappedObject, LazyLoadingInterface $proxy, $method, array $parameters, & $initializer) {
$initializer = null; // disable initialization
$wrappedObject = new HeavyComplexObject(); // fill your object with values here
return true; // confirm that initialization occurred correctly
};
$proxy = $factory->createProxy('MyApp\HeavyComplexObject', $initializer);
```
You can now use your object as before:
```php
// this will just work as before
$proxy->doFoo(); // OK!
```
## Lazy Initialization
As you can see, we use a closure to handle lazy initialization of the proxy instance at runtime. The initializer closure
signature should be as following:
```php
/**
* @var object $wrappedObject the instance (passed by reference) of the wrapped object,
* set it to your real object
* @var object $proxy the instance proxy that is being initialized
* @var string $method the name of the method that triggered lazy initialization
* @var array $parameters an ordered list of parameters passed to the method that
* triggered initialization, indexed by parameter name
* @var Closure $initializer a reference to the property that is the initializer for the
* proxy. Set it to null to disable further initialization
*
* @return bool true on success
*/
$initializer = function (& $wrappedObject, $proxy, $method, array $parameters, & $initializer) {};
```
The initializer closure should usually be coded like following:
```php
$initializer = function (& $wrappedObject, $proxy, $method, array $parameters, & $initializer) {
$newlyCreatedObject = new Foo(); // instantiation logic
$newlyCreatedObject->setBar('baz') // instantiation logic
$newlyCreatedObject->setBat('bam') // instantiation logic
$wrappedObject = $newlyCreatedObject; // set wrapped object in the proxy
$initializer = null; // disable initializer
return true; // report success
};
```
The
[`ProxyManager\Factory\LazyLoadingValueHolderFactory`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Factory/LazyLoadingValueHolderFactory.php)
produces proxies that implement both the
[`ProxyManager\Proxy\ValueHolderInterface`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Proxy/ValueHolderInterface.php)
and the
[`ProxyManager\Proxy\LazyLoadingInterface`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Proxy/LazyLoadingInterface.php).
At any point in time, you can set a new initializer for the proxy:
```php
$proxy->setProxyInitializer($initializer);
```
In your initializer, you currently **MUST** turn off any further initialization:
```php
$proxy->setProxyInitializer(null);
```
or
```php
$initializer = null; // if you use the initializer by reference
```
## Triggering Initialization
A lazy loading proxy is initialized whenever you access any property or method of it. Any of the following interactions would
trigger lazy initialization:
```php
// calling a method
$proxy->someMethod();
// reading a property
echo $proxy->someProperty;
// writing a property
$proxy->someProperty = 'foo';
// checking for existence of a property
isset($proxy->someProperty);
// removing a property
unset($proxy->someProperty);
// cloning the entire proxy
clone $proxy;
// serializing the proxy
$unserialized = serialize(unserialize($proxy));
```
Remember to call `$proxy->setProxyInitializer(null);` to disable initialization of your proxy, or it will happen more than
once.
## Proxying interfaces
You can also generate proxies from an interface FQCN. When you proxy an interface, you will only be able to access the
methods defined by the interface itself, even if the `wrappedObject` implements more methods. This will save some memory
since the proxy will not contain useless inherited properties.
## Known limitations
* methods using `func_get_args()`, `func_get_arg()` and `func_num_arg()` will not function properly for parameters that are
not part of the proxied object interface: use
[variadic arguments](http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list) instead.
## Tuning performance for production
See [Tuning ProxyManager for Production](tuning-for-production.md).
docs/null-object.md 0000644 00000005671 13645114176 0010255 0 ustar 00 ---
title: Null Object Proxy
---
# Null Object Proxy
A Null Object proxy is a [null object pattern](http://en.wikipedia.org/wiki/Null_Object_pattern) implementation.
The proxy factory creates a new object with defined neutral behaviour based on another object, class name or interface.
## What is null object proxy?
In your application, when you cannot return the object related to the request, the consumer of the model must check for the
return value and handle the failing condition gracefully, thus generating an explosion of conditionals throughout your code.
Fortunately, this seemingly-tangled situation can be simplified by creating a polymorphic implementation of the domain object,
which would implement the same interface as one of the objects in question, only that its methods would not do anything,
therefore offloading client code from doing repetitive checks for ugly null values when the operation is executed.
## Usage examples
```php
class UserMapper
{
private $adapter;
public function __construct(DatabaseAdapterInterface $adapter) {
$this->adapter = $adapter;
}
public function fetchById($id) {
$this->adapter->select('users', ['id' => $id]);
if (!$row = $this->adapter->fetch()) {
return null;
}
return $this->createUser($row);
}
private function createUser(array $row) {
$user = new Entity\User($row['name'], $row['email']);
$user->setId($row['id']);
return $user;
}
}
```
If you want to remove conditionals from client code, you need to have a version of the entity conforming to the corresponding
interface. With the Null Object Proxy, you can build this object :
```php
$factory = new \ProxyManager\Factory\NullObjectFactory();
$nullUser = $factory->createProxy('Entity\User');
var_dump($nullUser->getName()); // empty return
```
You can now return a valid entity :
```php
class UserMapper
{
private $adapter;
public function __construct(DatabaseAdapterInterface $adapter) {
$this->adapter = $adapter;
}
public function fetchById($id) {
$this->adapter->select('users', ['id' => $id]);
return $this->createUser($this->adapter->fetch());
}
private function createUser($row) {
if (!$row) {
$factory = new \ProxyManager\Factory\NullObjectFactory();
return $factory->createProxy('Entity\User');
}
$user = new Entity\User($row['name'], $row['email']);
$user->setId($row['id']);
return $user;
}
}
```
## Proxying interfaces
You can also generate proxies from an interface FQCN. By proxying an interface, you will only be able to access the methods
defined by the interface itself, and like with the object, the methods are empty.
## Tuning performance for production
See [Tuning ProxyManager for Production](tuning-for-production.md).
docs/remote-object.md 0000644 00000005457 13645114176 0010600 0 ustar 00 ---
title: Remote Object Proxy
---
# Remote Object Proxy
The remote object implementation is a mechanism that enables a local object to control another object on another server. Each
call method on the local object will do a network call to get information or execute operations on the remote object.
## What is remote object proxy?
A remote object is based on an interface. The remote interface defines the API that a consumer can call. This interface must
be implemented both by the client and the RPC server.
## Adapters
Laminas's RPC components (XmlRpc, JsonRpc & Soap) can be used with the remote object. You will need to require the one
you need via composer:
```sh
$ php composer.phar require laminas/laminas-xmlrpc:2.*
$ php composer.phar require laminas/laminas-json:2.*
$ php composer.phar require laminas/laminas-soap:2.*
```
ProxyManager comes with 3 adapters:
* `ProxyManager\Factory\RemoteObject\Adapter\XmlRpc`
* `ProxyManager\Factory\RemoteObject\Adapter\JsonRpc`
* `ProxyManager\Factory\RemoteObject\Adapter\Soap`
## Usage examples
RPC server side code (`xmlrpc.php` in your local webroot):
```php
interface FooServiceInterface
{
public function foo();
}
class Foo implements FooServiceInterface
{
/**
* Foo function
* @return string
*/
public function foo()
{
return 'bar remote';
}
}
$server = new Laminas\XmlRpc\Server();
$server->setClass('Foo', 'FooServiceInterface'); // my FooServiceInterface implementation
$server->handle();
```
Client side code (proxy) :
```php
interface FooServiceInterface
{
public function foo();
}
$factory = new \ProxyManager\Factory\RemoteObjectFactory(
new \ProxyManager\Factory\RemoteObject\Adapter\XmlRpc(
new \Laminas\XmlRpc\Client('https://localhost/xmlrpc.php')
)
);
$proxy = $factory->createProxy('FooServiceInterface');
var_dump($proxy->foo()); // "bar remote"
```
## Implementing custom adapters
Your adapters must implement `ProxyManager\Factory\RemoteObject\AdapterInterface`:
```php
interface AdapterInterface
{
/**
* Call remote object
*
* @param string $wrappedClass
* @param string $method
* @param array $params
*
* @return mixed
*/
public function call($wrappedClass, $method, array $params = []);
}
```
To create your implementation (for RESTful web services, for example), pass your adapter instance to your factory at
construction time.
## Known limitations
* methods using `func_get_args()`, `func_get_arg()` and `func_num_arg()` will not function properly for parameters that are
not part of the proxied object interface: use
[variadic arguments](http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list) instead.
## Tuning performance for production
See [Tuning ProxyManager for Production](tuning-for-production.md).
docs/tuning-for-production.md 0000644 00000003254 13645114176 0012306 0 ustar 00 ---
title: Tuning the ProxyManager for production
---
## Tuning the ProxyManager for production
By default, all proxy factories generate the required proxy classes at runtime.
Proxy generation causes I/O operations and uses significant amounts of reflection, so be sure to have generated all of your
proxies **before deploying your code on a live system**, or you may experience poor performance.
To generate proxies and store them as files, you need to use the `FileWriterGeneratorStrategy` by configuring ProxyManager.
The files generated in the directory will be needed to autoload the proxies.
You can configure ProxyManager so that it will try autoloading the proxies first. Generating them en-masse is not yet
implemented:
```php
$config = new \ProxyManager\Configuration();
// generate the proxies and store them as files
$fileLocator = new \ProxyManager\FileLocator\FileLocator(__DIR__.'/my/generated/classes/cache/dir');
$config->setGeneratorStrategy(new \ProxyManager\GeneratorStrategy\FileWriterGeneratorStrategy($fileLocator));
// set the directory to read the generated proxies from
$config->setProxiesTargetDir(__DIR__ . '/my/generated/classes/cache/dir');
// then register the autoloader
spl_autoload_register($config->getProxyAutoloader());
// pass the configuration to proxymanager factory
$factory = new ProxyManager\Factory\LazyLoadingValueHolderFactory($config);
```
You can also generate a classmap with all your proxy classes in it.
Please note that all the currently implemented `ProxyManager\Factory\*` classes accept a `ProxyManager\Configuration` object
as an optional constructor parameter. This allows for fine-tuning of ProxyManager according to your needs.
examples/access-interceptor-scope-localizer.php 0000644 00000002664 13645114176 0016001 0 ustar 00 counter += 1;
return $this;
}
}
(static function () : void {
$factory = new AccessInterceptorScopeLocalizerFactory();
$foo = new FluentCounter();
$proxy = $factory->createProxy(
$foo,
[
'fluentMethod' => static function (AccessInterceptorInterface $proxy, FluentCounter $realInstance) : void {
echo "pre-fluentMethod #{$realInstance->counter}!\n";
},
],
[
'fluentMethod' => static function (AccessInterceptorInterface $proxy, FluentCounter $realInstance) : void {
echo "post-fluentMethod #{$realInstance->counter}!\n";
},
]
);
$proxy->fluentMethod()->fluentMethod()->fluentMethod()->fluentMethod();
echo 'The proxy counter is now at ' . $proxy->counter . "\n";
echo 'The real instance counter is now at ' . $foo->counter . "\n";
})();
examples/ghost-object-skipped-properties.php 0000644 00000002534 13645114176 0015326 0 ustar 00 id;
}
public function getUsername() : ?string
{
return $this->username;
}
}
(static function () : void {
$proxy = (new LazyLoadingGhostFactory())->createProxy(
User::class,
static function (
GhostObjectInterface $proxy,
string $method,
array $parameters,
?Closure & $initializer,
array $properties
) {
$initializer = null;
var_dump('Triggered lazy-loading!');
$properties["\0ProxyManager\\Example\\GhostObjectSkippedProperties\\User\0username"] = 'Ocramius';
return true;
},
[
'skippedProperties' => ["\0User\0id"],
]
);
$idReflection = new ReflectionProperty(User::class, 'id');
$idReflection->setAccessible(true);
$idReflection->setValue($proxy, 123);
var_dump($proxy->getId());
var_dump($proxy->getUsername());
})();
examples/ghost-object.php 0000644 00000002634 13645114176 0011500 0 ustar 00 foo = $foo;
}
public function getFoo() : string
{
return $this->foo;
}
}
(static function () : void {
$startTime = microtime(true);
$factory = new LazyLoadingGhostFactory();
$i = 0;
do {
$proxy = $factory->createProxy(
Foo::class,
function (
GhostObjectInterface $proxy,
string $method,
array $parameters,
?Closure & $initializer,
array $properties
) : bool {
$initializer = null;
$properties["\0ProxyManager\\Example\\GhostObject\\Foo\0foo"] = 'Hello World!';
return true;
}
);
$i += 1;
} while ($i < 1000);
var_dump('time after 1000 instantiations: ' . (microtime(true) - $startTime));
echo $proxy->getFoo() . "\n";
var_dump('time after single call to doFoo: ' . (microtime(true) - $startTime));
})();
examples/remote-proxy.php 0000644 00000002161 13645114176 0011555 0 ustar 00 createProxy(Foo::class);
try {
var_dump($proxy->bar()); // bar remote !
} catch (RuntimeException $error) {
echo "To run this example, please following before:\n\n\$ php -S localhost:9876 -t \"" . __DIR__ . "\"\n";
exit(2);
}
})();
examples/remote-proxy/remote-proxy-server.php 0000644 00000000652 13645114176 0015536 0 ustar 00 setClass(new Foo(), 'Foo');
$server->setReturnResponse(false);
$server->handle();
})();
examples/smart-reference.php 0000644 00000001260 13645114176 0012164 0 ustar 00 createProxy(
new Foo(),
[
'doFoo' => function () : void {
echo "pre-foo!\n";
},
],
[
'doFoo' => function () : void {
echo "post-foo!\n";
},
]
);
$proxy->doFoo();
})();
examples/virtual-proxy.php 0000644 00000002112 13645114176 0011744 0 ustar 00 createProxy(
Foo::class,
static function (
?object & $wrappedObject, ?object $proxy, string $method, array $parameters, ?Closure & $initializer
) {
$initializer = null;
$wrappedObject = new Foo();
return true;
}
);
$i += 1;
} while ($i < 1000);
var_dump('time after 1000 instantiations: ' . (microtime(true) - $startTime));
$proxy->doFoo();
var_dump('time after single call to doFoo: ' . (microtime(true) - $startTime));
})();
proxy-manager.svg 0000644 00000033521 13645114176 0010072 0 ustar 00
src/ProxyManager/Autoloader/Autoloader.php 0000644 00000002264 13645114176 0014672 0 ustar 00 fileLocator = $fileLocator;
$this->classNameInflector = $classNameInflector;
}
/**
* {@inheritDoc}
*/
public function __invoke(string $className) : bool
{
if (class_exists($className, false) || ! $this->classNameInflector->isProxyClassName($className)) {
return false;
}
$file = $this->fileLocator->getProxyFileName($className);
if (! file_exists($file)) {
return false;
}
/* @noinspection PhpIncludeInspection */
/* @noinspection UsingInclusionOnceReturnValueInspection */
return (bool) require_once $file;
}
}
src/ProxyManager/Autoloader/AutoloaderInterface.php 0000644 00000000623 13645114176 0016510 0 ustar 00 proxyAutoloader = $proxyAutoloader;
}
public function getProxyAutoloader() : AutoloaderInterface
{
return $this->proxyAutoloader
?? $this->proxyAutoloader = new Autoloader(
new FileLocator($this->getProxiesTargetDir()),
$this->getClassNameInflector()
);
}
public function setProxiesNamespace(string $proxiesNamespace) : void
{
$this->proxiesNamespace = $proxiesNamespace;
}
public function getProxiesNamespace() : string
{
return $this->proxiesNamespace;
}
public function setProxiesTargetDir(string $proxiesTargetDir) : void
{
$this->proxiesTargetDir = $proxiesTargetDir;
}
public function getProxiesTargetDir() : string
{
return $this->proxiesTargetDir
?? $this->proxiesTargetDir = sys_get_temp_dir();
}
public function setGeneratorStrategy(GeneratorStrategyInterface $generatorStrategy) : void
{
$this->generatorStrategy = $generatorStrategy;
}
public function getGeneratorStrategy() : GeneratorStrategyInterface
{
return $this->generatorStrategy
?? $this->generatorStrategy = new EvaluatingGeneratorStrategy();
}
public function setClassNameInflector(ClassNameInflectorInterface $classNameInflector) : void
{
$this->classNameInflector = $classNameInflector;
}
public function getClassNameInflector() : ClassNameInflectorInterface
{
return $this->classNameInflector
?? $this->classNameInflector = new ClassNameInflector($this->getProxiesNamespace());
}
public function setSignatureGenerator(SignatureGeneratorInterface $signatureGenerator) : void
{
$this->signatureGenerator = $signatureGenerator;
}
public function getSignatureGenerator() : SignatureGeneratorInterface
{
return $this->signatureGenerator
?? $this->signatureGenerator = new SignatureGenerator();
}
public function setSignatureChecker(SignatureCheckerInterface $signatureChecker) : void
{
$this->signatureChecker = $signatureChecker;
}
public function getSignatureChecker() : SignatureCheckerInterface
{
return $this->signatureChecker
?? $this->signatureChecker = new SignatureChecker($this->getSignatureGenerator());
}
public function setClassSignatureGenerator(ClassSignatureGeneratorInterface $classSignatureGenerator) : void
{
$this->classSignatureGenerator = $classSignatureGenerator;
}
public function getClassSignatureGenerator() : ClassSignatureGeneratorInterface
{
return $this->classSignatureGenerator
?? $this->classSignatureGenerator = new ClassSignatureGenerator($this->getSignatureGenerator());
}
}
src/ProxyManager/Exception/DisabledMethodException.php 0000644 00000000717 13645114176 0017162 0 ustar 00 getMessage(), 0, $previous);
}
}
src/ProxyManager/Exception/InvalidProxiedClassException.php 0000644 00000003306 13645114176 0020216 0 ustar 00 getName()));
}
public static function finalClassNotSupported(ReflectionClass $reflection) : self
{
return new self(sprintf('Provided class "%s" is final and cannot be proxied', $reflection->getName()));
}
public static function abstractProtectedMethodsNotSupported(ReflectionClass $reflection) : self
{
return new self(sprintf(
'Provided class "%s" has following protected abstract methods, and therefore cannot be proxied:' . "\n%s",
$reflection->getName(),
implode(
"\n",
array_map(
static function (ReflectionMethod $reflectionMethod) : string {
return $reflectionMethod->getDeclaringClass()->getName() . '::' . $reflectionMethod->getName();
},
array_filter(
$reflection->getMethods(),
static function (ReflectionMethod $method) : bool {
return $method->isAbstract() && $method->isProtected();
}
)
)
)
));
}
}
src/ProxyManager/Exception/InvalidProxyDirectoryException.php 0000644 00000000676 13645114176 0020633 0 ustar 00 getName(),
$property->getDeclaringClass()->getName()
)
);
}
public static function nonReferenceableLocalizedReflectionProperties(
ReflectionClass $class,
Properties $properties
) : self {
return new self(sprintf(
'Cannot create references for following properties of class %s: %s',
$class->getName(),
implode(', ', array_map(static function (ReflectionProperty $property) : string {
return $property->getName();
}, $properties->getInstanceProperties()))
));
}
}
src/ProxyManager/Factory/AbstractBaseFactory.php 0000644 00000007622 13645114176 0015774 0 ustar 00
* @psalm-var array
*/
private array $checkedClasses = [];
public function __construct(?Configuration $configuration = null)
{
$this->configuration = $configuration ?: new Configuration();
}
/**
* Generate a proxy from a class name
*
* @param array $proxyOptions
*
* @throws InvalidSignatureException
* @throws MissingSignatureException
* @throws OutOfBoundsException
*
* @psalm-template RealObjectType of object
*
* @psalm-param class-string $className
*
* @psalm-return class-string
*/
protected function generateProxy(string $className, array $proxyOptions = []) : string
{
if (array_key_exists($className, $this->checkedClasses)) {
$generatedClassName = $this->checkedClasses[$className];
assert(is_a($generatedClassName, $className, true));
return $generatedClassName;
}
$proxyParameters = [
'className' => $className,
'factory' => static::class,
'proxyManagerVersion' => Version::getVersion(),
'proxyOptions' => $proxyOptions,
];
$proxyClassName = $this
->configuration
->getClassNameInflector()
->getProxyClassName($className, $proxyParameters);
if (! class_exists($proxyClassName)) {
$this->generateProxyClass(
$proxyClassName,
$className,
$proxyParameters,
$proxyOptions
);
}
$this
->configuration
->getSignatureChecker()
->checkSignature(new ReflectionClass($proxyClassName), $proxyParameters);
return $this->checkedClasses[$className] = $proxyClassName;
}
abstract protected function getGenerator() : ProxyGeneratorInterface;
/**
* Generates the provided `$proxyClassName` from the given `$className` and `$proxyParameters`
*
* @param array $proxyParameters
* @param array $proxyOptions
*
* @psalm-param class-string $proxyClassName
* @psalm-param class-string $className
*/
private function generateProxyClass(
string $proxyClassName,
string $className,
array $proxyParameters,
array $proxyOptions = []
) : void {
$className = $this->configuration->getClassNameInflector()->getUserClassName($className);
$phpClass = new ClassGenerator($proxyClassName);
/** @psalm-suppress TooManyArguments - generator interface was not updated due to BC compliance */
$this->getGenerator()->generate(new ReflectionClass($className), $phpClass, $proxyOptions);
$phpClass = $this->configuration->getClassSignatureGenerator()->addSignature($phpClass, $proxyParameters);
/** @psalm-suppress TooManyArguments - generator interface was not updated due to BC compliance */
$this->configuration->getGeneratorStrategy()->generate($phpClass, $proxyOptions);
$autoloader = $this->configuration->getProxyAutoloader();
$autoloader($proxyClassName);
}
}
src/ProxyManager/Factory/AccessInterceptorScopeLocalizerFactory.php 0000644 00000006125 13645114176 0021712 0 ustar 00 generator = new AccessInterceptorScopeLocalizerGenerator();
}
/**
* @param object $instance the object to be localized within the access interceptor
* @param array $prefixInterceptors an array (indexed by method name) of interceptor closures to be called
* before method logic is executed
* @param array $suffixInterceptors an array (indexed by method name) of interceptor closures to be called
* after method logic is executed
*
* @throws InvalidSignatureException
* @throws MissingSignatureException
* @throws OutOfBoundsException
*
* @psalm-template RealObjectType of object
*
* @psalm-param RealObjectType $instance
* @psalm-param array=,
* RealObjectType=,
* string=,
* array=,
* bool=
* ) : mixed> $prefixInterceptors
* @psalm-param array=,
* RealObjectType=,
* string=,
* array=,
* mixed=,
* bool=
* ) : mixed> $suffixInterceptors
*
* @psalm-return RealObjectType&AccessInterceptorInterface
*
* @psalm-suppress MixedInferredReturnType We ignore type checks here, since `staticProxyConstructor` is not
* interfaced (by design)
*/
public function createProxy(
object $instance,
array $prefixInterceptors = [],
array $suffixInterceptors = []
) : AccessInterceptorInterface {
$proxyClassName = $this->generateProxy(get_class($instance));
/**
* We ignore type checks here, since `staticProxyConstructor` is not interfaced (by design)
*
* @psalm-suppress MixedMethodCall
* @psalm-suppress MixedReturnStatement
*/
return $proxyClassName::staticProxyConstructor($instance, $prefixInterceptors, $suffixInterceptors);
}
/**
* {@inheritDoc}
*/
protected function getGenerator() : ProxyGeneratorInterface
{
return $this->generator;
}
}
src/ProxyManager/Factory/AccessInterceptorValueHolderFactory.php 0000644 00000006424 13645114176 0021210 0 ustar 00 generator = new AccessInterceptorValueHolderGenerator();
}
/**
* @param object $instance the object to be wrapped within the value holder
* @param array $prefixInterceptors an array (indexed by method name) of interceptor closures to be called
* before method logic is executed
* @param array $suffixInterceptors an array (indexed by method name) of interceptor closures to be called
* after method logic is executed
*
* @throws InvalidSignatureException
* @throws MissingSignatureException
* @throws OutOfBoundsException
*
* @psalm-template RealObjectType of object
*
* @psalm-param RealObjectType $instance
* @psalm-param array=,
* RealObjectType=,
* string=,
* array=,
* bool=
* ) : mixed> $prefixInterceptors
* @psalm-param array=,
* RealObjectType=,
* string=,
* array=,
* mixed=,
* bool=
* ) : mixed> $suffixInterceptors
*
* @psalm-return RealObjectType&AccessInterceptorInterface&ValueHolderInterface&AccessInterceptorValueHolderInterface
*
* @psalm-suppress MixedInferredReturnType We ignore type checks here, since `staticProxyConstructor` is not
* interfaced (by design)
*/
public function createProxy(
object $instance,
array $prefixInterceptors = [],
array $suffixInterceptors = []
) : AccessInterceptorValueHolderInterface {
$proxyClassName = $this->generateProxy(get_class($instance));
/**
* We ignore type checks here, since `staticProxyConstructor` is not interfaced (by design)
*
* @psalm-suppress MixedMethodCall
* @psalm-suppress MixedReturnStatement
*/
return $proxyClassName::staticProxyConstructor($instance, $prefixInterceptors, $suffixInterceptors);
}
/**
* {@inheritDoc}
*/
protected function getGenerator() : ProxyGeneratorInterface
{
return $this->generator;
}
}
src/ProxyManager/Factory/LazyLoadingGhostFactory.php 0000644 00000010757 13645114176 0016663 0 ustar 00 generator = new LazyLoadingGhostGenerator();
}
/**
* {@inheritDoc}
*/
protected function getGenerator() : ProxyGeneratorInterface
{
return $this->generator;
}
/**
* Creates a new lazy proxy instance of the given class with
* the given initializer
*
* Please refer to the following documentation when using this method:
*
* @link https://github.com/Ocramius/ProxyManager/blob/master/docs/lazy-loading-ghost-object.md
*
* @param string $className name of the class to be proxied
* @param Closure $initializer initializer to be passed to the proxy. The initializer closure should have following
* signature:
*
*
* $initializer = function (
* GhostObjectInterface $proxy,
* string $method,
* array $parameters,
* & $initializer,
* array $properties
* ) {};
*
*
* Where:
* - $proxy is the proxy instance on which the initializer is acting
* - $method is the name of the method that triggered the lazy initialization
* - $parameters are the parameters that were passed to $method
* - $initializer by-ref initializer - should be assigned null in the initializer body
* - $properties a by-ref map of the properties of the object, indexed by PHP
* internal property name. Assign values to it to initialize the
* object state
* @param mixed[] $proxyOptions a set of options to be used when generating the proxy. Currently supports only
* key "skippedProperties", which allows to skip lazy-loading of some properties.
* "skippedProperties" is a string[], containing a list of properties referenced
* via PHP's internal property name (i.e. "\0ClassName\0propertyName")
*
* @throws MissingSignatureException
* @throws InvalidSignatureException
* @throws OutOfBoundsException
*
* @psalm-template RealObjectType as object
*
* @psalm-param class-string $className
* @psalm-param Closure(
* RealObjectType&GhostObjectInterface=,
* string=,
* array=,
* ?Closure=,
* array=
* ) : bool $initializer
* @psalm-param array{skippedProperties?: array} $proxyOptions
*
* @psalm-return RealObjectType&GhostObjectInterface
*
* @psalm-suppress MixedInferredReturnType We ignore type checks here, since `staticProxyConstructor` is not
* interfaced (by design)
*/
public function createProxy(
string $className,
Closure $initializer,
array $proxyOptions = []
) : GhostObjectInterface {
$proxyClassName = $this->generateProxy($className, $proxyOptions);
/**
* We ignore type checks here, since `staticProxyConstructor` is not interfaced (by design)
*
* @psalm-suppress MixedMethodCall
* @psalm-suppress MixedReturnStatement
*/
return $proxyClassName::staticProxyConstructor($initializer);
}
}
src/ProxyManager/Factory/LazyLoadingValueHolderFactory.php 0000644 00000004103 13645114176 0017775 0 ustar 00 generator = new LazyLoadingValueHolderGenerator();
}
/**
* @param array $proxyOptions
*
* @psalm-template RealObjectType of object
*
* @psalm-param class-string $className
* @psalm-param Closure(
* RealObjectType|null=,
* RealObjectType&ValueHolderInterface&VirtualProxyInterface=,
* string=,
* array=,
* ?Closure=
* ) : bool $initializer
*
* @psalm-return RealObjectType&ValueHolderInterface&VirtualProxyInterface
*
* @psalm-suppress MixedInferredReturnType We ignore type checks here, since `staticProxyConstructor` is not
* interfaced (by design)
*/
public function createProxy(
string $className,
Closure $initializer,
array $proxyOptions = []
) : VirtualProxyInterface {
$proxyClassName = $this->generateProxy($className, $proxyOptions);
/**
* We ignore type checks here, since `staticProxyConstructor` is not interfaced (by design)
*
* @psalm-suppress MixedMethodCall
* @psalm-suppress MixedReturnStatement
*/
return $proxyClassName::staticProxyConstructor($initializer);
}
/**
* {@inheritDoc}
*/
protected function getGenerator() : ProxyGeneratorInterface
{
return $this->generator;
}
}
src/ProxyManager/Factory/NullObjectFactory.php 0000644 00000004145 13645114176 0015474 0 ustar 00 generator = new NullObjectGenerator();
}
/**
* @param object|string $instanceOrClassName the object to be wrapped or interface to transform to null object
*
* @throws InvalidSignatureException
* @throws MissingSignatureException
* @throws OutOfBoundsException
*
* @psalm-template RealObjectType of object
*
* @psalm-param RealObjectType|class-string $instanceOrClassName
*
* @psalm-return RealObjectType&NullObjectInterface
*
* @psalm-suppress MixedInferredReturnType We ignore type checks here, since `staticProxyConstructor` is not
* interfaced (by design)
*/
public function createProxy($instanceOrClassName) : NullObjectInterface
{
$className = is_object($instanceOrClassName) ? get_class($instanceOrClassName) : $instanceOrClassName;
$proxyClassName = $this->generateProxy($className);
/**
* We ignore type checks here, since `staticProxyConstructor` is not interfaced (by design)
*
* @psalm-suppress MixedMethodCall
* @psalm-suppress MixedReturnStatement
*/
return $proxyClassName::staticProxyConstructor();
}
/**
* {@inheritDoc}
*/
protected function getGenerator() : ProxyGeneratorInterface
{
return $this->generator;
}
}
src/ProxyManager/Factory/RemoteObject/Adapter/BaseAdapter.php 0000644 00000002355 13645114176 0020221 0 ustar 00
*/
protected array $map = [];
/**
* Constructor
*
* @param array $map map of service names to their aliases
*/
public function __construct(Client $client, array $map = [])
{
$this->client = $client;
$this->map = $map;
}
/**
* {@inheritDoc}
*/
public function call(string $wrappedClass, string $method, array $params = [])
{
$serviceName = $this->getServiceName($wrappedClass, $method);
if (array_key_exists($serviceName, $this->map)) {
$serviceName = $this->map[$serviceName];
}
return $this->client->call($serviceName, $params);
}
/**
* Get the service name will be used by the adapter
*/
abstract protected function getServiceName(string $wrappedClass, string $method) : string;
}
src/ProxyManager/Factory/RemoteObject/Adapter/JsonRpc.php 0000644 00000000533 13645114176 0017420 0 ustar 00 $params
*
* @return mixed
*/
public function call(string $wrappedClass, string $method, array $params = []);
}
src/ProxyManager/Factory/RemoteObjectFactory.php 0000644 00000004614 13645114176 0016016 0 ustar 00 adapter = $adapter;
$this->generator = new RemoteObjectGenerator();
}
/**
* @param string|object $instanceOrClassName
*
* @throws InvalidSignatureException
* @throws MissingSignatureException
* @throws OutOfBoundsException
*
* @psalm-template RealObjectType of object
*
* @psalm-param RealObjectType|class-string $instanceOrClassName
*
* @psalm-return RealObjectType&RemoteObjectInterface
*
* @psalm-suppress MixedInferredReturnType We ignore type checks here, since `staticProxyConstructor` is not
* interfaced (by design)
*/
public function createProxy($instanceOrClassName) : RemoteObjectInterface
{
$proxyClassName = $this->generateProxy(
is_object($instanceOrClassName) ? get_class($instanceOrClassName) : $instanceOrClassName
);
/**
* We ignore type checks here, since `staticProxyConstructor` is not interfaced (by design)
*
* @psalm-suppress MixedMethodCall
* @psalm-suppress MixedReturnStatement
*/
return $proxyClassName::staticProxyConstructor($this->adapter);
}
/**
* {@inheritDoc}
*/
protected function getGenerator() : ProxyGeneratorInterface
{
return $this->generator ?? $this->generator = new RemoteObjectGenerator();
}
}
src/ProxyManager/FileLocator/FileLocator.php 0000644 00000001642 13645114176 0015101 0 ustar 00 proxiesDirectory = $absolutePath;
}
/**
* {@inheritDoc}
*/
public function getProxyFileName(string $className) : string
{
return $this->proxiesDirectory . DIRECTORY_SEPARATOR . str_replace('\\', '', $className) . '.php';
}
}
src/ProxyManager/FileLocator/FileLocatorInterface.php 0000644 00000000466 13645114176 0016725 0 ustar 00 $interfaces
*
* @psalm-suppress MoreSpecificImplementedParamType parent interface does not specify type of array values
*/
public function setImplementedInterfaces(array $interfaces) : ZendClassGenerator
{
return parent::setImplementedInterfaces(array_map(
static function (string $interface) : string {
return '\\' . trim($interface, '\\');
},
$interfaces
));
}
}
src/ProxyManager/Generator/MagicMethodGenerator.php 0000644 00000001336 13645114176 0016451 0 ustar 00 setReturnsReference(strtolower($name) === '__get');
if (! $originalClass->hasMethod($name)) {
return;
}
$this->setReturnsReference($originalClass->getMethod($name)->returnsReference());
}
}
src/ProxyManager/Generator/MethodGenerator.php 0000644 00000002103 13645114176 0015501 0 ustar 00 setInterface(false);
$method->setBody('');
return $method;
}
/**
* {@inheritDoc} override needed to specify type in more detail
*/
public function getDocBlock() : ?DocBlockGenerator
{
return parent::getDocBlock();
}
/**
* {@inheritDoc} override needed to specify type in more detail
*/
public function getSourceContent() : ?string
{
return parent::getSourceContent();
}
}
src/ProxyManager/Generator/Util/ClassGeneratorUtils.php 0000644 00000001344 13645114176 0017272 0 ustar 00 getName();
if ($originalClass->hasMethod($methodName) && $originalClass->getMethod($methodName)->isFinal()) {
return false;
}
$classGenerator->addMethodFromGenerator($generatedMethod);
return true;
}
}
src/ProxyManager/Generator/Util/IdentifierSuffixer.php 0000644 00000002410 13645114176 0017126 0 ustar 00 getReturnType();
$originalReturnTypeName = $originalReturnType === null
? null
: $originalReturnType->getName();
if ($originalReturnTypeName === 'void') {
return $returnedValueExpression . ";\nreturn;";
}
return 'return ' . $returnedValueExpression . ';';
}
}
src/ProxyManager/Generator/Util/UniqueIdentifierGenerator.php 0000644 00000001761 13645114176 0020460 0 ustar 00 generate();
}
}
src/ProxyManager/GeneratorStrategy/EvaluatingGeneratorStrategy.php 0000644 00000002547 13645114176 0021642 0 ustar 00 canEval = ! ini_get('suhosin.executor.disable_eval');
// @codeCoverageIgnoreEnd
}
/**
* Evaluates the generated code before returning it
*
* {@inheritDoc}
*/
public function generate(ClassGenerator $classGenerator) : string
{
/** @var string $code */
$code = $classGenerator->generate();
// @codeCoverageIgnoreStart
if (! $this->canEval) {
$fileName = __DIR__ . '/EvaluatingGeneratorStrategy.php.tmp';
FileWriter::writeFile($fileName, "fileLocator = $fileLocator;
$this->emptyErrorHandler = static function () : void {
};
}
/**
* Write generated code to disk and return the class code
*
* {@inheritDoc}
*
* @throws FileNotWritableException
*/
public function generate(ClassGenerator $classGenerator) : string
{
/** @var string $generatedCode */
$generatedCode = $classGenerator->generate();
$className = $classGenerator->getNamespaceName() . '\\' . $classGenerator->getName();
$fileName = $this->fileLocator->getProxyFileName($className);
set_error_handler($this->emptyErrorHandler);
try {
FileWriter::writeFile($fileName, "proxyNamespace = $proxyNamespace;
$this->proxyMarker = '\\' . self::PROXY_MARKER . '\\';
$this->proxyMarkerLength = strlen($this->proxyMarker);
$this->parameterHasher = new ParameterHasher();
}
/**
* {@inheritDoc}
*
* @psalm-suppress MoreSpecificReturnType we ignore these issues because classes may not have been loaded yet
*/
public function getUserClassName(string $className) : string
{
$className = ltrim($className, '\\');
$position = strrpos($className, $this->proxyMarker);
if (! is_int($position)) {
/** @psalm-suppress LessSpecificReturnStatement */
return $className;
}
/** @psalm-suppress LessSpecificReturnStatement */
return substr(
$className,
$this->proxyMarkerLength + $position,
(int) strrpos($className, '\\') - ($position + $this->proxyMarkerLength)
);
}
/**
* {@inheritDoc}
*
* @psalm-suppress MoreSpecificReturnType we ignore these issues because classes may not have been loaded yet
*/
public function getProxyClassName(string $className, array $options = []) : string
{
/** @psalm-suppress LessSpecificReturnStatement */
return $this->proxyNamespace
. $this->proxyMarker
. $this->getUserClassName($className)
. '\\Generated' . $this->parameterHasher->hashParameters($options);
}
/**
* {@inheritDoc}
*/
public function isProxyClassName(string $className) : bool
{
return strrpos($className, $this->proxyMarker) !== false;
}
}
src/ProxyManager/Inflector/ClassNameInflectorInterface.php 0000644 00000003024 13645114176 0017751 0 ustar 00 |class-string> $className
* @psalm-return class-string
*/
public function getUserClassName(string $className) : string;
/**
* Retrieve the class name of the proxy for the given user-defined class name
*
* @param array $options arbitrary options to be used for the generated class name
*
* @psalm-template RealClassName of object
*
* @psalm-param class-string|class-string> $className
*
* @psalm-return class-string
*/
public function getProxyClassName(string $className, array $options = []) : string;
/**
* Retrieve whether the provided class name is a proxy
*
* @psalm-template RealClassName of object
* @psalm-param class-string|class-string> $className
*/
public function isProxyClassName(string $className) : bool;
}
src/ProxyManager/Inflector/Util/ParameterEncoder.php 0000644 00000000773 13645114176 0016561 0 ustar 00
* $interceptor = function ($proxy, $instance, string $method, array $params, & $returnEarly) {};
*
*
* @param string $methodName name of the intercepted method
* @param Closure|null $prefixInterceptor interceptor closure or null to unset the currently active interceptor
*
* @psalm-param null|Closure(
* InterceptedObjectType&AccessInterceptorInterface=,
* InterceptedObjectType=,
* string=,
* array=,
* bool=
* ) : mixed $prefixInterceptor
*/
public function setMethodPrefixInterceptor(string $methodName, ?Closure $prefixInterceptor = null) : void;
/**
* Set or remove the suffix interceptor for a method
*
* @link https://github.com/Ocramius/ProxyManager/blob/master/docs/access-interceptor-value-holder.md
*
* A prefix interceptor should have a signature like following:
*
*
* $interceptor = function ($proxy, $instance, string $method, array $params, $returnValue, & $returnEarly) {};
*
*
* @param string $methodName name of the intercepted method
* @param Closure|null $suffixInterceptor interceptor closure or null to unset the currently active interceptor
*
* @psalm-param null|Closure(
* InterceptedObjectType&AccessInterceptorInterface=,
* InterceptedObjectType=,
* string=,
* array=,
* mixed=,
* bool=
* ) : mixed $suffixInterceptor
*/
public function setMethodSuffixInterceptor(string $methodName, ?Closure $suffixInterceptor = null) : void;
}
src/ProxyManager/Proxy/AccessInterceptorValueHolderInterface.php 0000644 00000003343 13645114176 0021210 0 ustar 00 =,
* bool=
* ) : mixed $prefixInterceptor
*/
public function setMethodPrefixInterceptor(string $methodName, ?Closure $prefixInterceptor = null) : void;
/**
* {@inheritDoc}
*
* Definitions are duplicated here to allow templated definitions in this child type
*
* @param string $methodName name of the intercepted method
* @param Closure|null $suffixInterceptor interceptor closure or null to unset the currently active interceptor
*
* @psalm-param null|Closure(
* InterceptedObjectType&AccessInterceptorInterface=,
* InterceptedObjectType=,
* string=,
* array=,
* mixed=,
* bool=
* ) : mixed $suffixInterceptor
*/
public function setMethodSuffixInterceptor(string $methodName, ?Closure $suffixInterceptor = null) : void;
/**
* {@inheritDoc}
*
* Definitions are duplicated here to allow templated definitions in this child type
*
* @psalm-return InterceptedObjectType|null
*/
public function getWrappedValueHolderValue() : ?object;
}
src/ProxyManager/Proxy/Exception/RemoteObjectException.php 0000644 00000000423 13645114176 0020007 0 ustar 00 =,
* string=,
* array=,
* ?Closure=,
* array=
* ) : bool $initializer
*/
public function setProxyInitializer(?Closure $initializer = null);
/**
* {@inheritDoc}
*
* Definitions are duplicated here to allow templated definitions in this child type
*
* @psalm-suppress ImplementedReturnTypeMismatch Note that the closure signature below is slightly different
* from the one declared in LazyLoadingInterface.
* @psalm-return null|Closure(
* LazilyLoadedObjectType&GhostObjectInterface=,
* string,
* array=,
* ?Closure=,
* array=
* ) : bool
*/
public function getProxyInitializer() : ?Closure;
}
src/ProxyManager/Proxy/LazyLoadingInterface.php 0000644 00000003472 13645114176 0015655 0 ustar 00
* $initializer = function (
* & ?object $wrappedObject,
* LazyLoadingInterface $proxy,
* string $calledMethod,
* array $callParameters,
* & ?\Closure $initializer,
* array $propertiesToBeSet = [] // works only on ghost objects
* ) {};
*
*
* @return void
*
* @psalm-param null|Closure(
* LazilyLoadedObjectType|null=,
* LazilyLoadedObjectType&LazyLoadingInterface=,
* string=,
* array=,
* ?Closure=,
* array=
* ) : bool $initializer
*/
public function setProxyInitializer(?Closure $initializer = null);
/**
* @psalm-return null|Closure(
* LazilyLoadedObjectType|null=,
* LazilyLoadedObjectType&LazyLoadingInterface=,
* string,
* array=,
* ?Closure=,
* array=
* ) : bool
*/
public function getProxyInitializer() : ?Closure;
/**
* Force initialization of the proxy
*
* @return bool true if the proxy could be initialized
*/
public function initializeProxy() : bool;
/**
* Retrieves current initialization status of the proxy
*/
public function isProxyInitialized() : bool;
}
src/ProxyManager/Proxy/NullObjectInterface.php 0000644 00000000227 13645114176 0015474 0 ustar 00 setBody(UnsetPropertiesGenerator::generateSnippet(
Properties::fromReflectionClass($originalClass),
'this'
));
}
}
src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/SetMethodPrefixInterceptor.php 0000644 00000002232 13645114176 0027457 0 ustar 00 setType(Closure::class);
$interceptor->setDefaultValue(null);
$this->setParameter(new ParameterGenerator('methodName', 'string'));
$this->setParameter($interceptor);
$this->setReturnType('void');
$this->setBody('$this->' . $prefixInterceptor->getName() . '[$methodName] = $prefixInterceptor;');
}
}
src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/SetMethodSuffixInterceptor.php 0000644 00000002232 13645114176 0027466 0 ustar 00 setType(Closure::class);
$interceptor->setDefaultValue(null);
$this->setParameter(new ParameterGenerator('methodName', 'string'));
$this->setParameter($interceptor);
$this->setReturnType('void');
$this->setBody('$this->' . $suffixInterceptor->getName() . '[$methodName] = $suffixInterceptor;');
}
}
src/ProxyManager/ProxyGenerator/AccessInterceptor/PropertyGenerator/MethodPrefixInterceptors.php 0000644 00000001532 13645114176 0027574 0 ustar 00 setDefaultValue([]);
$this->setVisibility(self::VISIBILITY_PRIVATE);
$this->setDocBlock('@var \\Closure[] map of interceptors to be called per-method before execution');
}
}
src/ProxyManager/ProxyGenerator/AccessInterceptor/PropertyGenerator/MethodSuffixInterceptors.php 0000644 00000001530 13645114176 0027601 0 ustar 00 setDefaultValue([]);
$this->setVisibility(self::VISIBILITY_PRIVATE);
$this->setDocBlock('@var \\Closure[] map of interceptors to be called per-method after execution');
}
}
ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/BindProxyProperties.php 0000644 00000006002 13645114176 0030740 0 ustar 00 src getName()),
new ParameterGenerator('prefixInterceptors', 'array', []),
new ParameterGenerator('suffixInterceptors', 'array', []),
],
self::FLAG_PRIVATE,
null,
"@override constructor to setup interceptors\n\n"
. '@param \\' . $originalClass->getName() . " \$localizedObject\n"
. "@param \\Closure[] \$prefixInterceptors method interceptors to be used before method logic\n"
. '@param \\Closure[] $suffixInterceptors method interceptors to be used before method logic'
);
$localizedProperties = [];
$properties = Properties::fromReflectionClass($originalClass);
$nonReferenceableProperties = $properties
->onlyNonReferenceableProperties()
->onlyInstanceProperties();
if (! $nonReferenceableProperties->empty()) {
throw UnsupportedProxiedClassException::nonReferenceableLocalizedReflectionProperties(
$originalClass,
$nonReferenceableProperties
);
}
foreach ($properties->getAccessibleProperties() as $property) {
$propertyName = $property->getName();
$localizedProperties[] = '$this->' . $propertyName . ' = & $localizedObject->' . $propertyName . ';';
}
foreach ($properties->getPrivateProperties() as $property) {
$propertyName = $property->getName();
$localizedProperties[] = "\\Closure::bind(function () use (\$localizedObject) {\n "
. '$this->' . $propertyName . ' = & $localizedObject->' . $propertyName . ";\n"
. '}, $this, ' . var_export($property->getDeclaringClass()->getName(), true)
. ')->__invoke();';
}
$this->setBody(
($localizedProperties ? implode("\n\n", $localizedProperties) . "\n\n" : '')
. '$this->' . $prefixInterceptors->getName() . " = \$prefixInterceptors;\n"
. '$this->' . $suffixInterceptors->getName() . ' = $suffixInterceptors;'
);
}
}
ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/InterceptedMethod.php 0000644 00000002770 13645114176 0030364 0 ustar 00 src getParameters() as $parameter) {
$forwardedParams[] = ($parameter->isVariadic() ? '...' : '') . '$' . $parameter->getName();
}
$method->setBody(InterceptorGenerator::createInterceptedMethodBody(
'$returnValue = parent::'
. $originalMethod->getName() . '(' . implode(', ', $forwardedParams) . ');',
$method,
$prefixInterceptors,
$suffixInterceptors,
$originalMethod
));
return $method;
}
}
src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicClone.php 0000644 00000002146 13645114176 0027032 0 ustar 00 setBody(InterceptorGenerator::createInterceptedMethodBody(
$parent ? '$returnValue = parent::__clone();' : '$returnValue = null;',
$this,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}
src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicGet.php 0000644 00000003074 13645114176 0026512 0 ustar 00 setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}
src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicIsset.php 0000644 00000003117 13645114176 0027060 0 ustar 00 setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}
src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicSet.php 0000644 00000003226 13645114176 0026525 0 ustar 00 setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}
src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicSleep.php 0000644 00000002241 13645114176 0027036 0 ustar 00 setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}
src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicUnset.php 0000644 00000003117 13645114176 0027067 0 ustar 00 setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}
ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/StaticProxyConstructor.php 0000644 00000004133 13645114176 0031507 0 ustar 00 src/ProxyManager setType($originalClass->getName());
$prefix->setDefaultValue([]);
$suffix->setDefaultValue([]);
$prefix->setType('array');
$suffix->setType('array');
$this->setParameter($localizedObject);
$this->setParameter($prefix);
$this->setParameter($suffix);
$this->setReturnType($originalClass->getName());
$this->setDocBlock(
"Constructor to setup interceptors\n\n"
. '@param \\' . $originalClass->getName() . " \$localizedObject\n"
. "@param \\Closure[] \$prefixInterceptors method interceptors to be used before method logic\n"
. "@param \\Closure[] \$suffixInterceptors method interceptors to be used before method logic\n\n"
. '@return self'
);
$this->setBody(
'static $reflection;' . "\n\n"
. '$reflection = $reflection ?? new \ReflectionClass(__CLASS__);' . "\n"
. '$instance = $reflection->newInstanceWithoutConstructor();' . "\n\n"
. '$instance->bindProxyProperties($localizedObject, $prefixInterceptors, $suffixInterceptors);' . "\n\n"
. 'return $instance;'
);
}
}
ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/Util/InterceptorGenerator.php 0000644 00000006033 13645114176 0032033 0 ustar 00 src/ProxyManager {{$prefixInterceptorsName}}[{{$name}}])) {
$returnEarly = false;
$prefixReturnValue = $this->{{$prefixInterceptorsName}}[{{$name}}]->__invoke($this, $this, {{$name}}, {{$paramsString}}, $returnEarly);
if ($returnEarly) {
{{$prefixEarlyReturnExpression}}
}
}
{{$methodBody}}
if (isset($this->{{$suffixInterceptorsName}}[{{$name}}])) {
$returnEarly = false;
$suffixReturnValue = $this->{{$suffixInterceptorsName}}[{{$name}}]->__invoke($this, $this, {{$name}}, {{$paramsString}}, $returnValue, $returnEarly);
if ($returnEarly) {
{{$suffixEarlyReturnExpression}}
}
}
{{$returnExpression}}
PHP;
/**
* @param string $methodBody the body of the previously generated code.
* It MUST assign the return value to a variable
* `$returnValue` instead of directly returning
*/
public static function createInterceptedMethodBody(
string $methodBody,
MethodGenerator $method,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors,
?ReflectionMethod $originalMethod
) : string {
$replacements = [
'{{$name}}' => var_export($method->getName(), true),
'{{$prefixInterceptorsName}}' => $prefixInterceptors->getName(),
'{{$prefixEarlyReturnExpression}}' => ProxiedMethodReturnExpression::generate('$prefixReturnValue', $originalMethod),
'{{$methodBody}}' => $methodBody,
'{{$suffixInterceptorsName}}' => $suffixInterceptors->getName(),
'{{$suffixEarlyReturnExpression}}' => ProxiedMethodReturnExpression::generate('$suffixReturnValue', $originalMethod),
'{{$returnExpression}}' => ProxiedMethodReturnExpression::generate('$returnValue', $originalMethod),
'{{$paramsString}}' => 'array(' . implode(', ', array_map(static function (ParameterGenerator $parameter) : string {
return var_export($parameter->getName(), true) . ' => $' . $parameter->getName();
}, $method->getParameters())) . ')',
];
return str_replace(
array_keys($replacements),
$replacements,
self::TEMPLATE
);
}
}
src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizerGenerator.php 0000644 00000011475 13645114176 0023616 0 ustar 00 setExtendedClass($originalClass->getName());
$classGenerator->setImplementedInterfaces([AccessInterceptorInterface::class]);
$classGenerator->addPropertyFromGenerator($prefixInterceptors = new MethodPrefixInterceptors());
$classGenerator->addPropertyFromGenerator($suffixInterceptors = new MethodSuffixInterceptors());
array_map(
static function (MethodGenerator $generatedMethod) use ($originalClass, $classGenerator) : void {
ClassGeneratorUtils::addMethodIfNotFinal($originalClass, $classGenerator, $generatedMethod);
},
array_merge(
array_map(
$this->buildMethodInterceptor($prefixInterceptors, $suffixInterceptors),
ProxiedMethodsFilter::getProxiedMethods(
$originalClass,
['__get', '__set', '__isset', '__unset', '__clone', '__sleep']
)
),
[
new StaticProxyConstructor($originalClass),
new BindProxyProperties($originalClass, $prefixInterceptors, $suffixInterceptors),
new SetMethodPrefixInterceptor($prefixInterceptors),
new SetMethodSuffixInterceptor($suffixInterceptors),
new MagicGet($originalClass, $prefixInterceptors, $suffixInterceptors),
new MagicSet($originalClass, $prefixInterceptors, $suffixInterceptors),
new MagicIsset($originalClass, $prefixInterceptors, $suffixInterceptors),
new MagicUnset($originalClass, $prefixInterceptors, $suffixInterceptors),
new MagicSleep($originalClass, $prefixInterceptors, $suffixInterceptors),
new MagicClone($originalClass, $prefixInterceptors, $suffixInterceptors),
]
)
);
}
private function buildMethodInterceptor(
MethodPrefixInterceptors $prefixInterceptors,
MethodSuffixInterceptors $suffixInterceptors
) : callable {
return static function (ReflectionMethod $method) use ($prefixInterceptors, $suffixInterceptors) : InterceptedMethod {
return InterceptedMethod::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()),
$prefixInterceptors,
$suffixInterceptors
);
};
}
}
src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/InterceptedMethod.php 0000644 00000003154 13645114176 0027734 0 ustar 00 getParameters() as $parameter) {
$forwardedParams[] = ($parameter->isVariadic() ? '...' : '') . '$' . $parameter->getName();
}
$method->setBody(InterceptorGenerator::createInterceptedMethodBody(
'$returnValue = $this->' . $valueHolderProperty->getName() . '->'
. $originalMethod->getName() . '(' . implode(', ', $forwardedParams) . ');',
$method,
$valueHolderProperty,
$prefixInterceptors,
$suffixInterceptors,
$originalMethod
));
return $method;
}
}
src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicClone.php 0000644 00000002716 13645114176 0026331 0 ustar 00 {{$valueHolder}} = clone $this->{{$valueHolder}};
foreach ($this->{{$prefix}} as $key => $value) {
$this->{{$prefix}}[$key] = clone $value;
}
foreach ($this->{{$suffix}} as $key => $value) {
$this->{{$suffix}}[$key] = clone $value;
}
PHP;
/**
* Constructor
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $valueHolderProperty,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
parent::__construct($originalClass, '__clone');
$valueHolder = $valueHolderProperty->getName();
$prefix = $prefixInterceptors->getName();
$suffix = $suffixInterceptors->getName();
$replacements = [
'{{$valueHolder}}' => $valueHolder,
'{{$prefix}}' => $prefix,
'{{$suffix}}' => $suffix,
];
$this->setBody(str_replace(
array_keys($replacements),
$replacements,
self::TEMPLATE
));
}
}
src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicGet.php 0000644 00000004007 13645114176 0026003 0 ustar 00 getName();
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_GET,
'name',
'value',
$valueHolder,
'returnValue'
);
if (! $publicProperties->isEmpty()) {
$callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n"
. ' $returnValue = & $this->' . $valueHolderName . '->$name;'
. "\n} else {\n " . $callParent . "\n}\n\n";
}
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}
src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicIsset.php 0000644 00000004026 13645114176 0026354 0 ustar 00 getName();
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_ISSET,
'name',
'value',
$valueHolder,
'returnValue'
);
if (! $publicProperties->isEmpty()) {
$callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n"
. ' $returnValue = isset($this->' . $valueHolderName . '->$name);'
. "\n} else {\n " . $callParent . "\n}\n\n";
}
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}
src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicSet.php 0000644 00000004137 13645114176 0026023 0 ustar 00 getName();
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_SET,
'name',
'value',
$valueHolder,
'returnValue'
);
if (! $publicProperties->isEmpty()) {
$callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n"
. ' $returnValue = ($this->' . $valueHolderName . '->$name = $value);'
. "\n} else {\n " . $callParent . "\n}\n\n";
}
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}
src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicUnset.php 0000644 00000004070 13645114176 0026362 0 ustar 00 getName();
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_UNSET,
'name',
'value',
$valueHolder,
'returnValue'
);
if (! $publicProperties->isEmpty()) {
$callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n"
. ' unset($this->' . $valueHolderName . '->$name);'
. "\n} else {\n " . $callParent . "\n}\n\n";
}
$callParent .= '$returnValue = false;';
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}
ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/StaticProxyConstructor.php 0000644 00000005005 13645114176 0031002 0 ustar 00 src setDefaultValue([]);
$suffix->setDefaultValue([]);
$prefix->setType('array');
$suffix->setType('array');
$this->setParameter(new ParameterGenerator('wrappedObject'));
$this->setParameter($prefix);
$this->setParameter($suffix);
$this->setReturnType($originalClass->getName());
$this->setDocBlock(
"Constructor to setup interceptors\n\n"
. '@param \\' . $originalClass->getName() . " \$wrappedObject\n"
. "@param \\Closure[] \$prefixInterceptors method interceptors to be used before method logic\n"
. "@param \\Closure[] \$suffixInterceptors method interceptors to be used before method logic\n\n"
. '@return self'
);
$this->setBody(
'static $reflection;' . "\n\n"
. '$reflection = $reflection ?? new \ReflectionClass(__CLASS__);' . "\n"
. '$instance = $reflection->newInstanceWithoutConstructor();' . "\n\n"
. UnsetPropertiesGenerator::generateSnippet(Properties::fromReflectionClass($originalClass), 'instance')
. '$instance->' . $valueHolder->getName() . " = \$wrappedObject;\n"
. '$instance->' . $prefixInterceptors->getName() . " = \$prefixInterceptors;\n"
. '$instance->' . $suffixInterceptors->getName() . " = \$suffixInterceptors;\n\n"
. 'return $instance;'
);
}
}
ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/Util/InterceptorGenerator.php 0000644 00000006440 13645114176 0031331 0 ustar 00 src/ProxyManager {{$prefixInterceptorsName}}[{{$name}}])) {
$returnEarly = false;
$prefixReturnValue = $this->{{$prefixInterceptorsName}}[{{$name}}]->__invoke($this, $this->{{$valueHolderName}}, {{$name}}, {{$paramsString}}, $returnEarly);
if ($returnEarly) {
{{$returnEarlyPrefixExpression}}
}
}
{{$methodBody}}
if (isset($this->{{$suffixInterceptorsName}}[{{$name}}])) {
$returnEarly = false;
$suffixReturnValue = $this->{{$suffixInterceptorsName}}[{{$name}}]->__invoke($this, $this->{{$valueHolderName}}, {{$name}}, {{$paramsString}}, $returnValue, $returnEarly);
if ($returnEarly) {
{{$returnEarlySuffixExpression}}
}
}
{{$returnExpression}}
PHP;
/**
* @param string $methodBody the body of the previously generated code.
* It MUST assign the return value to a variable
* `$returnValue` instead of directly returning
*/
public static function createInterceptedMethodBody(
string $methodBody,
MethodGenerator $method,
PropertyGenerator $valueHolder,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors,
?ReflectionMethod $originalMethod
) : string {
$name = var_export($method->getName(), true);
$valueHolderName = $valueHolder->getName();
$prefixInterceptorsName = $prefixInterceptors->getName();
$suffixInterceptorsName = $suffixInterceptors->getName();
$params = [];
foreach ($method->getParameters() as $parameter) {
$parameterName = $parameter->getName();
$params[] = var_export($parameterName, true) . ' => $' . $parameter->getName();
}
$paramsString = 'array(' . implode(', ', $params) . ')';
$replacements = [
'{{$prefixInterceptorsName}}' => $prefixInterceptorsName,
'{{$name}}' => $name,
'{{$valueHolderName}}' => $valueHolderName,
'{{$paramsString}}' => $paramsString,
'{{$returnEarlyPrefixExpression}}' => ProxiedMethodReturnExpression::generate('$prefixReturnValue', $originalMethod),
'{{$methodBody}}' => $methodBody,
'{{$suffixInterceptorsName}}' => $suffixInterceptorsName,
'{{$returnEarlySuffixExpression}}' => ProxiedMethodReturnExpression::generate('$suffixReturnValue', $originalMethod),
'{{$returnExpression}}' => ProxiedMethodReturnExpression::generate('$returnValue', $originalMethod),
];
return str_replace(array_keys($replacements), $replacements, self::TEMPLATE);
}
}
src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolderGenerator.php 0000644 00000014462 13645114176 0023111 0 ustar 00 isInterface()) {
$interfaces[] = $originalClass->getName();
} else {
$classGenerator->setExtendedClass($originalClass->getName());
}
$classGenerator->setImplementedInterfaces($interfaces);
$classGenerator->addPropertyFromGenerator($valueHolder = new ValueHolderProperty($originalClass));
$classGenerator->addPropertyFromGenerator($prefixInterceptors = new MethodPrefixInterceptors());
$classGenerator->addPropertyFromGenerator($suffixInterceptors = new MethodSuffixInterceptors());
$classGenerator->addPropertyFromGenerator($publicProperties);
array_map(
static function (MethodGenerator $generatedMethod) use ($originalClass, $classGenerator) : void {
ClassGeneratorUtils::addMethodIfNotFinal($originalClass, $classGenerator, $generatedMethod);
},
array_merge(
array_map(
$this->buildMethodInterceptor($prefixInterceptors, $suffixInterceptors, $valueHolder),
ProxiedMethodsFilter::getProxiedMethods($originalClass)
),
[
Constructor::generateMethod($originalClass, $valueHolder),
new StaticProxyConstructor($originalClass, $valueHolder, $prefixInterceptors, $suffixInterceptors),
new GetWrappedValueHolderValue($valueHolder),
new SetMethodPrefixInterceptor($prefixInterceptors),
new SetMethodSuffixInterceptor($suffixInterceptors),
new MagicGet(
$originalClass,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors,
$publicProperties
),
new MagicSet(
$originalClass,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors,
$publicProperties
),
new MagicIsset(
$originalClass,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors,
$publicProperties
),
new MagicUnset(
$originalClass,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors,
$publicProperties
),
new MagicClone($originalClass, $valueHolder, $prefixInterceptors, $suffixInterceptors),
new MagicSleep($originalClass, $valueHolder),
new MagicWakeup($originalClass),
]
)
);
}
private function buildMethodInterceptor(
MethodPrefixInterceptors $prefixes,
MethodSuffixInterceptors $suffixes,
ValueHolderProperty $valueHolder
) : callable {
return static function (ReflectionMethod $method) use ($prefixes, $suffixes, $valueHolder) : InterceptedMethod {
return InterceptedMethod::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()),
$valueHolder,
$prefixes,
$suffixes
);
};
}
}
src/ProxyManager/ProxyGenerator/Assertion/CanProxyAssertion.php 0000644 00000004117 13645114176 0021065 0 ustar 00 isFinal()) {
throw InvalidProxiedClassException::finalClassNotSupported($originalClass);
}
}
/**
* @throws InvalidProxiedClassException
*/
private static function hasNoAbstractProtectedMethods(ReflectionClass $originalClass) : void
{
$protectedAbstract = array_filter(
$originalClass->getMethods(),
static function (ReflectionMethod $method) : bool {
return $method->isAbstract() && $method->isProtected();
}
);
if ($protectedAbstract) {
throw InvalidProxiedClassException::abstractProtectedMethodsNotSupported($originalClass);
}
}
/**
* @throws InvalidProxiedClassException
*/
private static function isNotInterface(ReflectionClass $originalClass) : void
{
if ($originalClass->isInterface()) {
throw InvalidProxiedClassException::interfaceNotSupported($originalClass);
}
}
}
src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php 0000644 00000002705 13645114176 0025527 0 ustar 00 setParameter(new ParameterGenerator('initializer'));
$this->setDocBlock("Constructor for lazy initialization\n\n@param \\Closure|null \$initializer");
$this->setBody(
'static $reflection;' . "\n\n"
. '$reflection = $reflection ?? new \ReflectionClass(__CLASS__);' . "\n"
. '$instance = $reflection->newInstanceWithoutConstructor();' . "\n\n"
. UnsetPropertiesGenerator::generateSnippet($properties, 'instance')
. '$instance->' . $initializerProperty->getName() . ' = $initializer;' . "\n\n"
. 'return $instance;'
);
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php 0000644 00000013133 13645114176 0025051 0 ustar 00 getName();
$initialization = $initTracker->getName();
$bodyTemplate = <<<'PHP'
if ($this->%s || ! $this->%s) {
return;
}
$this->%s = true;
%s
%s
$result = $this->%s->__invoke($this, $methodName, $parameters, $this->%s, $properties);
$this->%s = false;
return $result;
PHP;
$referenceableProperties = $properties->withoutNonReferenceableProperties();
$this->setBody(sprintf(
$bodyTemplate,
$initialization,
$initializer,
$initialization,
$this->propertiesInitializationCode($referenceableProperties),
$this->propertiesReferenceArrayCode($referenceableProperties),
$initializer,
$initializer,
$initialization
));
}
private function propertiesInitializationCode(Properties $properties) : string
{
$assignments = [];
foreach ($properties->getAccessibleProperties() as $property) {
$assignments[] = '$this->'
. $property->getName()
. ' = ' . $this->getExportedPropertyDefaultValue($property)
. ';';
}
foreach ($properties->getGroupedPrivateProperties() as $className => $privateProperties) {
$cacheKey = 'cache' . str_replace('\\', '_', $className);
$assignments[] = 'static $' . $cacheKey . ";\n\n"
. '$' . $cacheKey . ' ?: $' . $cacheKey . " = \\Closure::bind(static function (\$instance) {\n"
. $this->getPropertyDefaultsAssignments($privateProperties) . "\n"
. '}, null, ' . var_export($className, true) . ");\n\n"
. '$' . $cacheKey . "(\$this);\n\n";
}
return implode("\n", $assignments) . "\n\n";
}
/**
* @param ReflectionProperty[] $properties
*/
private function getPropertyDefaultsAssignments(array $properties) : string
{
return implode(
"\n",
array_map(
function (ReflectionProperty $property) : string {
return ' $instance->' . $property->getName()
. ' = ' . $this->getExportedPropertyDefaultValue($property) . ';';
},
$properties
)
);
}
private function propertiesReferenceArrayCode(Properties $properties) : string
{
$assignments = [];
foreach ($properties->getAccessibleProperties() as $propertyInternalName => $property) {
$assignments[] = ' '
. var_export($propertyInternalName, true) . ' => & $this->' . $property->getName()
. ',';
}
$code = "\$properties = [\n" . implode("\n", $assignments) . "\n];\n\n";
// must use assignments, as direct reference during array definition causes a fatal error (not sure why)
foreach ($properties->getGroupedPrivateProperties() as $className => $classPrivateProperties) {
$cacheKey = 'cacheFetch' . str_replace('\\', '_', $className);
$code .= 'static $' . $cacheKey . ";\n\n"
. '$' . $cacheKey . ' ?: $' . $cacheKey
. " = \\Closure::bind(function (\$instance, array & \$properties) {\n"
. $this->generatePrivatePropertiesAssignmentsCode($classPrivateProperties)
. '}, $this, ' . var_export($className, true) . ");\n\n"
. '$' . $cacheKey . '($this, $properties);';
}
return $code;
}
/**
* @param array $properties indexed by internal name
*/
private function generatePrivatePropertiesAssignmentsCode(array $properties) : string
{
$code = '';
foreach ($properties as $property) {
$key = "\0" . $property->getDeclaringClass()->getName() . "\0" . $property->getName();
$code .= ' $properties[' . var_export($key, true) . '] = '
. '& $instance->' . $property->getName() . ";\n";
}
return $code;
}
private function getExportedPropertyDefaultValue(ReflectionProperty $property) : string
{
$name = $property->getName();
$defaults = $property->getDeclaringClass()->getDefaultProperties();
return var_export($defaults[$name] ?? null, true);
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/GetProxyInitializer.php 0000644 00000001436 13645114176 0025762 0 ustar 00 setReturnType('?\\Closure');
$this->setBody('return $this->' . $initializerProperty->getName() . ';');
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/InitializeProxy.php 0000644 00000001730 13645114176 0025135 0 ustar 00 setReturnType('bool');
$this->setBody(
'return $this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName()
. '(\'initializeProxy\', []);'
);
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/IsProxyInitialized.php 0000644 00000001427 13645114176 0025600 0 ustar 00 setReturnType('bool');
$this->setBody('return ! $this->' . $initializerProperty->getName() . ';');
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicClone.php 0000644 00000001604 13645114176 0023773 0 ustar 00 setBody(
'$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName()
. '(\'__clone\', []);'
. ($originalClass->hasMethod('__clone') ? "\n\nparent::__clone();" : '')
);
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php 0000644 00000010366 13645114176 0023457 0 ustar 00 %s && ! $this->%s && $this->%s('__get', array('name' => $name));
if (isset(self::$%s[$name])) {
return $this->$name;
}
if (isset(self::$%s[$name])) {
if ($this->%s) {
return $this->$name;
}
// check protected property access via compatible class
$callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
$caller = isset($callers[1]) ? $callers[1] : [];
$object = isset($caller['object']) ? $caller['object'] : '';
$expectedType = self::$%s[$name];
if ($object instanceof $expectedType) {
return $this->$name;
}
$class = isset($caller['class']) ? $caller['class'] : '';
if ($class === $expectedType || is_subclass_of($class, $expectedType) || $class === 'ReflectionProperty') {
return $this->$name;
}
} elseif (isset(self::$%s[$name])) {
// check private property access via same class
$callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
$caller = isset($callers[1]) ? $callers[1] : [];
$class = isset($caller['class']) ? $caller['class'] : '';
static $accessorCache = [];
if (isset(self::$%s[$name][$class])) {
$cacheKey = $class . '#' . $name;
$accessor = isset($accessorCache[$cacheKey])
? $accessorCache[$cacheKey]
: $accessorCache[$cacheKey] = \Closure::bind(static function & ($instance) use ($name) {
return $instance->$name;
}, null, $class);
return $accessor($this);
}
if ($this->%s || 'ReflectionProperty' === $class) {
$tmpClass = key(self::$%s[$name]);
$cacheKey = $tmpClass . '#' . $name;
$accessor = isset($accessorCache[$cacheKey])
? $accessorCache[$cacheKey]
: $accessorCache[$cacheKey] = \Closure::bind(static function & ($instance) use ($name) {
return $instance->$name;
}, null, $tmpClass);
return $accessor($this);
}
}
%s
PHP;
/**
* @throws InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $initializerProperty,
MethodGenerator $callInitializer,
PublicPropertiesMap $publicProperties,
ProtectedPropertiesMap $protectedProperties,
PrivatePropertiesMap $privateProperties,
InitializationTracker $initializationTracker
) {
parent::__construct($originalClass, '__get', [new ParameterGenerator('name')]);
$override = $originalClass->hasMethod('__get');
$parentAccess = 'return parent::__get($name);';
if (! $override) {
$parentAccess = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_GET,
'name'
);
}
$this->setBody(sprintf(
$this->callParentTemplate,
$initializerProperty->getName(),
$initializationTracker->getName(),
$callInitializer->getName(),
$publicProperties->getName(),
$protectedProperties->getName(),
$initializationTracker->getName(),
$protectedProperties->getName(),
$privateProperties->getName(),
$privateProperties->getName(),
$initializationTracker->getName(),
$privateProperties->getName(),
$parentAccess
));
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php 0000644 00000007537 13645114176 0024035 0 ustar 00 $name);
}
if (isset(self::$%s[$name])) {
// check protected property access via compatible class
$callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
$caller = isset($callers[1]) ? $callers[1] : [];
$object = isset($caller['object']) ? $caller['object'] : '';
$expectedType = self::$%s[$name];
if ($object instanceof $expectedType) {
return isset($this->$name);
}
$class = isset($caller['class']) ? $caller['class'] : '';
if ($class === $expectedType || is_subclass_of($class, $expectedType)) {
return isset($this->$name);
}
} else {
// check private property access via same class
$callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
$caller = isset($callers[1]) ? $callers[1] : [];
$class = isset($caller['class']) ? $caller['class'] : '';
static $accessorCache = [];
if (isset(self::$%s[$name][$class])) {
$cacheKey = $class . '#' . $name;
$accessor = isset($accessorCache[$cacheKey])
? $accessorCache[$cacheKey]
: $accessorCache[$cacheKey] = \Closure::bind(static function ($instance) use ($name) {
return isset($instance->$name);
}, null, $class);
return $accessor($this);
}
if ('ReflectionProperty' === $class) {
$tmpClass = key(self::$%s[$name]);
$cacheKey = $tmpClass . '#' . $name;
$accessor = isset($accessorCache[$cacheKey])
? $accessorCache[$cacheKey]
: $accessorCache[$cacheKey] = \Closure::bind(static function ($instance) use ($name) {
return isset($instance->$name);
}, null, $tmpClass);
return $accessor($this);
}
}
%s
PHP;
/**
* @throws InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $initializerProperty,
MethodGenerator $callInitializer,
PublicPropertiesMap $publicProperties,
ProtectedPropertiesMap $protectedProperties,
PrivatePropertiesMap $privateProperties
) {
parent::__construct($originalClass, '__isset', [new ParameterGenerator('name')]);
$override = $originalClass->hasMethod('__isset');
$parentAccess = 'return parent::__isset($name);';
if (! $override) {
$parentAccess = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_ISSET,
'name'
);
}
$this->setBody(sprintf(
$this->callParentTemplate,
'$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName()
. '(\'__isset\', array(\'name\' => $name));',
$publicProperties->getName(),
$protectedProperties->getName(),
$protectedProperties->getName(),
$privateProperties->getName(),
$privateProperties->getName(),
$parentAccess
));
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php 0000644 00000010155 13645114176 0023467 0 ustar 00 $name = $value);
}
if (isset(self::$%s[$name])) {
// check protected property access via compatible class
$callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
$caller = isset($callers[1]) ? $callers[1] : [];
$object = isset($caller['object']) ? $caller['object'] : '';
$expectedType = self::$%s[$name];
if ($object instanceof $expectedType) {
return ($this->$name = $value);
}
$class = isset($caller['class']) ? $caller['class'] : '';
if ($class === $expectedType || is_subclass_of($class, $expectedType) || $class === 'ReflectionProperty') {
return ($this->$name = $value);
}
} elseif (isset(self::$%s[$name])) {
// check private property access via same class
$callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
$caller = isset($callers[1]) ? $callers[1] : [];
$class = isset($caller['class']) ? $caller['class'] : '';
static $accessorCache = [];
if (isset(self::$%s[$name][$class])) {
$cacheKey = $class . '#' . $name;
$accessor = isset($accessorCache[$cacheKey])
? $accessorCache[$cacheKey]
: $accessorCache[$cacheKey] = \Closure::bind(static function ($instance, $value) use ($name) {
return ($instance->$name = $value);
}, null, $class);
return $accessor($this, $value);
}
if ('ReflectionProperty' === $class) {
$tmpClass = key(self::$%s[$name]);
$cacheKey = $tmpClass . '#' . $name;
$accessor = isset($accessorCache[$cacheKey])
? $accessorCache[$cacheKey]
: $accessorCache[$cacheKey] = \Closure::bind(static function ($instance, $value) use ($name) {
return ($instance->$name = $value);
}, null, $tmpClass);
return $accessor($this, $value);
}
}
%s
PHP;
/**
* @throws InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $initializerProperty,
MethodGenerator $callInitializer,
PublicPropertiesMap $publicProperties,
ProtectedPropertiesMap $protectedProperties,
PrivatePropertiesMap $privateProperties
) {
parent::__construct(
$originalClass,
'__set',
[new ParameterGenerator('name'), new ParameterGenerator('value')]
);
$override = $originalClass->hasMethod('__set');
$parentAccess = 'return parent::__set($name, $value);';
if (! $override) {
$parentAccess = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_SET,
'name',
'value'
);
}
$this->setBody(sprintf(
$this->callParentTemplate,
'$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName()
. '(\'__set\', array(\'name\' => $name, \'value\' => $value));',
$publicProperties->getName(),
$protectedProperties->getName(),
$protectedProperties->getName(),
$privateProperties->getName(),
$privateProperties->getName(),
$privateProperties->getName(),
$parentAccess
));
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSleep.php 0000644 00000001661 13645114176 0024006 0 ustar 00 setBody(
'$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName()
. '(\'__sleep\', []);' . "\n\n"
. ($originalClass->hasMethod('__sleep') ? 'return parent::__sleep();' : 'return array_keys((array) $this);')
);
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php 0000644 00000007725 13645114176 0024043 0 ustar 00 $name);
return;
}
if (isset(self::$%s[$name])) {
// check protected property access via compatible class
$callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
$caller = isset($callers[1]) ? $callers[1] : [];
$object = isset($caller['object']) ? $caller['object'] : '';
$expectedType = self::$%s[$name];
if ($object instanceof $expectedType) {
unset($this->$name);
return;
}
$class = isset($caller['class']) ? $caller['class'] : '';
if ($class === $expectedType || is_subclass_of($class, $expectedType) || $class === 'ReflectionProperty') {
unset($this->$name);
return;
}
} elseif (isset(self::$%s[$name])) {
// check private property access via same class
$callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
$caller = isset($callers[1]) ? $callers[1] : [];
$class = isset($caller['class']) ? $caller['class'] : '';
static $accessorCache = [];
if (isset(self::$%s[$name][$class])) {
$cacheKey = $class . '#' . $name;
$accessor = isset($accessorCache[$cacheKey])
? $accessorCache[$cacheKey]
: $accessorCache[$cacheKey] = \Closure::bind(static function ($instance) use ($name) {
unset($instance->$name);
}, null, $class);
return $accessor($this);
}
if ('ReflectionProperty' === $class) {
$tmpClass = key(self::$%s[$name]);
$cacheKey = $tmpClass . '#' . $name;
$accessor = isset($accessorCache[$cacheKey])
? $accessorCache[$cacheKey]
: $accessorCache[$cacheKey] = \Closure::bind(static function ($instance) use ($name) {
unset($instance->$name);
}, null, $tmpClass);
return $accessor($this);
}
}
%s
PHP;
/**
* @throws InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $initializerProperty,
MethodGenerator $callInitializer,
PublicPropertiesMap $publicProperties,
ProtectedPropertiesMap $protectedProperties,
PrivatePropertiesMap $privateProperties
) {
parent::__construct($originalClass, '__unset', [new ParameterGenerator('name')]);
$override = $originalClass->hasMethod('__unset');
$parentAccess = 'return parent::__unset($name);';
if (! $override) {
$parentAccess = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_UNSET,
'name'
);
}
$this->setBody(sprintf(
$this->callParentTemplate,
'$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName()
. '(\'__unset\', array(\'name\' => $name));',
$publicProperties->getName(),
$protectedProperties->getName(),
$protectedProperties->getName(),
$privateProperties->getName(),
$privateProperties->getName(),
$privateProperties->getName(),
$parentAccess
));
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/SetProxyInitializer.php 0000644 00000001544 13645114176 0025776 0 ustar 00 setDefaultValue(null)],
self::FLAG_PUBLIC,
'$this->' . $initializerProperty->getName() . ' = $initializer;'
);
$this->setReturnType('void');
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/InitializationTracker.php 0000644 00000001463 13645114176 0026704 0 ustar 00 setVisibility(self::VISIBILITY_PRIVATE);
$this->setDocBlock('@var bool tracks initialization status - true while the object is initializing');
$this->setDefaultValue(false);
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/InitializerProperty.php 0000644 00000001377 13645114176 0026435 0 ustar 00 setVisibility(self::VISIBILITY_PRIVATE);
$this->setDocBlock('@var \\Closure|null initializer responsible for generating the wrapped object');
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMap.php 0000644 00000002614 13645114176 0026525 0 ustar 00 setVisibility(self::VISIBILITY_PRIVATE);
$this->setStatic(true);
$this->setDocBlock(
'@var array[][] visibility and default value of defined properties, indexed by property name and class name'
);
$this->setDefaultValue($this->getMap($properties));
}
/**
* @return array>
*/
private function getMap(Properties $properties) : array
{
$map = [];
foreach ($properties->getPrivateProperties() as $property) {
$map[$property->getName()][$property->getDeclaringClass()->getName()] = true;
}
return $map;
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMap.php 0000644 00000002557 13645114176 0027052 0 ustar 00 setVisibility(self::VISIBILITY_PRIVATE);
$this->setStatic(true);
$this->setDocBlock(
'@var string[][] declaring class name of defined protected properties, indexed by property name'
);
$this->setDefaultValue($this->getMap($properties));
}
/** @return string[] */
private function getMap(Properties $properties) : array
{
$map = [];
foreach ($properties->getProtectedProperties() as $property) {
$map[$property->getName()] = $property->getDeclaringClass()->getName();
}
return $map;
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php 0000644 00000015022 13645114176 0020551 0 ustar 00 } $proxyOptions
*/
public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator, array $proxyOptions = [])
{
CanProxyAssertion::assertClassCanBeProxied($originalClass, false);
$filteredProperties = Properties::fromReflectionClass($originalClass)
->filter($proxyOptions['skippedProperties'] ?? []);
$publicProperties = new PublicPropertiesMap($filteredProperties);
$privateProperties = new PrivatePropertiesMap($filteredProperties);
$protectedProperties = new ProtectedPropertiesMap($filteredProperties);
$classGenerator->setExtendedClass($originalClass->getName());
$classGenerator->setImplementedInterfaces([GhostObjectInterface::class]);
$classGenerator->addPropertyFromGenerator($initializer = new InitializerProperty());
$classGenerator->addPropertyFromGenerator($initializationTracker = new InitializationTracker());
$classGenerator->addPropertyFromGenerator($publicProperties);
$classGenerator->addPropertyFromGenerator($privateProperties);
$classGenerator->addPropertyFromGenerator($protectedProperties);
$init = new CallInitializer($initializer, $initializationTracker, $filteredProperties);
array_map(
static function (MethodGenerator $generatedMethod) use ($originalClass, $classGenerator) : void {
ClassGeneratorUtils::addMethodIfNotFinal($originalClass, $classGenerator, $generatedMethod);
},
array_merge(
$this->getAbstractProxiedMethods($originalClass),
[
$init,
new StaticProxyConstructor($initializer, $filteredProperties),
new MagicGet(
$originalClass,
$initializer,
$init,
$publicProperties,
$protectedProperties,
$privateProperties,
$initializationTracker
),
new MagicSet(
$originalClass,
$initializer,
$init,
$publicProperties,
$protectedProperties,
$privateProperties
),
new MagicIsset(
$originalClass,
$initializer,
$init,
$publicProperties,
$protectedProperties,
$privateProperties
),
new MagicUnset(
$originalClass,
$initializer,
$init,
$publicProperties,
$protectedProperties,
$privateProperties
),
new MagicClone($originalClass, $initializer, $init),
new MagicSleep($originalClass, $initializer, $init),
new SetProxyInitializer($initializer),
new GetProxyInitializer($initializer),
new InitializeProxy($initializer, $init),
new IsProxyInitialized($initializer),
]
)
);
}
/**
* Retrieves all abstract methods to be proxied
*
* @return MethodGenerator[]
*/
private function getAbstractProxiedMethods(ReflectionClass $originalClass) : array
{
return array_map(
static function (ReflectionMethod $method) : ProxyManagerMethodGenerator {
$generated = ProxyManagerMethodGenerator::fromReflectionWithoutBodyAndDocBlock(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName())
);
$generated->setAbstract(false);
return $generated;
},
ProxiedMethodsFilter::getAbstractProxiedMethods($originalClass)
);
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/GetProxyInitializer.php 0000644 00000001444 13645114176 0027107 0 ustar 00 setReturnType('?\\Closure');
$this->setBody('return $this->' . $initializerProperty->getName() . ';');
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/InitializeProxy.php 0000644 00000002052 13645114176 0026261 0 ustar 00 setReturnType('bool');
$initializer = $initializerProperty->getName();
$this->setBody(
'return $this->' . $initializer . ' && $this->' . $initializer
. '->__invoke($this->' . $valueHolderProperty->getName()
. ', $this, \'initializeProxy\', array(), $this->' . $initializer . ');'
);
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/IsProxyInitialized.php 0000644 00000001444 13645114176 0026725 0 ustar 00 setReturnType('bool');
$this->setBody('return null !== $this->' . $valueHolderProperty->getName() . ';');
}
}
ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/LazyLoadingMethodInterceptor.php 0000644 00000004176 13645114176 0030645 0 ustar 00 src getName();
$valueHolderName = $valueHolderProperty->getName();
$parameters = $originalMethod->getParameters();
$methodName = $originalMethod->getName();
$initializerParams = [];
$forwardedParams = [];
foreach ($parameters as $parameter) {
$parameterName = $parameter->getName();
$variadicPrefix = $parameter->isVariadic() ? '...' : '';
$initializerParams[] = var_export($parameterName, true) . ' => $' . $parameterName;
$forwardedParams[] = $variadicPrefix . '$' . $parameterName;
}
$method->setBody(
'$this->' . $initializerName
. ' && $this->' . $initializerName
. '->__invoke($this->' . $valueHolderName . ', $this, ' . var_export($methodName, true)
. ', array(' . implode(', ', $initializerParams) . '), $this->' . $initializerName . ");\n\n"
. ProxiedMethodReturnExpression::generate(
'$this->' . $valueHolderName . '->' . $methodName . '(' . implode(', ', $forwardedParams) . ')',
$originalMethod
)
);
return $method;
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicClone.php 0000644 00000002033 13645114176 0025116 0 ustar 00 getName();
$valueHolder = $valueHolderProperty->getName();
$this->setBody(
'$this->' . $initializer . ' && $this->' . $initializer
. '->__invoke($this->' . $valueHolder
. ', $this, \'__clone\', array(), $this->' . $initializer . ');' . "\n\n"
. '$this->' . $valueHolder . ' = clone $this->' . $valueHolder . ';'
);
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicGet.php 0000644 00000004345 13645114176 0024605 0 ustar 00 hasMethod('__get');
$initializer = $initializerProperty->getName();
$valueHolder = $valueHolderProperty->getName();
$callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n"
. ' return $this->' . $valueHolder . '->$name;'
. "\n}\n\n";
if ($hasParent) {
$this->setInitializerBody(
$initializer,
$valueHolder,
$callParent . 'return $this->' . $valueHolder . '->__get($name);'
);
return;
}
$this->setInitializerBody(
$initializer,
$valueHolder,
$callParent . PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_GET,
'name',
null,
$valueHolderProperty
)
);
}
private function setInitializerBody(string $initializer, string $valueHolder, string $callParent) : void
{
$this->setBody(
'$this->' . $initializer . ' && $this->' . $initializer
. '->__invoke($this->' . $valueHolder . ', $this, \'__get\', [\'name\' => $name], $this->'
. $initializer . ');'
. "\n\n" . $callParent
);
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicIsset.php 0000644 00000003454 13645114176 0025155 0 ustar 00 getName();
$valueHolder = $valueHolderProperty->getName();
$callParent = '';
if (! $publicProperties->isEmpty()) {
$callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n"
. ' return isset($this->' . $valueHolder . '->$name);'
. "\n}\n\n";
}
$callParent .= PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_ISSET,
'name',
null,
$valueHolderProperty
);
$this->setBody(
'$this->' . $initializer . ' && $this->' . $initializer
. '->__invoke($this->' . $valueHolder . ', $this, \'__isset\', array(\'name\' => $name), $this->'
. $initializer . ');' . "\n\n" . $callParent
);
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicSet.php 0000644 00000004111 13645114176 0024610 0 ustar 00 hasMethod('__set');
$initializer = $initializerProperty->getName();
$valueHolder = $valueHolderProperty->getName();
$callParent = '';
if (! $publicProperties->isEmpty()) {
$callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n"
. ' return ($this->' . $valueHolder . '->$name = $value);'
. "\n}\n\n";
}
$callParent .= $hasParent
? 'return $this->' . $valueHolder . '->__set($name, $value);'
: PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_SET,
'name',
'value',
$valueHolderProperty
);
$this->setBody(
'$this->' . $initializer . ' && $this->' . $initializer
. '->__invoke($this->' . $valueHolder . ', $this, '
. '\'__set\', array(\'name\' => $name, \'value\' => $value), $this->' . $initializer . ');'
. "\n\n" . $callParent
);
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicSleep.php 0000644 00000002051 13645114176 0025126 0 ustar 00 getName();
$valueHolder = $valueHolderProperty->getName();
$this->setBody(
'$this->' . $initializer . ' && $this->' . $initializer
. '->__invoke($this->' . $valueHolder . ', $this, \'__sleep\', array(), $this->'
. $initializer . ');' . "\n\n"
. 'return array(' . var_export($valueHolder, true) . ');'
);
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicUnset.php 0000644 00000003747 13645114176 0025171 0 ustar 00 hasMethod('__unset');
$initializer = $initializerProperty->getName();
$valueHolder = $valueHolderProperty->getName();
$callParent = '';
if (! $publicProperties->isEmpty()) {
$callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n"
. ' unset($this->' . $valueHolder . '->$name);' . "\n\n return;"
. "\n}\n\n";
}
$callParent .= $hasParent
? 'return $this->' . $valueHolder . '->__unset($name);'
: PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_UNSET,
'name',
null,
$valueHolderProperty
);
$this->setBody(
'$this->' . $initializer . ' && $this->' . $initializer
. '->__invoke($this->' . $valueHolder . ', $this, \'__unset\', array(\'name\' => $name), $this->'
. $initializer . ');' . "\n\n" . $callParent
);
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/SetProxyInitializer.php 0000644 00000002115 13645114176 0027117 0 ustar 00 setType(Closure::class);
$initializerParameter->setDefaultValue(null);
$this->setParameter($initializerParameter);
$this->setBody('$this->' . $initializerProperty->getName() . ' = $initializer;');
$this->setReturnType('void');
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/PropertyGenerator/InitializerProperty.php 0000644 00000001405 13645114176 0027553 0 ustar 00 setVisibility(self::VISIBILITY_PRIVATE);
$this->setDocBlock('@var \\Closure|null initializer responsible for generating the wrapped object');
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/PropertyGenerator/ValueHolderProperty.php 0000644 00000001757 13645114176 0027514 0 ustar 00 setWordWrap(false);
$docBlock->setLongDescription('@var \\' . $type->getName() . '|null wrapped object, if the proxy is initialized');
$this->setDocBlock($docBlock);
$this->setVisibility(self::VISIBILITY_PRIVATE);
}
}
src/ProxyManager/ProxyGenerator/LazyLoadingValueHolderGenerator.php 0000644 00000012754 13645114176 0021710 0 ustar 00 isInterface()) {
$interfaces[] = $originalClass->getName();
} else {
$classGenerator->setExtendedClass($originalClass->getName());
}
$classGenerator->setImplementedInterfaces($interfaces);
$classGenerator->addPropertyFromGenerator($valueHolder = new ValueHolderProperty($originalClass));
$classGenerator->addPropertyFromGenerator($initializer = new InitializerProperty());
$classGenerator->addPropertyFromGenerator($publicProperties);
array_map(
static function (MethodGenerator $generatedMethod) use ($originalClass, $classGenerator) : void {
ClassGeneratorUtils::addMethodIfNotFinal($originalClass, $classGenerator, $generatedMethod);
},
array_merge(
array_map(
$this->buildLazyLoadingMethodInterceptor($initializer, $valueHolder),
ProxiedMethodsFilter::getProxiedMethods($originalClass)
),
[
new StaticProxyConstructor($initializer, Properties::fromReflectionClass($originalClass)),
Constructor::generateMethod($originalClass, $valueHolder),
new MagicGet($originalClass, $initializer, $valueHolder, $publicProperties),
new MagicSet($originalClass, $initializer, $valueHolder, $publicProperties),
new MagicIsset($originalClass, $initializer, $valueHolder, $publicProperties),
new MagicUnset($originalClass, $initializer, $valueHolder, $publicProperties),
new MagicClone($originalClass, $initializer, $valueHolder),
new MagicSleep($originalClass, $initializer, $valueHolder),
new MagicWakeup($originalClass),
new SetProxyInitializer($initializer),
new GetProxyInitializer($initializer),
new InitializeProxy($initializer, $valueHolder),
new IsProxyInitialized($valueHolder),
new GetWrappedValueHolderValue($valueHolder),
]
)
);
}
private function buildLazyLoadingMethodInterceptor(
InitializerProperty $initializer,
ValueHolderProperty $valueHolder
) : callable {
return static function (ReflectionMethod $method) use ($initializer, $valueHolder) : LazyLoadingMethodInterceptor {
return LazyLoadingMethodInterceptor::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()),
$initializer,
$valueHolder
);
};
}
}
src/ProxyManager/ProxyGenerator/NullObject/MethodGenerator/NullObjectMethodInterceptor.php 0000644 00000001505 13645114176 0026232 0 ustar 00 returnsReference()) {
$reference = IdentifierSuffixer::getIdentifier('ref');
$method->setBody("\$reference = null;\nreturn \$" . $reference . ';');
}
return $method;
}
}
src/ProxyManager/ProxyGenerator/NullObject/MethodGenerator/StaticProxyConstructor.php 0000644 00000003235 13645114176 0025352 0 ustar 00 ' . $publicProperty->getName() . ' = null;';
},
Properties::fromReflectionClass($originalClass)
->onlyNullableProperties()
->getPublicProperties()
);
$this->setReturnType($originalClass->getName());
$this->setDocBlock('Constructor for null object initialization');
$this->setBody(
'static $reflection;' . "\n\n"
. '$reflection = $reflection ?? new \ReflectionClass(__CLASS__);' . "\n"
. '$instance = $reflection->newInstanceWithoutConstructor();' . "\n\n"
. ($nullableProperties ? implode("\n", $nullableProperties) . "\n\n" : '')
. 'return $instance;'
);
}
}
src/ProxyManager/ProxyGenerator/NullObjectGenerator.php 0000644 00000003747 13645114176 0017403 0 ustar 00 isInterface()) {
$interfaces[] = $originalClass->getName();
} else {
$classGenerator->setExtendedClass($originalClass->getName());
}
$classGenerator->setImplementedInterfaces($interfaces);
foreach (ProxiedMethodsFilter::getProxiedMethods($originalClass, []) as $method) {
$classGenerator->addMethodFromGenerator(
NullObjectMethodInterceptor::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName())
)
);
}
ClassGeneratorUtils::addMethodIfNotFinal(
$originalClass,
$classGenerator,
new StaticProxyConstructor($originalClass)
);
}
}
src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesMap.php 0000644 00000002262 13645114176 0023106 0 ustar 00 */
private array $publicProperties = [];
/**
* @throws InvalidArgumentException
*/
public function __construct(Properties $properties)
{
parent::__construct(IdentifierSuffixer::getIdentifier('publicProperties'));
foreach ($properties->getPublicProperties() as $publicProperty) {
$this->publicProperties[$publicProperty->getName()] = true;
}
$this->setDefaultValue($this->publicProperties);
$this->setVisibility(self::VISIBILITY_PRIVATE);
$this->setStatic(true);
$this->setDocBlock('@var bool[] map of public properties of the parent class');
}
public function isEmpty() : bool
{
return ! $this->publicProperties;
}
}
src/ProxyManager/ProxyGenerator/ProxyGeneratorInterface.php 0000644 00000001052 13645114176 0020267 0 ustar 00 setDocBlock('@param string $name');
$this->setBody(
'$return = $this->' . $adapterProperty->getName() . '->call(' . var_export($originalClass->getName(), true)
. ', \'__get\', array($name));' . "\n\n" . 'return $return;'
);
}
}
src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicIsset.php 0000644 00000002013 13645114176 0023155 0 ustar 00 setDocBlock('@param string $name');
$this->setBody(
'$return = $this->' . $adapterProperty->getName() . '->call(' . var_export($originalClass->getName(), true)
. ', \'__isset\', array($name));' . "\n\n"
. 'return $return;'
);
}
}
src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicSet.php 0000644 00000002152 13645114176 0022625 0 ustar 00 setDocBlock('@param string \$name\n@param mixed \$value');
$this->setBody(
'$return = $this->' . $adapterProperty->getName() . '->call(' . var_export($originalClass->getName(), true)
. ', \'__set\', array($name, $value));' . "\n\n"
. 'return $return;'
);
}
}
src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicUnset.php 0000644 00000002013 13645114176 0023164 0 ustar 00 setDocBlock('@param string $name');
$this->setBody(
'$return = $this->' . $adapterProperty->getName() . '->call(' . var_export($originalClass->getName(), true)
. ', \'__unset\', array($name));' . "\n\n"
. 'return $return;'
);
}
}
src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/RemoteObjectMethod.php 0000644 00000004762 13645114176 0024665 0 ustar 00 ' . $adapterProperty->getName()
. '->call(' . var_export($originalClass->getName(), true)
. ', ' . var_export($originalMethod->getName(), true) . ', $args);' . "\n\n"
. ProxiedMethodReturnExpression::generate('$return', $originalMethod);
$defaultValues = self::getDefaultValuesForMethod($originalMethod);
$declaredParameterCount = count($originalMethod->getParameters());
$method->setBody(
strtr(
self::TEMPLATE,
[
'#PROXIED_RETURN#' => $proxiedReturn,
'#DEFAULT_VALUES#' => var_export($defaultValues, true),
'#PARAMETER_COUNT#' => var_export($declaredParameterCount, true),
]
)
);
return $method;
}
/**
* @return array
*/
private static function getDefaultValuesForMethod(MethodReflection $originalMethod) : array
{
$defaultValues = [];
foreach ($originalMethod->getParameters() as $parameter) {
if ($parameter->isOptional() && $parameter->isDefaultValueAvailable()) {
/** @psalm-var int|float|bool|array|string|null */
$defaultValues[] = $parameter->getDefaultValue();
continue;
}
if ($parameter->isVariadic()) {
continue;
}
$defaultValues[] = null;
}
return $defaultValues;
}
}
src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/StaticProxyConstructor.php 0000644 00000003345 13645114176 0025675 0 ustar 00 getName();
parent::__construct(
'staticProxyConstructor',
[new ParameterGenerator($adapterName, AdapterInterface::class)],
MethodGenerator::FLAG_PUBLIC | MethodGenerator::FLAG_STATIC,
null,
'Constructor for remote object control\n\n'
. '@param \\ProxyManager\\Factory\\RemoteObject\\AdapterInterface \$adapter'
);
$body = 'static $reflection;' . "\n\n"
. '$reflection = $reflection ?? new \ReflectionClass(__CLASS__);' . "\n"
. '$instance = $reflection->newInstanceWithoutConstructor();' . "\n\n"
. '$instance->' . $adapterName . ' = $' . $adapterName . ";\n\n"
. UnsetPropertiesGenerator::generateSnippet(Properties::fromReflectionClass($originalClass), 'instance');
$this->setBody($body . "\n\nreturn \$instance;");
}
}
src/ProxyManager/ProxyGenerator/RemoteObject/PropertyGenerator/AdapterProperty.php 0000644 00000001427 13645114176 0024666 0 ustar 00 setVisibility(self::VISIBILITY_PRIVATE);
$this->setDocBlock('@var \\' . AdapterInterface::class . ' Remote web service adapter');
}
}
src/ProxyManager/ProxyGenerator/RemoteObjectGenerator.php 0000644 00000006520 13645114176 0017714 0 ustar 00 isInterface()) {
$interfaces[] = $originalClass->getName();
} else {
$classGenerator->setExtendedClass($originalClass->getName());
}
$classGenerator->setImplementedInterfaces($interfaces);
$classGenerator->addPropertyFromGenerator($adapter = new AdapterProperty());
array_map(
static function (MethodGenerator $generatedMethod) use ($originalClass, $classGenerator) : void {
ClassGeneratorUtils::addMethodIfNotFinal($originalClass, $classGenerator, $generatedMethod);
},
array_merge(
array_map(
static function (ReflectionMethod $method) use ($adapter, $originalClass) : RemoteObjectMethod {
return RemoteObjectMethod::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()),
$adapter,
$originalClass
);
},
ProxiedMethodsFilter::getProxiedMethods(
$originalClass,
['__get', '__set', '__isset', '__unset']
)
),
[
new StaticProxyConstructor($originalClass, $adapter),
new MagicGet($originalClass, $adapter),
new MagicSet($originalClass, $adapter),
new MagicIsset($originalClass, $adapter),
new MagicUnset($originalClass, $adapter),
]
)
);
}
}
src/ProxyManager/ProxyGenerator/Util/GetMethodIfExists.php 0000644 00000000740 13645114176 0017735 0 ustar 00 hasMethod($method) ? $class->getMethod($method) : null;
}
}
src/ProxyManager/ProxyGenerator/Util/Properties.php 0000644 00000015061 13645114176 0016534 0 ustar 00 properties = $properties;
}
public static function fromReflectionClass(ReflectionClass $reflection) : self
{
$class = $reflection;
$parentClasses = [];
do {
$parentClasses[] = $class;
$class = $class->getParentClass();
} while ($class);
return new self(array_merge(
...array_map(static function (ReflectionClass $class) : array {
return array_values(array_filter(
$class->getProperties(),
static function (ReflectionProperty $property) use ($class) : bool {
return $class->getName() === $property->getDeclaringClass()->getName()
&& ! $property->isStatic();
}
));
}, $parentClasses)
));
}
/**
* @param array $excludedProperties
*/
public function filter(array $excludedProperties) : self
{
$properties = $this->getInstanceProperties();
foreach ($excludedProperties as $propertyName) {
unset($properties[$propertyName]);
}
return new self($properties);
}
public function onlyNonReferenceableProperties() : self
{
return new self(array_filter($this->properties, static function (ReflectionProperty $property) : bool {
if (! $property->hasType()) {
return false;
}
return ! array_key_exists(
$property->getName(),
// https://bugs.php.net/bug.php?id=77673
array_flip(array_keys($property->getDeclaringClass()->getDefaultProperties()))
);
}));
}
/** @deprecated Since PHP 7.4.1, all properties can be unset, regardless if typed or not */
public function onlyPropertiesThatCanBeUnset() : self
{
return $this;
}
/**
* Properties that cannot be referenced are non-nullable typed properties that aren't initialised
*/
public function withoutNonReferenceableProperties() : self
{
return new self(array_filter($this->properties, static function (ReflectionProperty $property) : bool {
if (! $property->hasType()) {
return true;
}
/** @var ReflectionType $type */
$type = $property->getType();
if ($type->allowsNull()) {
return true;
}
return array_key_exists(
$property->getName(),
// https://bugs.php.net/bug.php?id=77673
array_flip(array_keys($property->getDeclaringClass()->getDefaultProperties()))
);
}));
}
public function onlyNullableProperties() : self
{
return new self(array_filter(
$this->properties,
static function (ReflectionProperty $property) : bool {
/** @var ReflectionType|null $type */
$type = $property->getType();
return $type === null || $type->allowsNull();
}
));
}
public function onlyInstanceProperties() : self
{
return new self(array_values(array_merge($this->getAccessibleProperties(), $this->getPrivateProperties())));
}
public function empty() : bool
{
return $this->properties === [];
}
/**
* @return array indexed by the property internal visibility-aware name
*/
public function getPublicProperties() : array
{
$publicProperties = [];
foreach ($this->properties as $property) {
if (! $property->isPublic()) {
continue;
}
$publicProperties[$property->getName()] = $property;
}
return $publicProperties;
}
/**
* @return array indexed by the property internal visibility-aware name (\0*\0propertyName)
*/
public function getProtectedProperties() : array
{
$protectedProperties = [];
foreach ($this->properties as $property) {
if (! $property->isProtected()) {
continue;
}
$protectedProperties["\0*\0" . $property->getName()] = $property;
}
return $protectedProperties;
}
/**
* @return array indexed by the property internal visibility-aware name (\0ClassName\0propertyName)
*/
public function getPrivateProperties() : array
{
$privateProperties = [];
foreach ($this->properties as $property) {
if (! $property->isPrivate()) {
continue;
}
$declaringClass = $property->getDeclaringClass()->getName();
$privateProperties["\0" . $declaringClass . "\0" . $property->getName()] = $property;
}
return $privateProperties;
}
/**
* @return array indexed by the property internal visibility-aware name (\0*\0propertyName)
*/
public function getAccessibleProperties() : array
{
return array_merge($this->getPublicProperties(), $this->getProtectedProperties());
}
/**
* @return array> indexed by class name and property name
*/
public function getGroupedPrivateProperties() : array
{
$propertiesMap = [];
foreach ($this->getPrivateProperties() as $property) {
$propertiesMap[$property->getDeclaringClass()->getName()][$property->getName()] = $property;
}
return $propertiesMap;
}
/**
* @return array indexed by the property internal visibility-aware name (\0*\0propertyName)
*/
public function getInstanceProperties() : array
{
return array_merge($this->getAccessibleProperties(), $this->getPrivateProperties());
}
}
src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php 0000644 00000004625 13645114176 0020510 0 ustar 00 */
private static $defaultExcluded = [
'__get',
'__set',
'__isset',
'__unset',
'__clone',
'__sleep',
'__wakeup',
];
/**
* @param ReflectionClass $class reflection class from which methods should be extracted
* @param array $excluded methods to be ignored
*
* @return ReflectionMethod[]
*/
public static function getProxiedMethods(ReflectionClass $class, ?array $excluded = null) : array
{
return self::doFilter($class, $excluded ?? self::$defaultExcluded);
}
/**
* @param ReflectionClass $class reflection class from which methods should be extracted
* @param array $excluded methods to be ignored
*
* @return ReflectionMethod[]
*/
public static function getAbstractProxiedMethods(ReflectionClass $class, ?array $excluded = null) : array
{
return self::doFilter($class, $excluded ?? self::$defaultExcluded, true);
}
/**
* @param array $excluded
*
* @return array
*/
private static function doFilter(ReflectionClass $class, array $excluded, bool $requireAbstract = false) : array
{
$ignored = array_flip(array_map('strtolower', $excluded));
return array_values(array_filter(
$class->getMethods(ReflectionMethod::IS_PUBLIC),
static function (ReflectionMethod $method) use ($ignored, $requireAbstract) : bool {
return (! $requireAbstract || $method->isAbstract()) && ! (
array_key_exists(strtolower($method->getName()), $ignored)
|| self::methodCannotBeProxied($method)
);
}
));
}
/**
* Checks whether the method cannot be proxied
*/
private static function methodCannotBeProxied(ReflectionMethod $method) : bool
{
return $method->isConstructor() || $method->isFinal() || $method->isStatic();
}
}
src/ProxyManager/ProxyGenerator/Util/PublicScopeSimulator.php 0000644 00000014523 13645114176 0020512 0 ustar 00 ' . $valueHolder->getName();
}
return '$realInstanceReflection = new \\ReflectionClass(get_parent_class($this));' . "\n\n"
. 'if (! $realInstanceReflection->hasProperty($' . $nameParameter . ')) {' . "\n"
. ' $targetObject = ' . $target . ';' . "\n\n"
. self::getUndefinedPropertyNotice($operationType, $nameParameter)
. ' ' . self::getOperation($operationType, $nameParameter, $valueParameter) . "\n"
. " return;\n"
. '}' . "\n\n"
. '$targetObject = ' . self::getTargetObject($valueHolder) . ";\n"
. '$accessor = function ' . $byRef . '() use ($targetObject, $name' . $value . ') {' . "\n"
. ' ' . self::getOperation($operationType, $nameParameter, $valueParameter) . "\n"
. "};\n"
. self::getScopeReBind()
. (
$returnPropertyName
? '$' . $returnPropertyName . ' = ' . $byRef . '$accessor();'
: '$returnValue = ' . $byRef . '$accessor();' . "\n\n" . 'return $returnValue;'
);
}
/**
* This will generate code that triggers a notice if access is attempted on a non-existing property
*/
private static function getUndefinedPropertyNotice(string $operationType, string $nameParameter) : string
{
if ($operationType !== self::OPERATION_GET) {
return '';
}
return ' $backtrace = debug_backtrace(false);' . "\n"
. ' trigger_error(' . "\n"
. ' sprintf(' . "\n"
. ' \'Undefined property: %s::$%s in %s on line %s\',' . "\n"
. ' get_parent_class($this),' . "\n"
. ' $' . $nameParameter . ',' . "\n"
. ' $backtrace[0][\'file\'],' . "\n"
. ' $backtrace[0][\'line\']' . "\n"
. ' ),' . "\n"
. ' \E_USER_NOTICE' . "\n"
. ' );' . "\n";
}
/**
* Defines whether the given operation produces a reference.
*
* Note: if the object is a wrapper, the wrapped instance is accessed directly. If the object
* is a ghost or the proxy has no wrapper, then an instance of the parent class is created via
* on-the-fly unserialization
*/
private static function getByRefReturnValue(string $operationType) : string
{
return $operationType === self::OPERATION_GET || $operationType === self::OPERATION_SET ? '& ' : '';
}
/**
* Retrieves the logic to fetch the object on which access should be attempted
*/
private static function getTargetObject(?PropertyGenerator $valueHolder = null) : string
{
if ($valueHolder) {
return '$this->' . $valueHolder->getName();
}
return '$realInstanceReflection->newInstanceWithoutConstructor()';
}
/**
* @throws InvalidArgumentException
*/
private static function getOperation(string $operationType, string $nameParameter, ?string $valueParameter) : string
{
switch ($operationType) {
case self::OPERATION_GET:
return 'return $targetObject->$' . $nameParameter . ';';
case self::OPERATION_SET:
if ($valueParameter === null) {
throw new InvalidArgumentException('Parameter $valueParameter not provided');
}
return 'return $targetObject->$' . $nameParameter . ' = $' . $valueParameter . ';';
case self::OPERATION_ISSET:
return 'return isset($targetObject->$' . $nameParameter . ');';
case self::OPERATION_UNSET:
return 'unset($targetObject->$' . $nameParameter . ');';
}
throw new InvalidArgumentException(sprintf('Invalid operation "%s" provided', $operationType));
}
/**
* Generates code to bind operations to the parent scope
*/
private static function getScopeReBind() : string
{
return '$backtrace = debug_backtrace(true);' . "\n"
. '$scopeObject = isset($backtrace[1][\'object\'])'
. ' ? $backtrace[1][\'object\'] : new \ProxyManager\Stub\EmptyClassStub();' . "\n"
. '$accessor = $accessor->bindTo($scopeObject, get_class($scopeObject));' . "\n";
}
}
src/ProxyManager/ProxyGenerator/Util/UnsetPropertiesGenerator.php 0000644 00000005772 13645114176 0021432 0 ustar 00 __invoke($%s);
PHP;
public static function generateSnippet(Properties $properties, string $instanceName) : string
{
return self::generateUnsetAccessiblePropertiesCode($properties, $instanceName)
. self::generateUnsetPrivatePropertiesCode($properties, $instanceName);
}
private static function generateUnsetAccessiblePropertiesCode(Properties $properties, string $instanceName) : string
{
$accessibleProperties = $properties->getAccessibleProperties();
if (! $accessibleProperties) {
return '';
}
return self::generateUnsetStatement($accessibleProperties, $instanceName) . "\n\n";
}
private static function generateUnsetPrivatePropertiesCode(Properties $properties, string $instanceName) : string
{
$groups = $properties->getGroupedPrivateProperties();
if (! $groups) {
return '';
}
$unsetClosureCalls = [];
foreach ($groups as $privateProperties) {
/** @var ReflectionProperty $firstProperty */
$firstProperty = reset($privateProperties);
$unsetClosureCalls[] = self::generateUnsetClassPrivatePropertiesBlock(
$firstProperty->getDeclaringClass(),
$privateProperties,
$instanceName
);
}
return implode("\n\n", $unsetClosureCalls) . "\n\n";
}
/** @param array $properties */
private static function generateUnsetClassPrivatePropertiesBlock(
ReflectionClass $declaringClass,
array $properties,
string $instanceName
) : string {
$declaringClassName = $declaringClass->getName();
return sprintf(
self::CLOSURE_TEMPLATE,
$declaringClassName,
self::generateUnsetStatement($properties, 'instance'),
$instanceName,
var_export($declaringClassName, true),
$instanceName
);
}
/** @param array $properties */
private static function generateUnsetStatement(array $properties, string $instanceName) : string
{
return 'unset('
. implode(
', ',
array_map(
static function (ReflectionProperty $property) use ($instanceName) : string {
return '$' . $instanceName . '->' . $property->getName();
},
$properties
)
)
. ');';
}
}
src/ProxyManager/ProxyGenerator/ValueHolder/MethodGenerator/Constructor.php 0000644 00000006236 13645114176 0023315 0 ustar 00 setBody(
'static $reflection;' . "\n\n"
. 'if (! $this->' . $valueHolder->getName() . ') {' . "\n"
. ' $reflection = $reflection ?: new \ReflectionClass('
. var_export($originalClass->getName(), true)
. ");\n"
. ' $this->' . $valueHolder->getName() . ' = $reflection->newInstanceWithoutConstructor();' . "\n"
. UnsetPropertiesGenerator::generateSnippet(Properties::fromReflectionClass($originalClass), 'this')
. '}'
. ($originalConstructor ? self::generateOriginalConstructorCall($originalConstructor, $valueHolder) : '')
);
return $constructor;
}
private static function generateOriginalConstructorCall(
MethodReflection $originalConstructor,
PropertyGenerator $valueHolder
) : string {
return "\n\n"
. '$this->' . $valueHolder->getName() . '->' . $originalConstructor->getName() . '('
. implode(
', ',
array_map(
static function (ParameterReflection $parameter) : string {
return ($parameter->isVariadic() ? '...' : '') . '$' . $parameter->getName();
},
$originalConstructor->getParameters()
)
)
. ');';
}
private static function getConstructor(ReflectionClass $class) : ?MethodReflection
{
$constructors = array_map(
static function (ReflectionMethod $method) : MethodReflection {
return new MethodReflection(
$method->getDeclaringClass()->getName(),
$method->getName()
);
},
array_filter(
$class->getMethods(),
static function (ReflectionMethod $method) : bool {
return $method->isConstructor();
}
)
);
return reset($constructors) ?: null;
}
}
src/ProxyManager/ProxyGenerator/ValueHolder/MethodGenerator/GetWrappedValueHolderValue.php 0000644 00000001453 13645114176 0026156 0 ustar 00 setBody('return $this->' . $valueHolderProperty->getName() . ';');
$this->setReturnType('?object');
}
}
src/ProxyManager/ProxyGenerator/ValueHolder/MethodGenerator/MagicSleep.php 0000644 00000001215 13645114176 0022771 0 ustar 00 setBody('return array(' . var_export($valueHolderProperty->getName(), true) . ');');
}
}
src/ProxyManager/Signature/ClassSignatureGenerator.php 0000644 00000002135 13645114176 0017230 0 ustar 00 signatureGenerator = $signatureGenerator;
}
/**
* {@inheritDoc}
*
* @throws InvalidArgumentException
*/
public function addSignature(ClassGenerator $classGenerator, array $parameters) : ClassGenerator
{
$classGenerator->addPropertyFromGenerator(new PropertyGenerator(
'signature' . $this->signatureGenerator->generateSignatureKey($parameters),
$this->signatureGenerator->generateSignature($parameters),
PropertyGenerator::FLAG_STATIC | PropertyGenerator::FLAG_PRIVATE
));
return $classGenerator;
}
}
src/ProxyManager/Signature/ClassSignatureGeneratorInterface.php 0000644 00000000673 13645114176 0021056 0 ustar 00 $parameters
*/
public function addSignature(ClassGenerator $classGenerator, array $parameters) : ClassGenerator;
}
src/ProxyManager/Signature/Exception/ExceptionInterface.php 0000644 00000000273 13645114176 0020150 0 ustar 00 getName(),
$expected,
count($parameters)
));
}
}
src/ProxyManager/Signature/Exception/MissingSignatureException.php 0000644 00000001300 13645114176 0021533 0 ustar 00 getName(),
$expected,
count($parameters)
));
}
}
src/ProxyManager/Signature/SignatureChecker.php 0000644 00000003015 13645114176 0015656 0 ustar 00 signatureGenerator = $signatureGenerator;
}
/**
* {@inheritDoc}
*/
public function checkSignature(ReflectionClass $class, array $parameters) : void
{
$propertyName = 'signature' . $this->signatureGenerator->generateSignatureKey($parameters);
$signature = $this->signatureGenerator->generateSignature($parameters);
$defaultProperties = $class->getDefaultProperties();
if (! (array_key_exists($propertyName, $defaultProperties) && is_string($defaultProperties[$propertyName]))) {
throw MissingSignatureException::fromMissingSignature($class, $parameters, $signature);
}
if ($defaultProperties[$propertyName] !== $signature) {
throw InvalidSignatureException::fromInvalidSignature(
$class,
$parameters,
$defaultProperties[$propertyName],
$signature
);
}
}
}
src/ProxyManager/Signature/SignatureCheckerInterface.php 0000644 00000001234 13645114176 0017500 0 ustar 00 $parameters
*
* @return void
*
* @throws InvalidSignatureException
* @throws MissingSignatureException
*/
public function checkSignature(ReflectionClass $class, array $parameters);
}
src/ProxyManager/Signature/SignatureGenerator.php 0000644 00000001601 13645114176 0016237 0 ustar 00 parameterEncoder = new ParameterEncoder();
$this->parameterHasher = new ParameterHasher();
}
/**
* {@inheritDoc}
*/
public function generateSignature(array $parameters) : string
{
return $this->parameterEncoder->encodeParameters($parameters);
}
/**
* {@inheritDoc}
*/
public function generateSignatureKey(array $parameters) : string
{
return $this->parameterHasher->hashParameters($parameters);
}
}
src/ProxyManager/Signature/SignatureGeneratorInterface.php 0000644 00000001175 13645114176 0020066 0 ustar 00 $parameters
*/
public function generateSignature(array $parameters) : string;
/**
* Generates a signature key to be looked up when verifying generated code validity
*
* @param array $parameters
*/
public function generateSignatureKey(array $parameters) : string;
}
src/ProxyManager/Stub/EmptyClassStub.php 0000644 00000000212 13645114176 0014322 0 ustar 00 @,
* where the detected version is what composer could detect.
*
* @throws OutOfBoundsException
*
* @psalm-pure
*/
public static function getVersion() : string
{
return Versions::getVersion('ocramius/proxy-manager');
}
}