Augmentation vers version 3.3.0

This commit is contained in:
Gauvain Boiché
2020-03-31 15:31:03 +02:00
parent d926806907
commit a1864c0414
2618 changed files with 406015 additions and 31377 deletions

View File

@@ -0,0 +1,375 @@
---
title: Changelog
---
This is a list of changes/improvements that were introduced in ProxyManager
## 2.1.1
This release provides aggressive improvements in static introspection via
[phpstan/phpstan](https://github.com/phpstan/phpstan). No functional
changes, just removed possible bugs due to excessive parameter count
in method calls, as well as test type hint fixes.
Total issues resolved: **3**
- [351: Travis already has xdebug pre-installed for PHP 7.1](https://github.com/Ocramius/ProxyManager/pull/351)
- [358: Fix #351 - travis has xdebug pre-installed for PHP 7.1](https://github.com/Ocramius/ProxyManager/pull/358)
- [361: PHPStan inspection fixes](https://github.com/Ocramius/ProxyManager/pull/361)
## 2.1.0
### Improved
- Introduced support for PHP 7.1, `void` and nullable (`?`)
types [#314](https://github.com/Ocramius/ProxyManager/issues/314)
[#327](https://github.com/Ocramius/ProxyManager/pull/327)
- The test suite is now fully CI-covered by mutation testing [#348](https://github.com/Ocramius/ProxyManager/pull/348)
- Moved all performance testing to PHPBench [#326](https://github.com/Ocramius/ProxyManager/pull/326)
- PHP 7.0 support dropped [#327](https://github.com/Ocramius/ProxyManager/pull/327)
Total issues resolved: **9**
- [290: Branch-alias version bump: master bump to 3.0.x](https://github.com/Ocramius/ProxyManager/pull/290)
- [314: Nullable params type](https://github.com/Ocramius/ProxyManager/issues/314)
- [326: Moved performance tests to phpbench-based suite](https://github.com/Ocramius/ProxyManager/pull/326)
- [327: PHP 7.1 support](https://github.com/Ocramius/ProxyManager/pull/327)
- [336: Hotfix tests for php 7 support](https://github.com/Ocramius/ProxyManager/pull/336)
- [339: Provided type ?... is invalid](https://github.com/Ocramius/ProxyManager/issues/339)
- [343: Fix typo in ghost objects lazy loading code example](https://github.com/Ocramius/ProxyManager/pull/343)
- [348: Introduced full mutation testing](https://github.com/Ocramius/ProxyManager/pull/348)
- [349: install xdebug manually for php 7.1](https://github.com/Ocramius/ProxyManager/pull/349)
## 2.0.4
### Fixed
- Remove deprecated `getMock` usage from tests [#325](https://github.com/Ocramius/ProxyManager/pull/325)
- Fix incorrect type in docs example [#329](https://github.com/Ocramius/ProxyManager/pull/329)
- Bug when proxy `__get` magic method [#344](https://github.com/Ocramius/ProxyManager/pull/344)
- Fix lazy loading value holder magic method support [#345](https://github.com/Ocramius/ProxyManager/pull/345)
## 2.0.3
### Fixed
- Various test suite cleanups, mostly because of
[new PHPUnit 5.4.0 deprecations being introduced](https://github.com/sebastianbergmann/phpunit/wiki/Release-Announcement-for-PHPUnit-5.4.0)
[#318](https://github.com/Ocramius/ProxyManager/issues/318)
- Removed `zendframework/zend-code:3.0.3` from installable dependencies, since
a critical bug was introduced in it [#321](https://github.com/Ocramius/ProxyManager/issues/321)
[#323](https://github.com/Ocramius/ProxyManager/issues/323)
[#324](https://github.com/Ocramius/ProxyManager/issues/324). Please upgrade to
`zendframework/zend-code:3.0.4` or newer.
## 2.0.2
### Fixed
- Various optimizations were performed in the [`ocramius/package-versions`](https://github.com/Ocramius/PackageVersions)
integration in order to prevent "class not found" fatals. [#294](https://github.com/Ocramius/ProxyManager/issues/294)
- Null objects produced via a given class name were not extending from the given class name, causing obvious LSP
compliance and type-compatibility issues. [#300](https://github.com/Ocramius/ProxyManager/issues/300)
[#301](https://github.com/Ocramius/ProxyManager/issues/301)
- Specific installation versions were removed from the [README.md](README.md) install instructions, since composer
is installing the latest available version by default. [#305](https://github.com/Ocramius/ProxyManager/issues/305)
- PHP 7.0.6 support was dropped. PHP 7.0.6 includes some nasty reflection bugs that caused `__isset` to be called when
`ReflectionProperty#getValue()` is used (https://bugs.php.net/72174).
[#306](https://github.com/Ocramius/ProxyManager/issues/306)
[#308](https://github.com/Ocramius/ProxyManager/issues/308)
- PHP 7.0.7 contains additional limitations as to when `$this` can be used. Specifically, `$this` cannot be used as a
parameter name for closures that have an already assigned `$this`. Due to `$this` being incorrectly used as parameter
name within this library, running ProxyManager on PHP 7.0.7 would have caused a fatal error.
[#306](https://github.com/Ocramius/ProxyManager/issues/306)
[#308](https://github.com/Ocramius/ProxyManager/issues/308)
[#316](https://github.com/Ocramius/ProxyManager/issues/316)
- PHP 7.1.0-DEV includes type-checks for incompatible arithmetic operations: some of those operations were erroneously
performed in the library internals. [#308](https://github.com/Ocramius/ProxyManager/issues/308)
## 2.0.1
### Fixed
- Travis-CI environment was fixed to test the library using the minimum dependencies version.
### Added
- Added unit test to make sure that properties skipped should be preserved even being cloned.
## 2.0.0
### BC Breaks
Please refer to [the upgrade documentation](UPGRADE.md) to see which backwards-incompatible
changes were applied to this release.
### New features
#### PHP 7 support
ProxyManager will now correctly operate in PHP 7 environments.
#### PHP 7 Return type hints
ProxyManager will now correctly mimic signatures of methods with return type hints:
```php
class SayHello
{
public function hello() : string
{
return 'hello!';
}
}
```
#### PHP 7 Scalar type hints
ProxyManager will now correctly mimic signatures of methods with scalar type hints
```php
class SayHello
{
public function hello(string $name) : string
{
return 'hello, ' . $name;
}
}
```
#### PHP 5.6 Variadics support
ProxyManager will now correctly mimic behavior of methods with variadic parameters:
```php
class SayHello
{
public function hello(string ...$names) : string
{
return 'hello, ' . implode(', ', $names);
}
}
```
By-ref variadic arguments are also supported:
```php
class SayHello
{
public function hello(string ... & $names)
{
foreach ($names as & $name) {
$name = 'hello, ' . $name;
}
}
}
```
#### Constructors in proxies are not replaced anymore
In ProxyManager v1.x, the constructor of a proxy was completely replaced with a method
accepting proxy-specific parameters.
This is no longer true, and you will be able to use the constructor of your objects as
if the class wasn't proxied at all:
```php
class SayHello
{
public function __construct()
{
echo 'Hello!';
}
}
/* @var $proxyGenerator \ProxyManager\ProxyGenerator\ProxyGeneratorInterface */
$proxyClass = $proxyGenerator->generateProxy(
new ReflectionClass(SayHello::class),
new ClassGenerator('ProxyClassName')
);
eval('<?php ' . $proxyClass->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.

View File

@@ -1,8 +1,8 @@
# Proxy Manager
This library aims at providing abstraction for generating various kinds of [proxy classes](http://marco-pivetta.com/proxy-pattern-in-php/).
This library aims at providing abstraction for generating various kinds of [proxy classes](http://ocramius.github.io/presentations/proxy-pattern-in-php/).
![ProxyManager](proxy-manager.png)
![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/)
@@ -19,14 +19,19 @@ This library aims at providing abstraction for generating various kinds of [prox
## Documentation
You can learn about the proxy pattern and how to use the **ProxyManager** on the [online documentation](http://ocramius.github.io/ProxyManager).
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).
## Help/Support
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Ocramius/ProxyManager?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
## Installation
The suggested installation method is via [composer](https://getcomposer.org/):
```sh
php composer.phar require ocramius/proxy-manager:1.0.*
php composer.phar require ocramius/proxy-manager
```
## Proxy example
@@ -37,9 +42,9 @@ Here's how you build a lazy loadable object with ProxyManager using a *Virtual P
$factory = new \ProxyManager\Factory\LazyLoadingValueHolderFactory();
$proxy = $factory->createProxy(
'MyApp\HeavyComplexObject',
\MyApp\HeavyComplexObject::class,
function (& $wrappedObject, $proxy, $method, $parameters, & $initializer) {
$wrappedObject = new HeavyComplexObject(); // instantiation logic here
$wrappedObject = new \MyApp\HeavyComplexObject(); // instantiation logic here
$initializer = null; // turning off further lazy initialization
}
);

View File

@@ -1,10 +1,14 @@
---
title: Stability
---
This is a list of supported versions, with their expected release/support time-frames:
# 0.5.x
# 2.0.x
* Release date: 2013-12-01
* Bug fixes: till 2015-03-11
* Security fixes: till 2015-12-11
* Release date: 2016-01-30
* Bug fixes: till 2017-12-31
* Security fixes: till 2018-12-31
# 1.0.x
@@ -12,8 +16,8 @@ This is a list of supported versions, with their expected release/support time-f
* Bug fixes: till 2015-12-11
* Security fixes: till 2016-12-11
# 2.0.x
# 0.5.x
* Release date: TBA
* Bug fixes: TBA
* Security fixes: TBA
* Release date: 2013-12-01
* Bug fixes: till 2015-03-11
* Security fixes: till 2015-12-11

View File

@@ -1,5 +1,38 @@
---
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

View File

@@ -15,24 +15,30 @@
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com",
"homepage": "http://ocramius.github.com/"
"homepage": "http://ocramius.github.io/"
}
],
"require": {
"php": ">=5.3.3",
"zendframework/zend-code": ">2.2.5,<3.0"
"php": "^7.1.0",
"zendframework/zend-code": "^3.1.0",
"ocramius/package-versions": "^1.1.1"
},
"require-dev": {
"ext-phar": "*",
"phpunit/phpunit": "~4.0",
"squizlabs/php_codesniffer": "1.5.*"
"ext-phar": "*",
"phpunit/phpunit": "^5.6.4",
"phpunit/phpunit-mock-objects": "^3.4.1",
"squizlabs/php_codesniffer": "^2.7.0",
"couscous/couscous": "^1.5.2",
"phpbench/phpbench": "^0.12.2",
"phpstan/phpstan": "^0.6.4",
"nikic/php-parser": "^3.0.4",
"humbug/humbug": "dev-master@DEV"
},
"suggest": {
"zendframework/zend-stdlib": "To use the hydrator proxy",
"ocramius/generated-hydrator": "To have very fast object to array to object conversion for ghost objects",
"zendframework/zend-xmlrpc": "To have the XmlRpc adapter (Remote Object feature)",
"zendframework/zend-json": "To have the JsonRpc adapter (Remote Object feature)",
"zendframework/zend-soap": "To have the Soap adapter (Remote Object feature)"
"zendframework/zend-xmlrpc": "To have the XmlRpc adapter (Remote Object feature)",
"zendframework/zend-json": "To have the JsonRpc adapter (Remote Object feature)",
"zendframework/zend-soap": "To have the Soap adapter (Remote Object feature)"
},
"autoload": {
"psr-0": {
@@ -41,13 +47,14 @@
},
"autoload-dev": {
"psr-0": {
"ProxyManagerBench\\": "tests",
"ProxyManagerTest\\": "tests",
"ProxyManagerTestAsset\\": "tests"
}
},
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
"dev-master": "3.0.x-dev"
}
}
}

View File

@@ -0,0 +1,125 @@
/*
Copyright (c) 2006, Ivan Sagalaev
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of highlight.js nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
background: #444;
-webkit-text-size-adjust: none;
}
.hljs-keyword,
.hljs-literal,
.hljs-change,
.hljs-winutils,
.hljs-flow,
.nginx .hljs-title,
.tex .hljs-special {
color: white;
}
.hljs,
.hljs-subst {
color: #ddd;
}
.hljs-string,
.hljs-title,
.hljs-type,
.ini .hljs-title,
.hljs-tag .hljs-value,
.css .hljs-rules .hljs-value,
.hljs-preprocessor,
.hljs-pragma,
.ruby .hljs-symbol,
.ruby .hljs-symbol .hljs-string,
.ruby .hljs-class .hljs-parent,
.hljs-built_in,
.django .hljs-template_tag,
.django .hljs-variable,
.smalltalk .hljs-class,
.hljs-javadoc,
.ruby .hljs-string,
.django .hljs-filter .hljs-argument,
.smalltalk .hljs-localvars,
.smalltalk .hljs-array,
.hljs-attr_selector,
.hljs-pseudo,
.hljs-addition,
.hljs-stream,
.hljs-envvar,
.apache .hljs-tag,
.apache .hljs-cbracket,
.tex .hljs-command,
.hljs-prompt,
.coffeescript .hljs-attribute {
color: #d88;
}
.hljs-comment,
.hljs-annotation,
.hljs-decorator,
.hljs-pi,
.hljs-doctype,
.hljs-deletion,
.hljs-shebang,
.apache .hljs-sqbracket,
.tex .hljs-formula {
color: #777;
}
.hljs-keyword,
.hljs-literal,
.hljs-title,
.css .hljs-id,
.hljs-phpdoc,
.hljs-dartdoc,
.hljs-type,
.vbscript .hljs-built_in,
.rsl .hljs-built_in,
.smalltalk .hljs-class,
.diff .hljs-header,
.hljs-chunk,
.hljs-winutils,
.bash .hljs-variable,
.apache .hljs-tag,
.tex .hljs-special,
.hljs-request,
.hljs-status {
font-weight: bold;
}
.coffeescript .javascript,
.javascript .xml,
.tex .hljs-formula,
.xml .javascript,
.xml .vbscript,
.xml .css,
.xml .hljs-cdata {
opacity: 0.5;
}

View File

@@ -0,0 +1,179 @@
html { font-family: sans-serif; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; }
body { margin: 0; }
[hidden], template { display: none; }
a { background: transparent; }
a:active, a:hover { outline: 0; }
h1 { font-size: 2em; margin: 0.67em 0; }
img { border: 0; }
svg:not(:root) { overflow: hidden; }
figure { margin: 1em 40px; }
hr { -moz-box-sizing: content-box; box-sizing: content-box; height: 0; }
pre { overflow: auto; }
code, kbd, pre, samp { font-family: 'Menlo', 'Monaco', monospace; font-size: 1em; }
button, input, optgroup, select, textarea { color: inherit; font: inherit; margin: 0; }
button { overflow: visible; }
button, select { text-transform: none; }
button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; cursor: pointer; }
h1, h2, h3, h4, h5, h6, p, ul, ol, li { margin: 0; padding: 0; line-height: normal; }
a { color: #31811D; text-decoration: none; }
a:hover { color: #2F611C; text-decoration: none; }
h3 { margin-bottom: 1em; color: #17324f; font-weight: 400; font-size: 3em; }
h4 { margin-bottom: 1em; font-weight: 400; font-size: 1.375em; }
p { margin-bottom: 1.5em; font-size: 1.125em; }
hr { margin: 2.5em 0; padding: 0; width: 100%; height: 3px; border: 0; background: #e6eaef; }
pre code { border-radius: 4px; font-size: 15px; padding: 20px; border: 1px solid #EBEBEB; }
.content h1 { padding-bottom: 12px; border-bottom: 1px solid #EFEFEF; margin-bottom: 20px; }
.main-nav ul li{ position: relative;}
.main-nav ul li{ position: relative;}
.main-nav ul li:hover { background: #2F611C;}
.main-nav ul li:hover > ul { display:block }
.main-nav ul li > ul { display:none; position: absolute; list-style: none; width: 100%; z-index: 9;}
.main-nav ul li > ul li a { color: #BFE5F1; display: block; background: #2F611C; padding: 20px;}
.main-nav ul li > ul li a:hover { background: #1C440C;}
/* Menu Sidebar*/
.button-block { padding-top: 15px; }
.button-block .btn-1 { margin-right: 6px; }
.btn, .spy-nav a { position: relative; display: inline-block; margin: 0; padding: 0 20px; height: 57px; border: 0; vertical-align: top; text-align: center; text-transform: uppercase; font-weight: 400; font-size: 1.125em; transitionP: all .2s; line-height: 57px; }
.btn.btn-action, .spy-nav a.btn-action { background: #ee2d4d; color: #fff; }
.btn.btn-action:hover, .spy-nav a.btn-action:hover { background: #bf0f2d; }
.btn.btn-default, .spy-nav a { background: #31811D; color: #fff; }
.btn.btn-default:hover, .spy-nav a:hover { background: #2F611C; color: #fff; }
.btn.btn-text, .spy-nav a.btn-text { color: #18324f; font-weight: 600; }
.btn.btn-full, .spy-nav a { display: block; }
@media only screen and (max-width: 480px) {
.site-header{ position: fixed !important; top: 0; z-index: 999999}
.page-title-wrapper{ margin-top: 70px;}
.main-wrapper iframe{ width: 42% !important; float: left; margin-left: 8%;}
.content iframe{width: 100%; height: 100%;}
}
.btn-top { position: relative; display: inline-block; float: right; margin: 20px 0 0; padding: 0 30px 0 50px; height: 57px; border: 2px solid #31811D; color: #31811D; vertical-align: top; text-align: center; text-transform: uppercase; font-weight: 600; font-size: 1.125em; line-height: 53px; transition: all .2s; }
.btn-top:before { background-position: 0 -140px; width: 52px; height: 52px; position: absolute; top: 0; left: 0; content: ''; }
@media only screen and (min-resolution: 2dppx), (-webkit-min-device-pixel-ratio: 2) { .btn-top:before { background-position: 0 -140px; background-size: 152px auto; width: 52px; height: 52px; } }
.btn-top:hover { border-color: #2F611C; color: #2F611C; }
@media only screen and (max-width: 768px) { .btn-top { float: none; margin-top: 0; margin-bottom: 30px; white-space: nowrap; } }
@media only screen and (max-width: 480px) { .btn-top { font-size: 0.9em; line-height: 46px; height: 48px; padding-right: 22px; padding-left: 47px; } }
.btn-download { float: right; padding-left: 50px; }
.btn-download:before { background-position: -18px -116px; width: 16px; height: 16px; position: absolute; top: 50%; left: 20px; margin-top: -7px; content: ''; }
@media only screen and (min-resolution: 2dppx), (-webkit-min-device-pixel-ratio: 2) { .btn-download:before { background-position: -18px -116px; background-size: 152px auto; width: 16px; height: 16px; } }
.btn-done { float: right; padding-left: 50px; }
.btn-done:before { background-position: 0 -116px; width: 18px; height: 14px; position: absolute; top: 50%; margin-top: -7px; left: 20px; content: ''; }
@media only screen and (min-resolution: 2dppx), (-webkit-min-device-pixel-ratio: 2) { .btn-done:before { background-position: 0 -116px; background-size: 152px auto; width: 18px; height: 13.5px; margin-top: -6.75px; } }
.btn-cancel { float: right; }
*, *:before, *:after { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
html { -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; min-width: 280px; }
@media only screen and (min-width: 64.063em) and (max-width: 90em) { html { min-width: 960px; } }
body { font-size: 16px; font-family: 'Source Sans Pro', sans-serif; min-width: 290px; }
@media only screen and (max-width: 480px) { body.page-home { background-image: none; } }
img { max-width: 100%; height: auto !important; }
input { -webkit-appearance: none; -webkit-border-radius: 0; border-radius: 0; }
main { overflow: hidden; }
.clearfix:after { content: ""; display: table; clear: both; }
.container { width: 60em; margin-left: auto; margin-right: auto; }
.container:after { content: " "; display: block; clear: both; }
@media only screen and (max-width: 1024px) { .container { margin-left: 15px; margin-right: 15px; width: auto; } }
.content ul { padding-left: 19px; list-style-type: square; margin-top: 15px; }
/* Menu top */
.main-nav { float: right; }
.fixed-wrapper .main-nav { display: none; }
.active .main-nav { display: block; }
.main-nav > ul { list-style: none; }
.main-nav > ul > li { float: left; }
.main-nav > ul > li > a { display: block; padding: 22px 30px; color: #fff; text-decoration: none; text-transform: uppercase; font-weight: 600; font-size: 1.375em; line-height: 29px; transition: all .2s; }
.main-nav > ul > li > a:hover { color: #bfe5f1; background-color: #2F611C; }
.main-nav > ul > li > a.active { background-color: #2F611C; color: #fff; }
.main-nav > ul > li > a.opened { background-color: #18324f; }
@media only screen and (max-width: 600px) { .main-nav { font-size: 0.86em; }
.main-nav > ul > li > a { padding-left: 18px; padding-right: 18px; } }
@media only screen and (max-width: 480px) { .main-nav > ul > li > a { font-size: 0.95em; padding: 22px 6px; } }
.site-header { position: relative; width: 100%; background: #31811D; }
.site-header h1 { float: left; margin: 15px 0; }
.site-header h1 a { display: block; }
.site-header h1 img { display: block; max-height: 42px; }
@media only screen and (max-width: 480px) { .site-header h1 img { max-height: 42px; } }
.page-title-wrapper { position: relative; padding: 26px 0 55px; text-align: center; }
.page-title-wrapper .page-title { margin-top: 44px; color: #17324f; font-weight: 400; font-size: 3.75em; }
.page-title-wrapper .linguistics { padding: 0 2em 13px; border-bottom: 1px solid #D4DBE3; color: #18324f; text-transform: uppercase; font-weight: 400; font-size: 1.25em; }
@media only screen and (max-width: 768px) { .page-title-wrapper { font-size: 0.8em; } }
@media only screen and (max-width: 480px) { .page-title-wrapper { font-size: 0.65em; } }
.site-footer { margin-top: 20px; padding-top: 50px; padding-bottom: 50px; background: #18324f; color: #7c8ea3; text-align: center; }
.site-footer .container { position: relative; }
.footer-logos ul li{ list-style: none; display: inline-block;}
.footer-logos ul li a{ padding-left: 10px; padding-right: 10px}
.site-footer a{ color: white !important;}
.site-footer p { text-align: left; display: block; margin-bottom: 1.5em; font-size: 1.125em; }
@media only screen and (max-width: 1024px) { .site-footer p { margin-left: 0; } }
@media only screen and (max-width: 768px) { .site-footer p { text-align: center; margin-left: auto; } }
@media only screen and (max-width: 600px) { .site-footer { font-size: 0.9em; } }
.main-logo { display: block; float: left; margin: 30px 0; }
.fixed-wrapper .main-logo { display: none; }
.main-logo img { display: block; }
.main-logo figure { position: relative; margin: 0; max-width: 56px; }
.main-logo figcaption { position: absolute; width: 160px; top: 20px; left: 68px; }
.active .main-logo { display: block; margin: 0; }
.active .main-logo figure { max-width: 42px; }
.active .main-logo figcaption { width: 120px; top: 15px; left: 55px; }
@media only screen and (max-width: 480px) { .main-logo figcaption { display: none; } }
.container { width: 60em; margin-left: auto; margin-right: auto; }
.container:after { content: " "; display: block; clear: both; }
@media only screen and (max-width: 1024px) { .container { margin-left: 15px; margin-right: 15px; width: auto; } }
.component-demo { position: relative; padding: 1px 0 0; background: #e6eaef; }
.component-demo:before { background-image: url("../img/enf.png"); }
@media only screen and (max-width: 480px) { .component-demo { padding: 40px 0 0; } }
.component-info .container { position: relative; }
.component-info .sidebar { width: 220px; float: left; margin-top: 75px; }
.component-info .sidebar .component-meta { margin-top: 10px; font-size: 1.125em; }
.component-info .sidebar .component-meta span { white-space: nowrap; margin-bottom: 10px; padding-left: 25px; color: #18324f; }
.component-info .sidebar.sticky { position: fixed; margin-top: 38px; }
.component-info .content { width: 64.58333333%; float: right; margin-top: 60px; margin-bottom: 75px; }
.component-info .content h3 { margin-bottom: .75em; font-size: 2.75em; }
.component-info .content p { margin-bottom: 1.75em; line-height: 1.45; }
@media only screen and (max-width: 480px) { .component-info .content .section-title { font-size: 2.1em; } }
@media only screen and (max-width: 768px) { .component-info .sidebar { float: none; margin-left: auto; margin-right: auto; }
.component-info .sidebar .component-meta { margin-top: 30px; text-align: center; }
.component-info .sidebar.sticky { position: relative; margin-top: 75px; }
.component-info .content { float: none; width: 100%; } }
@media only screen and (max-width: 480px) { .component-info .content { margin-bottom: 0; } }
.spy-nav { margin-bottom: 10px; }
.spy-nav ul { list-style: none; }
.spy-nav a { padding-top: 9px; padding-bottom: 10px; height: auto; border-bottom: 1px solid #4B8B20; text-align: left; text-transform: none; font-size: 1.375em; line-height: normal; }
.spy-nav .active a { border-bottom-color: #fff; background: #fff; color: #17324f; }
.spy-nav select{ width: 100%; margin-bottom: 10px}
.main-wrapper { margin: 44px auto 44px; max-width: 600px; }
.main-wrapper label { display: block; margin-bottom: .75em; color: #3f4e5e; font-size: 1.25em; }
.main-wrapper .text-field { padding: 0 15px; width: 100%; height: 40px; border: 1px solid #CBD3DD; font-size: 1.125em; }
.main-wrapper ::-webkit-input-placeholder { color: #CBD3DD; font-style: italic; font-size: 18px; }
.main-wrapper :-moz-placeholder { color: #CBD3DD; font-style: italic; font-size: 18px; }
.main-wrapper ::-moz-placeholder { color: #CBD3DD; font-style: italic; font-size: 18px; }
.main-wrapper :-ms-input-placeholder { color: #CBD3DD; font-style: italic; font-size: 18px; }
.page-icon-wrapper:before, .page-icon-wrapper .logo-asp:before, .page-icon-wrapper .logo-hibernate:before, .page-icon-wrapper .logo-angularjs:before, .page-icon-wrapper .logo-requirejs:before, .page-icon-wrapper .logo-reward:before, .component-demo:before {background-repeat: no-repeat; background-size: 100%; width: 92px; height: 108px; position: absolute; left: 50%; margin-left: -46px; top: 0; bottom: auto; margin-top: -28px; content: ''; }
.container { width: 60em; margin-left: auto; margin-right: auto; }
.container:after { content: " "; display: block; clear: both; }
@media only screen and (max-width: 1024px) { .container { margin-left: 15px; margin-right: 15px; width: auto; } }
.bcms-clearfix:after {content: ""; visibility: hidden; display: block; height: 0; clear: both; }

View File

@@ -0,0 +1,117 @@
<!DOCTYPE html>
<html class="no-js" id="top">
<head>
<title>ProxyManager Documentation{% if title is defined %} - {{ title }}{% endif %}</title>
<meta name="description" content="A proxyManager write in php" />
<meta name="keywords" content="ProxyManager, proxy, manager, ocramius, Marco Pivetta, php" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<link href='http://fonts.googleapis.com/css?family=Source+Sans+Pro:200,300,400,600' rel='stylesheet' type='text/css'>
<link href="{{ baseUrl }}/css/main.css" rel="stylesheet" />
<link href="{{ baseUrl }}/css/highlight.dark.css" rel="stylesheet" />
<script src="//code.jquery.com/jquery-2.1.3.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.3/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<link rel="shortcut icon" href="favicon.ico">
</head>
<body>
<header class="site-header">
<div class="container">
<h1><a href="{{ baseUrl }}"><img alt="ProxyManager" src="{{ baseUrl }}/img/block.png" /></a></h1>
<nav class="main-nav" role="navigation">
<ul>
<li><a href="https://github.com/Ocramius/ProxyManager" target="_blank">Github</a></li>
{% if versions is defined %}
<li>
<a href="#">Version</a>
<ul>
{% for key, version in versions.items %}
<li><a href="{{ version.link|e }}">{{ version.name|e }}</a></li>
{% endfor %}
</ul>
</li>
{% endif %}
</ul>
<div class="bcms-clearfix"></div>
</nav>
</div>
</header>
<main role="main">
<section class="component-content"><div class="page-title-wrapper">
<div class="container">
<img src="https://github.com/Ocramius/ProxyManager/raw/master/proxy-manager.png">
<h2 class="page-title">Proxy Manager</h2>
</div>
</div>
<div class="component-demo" id="live-demo">
<div class="container">
<div class="main-wrapper" style="text-align: right">
<iframe src="http://ghbtns.com/github-btn.html?user=ocramius&amp;repo=ProxyManager&amp;type=fork&amp;count=true&amp;size=large"
allowtransparency="true" frameborder="0" scrolling="0" width="310" height="40"></iframe>
<iframe src="http://ghbtns.com/github-btn.html?user=ocramius&amp;repo=ProxyManager&amp;type=watch&amp;count=true&amp;size=large"
allowtransparency="true" frameborder="0" scrolling="0" width="200" height="40"></iframe>
</div>
<div class="bcms-clearfix bcms-clearfix"></div>
</div>
</div>
<div class="component-info">
<div class="container">
<aside class="sidebar">
<nav class="spy-nav">
<ul>
<li><a href="{{ baseUrl }}/index.html">Intro</a></li>
<li><a href="{{ baseUrl }}/docs/lazy-loading-value-holder.html">Virtual Proxy</a></li>
<li><a href="{{ baseUrl }}/docs/lazy-loading-ghost-object.html">Ghost Object</a></li>
<li><a href="{{ baseUrl }}/docs/access-interceptor-value-holder.html">Smart Reference</a></li>
<li><a href="{{ baseUrl }}/docs/access-interceptor-scope-localizer.html">Smart Reference<br/>(fluent safe)</a></li>
<li><a href="{{ baseUrl }}/docs/null-object.html">Null Objects</a></li>
<li><a href="{{ baseUrl }}/docs/remote-object.html">Remote Object</a></li>
<li><a href="{{ baseUrl }}/docs/generator-strategies.html">Code Evaluators</a></li>
<li><a href="{{ baseUrl }}/docs/tuning-for-production.html">Tuning for Production</a></li>
</ul>
</nav>
<div class="bcms-clearfix bcms-clearfix"></div>
<a class="btn btn-action btn-full download-component"
href="{{ baseUrl }}/docs/download.html">Download</a>
<div class="bcms-clearfix"></div>
</aside>
<div class="content">
{% block content %}
{{ content|raw }}
{% endblock %}
</div>
</div>
</div>
</section>
</main>
<footer class="site-footer" role="contentinfo">
<div class="container">
<div class="footer-logos">
<ul>
<li><a href="{{ baseUrl }}/stability.html">Stability</a> | </li>
<li><a href="{{ baseUrl }}/upgrade.html">Upgrade Notes</a> | </li>
<li><a href="{{ baseUrl }}/changelog.html">Changelog</a> | </li>
<li><a href="{{ baseUrl }}/contributing.html">Contributing</a> | </li>
<li><a href="{{ baseUrl }}/docs/credits.html">Credits</a> | </li>
<li><a href="{{ baseUrl }}/docs/copyright.html">Copyright</a></li>
</ul>
</div>
</div>
<div class="bcms-clearfix"></div>
</footer>
<div class="bcms-clearfix"></div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -1,3 +1,7 @@
---
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
@@ -60,8 +64,10 @@ would break an [access interceptor value holder](access-interceptor-value-holder
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
* if a proxied object contains private properties, then an exception will be thrown if you use
PHP `< 5.4.0`.
* 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
@@ -86,8 +92,8 @@ $factory = new Factory();
$proxy = $factory->createProxy(
new Foo(),
array('doFoo' => function () { echo "PreFoo!\n"; }),
array('doFoo' => function () { echo "PostFoo!\n"; })
['doFoo' => function () { echo "PreFoo!\n"; }],
['doFoo' => function () { echo "PostFoo!\n"; }]
);
$proxy->doFoo();

View File

@@ -1,3 +1,7 @@
---
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
@@ -35,8 +39,8 @@ $factory = new Factory();
$proxy = $factory->createProxy(
new Foo(),
array('doFoo' => function () { echo "PreFoo!\n"; }),
array('doFoo' => function () { echo "PostFoo!\n"; })
['doFoo' => function () { echo "PreFoo!\n"; }],
['doFoo' => function () { echo "PostFoo!\n"; }]
);
$proxy->doFoo();
@@ -54,10 +58,8 @@ PostFoo!
A proxy produced by the
[`ProxyManager\Factory\AccessInterceptorValueHolderFactory`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Factory/AccessInterceptorValueHolderFactory.php)
implements both the
[`ProxyManager\Proxy\ValueHolderInterface`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Proxy/ValueHolderInterface.php)
and the
[`ProxyManager\Proxy\AccessInterceptorInterface`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Proxy/ValueHolderInterface.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:
@@ -68,7 +70,7 @@ $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 following signature:
A prefix interceptor (executed before method logic) should have the following signature:
```php
/**
@@ -85,7 +87,7 @@ A prefix interceptor (executed before method logic) should have following signat
$prefixInterceptor = function ($proxy, $instance, $method, $params, & $returnEarly) {};
```
A suffix interceptor (executed after method logic) should have following signature:
A suffix interceptor (executed after method logic) should have the following signature:
```php
/**
@@ -103,6 +105,13 @@ A suffix interceptor (executed after method logic) should have following signatu
$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](https://github.com/Ocramius/ProxyManager/blob/master/docs/tuning-for-production.md).
See [Tuning ProxyManager for Production](https://github.com/Ocramius/ProxyManager/blob/master/docs/tuning-for-production.md).

View File

@@ -0,0 +1,15 @@
---
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.
---

View File

@@ -0,0 +1,26 @@
---
title: Credits
---
# Credits
The idea was originated by a [talk about Proxies in PHP OOP](http://marco-pivetta.com/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)

View File

@@ -0,0 +1,11 @@
---
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.*
```

View File

@@ -1,3 +1,7 @@
---
title: Generator strategies
---
# Generator strategies
ProxyManager allows you to generate classes based on generator strategies and a
@@ -13,4 +17,4 @@ Currently, 3 generator strategies are shipped with ProxyManager:
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.
instance as constructor parameter, and based on it, writes the generated class to a file before returning its code.

View File

@@ -1,7 +1,17 @@
---
title: Lazy Loading Ghost Object Proxies
---
# Lazy Loading Ghost Object Proxies
A lazy loading ghost object proxy is a ghost proxy that looks exactly like the real instance of the proxied subject,
but which has all properties nulled before initialization.
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 actually just an empty instance
that gets all properties populated as soon as they are needed.
Those properties do not really exist until the ghost object is actually
initialized.
## Lazy loading with the Ghost Object
@@ -41,20 +51,20 @@ 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 following applies
You usually need a ghost object in cases where 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 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
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 of all, define your object's logic without taking care of lazy loading:
First, define your object's logic without taking care of lazy loading:
```php
namespace MyApp;
@@ -74,88 +84,186 @@ class Customer
}
```
Then use the proxy manager to create a ghost object of it.
You will be responsible of setting its state during lazy loading:
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\LazyLoadingInterface;
use ProxyManager\Proxy\GhostObjectInterface;
require_once __DIR__ . '/vendor/autoload.php';
$factory = new LazyLoadingGhostFactory();
$initializer = function (LazyLoadingInterface $proxy, $method, array $parameters, & $initializer) {
$initializer = function (
GhostObjectInterface $ghostObject,
string $method,
array $parameters,
& $initializer,
array $properties
) {
$initializer = null; // disable initialization
// load data and modify the object here
$proxy->setName('Agent');
$proxy->setSurname('Smith');
$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
};
$instance = $factory->createProxy('MyApp\Customer', $initializer);
$ghostObject = $factory->createProxy(\MyApp\Customer::class, $initializer);
```
You can now simply use your object as before:
You can now use your object as before:
```php
// this will just work as before
echo $proxy->getName() . ' ' . $proxy->getSurname(); // Agent Smith
// this will work as before
echo $ghostObject->getName() . ' ' . $ghostObject->getSurname(); // Agent Smith
```
## Lazy Initialization
As you can see, we use a closure to handle lazy initialization of the proxy instance at runtime.
The initializer closure signature for ghost objects should be as following:
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 $proxy the instance 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 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 ($proxy, $method, $parameters, & $initializer) {};
$initializer = function (
\ProxyManager\Proxy\GhostObjectInterface $ghostObject,
string $method,
array $parameters,
& $initializer,
array $properties
) {};
```
The initializer closure should usually be coded like following:
```php
$initializer = function ($proxy, $method, $parameters, & $initializer) {
$initializer = function (
\ProxyManager\Proxy\GhostObjectInterface $ghostObject,
string $method,
array $parameters,
& $initializer,
array $properties
) {
$initializer = null; // disable initializer for this proxy instance
// modify the object with loaded data
$proxy->setFoo(/* ... */);
$proxy->setBar(/* ... */);
// 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` simply 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 very important 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 since 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 both the
[`ProxyManager\Proxy\GhostObjectInterface`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Proxy/GhostObjectInterface.php)
and the
[`ProxyManager\Proxy\LazyLoadingInterface`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Proxy/LazyLoadingInterface.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
$proxy->setProxyInitializer($initializer);
$ghostObject->setProxyInitializer($initializer);
```
In your initializer, you **MUST** turn off any further initialization:
```php
$proxy->setProxyInitializer(null);
$ghostObject->setProxyInitializer(null);
```
or
@@ -164,43 +272,137 @@ or
$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, or else initialization will trigger
more than once.
## Triggering Initialization
A lazy loading ghost object is initialized whenever you access any property or method of it.
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
$proxy->someMethod();
// calling a method (only if the method accesses internal state)
$ghostObject->someMethod();
// reading a property
echo $proxy->someProperty;
echo $ghostObject->someProperty;
// writing a property
$proxy->someProperty = 'foo';
$ghostObject->someProperty = 'foo';
// checking for existence of a property
isset($proxy->someProperty);
isset($ghostObject->someProperty);
// removing a property
unset($proxy->someProperty);
unset($ghostObject->someProperty);
// accessing a property via reflection
$reflection = new \ReflectionProperty($ghostObject, 'someProperty');
$reflection->setAccessible(true);
$reflection->getValue($ghostObject);
// cloning the entire proxy
clone $proxy;
clone $ghostObject;
// serializing the proxy
$unserialized = unserialize(serialize($proxy));
$unserialized = unserialize(serialize($ghostObject));
```
Remember to call `$proxy->setProxyInitializer(null);` to disable initialization of your proxy, or it will happen more
than once.
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 completely ignored 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 near-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
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, even if the `wrappedObject` implements more methods. This will anyway save
some memory since the proxy won't contain any properties.
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](https://github.com/Ocramius/ProxyManager/blob/master/docs/tuning-for-production.md).
See [Tuning ProxyManager for Production](tuning-for-production.md).

View File

@@ -1,3 +1,7 @@
---
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
@@ -66,7 +70,7 @@ class HeavyComplexObject
}
public function doFoo() {
echo "OK!"
echo 'OK!';
}
}
```
@@ -89,7 +93,7 @@ $initializer = function (& $wrappedObject, LazyLoadingInterface $proxy, $method,
return true; // confirm that initialization occurred correctly
};
$instance = $factory->createProxy('MyApp\HeavyComplexObject', $initializer);
$proxy = $factory->createProxy('MyApp\HeavyComplexObject', $initializer);
```
You can now simply use your object as before:
@@ -110,20 +114,20 @@ The initializer closure signature should be as following:
* 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 string $parameters an ordered list of parameters passed to the method that
* @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, $parameters, & $initializer) {};
$initializer = function (& $wrappedObject, $proxy, $method, array $parameters, & $initializer) {};
```
The initializer closure should usually be coded like following:
```php
$initializer = function (& $wrappedObject, $proxy, $method, $parameters, & $initializer) {
$initializer = function (& $wrappedObject, $proxy, $method, array $parameters, & $initializer) {
$newlyCreatedObject = new Foo(); // instantiation logic
$newlyCreatedObject->setBar('baz') // instantiation logic
$newlyCreatedObject->setBat('bam') // instantiation logic
@@ -197,6 +201,13 @@ You can also generate proxies from an interface FQCN. By proxying an interface,
methods defined by the interface itself, even if the `wrappedObject` implements more methods. This will anyway save
some memory since the proxy won't 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](https://github.com/Ocramius/ProxyManager/blob/master/docs/tuning-for-production.md).
See [Tuning ProxyManager for Production](tuning-for-production.md).

View File

@@ -1,3 +1,7 @@
---
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.
@@ -9,7 +13,7 @@ In your application, when you can't return the object related to the request, th
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 sorted out simply by creating a polymorphic implementation of the
domain object, which would implement the same interface as the one of the object in question, only that its methods
wouldnt do anything, therefore offloading client code from doing repetitive checks for ugly null values when the operation
wouldn't do anything, therefore offloading client code from doing repetitive checks for ugly null values when the operation
is executed.
## Usage examples
@@ -24,16 +28,20 @@ class UserMapper
}
public function fetchById($id) {
$this->adapter->select("users", array("id" => $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"]);
$user = new Entity\User($row['name'], $row['email']);
$user->setId($row['id']);
return $user;
}
}
@@ -62,7 +70,8 @@ class UserMapper
}
public function fetchById($id) {
$this->adapter->select("users", array("id" => $id));
$this->adapter->select('users', ['id' => $id]);
return $this->createUser($this->adapter->fetch());
}
@@ -72,8 +81,11 @@ class UserMapper
return $factory->createProxy('Entity\User');
}
$user = new Entity\User($row["name"], $row["email"]);
$user->setId($row["id"]);
$user = new Entity\User($row['name'], $row['email']);
$user->setId($row['id']);
return $user;
}
}
@@ -86,4 +98,4 @@ methods defined by the interface itself, and like with the object, the methods a
## Tuning performance for production
See [Tuning ProxyManager for Production](https://github.com/Ocramius/ProxyManager/blob/master/docs/tuning-for-production.md).
See [Tuning ProxyManager for Production](tuning-for-production.md).

View File

@@ -1,3 +1,7 @@
---
title: Remote Object Proxy
---
# Remote Object Proxy
The remote object implementation is a mechanism that enables an local object to control an other object on an other server.
@@ -88,13 +92,20 @@ interface AdapterInterface
*
* @return mixed
*/
public function call($wrappedClass, $method, array $params = array());
public function call($wrappedClass, $method, array $params = []);
}
```
It is very easy to create your own implementation (for RESTful web services, for example). Simply pass
your own 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](https://github.com/Ocramius/ProxyManager/blob/master/docs/tuning-for-production.md).
See [Tuning ProxyManager for Production](tuning-for-production.md).

View File

@@ -1,3 +1,7 @@
---
title: Tuning the ProxyManager for production
---
## Tuning the ProxyManager for production
By default, all proxy factories generate the required proxy classes at runtime.

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Autoloader;
use ProxyManager\FileLocator\FileLocatorInterface;
@@ -52,7 +54,7 @@ class Autoloader implements AutoloaderInterface
/**
* {@inheritDoc}
*/
public function __invoke($className)
public function __invoke(string $className) : bool
{
if (class_exists($className, false) || ! $this->classNameInflector->isProxyClassName($className)) {
return false;
@@ -64,6 +66,8 @@ class Autoloader implements AutoloaderInterface
return false;
}
/* @noinspection PhpIncludeInspection */
/* @noinspection UsingInclusionOnceReturnValueInspection */
return (bool) require_once $file;
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Autoloader;
/**
@@ -33,5 +35,5 @@ interface AutoloaderInterface
*
* @return bool
*/
public function __invoke($className);
public function __invoke(string $className) : bool;
}

View File

@@ -16,12 +16,14 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager;
use ProxyManager\Autoloader\Autoloader;
use ProxyManager\Autoloader\AutoloaderInterface;
use ProxyManager\FileLocator\FileLocator;
use ProxyManager\GeneratorStrategy\FileWriterGeneratorStrategy;
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
use ProxyManager\GeneratorStrategy\GeneratorStrategyInterface;
use ProxyManager\Inflector\ClassNameInflector;
use ProxyManager\Inflector\ClassNameInflectorInterface;
@@ -82,37 +84,12 @@ class Configuration
*/
protected $classSignatureGenerator;
/**
* @deprecated deprecated since version 0.5
* @codeCoverageIgnore
*/
public function setAutoGenerateProxies()
{
}
/**
* @return bool
*
* @deprecated deprecated since version 0.5
* @codeCoverageIgnore
*/
public function doesAutoGenerateProxies()
{
return true;
}
/**
* @param AutoloaderInterface $proxyAutoloader
*/
public function setProxyAutoloader(AutoloaderInterface $proxyAutoloader)
public function setProxyAutoloader(AutoloaderInterface $proxyAutoloader) : void
{
$this->proxyAutoloader = $proxyAutoloader;
}
/**
* @return AutoloaderInterface
*/
public function getProxyAutoloader()
public function getProxyAutoloader() : AutoloaderInterface
{
return $this->proxyAutoloader
?: $this->proxyAutoloader = new Autoloader(
@@ -121,119 +98,75 @@ class Configuration
);
}
/**
* @param string $proxiesNamespace
*/
public function setProxiesNamespace($proxiesNamespace)
public function setProxiesNamespace(string $proxiesNamespace) : void
{
$this->proxiesNamespace = $proxiesNamespace;
}
/**
* @return string
*/
public function getProxiesNamespace()
public function getProxiesNamespace() : string
{
return $this->proxiesNamespace;
}
/**
* @param string $proxiesTargetDir
*/
public function setProxiesTargetDir($proxiesTargetDir)
public function setProxiesTargetDir(string $proxiesTargetDir) : void
{
$this->proxiesTargetDir = (string) $proxiesTargetDir;
$this->proxiesTargetDir = $proxiesTargetDir;
}
/**
* @return string
*/
public function getProxiesTargetDir()
public function getProxiesTargetDir() : string
{
return $this->proxiesTargetDir ?: $this->proxiesTargetDir = sys_get_temp_dir();
}
/**
* @param GeneratorStrategyInterface $generatorStrategy
*/
public function setGeneratorStrategy(GeneratorStrategyInterface $generatorStrategy)
public function setGeneratorStrategy(GeneratorStrategyInterface $generatorStrategy) : void
{
$this->generatorStrategy = $generatorStrategy;
}
/**
* @return GeneratorStrategyInterface
*/
public function getGeneratorStrategy()
public function getGeneratorStrategy() : GeneratorStrategyInterface
{
return $this->generatorStrategy
?: $this->generatorStrategy = new FileWriterGeneratorStrategy(
new FileLocator($this->getProxiesTargetDir())
);
?: $this->generatorStrategy = new EvaluatingGeneratorStrategy();
}
/**
* @param ClassNameInflectorInterface $classNameInflector
*/
public function setClassNameInflector(ClassNameInflectorInterface $classNameInflector)
public function setClassNameInflector(ClassNameInflectorInterface $classNameInflector) : void
{
$this->classNameInflector = $classNameInflector;
}
/**
* @return ClassNameInflectorInterface
*/
public function getClassNameInflector()
public function getClassNameInflector() : ClassNameInflectorInterface
{
return $this->classNameInflector
?: $this->classNameInflector = new ClassNameInflector($this->getProxiesNamespace());
}
/**
* @param SignatureGeneratorInterface $signatureGenerator
*/
public function setSignatureGenerator(SignatureGeneratorInterface $signatureGenerator)
public function setSignatureGenerator(SignatureGeneratorInterface $signatureGenerator) : void
{
$this->signatureGenerator = $signatureGenerator;
}
/**
* @return SignatureGeneratorInterface
*/
public function getSignatureGenerator()
public function getSignatureGenerator() : SignatureGeneratorInterface
{
return $this->signatureGenerator ?: $this->signatureGenerator = new SignatureGenerator();
}
/**
* @param SignatureCheckerInterface $signatureChecker
*/
public function setSignatureChecker(SignatureCheckerInterface $signatureChecker)
public function setSignatureChecker(SignatureCheckerInterface $signatureChecker) : void
{
$this->signatureChecker = $signatureChecker;
}
/**
* @return SignatureCheckerInterface
*/
public function getSignatureChecker()
public function getSignatureChecker() : SignatureCheckerInterface
{
return $this->signatureChecker
?: $this->signatureChecker = new SignatureChecker($this->getSignatureGenerator());
}
/**
* @param ClassSignatureGeneratorInterface $classSignatureGenerator
*/
public function setClassSignatureGenerator(ClassSignatureGeneratorInterface $classSignatureGenerator)
public function setClassSignatureGenerator(ClassSignatureGeneratorInterface $classSignatureGenerator) : void
{
$this->classSignatureGenerator = $classSignatureGenerator;
}
/**
* @return ClassSignatureGeneratorInterface
*/
public function getClassSignatureGenerator()
public function getClassSignatureGenerator() : ClassSignatureGeneratorInterface
{
return $this->classSignatureGenerator
?: new ClassSignatureGenerator($this->getSignatureGenerator());

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Exception;
use BadMethodCallException;
@@ -30,13 +32,8 @@ class DisabledMethodException extends BadMethodCallException implements Exceptio
{
const NAME = __CLASS__;
/**
* @param string $method
*
* @return self
*/
public static function disabledMethod($method)
public static function disabledMethod(string $method) : self
{
return new self(sprintf('Method "%s" is forcefully disabled', (string) $method));
return new self(sprintf('Method "%s" is forcefully disabled', $method));
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Exception;
/**

View File

@@ -16,11 +16,10 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Exception;
use InvalidArgumentException;
use ReflectionClass;
use ReflectionMethod;
use UnexpectedValueException;
/**
@@ -31,13 +30,7 @@ use UnexpectedValueException;
*/
class FileNotWritableException extends UnexpectedValueException implements ExceptionInterface
{
/**
* @param string $fromPath
* @param string $toPath
*
* @return self
*/
public static function fromInvalidMoveOperation($fromPath, $toPath)
public static function fromInvalidMoveOperation(string $fromPath, string $toPath) : self
{
return new self(sprintf(
'Could not move file "%s" to location "%s": '
@@ -48,19 +41,26 @@ class FileNotWritableException extends UnexpectedValueException implements Excep
}
/**
* @deprecated this method is unused, and will be removed in ProxyManager 3.0.0
*
* @param string $path
*
* @return self
*/
public static function fromNonWritableLocation($path)
public static function fromNonWritableLocation($path) : self
{
$messages = array();
$messages = [];
$destination = realpath($path);
if (($destination = realpath($path)) && ! is_file($destination)) {
if (! $destination) {
$messages[] = 'path does not exist';
}
if ($destination && ! is_file($destination)) {
$messages[] = 'exists and is not a file';
}
if (! is_writable($destination)) {
if ($destination && ! is_writable($destination)) {
$messages[] = 'is not writable';
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Exception;
use InvalidArgumentException;
@@ -30,32 +32,17 @@ use ReflectionMethod;
*/
class InvalidProxiedClassException extends InvalidArgumentException implements ExceptionInterface
{
/**
* @param ReflectionClass $reflection
*
* @return self
*/
public static function interfaceNotSupported(ReflectionClass $reflection)
public static function interfaceNotSupported(ReflectionClass $reflection) : self
{
return new self(sprintf('Provided interface "%s" cannot be proxied', $reflection->getName()));
}
/**
* @param ReflectionClass $reflection
*
* @return self
*/
public static function finalClassNotSupported(ReflectionClass $reflection)
public static function finalClassNotSupported(ReflectionClass $reflection) : self
{
return new self(sprintf('Provided class "%s" is final and cannot be proxied', $reflection->getName()));
}
/**
* @param ReflectionClass $reflection
*
* @return self
*/
public static function abstractProtectedMethodsNotSupported(ReflectionClass $reflection)
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",
@@ -63,12 +50,12 @@ class InvalidProxiedClassException extends InvalidArgumentException implements E
implode(
"\n",
array_map(
function (ReflectionMethod $reflectionMethod) {
function (ReflectionMethod $reflectionMethod) : string {
return $reflectionMethod->getDeclaringClass()->getName() . '::' . $reflectionMethod->getName();
},
array_filter(
$reflection->getMethods(),
function (ReflectionMethod $method) {
function (ReflectionMethod $method) : bool {
return $method->isAbstract() && $method->isProtected();
}
)

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Exception;
use InvalidArgumentException;
@@ -28,13 +30,8 @@ use InvalidArgumentException;
*/
class InvalidProxyDirectoryException extends InvalidArgumentException implements ExceptionInterface
{
/**
* @param string $directory
*
* @return self
*/
public static function proxyDirectoryNotFound($directory)
public static function proxyDirectoryNotFound(string $directory) : self
{
return new self(sprintf('Provided directory "%s" does not exist', (string) $directory));
return new self(sprintf('Provided directory "%s" does not exist', $directory));
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Exception;
use LogicException;
@@ -29,12 +31,7 @@ use ReflectionProperty;
*/
class UnsupportedProxiedClassException extends LogicException implements ExceptionInterface
{
/**
* @param ReflectionProperty $property
*
* @return self
*/
public static function unsupportedLocalizedReflectionProperty(ReflectionProperty $property)
public static function unsupportedLocalizedReflectionProperty(ReflectionProperty $property) : self
{
return new self(
sprintf(

View File

@@ -16,10 +16,15 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Factory;
use ProxyManager\Configuration;
use ProxyManager\Generator\ClassGenerator;
use ProxyManager\ProxyGenerator\ProxyGeneratorInterface;
use ProxyManager\Signature\Exception\InvalidSignatureException;
use ProxyManager\Signature\Exception\MissingSignatureException;
use ProxyManager\Version;
use ReflectionClass;
@@ -41,7 +46,7 @@ abstract class AbstractBaseFactory
*
* @var string[]
*/
private $checkedClasses = array();
private $checkedClasses = [];
/**
* @param \ProxyManager\Configuration $configuration
@@ -53,27 +58,39 @@ abstract class AbstractBaseFactory
/**
* Generate a proxy from a class name
* @param string $className
*
* @param string $className
* @param mixed[] $proxyOptions
*
* @return string proxy class name
*
* @throws InvalidSignatureException
* @throws MissingSignatureException
* @throws \OutOfBoundsException
*/
protected function generateProxy($className)
protected function generateProxy(string $className, array $proxyOptions = []) : string
{
if (isset($this->checkedClasses[$className])) {
if (\array_key_exists($className, $this->checkedClasses)) {
return $this->checkedClasses[$className];
}
$proxyParameters = array(
$proxyParameters = [
'className' => $className,
'factory' => get_class($this),
'proxyManagerVersion' => Version::VERSION
);
'proxyManagerVersion' => Version::getVersion(),
];
$proxyClassName = $this
->configuration
->getClassNameInflector()
->getProxyClassName($className, $proxyParameters);
if (! class_exists($proxyClassName)) {
$this->generateProxyClass($proxyClassName, $className, $proxyParameters);
$this->generateProxyClass(
$proxyClassName,
$className,
$proxyParameters,
$proxyOptions
);
}
$this
@@ -84,29 +101,33 @@ abstract class AbstractBaseFactory
return $this->checkedClasses[$className] = $proxyClassName;
}
/**
* @return \ProxyManager\ProxyGenerator\ProxyGeneratorInterface
*/
abstract protected function getGenerator();
abstract protected function getGenerator() : ProxyGeneratorInterface;
/**
* Generates the provided `$proxyClassName` from the given `$className` and `$proxyParameters`
* @param string $proxyClassName
* @param string $className
* @param array $proxyParameters
*
* @return void
* @param string $proxyClassName
* @param string $className
* @param array $proxyParameters
* @param mixed[] $proxyOptions
*/
private function generateProxyClass($proxyClassName, $className, array $proxyParameters)
{
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);
$this->getGenerator()->generate(new ReflectionClass($className), $phpClass, $proxyOptions);
$phpClass = $this->configuration->getClassSignatureGenerator()->addSignature($phpClass, $proxyParameters);
$this->configuration->getGeneratorStrategy()->generate($phpClass);
$this->configuration->getProxyAutoloader()->__invoke($proxyClassName);
$this->configuration->getGeneratorStrategy()->generate($phpClass, $proxyOptions);
$autoloader = $this->configuration->getProxyAutoloader();
$autoloader($proxyClassName);
}
}

View File

@@ -16,9 +16,15 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Factory;
use ProxyManager\Proxy\AccessInterceptorInterface;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizerGenerator;
use ProxyManager\ProxyGenerator\ProxyGeneratorInterface;
use ProxyManager\Signature\Exception\InvalidSignatureException;
use ProxyManager\Signature\Exception\MissingSignatureException;
/**
* Factory responsible of producing proxy objects
@@ -40,19 +46,26 @@ class AccessInterceptorScopeLocalizerFactory extends AbstractBaseFactory
* @param \Closure[] $suffixInterceptors an array (indexed by method name) of interceptor closures to be called
* after method logic is executed
*
* @return \ProxyManager\Proxy\AccessInterceptorInterface
* @return AccessInterceptorInterface
*
* @throws InvalidSignatureException
* @throws MissingSignatureException
* @throws \OutOfBoundsException
*/
public function createProxy($instance, array $prefixInterceptors = array(), array $suffixInterceptors = array())
{
public function createProxy(
$instance,
array $prefixInterceptors = [],
array $suffixInterceptors = []
) : AccessInterceptorInterface {
$proxyClassName = $this->generateProxy(get_class($instance));
return new $proxyClassName($instance, $prefixInterceptors, $suffixInterceptors);
return $proxyClassName::staticProxyConstructor($instance, $prefixInterceptors, $suffixInterceptors);
}
/**
* {@inheritDoc}
*/
protected function getGenerator()
protected function getGenerator() : ProxyGeneratorInterface
{
return $this->generator ?: $this->generator = new AccessInterceptorScopeLocalizerGenerator();
}

View File

@@ -16,9 +16,15 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Factory;
use ProxyManager\Proxy\AccessInterceptorValueHolderInterface;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolderGenerator;
use ProxyManager\ProxyGenerator\ProxyGeneratorInterface;
use ProxyManager\Signature\Exception\InvalidSignatureException;
use ProxyManager\Signature\Exception\MissingSignatureException;
/**
* Factory responsible of producing proxy objects
@@ -40,19 +46,26 @@ class AccessInterceptorValueHolderFactory extends AbstractBaseFactory
* @param \Closure[] $suffixInterceptors an array (indexed by method name) of interceptor closures to be called
* after method logic is executed
*
* @return \ProxyManager\Proxy\AccessInterceptorInterface|\ProxyManager\Proxy\ValueHolderInterface
* @return AccessInterceptorValueHolderInterface
*
* @throws InvalidSignatureException
* @throws MissingSignatureException
* @throws \OutOfBoundsException
*/
public function createProxy($instance, array $prefixInterceptors = array(), array $suffixInterceptors = array())
{
public function createProxy(
$instance,
array $prefixInterceptors = [],
array $suffixInterceptors = []
) : AccessInterceptorValueHolderInterface {
$proxyClassName = $this->generateProxy(get_class($instance));
return new $proxyClassName($instance, $prefixInterceptors, $suffixInterceptors);
return $proxyClassName::staticProxyConstructor($instance, $prefixInterceptors, $suffixInterceptors);
}
/**
* {@inheritDoc}
*/
protected function getGenerator()
protected function getGenerator() : ProxyGeneratorInterface
{
return $this->generator ?: $this->generator = new AccessInterceptorValueHolderGenerator();
}

View File

@@ -16,19 +16,24 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Factory;
use Closure;
use ProxyManager\Proxy\GhostObjectInterface;
use ProxyManager\ProxyGenerator\LazyLoadingGhostGenerator;
use ProxyManager\ProxyGenerator\ProxyGeneratorInterface;
use ProxyManager\Signature\Exception\InvalidSignatureException;
use ProxyManager\Signature\Exception\MissingSignatureException;
/**
* Factory responsible of producing ghost instances
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*
* @method \ProxyManager\Proxy\GhostObjectInterface createProxy($className, \Closure $initializer)
*/
class LazyLoadingGhostFactory extends AbstractLazyFactory
class LazyLoadingGhostFactory extends AbstractBaseFactory
{
/**
* @var \ProxyManager\ProxyGenerator\LazyLoadingGhostGenerator|null
@@ -38,8 +43,60 @@ class LazyLoadingGhostFactory extends AbstractLazyFactory
/**
* {@inheritDoc}
*/
protected function getGenerator()
protected function getGenerator() : ProxyGeneratorInterface
{
return $this->generator ?: $this->generator = new LazyLoadingGhostGenerator();
}
/**
* 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:
*
* <code>
* $initializer = function (
* GhostObjectInterface $proxy,
* string $method,
* array $parameters,
* & $initializer,
* array $properties
* ) {};
* </code>
*
* 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")
*
* @return GhostObjectInterface
*
* @throws MissingSignatureException
* @throws InvalidSignatureException
* @throws \OutOfBoundsException
*/
public function createProxy(
string $className,
Closure $initializer,
array $proxyOptions = []
) : GhostObjectInterface {
$proxyClassName = $this->generateProxy($className, $proxyOptions);
return $proxyClassName::staticProxyConstructor($initializer);
}
}

View File

@@ -16,29 +16,41 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Factory;
use ProxyManager\Proxy\VirtualProxyInterface;
use ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator;
use ProxyManager\ProxyGenerator\ProxyGeneratorInterface;
/**
* Factory responsible of producing virtual proxy instances
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*
* @method \ProxyManager\Proxy\VirtualProxyInterface createProxy($className, \Closure $initializer)
*/
class LazyLoadingValueHolderFactory extends AbstractLazyFactory
class LazyLoadingValueHolderFactory extends AbstractBaseFactory
{
/**
* @var \ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator|null
*/
private $generator;
public function createProxy(
string $className,
\Closure $initializer,
array $proxyOptions = []
) : VirtualProxyInterface {
$proxyClassName = $this->generateProxy($className, $proxyOptions);
return $proxyClassName::staticProxyConstructor($initializer);
}
/**
* {@inheritDoc}
*/
protected function getGenerator()
protected function getGenerator() : ProxyGeneratorInterface
{
return $this->generator ?: $this->generator = new LazyLoadingValueHolderGenerator();
}

View File

@@ -16,9 +16,15 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Factory;
use ProxyManager\Proxy\NullObjectInterface;
use ProxyManager\ProxyGenerator\NullObjectGenerator;
use ProxyManager\ProxyGenerator\ProxyGeneratorInterface;
use ProxyManager\Signature\Exception\InvalidSignatureException;
use ProxyManager\Signature\Exception\MissingSignatureException;
/**
* Factory responsible of producing proxy objects
@@ -36,20 +42,24 @@ class NullObjectFactory extends AbstractBaseFactory
/**
* @param object $instanceOrClassName the object to be wrapped or interface to transform to null object
*
* @return \ProxyManager\Proxy\NullobjectInterface
* @return NullObjectInterface
*
* @throws InvalidSignatureException
* @throws MissingSignatureException
* @throws \OutOfBoundsException
*/
public function createProxy($instanceOrClassName)
public function createProxy($instanceOrClassName) : NullObjectInterface
{
$className = is_object($instanceOrClassName) ? get_class($instanceOrClassName) : $instanceOrClassName;
$proxyClassName = $this->generateProxy($className);
return new $proxyClassName();
return $proxyClassName::staticProxyConstructor();
}
/**
* {@inheritDoc}
*/
protected function getGenerator()
protected function getGenerator() : ProxyGeneratorInterface
{
return $this->generator ?: $this->generator = new NullObjectGenerator();
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Factory\RemoteObject\Adapter;
use ProxyManager\Factory\RemoteObject\AdapterInterface;
@@ -41,7 +43,7 @@ abstract class BaseAdapter implements AdapterInterface
*
* @var string[]
*/
protected $map = array();
protected $map = [];
/**
* Constructor
@@ -49,7 +51,7 @@ abstract class BaseAdapter implements AdapterInterface
* @param Client $client
* @param array $map map of service names to their aliases
*/
public function __construct(Client $client, array $map = array())
public function __construct(Client $client, array $map = [])
{
$this->client = $client;
$this->map = $map;
@@ -58,11 +60,11 @@ abstract class BaseAdapter implements AdapterInterface
/**
* {@inheritDoc}
*/
public function call($wrappedClass, $method, array $params = array())
public function call(string $wrappedClass, string $method, array $params = [])
{
$serviceName = $this->getServiceName($wrappedClass, $method);
if (isset($this->map[$serviceName])) {
if (\array_key_exists($serviceName, $this->map)) {
$serviceName = $this->map[$serviceName];
}
@@ -77,5 +79,5 @@ abstract class BaseAdapter implements AdapterInterface
*
* @return string Service name
*/
abstract protected function getServiceName($wrappedClass, $method);
abstract protected function getServiceName(string $wrappedClass, string $method) : string;
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Factory\RemoteObject\Adapter;
/**
@@ -29,7 +31,7 @@ class JsonRpc extends BaseAdapter
/**
* {@inheritDoc}
*/
protected function getServiceName($wrappedClass, $method)
protected function getServiceName(string $wrappedClass, string $method) : string
{
return $wrappedClass . '.' . $method;
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Factory\RemoteObject\Adapter;
/**
@@ -29,8 +31,8 @@ class Soap extends BaseAdapter
/**
* {@inheritDoc}
*/
protected function getServiceName($wrappedClass, $method)
protected function getServiceName(string $wrappedClass, string $method) : string
{
return (string) $method;
return $method;
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Factory\RemoteObject\Adapter;
/**
@@ -29,7 +31,7 @@ class XmlRpc extends BaseAdapter
/**
* {@inheritDoc}
*/
protected function getServiceName($wrappedClass, $method)
protected function getServiceName(string $wrappedClass, string $method) : string
{
return $wrappedClass . '.' . $method;
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Factory\RemoteObject;
/**
@@ -33,5 +35,5 @@ interface AdapterInterface
* @param string $method
* @param array $params
*/
public function call($wrappedClass, $method, array $params = array());
public function call(string $wrappedClass, string $method, array $params = []);
}

View File

@@ -16,11 +16,17 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Factory;
use ProxyManager\Configuration;
use ProxyManager\Factory\RemoteObject\AdapterInterface;
use ProxyManager\Proxy\RemoteObjectInterface;
use ProxyManager\ProxyGenerator\ProxyGeneratorInterface;
use ProxyManager\ProxyGenerator\RemoteObjectGenerator;
use ProxyManager\Signature\Exception\InvalidSignatureException;
use ProxyManager\Signature\Exception\MissingSignatureException;
/**
* Factory responsible of producing remote proxy objects
@@ -56,20 +62,25 @@ class RemoteObjectFactory extends AbstractBaseFactory
/**
* @param string|object $instanceOrClassName
*
* @return \ProxyManager\Proxy\RemoteObjectInterface
* @return RemoteObjectInterface
*
* @throws InvalidSignatureException
* @throws MissingSignatureException
* @throws \OutOfBoundsException
*/
public function createProxy($instanceOrClassName)
public function createProxy($instanceOrClassName) : RemoteObjectInterface
{
$className = is_object($instanceOrClassName) ? get_class($instanceOrClassName) : $instanceOrClassName;
$proxyClassName = $this->generateProxy($className);
$proxyClassName = $this->generateProxy(
is_object($instanceOrClassName) ? get_class($instanceOrClassName) : $instanceOrClassName
);
return new $proxyClassName($this->adapter);
return $proxyClassName::staticProxyConstructor($this->adapter);
}
/**
* {@inheritDoc}
*/
protected function getGenerator()
protected function getGenerator() : ProxyGeneratorInterface
{
return $this->generator ?: $this->generator = new RemoteObjectGenerator();
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\FileLocator;
use ProxyManager\Exception\InvalidProxyDirectoryException;
@@ -38,19 +40,21 @@ class FileLocator implements FileLocatorInterface
*
* @throws \ProxyManager\Exception\InvalidProxyDirectoryException
*/
public function __construct($proxiesDirectory)
public function __construct(string $proxiesDirectory)
{
$this->proxiesDirectory = realpath($proxiesDirectory);
$absolutePath = realpath($proxiesDirectory);
if (false === $this->proxiesDirectory) {
if (false === $absolutePath) {
throw InvalidProxyDirectoryException::proxyDirectoryNotFound($proxiesDirectory);
}
$this->proxiesDirectory = $absolutePath;
}
/**
* {@inheritDoc}
*/
public function getProxyFileName($className)
public function getProxyFileName(string $className) : string
{
return $this->proxiesDirectory . DIRECTORY_SEPARATOR . str_replace('\\', '', $className) . '.php';
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\FileLocator;
/**
@@ -33,5 +35,5 @@ interface FileLocatorInterface
*
* @return string
*/
public function getProxyFileName($className);
public function getProxyFileName(string $className) : string;
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Generator;
use Zend\Code\Generator\ClassGenerator as ZendClassGenerator;
@@ -31,7 +33,7 @@ class ClassGenerator extends ZendClassGenerator
/**
* {@inheritDoc}
*/
public function setExtendedClass($extendedClass)
public function setExtendedClass($extendedClass) : parent
{
if ($extendedClass) {
$extendedClass = '\\' . trim($extendedClass, '\\');
@@ -43,7 +45,7 @@ class ClassGenerator extends ZendClassGenerator
/**
* {@inheritDoc}
*/
public function setImplementedInterfaces(array $interfaces)
public function setImplementedInterfaces(array $interfaces) : parent
{
foreach ($interfaces as & $interface) {
$interface = '\\' . trim($interface, '\\');

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Generator;
use ReflectionClass;
@@ -33,7 +35,7 @@ class MagicMethodGenerator extends MethodGenerator
* @param string $name
* @param array $parameters
*/
public function __construct(ReflectionClass $originalClass, $name, array $parameters = array())
public function __construct(ReflectionClass $originalClass, string $name, array $parameters = [])
{
parent::__construct(
$name,

View File

@@ -16,9 +16,10 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Generator;
use Zend\Code\Generator\DocBlockGenerator;
use Zend\Code\Generator\MethodGenerator as ZendMethodGenerator;
use Zend\Code\Reflection\MethodReflection;
@@ -31,131 +32,15 @@ use Zend\Code\Reflection\MethodReflection;
class MethodGenerator extends ZendMethodGenerator
{
/**
* @var bool
*/
protected $returnsReference = false;
/**
* @param boolean $returnsReference
*/
public function setReturnsReference($returnsReference)
{
$this->returnsReference = (bool) $returnsReference;
}
/**
* @return boolean
*/
public function returnsReference()
{
return $this->returnsReference;
}
/**
* @override enforces generation of \ProxyManager\Generator\MethodGenerator
*
* {@inheritDoc}
*/
public static function fromReflection(MethodReflection $reflectionMethod)
public static function fromReflection(MethodReflection $reflectionMethod) : self
{
/* @var $method self */
$method = new static();
$method = parent::fromReflection($reflectionMethod);
$method->setSourceContent($reflectionMethod->getContents(false));
$method->setSourceDirty(false);
if ($reflectionMethod->getDocComment() != '') {
$method->setDocBlock(DocBlockGenerator::fromReflection($reflectionMethod->getDocBlock()));
}
$method->setFinal($reflectionMethod->isFinal());
$method->setVisibility(self::extractVisibility($reflectionMethod));
foreach ($reflectionMethod->getParameters() as $reflectionParameter) {
$method->setParameter(ParameterGenerator::fromReflection($reflectionParameter));
}
$method->setStatic($reflectionMethod->isStatic());
$method->setName($reflectionMethod->getName());
$method->setBody($reflectionMethod->getBody());
$method->setReturnsReference($reflectionMethod->returnsReference());
$method->setInterface(false);
return $method;
}
/**
* Retrieves the visibility for the given method reflection
*
* @param MethodReflection $reflectionMethod
*
* @return string
*/
private static function extractVisibility(MethodReflection $reflectionMethod)
{
if ($reflectionMethod->isPrivate()) {
return static::VISIBILITY_PRIVATE;
}
if ($reflectionMethod->isProtected()) {
return static::VISIBILITY_PROTECTED;
}
return static::VISIBILITY_PUBLIC;
}
/**
* @override fixes by-reference return value in zf2's method generator
*
* {@inheritDoc}
*/
public function generate()
{
$output = '';
$indent = $this->getIndentation();
if (null !== ($docBlock = $this->getDocBlock())) {
$docBlock->setIndentation($indent);
$output .= $docBlock->generate();
}
$output .= $indent . $this->generateMethodDeclaration() . self::LINE_FEED . $indent . '{' . self::LINE_FEED;
if ($this->body) {
$output .= preg_replace('#^(.+?)$#m', $indent . $indent . '$1', trim($this->body))
. self::LINE_FEED;
}
$output .= $indent . '}' . self::LINE_FEED;
return $output;
}
/**
* @return string
*/
private function generateMethodDeclaration()
{
$output = $this->generateVisibility()
. ' function '
. (($this->returnsReference()) ? '& ' : '')
. $this->getName() . '(';
$parameterOutput = array();
foreach ($this->getParameters() as $parameter) {
$parameterOutput[] = $parameter->generate();
}
return $output . implode(', ', $parameterOutput) . ')';
}
/**
* @return string
*/
private function generateVisibility()
{
return $this->isAbstract() ? 'abstract ' : (($this->isFinal()) ? 'final ' : '')
. ($this->getVisibility() . (($this->isStatic()) ? ' static' : ''));
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Generator\Util;
use ReflectionClass;
@@ -30,18 +32,11 @@ use Zend\Code\Generator\MethodGenerator;
*/
final class ClassGeneratorUtils
{
/**
* @param ReflectionClass $originalClass
* @param ClassGenerator $classGenerator
* @param MethodGenerator $generatedMethod
*
* @return void|false
*/
public static function addMethodIfNotFinal(
ReflectionClass $originalClass,
ClassGenerator $classGenerator,
MethodGenerator $generatedMethod
) {
) : bool {
$methodName = $generatedMethod->getName();
if ($originalClass->hasMethod($methodName) && $originalClass->getMethod($methodName)->isFinal()) {
@@ -49,5 +44,7 @@ final class ClassGeneratorUtils
}
$classGenerator->addMethodFromGenerator($generatedMethod);
return true;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Generator\Util;
/**
* Utility class to generate return expressions in method, given a method signature.
*
* This is required since return expressions may be forbidden by the method signature (void).
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
final class ProxiedMethodReturnExpression
{
public static function generate(string $returnedValueExpression, ?\ReflectionMethod $originalMethod) : string
{
if ($originalMethod && 'void' === (string) $originalMethod->getReturnType()) {
return $returnedValueExpression . ";\nreturn;";
}
return 'return ' . $returnedValueExpression . ';';
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Generator\Util;
/**
@@ -37,7 +39,7 @@ abstract class UniqueIdentifierGenerator
*
* @return string
*/
public static function getIdentifier($name)
public static function getIdentifier(string $name) : string
{
return str_replace(
'.',

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\GeneratorStrategy;
use Zend\Code\Generator\ClassGenerator;
@@ -31,7 +33,7 @@ class BaseGeneratorStrategy implements GeneratorStrategyInterface
/**
* {@inheritDoc}
*/
public function generate(ClassGenerator $classGenerator)
public function generate(ClassGenerator $classGenerator) : string
{
return $classGenerator->generate();
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\GeneratorStrategy;
use Zend\Code\Generator\ClassGenerator;
@@ -38,7 +40,9 @@ class EvaluatingGeneratorStrategy implements GeneratorStrategyInterface
*/
public function __construct()
{
// @codeCoverageIgnoreStart
$this->canEval = ! ini_get('suhosin.executor.disable_eval');
// @codeCoverageIgnoreEnd
}
/**
@@ -46,21 +50,22 @@ class EvaluatingGeneratorStrategy implements GeneratorStrategyInterface
*
* {@inheritDoc}
*/
public function generate(ClassGenerator $classGenerator)
public function generate(ClassGenerator $classGenerator) : string
{
$code = $classGenerator->generate();
// @codeCoverageIgnoreStart
if (! $this->canEval) {
// @codeCoverageIgnoreStart
$fileName = sys_get_temp_dir() . '/EvaluatingGeneratorStrategy.php.tmp.' . uniqid('', true);
$fileName = tempnam(sys_get_temp_dir(), 'EvaluatingGeneratorStrategy.php.tmp.');
file_put_contents($fileName, "<?php\n" . $code);
/* @noinspection PhpIncludeInspection */
require $fileName;
unlink($fileName);
return $code;
// @codeCoverageIgnoreEnd
}
// @codeCoverageIgnoreEnd
eval($code);

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\GeneratorStrategy;
use ProxyManager\Exception\FileNotWritableException;
@@ -56,8 +58,10 @@ class FileWriterGeneratorStrategy implements GeneratorStrategyInterface
* Write generated code to disk and return the class code
*
* {@inheritDoc}
*
* @throws FileNotWritableException
*/
public function generate(ClassGenerator $classGenerator)
public function generate(ClassGenerator $classGenerator) : string
{
$className = trim($classGenerator->getNamespaceName(), '\\')
. '\\' . trim($classGenerator->getName(), '\\');
@@ -68,15 +72,11 @@ class FileWriterGeneratorStrategy implements GeneratorStrategyInterface
try {
$this->writeFile("<?php\n\n" . $generatedCode, $fileName);
} catch (FileNotWritableException $fileNotWritable) {
return $generatedCode;
} finally {
restore_error_handler();
throw $fileNotWritable;
}
restore_error_handler();
return $generatedCode;
}
/**
@@ -88,13 +88,11 @@ class FileWriterGeneratorStrategy implements GeneratorStrategyInterface
*
* @throws FileNotWritableException
*/
private function writeFile($source, $location)
private function writeFile(string $source, string $location) : void
{
$tmpFileName = $location . '.' . uniqid('', true);
$tmpFileName = tempnam($location, 'temporaryProxyManagerFile');
if (! file_put_contents($tmpFileName, $source)) {
throw FileNotWritableException::fromNonWritableLocation($tmpFileName);
}
file_put_contents($tmpFileName, $source);
if (! rename($tmpFileName, $location)) {
unlink($tmpFileName);

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\GeneratorStrategy;
use Zend\Code\Generator\ClassGenerator;
@@ -35,5 +37,5 @@ interface GeneratorStrategyInterface
*
* @return string the class body
*/
public function generate(ClassGenerator $classGenerator);
public function generate(ClassGenerator $classGenerator) : string;
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Inflector;
use ProxyManager\Inflector\Util\ParameterHasher;
@@ -51,9 +53,9 @@ final class ClassNameInflector implements ClassNameInflectorInterface
/**
* @param string $proxyNamespace
*/
public function __construct($proxyNamespace)
public function __construct(string $proxyNamespace)
{
$this->proxyNamespace = (string) $proxyNamespace;
$this->proxyNamespace = $proxyNamespace;
$this->proxyMarker = '\\' . static::PROXY_MARKER . '\\';
$this->proxyMarkerLength = strlen($this->proxyMarker);
$this->parameterHasher = new ParameterHasher();
@@ -62,7 +64,7 @@ final class ClassNameInflector implements ClassNameInflectorInterface
/**
* {@inheritDoc}
*/
public function getUserClassName($className)
public function getUserClassName(string $className) : string
{
$className = ltrim($className, '\\');
@@ -80,7 +82,7 @@ final class ClassNameInflector implements ClassNameInflectorInterface
/**
* {@inheritDoc}
*/
public function getProxyClassName($className, array $options = array())
public function getProxyClassName(string $className, array $options = []) : string
{
return $this->proxyNamespace
. $this->proxyMarker
@@ -91,7 +93,7 @@ final class ClassNameInflector implements ClassNameInflectorInterface
/**
* {@inheritDoc}
*/
public function isProxyClassName($className)
public function isProxyClassName(string $className) : bool
{
return false !== strrpos($className, $this->proxyMarker);
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Inflector;
/**
@@ -38,7 +40,7 @@ interface ClassNameInflectorInterface
*
* @return string
*/
public function getUserClassName($className);
public function getUserClassName(string $className) : string;
/**
* Retrieve the class name of the proxy for the given user-defined class name
@@ -48,7 +50,7 @@ interface ClassNameInflectorInterface
*
* @return string
*/
public function getProxyClassName($className, array $options = array());
public function getProxyClassName(string $className, array $options = []) : string;
/**
* Retrieve whether the provided class name is a proxy
@@ -57,5 +59,5 @@ interface ClassNameInflectorInterface
*
* @return bool
*/
public function isProxyClassName($className);
public function isProxyClassName(string $className) : bool;
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Inflector\Util;
/**
@@ -34,7 +36,7 @@ class ParameterEncoder
*
* @return string
*/
public function encodeParameters(array $parameters)
public function encodeParameters(array $parameters) : string
{
return base64_encode(serialize($parameters));
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Inflector\Util;
/**
@@ -33,7 +35,7 @@ class ParameterHasher
*
* @return string
*/
public function hashParameters(array $parameters)
public function hashParameters(array $parameters) : string
{
return md5(serialize($parameters));
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Proxy;
/**
@@ -34,7 +36,7 @@ interface AccessInterceptorInterface extends ProxyInterface
* A prefix interceptor should have a signature like following:
*
* <code>
* $prefixInterceptor = function ($proxy, $instance, $method, $params, & $returnEarly) {};
* $interceptor = function ($proxy, $instance, string $method, array $params, & $returnEarly) {};
* </code>
*
* @param string $methodName name of the intercepted method
@@ -42,7 +44,7 @@ interface AccessInterceptorInterface extends ProxyInterface
*
* @return void
*/
public function setMethodPrefixInterceptor($methodName, \Closure $prefixInterceptor = null);
public function setMethodPrefixInterceptor(string $methodName, \Closure $prefixInterceptor = null);
/**
* Set or remove the suffix interceptor for a method
@@ -52,7 +54,7 @@ interface AccessInterceptorInterface extends ProxyInterface
* A prefix interceptor should have a signature like following:
*
* <code>
* $suffixInterceptor = function ($proxy, $instance, $method, $params, $returnValue, & $returnEarly) {};
* $interceptor = function ($proxy, $instance, string $method, array $params, $returnValue, & $returnEarly) {};
* </code>
*
* @param string $methodName name of the intercepted method
@@ -60,5 +62,5 @@ interface AccessInterceptorInterface extends ProxyInterface
*
* @return void
*/
public function setMethodSuffixInterceptor($methodName, \Closure $suffixInterceptor = null);
public function setMethodSuffixInterceptor(string $methodName, \Closure $suffixInterceptor = null);
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Proxy;
/**
* Aggregates AccessInterceptor and ValueHolderInterface, mostly for return type hinting
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
interface AccessInterceptorValueHolderInterface extends AccessInterceptorInterface, ValueHolderInterface
{
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Proxy\Exception;
use RuntimeException;

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Proxy;
/**

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Proxy;
/**

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Proxy;
/**
@@ -34,7 +36,7 @@ interface LazyLoadingInterface extends ProxyInterface
* An initializer should have a signature like following:
*
* <code>
* $initializer = function (& $wrappedObject, $proxy, $method, $parameters, & $initializer) {};
* $initializer = function (& $wrappedObject, $proxy, string $method, array $parameters, & $initializer) {};
* </code>
*
* @param \Closure|null $initializer
@@ -53,12 +55,12 @@ interface LazyLoadingInterface extends ProxyInterface
*
* @return bool true if the proxy could be initialized
*/
public function initializeProxy();
public function initializeProxy() : bool;
/**
* Retrieves current initialization status of the proxy
*
* @return bool
*/
public function isProxyInitialized();
public function isProxyInitialized() : bool;
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Proxy;
/**

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Proxy;
/**

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Proxy;
/**

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Proxy;
/**

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Proxy;
/**

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\Proxy;
/**

View File

@@ -16,11 +16,14 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\ProxyGenerator\Util\Properties;
use ProxyManager\ProxyGenerator\Util\UnsetPropertiesGenerator;
use ReflectionClass;
use ReflectionProperty;
/**
* Magic `__wakeup` for lazy loading value holder objects
@@ -32,19 +35,16 @@ class MagicWakeup extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
*/
public function __construct(ReflectionClass $originalClass)
{
parent::__construct($originalClass, '__wakeup');
/* @var $publicProperties \ReflectionProperty[] */
$publicProperties = $originalClass->getProperties(ReflectionProperty::IS_PUBLIC);
$unsetProperties = array();
foreach ($publicProperties as $publicProperty) {
$unsetProperties[] = '$this->' . $publicProperty->getName();
}
$this->setBody($unsetProperties ? 'unset(' . implode(', ', $unsetProperties) . ");" : '');
$this->setBody(UnsetPropertiesGenerator::generateSnippet(
Properties::fromReflectionClass($originalClass),
'this'
));
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator;
use Closure;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use Zend\Code\Generator\PropertyGenerator;
/**
@@ -33,6 +36,10 @@ class SetMethodPrefixInterceptor extends MethodGenerator
{
/**
* Constructor
*
* @param PropertyGenerator $prefixInterceptor
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(PropertyGenerator $prefixInterceptor)
{
@@ -40,11 +47,11 @@ class SetMethodPrefixInterceptor extends MethodGenerator
$interceptor = new ParameterGenerator('prefixInterceptor');
$interceptor->setType('Closure');
$interceptor->setType(Closure::class);
$interceptor->setDefaultValue(null);
$this->setParameter(new ParameterGenerator('methodName'));
$this->setParameter(new ParameterGenerator('methodName', 'string'));
$this->setParameter($interceptor);
$this->setDocblock('{@inheritDoc}');
$this->setDocBlock('{@inheritDoc}');
$this->setBody('$this->' . $prefixInterceptor->getName() . '[$methodName] = $prefixInterceptor;');
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator;
use Closure;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use Zend\Code\Generator\PropertyGenerator;
/**
@@ -33,6 +36,10 @@ class SetMethodSuffixInterceptor extends MethodGenerator
{
/**
* Constructor
*
* @param PropertyGenerator $suffixInterceptor
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(PropertyGenerator $suffixInterceptor)
{
@@ -40,11 +47,11 @@ class SetMethodSuffixInterceptor extends MethodGenerator
$interceptor = new ParameterGenerator('suffixInterceptor');
$interceptor->setType('Closure');
$interceptor->setType(Closure::class);
$interceptor->setDefaultValue(null);
$this->setParameter(new ParameterGenerator('methodName'));
$this->setParameter(new ParameterGenerator('methodName', 'string'));
$this->setParameter($interceptor);
$this->setDocblock('{@inheritDoc}');
$this->setDocBlock('{@inheritDoc}');
$this->setBody('$this->' . $suffixInterceptor->getName() . '[$methodName] = $suffixInterceptor;');
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptor\PropertyGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
@@ -31,13 +33,15 @@ class MethodPrefixInterceptors extends PropertyGenerator
{
/**
* Constructor
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct()
{
parent::__construct(UniqueIdentifierGenerator::getIdentifier('methodPrefixInterceptors'));
$this->setDefaultValue(array());
$this->setDefaultValue([]);
$this->setVisibility(self::VISIBILITY_PRIVATE);
$this->setDocblock('@var \\Closure[] map of interceptors to be called per-method before execution');
$this->setDocBlock('@var \\Closure[] map of interceptors to be called per-method before execution');
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptor\PropertyGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
@@ -31,13 +33,15 @@ class MethodSuffixInterceptors extends PropertyGenerator
{
/**
* Constructor
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct()
{
parent::__construct(UniqueIdentifierGenerator::getIdentifier('methodSuffixInterceptors'));
$this->setDefaultValue(array());
$this->setDefaultValue([]);
$this->setVisibility(self::VISIBILITY_PRIVATE);
$this->setDocblock('@var \\Closure[] map of interceptors to be called per-method after execution');
$this->setDocBlock('@var \\Closure[] map of interceptors to be called per-method after execution');
}
}

View File

@@ -0,0 +1,89 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\Util\Properties;
use ReflectionClass;
use Zend\Code\Generator\PropertyGenerator;
/**
* The `bindProxyProperties` method implementation for access interceptor scope localizers
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
class BindProxyProperties extends MethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
parent::__construct(
'bindProxyProperties',
[
new ParameterGenerator('localizedObject', $originalClass->getName()),
new ParameterGenerator('prefixInterceptors', 'array', []),
new ParameterGenerator('suffixInterceptors', 'array', []),
],
static::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);
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;"
);
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
@@ -37,30 +39,31 @@ class InterceptedMethod extends MethodGenerator
* @param \Zend\Code\Generator\PropertyGenerator $suffixInterceptors
*
* @return self
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public static function generateMethod(
MethodReflection $originalMethod,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
) : self {
/* @var $method self */
$method = static::fromReflection($originalMethod);
$forwardedParams = array();
$forwardedParams = [];
foreach ($originalMethod->getParameters() as $parameter) {
$forwardedParams[] = '$' . $parameter->getName();
$forwardedParams[] = ($parameter->isVariadic() ? '...' : '') . '$' . $parameter->getName();
}
$method->setDocblock('{@inheritDoc}');
$method->setBody(
InterceptorGenerator::createInterceptedMethodBody(
'$returnValue = parent::'
. $originalMethod->getName() . '(' . implode(', ', $forwardedParams) . ');',
$method,
$prefixInterceptors,
$suffixInterceptors
)
);
$method->setDocBlock('{@inheritDoc}');
$method->setBody(InterceptorGenerator::createInterceptedMethodBody(
'$returnValue = parent::'
. $originalMethod->getName() . '(' . implode(', ', $forwardedParams) . ');',
$method,
$prefixInterceptors,
$suffixInterceptors,
$originalMethod
));
return $method;
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use ReflectionClass;
use Zend\Code\Generator\PropertyGenerator;
@@ -33,6 +36,10 @@ class MagicClone extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
*/
public function __construct(
ReflectionClass $originalClass,
@@ -41,13 +48,14 @@ class MagicClone extends MagicMethodGenerator
) {
parent::__construct($originalClass, '__clone');
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$originalClass->hasMethod('__clone') ? '$returnValue = parent::__clone();' : '$returnValue = null;',
$this,
$prefixInterceptors,
$suffixInterceptors
)
);
$parent = GetMethodIfExists::get($originalClass, '__clone');
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$parent ? '$returnValue = parent::__clone();' : '$returnValue = null;',
$this,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
use ReflectionClass;
@@ -37,21 +40,24 @@ class MagicGet extends MagicMethodGenerator
* @param ReflectionClass $originalClass
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
parent::__construct($originalClass, '__get', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__get', [new ParameterGenerator('name')]);
$override = $originalClass->hasMethod('__get');
$parent = GetMethodIfExists::get($originalClass, '__get');
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name');
if ($override) {
$callParent = '$returnValue = & parent::__get($name);';
} else {
$callParent = '$returnValue = & parent::__get($name);';
if (! $parent) {
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_GET,
'name',
@@ -61,13 +67,12 @@ class MagicGet extends MagicMethodGenerator
);
}
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors
)
);
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
use ReflectionClass;
@@ -37,21 +40,24 @@ class MagicIsset extends MagicMethodGenerator
* @param ReflectionClass $originalClass
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
parent::__construct($originalClass, '__isset', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__isset', [new ParameterGenerator('name')]);
$override = $originalClass->hasMethod('__isset');
$parent = GetMethodIfExists::get($originalClass, '__isset');
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name');
if ($override) {
$callParent = '$returnValue = & parent::__isset($name);';
} else {
$callParent = '$returnValue = & parent::__isset($name);';
if (! $parent) {
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_ISSET,
'name',
@@ -61,13 +67,12 @@ class MagicIsset extends MagicMethodGenerator
);
}
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors
)
);
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
use ReflectionClass;
@@ -37,6 +40,9 @@ class MagicSet extends MagicMethodGenerator
* @param \ReflectionClass $originalClass
* @param \Zend\Code\Generator\PropertyGenerator $prefixInterceptors
* @param \Zend\Code\Generator\PropertyGenerator $suffixInterceptors
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
@@ -46,16 +52,16 @@ class MagicSet extends MagicMethodGenerator
parent::__construct(
$originalClass,
'__set',
array(new ParameterGenerator('name'), new ParameterGenerator('value'))
[new ParameterGenerator('name'), new ParameterGenerator('value')]
);
$override = $originalClass->hasMethod('__set');
$parent = GetMethodIfExists::get($originalClass, '__set');
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name');
if ($override) {
$callParent = '$returnValue = & parent::__set($name, $value);';
} else {
$callParent = '$returnValue = & parent::__set($name, $value);';
if (! $parent) {
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_SET,
'name',
@@ -65,13 +71,12 @@ class MagicSet extends MagicMethodGenerator
);
}
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors
)
);
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use ReflectionClass;
use Zend\Code\Generator\PropertyGenerator;
@@ -33,6 +36,10 @@ class MagicSleep extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
*/
public function __construct(
ReflectionClass $originalClass,
@@ -41,17 +48,16 @@ class MagicSleep extends MagicMethodGenerator
) {
parent::__construct($originalClass, '__sleep');
$callParent = $originalClass->hasMethod('__sleep')
? '$returnValue = & parent::__sleep();'
: '$returnValue = array_keys((array) $this);';
$parent = GetMethodIfExists::get($originalClass, '__sleep');
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors
)
);
$callParent = $parent ? '$returnValue = & parent::__sleep();' : '$returnValue = array_keys((array) $this);';
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
use ReflectionClass;
@@ -37,21 +40,24 @@ class MagicUnset extends MagicMethodGenerator
* @param ReflectionClass $originalClass
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
parent::__construct($originalClass, '__unset', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__unset', [new ParameterGenerator('name')]);
$override = $originalClass->hasMethod('__unset');
$parent = GetMethodIfExists::get($originalClass, '__unset');
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name');
if ($override) {
$callParent = '$returnValue = & parent::__unset($name);';
} else {
$callParent = '$returnValue = & parent::__unset($name);';
if (! $parent) {
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_UNSET,
'name',
@@ -61,13 +67,12 @@ class MagicUnset extends MagicMethodGenerator
);
}
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors
)
);
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -0,0 +1,76 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
use Zend\Code\Generator\ParameterGenerator;
use ReflectionClass;
/**
* The `staticProxyConstructor` implementation for an access interceptor scope localizer proxy
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
class StaticProxyConstructor extends MethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(ReflectionClass $originalClass)
{
parent::__construct('staticProxyConstructor', [], static::FLAG_PUBLIC | static::FLAG_STATIC);
$localizedObject = new ParameterGenerator('localizedObject');
$prefix = new ParameterGenerator('prefixInterceptors');
$suffix = new ParameterGenerator('suffixInterceptors');
$localizedObject->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 ?: $reflection = new \ReflectionClass(__CLASS__);' . "\n"
. '$instance = $reflection->newInstanceWithoutConstructor();' . "\n\n"
. '$instance->bindProxyProperties($localizedObject, $prefixInterceptors, $suffixInterceptors);' . "\n\n"
. 'return $instance;'
);
}
}

View File

@@ -16,9 +16,12 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\Util;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\Generator\Util\ProxiedMethodReturnExpression;
use Zend\Code\Generator\PropertyGenerator;
/**
@@ -27,8 +30,7 @@ use Zend\Code\Generator\PropertyGenerator;
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*
* @internal - this class is just here as a small utility for this component,
* don't use it in your own code
* @private - this class is just here as a small utility for this component, don't use it in your own code
*/
class InterceptorGenerator
{
@@ -39,19 +41,21 @@ class InterceptorGenerator
* @param \ProxyManager\Generator\MethodGenerator $method
* @param \Zend\Code\Generator\PropertyGenerator $prefixInterceptors
* @param \Zend\Code\Generator\PropertyGenerator $suffixInterceptors
* @param \ReflectionMethod|null $originalMethod
*
* @return string
*/
public static function createInterceptedMethodBody(
$methodBody,
string $methodBody,
MethodGenerator $method,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
$name = var_export($method->getName(), true);
$prefixInterceptors = $prefixInterceptors->getName();
$suffixInterceptors = $suffixInterceptors->getName();
$params = array();
PropertyGenerator $suffixInterceptors,
?\ReflectionMethod $originalMethod
) : string {
$name = var_export($method->getName(), true);
$prefixInterceptorsName = $prefixInterceptors->getName();
$suffixInterceptorsName = $suffixInterceptors->getName();
$params = [];
foreach ($method->getParameters() as $parameter) {
$parameterName = $parameter->getName();
@@ -60,23 +64,23 @@ class InterceptorGenerator
$paramsString = 'array(' . implode(', ', $params) . ')';
return "if (isset(\$this->$prefixInterceptors" . "[$name])) {\n"
return "if (isset(\$this->$prefixInterceptorsName" . "[$name])) {\n"
. " \$returnEarly = false;\n"
. " \$prefixReturnValue = \$this->$prefixInterceptors" . "[$name]->__invoke("
. " \$prefixReturnValue = \$this->$prefixInterceptorsName" . "[$name]->__invoke("
. "\$this, \$this, $name, $paramsString, \$returnEarly);\n\n"
. " if (\$returnEarly) {\n"
. " return \$prefixReturnValue;\n"
. ' ' . ProxiedMethodReturnExpression::generate('$prefixReturnValue', $originalMethod) . "\n"
. " }\n"
. "}\n\n"
. $methodBody . "\n\n"
. "if (isset(\$this->$suffixInterceptors" . "[$name])) {\n"
. "if (isset(\$this->$suffixInterceptorsName" . "[$name])) {\n"
. " \$returnEarly = false;\n"
. " \$suffixReturnValue = \$this->$suffixInterceptors" . "[$name]->__invoke("
. " \$suffixReturnValue = \$this->$suffixInterceptorsName" . "[$name]->__invoke("
. "\$this, \$this, $name, $paramsString, \$returnValue, \$returnEarly);\n\n"
. " if (\$returnEarly) {\n"
. " return \$suffixReturnValue;\n"
. ' ' . ProxiedMethodReturnExpression::generate('$suffixReturnValue', $originalMethod) . "\n"
. " }\n"
. "}\n\n"
. "return \$returnValue;";
. ProxiedMethodReturnExpression::generate('$returnValue', $originalMethod);
}
}

View File

@@ -16,13 +16,18 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator;
use ProxyManager\Exception\InvalidProxiedClassException;
use ProxyManager\Generator\Util\ClassGeneratorUtils;
use ProxyManager\Proxy\AccessInterceptorInterface;
use ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator\SetMethodPrefixInterceptor;
use ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator\SetMethodSuffixInterceptor;
use ProxyManager\ProxyGenerator\AccessInterceptor\PropertyGenerator\MethodPrefixInterceptors;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\Constructor;
use ProxyManager\ProxyGenerator\AccessInterceptor\PropertyGenerator\MethodSuffixInterceptors;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\BindProxyProperties;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\InterceptedMethod;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\MagicClone;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\MagicGet;
@@ -30,6 +35,7 @@ use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\MagicSet;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\MagicSleep;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\MagicUnset;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\StaticProxyConstructor;
use ProxyManager\ProxyGenerator\Assertion\CanProxyAssertion;
use ProxyManager\ProxyGenerator\Util\ProxiedMethodsFilter;
use ReflectionClass;
@@ -51,15 +57,19 @@ class AccessInterceptorScopeLocalizerGenerator implements ProxyGeneratorInterfac
{
/**
* {@inheritDoc}
*
* @throws \InvalidArgumentException
* @throws InvalidProxiedClassException
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator)
{
CanProxyAssertion::assertClassCanBeProxied($originalClass, false);
$classGenerator->setExtendedClass($originalClass->getName());
$classGenerator->setImplementedInterfaces(array('ProxyManager\\Proxy\\AccessInterceptorInterface'));
$classGenerator->setImplementedInterfaces([AccessInterceptorInterface::class]);
$classGenerator->addPropertyFromGenerator($prefixInterceptors = new MethodPrefixInterceptors());
$classGenerator->addPropertyFromGenerator($suffixInterceptors = new MethodPrefixInterceptors());
$classGenerator->addPropertyFromGenerator($suffixInterceptors = new MethodSuffixInterceptors());
array_map(
function (MethodGenerator $generatedMethod) use ($originalClass, $classGenerator) {
@@ -67,20 +77,15 @@ class AccessInterceptorScopeLocalizerGenerator implements ProxyGeneratorInterfac
},
array_merge(
array_map(
function (ReflectionMethod $method) use ($prefixInterceptors, $suffixInterceptors) {
return InterceptedMethod::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()),
$prefixInterceptors,
$suffixInterceptors
);
},
$this->buildMethodInterceptor($prefixInterceptors, $suffixInterceptors),
ProxiedMethodsFilter::getProxiedMethods(
$originalClass,
array('__get', '__set', '__isset', '__unset', '__clone', '__sleep')
['__get', '__set', '__isset', '__unset', '__clone', '__sleep']
)
),
array(
new Constructor($originalClass, $prefixInterceptors, $suffixInterceptors),
[
new StaticProxyConstructor($originalClass),
new BindProxyProperties($originalClass, $prefixInterceptors, $suffixInterceptors),
new SetMethodPrefixInterceptor($prefixInterceptors),
new SetMethodSuffixInterceptor($suffixInterceptors),
new MagicGet($originalClass, $prefixInterceptors, $suffixInterceptors),
@@ -89,8 +94,21 @@ class AccessInterceptorScopeLocalizerGenerator implements ProxyGeneratorInterfac
new MagicUnset($originalClass, $prefixInterceptors, $suffixInterceptors),
new MagicSleep($originalClass, $prefixInterceptors, $suffixInterceptors),
new MagicClone($originalClass, $prefixInterceptors, $suffixInterceptors),
)
]
)
);
}
private function buildMethodInterceptor(
MethodPrefixInterceptors $prefixInterceptors,
MethodSuffixInterceptors $suffixInterceptors
) : callable {
return function (ReflectionMethod $method) use ($prefixInterceptors, $suffixInterceptors) : InterceptedMethod {
return InterceptedMethod::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()),
$prefixInterceptors,
$suffixInterceptors
);
};
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
@@ -38,32 +40,33 @@ class InterceptedMethod extends MethodGenerator
* @param \Zend\Code\Generator\PropertyGenerator $suffixInterceptors
*
* @return self
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public static function generateMethod(
MethodReflection $originalMethod,
PropertyGenerator $valueHolderProperty,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
) : self {
/* @var $method self */
$method = static::fromReflection($originalMethod);
$forwardedParams = array();
$forwardedParams = [];
foreach ($originalMethod->getParameters() as $parameter) {
$forwardedParams[] = '$' . $parameter->getName();
$forwardedParams[] = ($parameter->isVariadic() ? '...' : '') . '$' . $parameter->getName();
}
$method->setDocblock('{@inheritDoc}');
$method->setBody(
InterceptorGenerator::createInterceptedMethodBody(
'$returnValue = $this->' . $valueHolderProperty->getName() . '->'
. $originalMethod->getName() . '(' . implode(', ', $forwardedParams) . ');',
$method,
$valueHolderProperty,
$prefixInterceptors,
$suffixInterceptors
)
);
$method->setDocBlock('{@inheritDoc}');
$method->setBody(InterceptorGenerator::createInterceptedMethodBody(
'$returnValue = $this->' . $valueHolderProperty->getName() . '->'
. $originalMethod->getName() . '(' . implode(', ', $forwardedParams) . ');',
$method,
$valueHolderProperty,
$prefixInterceptors,
$suffixInterceptors,
$originalMethod
));
return $method;
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
@@ -32,6 +34,11 @@ class MagicClone extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $valueHolderProperty
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
*/
public function __construct(
ReflectionClass $originalClass,
@@ -52,7 +59,7 @@ class MagicClone extends MagicMethodGenerator
. "}\n\n"
. "foreach (\$this->$suffix as \$key => \$value) {\n"
. " \$this->$suffix" . "[\$key] = clone \$value;\n"
. "}"
. '}'
);
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
@@ -36,6 +39,15 @@ class MagicGet extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $valueHolder
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
* @param PublicPropertiesMap $publicProperties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
@@ -44,12 +56,12 @@ class MagicGet extends MagicMethodGenerator
PropertyGenerator $suffixInterceptors,
PublicPropertiesMap $publicProperties
) {
parent::__construct($originalClass, '__get', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__get', [new ParameterGenerator('name')]);
$override = $originalClass->hasMethod('__get');
$parent = GetMethodIfExists::get($originalClass, '__get');
$valueHolderName = $valueHolder->getName();
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name');
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_GET,
@@ -65,14 +77,13 @@ class MagicGet extends MagicMethodGenerator
. "\n} else {\n $callParent\n}\n\n";
}
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors
)
);
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
@@ -36,6 +39,14 @@ class MagicIsset extends MagicMethodGenerator
{
/**
* Constructor
* @param ReflectionClass $originalClass
* @param PropertyGenerator $valueHolder
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
* @param PublicPropertiesMap $publicProperties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
@@ -44,12 +55,12 @@ class MagicIsset extends MagicMethodGenerator
PropertyGenerator $suffixInterceptors,
PublicPropertiesMap $publicProperties
) {
parent::__construct($originalClass, '__isset', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__isset', [new ParameterGenerator('name')]);
$override = $originalClass->hasMethod('__isset');
$parent = GetMethodIfExists::get($originalClass, '__isset');
$valueHolderName = $valueHolder->getName();
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name');
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_ISSET,
@@ -65,14 +76,13 @@ class MagicIsset extends MagicMethodGenerator
. "\n} else {\n $callParent\n}\n\n";
}
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors
)
);
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
@@ -36,6 +39,14 @@ class MagicSet extends MagicMethodGenerator
{
/**
* Constructor
* @param ReflectionClass $originalClass
* @param PropertyGenerator $valueHolder
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
* @param PublicPropertiesMap $publicProperties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
@@ -47,13 +58,13 @@ class MagicSet extends MagicMethodGenerator
parent::__construct(
$originalClass,
'__set',
array(new ParameterGenerator('name'), new ParameterGenerator('value'))
[new ParameterGenerator('name'), new ParameterGenerator('value')]
);
$override = $originalClass->hasMethod('__set');
$parent = GetMethodIfExists::get($originalClass, '__set');
$valueHolderName = $valueHolder->getName();
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name');
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_SET,
@@ -69,14 +80,13 @@ class MagicSet extends MagicMethodGenerator
. "\n} else {\n $callParent\n}\n\n";
}
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors
)
);
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
@@ -36,6 +39,14 @@ class MagicUnset extends MagicMethodGenerator
{
/**
* Constructor
* @param ReflectionClass $originalClass
* @param PropertyGenerator $valueHolder
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
* @param PublicPropertiesMap $publicProperties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
@@ -44,12 +55,12 @@ class MagicUnset extends MagicMethodGenerator
PropertyGenerator $suffixInterceptors,
PublicPropertiesMap $publicProperties
) {
parent::__construct($originalClass, '__unset', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__unset', [new ParameterGenerator('name')]);
$override = $originalClass->hasMethod('__unset');
$parent = GetMethodIfExists::get($originalClass, '__unset');
$valueHolderName = $valueHolder->getName();
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name');
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_UNSET,
@@ -67,14 +78,13 @@ class MagicUnset extends MagicMethodGenerator
$callParent .= '$returnValue = false;';
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors
)
);
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -0,0 +1,88 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\ProxyGenerator\Util\Properties;
use ProxyManager\ProxyGenerator\Util\UnsetPropertiesGenerator;
use ReflectionClass;
use Zend\Code\Generator\ParameterGenerator;
use Zend\Code\Generator\PropertyGenerator;
/**
* The `staticProxyConstructor` implementation for access interceptor value holders
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
class StaticProxyConstructor extends MethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $valueHolder
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $valueHolder,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
parent::__construct('staticProxyConstructor', [], static::FLAG_PUBLIC | static::FLAG_STATIC);
$prefix = new ParameterGenerator('prefixInterceptors');
$suffix = new ParameterGenerator('suffixInterceptors');
$prefix->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 ?: $reflection = new \ReflectionClass(__CLASS__);' . "\n"
. '$instance = (new \ReflectionClass(get_class()))->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;'
);
}
}

View File

@@ -16,9 +16,12 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\Util;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\Generator\Util\ProxiedMethodReturnExpression;
use Zend\Code\Generator\PropertyGenerator;
/**
@@ -27,8 +30,7 @@ use Zend\Code\Generator\PropertyGenerator;
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*
* @internal - this class is just here as a small utility for this component,
* don't use it in your own code
* @private - this class is just here as a small utility for this component, don't use it in your own code
*/
class InterceptorGenerator
{
@@ -40,21 +42,23 @@ class InterceptorGenerator
* @param \Zend\Code\Generator\PropertyGenerator $valueHolder
* @param \Zend\Code\Generator\PropertyGenerator $prefixInterceptors
* @param \Zend\Code\Generator\PropertyGenerator $suffixInterceptors
* @param \ReflectionMethod|null $originalMethod
*
* @return string
*/
public static function createInterceptedMethodBody(
$methodBody,
string $methodBody,
MethodGenerator $method,
PropertyGenerator $valueHolder,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
$name = var_export($method->getName(), true);
$valueHolder = $valueHolder->getName();
$prefixInterceptors = $prefixInterceptors->getName();
$suffixInterceptors = $suffixInterceptors->getName();
$params = array();
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();
@@ -63,23 +67,23 @@ class InterceptorGenerator
$paramsString = 'array(' . implode(', ', $params) . ')';
return "if (isset(\$this->$prefixInterceptors" . "[$name])) {\n"
return "if (isset(\$this->$prefixInterceptorsName" . "[$name])) {\n"
. " \$returnEarly = false;\n"
. " \$prefixReturnValue = \$this->$prefixInterceptors" . "[$name]->__invoke("
. "\$this, \$this->$valueHolder, $name, $paramsString, \$returnEarly);\n\n"
. " \$prefixReturnValue = \$this->$prefixInterceptorsName" . "[$name]->__invoke("
. "\$this, \$this->$valueHolderName, $name, $paramsString, \$returnEarly);\n\n"
. " if (\$returnEarly) {\n"
. " return \$prefixReturnValue;\n"
. ' ' . ProxiedMethodReturnExpression::generate('$prefixReturnValue', $originalMethod) . "\n"
. " }\n"
. "}\n\n"
. $methodBody . "\n\n"
. "if (isset(\$this->$suffixInterceptors" . "[$name])) {\n"
. "if (isset(\$this->$suffixInterceptorsName" . "[$name])) {\n"
. " \$returnEarly = false;\n"
. " \$suffixReturnValue = \$this->$suffixInterceptors" . "[$name]->__invoke("
. "\$this, \$this->$valueHolder, $name, $paramsString, \$returnValue, \$returnEarly);\n\n"
. " \$suffixReturnValue = \$this->$suffixInterceptorsName" . "[$name]->__invoke("
. "\$this, \$this->$valueHolderName, $name, $paramsString, \$returnValue, \$returnEarly);\n\n"
. " if (\$returnEarly) {\n"
. " return \$suffixReturnValue;\n"
. ' ' . ProxiedMethodReturnExpression::generate('$suffixReturnValue', $originalMethod) . "\n"
. " }\n"
. "}\n\n"
. "return \$returnValue;";
. ProxiedMethodReturnExpression::generate('$returnValue', $originalMethod);
}
}

View File

@@ -16,25 +16,31 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator;
use ProxyManager\Exception\InvalidProxiedClassException;
use ProxyManager\Generator\Util\ClassGeneratorUtils;
use ProxyManager\Proxy\AccessInterceptorValueHolderInterface;
use ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator\MagicWakeup;
use ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator\SetMethodPrefixInterceptor;
use ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator\SetMethodSuffixInterceptor;
use ProxyManager\ProxyGenerator\AccessInterceptor\PropertyGenerator\MethodPrefixInterceptors;
use ProxyManager\ProxyGenerator\AccessInterceptor\PropertyGenerator\MethodSuffixInterceptors;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\Constructor;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\InterceptedMethod;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\MagicClone;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\MagicGet;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\MagicIsset;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\MagicSet;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\MagicUnset;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\StaticProxyConstructor;
use ProxyManager\ProxyGenerator\Assertion\CanProxyAssertion;
use ProxyManager\ProxyGenerator\LazyLoadingValueHolder\PropertyGenerator\ValueHolderProperty;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\Properties;
use ProxyManager\ProxyGenerator\Util\ProxiedMethodsFilter;
use ProxyManager\ProxyGenerator\ValueHolder\MethodGenerator\Constructor;
use ProxyManager\ProxyGenerator\ValueHolder\MethodGenerator\GetWrappedValueHolderValue;
use ProxyManager\ProxyGenerator\ValueHolder\MethodGenerator\MagicSleep;
use ReflectionClass;
@@ -56,16 +62,17 @@ class AccessInterceptorValueHolderGenerator implements ProxyGeneratorInterface
{
/**
* {@inheritDoc}
*
* @throws \InvalidArgumentException
* @throws InvalidProxiedClassException
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator)
{
CanProxyAssertion::assertClassCanBeProxied($originalClass);
$publicProperties = new PublicPropertiesMap($originalClass);
$interfaces = array(
'ProxyManager\\Proxy\\AccessInterceptorInterface',
'ProxyManager\\Proxy\\ValueHolderInterface',
);
$publicProperties = new PublicPropertiesMap(Properties::fromReflectionClass($originalClass));
$interfaces = [AccessInterceptorValueHolderInterface::class];
if ($originalClass->isInterface()) {
$interfaces[] = $originalClass->getName();
@@ -85,18 +92,12 @@ class AccessInterceptorValueHolderGenerator implements ProxyGeneratorInterface
},
array_merge(
array_map(
function (ReflectionMethod $method) use ($prefixInterceptors, $suffixInterceptors, $valueHolder) {
return InterceptedMethod::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()),
$valueHolder,
$prefixInterceptors,
$suffixInterceptors
);
},
$this->buildMethodInterceptor($prefixInterceptors, $suffixInterceptors, $valueHolder),
ProxiedMethodsFilter::getProxiedMethods($originalClass)
),
array(
new Constructor($originalClass, $valueHolder, $prefixInterceptors, $suffixInterceptors),
[
Constructor::generateMethod($originalClass, $valueHolder),
new StaticProxyConstructor($originalClass, $valueHolder, $prefixInterceptors, $suffixInterceptors),
new GetWrappedValueHolderValue($valueHolder),
new SetMethodPrefixInterceptor($prefixInterceptors),
new SetMethodSuffixInterceptor($suffixInterceptors),
@@ -130,9 +131,24 @@ class AccessInterceptorValueHolderGenerator implements ProxyGeneratorInterface
),
new MagicClone($originalClass, $valueHolder, $prefixInterceptors, $suffixInterceptors),
new MagicSleep($originalClass, $valueHolder),
new MagicWakeup($originalClass, $valueHolder),
)
new MagicWakeup($originalClass),
]
)
);
}
private function buildMethodInterceptor(
MethodPrefixInterceptors $prefixes,
MethodSuffixInterceptors $suffixes,
ValueHolderProperty $valueHolder
) : callable {
return function (ReflectionMethod $method) use ($prefixes, $suffixes, $valueHolder) : InterceptedMethod {
return InterceptedMethod::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()),
$valueHolder,
$prefixes,
$suffixes
);
};
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\Assertion;
use BadMethodCallException;
@@ -47,7 +49,7 @@ final class CanProxyAssertion
*
* @throws InvalidProxiedClassException
*/
public static function assertClassCanBeProxied(ReflectionClass $originalClass, $allowInterfaces = true)
public static function assertClassCanBeProxied(ReflectionClass $originalClass, bool $allowInterfaces = true) : void
{
self::isNotFinal($originalClass);
self::hasNoAbstractProtectedMethods($originalClass);
@@ -62,7 +64,7 @@ final class CanProxyAssertion
*
* @throws InvalidProxiedClassException
*/
private static function isNotFinal(ReflectionClass $originalClass)
private static function isNotFinal(ReflectionClass $originalClass) : void
{
if ($originalClass->isFinal()) {
throw InvalidProxiedClassException::finalClassNotSupported($originalClass);
@@ -74,11 +76,11 @@ final class CanProxyAssertion
*
* @throws InvalidProxiedClassException
*/
private static function hasNoAbstractProtectedMethods(ReflectionClass $originalClass)
private static function hasNoAbstractProtectedMethods(ReflectionClass $originalClass) : void
{
$protectedAbstract = array_filter(
$originalClass->getMethods(),
function (ReflectionMethod $method) {
function (ReflectionMethod $method) : bool {
return $method->isAbstract() && $method->isProtected();
}
);
@@ -93,7 +95,7 @@ final class CanProxyAssertion
*
* @throws InvalidProxiedClassException
*/
private static function isNotInterface(ReflectionClass $originalClass)
private static function isNotInterface(ReflectionClass $originalClass) : void
{
if ($originalClass->isInterface()) {
throw InvalidProxiedClassException::interfaceNotSupported($originalClass);

View File

@@ -0,0 +1,61 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoading\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\ProxyGenerator\Util\Properties;
use ProxyManager\ProxyGenerator\Util\UnsetPropertiesGenerator;
use Zend\Code\Generator\ParameterGenerator;
use Zend\Code\Generator\PropertyGenerator;
/**
* The `staticProxyConstructor` implementation for lazy loading proxies
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
class StaticProxyConstructor extends MethodGenerator
{
/**
* Static constructor
*
* @param PropertyGenerator $initializerProperty
* @param Properties $properties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(PropertyGenerator $initializerProperty, Properties $properties)
{
parent::__construct('staticProxyConstructor', [], static::FLAG_PUBLIC | static::FLAG_STATIC);
$this->setParameter(new ParameterGenerator('initializer'));
$this->setDocBlock("Constructor for lazy initialization\n\n@param \\Closure|null \$initializer");
$this->setBody(
'static $reflection;' . "\n\n"
. '$reflection = $reflection ?: $reflection = new \ReflectionClass(__CLASS__);' . "\n"
. '$instance = (new \ReflectionClass(get_class()))->newInstanceWithoutConstructor();' . "\n\n"
. UnsetPropertiesGenerator::generateSnippet($properties, 'instance')
. '$instance->' . $initializerProperty->getName() . ' = $initializer;' . "\n\n"
. 'return $instance;'
);
}
}

View File

@@ -16,11 +16,15 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
use ProxyManager\ProxyGenerator\Util\Properties;
use ReflectionProperty;
use Zend\Code\Generator\PropertyGenerator;
/**
@@ -34,34 +38,160 @@ class CallInitializer extends MethodGenerator
{
/**
* Constructor
*
* @param PropertyGenerator $initializerProperty
* @param PropertyGenerator $initTracker
* @param Properties $properties
*/
public function __construct(
PropertyGenerator $initializerProperty,
PropertyGenerator $publicPropsDefaults,
PropertyGenerator $initTracker
PropertyGenerator $initTracker,
Properties $properties
) {
parent::__construct(UniqueIdentifierGenerator::getIdentifier('callInitializer'));
$this->setDocblock("Triggers initialization logic for this ghost object");
$docBlock = <<<'DOCBLOCK'
Triggers initialization logic for this ghost object
$this->setParameters(array(
new ParameterGenerator('methodName'),
new ParameterGenerator('parameters', 'array'),
));
@param string $methodName
@param mixed[] $parameters
$this->setVisibility(static::VISIBILITY_PRIVATE);
@return mixed
DOCBLOCK;
parent::__construct(
UniqueIdentifierGenerator::getIdentifier('callInitializer'),
[
new ParameterGenerator('methodName'),
new ParameterGenerator('parameters', 'array'),
],
static::FLAG_PRIVATE,
null,
$docBlock
);
$initializer = $initializerProperty->getName();
$initialization = $initTracker->getName();
$this->setBody(
'if ($this->' . $initialization . ' || ! $this->' . $initializer . ') {' . "\n return;\n}\n\n"
. "\$this->" . $initialization . " = true;\n\n"
. "foreach (self::\$" . $publicPropsDefaults->getName() . " as \$key => \$default) {\n"
. " \$this->\$key = \$default;\n"
. "}\n\n"
. '$this->' . $initializer . '->__invoke'
. '($this, $methodName, $parameters, $this->' . $initializer . ');' . "\n\n"
. "\$this->" . $initialization . " = false;"
$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;
$this->setBody(sprintf(
$bodyTemplate,
$initialization,
$initializer,
$initialization,
$this->propertiesInitializationCode($properties),
$this->propertiesReferenceArrayCode($properties),
$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(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
*
* @return string
*/
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 ReflectionProperty[] $properties indexed by internal name
*
* @return string
*/
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);
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
@@ -32,11 +34,15 @@ class GetProxyInitializer extends MethodGenerator
{
/**
* Constructor
*
* @param PropertyGenerator $initializerProperty
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(PropertyGenerator $initializerProperty)
{
parent::__construct('getProxyInitializer');
$this->setDocblock('{@inheritDoc}');
$this->setDocBlock('{@inheritDoc}');
$this->setBody('return $this->' . $initializerProperty->getName() . ';');
}
}

Some files were not shown because too many files have changed in this diff Show More