first commit

This commit is contained in:
gauvainboiche
2020-03-03 22:45:25 +01:00
commit 058c2110e9
4420 changed files with 598645 additions and 0 deletions

View File

@@ -0,0 +1,388 @@
<?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.
*/
namespace ProxyManagerTest\Functional;
use PHPUnit_Framework_SkippedTestError;
use PHPUnit_Framework_TestCase;
use ProxyManager\Exception\UnsupportedProxiedClassException;
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
use ProxyManager\Proxy\AccessInterceptorInterface;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizerGenerator;
use ProxyManagerTestAsset\BaseClass;
use ProxyManagerTestAsset\ClassWithPublicArrayProperty;
use ProxyManagerTestAsset\ClassWithPublicProperties;
use ProxyManagerTestAsset\ClassWithSelfHint;
use ReflectionClass;
use ProxyManager\Generator\ClassGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
/**
* Tests for {@see \ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizerGenerator} produced objects
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*
* @group Functional
* @coversNothing
*/
class AccessInterceptorScopeLocalizerFunctionalTest extends PHPUnit_Framework_TestCase
{
/**
* {@inheritDoc}
*/
public static function setUpBeforeClass()
{
if (! method_exists('Closure', 'bind')) {
throw new PHPUnit_Framework_SkippedTestError(
'PHP 5.3 doesn\'t support scope localization of private properties'
);
}
}
/**
* @dataProvider getProxyMethods
*/
public function testMethodCalls($className, $instance, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($className);
/* @var $proxy \ProxyManager\Proxy\AccessInterceptorInterface */
$proxy = new $proxyName($instance);
$this->assertProxySynchronized($instance, $proxy);
$this->assertSame($expectedValue, call_user_func_array(array($proxy, $method), $params));
$listener = $this->getMock('stdClass', array('__invoke'));
$listener
->expects($this->once())
->method('__invoke')
->with($proxy, $proxy, $method, $params, false);
$proxy->setMethodPrefixInterceptor(
$method,
function ($proxy, $instance, $method, $params, & $returnEarly) use ($listener) {
$listener->__invoke($proxy, $instance, $method, $params, $returnEarly);
}
);
$this->assertSame($expectedValue, call_user_func_array(array($proxy, $method), $params));
$random = uniqid();
$proxy->setMethodPrefixInterceptor(
$method,
function ($proxy, $instance, $method, $params, & $returnEarly) use ($random) {
$returnEarly = true;
return $random;
}
);
$this->assertSame($random, call_user_func_array(array($proxy, $method), $params));
$this->assertProxySynchronized($instance, $proxy);
}
/**
* @dataProvider getProxyMethods
*/
public function testMethodCallsWithSuffixListener($className, $instance, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($className);
/* @var $proxy \ProxyManager\Proxy\AccessInterceptorInterface */
$proxy = new $proxyName($instance);
$listener = $this->getMock('stdClass', array('__invoke'));
$listener
->expects($this->once())
->method('__invoke')
->with($proxy, $proxy, $method, $params, $expectedValue, false);
$proxy->setMethodSuffixInterceptor(
$method,
function ($proxy, $instance, $method, $params, $returnValue, & $returnEarly) use ($listener) {
$listener->__invoke($proxy, $instance, $method, $params, $returnValue, $returnEarly);
}
);
$this->assertSame($expectedValue, call_user_func_array(array($proxy, $method), $params));
$random = uniqid();
$proxy->setMethodSuffixInterceptor(
$method,
function ($proxy, $instance, $method, $params, $returnValue, & $returnEarly) use ($random) {
$returnEarly = true;
return $random;
}
);
$this->assertSame($random, call_user_func_array(array($proxy, $method), $params));
$this->assertProxySynchronized($instance, $proxy);
}
/**
* @dataProvider getProxyMethods
*/
public function testMethodCallsAfterUnSerialization($className, $instance, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($className);
/* @var $proxy \ProxyManager\Proxy\AccessInterceptorInterface */
$proxy = unserialize(serialize(new $proxyName($instance)));
$this->assertSame($expectedValue, call_user_func_array(array($proxy, $method), $params));
$this->assertProxySynchronized($instance, $proxy);
}
/**
* @dataProvider getProxyMethods
*/
public function testMethodCallsAfterCloning($className, $instance, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($className);
/* @var $proxy \ProxyManager\Proxy\AccessInterceptorInterface */
$proxy = new $proxyName($instance);
$cloned = clone $proxy;
$this->assertProxySynchronized($instance, $proxy);
$this->assertSame($expectedValue, call_user_func_array(array($cloned, $method), $params));
$this->assertProxySynchronized($instance, $proxy);
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyReadAccess($instance, $proxy, $publicProperty, $propertyValue)
{
/* @var $proxy \ProxyManager\Proxy\AccessInterceptorInterface */
$this->assertSame($propertyValue, $proxy->$publicProperty);
$this->assertProxySynchronized($instance, $proxy);
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyWriteAccess($instance, $proxy, $publicProperty)
{
/* @var $proxy \ProxyManager\Proxy\AccessInterceptorInterface */
$newValue = uniqid();
$proxy->$publicProperty = $newValue;
$this->assertSame($newValue, $proxy->$publicProperty);
$this->assertProxySynchronized($instance, $proxy);
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyExistence($instance, $proxy, $publicProperty)
{
/* @var $proxy \ProxyManager\Proxy\AccessInterceptorInterface */
$this->assertSame(isset($instance->$publicProperty), isset($proxy->$publicProperty));
$this->assertProxySynchronized($instance, $proxy);
$instance->$publicProperty = null;
$this->assertFalse(isset($proxy->$publicProperty));
$this->assertProxySynchronized($instance, $proxy);
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyUnset($instance, $proxy, $publicProperty)
{
$this->markTestSkipped('It is currently not possible to synchronize properties un-setting');
/* @var $proxy \ProxyManager\Proxy\AccessInterceptorInterface */
unset($proxy->$publicProperty);
$this->assertFalse(isset($instance->$publicProperty));
$this->assertFalse(isset($proxy->$publicProperty));
$this->assertProxySynchronized($instance, $proxy);
}
/**
* Verifies that accessing a public property containing an array behaves like in a normal context
*/
public function testCanWriteToArrayKeysInPublicProperty()
{
$instance = new ClassWithPublicArrayProperty();
$className = get_class($instance);
$proxyName = $this->generateProxy($className);
/* @var $proxy ClassWithPublicArrayProperty */
$proxy = new $proxyName($instance);
$proxy->arrayProperty['foo'] = 'bar';
$this->assertSame('bar', $proxy->arrayProperty['foo']);
$proxy->arrayProperty = array('tab' => 'taz');
$this->assertSame(array('tab' => 'taz'), $proxy->arrayProperty);
$this->assertProxySynchronized($instance, $proxy);
}
/**
* Verifies that public properties retrieved via `__get` don't get modified in the object state
*/
public function testWillNotModifyRetrievedPublicProperties()
{
$instance = new ClassWithPublicProperties();
$className = get_class($instance);
$proxyName = $this->generateProxy($className);
/* @var $proxy ClassWithPublicProperties */
$proxy = new $proxyName($instance);
$variable = $proxy->property0;
$this->assertSame('property0', $variable);
$variable = 'foo';
$this->assertSame('property0', $proxy->property0);
$this->assertProxySynchronized($instance, $proxy);
}
/**
* Verifies that public properties references retrieved via `__get` modify in the object state
*/
public function testWillModifyByRefRetrievedPublicProperties()
{
$instance = new ClassWithPublicProperties();
$className = get_class($instance);
$proxyName = $this->generateProxy($className);
/* @var $proxy ClassWithPublicProperties */
$proxy = new $proxyName($instance);
$variable = & $proxy->property0;
$this->assertSame('property0', $variable);
$variable = 'foo';
$this->assertSame('foo', $proxy->property0);
$this->assertProxySynchronized($instance, $proxy);
}
/**
* Generates a proxy for the given class name, and retrieves its class name
*
* @param string $parentClassName
*
* @return string
*
* @throws UnsupportedProxiedClassException
*/
private function generateProxy($parentClassName)
{
$generatedClassName = __NAMESPACE__ . '\\' . UniqueIdentifierGenerator::getIdentifier('Foo');
$generator = new AccessInterceptorScopeLocalizerGenerator();
$generatedClass = new ClassGenerator($generatedClassName);
$strategy = new EvaluatingGeneratorStrategy();
$generator->generate(new ReflectionClass($parentClassName), $generatedClass);
$strategy->generate($generatedClass);
return $generatedClassName;
}
/**
* Generates a list of object | invoked method | parameters | expected result
*
* @return array
*/
public function getProxyMethods()
{
$selfHintParam = new ClassWithSelfHint();
$data = array(
array(
'ProxyManagerTestAsset\\BaseClass',
new BaseClass(),
'publicMethod',
array(),
'publicMethodDefault'
),
array(
'ProxyManagerTestAsset\\BaseClass',
new BaseClass(),
'publicTypeHintedMethod',
array('param' => new \stdClass()),
'publicTypeHintedMethodDefault'
),
array(
'ProxyManagerTestAsset\\BaseClass',
new BaseClass(),
'publicByReferenceMethod',
array(),
'publicByReferenceMethodDefault'
),
);
if (PHP_VERSION_ID >= 50401) {
// PHP < 5.4.1 misbehaves, throwing strict standards, see https://bugs.php.net/bug.php?id=60573
$data[] = array(
'ProxyManagerTestAsset\\ClassWithSelfHint',
new ClassWithSelfHint(),
'selfHintMethod',
array('parameter' => $selfHintParam),
$selfHintParam
);
}
return $data;
}
/**
* Generates proxies and instances with a public property to feed to the property accessor methods
*
* @return array
*/
public function getPropertyAccessProxies()
{
$instance1 = new BaseClass();
$proxyName1 = $this->generateProxy(get_class($instance1));
return array(
array(
$instance1,
new $proxyName1($instance1),
'publicProperty',
'publicPropertyDefault',
),
);
}
/**
* @param object $instance
* @param AccessInterceptorInterface $proxy
*/
private function assertProxySynchronized($instance, AccessInterceptorInterface $proxy)
{
$reflectionClass = new ReflectionClass($instance);
foreach ($reflectionClass->getProperties() as $property) {
$property->setAccessible(true);
$this->assertSame(
$property->getValue($instance),
$property->getValue($proxy),
'Property "' . $property->getName() . '" is synchronized between instance and proxy'
);
}
}
}

View File

@@ -0,0 +1,360 @@
<?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.
*/
namespace ProxyManagerTest\Functional;
use PHPUnit_Framework_TestCase;
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolderGenerator;
use ProxyManagerTestAsset\BaseClass;
use ProxyManagerTestAsset\ClassWithPublicArrayProperty;
use ProxyManagerTestAsset\ClassWithPublicProperties;
use ProxyManagerTestAsset\ClassWithSelfHint;
use ReflectionClass;
use ProxyManager\Generator\ClassGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
/**
* Tests for {@see \ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator} produced objects
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*
* @group Functional
* @coversNothing
*/
class AccessInterceptorValueHolderFunctionalTest extends PHPUnit_Framework_TestCase
{
/**
* @dataProvider getProxyMethods
*/
public function testMethodCalls($className, $instance, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($className);
/* @var $proxy \ProxyManager\Proxy\AccessInterceptorInterface|\ProxyManager\Proxy\ValueHolderInterface */
$proxy = new $proxyName($instance);
$this->assertSame($instance, $proxy->getWrappedValueHolderValue());
$this->assertSame($expectedValue, call_user_func_array(array($proxy, $method), $params));
$listener = $this->getMock('stdClass', array('__invoke'));
$listener
->expects($this->once())
->method('__invoke')
->with($proxy, $instance, $method, $params, false);
$proxy->setMethodPrefixInterceptor(
$method,
function ($proxy, $instance, $method, $params, & $returnEarly) use ($listener) {
$listener->__invoke($proxy, $instance, $method, $params, $returnEarly);
}
);
$this->assertSame($expectedValue, call_user_func_array(array($proxy, $method), $params));
$random = uniqid();
$proxy->setMethodPrefixInterceptor(
$method,
function ($proxy, $instance, $method, $params, & $returnEarly) use ($random) {
$returnEarly = true;
return $random;
}
);
$this->assertSame($random, call_user_func_array(array($proxy, $method), $params));
}
/**
* @dataProvider getProxyMethods
*/
public function testMethodCallsWithSuffixListener($className, $instance, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($className);
/* @var $proxy \ProxyManager\Proxy\AccessInterceptorInterface|\ProxyManager\Proxy\ValueHolderInterface */
$proxy = new $proxyName($instance);
$listener = $this->getMock('stdClass', array('__invoke'));
$listener
->expects($this->once())
->method('__invoke')
->with($proxy, $instance, $method, $params, $expectedValue, false);
$proxy->setMethodSuffixInterceptor(
$method,
function ($proxy, $instance, $method, $params, $returnValue, & $returnEarly) use ($listener) {
$listener->__invoke($proxy, $instance, $method, $params, $returnValue, $returnEarly);
}
);
$this->assertSame($expectedValue, call_user_func_array(array($proxy, $method), $params));
$random = uniqid();
$proxy->setMethodSuffixInterceptor(
$method,
function ($proxy, $instance, $method, $params, $returnValue, & $returnEarly) use ($random) {
$returnEarly = true;
return $random;
}
);
$this->assertSame($random, call_user_func_array(array($proxy, $method), $params));
}
/**
* @dataProvider getProxyMethods
*/
public function testMethodCallsAfterUnSerialization($className, $instance, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($className);
/* @var $proxy \ProxyManager\Proxy\AccessInterceptorInterface|\ProxyManager\Proxy\ValueHolderInterface */
$proxy = unserialize(serialize(new $proxyName($instance)));
$this->assertSame($expectedValue, call_user_func_array(array($proxy, $method), $params));
$this->assertEquals($instance, $proxy->getWrappedValueHolderValue());
}
/**
* @dataProvider getProxyMethods
*/
public function testMethodCallsAfterCloning($className, $instance, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($className);
/* @var $proxy \ProxyManager\Proxy\AccessInterceptorInterface|\ProxyManager\Proxy\ValueHolderInterface */
$proxy = new $proxyName($instance);
$cloned = clone $proxy;
$this->assertNotSame($proxy->getWrappedValueHolderValue(), $cloned->getWrappedValueHolderValue());
$this->assertSame($expectedValue, call_user_func_array(array($cloned, $method), $params));
$this->assertEquals($instance, $cloned->getWrappedValueHolderValue());
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyReadAccess($instance, $proxy, $publicProperty, $propertyValue)
{
/* @var $proxy \ProxyManager\Proxy\AccessInterceptorInterface|\ProxyManager\Proxy\ValueHolderInterface */
$this->assertSame($propertyValue, $proxy->$publicProperty);
$this->assertEquals($instance, $proxy->getWrappedValueHolderValue());
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyWriteAccess($instance, $proxy, $publicProperty)
{
/* @var $proxy \ProxyManager\Proxy\AccessInterceptorInterface|\ProxyManager\Proxy\ValueHolderInterface */
$newValue = uniqid();
$proxy->$publicProperty = $newValue;
$this->assertSame($newValue, $proxy->$publicProperty);
$this->assertSame($newValue, $proxy->getWrappedValueHolderValue()->$publicProperty);
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyExistence($instance, $proxy, $publicProperty)
{
/* @var $proxy \ProxyManager\Proxy\AccessInterceptorInterface|\ProxyManager\Proxy\ValueHolderInterface */
$this->assertSame(isset($instance->$publicProperty), isset($proxy->$publicProperty));
$this->assertEquals($instance, $proxy->getWrappedValueHolderValue());
$proxy->getWrappedValueHolderValue()->$publicProperty = null;
$this->assertFalse(isset($proxy->$publicProperty));
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyUnset($instance, $proxy, $publicProperty)
{
/* @var $proxy \ProxyManager\Proxy\AccessInterceptorInterface|\ProxyManager\Proxy\ValueHolderInterface */
$instance = $proxy->getWrappedValueHolderValue() ? $proxy->getWrappedValueHolderValue() : $instance;
unset($proxy->$publicProperty);
$this->assertFalse(isset($instance->$publicProperty));
$this->assertFalse(isset($proxy->$publicProperty));
}
/**
* Verifies that accessing a public property containing an array behaves like in a normal context
*/
public function testCanWriteToArrayKeysInPublicProperty()
{
$instance = new ClassWithPublicArrayProperty();
$className = get_class($instance);
$proxyName = $this->generateProxy($className);
/* @var $proxy ClassWithPublicArrayProperty */
$proxy = new $proxyName($instance);
$proxy->arrayProperty['foo'] = 'bar';
$this->assertSame('bar', $proxy->arrayProperty['foo']);
$proxy->arrayProperty = array('tab' => 'taz');
$this->assertSame(array('tab' => 'taz'), $proxy->arrayProperty);
}
/**
* Verifies that public properties retrieved via `__get` don't get modified in the object state
*/
public function testWillNotModifyRetrievedPublicProperties()
{
$instance = new ClassWithPublicProperties();
$className = get_class($instance);
$proxyName = $this->generateProxy($className);
/* @var $proxy ClassWithPublicProperties */
$proxy = new $proxyName($instance);
$variable = $proxy->property0;
$this->assertSame('property0', $variable);
$variable = 'foo';
$this->assertSame('property0', $proxy->property0);
}
/**
* Verifies that public properties references retrieved via `__get` modify in the object state
*/
public function testWillModifyByRefRetrievedPublicProperties()
{
$instance = new ClassWithPublicProperties();
$className = get_class($instance);
$proxyName = $this->generateProxy($className);
/* @var $proxy ClassWithPublicProperties */
$proxy = new $proxyName($instance);
$variable = & $proxy->property0;
$this->assertSame('property0', $variable);
$variable = 'foo';
$this->assertSame('foo', $proxy->property0);
}
/**
* Generates a proxy for the given class name, and retrieves its class name
*
* @param string $parentClassName
*
* @return string
*/
private function generateProxy($parentClassName)
{
$generatedClassName = __NAMESPACE__ . '\\' . UniqueIdentifierGenerator::getIdentifier('Foo');
$generator = new AccessInterceptorValueHolderGenerator();
$generatedClass = new ClassGenerator($generatedClassName);
$strategy = new EvaluatingGeneratorStrategy();
$generator->generate(new ReflectionClass($parentClassName), $generatedClass);
$strategy->generate($generatedClass);
return $generatedClassName;
}
/**
* Generates a list of object | invoked method | parameters | expected result
*
* @return array
*/
public function getProxyMethods()
{
$selfHintParam = new ClassWithSelfHint();
$data = array(
array(
'ProxyManagerTestAsset\\BaseClass',
new BaseClass(),
'publicMethod',
array(),
'publicMethodDefault'
),
array(
'ProxyManagerTestAsset\\BaseClass',
new BaseClass(),
'publicTypeHintedMethod',
array('param' => new \stdClass()),
'publicTypeHintedMethodDefault'
),
array(
'ProxyManagerTestAsset\\BaseClass',
new BaseClass(),
'publicByReferenceMethod',
array(),
'publicByReferenceMethodDefault'
),
array(
'ProxyManagerTestAsset\\BaseInterface',
new BaseClass(),
'publicMethod',
array(),
'publicMethodDefault'
),
);
if (PHP_VERSION_ID >= 50401) {
// PHP < 5.4.1 misbehaves, throwing strict standards, see https://bugs.php.net/bug.php?id=60573
$data[] = array(
'ProxyManagerTestAsset\\ClassWithSelfHint',
new ClassWithSelfHint(),
'selfHintMethod',
array('parameter' => $selfHintParam),
$selfHintParam
);
}
return $data;
}
/**
* Generates proxies and instances with a public property to feed to the property accessor methods
*
* @return array
*/
public function getPropertyAccessProxies()
{
$instance1 = new BaseClass();
$proxyName1 = $this->generateProxy(get_class($instance1));
$instance2 = new BaseClass();
$proxyName2 = $this->generateProxy(get_class($instance2));
return array(
array(
$instance1,
new $proxyName1($instance1),
'publicProperty',
'publicPropertyDefault',
),
array(
$instance2,
unserialize(serialize(new $proxyName2($instance2))),
'publicProperty',
'publicPropertyDefault',
),
);
}
}

View File

@@ -0,0 +1,196 @@
<?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.
*/
namespace ProxyManagerTest\Functional;
/**
* Base performance test logic for lazy loading proxies
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*
* @group Performance
* @coversNothing
*/
abstract class BaseLazyLoadingPerformanceTest extends BasePerformanceTest
{
/**
* @param string $className
* @param object[] $instances
* @param \ProxyManager\Proxy\LazyLoadingInterface[] $proxies
* @param string $methodName
* @param array $parameters
*/
protected function profileMethodAccess($className, array $instances, array $proxies, $methodName, array $parameters)
{
$iterations = count($instances);
$this->startCapturing();
foreach ($instances as $instance) {
call_user_func_array(array($instance, $methodName), $parameters);
}
$baseProfile = $this->endCapturing(
$iterations . ' calls to ' . $className . '#' . $methodName . ': %fms / %fKb'
);
$this->startCapturing();
foreach ($proxies as $proxy) {
call_user_func_array(array($proxy, $methodName), $parameters);
}
$proxyProfile = $this->endCapturing(
$iterations . ' calls to proxied ' . $className . '#' . $methodName . ': %fms / %fKb'
);
$this->compareProfile($baseProfile, $proxyProfile);
}
/**
* @param string $className
* @param object[] $instances
* @param \ProxyManager\Proxy\LazyLoadingInterface[] $proxies
* @param string $property
*/
protected function profilePropertyWrites($className, array $instances, array $proxies, $property)
{
$iterations = count($instances);
$this->startCapturing();
foreach ($instances as $instance) {
$instance->$property = 'foo';
}
$baseProfile = $this->endCapturing(
$iterations . ' writes of ' . $className . '::' . $property . ': %fms / %fKb'
);
$this->startCapturing();
foreach ($proxies as $proxy) {
$proxy->$property = 'foo';
}
$proxyProfile = $this->endCapturing(
$iterations . ' writes of proxied ' . $className . '::' . $property . ': %fms / %fKb'
);
$this->compareProfile($baseProfile, $proxyProfile);
}
/**
* @param string $className
* @param object[] $instances
* @param \ProxyManager\Proxy\LazyLoadingInterface[] $proxies
* @param string $property
*/
protected function profilePropertyReads($className, array $instances, array $proxies, $property)
{
$iterations = count($instances);
$this->startCapturing();
foreach ($instances as $instance) {
$instance->$property;
}
$baseProfile = $this->endCapturing(
$iterations . ' reads of ' . $className . '::' . $property . ': %fms / %fKb'
);
$this->startCapturing();
foreach ($proxies as $proxy) {
$proxy->$property;
}
$proxyProfile = $this->endCapturing(
$iterations . ' reads of proxied ' . $className . '::' . $property . ': %fms / %fKb'
);
$this->compareProfile($baseProfile, $proxyProfile);
}
/**
* @param string $className
* @param object[] $instances
* @param \ProxyManager\Proxy\LazyLoadingInterface[] $proxies
* @param string $property
*/
protected function profilePropertyIsset($className, array $instances, array $proxies, $property)
{
$iterations = count($instances);
$this->startCapturing();
foreach ($instances as $instance) {
isset($instance->$property);
}
$baseProfile = $this->endCapturing(
$iterations . ' isset of ' . $className . '::' . $property . ': %fms / %fKb'
);
$this->startCapturing();
foreach ($proxies as $proxy) {
isset($proxy->$property);
}
$proxyProfile = $this->endCapturing(
$iterations . ' isset of proxied ' . $className . '::' . $property . ': %fms / %fKb'
);
$this->compareProfile($baseProfile, $proxyProfile);
}
/**
* @param string $className
* @param object[] $instances
* @param \ProxyManager\Proxy\LazyLoadingInterface[] $proxies
* @param string $property
*/
protected function profilePropertyUnset($className, array $instances, array $proxies, $property)
{
$iterations = count($instances);
$this->startCapturing();
foreach ($instances as $instance) {
unset($instance->$property);
}
$baseProfile = $this->endCapturing(
$iterations . ' unset of ' . $className . '::' . $property . ': %fms / %fKb'
);
$this->startCapturing();
foreach ($proxies as $proxy) {
unset($proxy->$property);
}
$proxyProfile = $this->endCapturing(
$iterations . ' unset of proxied ' . $className . '::' . $property . ': %fms / %fKb'
);
$this->compareProfile($baseProfile, $proxyProfile);
}
/**
* Generates a proxy for the given class name, and retrieves its class name
*
* @param string $parentClassName
*
* @return string
*/
abstract protected function generateProxy($parentClassName);
}

View File

@@ -0,0 +1,101 @@
<?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.
*/
namespace ProxyManagerTest\Functional;
use PHPUnit_Framework_TestCase;
/**
* Base performance test logic
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*
* @group Performance
* @coversNothing
*/
abstract class BasePerformanceTest extends PHPUnit_Framework_TestCase
{
/**
* @var float time when last capture was started
*/
private $startTime = 0;
/**
* @var int bytes when last capture was started
*/
private $startMemory = 0;
/**
* {@inheritDoc}
*/
public static function setUpBeforeClass()
{
$header = "Performance test - " . get_called_class() . ":";
echo "\n\n" . str_repeat('=', strlen($header)) . "\n" . $header . "\n\n";
}
/**
* Start profiler snapshot
*/
protected function startCapturing()
{
$this->startMemory = memory_get_usage();
$this->startTime = microtime(true);
}
/**
* Echo current profiler output
*
* @param string $messageTemplate
*
* @return array
*/
protected function endCapturing($messageTemplate)
{
$time = microtime(true) - $this->startTime;
$memory = memory_get_usage() - $this->startMemory;
if (gc_enable()) {
gc_collect_cycles();
}
echo sprintf($messageTemplate, $time, $memory / 1024) . "\n";
return array(
'time' => $time,
'memory' => $memory
);
}
/**
* Display comparison between two profiles
*
* @param array $baseProfile
* @param array $proxyProfile
*/
protected function compareProfile(array $baseProfile, array $proxyProfile)
{
$baseMemory = max(1, $baseProfile['memory']);
$timeOverhead = (($proxyProfile['time'] / $baseProfile['time']) - 1) * 100;
$memoryOverhead = (($proxyProfile['memory'] / $baseMemory) - 1) * 100;
echo sprintf('Comparison time / memory: %.2f%% / %.2f%%', $timeOverhead, $memoryOverhead) . "\n\n";
}
}

View File

@@ -0,0 +1,171 @@
<?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.
*/
namespace ProxyManagerTest\Functional;
use PHPUnit_Framework_TestCase;
use PHPUnit_Util_PHP;
use ReflectionClass;
/**
* Verifies that proxy-manager will not attempt to `eval()` code that will cause fatal errors
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*
* @group Functional
* @coversNothing
*/
class FatalPreventionFunctionalTest extends PHPUnit_Framework_TestCase
{
private $template = <<<'PHP'
<?php
require_once %s;
$className = %s;
$generatedClass = new ProxyManager\Generator\ClassGenerator(uniqid('generated'));
$generatorStrategy = new ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy();
$classGenerator = new %s;
$classSignatureGenerator = new ProxyManager\Signature\ClassSignatureGenerator(
new ProxyManager\Signature\SignatureGenerator()
);
try {
$classGenerator->generate(new ReflectionClass($className), $generatedClass);
$classSignatureGenerator->addSignature($generatedClass, array('eval tests'));
$generatorStrategy->generate($generatedClass);
} catch (ProxyManager\Exception\ExceptionInterface $e) {
} catch (ReflectionException $e) {
}
echo 'SUCCESS: ' . %s;
PHP;
/**
* Verifies that code generation and evaluation will not cause fatals with any given class
*
* @param string $generatorClass an instantiable class (no arguments) implementing
* the {@see \ProxyManager\ProxyGenerator\ProxyGeneratorInterface}
* @param string $className a valid (existing/autoloadable) class name
*
* @dataProvider getTestedClasses
*/
public function testCodeGeneration($generatorClass, $className)
{
if (defined('HHVM_VERSION')) {
$this->markTestSkipped('HHVM is just too slow for this kind of test right now.');
}
if (PHP_VERSION_ID < 50401) {
$this->markTestSkipped('Can\'t run this test suite on php < 5.4.1');
}
$runner = PHPUnit_Util_PHP::factory();
$code = sprintf(
$this->template,
var_export(realpath(__DIR__ . '/../../../vendor/autoload.php'), true),
var_export($className, true),
$generatorClass,
var_export($className, true)
);
$result = $runner->runJob($code, array('-n'));
if (('SUCCESS: ' . $className) !== $result['stdout']) {
$this->fail(sprintf(
"Crashed with class '%s' and generator '%s'.\n\nStdout:\n%s\nStderr:\n%s\nGenerated code:\n%s'",
$generatorClass,
$className,
$result['stdout'],
$result['stderr'],
$code
));
}
$this->assertSame('SUCCESS: ' . $className, $result['stdout']);
}
/**
* @return string[][]
*/
public function getTestedClasses()
{
$that = $this;
return call_user_func_array(
'array_merge',
array_map(
function ($generator) use ($that) {
return array_map(
function ($class) use ($generator) {
return array($generator, $class);
},
$that->getProxyTestedClasses()
);
},
array(
'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizerGenerator',
'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolderGenerator',
'ProxyManager\\ProxyGenerator\\LazyLoadingGhostGenerator',
'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolderGenerator',
'ProxyManager\\ProxyGenerator\\NullObjectGenerator',
'ProxyManager\\ProxyGenerator\\RemoteObjectGenerator',
)
)
);
}
/**
* @private (public only for PHP 5.3 compatibility)
*
* @return string[]
*/
public function getProxyTestedClasses()
{
$skippedPaths = array(
realpath(__DIR__ . '/../../src'),
realpath(__DIR__ . '/../../vendor'),
realpath(__DIR__ . '/../../tests/ProxyManagerTest'),
);
return array_filter(
get_declared_classes(),
function ($className) use ($skippedPaths) {
$reflectionClass = new ReflectionClass($className);
$fileName = $reflectionClass->getFileName();
if (! $fileName) {
return false;
}
$realPath = realpath($fileName);
foreach ($skippedPaths as $skippedPath) {
if (0 === strpos($realPath, $skippedPath)) {
// skip classes defined within ProxyManager, vendor or the test suite
return false;
}
}
return true;
}
);
}
}

View File

@@ -0,0 +1,441 @@
<?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.
*/
namespace ProxyManagerTest\Functional;
use PHPUnit_Framework_TestCase;
use PHPUnit_Framework_MockObject_MockObject as Mock;
use ProxyManager\Generator\ClassGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
use ProxyManager\Proxy\GhostObjectInterface;
use ProxyManager\ProxyGenerator\LazyLoadingGhostGenerator;
use ProxyManagerTestAsset\BaseClass;
use ProxyManagerTestAsset\ClassWithPublicArrayProperty;
use ProxyManagerTestAsset\ClassWithPublicProperties;
use ProxyManagerTestAsset\ClassWithProtectedProperties;
use ProxyManagerTestAsset\ClassWithPrivateProperties;
use ProxyManagerTestAsset\ClassWithSelfHint;
use ReflectionClass;
use ReflectionProperty;
/**
* Tests for {@see \ProxyManager\ProxyGenerator\LazyLoadingGhostGenerator} produced objects
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*
* @group Functional
* @coversNothing
*/
class LazyLoadingGhostFunctionalTest extends PHPUnit_Framework_TestCase
{
/**
* @dataProvider getProxyMethods
*/
public function testMethodCalls($className, $instance, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($className);
/* @var $proxy \ProxyManager\Proxy\GhostObjectInterface|BaseClass */
$proxy = new $proxyName($this->createInitializer($className, $instance));
$this->assertFalse($proxy->isProxyInitialized());
$this->assertSame($expectedValue, call_user_func_array(array($proxy, $method), $params));
$this->assertTrue($proxy->isProxyInitialized());
}
/**
* @dataProvider getProxyMethods
*/
public function testMethodCallsAfterUnSerialization($className, $instance, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($className);
/* @var $proxy \ProxyManager\Proxy\GhostObjectInterface|BaseClass */
$proxy = unserialize(serialize(new $proxyName($this->createInitializer($className, $instance))));
$this->assertTrue($proxy->isProxyInitialized());
$this->assertSame($expectedValue, call_user_func_array(array($proxy, $method), $params));
}
/**
* @dataProvider getProxyMethods
*/
public function testMethodCallsAfterCloning($className, $instance, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($className);
/* @var $proxy \ProxyManager\Proxy\GhostObjectInterface|BaseClass */
$proxy = new $proxyName($this->createInitializer($className, $instance));
$cloned = clone $proxy;
$this->assertTrue($cloned->isProxyInitialized());
$this->assertSame($expectedValue, call_user_func_array(array($cloned, $method), $params));
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyReadAccess($instance, $proxy, $publicProperty, $propertyValue)
{
/* @var $proxy \ProxyManager\Proxy\GhostObjectInterface */
$this->assertSame($propertyValue, $proxy->$publicProperty);
$this->assertTrue($proxy->isProxyInitialized());
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyWriteAccess($instance, $proxy, $publicProperty)
{
/* @var $proxy \ProxyManager\Proxy\GhostObjectInterface */
$newValue = uniqid();
$proxy->$publicProperty = $newValue;
$this->assertTrue($proxy->isProxyInitialized());
$this->assertSame($newValue, $proxy->$publicProperty);
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyExistence($instance, $proxy, $publicProperty)
{
/* @var $proxy \ProxyManager\Proxy\GhostObjectInterface */
$this->assertSame(isset($instance->$publicProperty), isset($proxy->$publicProperty));
$this->assertTrue($proxy->isProxyInitialized());
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyAbsence($instance, $proxy, $publicProperty)
{
/* @var $proxy \ProxyManager\Proxy\GhostObjectInterface */
$proxy->$publicProperty = null;
$this->assertFalse(isset($proxy->$publicProperty));
$this->assertTrue($proxy->isProxyInitialized());
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyUnset($instance, $proxy, $publicProperty)
{
/* @var $proxy \ProxyManager\Proxy\GhostObjectInterface */
unset($proxy->$publicProperty);
$this->assertTrue($proxy->isProxyInitialized());
$this->assertTrue(isset($instance->$publicProperty));
$this->assertFalse(isset($proxy->$publicProperty));
}
/**
* Verifies that accessing a public property containing an array behaves like in a normal context
*/
public function testCanWriteToArrayKeysInPublicProperty()
{
$instance = new ClassWithPublicArrayProperty();
$className = get_class($instance);
$initializer = $this->createInitializer($className, $instance);
$proxyName = $this->generateProxy($className);
/* @var $proxy ClassWithPublicArrayProperty */
$proxy = new $proxyName($initializer);
$proxy->arrayProperty['foo'] = 'bar';
$this->assertSame('bar', $proxy->arrayProperty['foo']);
$proxy->arrayProperty = array('tab' => 'taz');
$this->assertSame(array('tab' => 'taz'), $proxy->arrayProperty);
}
/**
* Verifies that public properties retrieved via `__get` don't get modified in the object itself
*/
public function testWillNotModifyRetrievedPublicProperties()
{
$instance = new ClassWithPublicProperties();
$className = get_class($instance);
$initializer = $this->createInitializer($className, $instance);
$proxyName = $this->generateProxy($className);
/* @var $proxy ClassWithPublicProperties */
$proxy = new $proxyName($initializer);
$variable = $proxy->property0;
$this->assertSame('property0', $variable);
$variable = 'foo';
$this->assertSame('property0', $proxy->property0);
}
/**
* Verifies that public properties references retrieved via `__get` modify in the object state
*/
public function testWillModifyByRefRetrievedPublicProperties()
{
$instance = new ClassWithPublicProperties();
$className = get_class($instance);
$initializer = $this->createInitializer($className, $instance);
$proxyName = $this->generateProxy($className);
/* @var $proxy ClassWithPublicProperties */
$proxy = new $proxyName($initializer);
$variable = & $proxy->property0;
$this->assertSame('property0', $variable);
$variable = 'foo';
$this->assertSame('foo', $proxy->property0);
}
public function testKeepsInitializerWhenNotOverwitten()
{
$instance = new BaseClass();
$proxyName = $this->generateProxy(get_class($instance));
$initializer = function () {
};
/* @var $proxy \ProxyManager\Proxy\GhostObjectInterface */
$proxy = new $proxyName($initializer);
$proxy->initializeProxy();
$this->assertSame($initializer, $proxy->getProxyInitializer());
}
/**
* Verifies that public properties are not being initialized multiple times
*/
public function testKeepsInitializedPublicProperties()
{
$instance = new BaseClass();
$proxyName = $this->generateProxy(get_class($instance));
$initializer = function (BaseClass $proxy, $method, $parameters, & $initializer) {
$initializer = null;
$proxy->publicProperty = 'newValue';
};
/* @var $proxy \ProxyManager\Proxy\GhostObjectInterface|BaseClass */
$proxy = new $proxyName($initializer);
$proxy->initializeProxy();
$this->assertSame('newValue', $proxy->publicProperty);
$proxy->publicProperty = 'otherValue';
$proxy->initializeProxy();
$this->assertSame('otherValue', $proxy->publicProperty);
}
/**
* Verifies that properties' default values are preserved
*/
public function testPublicPropertyDefaultWillBePreserved()
{
$instance = new ClassWithPublicProperties();
$proxyName = $this->generateProxy(get_class($instance));
/* @var $proxy ClassWithPublicProperties */
$proxy = new $proxyName(function () {
});
$this->assertSame('property0', $proxy->property0);
}
/**
* Verifies that protected properties' default values are preserved
*/
public function testProtectedPropertyDefaultWillBePreserved()
{
$instance = new ClassWithProtectedProperties();
$proxyName = $this->generateProxy(get_class($instance));
/* @var $proxy ClassWithProtectedProperties */
$proxy = new $proxyName(function () {
});
// Check protected property via reflection
$reflectionProperty = new ReflectionProperty($instance, 'property0');
$reflectionProperty->setAccessible(true);
$this->assertSame('property0', $reflectionProperty->getValue($proxy));
}
/**
* Verifies that private properties' default values are preserved
*/
public function testPrivatePropertyDefaultWillBePreserved()
{
$instance = new ClassWithPrivateProperties();
$proxyName = $this->generateProxy(get_class($instance));
/* @var $proxy ClassWithPrivateProperties */
$proxy = new $proxyName(function () {
});
// Check protected property via reflection
$reflectionProperty = new ReflectionProperty($instance, 'property0');
$reflectionProperty->setAccessible(true);
$this->assertSame('property0', $reflectionProperty->getValue($proxy));
}
/**
* Generates a proxy for the given class name, and retrieves its class name
*
* @param string $parentClassName
*
* @return string
*/
private function generateProxy($parentClassName)
{
$generatedClassName = __NAMESPACE__ . '\\' . UniqueIdentifierGenerator::getIdentifier('Foo');
$generator = new LazyLoadingGhostGenerator();
$generatedClass = new ClassGenerator($generatedClassName);
$strategy = new EvaluatingGeneratorStrategy();
$generator->generate(new ReflectionClass($parentClassName), $generatedClass);
$strategy->generate($generatedClass);
return $generatedClassName;
}
/**
* @param string $className
* @param object $realInstance
* @param Mock $initializerMatcher
*
* @return \Closure
*/
private function createInitializer($className, $realInstance, Mock $initializerMatcher = null)
{
if (null === $initializerMatcher) {
$initializerMatcher = $this->getMock('stdClass', array('__invoke'));
$initializerMatcher
->expects($this->once())
->method('__invoke')
->with(
$this->logicalAnd(
$this->isInstanceOf('ProxyManager\\Proxy\\GhostObjectInterface'),
$this->isInstanceOf($className)
)
);
}
$initializerMatcher = $initializerMatcher ?: $this->getMock('stdClass', array('__invoke'));
return function (
GhostObjectInterface $proxy,
$method,
$params,
& $initializer
) use (
$initializerMatcher,
$realInstance
) {
$initializer = null;
$reflectionClass = new ReflectionClass($realInstance);
foreach ($reflectionClass->getProperties() as $property) {
$property->setAccessible(true);
$property->setValue($proxy, $property->getValue($realInstance));
}
$initializerMatcher->__invoke($proxy, $method, $params);
};
}
/**
* Generates a list of object | invoked method | parameters | expected result
*
* @return array
*/
public function getProxyMethods()
{
$selfHintParam = new ClassWithSelfHint();
$data = array(
array(
'ProxyManagerTestAsset\\BaseClass',
new BaseClass(),
'publicMethod',
array(),
'publicMethodDefault'
),
array(
'ProxyManagerTestAsset\\BaseClass',
new BaseClass(),
'publicTypeHintedMethod',
array(new \stdClass()),
'publicTypeHintedMethodDefault'
),
array(
'ProxyManagerTestAsset\\BaseClass',
new BaseClass(),
'publicByReferenceMethod',
array(),
'publicByReferenceMethodDefault'
),
);
if (PHP_VERSION_ID >= 50401) {
// PHP < 5.4.1 misbehaves, throwing strict standards, see https://bugs.php.net/bug.php?id=60573
$data[] = array(
'ProxyManagerTestAsset\\ClassWithSelfHint',
new ClassWithSelfHint(),
'selfHintMethod',
array('parameter' => $selfHintParam),
$selfHintParam
);
}
return $data;
}
/**
* Generates proxies and instances with a public property to feed to the property accessor methods
*
* @return array
*/
public function getPropertyAccessProxies()
{
$instance1 = new BaseClass();
$proxyName1 = $this->generateProxy(get_class($instance1));
$instance2 = new BaseClass();
$proxyName2 = $this->generateProxy(get_class($instance2));
return array(
array(
$instance1,
new $proxyName1($this->createInitializer('ProxyManagerTestAsset\\BaseClass', $instance1)),
'publicProperty',
'publicPropertyDefault',
),
array(
$instance2,
unserialize(
serialize(new $proxyName2($this->createInitializer('ProxyManagerTestAsset\\BaseClass', $instance2)))
),
'publicProperty',
'publicPropertyDefault',
),
);
}
}

View File

@@ -0,0 +1,160 @@
<?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.
*/
namespace ProxyManagerTest\Functional;
use ProxyManager\Generator\ClassGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
use ProxyManager\Proxy\GhostObjectInterface;
use ProxyManager\ProxyGenerator\LazyLoadingGhostGenerator;
use ReflectionClass;
/**
* Tests for {@see \ProxyManager\ProxyGenerator\LazyLoadingGhostGenerator} produced objects
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*
* @group Performance
* @coversNothing
*/
class LazyLoadingGhostPerformanceTest extends BaseLazyLoadingPerformanceTest
{
/**
* @outputBuffering
* @dataProvider getTestedClasses
*
* @param string $className
* @param array $methods
* @param array $properties
* @param \ReflectionProperty[] $reflectionProperties
*
* @return void
*/
public function testProxyInstantiationPerformance(
$className,
array $methods,
array $properties,
array $reflectionProperties
) {
$proxyName = $this->generateProxy($className);
$iterations = 20000;
$instances = array();
/* @var $proxies \ProxyManager\Proxy\GhostObjectInterface[] */
$proxies = array();
$realInstance = new $className();
$initializer = function (
GhostObjectInterface $proxy,
$method,
$params,
& $initializer
) use (
$reflectionProperties,
$realInstance
) {
$initializer = null;
foreach ($reflectionProperties as $reflectionProperty) {
$reflectionProperty->setValue($proxy, $reflectionProperty->getValue($realInstance));
}
return true;
};
$this->startCapturing();
for ($i = 0; $i < $iterations; $i += 1) {
$instances[] = new $className();
}
$baseProfile = $this->endCapturing(
'Instantiation for ' . $iterations . ' objects of type ' . $className . ': %fms / %fKb'
);
$this->startCapturing();
for ($i = 0; $i < $iterations; $i += 1) {
$proxies[] = new $proxyName($initializer);
}
$proxyProfile = $this->endCapturing(
'Instantiation for ' . $iterations . ' proxies of type ' . $className . ': %fms / %fKb'
);
$this->compareProfile($baseProfile, $proxyProfile);
$this->startCapturing();
foreach ($proxies as $proxy) {
$proxy->initializeProxy();
}
$this->endCapturing('Initialization of ' . $iterations . ' proxies of type ' . $className . ': %fms / %fKb');
foreach ($methods as $methodName => $parameters) {
$this->profileMethodAccess($className, $instances, $proxies, $methodName, $parameters);
}
foreach ($properties as $property) {
$this->profilePropertyWrites($className, $instances, $proxies, $property);
$this->profilePropertyReads($className, $instances, $proxies, $property);
$this->profilePropertyIsset($className, $instances, $proxies, $property);
$this->profilePropertyUnset($className, $instances, $proxies, $property);
}
}
/**
* @return array
*/
public function getTestedClasses()
{
$testedClasses = array(
array('stdClass', array(), array()),
array('ProxyManagerTestAsset\\BaseClass', array('publicMethod' => array()), array('publicProperty')),
);
foreach ($testedClasses as $key => $testedClass) {
$reflectionProperties = array();
$reflectionClass = new ReflectionClass($testedClass[0]);
foreach ($reflectionClass->getProperties() as $property) {
$property->setAccessible(true);
$reflectionProperties[$property->getName()] = $property;
}
$testedClasses[$key][] = $reflectionProperties;
}
return $testedClasses;
}
/**
* {@inheritDoc}
*/
protected function generateProxy($parentClassName)
{
$generatedClassName = __NAMESPACE__ . '\\' . UniqueIdentifierGenerator::getIdentifier('Foo');
$generator = new LazyLoadingGhostGenerator();
$generatedClass = new ClassGenerator($generatedClassName);
$strategy = new EvaluatingGeneratorStrategy();
$generator->generate(new ReflectionClass($parentClassName), $generatedClass);
$strategy->generate($generatedClass);
return $generatedClassName;
}
}

View File

@@ -0,0 +1,386 @@
<?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.
*/
namespace ProxyManagerTest\Functional;
use PHPUnit_Framework_TestCase;
use PHPUnit_Framework_MockObject_MockObject as Mock;
use ProxyManager\Generator\ClassGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
use ProxyManager\Proxy\VirtualProxyInterface;
use ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator;
use ProxyManagerTestAsset\BaseClass;
use ProxyManagerTestAsset\ClassWithPublicArrayProperty;
use ProxyManagerTestAsset\ClassWithPublicProperties;
use ProxyManagerTestAsset\ClassWithSelfHint;
use ReflectionClass;
/**
* Tests for {@see \ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator} produced objects
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*
* @group Functional
* @coversNothing
*/
class LazyLoadingValueHolderFunctionalTest extends PHPUnit_Framework_TestCase
{
/**
* @dataProvider getProxyMethods
*/
public function testMethodCalls($className, $instance, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($className);
/* @var $proxy \ProxyManager\Proxy\VirtualProxyInterface|BaseClass */
$proxy = new $proxyName($this->createInitializer($className, $instance));
$this->assertFalse($proxy->isProxyInitialized());
$this->assertSame($expectedValue, call_user_func_array(array($proxy, $method), $params));
$this->assertTrue($proxy->isProxyInitialized());
$this->assertSame($instance, $proxy->getWrappedValueHolderValue());
}
/**
* @dataProvider getProxyMethods
*/
public function testMethodCallsAfterUnSerialization($className, $instance, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($className);
/* @var $proxy \ProxyManager\Proxy\VirtualProxyInterface|BaseClass */
$proxy = unserialize(serialize(new $proxyName($this->createInitializer($className, $instance))));
$this->assertTrue($proxy->isProxyInitialized());
$this->assertSame($expectedValue, call_user_func_array(array($proxy, $method), $params));
$this->assertEquals($instance, $proxy->getWrappedValueHolderValue());
}
/**
* @dataProvider getProxyMethods
*/
public function testMethodCallsAfterCloning($className, $instance, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($className);
/* @var $proxy \ProxyManager\Proxy\VirtualProxyInterface|BaseClass */
$proxy = new $proxyName($this->createInitializer($className, $instance));
$cloned = clone $proxy;
$this->assertTrue($cloned->isProxyInitialized());
$this->assertNotSame($proxy->getWrappedValueHolderValue(), $cloned->getWrappedValueHolderValue());
$this->assertSame($expectedValue, call_user_func_array(array($cloned, $method), $params));
$this->assertEquals($instance, $cloned->getWrappedValueHolderValue());
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyReadAccess($instance, $proxy, $publicProperty, $propertyValue)
{
/* @var $proxy \ProxyManager\Proxy\VirtualProxyInterface|BaseClass */
$this->assertSame($propertyValue, $proxy->$publicProperty);
$this->assertTrue($proxy->isProxyInitialized());
$this->assertEquals($instance, $proxy->getWrappedValueHolderValue());
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyWriteAccess($instance, $proxy, $publicProperty)
{
/* @var $proxy \ProxyManager\Proxy\VirtualProxyInterface|BaseClass */
$newValue = uniqid();
$proxy->$publicProperty = $newValue;
$this->assertTrue($proxy->isProxyInitialized());
$this->assertSame($newValue, $proxy->$publicProperty);
$this->assertSame($newValue, $proxy->getWrappedValueHolderValue()->$publicProperty);
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyExistence($instance, $proxy, $publicProperty)
{
/* @var $proxy \ProxyManager\Proxy\VirtualProxyInterface|BaseClass */
$this->assertSame(isset($instance->$publicProperty), isset($proxy->$publicProperty));
$this->assertTrue($proxy->isProxyInitialized());
$this->assertEquals($instance, $proxy->getWrappedValueHolderValue());
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyAbsence($instance, $proxy, $publicProperty)
{
/* @var $proxy \ProxyManager\Proxy\VirtualProxyInterface|BaseClass */
$instance = $proxy->getWrappedValueHolderValue() ? $proxy->getWrappedValueHolderValue() : $instance;
$instance->$publicProperty = null;
$this->assertFalse(isset($proxy->$publicProperty));
$this->assertTrue($proxy->isProxyInitialized());
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyUnset($instance, $proxy, $publicProperty)
{
/* @var $proxy \ProxyManager\Proxy\VirtualProxyInterface|BaseClass */
$instance = $proxy->getWrappedValueHolderValue() ? $proxy->getWrappedValueHolderValue() : $instance;
unset($proxy->$publicProperty);
$this->assertTrue($proxy->isProxyInitialized());
$this->assertFalse(isset($instance->$publicProperty));
$this->assertFalse(isset($proxy->$publicProperty));
}
/**
* Verifies that accessing a public property containing an array behaves like in a normal context
*/
public function testCanWriteToArrayKeysInPublicProperty()
{
$instance = new ClassWithPublicArrayProperty();
$className = get_class($instance);
$initializer = $this->createInitializer($className, $instance);
$proxyName = $this->generateProxy($className);
/* @var $proxy ClassWithPublicArrayProperty */
$proxy = new $proxyName($initializer);
$proxy->arrayProperty['foo'] = 'bar';
$this->assertSame('bar', $proxy->arrayProperty['foo']);
$proxy->arrayProperty = array('tab' => 'taz');
$this->assertSame(array('tab' => 'taz'), $proxy->arrayProperty);
}
/**
* Verifies that public properties retrieved via `__get` don't get modified in the object itself
*/
public function testWillNotModifyRetrievedPublicProperties()
{
$instance = new ClassWithPublicProperties();
$className = get_class($instance);
$initializer = $this->createInitializer($className, $instance);
$proxyName = $this->generateProxy($className);
/* @var $proxy ClassWithPublicProperties */
$proxy = new $proxyName($initializer);
$variable = $proxy->property0;
$this->assertSame('property0', $variable);
$variable = 'foo';
$this->assertSame('property0', $proxy->property0);
}
/**
* Verifies that public properties references retrieved via `__get` modify in the object state
*/
public function testWillModifyByRefRetrievedPublicProperties()
{
$instance = new ClassWithPublicProperties();
$className = get_class($instance);
$initializer = $this->createInitializer($className, $instance);
$proxyName = $this->generateProxy($className);
/* @var $proxy ClassWithPublicProperties */
$proxy = new $proxyName($initializer);
$variable = & $proxy->property0;
$this->assertSame('property0', $variable);
$variable = 'foo';
$this->assertSame('foo', $proxy->property0);
}
/**
* @group 16
*
* Verifies that initialization of a value holder proxy may happen multiple times
*/
public function testWillAllowMultipleProxyInitialization()
{
$proxyClass = $this->generateProxy('ProxyManagerTestAsset\\BaseClass');
$counter = 0;
$initializer = function (& $wrappedInstance) use (& $counter) {
$wrappedInstance = new BaseClass();
$wrappedInstance->publicProperty = (string) ($counter += 1);
};
/* @var $proxy BaseClass */
$proxy = new $proxyClass($initializer);
$this->assertSame('1', $proxy->publicProperty);
$this->assertSame('2', $proxy->publicProperty);
$this->assertSame('3', $proxy->publicProperty);
}
/**
* Generates a proxy for the given class name, and retrieves its class name
*
* @param string $parentClassName
*
* @return string
*/
private function generateProxy($parentClassName)
{
$generatedClassName = __NAMESPACE__ . '\\' . UniqueIdentifierGenerator::getIdentifier('Foo');
$generator = new LazyLoadingValueHolderGenerator();
$generatedClass = new ClassGenerator($generatedClassName);
$strategy = new EvaluatingGeneratorStrategy();
$generator->generate(new ReflectionClass($parentClassName), $generatedClass);
$strategy->generate($generatedClass);
return $generatedClassName;
}
/**
* @param string $className
* @param object $realInstance
* @param Mock $initializerMatcher
*
* @return \Closure
*/
private function createInitializer($className, $realInstance, Mock $initializerMatcher = null)
{
if (null === $initializerMatcher) {
$initializerMatcher = $this->getMock('stdClass', array('__invoke'));
$initializerMatcher
->expects($this->once())
->method('__invoke')
->with(
$this->logicalAnd(
$this->isInstanceOf('ProxyManager\\Proxy\\VirtualProxyInterface'),
$this->isInstanceOf($className)
),
$realInstance
);
}
$initializerMatcher = $initializerMatcher ?: $this->getMock('stdClass', array('__invoke'));
return function (
& $wrappedObject,
VirtualProxyInterface $proxy,
$method,
$params,
& $initializer
) use (
$initializerMatcher,
$realInstance
) {
$initializer = null;
$wrappedObject = $realInstance;
$initializerMatcher->__invoke($proxy, $wrappedObject, $method, $params);
};
}
/**
* Generates a list of object | invoked method | parameters | expected result
*
* @return array
*/
public function getProxyMethods()
{
$selfHintParam = new ClassWithSelfHint();
$data = array(
array(
'ProxyManagerTestAsset\\BaseClass',
new BaseClass(),
'publicMethod',
array(),
'publicMethodDefault'
),
array(
'ProxyManagerTestAsset\\BaseClass',
new BaseClass(),
'publicTypeHintedMethod',
array(new \stdClass()),
'publicTypeHintedMethodDefault'
),
array(
'ProxyManagerTestAsset\\BaseClass',
new BaseClass(),
'publicByReferenceMethod',
array(),
'publicByReferenceMethodDefault'
),
array(
'ProxyManagerTestAsset\\BaseInterface',
new BaseClass(),
'publicMethod',
array(),
'publicMethodDefault'
),
);
if (PHP_VERSION_ID >= 50401) {
// PHP < 5.4.1 misbehaves, throwing strict standards, see https://bugs.php.net/bug.php?id=60573
$data[] = array(
'ProxyManagerTestAsset\\ClassWithSelfHint',
new ClassWithSelfHint(),
'selfHintMethod',
array('parameter' => $selfHintParam),
$selfHintParam
);
}
return $data;
}
/**
* Generates proxies and instances with a public property to feed to the property accessor methods
*
* @return array
*/
public function getPropertyAccessProxies()
{
$instance1 = new BaseClass();
$proxyName1 = $this->generateProxy(get_class($instance1));
$instance2 = new BaseClass();
$proxyName2 = $this->generateProxy(get_class($instance2));
return array(
array(
$instance1,
new $proxyName1($this->createInitializer('ProxyManagerTestAsset\\BaseClass', $instance1)),
'publicProperty',
'publicPropertyDefault',
),
array(
$instance2,
unserialize(
serialize(new $proxyName2($this->createInitializer('ProxyManagerTestAsset\\BaseClass', $instance2)))
),
'publicProperty',
'publicPropertyDefault',
),
);
}
}

View File

@@ -0,0 +1,134 @@
<?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.
*/
namespace ProxyManagerTest\Functional;
use ProxyManager\Generator\ClassGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
use ProxyManager\Proxy\VirtualProxyInterface;
use ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator;
use ReflectionClass;
/**
* Tests for {@see \ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator} produced objects
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*
* @group Performance
* @coversNothing
*/
class LazyLoadingValueHolderPerformanceTest extends BaseLazyLoadingPerformanceTest
{
/**
* @outputBuffering
* @dataProvider getTestedClasses
*
* @param string $className
* @param array $methods
* @param array $properties
*
* @return void
*/
public function testProxyInstantiationPerformance($className, array $methods, array $properties)
{
$proxyName = $this->generateProxy($className);
$iterations = 20000;
$instances = array();
/* @var $proxies \ProxyManager\Proxy\VirtualProxyInterface[] */
$proxies = array();
$initializer = function (
& $valueHolder,
VirtualProxyInterface $proxy,
$method,
$params,
& $initializer
) use ($className) {
$initializer = null;
$valueHolder = new $className();
return true;
};
$this->startCapturing();
for ($i = 0; $i < $iterations; $i += 1) {
$instances[] = new $className();
}
$baseProfile = $this->endCapturing(
'Instantiation for ' . $iterations . ' objects of type ' . $className . ': %fms / %fKb'
);
$this->startCapturing();
for ($i = 0; $i < $iterations; $i += 1) {
$proxies[] = new $proxyName($initializer);
}
$proxyProfile = $this->endCapturing(
'Instantiation for ' . $iterations . ' proxies of type ' . $className . ': %fms / %fKb'
);
$this->compareProfile($baseProfile, $proxyProfile);
$this->startCapturing();
foreach ($proxies as $proxy) {
$proxy->initializeProxy();
}
$this->endCapturing('Initialization of ' . $iterations . ' proxies of type ' . $className . ': %fms / %fKb');
foreach ($methods as $methodName => $parameters) {
$this->profileMethodAccess($className, $instances, $proxies, $methodName, $parameters);
}
foreach ($properties as $property) {
$this->profilePropertyWrites($className, $instances, $proxies, $property);
$this->profilePropertyReads($className, $instances, $proxies, $property);
$this->profilePropertyIsset($className, $instances, $proxies, $property);
$this->profilePropertyUnset($className, $instances, $proxies, $property);
}
}
/**
* @return array
*/
public function getTestedClasses()
{
return array(
array('stdClass', array(), array()),
array('ProxyManagerTestAsset\\BaseClass', array('publicMethod' => array()), array('publicProperty')),
);
}
/**
* {@inheritDoc}
*/
protected function generateProxy($parentClassName)
{
$generatedClassName = __NAMESPACE__ . '\\' . UniqueIdentifierGenerator::getIdentifier('Foo');
$generator = new LazyLoadingValueHolderGenerator();
$generatedClass = new ClassGenerator($generatedClassName);
$strategy = new EvaluatingGeneratorStrategy();
$generator->generate(new ReflectionClass($parentClassName), $generatedClass);
$strategy->generate($generatedClass);
return $generatedClassName;
}
}

View File

@@ -0,0 +1,123 @@
<?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.
*/
namespace ProxyManagerTest\Functional;
use PHPUnit_Framework_TestCase;
use ProxyManager\Factory\AccessInterceptorScopeLocalizerFactory;
use ProxyManager\Factory\AccessInterceptorValueHolderFactory;
use ProxyManager\Factory\LazyLoadingGhostFactory;
use ProxyManager\Factory\LazyLoadingValueHolderFactory;
use ReflectionClass;
use ReflectionProperty;
/**
* Verifies that proxy factories don't conflict with each other when generating proxies
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*
* @link https://github.com/Ocramius/ProxyManager/issues/10
*
* @group Functional
* @group issue-10
* @coversNothing
*/
class MultipleProxyGenerationTest extends PHPUnit_Framework_TestCase
{
/**
* Verifies that proxies generated from different factories will retain their specific implementation
* and won't conflict
*
* @dataProvider getTestedClasses
*/
public function testCanGenerateMultipleDifferentProxiesForSameClass($className)
{
$skipScopeLocalizerTests = false;
$ghostProxyFactory = new LazyLoadingGhostFactory();
$virtualProxyFactory = new LazyLoadingValueHolderFactory();
$accessInterceptorFactory = new AccessInterceptorValueHolderFactory();
$accessInterceptorScopeLocalizerFactory = new AccessInterceptorScopeLocalizerFactory();
$initializer = function () {
};
$reflectionClass = new ReflectionClass($className);
if ((! method_exists('Closure', 'bind')) && $reflectionClass->getProperties(ReflectionProperty::IS_PRIVATE)) {
$skipScopeLocalizerTests = true;
}
$generated = array(
$ghostProxyFactory->createProxy($className, $initializer),
$virtualProxyFactory->createProxy($className, $initializer),
$accessInterceptorFactory->createProxy(new $className()),
);
if (! $skipScopeLocalizerTests) {
$generated[] = $accessInterceptorScopeLocalizerFactory->createProxy(new $className());
}
foreach ($generated as $key => $proxy) {
$this->assertInstanceOf($className, $proxy);
foreach ($generated as $comparedKey => $comparedProxy) {
if ($comparedKey === $key) {
continue;
}
$this->assertNotSame(get_class($comparedProxy), get_class($proxy));
}
}
$this->assertInstanceOf('ProxyManager\Proxy\GhostObjectInterface', $generated[0]);
$this->assertInstanceOf('ProxyManager\Proxy\VirtualProxyInterface', $generated[1]);
$this->assertInstanceOf('ProxyManager\Proxy\AccessInterceptorInterface', $generated[2]);
$this->assertInstanceOf('ProxyManager\Proxy\ValueHolderInterface', $generated[2]);
if (! $skipScopeLocalizerTests) {
$this->assertInstanceOf('ProxyManager\Proxy\AccessInterceptorInterface', $generated[3]);
}
}
/**
* @return string[][]
*/
public function getTestedClasses()
{
$data = array(
array('ProxyManagerTestAsset\\BaseClass'),
array('ProxyManagerTestAsset\\ClassWithMagicMethods'),
array('ProxyManagerTestAsset\\ClassWithFinalMethods'),
array('ProxyManagerTestAsset\\ClassWithFinalMagicMethods'),
array('ProxyManagerTestAsset\\ClassWithByRefMagicMethods'),
array('ProxyManagerTestAsset\\ClassWithMixedProperties'),
array('ProxyManagerTestAsset\\ClassWithPrivateProperties'),
array('ProxyManagerTestAsset\\ClassWithProtectedProperties'),
array('ProxyManagerTestAsset\\ClassWithPublicProperties'),
array('ProxyManagerTestAsset\\EmptyClass'),
array('ProxyManagerTestAsset\\HydratedObject'),
);
if (PHP_VERSION_ID >= 50401) {
// PHP < 5.4.1 misbehaves, throwing strict standards, see https://bugs.php.net/bug.php?id=60573
$data[] = array('ProxyManagerTestAsset\\ClassWithSelfHint');
}
return $data;
}
}

View File

@@ -0,0 +1,223 @@
<?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.
*/
namespace ProxyManagerTest\Functional;
use PHPUnit_Framework_TestCase;
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
use ProxyManager\ProxyGenerator\NullObjectGenerator;
use ProxyManagerTestAsset\BaseClass;
use ProxyManagerTestAsset\ClassWithSelfHint;
use ReflectionClass;
use ProxyManager\Generator\ClassGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
/**
* Tests for {@see \ProxyManager\ProxyGenerator\NullObjectGenerator} produced objects
*
* @author Vincent Blanchon <blanchon.vincent@gmail.com>
* @license MIT
*
* @group Functional
* @coversNothing
*/
class NullObjectFunctionalTest extends PHPUnit_Framework_TestCase
{
/**
* @dataProvider getProxyMethods
*/
public function testMethodCalls($className, $instance, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($className);
/* @var $proxy \ProxyManager\Proxy\NullObjectInterface */
$proxy = new $proxyName();
$this->assertSame(null, call_user_func_array(array($proxy, $method), $params));
}
/**
* @dataProvider getProxyMethods
*/
public function testMethodCallsAfterUnSerialization($className, $instance, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($className);
/* @var $proxy \ProxyManager\Proxy\NullObjectInterface */
$proxy = unserialize(serialize(new $proxyName()));
$this->assertSame(null, call_user_func_array(array($proxy, $method), $params));
}
/**
* @dataProvider getProxyMethods
*/
public function testMethodCallsAfterCloning($className, $instance, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($className);
/* @var $proxy \ProxyManager\Proxy\NullObjectInterface */
$proxy = new $proxyName();
$cloned = clone $proxy;
$this->assertSame(null, call_user_func_array(array($cloned, $method), $params));
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyReadAccess($instance, $proxy, $publicProperty, $propertyValue)
{
/* @var $proxy \ProxyManager\Proxy\NullObjectInterface */
$this->assertSame(null, $proxy->$publicProperty);
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyWriteAccess($instance, $proxy, $publicProperty)
{
/* @var $proxy \ProxyManager\Proxy\NullObjectInterface */
$newValue = uniqid();
$proxy->$publicProperty = $newValue;
$this->assertSame($newValue, $proxy->$publicProperty);
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyExistence($instance, $proxy, $publicProperty)
{
/* @var $proxy \ProxyManager\Proxy\NullObjectInterface */
$this->assertSame(null, $proxy->$publicProperty);
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testPropertyUnset($instance, $proxy, $publicProperty)
{
/* @var $proxy \ProxyManager\Proxy\NullObjectInterface */
unset($proxy->$publicProperty);
$this->assertTrue(isset($instance->$publicProperty));
$this->assertFalse(isset($proxy->$publicProperty));
}
/**
* Generates a proxy for the given class name, and retrieves its class name
*
* @param string $parentClassName
*
* @return string
*/
private function generateProxy($parentClassName)
{
$generatedClassName = __NAMESPACE__ . '\\' . UniqueIdentifierGenerator::getIdentifier('Foo');
$generator = new NullObjectGenerator();
$generatedClass = new ClassGenerator($generatedClassName);
$strategy = new EvaluatingGeneratorStrategy();
$generator->generate(new ReflectionClass($parentClassName), $generatedClass);
$strategy->generate($generatedClass);
return $generatedClassName;
}
/**
* Generates a list of object | invoked method | parameters | expected result
*
* @return array
*/
public function getProxyMethods()
{
$selfHintParam = new ClassWithSelfHint();
$data = array(
array(
'ProxyManagerTestAsset\\BaseClass',
new BaseClass(),
'publicMethod',
array(),
'publicMethodDefault'
),
array(
'ProxyManagerTestAsset\\BaseClass',
new BaseClass(),
'publicTypeHintedMethod',
array('param' => new \stdClass()),
'publicTypeHintedMethodDefault'
),
array(
'ProxyManagerTestAsset\\BaseClass',
new BaseClass(),
'publicByReferenceMethod',
array(),
'publicByReferenceMethodDefault'
),
array(
'ProxyManagerTestAsset\\BaseInterface',
new BaseClass(),
'publicMethod',
array(),
'publicMethodDefault'
),
);
if (PHP_VERSION_ID >= 50401) {
// PHP < 5.4.1 misbehaves, throwing strict standards, see https://bugs.php.net/bug.php?id=60573
$data[] = array(
'ProxyManagerTestAsset\\ClassWithSelfHint',
new ClassWithSelfHint(),
'selfHintMethod',
array('parameter' => $selfHintParam),
$selfHintParam
);
}
return $data;
}
/**
* Generates proxies and instances with a public property to feed to the property accessor methods
*
* @return array
*/
public function getPropertyAccessProxies()
{
$instance1 = new BaseClass();
$proxyName1 = $this->generateProxy(get_class($instance1));
$instance2 = new BaseClass();
$proxyName2 = $this->generateProxy(get_class($instance2));
return array(
array(
$instance1,
new $proxyName1($instance1),
'publicProperty',
'publicPropertyDefault',
),
array(
$instance2,
unserialize(serialize(new $proxyName2($instance2))),
'publicProperty',
'publicPropertyDefault',
),
);
}
}

View File

@@ -0,0 +1,231 @@
<?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.
*/
namespace ProxyManagerTest\Functional;
use PHPUnit_Framework_TestCase;
use ProxyManager\Factory\RemoteObject\Adapter\JsonRpc as JsonRpcAdapter;
use ProxyManager\Factory\RemoteObject\Adapter\XmlRpc as XmlRpcAdapter;
use ProxyManager\Generator\ClassGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
use ProxyManager\ProxyGenerator\RemoteObjectGenerator;
use ProxyManagerTestAsset\ClassWithSelfHint;
use ProxyManagerTestAsset\RemoteProxy\Foo;
use ReflectionClass;
/**
* Tests for {@see \ProxyManager\ProxyGenerator\RemoteObjectGenerator} produced objects
*
* @author Vincent Blanchon <blanchon.vincent@gmail.com>
* @license MIT
*
* @group Functional
* @coversNothing
*/
class RemoteObjectFunctionalTest extends PHPUnit_Framework_TestCase
{
/**
* @param mixed $expectedValue
* @param string $method
* @param array $params
*
* @return XmlRpcAdapter
*/
protected function getXmlRpcAdapter($expectedValue, $method, array $params)
{
$client = $this
->getMockBuilder('Zend\Server\Client')
->setMethods(array('call'))
->getMock();
$client
->expects($this->any())
->method('call')
->with($this->stringEndsWith($method), $params)
->will($this->returnValue($expectedValue));
$adapter = new XmlRpcAdapter(
$client,
array(
'ProxyManagerTestAsset\RemoteProxy\Foo.foo'
=> 'ProxyManagerTestAsset\RemoteProxy\FooServiceInterface.foo'
)
);
return $adapter;
}
/**
* @param mixed $expectedValue
* @param string $method
* @param array $params
*
* @return JsonRpcAdapter
*/
protected function getJsonRpcAdapter($expectedValue, $method, array $params)
{
$client = $this
->getMockBuilder('Zend\Server\Client')
->setMethods(array('call'))
->getMock();
$client
->expects($this->any())
->method('call')
->with($this->stringEndsWith($method), $params)
->will($this->returnValue($expectedValue));
$adapter = new JsonRpcAdapter(
$client,
array(
'ProxyManagerTestAsset\RemoteProxy\Foo.foo'
=> 'ProxyManagerTestAsset\RemoteProxy\FooServiceInterface.foo'
)
);
return $adapter;
}
/**
* @dataProvider getProxyMethods
*/
public function testXmlRpcMethodCalls($instanceOrClassname, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($instanceOrClassname);
/* @var $proxy \ProxyManager\Proxy\RemoteObjectInterface */
$proxy = new $proxyName($this->getXmlRpcAdapter($expectedValue, $method, $params));
$this->assertSame($expectedValue, call_user_func_array(array($proxy, $method), $params));
}
/**
* @dataProvider getProxyMethods
*/
public function testJsonRpcMethodCalls($instanceOrClassname, $method, $params, $expectedValue)
{
$proxyName = $this->generateProxy($instanceOrClassname);
/* @var $proxy \ProxyManager\Proxy\RemoteObjectInterface */
$proxy = new $proxyName($this->getJsonRpcAdapter($expectedValue, $method, $params));
$this->assertSame($expectedValue, call_user_func_array(array($proxy, $method), $params));
}
/**
* @dataProvider getPropertyAccessProxies
*/
public function testJsonRpcPropertyReadAccess($instanceOrClassname, $publicProperty, $propertyValue)
{
$proxyName = $this->generateProxy($instanceOrClassname);
/* @var $proxy \ProxyManager\Proxy\RemoteObjectInterface */
$proxy = new $proxyName(
$this->getJsonRpcAdapter($propertyValue, '__get', array($publicProperty))
);
/* @var $proxy \ProxyManager\Proxy\NullObjectInterface */
$this->assertSame($propertyValue, $proxy->$publicProperty);
}
/**
* Generates a proxy for the given class name, and retrieves its class name
*
* @param string $parentClassName
*
* @return string
*/
private function generateProxy($parentClassName)
{
$generatedClassName = __NAMESPACE__ . '\\' . UniqueIdentifierGenerator::getIdentifier('Foo');
$generator = new RemoteObjectGenerator();
$generatedClass = new ClassGenerator($generatedClassName);
$strategy = new EvaluatingGeneratorStrategy();
$generator->generate(new ReflectionClass($parentClassName), $generatedClass);
$strategy->generate($generatedClass);
return $generatedClassName;
}
/**
* Generates a list of object | invoked method | parameters | expected result
*
* @return array
*/
public function getProxyMethods()
{
$selfHintParam = new ClassWithSelfHint();
$data = array(
array(
'ProxyManagerTestAsset\RemoteProxy\FooServiceInterface',
'foo',
array(),
'bar remote'
),
array(
'ProxyManagerTestAsset\RemoteProxy\Foo',
'foo',
array(),
'bar remote'
),
array(
new Foo(),
'foo',
array(),
'bar remote'
),
array(
'ProxyManagerTestAsset\RemoteProxy\BazServiceInterface',
'baz',
array('baz'),
'baz remote'
),
);
if (PHP_VERSION_ID >= 50401) {
// PHP < 5.4.1 misbehaves, throwing strict standards, see https://bugs.php.net/bug.php?id=60573
$data[] = array(
new ClassWithSelfHint(),
'selfHintMethod',
array($selfHintParam),
$selfHintParam
);
}
return $data;
}
/**
* Generates proxies and instances with a public property to feed to the property accessor methods
*
* @return array
*/
public function getPropertyAccessProxies()
{
return array(
array(
'ProxyManagerTestAsset\RemoteProxy\FooServiceInterface',
'publicProperty',
'publicProperty remote',
),
);
}
}