PK oYsNiZ README.mdnu W+A # 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), which are also
[compiled to HTML](http://ocramius.github.io/ProxyManager).
## Professional Support
[Professionally supported `ocramius/proxy-manager` is available through Tidelift](https://tidelift.com/subscription/pkg/packagist-ocramius-proxy-manager?utm_source=packagist-ocramius-proxy-manager&utm_medium=referral&utm_campaign=readme) .
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 [online documentation](http://ocramius.github.io/ProxyManager) for more supported proxy types and examples.
PK oYsNQ"
UPGRADE.mdnu W+A ---
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.
PK oYsN
?x = src/ProxyManager/Exception/InvalidProxyDirectoryException.phpnu W+A 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()))
));
}
}
PK oYsNTJ> 7 src/ProxyManager/Exception/FileNotWritableException.phpnu W+A 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();
}
)
)
)
));
}
}
PK oYsNr
6 src/ProxyManager/Exception/DisabledMethodException.phpnu W+A
* $initializer = function (& $wrappedObject, $proxy, string $method, array $parameters, & $initializer) {};
*
*
* @return mixed
*/
public function setProxyInitializer(?Closure $initializer = null);
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;
}
PK oYsNЋLU U 5 src/ProxyManager/Proxy/AccessInterceptorInterface.phpnu W+A
* $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
*/
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
*/
public function setMethodSuffixInterceptor(string $methodName, ?Closure $suffixInterceptor = null) : void;
}
PK oYsNi2 @ src/ProxyManager/Proxy/AccessInterceptorValueHolderInterface.phpnu W+A @,
* where the detected version is what composer could detect.
*
* @throws OutOfBoundsException
*/
public static function getVersion() : string
{
return Versions::getVersion('ocramius/proxy-manager');
}
}
PK oYsNܰz. . 1 src/ProxyManager/Inflector/ClassNameInflector.phpnu W+A proxyNamespace = $proxyNamespace;
$this->proxyMarker = '\\' . static::PROXY_MARKER . '\\';
$this->proxyMarkerLength = strlen($this->proxyMarker);
$this->parameterHasher = new ParameterHasher();
}
/**
* {@inheritDoc}
*/
public function getUserClassName(string $className) : string
{
$className = ltrim($className, '\\');
$position = strrpos($className, $this->proxyMarker);
if ($position === false) {
return $className;
}
return substr(
$className,
$this->proxyMarkerLength + $position,
strrpos($className, '\\') - ($position + $this->proxyMarkerLength)
);
}
/**
* {@inheritDoc}
*/
public function getProxyClassName(string $className, array $options = []) : string
{
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;
}
}
PK oYsN.# 4 src/ProxyManager/Inflector/Util/ParameterEncoder.phpnu W+A proxiesDirectory = $absolutePath;
}
/**
* {@inheritDoc}
*/
public function getProxyFileName(string $className) : string
{
return $this->proxiesDirectory . DIRECTORY_SEPARATOR . str_replace('\\', '', $className) . '.php';
}
}
PK oYsN ( src/ProxyManager/Stub/EmptyClassStub.phpnu W+A setInterface(false);
$method->setBody('');
return $method;
}
}
PK oYsNGz 6 src/ProxyManager/Generator/Util/IdentifierSuffixer.phpnu W+A getReturnType() === 'void') {
return $returnedValueExpression . ";\nreturn;";
}
return 'return ' . $returnedValueExpression . ';';
}
}
PK oYsNYO O = src/ProxyManager/Generator/Util/UniqueIdentifierGenerator.phpnu W+A getName();
if ($originalClass->hasMethod($methodName) && $originalClass->getMethod($methodName)->isFinal()) {
return false;
}
$classGenerator->addMethodFromGenerator($generatedMethod);
return true;
}
}
PK oYsNѭ 3 src/ProxyManager/Generator/MagicMethodGenerator.phpnu W+A setReturnsReference(strtolower($name) === '__get');
if (! $originalClass->hasMethod($name)) {
return;
}
$this->setReturnsReference($originalClass->getMethod($name)->returnsReference());
}
}
PK oYsNo o - src/ProxyManager/Generator/ClassGenerator.phpnu W+A generate();
}
}
PK oYsNZm m A src/ProxyManager/GeneratorStrategy/GeneratorStrategyInterface.phpnu W+A canEval = ! ini_get('suhosin.executor.disable_eval');
// @codeCoverageIgnoreEnd
}
/**
* Evaluates the generated code before returning it
*
* {@inheritDoc}
*/
public function generate(ClassGenerator $classGenerator) : string
{
$code = $classGenerator->generate();
// @codeCoverageIgnoreStart
if (! $this->canEval) {
$fileName = tempnam(sys_get_temp_dir(), 'EvaluatingGeneratorStrategy.php.tmp.');
assert(is_string($fileName));
file_put_contents($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
{
$className = trim($classGenerator->getNamespaceName(), '\\')
. '\\' . trim($classGenerator->getName(), '\\');
$generatedCode = $classGenerator->generate();
$fileName = $this->fileLocator->getProxyFileName($className);
set_error_handler($this->emptyErrorHandler);
try {
$this->writeFile("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
?: new ClassSignatureGenerator($this->getSignatureGenerator());
}
}
PK oYsN]7=( ( = src/ProxyManager/Factory/RemoteObject/Adapter/BaseAdapter.phpnu W+A client = $client;
$this->map = $map;
}
/**
* {@inheritDoc}
*
* @param mixed[] $params
*/
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;
}
PK oYsNY Y 8 src/ProxyManager/Factory/RemoteObject/Adapter/XmlRpc.phpnu W+A > 6 src/ProxyManager/Factory/RemoteObject/Adapter/Soap.phpnu W+A J J @ src/ProxyManager/Factory/AccessInterceptorValueHolderFactory.phpnu W+A generateProxy(get_class($instance));
return $proxyClassName::staticProxyConstructor($instance, $prefixInterceptors, $suffixInterceptors);
}
/**
* {@inheritDoc}
*/
protected function getGenerator() : ProxyGeneratorInterface
{
return $this->generator ?: $this->generator = new AccessInterceptorValueHolderGenerator();
}
}
PK oYsN]H H C src/ProxyManager/Factory/AccessInterceptorScopeLocalizerFactory.phpnu W+A generateProxy(get_class($instance));
return $proxyClassName::staticProxyConstructor($instance, $prefixInterceptors, $suffixInterceptors);
}
/**
* {@inheritDoc}
*/
protected function getGenerator() : ProxyGeneratorInterface
{
return $this->generator ?: $this->generator = new AccessInterceptorScopeLocalizerGenerator();
}
}
PK oYsNu: : src/ProxyManager/Factory/LazyLoadingValueHolderFactory.phpnu W+A generateProxy($className, $proxyOptions);
return $proxyClassName::staticProxyConstructor($initializer);
}
/**
* {@inheritDoc}
*/
protected function getGenerator() : ProxyGeneratorInterface
{
return $this->generator ?: $this->generator = new LazyLoadingValueHolderGenerator();
}
}
PK oYsNzts s 0 src/ProxyManager/Factory/AbstractBaseFactory.phpnu W+A configuration = $configuration ?: new Configuration();
}
/**
* Generate a proxy from a class name
*
* @param mixed[] $proxyOptions
*
* @throws InvalidSignatureException
* @throws MissingSignatureException
* @throws OutOfBoundsException
*/
protected function generateProxy(string $className, array $proxyOptions = []) : string
{
if (array_key_exists($className, $this->checkedClasses)) {
return $this->checkedClasses[$className];
}
$proxyParameters = [
'className' => $className,
'factory' => static::class,
'proxyManagerVersion' => Version::getVersion(),
];
$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 string[] $proxyParameters
* @param mixed[] $proxyOptions
*/
private function generateProxyClass(
string $proxyClassName,
string $className,
array $proxyParameters,
array $proxyOptions = []
) : void {
$className = $this->configuration->getClassNameInflector()->getUserClassName($className);
$phpClass = new ClassGenerator($proxyClassName);
$this->getGenerator()->generate(new ReflectionClass($className), $phpClass, $proxyOptions);
$phpClass = $this->configuration->getClassSignatureGenerator()->addSignature($phpClass, $proxyParameters);
$this->configuration->getGeneratorStrategy()->generate($phpClass, $proxyOptions);
$autoloader = $this->configuration->getProxyAutoloader();
$autoloader($proxyClassName);
}
}
PK oYsN