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 @@
* <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.
......@@ -27,17 +29,15 @@ namespace Doctrine\ORM;
* @author Roman Borschel <roman@code-factory.org>
* @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 $_cacheDir;
private $_em;
/**
* 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.
* Generates and stores proxy class files in the given cache directory.
*
* @param EntityManager $em
* @param string $cacheDir
......@@ -48,67 +48,58 @@ class DynamicProxyGenerator
if ($cacheDir === null) {
$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 mixed $identifier
* @return object
* @param string $proxyClassName
* @param string $fileName
*/
public function getReferenceProxy($className, $identifier)
public function generateReferenceProxyClass($className)
{
$class = $this->_em->getClassMetadata($className);
$proxyClassName = str_replace('\\', '_', $className) . 'RProxy';
if ( ! class_exists($proxyClassName, false)) {
if (!class_exists($proxyClassName, false)) {
$this->_em->getMetadataFactory()->setMetadataFor(self::$_ns . $proxyClassName, $class);
$fileName = $this->_cacheDir . $proxyClassName . '.g.php';
if ( ! file_exists($fileName)) {
$this->_generateReferenceProxyClass($className, $identifier, $proxyClassName, $fileName);
if (file_exists($fileName)) {
require $fileName;
$proxyClassName = '\\' . self::$_ns . $proxyClassName;
return $proxyClassName;
}
require $fileName;
}
$proxyClassName = '\\' . self::$_ns . $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);
}
require $fileName;
}
$proxyClassName = '\\' . self::$_ns . $proxyClassName;
$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);
return new $proxyClassName($this->_em, $assoc, $owner);
file_put_contents($fileName, $file);
require $fileName;
$proxyClassName = '\\' . self::$_ns . $proxyClassName;
return $proxyClassName;
}
/**
* Generates a proxy class.
*
* @param string $className
* @param mixed $id
* @param string $proxyClassName
* @param string $fileName
*/
private function _generateReferenceProxyClass($className, $id, $proxyClassName, $fileName)
protected function _generateMethods(ClassMetadata $class)
{
$class = $this->_em->getClassMetadata($className);
$file = self::$_proxyClassTemplate;
$methods = '';
foreach ($class->reflClass->getMethods() as $method) {
if ($method->getName() == '__construct') {
continue;
}
if ($method->isPublic() && ! $method->isFinal()) {
$methods .= PHP_EOL . 'public function ' . $method->getName() . '(';
$firstParam = true;
......@@ -127,7 +118,11 @@ class DynamicProxyGenerator
$methods .= '}' . PHP_EOL;
}
}
return $methods;
}
public function _generateSleep(ClassMetadata $class)
{
$sleepImpl = '';
if ($class->reflClass->hasMethod('__sleep')) {
$sleepImpl .= 'return parent::__sleep();';
......@@ -144,29 +139,18 @@ class DynamicProxyGenerator
}
$sleepImpl .= ');';
}
$placeholders = array(
'<proxyClassName>', '<className>',
'<methods>', '<sleepImpl>'
);
$replacements = array(
$proxyClassName, $className, $methods, $sleepImpl
);
$file = str_replace($placeholders, $replacements, $file);
file_put_contents($fileName, $file);
return $sleepImpl;
}
/**
* 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 mixed $id
* @param string $proxyClassName
* @param string $fileName
*/
private function _generateAssociationProxyClass($className, $proxyClassName, $fileName)
public function generateAssociationProxyClass($className, $proxyClassName)
{
$class = $this->_em->getClassMetadata($className);
$file = self::$_assocProxyClassTemplate;
......@@ -220,6 +204,7 @@ class DynamicProxyGenerator
$file = str_replace($placeholders, $replacements, $file);
file_put_contents($fileName, $file);
return $fileName;
}
/** Proxy class code template */
......@@ -228,19 +213,18 @@ class DynamicProxyGenerator
/** This class was generated by the Doctrine ORM. DO NOT EDIT THIS FILE. */
namespace Doctrine\Generated\Proxies {
class <proxyClassName> extends \<className> {
private $_em;
private $_class;
private $_entityPersister;
private $_identifier;
private $_loaded = false;
public function __construct($em, $class, $identifier) {
$this->_em = $em;
$this->_class = $class;
$this->_class->setIdentifierValues($this, $identifier);
public function __construct($entityPersister, $identifier) {
$this->_entityPersister = $entityPersister;
$this->_identifier = $identifier;
}
private function _load() {
if ( ! $this->_loaded) {
$this->_em->getUnitOfWork()->getEntityPersister($this->_class->name)->load($this->_identifier, $this);
unset($this->_em);
unset($this->_class);
$this->_entityPersister->load($this->_identifier, $this);
unset($this->_entityPersister);
unset($this->_identifier);
$this->_loaded = true;
}
}
......
......@@ -26,6 +26,8 @@ use Doctrine\Common\DoctrineException;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\Mapping\ClassMetadata;
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.
......@@ -116,11 +118,11 @@ class EntityManager
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.
......@@ -144,7 +146,8 @@ class EntityManager
$this->_metadataFactory = new ClassMetadataFactory($this);
$this->_metadataFactory->setCacheDriver($this->_config->getMetadataCacheImpl());
$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
}
/**
* 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()
{
return $this->_proxyGenerator;
return $this->_proxyFactory;
}
/**
......@@ -595,4 +598,4 @@ class EntityManager
return new EntityManager($conn, $config, $eventManager);
}
}
\ No newline at end of file
}
......@@ -461,4 +461,4 @@ class StandardEntityPersister
{
$this->_evm->dispatchEvent(Events::postUpdate);
}
}
\ No newline at end of file
}
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