/** * 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]); } }
private static function spyInvokations($object, array $methodOverrides, ReflectionClass $reflection) { $factory = new AccessInterceptorValueHolderFactory(); $mock = $factory->createProxy($object); $id = spl_object_hash($mock); self::$mockInspectors[$id] = new ObjectInspector(); foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { if ($method->isStatic()) { continue; } $prefix = function ($proxy, $instance, $method, $parameters, &$returnEarly) use($id, $methodOverrides) { if (array_key_exists($method, $methodOverrides)) { $returnEarly = true; $returnValue = $methodOverrides[$method]; if ($returnValue instanceof \Closure) { $returnValue = call_user_func_array($returnValue, $parameters); } self::$mockInspectors[$id]->recordInvokation($method, array_values($parameters), $returnValue); return $returnValue; } }; $suffix = function ($proxy, $instance, $method, $parameters, $returnValue) use($id) { self::$mockInspectors[$id]->recordInvokation($method, array_values($parameters), $returnValue); }; $mock->setMethodPrefixInterceptor($method->getName(), $prefix); $mock->setMethodSuffixInterceptor($method->getName(), $suffix); } return $mock; }
/** * @param mixed $instance * * @return AccessInterceptorInterface */ public function generate($instance) : AccessInterceptorInterface { $class = new ReflectionClass($instance); $proxy = $this->proxyFactory->createProxy($instance); foreach ($this->collectCacheMethods($class) as $method => $annotations) { $this->registerPrefixInterceptor($proxy, $method, $annotations); $this->registerSuffixInterceptor($proxy, $method, $annotations); } return $proxy; }
public function proxyFromBean(AbstractBean $bean) { $object = $bean->getObject(); if ($object === null) { return null; } $rc = new \ReflectionClass($object); $methods = $rc->getMethods(); $beforeCall = $this->getBeforeCallMethod($methods, $bean); $afterCall = $this->getAfterCallMethod($methods, $bean); $proxyObject = $this->proxyFactory->createProxy($object, $beforeCall, $afterCall); return $proxyObject; }
/** * {@inheritDoc} * * @covers \ProxyManager\Factory\AccessInterceptorValueHolderFactory::__construct * @covers \ProxyManager\Factory\AccessInterceptorValueHolderFactory::createProxy * @covers \ProxyManager\Factory\AccessInterceptorValueHolderFactory::getGenerator * * NOTE: serious mocking going on in here (a class is generated on-the-fly) - careful */ public function testWillTryAutoGeneration() { $instance = new stdClass(); $proxyClassName = UniqueIdentifierGenerator::getIdentifier('bar'); $generator = $this->getMock('ProxyManager\\GeneratorStrategy\\GeneratorStrategyInterface'); $autoloader = $this->getMock('ProxyManager\\Autoloader\\AutoloaderInterface'); $this->config->expects($this->any())->method('getGeneratorStrategy')->will($this->returnValue($generator)); $this->config->expects($this->any())->method('getProxyAutoloader')->will($this->returnValue($autoloader)); $generator->expects($this->once())->method('generate')->with($this->callback(function (ClassGenerator $targetClass) use($proxyClassName) { return $targetClass->getName() === $proxyClassName; })); // simulate autoloading $autoloader->expects($this->once())->method('__invoke')->with($proxyClassName)->will($this->returnCallback(function () use($proxyClassName) { eval('class ' . $proxyClassName . ' extends \\ProxyManagerTestAsset\\AccessInterceptorValueHolderMock {}'); })); $this->inflector->expects($this->once())->method('getProxyClassName')->with('stdClass')->will($this->returnValue($proxyClassName)); $this->inflector->expects($this->once())->method('getUserClassName')->with('stdClass')->will($this->returnValue('ProxyManagerTestAsset\\LazyLoadingMock')); $this->signatureChecker->expects($this->atLeastOnce())->method('checkSignature'); $this->classSignatureGenerator->expects($this->once())->method('addSignature')->will($this->returnArgument(0)); $factory = new AccessInterceptorValueHolderFactory($this->config); /* @var $proxy \ProxyManagerTestAsset\AccessInterceptorValueHolderMock */ $proxy = $factory->createProxy($instance, array('foo'), array('bar')); $this->assertInstanceOf($proxyClassName, $proxy); $this->assertSame($instance, $proxy->instance); $this->assertSame(array('foo'), $proxy->prefixInterceptors); $this->assertSame(array('bar'), $proxy->suffixInterceptors); }
/** * @param Table $table * @return AccessInterceptorInterface */ public function createProxy(RestController $controller) { return $this->factory->createProxy($controller); }
<?php require_once __DIR__ . '/../vendor/autoload.php'; use ProxyManager\Factory\AccessInterceptorValueHolderFactory; class Foo { public function doFoo() { echo "Foo!\n"; } } $factory = new AccessInterceptorValueHolderFactory(); $proxy = $factory->createProxy(new Foo(), array('doFoo' => function () { echo "pre-foo!\n"; }), array('doFoo' => function () { echo "post-foo!\n"; })); $proxy->doFoo();
/** * Transform Entity to a new Object * If the target Object has the EntityProxy trait, then it will also generate a proxy class * * @param $objSrc * @return mixed */ public function transform($objSrc) { $objSrcData = $this->hydrator->extract($objSrc); $entityProxyClass = $this->annotationReader->getEntityTransformTargetClass(get_class($objSrc)); if ($entityProxyClass != $this->class) { $entityAnnotations = $this->annotationReader->getEntityAnnotations($this->class); $associations = $entityAnnotations->getAssociationMappings(); foreach ($associations as $key => $association) { if (isset($objSrcData[$key])) { $collection = $objSrcData[$key]; $items = array(); foreach ($collection as $item) { $items[] = $item; } $objSrcData[$key] = new ArrayCollection($items); } } $objDest = $this->instantiate($entityProxyClass); $this->hydrator->hydrate($objSrcData, $objDest); /** * Check if EntityProxy */ if ($this->isEntityProxy($objDest)) { $reflectionClass = new \ReflectionClass($objDest); $reflectionProperty = $reflectionClass->getProperty('entity'); $reflectionProperty->setAccessible(true); $reflectionProperty->setValue($objDest, $objSrc); $reflectionProperty->setAccessible(false); $reflectionProperty = $reflectionClass->getProperty('hydrator'); $reflectionProperty->setAccessible(true); $reflectionProperty->setValue($objDest, $this->hydrator); $reflectionProperty->setAccessible(false); $syncedPropertyAnnotations = $this->annotationReader->getEntityTransformationSyncedProperties($this->class); if ($syncedPropertyAnnotations) { $syncedProperties = $syncedPropertyAnnotations; } else { $syncedProperties = array_keys($objSrcData); } $reflectionProperty = $reflectionClass->getProperty('syncedProperties'); $reflectionProperty->setAccessible(true); $reflectionProperty->setValue($objDest, $syncedProperties); $reflectionProperty->setAccessible(false); $factory = new Factory(); $proxy = $factory->createProxy($objDest, array()); $syncAuto = $this->annotationReader->getEntitySyncAuto($this->class); if ($syncAuto) { $syncedListeners = $this->annotationReader->getEntitySyncListeners($this->class); if (!$syncedListeners) { $syncedListeners = array(); foreach (array_keys($objSrcData) as $property) { $syncedListeners[] = Inflector::camelize('set_' . $property); } foreach ($associations as $associationKey => $association) { $associationKey = Inflector::singularize($associationKey); $syncedListeners[] = Inflector::camelize('add_' . $associationKey); $syncedListeners[] = Inflector::camelize('remove_' . $associationKey); } } foreach ($syncedListeners as $syncListener) { $proxy->setMethodSuffixInterceptor($syncListener, function ($proxy, $instance) { $instance->syncToEntity(); }); } } return $proxy; } return $objDest; } return $objSrc; }
/** * Transform Entity to proxy object * * @param $objSrc * @return mixed * @throws \Exception */ public function transform($objSrc) { // No need to transform scalar or null values if (is_scalar($objSrc) || is_null($objSrc)) { return $objSrc; } $objSrcData = $this->hydrator->extract($objSrc); $objSrcClass = $this->annotationReader->getDoctrineProxyResolver()->unwrapDoctrineProxyClass(get_class($objSrc)); $proxyClass = $this->annotationReader->getProxyTargetClass(get_class($objSrc)); if (!$proxyClass) { return $objSrc; } if ($proxyClass != $objSrcClass) { $doctrineAnnotations = $this->annotationReader->getDoctrineAnnotations($this->class); $associations = $doctrineAnnotations->getAssociationNames(); /** * Lazy Load Associations */ foreach ($associations as $key) { if (isset($objSrcData[$key])) { $propertyValue = $objSrcData[$key]; $factory = new LazyLoadingValueHolderFactory(); $initializer = function (&$wrappedObject, LazyLoadingInterface $proxy, $method, array $parameters, &$initializer) use($propertyValue) { $initializer = null; if ($propertyValue instanceof ArrayCollection) { $items = array(); foreach ($propertyValue as $item) { $items[] = $this->proxyManager->transform($item); } $wrappedObject = new ArrayCollection($items); } else { $wrappedObject = $this->proxyManager->transform($propertyValue); } return true; }; // check if property final $reflectionClass = new \ReflectionClass(get_class($propertyValue)); if ($reflectionClass->isFinal()) { $objSrcData[$key] = $propertyValue; } else { if ($propertyValue instanceof Collection) { $objSrcData[$key] = $factory->createProxy(get_class($propertyValue), $initializer); } else { $objSrcData[$key] = $factory->createProxy($this->annotationReader->getProxyTargetClass(get_class($propertyValue)), $initializer); } } } } $objDest = $this->proxyManager->instantiate($proxyClass); /** * Throw Exceptions */ if (!$objDest instanceof $objSrcClass) { throw new \Exception('The proxy target class should extend the underlying data object. Proxy Class: ' . $proxyClass); } if (!$this->isProxy($objDest)) { throw new \Exception('The proxy target class should use the Proxy trait. Proxy Class: ' . $proxyClass); } /** * Hydrate the data */ $this->hydrate($objSrcData, $objDest); /** * Sync Properties */ $syncProperties = $this->annotationReader->getProxySyncedProperties($this->class); if ($syncProperties == Constants::SYNC_PROPERTIES_ALL) { $syncProperties = array_keys($objSrcData); } /** * Sync Methods */ $syncMethods = $this->annotationReader->getProxySyncMethods($this->class); if ($syncMethods == Constants::SYNC_METHODS_ALL) { $syncMethods = array(); foreach (array_keys($objSrcData) as $property) { $syncMethods[] = Inflector::camelize('set_' . $property); } foreach ($associations as $associationKey => $association) { $associationKey = Inflector::singularize($associationKey); $associationKeyPlural = Inflector::pluralize($associationKey); $syncMethods[] = Inflector::camelize('add_' . $associationKey); $syncMethods[] = Inflector::camelize('remove_' . $associationKey); $syncMethods[] = Inflector::camelize('set_' . $associationKeyPlural); $syncMethods[] = Inflector::camelize('set_' . $associationKey); } } elseif ($syncMethods == Constants::SYNC_METHODS_NONE) { $syncMethods = array(); } /** * Set properties on proxied object */ PropertyAccess::set($objDest, 'dataObject', $objSrc); PropertyAccess::set($objDest, 'transformer', $this); PropertyAccess::set($objDest, 'syncProperties', $syncProperties); PropertyAccess::set($objDest, 'syncMethods', $syncMethods); /** * Attach Interceptors */ $factory = new Factory(); $proxy = $factory->createProxy($objDest, array()); foreach ($syncMethods as $syncMethod) { $proxy->setMethodSuffixInterceptor($syncMethod, function ($proxy, $instance) use($syncMethod) { $syncMethods = PropertyAccess::get($instance, 'syncMethods'); if (is_array($syncMethods) && in_array($syncMethod, $syncMethods)) { $instance->syncData(); } }); } return $proxy; } return $objSrc; }
/** * @param Table $table * @return AccessInterceptorInterface */ public function createProxy(Table $table) { return $this->factory->createProxy($table); }
/** * Create a new entity manager * * @param DriverInterface $driver * @param MapperInterface $mapper * @param SerialiserMap $serialiser_map * @param KeySchemeInterface $key_scheme * @param Configuration $configuration * @param EntityCachingInterface $cache * @return EntityManager */ public static function build(DriverInterface $driver, MapperInterface $mapper, SerialiserMap $serialiser_map = null, KeySchemeInterface $key_scheme = null, Configuration $configuration = null, EntityCachingInterface $cache = null) { $em_conf = $configuration ?: new Configuration(); $proxy_conf = new \ProxyManager\Configuration(); $proxy_conf->setProxiesTargetDir($em_conf->getCacheDir()); $proxy_conf->setProxiesNamespace(Writer::PROXY_NAMESPACE); $proxy_factory = new AccessInterceptorValueHolderFactory($proxy_conf); $interceptor_factor = new EntityManagerInterceptorFactory(); $em = new self($driver, $mapper, $serialiser_map, $key_scheme, $em_conf, $cache); $proxy = $proxy_factory->createProxy($em, $interceptor_factor->getPrefixInterceptors(), $interceptor_factor->getSuffixInterceptors()); $em->setProxy($proxy); return $proxy; }