Commit 197ed0b7 authored by piccoloprincipe's avatar piccoloprincipe

[2.0] working implementation of reference proxies

parent 0b9a2e0c
<?php
/*
* $Id$
*
* 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 LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\DynamicProxy;
use Doctrine\ORM\EntityManager;
/**
* This Factory is used to create proxy objects for entities at runtime.
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
*/
class Factory
{
private $_em;
private $_generator;
/**
* Initializes a new instance of the <tt>DynamicProxyGenerator</tt> class that is
* connected to the given <tt>EntityManager</tt> and stores proxy class files in
* the given cache directory.
*
* @param EntityManager $em
* @param Generator $generator
*/
public function __construct(EntityManager $em, Generator $generator)
{
$this->_em = $em;
$this->_generator = $generator;
}
/**
* Gets a reference proxy instance.
*
* @param string $className
* @param mixed $identifier
* @return object
*/
public function getReferenceProxy($className, $identifier)
{
$proxyClassName = $this->_generator->generateReferenceProxyClass($className);
$entityPersister = $this->_em->getUnitOfWork()->getEntityPersister($className);
return new $proxyClassName($entityPersister, $identifier);
}
/**
* Gets an association proxy instance.
*/
public function getAssociationProxy($owner, \Doctrine\ORM\Mapping\AssociationMapping $assoc)
{
throw new Exception("Not yet implemented.");
$proxyClassName = str_replace('\\', '_', $assoc->getTargetEntityName()) . 'AProxy';
if ( ! class_exists($proxyClassName, false)) {
$this->_em->getMetadataFactory()->setMetadataFor(self::$_ns . $proxyClassName, $this->_em->getClassMetadata($assoc->getTargetEntityName()));
$fileName = $this->_cacheDir . $proxyClassName . '.g.php';
if ( ! file_exists($fileName)) {
$this->_generateAssociationProxyClass($assoc->getTargetEntityName(), $proxyClassName, $fileName);
}
require $fileName;
}
$proxyClassName = '\\' . self::$_ns . $proxyClassName;
return new $proxyClassName($this->_em, $assoc, $owner);
}
}
...@@ -19,7 +19,9 @@ ...@@ -19,7 +19,9 @@
* <http://www.doctrine-project.org>. * <http://www.doctrine-project.org>.
*/ */
namespace Doctrine\ORM; namespace Doctrine\ORM\DynamicProxy;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;
/** /**
* The DynamicProxyGenerator is used to generate proxy objects for entities at runtime. * The DynamicProxyGenerator is used to generate proxy objects for entities at runtime.
...@@ -27,17 +29,15 @@ namespace Doctrine\ORM; ...@@ -27,17 +29,15 @@ namespace Doctrine\ORM;
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @since 2.0 * @since 2.0
*/ */
class DynamicProxyGenerator class Generator
{ {
/** The namspace for the generated proxy classes. */ /** The namespace for the generated proxy classes. */
private static $_ns = 'Doctrine\Generated\Proxies\\'; private static $_ns = 'Doctrine\Generated\Proxies\\';
private $_cacheDir; private $_cacheDir;
private $_em; private $_em;
/** /**
* Initializes a new instance of the <tt>DynamicProxyGenerator</tt> class that is * Generates and stores proxy class files in the given cache directory.
* connected to the given <tt>EntityManager</tt> and stores proxy class files in
* the given cache directory.
* *
* @param EntityManager $em * @param EntityManager $em
* @param string $cacheDir * @param string $cacheDir
...@@ -48,67 +48,58 @@ class DynamicProxyGenerator ...@@ -48,67 +48,58 @@ class DynamicProxyGenerator
if ($cacheDir === null) { if ($cacheDir === null) {
$cacheDir = sys_get_temp_dir(); $cacheDir = sys_get_temp_dir();
} }
$this->_cacheDir = $cacheDir; $this->_cacheDir = rtrim($cacheDir, '/') . '/';
} }
/** /**
* Gets a reference proxy instance. * Generates a reference proxy class.
* This is a proxy for an object which we have the id for retrieval.
* *
* @param string $className * @param string $className
* @param mixed $identifier * @param string $proxyClassName
* @return object * @param string $fileName
*/ */
public function getReferenceProxy($className, $identifier) public function generateReferenceProxyClass($className)
{ {
$class = $this->_em->getClassMetadata($className); $class = $this->_em->getClassMetadata($className);
$proxyClassName = str_replace('\\', '_', $className) . 'RProxy'; $proxyClassName = str_replace('\\', '_', $className) . 'RProxy';
if ( ! class_exists($proxyClassName, false)) { if (!class_exists($proxyClassName, false)) {
$this->_em->getMetadataFactory()->setMetadataFor(self::$_ns . $proxyClassName, $class); $this->_em->getMetadataFactory()->setMetadataFor(self::$_ns . $proxyClassName, $class);
$fileName = $this->_cacheDir . $proxyClassName . '.g.php'; $fileName = $this->_cacheDir . $proxyClassName . '.g.php';
if ( ! file_exists($fileName)) { if (file_exists($fileName)) {
$this->_generateReferenceProxyClass($className, $identifier, $proxyClassName, $fileName);
}
require $fileName; require $fileName;
}
$proxyClassName = '\\' . self::$_ns . $proxyClassName; $proxyClassName = '\\' . self::$_ns . $proxyClassName;
return $proxyClassName;
return new $proxyClassName($this->_em, $class, $identifier);
} }
/**
* Gets an association proxy instance.
*/
public function getAssociationProxy($owner, \Doctrine\ORM\Mapping\AssociationMapping $assoc)
{
$proxyClassName = str_replace('\\', '_', $assoc->getTargetEntityName()) . 'AProxy';
if ( ! class_exists($proxyClassName, false)) {
$this->_em->getMetadataFactory()->setMetadataFor(self::$_ns . $proxyClassName, $this->_em->getClassMetadata($assoc->getTargetEntityName()));
$fileName = $this->_cacheDir . $proxyClassName . '.g.php';
if ( ! file_exists($fileName)) {
$this->_generateAssociationProxyClass($assoc->getTargetEntityName(), $proxyClassName, $fileName);
} }
$file = self::$_proxyClassTemplate;
$methods = $this->_generateMethods($class);
$sleepImpl = $this->_generateSleep($class);
$placeholders = array(
'<proxyClassName>', '<className>',
'<methods>', '<sleepImpl>'
);
$replacements = array(
$proxyClassName, $className, $methods, $sleepImpl
);
$file = str_replace($placeholders, $replacements, $file);
file_put_contents($fileName, $file);
require $fileName; require $fileName;
}
$proxyClassName = '\\' . self::$_ns . $proxyClassName; $proxyClassName = '\\' . self::$_ns . $proxyClassName;
return $proxyClassName;
return new $proxyClassName($this->_em, $assoc, $owner);
} }
/** protected function _generateMethods(ClassMetadata $class)
* Generates a proxy class.
*
* @param string $className
* @param mixed $id
* @param string $proxyClassName
* @param string $fileName
*/
private function _generateReferenceProxyClass($className, $id, $proxyClassName, $fileName)
{ {
$class = $this->_em->getClassMetadata($className);
$file = self::$_proxyClassTemplate;
$methods = ''; $methods = '';
foreach ($class->reflClass->getMethods() as $method) { foreach ($class->reflClass->getMethods() as $method) {
if ($method->getName() == '__construct') {
continue;
}
if ($method->isPublic() && ! $method->isFinal()) { if ($method->isPublic() && ! $method->isFinal()) {
$methods .= PHP_EOL . 'public function ' . $method->getName() . '('; $methods .= PHP_EOL . 'public function ' . $method->getName() . '(';
$firstParam = true; $firstParam = true;
...@@ -127,7 +118,11 @@ class DynamicProxyGenerator ...@@ -127,7 +118,11 @@ class DynamicProxyGenerator
$methods .= '}' . PHP_EOL; $methods .= '}' . PHP_EOL;
} }
} }
return $methods;
}
public function _generateSleep(ClassMetadata $class)
{
$sleepImpl = ''; $sleepImpl = '';
if ($class->reflClass->hasMethod('__sleep')) { if ($class->reflClass->hasMethod('__sleep')) {
$sleepImpl .= 'return parent::__sleep();'; $sleepImpl .= 'return parent::__sleep();';
...@@ -144,29 +139,18 @@ class DynamicProxyGenerator ...@@ -144,29 +139,18 @@ class DynamicProxyGenerator
} }
$sleepImpl .= ');'; $sleepImpl .= ');';
} }
return $sleepImpl;
$placeholders = array(
'<proxyClassName>', '<className>',
'<methods>', '<sleepImpl>'
);
$replacements = array(
$proxyClassName, $className, $methods, $sleepImpl
);
$file = str_replace($placeholders, $replacements, $file);
file_put_contents($fileName, $file);
} }
/** /**
* Generates a proxy class. * Generates a proxy class.
* This is a proxy class for an object which we have the association where
* it is involved, but no primary key to retrieve it.
* *
* @param string $className * @param string $className
* @param mixed $id
* @param string $proxyClassName * @param string $proxyClassName
* @param string $fileName
*/ */
private function _generateAssociationProxyClass($className, $proxyClassName, $fileName) public function generateAssociationProxyClass($className, $proxyClassName)
{ {
$class = $this->_em->getClassMetadata($className); $class = $this->_em->getClassMetadata($className);
$file = self::$_assocProxyClassTemplate; $file = self::$_assocProxyClassTemplate;
...@@ -220,6 +204,7 @@ class DynamicProxyGenerator ...@@ -220,6 +204,7 @@ class DynamicProxyGenerator
$file = str_replace($placeholders, $replacements, $file); $file = str_replace($placeholders, $replacements, $file);
file_put_contents($fileName, $file); file_put_contents($fileName, $file);
return $fileName;
} }
/** Proxy class code template */ /** Proxy class code template */
...@@ -228,19 +213,18 @@ class DynamicProxyGenerator ...@@ -228,19 +213,18 @@ class DynamicProxyGenerator
/** This class was generated by the Doctrine ORM. DO NOT EDIT THIS FILE. */ /** This class was generated by the Doctrine ORM. DO NOT EDIT THIS FILE. */
namespace Doctrine\Generated\Proxies { namespace Doctrine\Generated\Proxies {
class <proxyClassName> extends \<className> { class <proxyClassName> extends \<className> {
private $_em; private $_entityPersister;
private $_class; private $_identifier;
private $_loaded = false; private $_loaded = false;
public function __construct($em, $class, $identifier) { public function __construct($entityPersister, $identifier) {
$this->_em = $em; $this->_entityPersister = $entityPersister;
$this->_class = $class; $this->_identifier = $identifier;
$this->_class->setIdentifierValues($this, $identifier);
} }
private function _load() { private function _load() {
if ( ! $this->_loaded) { if ( ! $this->_loaded) {
$this->_em->getUnitOfWork()->getEntityPersister($this->_class->name)->load($this->_identifier, $this); $this->_entityPersister->load($this->_identifier, $this);
unset($this->_em); unset($this->_entityPersister);
unset($this->_class); unset($this->_identifier);
$this->_loaded = true; $this->_loaded = true;
} }
} }
......
...@@ -26,6 +26,8 @@ use Doctrine\Common\DoctrineException; ...@@ -26,6 +26,8 @@ use Doctrine\Common\DoctrineException;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadataFactory; use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\DynamicProxy\Factory as ProxyFactory;
use Doctrine\ORM\DynamicProxy\Generator;
/** /**
* The EntityManager is the central access point to ORM functionality. * The EntityManager is the central access point to ORM functionality.
...@@ -116,11 +118,11 @@ class EntityManager ...@@ -116,11 +118,11 @@ class EntityManager
private $_hydrators = array(); private $_hydrators = array();
/** /**
* The proxy generator. * The proxy factory which creates association or reference proxies.
* *
* @var DynamicProxyGenerator * @var ProxyFactory
*/ */
private $_proxyGenerator; private $_proxyFactory;
/** /**
* Whether the EntityManager is closed or not. * Whether the EntityManager is closed or not.
...@@ -144,7 +146,8 @@ class EntityManager ...@@ -144,7 +146,8 @@ class EntityManager
$this->_metadataFactory = new ClassMetadataFactory($this); $this->_metadataFactory = new ClassMetadataFactory($this);
$this->_metadataFactory->setCacheDriver($this->_config->getMetadataCacheImpl()); $this->_metadataFactory->setCacheDriver($this->_config->getMetadataCacheImpl());
$this->_unitOfWork = new UnitOfWork($this); $this->_unitOfWork = new UnitOfWork($this);
$this->_proxyGenerator = new DynamicProxyGenerator($this, $this->_config->getCacheDir()); //FIX: this should be in a factory
$this->_proxyFactory = new ProxyFactory($this, new Generator($this, $this->_config->getCacheDir()));
} }
/** /**
...@@ -559,13 +562,13 @@ class EntityManager ...@@ -559,13 +562,13 @@ class EntityManager
} }
/** /**
* Gets the proxy generator used by the EntityManager to create entity proxies. * Gets the proxy factory used by the EntityManager to create entity proxies.
* *
* @return DynamicProxyGenerator * @return ProxyFactory
*/ */
public function getProxyGenerator() public function getProxyGenerator()
{ {
return $this->_proxyGenerator; return $this->_proxyFactory;
} }
/** /**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment