From fe7ef4bbeb2b1da767c44b6d31070f1e59854ecd Mon Sep 17 00:00:00 2001 From: romanb Date: Tue, 15 Dec 2009 21:06:32 +0000 Subject: [PATCH] [2.0][DDC-122][DDC-97][DDC-98][DDC-147] Refactored metadata mapping drivers by removing the useless PRELOAD mode. Introduced ClassMetadataFactory#getAllMetadata. Classloader simplifications. Old classloaders deprecated. New Doctrine\Common\ClassLoader is almost the same as the previous IsolatedClassLoader. --- bin/doctrine.php | 4 +- lib/Doctrine/Common/ClassLoader.php | 149 ++++++++++++++++++ lib/Doctrine/Common/DoctrineException.php | 1 + lib/Doctrine/Common/GlobalClassLoader.php | 2 + lib/Doctrine/Common/IsolatedClassLoader.php | 2 + .../Common/PropertyChangedListener.php | 6 +- lib/Doctrine/DBAL/Connection.php | 6 +- lib/Doctrine/ORM/Configuration.php | 2 + lib/Doctrine/ORM/EntityManager.php | 2 +- lib/Doctrine/ORM/EntityRepository.php | 1 + .../ORM/Mapping/ClassMetadataFactory.php | 52 ++++-- .../ORM/Mapping/Driver/AbstractFileDriver.php | 103 +++--------- .../ORM/Mapping/Driver/AnnotationDriver.php | 10 +- .../ORM/Mapping/Driver/DatabaseDriver.php | 8 +- lib/Doctrine/ORM/Mapping/Driver/Driver.php | 11 +- lib/Doctrine/ORM/Mapping/Driver/PhpDriver.php | 11 +- lib/Doctrine/ORM/Mapping/OneToOneMapping.php | 5 + .../AbstractCollectionPersister.php | 4 +- .../Persisters/StandardEntityPersister.php | 2 +- .../Tools/Cli/Tasks/GenerateProxiesTask.php | 8 +- .../ORM/Tools/Cli/Tasks/SchemaToolTask.php | 8 +- .../Tools/Export/ClassMetadataExporter.php | 6 +- lib/Doctrine/ORM/UnitOfWork.php | 23 ++- .../Tests/Mocks/MetadataDriverMock.php | 6 +- .../Tests/ORM/Mapping/MappingDriverTest.php | 30 +--- tests/Doctrine/Tests/TestInit.php | 4 +- tools/sandbox/cli-config.php | 8 +- tools/sandbox/doctrine.php | 5 +- tools/sandbox/index.php | 20 +-- 29 files changed, 307 insertions(+), 192 deletions(-) create mode 100644 lib/Doctrine/Common/ClassLoader.php diff --git a/bin/doctrine.php b/bin/doctrine.php index d93fd4f13..15585f8c5 100644 --- a/bin/doctrine.php +++ b/bin/doctrine.php @@ -1,8 +1,8 @@ register(); $cli = new \Doctrine\ORM\Tools\Cli\CliController(); diff --git a/lib/Doctrine/Common/ClassLoader.php b/lib/Doctrine/Common/ClassLoader.php new file mode 100644 index 000000000..b2c525595 --- /dev/null +++ b/lib/Doctrine/Common/ClassLoader.php @@ -0,0 +1,149 @@ +. + */ + +namespace Doctrine\Common; + +/** + * A ClassLoader is an autoloader for class files that can be + * installed on the SPL autoload stack. It is a class loader that loads only classes + * of a specific namespace or all namespaces and is suitable for working together + * with other autoloaders in the SPL autoload stack. + * + * If no include path is configured through {@link setIncludePath}, a ClassLoader + * relies on the PHP include_path. + * + * @author Roman Borschel + * @since 2.0 + */ +class ClassLoader +{ + private $_fileExtension = '.php'; + private $_namespace; + private $_includePath; + private $_namespaceSeparator = '\\'; + + /** + * Creates a new ClassLoader that loads classes of the + * specified namespace. + * + * @param string $ns The namespace to use. + */ + public function __construct($ns = null, $includePath = null) + { + $this->_namespace = $ns; + $this->_includePath = $includePath; + } + + /** + * Sets the namespace separator used by classes in the namespace of this class loader. + * + * @param string $sep The separator to use. + */ + public function setNamespaceSeparator($sep) + { + $this->_namespaceSeparator = $sep; + } + + /** + * Gets the namespace separator used by classes in the namespace of this class loader. + * + * @return string + */ + public function getNamespaceSeparator() + { + return $this->_namespaceSeparator; + } + + /** + * Sets the base include path for all class files in the namespace of this class loader. + * + * @param string $includePath + */ + public function setIncludePath($includePath) + { + $this->_includePath = $includePath; + } + + /** + * Gets the base include path for all class files in the namespace of this class loader. + * + * @return string + */ + public function getIncludePath() + { + return $this->_includePath; + } + + /** + * Sets the file extension of class files in the namespace of this class loader. + * + * @param string $fileExtension + */ + public function setFileExtension($fileExtension) + { + $this->_fileExtension = $fileExtension; + } + + /** + * Gets the file extension of class files in the namespace of this class loader. + * + * @return string + */ + public function getFileExtension() + { + return $this->_fileExtension; + } + + /** + * Installs this class loader on the SPL autoload stack. + */ + public function register() + { + spl_autoload_register(array($this, 'loadClass')); + } + + /** + * Uninstalls this class loader on the SPL autoload stack. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $classname The name of the class to load. + * @return boolean TRUE if the class has been successfully loaded, FALSE otherwise. + */ + public function loadClass($className) + { + if ($this->_namespace !== null && strpos($className, $this->_namespace.$this->_namespaceSeparator) !== 0) { + return false; + } + + require ($this->_includePath !== null ? $this->_includePath . DIRECTORY_SEPARATOR : '') + . str_replace($this->_namespaceSeparator, DIRECTORY_SEPARATOR, $className) + . $this->_fileExtension; + + return true; + } +} \ No newline at end of file diff --git a/lib/Doctrine/Common/DoctrineException.php b/lib/Doctrine/Common/DoctrineException.php index 6c63b4c4e..994d79fe9 100644 --- a/lib/Doctrine/Common/DoctrineException.php +++ b/lib/Doctrine/Common/DoctrineException.php @@ -31,6 +31,7 @@ namespace Doctrine\Common; * @author Guilherme Blanco * @author Jonathan Wage * @author Roman Borschel + * @todo Remove */ class DoctrineException extends \Exception { diff --git a/lib/Doctrine/Common/GlobalClassLoader.php b/lib/Doctrine/Common/GlobalClassLoader.php index 2c76f9d81..db06ddb19 100644 --- a/lib/Doctrine/Common/GlobalClassLoader.php +++ b/lib/Doctrine/Common/GlobalClassLoader.php @@ -46,6 +46,8 @@ namespace Doctrine\Common; * @author Guilherme Blanco * @author Jonathan Wage * @author Roman Borschel + * + * @deprecated Use Doctrine\Common\ClassLoader instead. */ class GlobalClassLoader { diff --git a/lib/Doctrine/Common/IsolatedClassLoader.php b/lib/Doctrine/Common/IsolatedClassLoader.php index 610489147..48768e396 100644 --- a/lib/Doctrine/Common/IsolatedClassLoader.php +++ b/lib/Doctrine/Common/IsolatedClassLoader.php @@ -31,6 +31,8 @@ namespace Doctrine\Common; * * @author Roman Borschel * @since 2.0 + * + * @deprecated Use Doctrine\Common\ClassLoader instead. */ class IsolatedClassLoader { diff --git a/lib/Doctrine/Common/PropertyChangedListener.php b/lib/Doctrine/Common/PropertyChangedListener.php index ec6bed89f..87c5b413d 100644 --- a/lib/Doctrine/Common/PropertyChangedListener.php +++ b/lib/Doctrine/Common/PropertyChangedListener.php @@ -36,8 +36,12 @@ namespace Doctrine\Common; interface PropertyChangedListener { /** - * @todo Document this function + * Notifies the listener of a property change. * + * @param object $sender The object on which the property changed. + * @param string $propertyName The name of the property that changed. + * @param mixed $oldValue The old value of the property that changed. + * @param mixed $newValue The new value of the property that changed. */ function propertyChanged($sender, $propertyName, $oldValue, $newValue); } diff --git a/lib/Doctrine/DBAL/Connection.php b/lib/Doctrine/DBAL/Connection.php index 5e4fb546a..4962664ff 100644 --- a/lib/Doctrine/DBAL/Connection.php +++ b/lib/Doctrine/DBAL/Connection.php @@ -179,9 +179,9 @@ class Connection $this->_config = $config; $this->_eventManager = $eventManager; - if (!isset($params['platform'])) { + if ( ! isset($params['platform'])) { $this->_platform = $driver->getDatabasePlatform(); - } else if($params['platform'] instanceof \Doctrine\DBAL\Platforms\AbstractPlatform) { + } else if ($params['platform'] instanceof Platforms\AbstractPlatform) { $this->_platform = $params['platform']; } else { throw DBALException::invalidPlatformSpecified(); @@ -632,7 +632,7 @@ class Connection /** * Returns the current transaction nesting level. * - * @return integer The nesting level. A value of 0 means theres no active transaction. + * @return integer The nesting level. A value of 0 means theres no active transaction. */ public function getTransactionNestingLevel() { diff --git a/lib/Doctrine/ORM/Configuration.php b/lib/Doctrine/ORM/Configuration.php index d91dd27e5..b0df3bb14 100644 --- a/lib/Doctrine/ORM/Configuration.php +++ b/lib/Doctrine/ORM/Configuration.php @@ -111,6 +111,8 @@ class Configuration extends \Doctrine\DBAL\Configuration * Sets the cache driver implementation that is used for metadata caching. * * @param object $driverImpl + * @todo Force parameter to be a Closure to ensure lazy evaluation + * (as soon as a metadata cache is in effect, the driver never needs to initialize). */ public function setMetadataDriverImpl($driverImpl) { diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index a6e43f278..b0818cf4f 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -210,7 +210,7 @@ class EntityManager * @internal Performance-sensitive method. */ public function getClassMetadata($className) - { + { return $this->_metadataFactory->getMetadataFor($className); } diff --git a/lib/Doctrine/ORM/EntityRepository.php b/lib/Doctrine/ORM/EntityRepository.php index 78cf37b2a..2a33075c6 100644 --- a/lib/Doctrine/ORM/EntityRepository.php +++ b/lib/Doctrine/ORM/EntityRepository.php @@ -89,6 +89,7 @@ class EntityRepository } if ( ! is_array($id) || count($id) <= 1) { + //FIXME: Not correct. Relies on specific order. $value = is_array($id) ? array_values($id) : array($id); $id = array_combine($this->_class->identifier, $value); } diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index c9ffeda87..1da00b0ca 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -50,6 +50,7 @@ class ClassMetadataFactory /** The used cache driver. */ private $_cacheDriver; private $_loadedMetadata = array(); + private $_initialized = false; /** * Creates a new factory instance that uses the given metadata driver implementation. @@ -59,24 +60,16 @@ class ClassMetadataFactory public function __construct(\Doctrine\ORM\EntityManager $em) { $this->_em = $em; - $this->_driver = $em->getConfiguration()->getMetadataDriverImpl(); - $this->_targetPlatform = $em->getConnection()->getDatabasePlatform(); - $this->_evm = $em->getEventManager(); } /** - * Sets the cache driver used by the factory to cache ClassMetadata instances - * and invokes the preload() method of the metadata driver to prepopulate the cache. + * Sets the cache driver used by the factory to cache ClassMetadata instances. * * @param Doctrine\Common\Cache\Cache $cacheDriver */ public function setCacheDriver($cacheDriver) { $this->_cacheDriver = $cacheDriver; - foreach ($this->_driver->preload() as $className) { - $cacheKey = "$className\$CLASSMETADATA"; - $this->_cacheDriver->save($cacheKey, $this->getMetadataFor($className), null); - } } /** @@ -93,6 +86,38 @@ class ClassMetadataFactory { return $this->_loadedMetadata; } + + /** + * Forces the factory to load the metadata of all classes known to the underlying + * mapping driver. + * + * @return array The ClassMetadata instances of all mapped classes. + */ + public function getAllMetadata() + { + if ( ! $this->_initialized) { + $this->_initialize(); + } + + $metadata = array(); + foreach ($this->_driver->getAllClassNames() as $className) { + $metadata[] = $this->getMetadataFor($className); + } + + return $metadata; + } + + /** + * Lazy initialization of this stuff, especially the metadata driver, + * since these are not needed at all when a metadata cache is active. + */ + private function _initialize() + { + $this->_driver = $this->_em->getConfiguration()->getMetadataDriverImpl(); + $this->_targetPlatform = $this->_em->getConnection()->getDatabasePlatform(); + $this->_evm = $this->_em->getEventManager(); + $this->_initialized = true; + } /** * Gets the class metadata descriptor for a class. @@ -121,9 +146,10 @@ class ClassMetadataFactory } /** + * Checks whether the factory has the metadata for a class loaded already. * - * @param $className - * @return boolean + * @param string $className + * @return boolean TRUE if the metadata of the class in question is already loaded, FALSE otherwise. */ public function hasMetadataFor($className) { @@ -152,6 +178,10 @@ class ClassMetadataFactory */ protected function _loadMetadata($name) { + if ( ! $this->_initialized) { + $this->_initialize(); + } + $loaded = array(); // Collect parent classes, ignoring transient (not-mapped) classes. diff --git a/lib/Doctrine/ORM/Mapping/Driver/AbstractFileDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AbstractFileDriver.php index c6040a6c4..d7301a1c2 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AbstractFileDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AbstractFileDriver.php @@ -25,40 +25,21 @@ use Doctrine\ORM\Mapping\MappingException; /** * Base driver for file-based metadata drivers. + * + * A file driver operates in a mode where it loads the mapping files of individual + * classes on demand. This requires the user to adhere to the convention of 1 mapping + * file per class and the file names of the mapping files must correspond to the full + * class name, including namespace, with the namespace delimiters '\', replaced by dots '.'. * * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.doctrine-project.com * @since 2.0 * @version $Revision: 1393 $ * @author Jonathan H. Wage + * @author Roman Borschel */ abstract class AbstractFileDriver implements Driver { - /** - * The FILE_PER_CLASS mode is an operating mode of the FileDriver where it loads - * the mapping files of individual classes on demand. This requires the user to - * adhere to the convention of 1 mapping file per class and the file names of - * the mapping files must correspond to the full class name, including namespace, - * with the namespace delimiters '\', replaced by dots '.'. - * This is the default behavior. - * - * Example: - * Class: My\Project\Model\User - * Mapping file: My.Project.Model.User.dcm.xml - * - * @var integer - */ - const FILE_PER_CLASS = 1; - - /** - * The PRELOAD mode is an operating mode of the FileDriver where it loads - * all mapping files in advance. It does not require a naming convention - * or the convention of 1 class per mapping file. - * - * @var integer - */ - const PRELOAD = 2; - /** * The paths where to look for mapping files. * @@ -66,13 +47,6 @@ abstract class AbstractFileDriver implements Driver */ protected $_paths; - /** - * The operating mode. Either FILE_PER_CLASS or PRELOAD. - * - * @var integer - */ - protected $_mode; - /** * The file extension of mapping documents. * @@ -80,24 +54,15 @@ abstract class AbstractFileDriver implements Driver */ protected $_fileExtension; - /** - * Any preloaded elements. - * - * @var array - */ - protected $_elements = array(); - /** * Initializes a new FileDriver that looks in the given path(s) for mapping * documents and operates in the specified operating mode. * * @param string|array $paths One or multiple paths where mapping documents can be found. - * @param integer $mode The operating mode. Either PRELOAD or FILE_PER_CLASS (default). */ - public function __construct($paths, $mode = self::FILE_PER_CLASS) + public function __construct($paths) { $this->_paths = (array) $paths; - $this->_mode = $mode; } /** @@ -129,24 +94,8 @@ abstract class AbstractFileDriver implements Driver */ public function getElement($className) { - if (isset($this->_elements[$className])) { - $element = $this->_elements[$className]; - unset($this->_elements[$className]); - return $element; - } else { - $result = $this->_loadMappingFile($this->_findMappingFile($className)); - return $result[$className]; - } - } - - /** - * Gets any preloaded elements. - * - * @return array - */ - public function getPreloadedElements() - { - return $this->_elements; + $result = $this->_loadMappingFile($this->_findMappingFile($className)); + return $result[$className]; } /** @@ -160,45 +109,37 @@ abstract class AbstractFileDriver implements Driver public function isTransient($className) { $isTransient = true; - if ($this->_mode == self::FILE_PER_CLASS) { - // check whether file exists - foreach ((array)$this->_paths as $path) { - if (file_exists($path . DIRECTORY_SEPARATOR . str_replace('\\', '.', $className) . $this->_fileExtension)) { - $isTransient = false; - break; - } + // check whether file exists + foreach ((array)$this->_paths as $path) { + if (file_exists($path . DIRECTORY_SEPARATOR . str_replace('\\', '.', $className) . $this->_fileExtension)) { + $isTransient = false; + break; } - } else { - $isTransient = isset($this->_elements[$className]); } return $isTransient; } - + /** - * Preloads all mapping information found in any documents within the - * configured paths and returns a list of class names that have been preloaded. + * Gets the names of all mapped classes known to this driver. * - * @return array The list of class names that have been preloaded. + * @return array The names of all mapped classes known to this driver. */ - public function preload($force = false) + public function getAllClassNames() { - if ($this->_mode != self::PRELOAD && ! $force) { - return array(); - } - + $clasNames = array(); foreach ((array)$this->_paths as $path) { if (is_dir($path)) { $files = glob($path . '/*'); foreach ($files as $file) { - $this->_elements = array_merge($this->_elements, $this->_loadMappingFile($file)); + $classNames[] = str_replace(array($this->_fileExtension, '.'), array('', '\\'), basename($file)); } } else if (is_file($path)) { - $this->_elements = array_merge($this->_elements, $this->_loadMappingFile($path)); + $classNames[] = str_replace(array($this->_fileExtension, '.'), array('', '\\'), basename($file)); } } - return array_keys($this->_elements); + return $classNames; } /** diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index 74bf53bc2..11a1e3e95 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -336,16 +336,19 @@ class AnnotationDriver implements Driver ! isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass']); } - public function preload() + /** + * {@inheritDoc} + */ + public function getAllClassNames() { if ($this->_classDirectory) { $iter = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->_classDirectory), - \RecursiveIteratorIterator::LEAVES_ONLY); + \RecursiveIteratorIterator::LEAVES_ONLY); $declared = get_declared_classes(); foreach ($iter as $item) { $info = pathinfo($item->getPathName()); - if (! isset($info['extension']) || $info['extension'] != 'php') { + if ( ! isset($info['extension']) || $info['extension'] != 'php') { continue; } require_once $item->getPathName(); @@ -363,4 +366,5 @@ class AnnotationDriver implements Driver return array(); } } + } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php b/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php index 1a77e0798..c541fc008 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php @@ -38,7 +38,6 @@ use Doctrine\Common\DoctrineException, * @version $Revision$ * @author Guilherme Blanco * @author Jonathan Wage - * @author Roman Borschel */ class DatabaseDriver implements Driver { @@ -170,12 +169,9 @@ class DatabaseDriver implements Driver } /** - * Preloads all mapping information found in any documents within the - * configured paths and returns a list of class names that have been preloaded. - * - * @return array The list of class names that have been preloaded. + * {@inheritDoc} */ - public function preload() + public function getAllClassNames() { $tables = array(); foreach ($this->_sm->listTables() as $table) { diff --git a/lib/Doctrine/ORM/Mapping/Driver/Driver.php b/lib/Doctrine/ORM/Mapping/Driver/Driver.php index 6b6dd8554..01ece3470 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/Driver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/Driver.php @@ -40,7 +40,14 @@ interface Driver * @param string $className * @param ClassMetadataInfo $metadata */ - public function loadMetadataForClass($className, ClassMetadataInfo $metadata); + function loadMetadataForClass($className, ClassMetadataInfo $metadata); + + /** + * Gets the names of all mapped classes known to this driver. + * + * @return array The names of all mapped classes known to this driver. + */ + function getAllClassNames(); /** * Whether the class with the specified name should have its metadata loaded. @@ -50,5 +57,5 @@ interface Driver * @param string $className * @return boolean */ - public function isTransient($className); + function isTransient($className); } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/Driver/PhpDriver.php b/lib/Doctrine/ORM/Mapping/Driver/PhpDriver.php index ee40b589f..0c7335018 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/PhpDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/PhpDriver.php @@ -66,23 +66,20 @@ class PhpDriver implements Driver } /** - * Preloads all mapping information found in any documents within the - * configured paths and returns a list of class names that have been preloaded. - * - * @return array The list of class names that have been preloaded. + * {@inheritDoc} */ - public function preload() + public function getAllClassNames() { if ( ! is_dir($this->_directory)) { throw MappingException::phpDriverRequiresConfiguredDirectoryPath(); } $iter = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->_directory), - \RecursiveIteratorIterator::LEAVES_ONLY); + \RecursiveIteratorIterator::LEAVES_ONLY); $classes = array(); foreach ($iter as $item) { $info = pathinfo($item->getPathName()); - if (! isset($info['extension']) || $info['extension'] != 'php') { + if ( ! isset($info['extension']) || $info['extension'] != 'php') { continue; } $className = $info['filename']; diff --git a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php index 6b377a170..8806c039a 100644 --- a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php +++ b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php @@ -225,6 +225,11 @@ class OneToOneMapping extends AssociationMapping if ($inverseField) { $hints['fetched'][$targetClass->rootEntityName][$inverseField] = true; } + /* cascade read-only status + if ($em->getUnitOfWork()->isReadOnly($sourceEntity)) { + $hints[Query::HINT_READ_ONLY] = true; + } + */ $targetEntity = $em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->load($joinColumnValues, $targetEntity, $this, $hints); diff --git a/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php b/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php index 42d2bf949..498aa8bb5 100644 --- a/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php +++ b/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php @@ -21,8 +21,8 @@ namespace Doctrine\ORM\Persisters; -use Doctrine\ORM\EntityManager; -use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\EntityManager, + Doctrine\ORM\PersistentCollection; /** * Base class for all collection persisters. diff --git a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php index 3bec7987e..2e863c70b 100644 --- a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php @@ -600,7 +600,7 @@ class StandardEntityPersister * @param $result The SQL result. * @param object $entity The entity object to fill. * @param array $hints Hints for entity creation. - * @return object The filled and managed entity object. + * @return object The filled and managed entity object or NULL, if the SQL result is empty. */ private function _createEntity($result, $entity = null, array $hints = array()) { diff --git a/lib/Doctrine/ORM/Tools/Cli/Tasks/GenerateProxiesTask.php b/lib/Doctrine/ORM/Tools/Cli/Tasks/GenerateProxiesTask.php index 8a614eda6..d288a270e 100644 --- a/lib/Doctrine/ORM/Tools/Cli/Tasks/GenerateProxiesTask.php +++ b/lib/Doctrine/ORM/Tools/Cli/Tasks/GenerateProxiesTask.php @@ -67,14 +67,8 @@ class GenerateProxiesTask extends AbstractTask $em = $this->getEntityManager(); $cmf = $em->getMetadataFactory(); - $driver = $em->getConfiguration()->getMetadataDriverImpl(); - $classes = array(); - $preloadedClasses = $driver->preload(true); - - foreach ($preloadedClasses as $className) { - $classes[] = $cmf->getMetadataFor($className); - } + $classes = $cmf->getAllMetadata(); $printer = $this->getPrinter(); $factory = $em->getProxyFactory(); diff --git a/lib/Doctrine/ORM/Tools/Cli/Tasks/SchemaToolTask.php b/lib/Doctrine/ORM/Tools/Cli/Tasks/SchemaToolTask.php index c2d154bc2..dd25e4170 100644 --- a/lib/Doctrine/ORM/Tools/Cli/Tasks/SchemaToolTask.php +++ b/lib/Doctrine/ORM/Tools/Cli/Tasks/SchemaToolTask.php @@ -158,14 +158,8 @@ class SchemaToolTask extends AbstractTask $em = $this->getEntityManager(); $cmf = $em->getMetadataFactory(); - $driver = $em->getConfiguration()->getMetadataDriverImpl(); - $classes = array(); - $preloadedClasses = $driver->preload(true); - - foreach ($preloadedClasses as $className) { - $classes[] = $cmf->getMetadataFor($className); - } + $classes = $cmf->getAllMetadata(); $printer = $this->getPrinter(); diff --git a/lib/Doctrine/ORM/Tools/Export/ClassMetadataExporter.php b/lib/Doctrine/ORM/Tools/Export/ClassMetadataExporter.php index ca17581f8..81f30fa9f 100644 --- a/lib/Doctrine/ORM/Tools/Export/ClassMetadataExporter.php +++ b/lib/Doctrine/ORM/Tools/Export/ClassMetadataExporter.php @@ -113,7 +113,7 @@ class ClassMetadataExporter if (is_null($source)) { throw DoctrineException::fileMappingDriversRequireDirectoryPath(); } - $driver = new $class($source, constant($class . '::PRELOAD')); + $driver = new $class($source); } else if ($class == 'Doctrine\ORM\Mapping\Driver\AnnotationDriver') { $reader = new \Doctrine\Common\Annotations\AnnotationReader(new \Doctrine\Common\Cache\ArrayCache); $reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\'); @@ -148,8 +148,8 @@ class ClassMetadataExporter foreach ($this->_mappingSources as $d) { list($source, $driver) = $d; - $preloadedClasses = $driver->preload(true); - foreach ($preloadedClasses as $className) { + $allClasses = $driver->getAllClassNames(); + foreach ($allClasses as $className) { if (class_exists($className, false)) { $metadata = new ClassMetadata($className); } else { diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 9f4258977..6964fc62f 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -223,6 +223,8 @@ class UnitOfWork implements PropertyChangedListener * @var array */ private $_orphanRemovals = array(); + + //private $_readOnlyObjects = array(); /** * Initializes a new UnitOfWork instance, bound to the given EntityManager. @@ -395,7 +397,7 @@ class UnitOfWork implements PropertyChangedListener $class = $this->_em->getClassMetadata($className); // Skip class if change tracking happens through notification - if ($class->isChangeTrackingNotify()) { + if ($class->isChangeTrackingNotify() /* || $class->isReadOnly*/) { continue; } @@ -405,7 +407,7 @@ class UnitOfWork implements PropertyChangedListener foreach ($entitiesToProcess as $entity) { // Ignore uninitialized proxy objects - if ($entity instanceof Proxy && ! $entity->__isInitialized__()) { + if (/* $entity is readOnly || */ $entity instanceof Proxy && ! $entity->__isInitialized__()) { continue; } // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION are processed here. @@ -1695,15 +1697,18 @@ class UnitOfWork implements PropertyChangedListener * Creates an entity. Used for reconstitution of entities during hydration. * * @ignore - * @param string $className The name of the entity class. - * @param array $data The data for the entity. - * @return object The created entity instance. + * @param string $className The name of the entity class. + * @param array $data The data for the entity. + * @param array $hints Any hints to account for during reconstitution/lookup of the entity. + * @return object The entity instance. * @internal Highly performance-sensitive method. + * * @todo Rename: getOrCreateEntity */ public function createEntity($className, array $data, &$hints = array()) { $class = $this->_em->getClassMetadata($className); + //$isReadOnly = isset($hints[Query::HINT_READ_ONLY]); if ($class->isIdentifierComposite) { $id = array(); @@ -1765,9 +1770,11 @@ class UnitOfWork implements PropertyChangedListener } } if ( ! $associatedId) { + // Foreign key is NULL $class->reflFields[$field]->setValue($entity, null); $this->_originalEntityData[$oid][$field] = null; } else { + // Foreign key is set // Check identity map first // FIXME: Can break easily with composite keys if join column values are in // wrong order. The correct order is the one in ClassMetadata#identifier. @@ -1788,15 +1795,15 @@ class UnitOfWork implements PropertyChangedListener $class->reflFields[$field]->setValue($entity, $newValue); } } else { - // Inverse side can never be lazy - $targetEntity = $assoc->load($entity, null, $this->_em); - $class->reflFields[$field]->setValue($entity, $targetEntity); + // Inverse side of x-to-one can never be lazy + $class->reflFields[$field]->setValue($entity, $assoc->load($entity, null, $this->_em)); } } else { // Inject collection $reflField = $class->reflFields[$field]; $pColl = new PersistentCollection( $this->_em, $targetClass, + //TODO: getValue might be superfluous once DDC-79 is implemented. $reflField->getValue($entity) ?: new ArrayCollection ); $pColl->setOwner($entity, $assoc); diff --git a/tests/Doctrine/Tests/Mocks/MetadataDriverMock.php b/tests/Doctrine/Tests/Mocks/MetadataDriverMock.php index 045720e90..fb533f465 100644 --- a/tests/Doctrine/Tests/Mocks/MetadataDriverMock.php +++ b/tests/Doctrine/Tests/Mocks/MetadataDriverMock.php @@ -2,9 +2,9 @@ namespace Doctrine\Tests\Mocks; -class MetadataDriverMock +class MetadataDriverMock implements \Doctrine\ORM\Mapping\Driver\Driver { - public function loadMetadataForClass($className, \Doctrine\ORM\Mapping\ClassMetadata $metadata) + public function loadMetadataForClass($className, \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata) { return; } @@ -14,7 +14,7 @@ class MetadataDriverMock return false; } - public function preload() + public function getAllClassNames() { return array(); } diff --git a/tests/Doctrine/Tests/ORM/Mapping/MappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/MappingDriverTest.php index 78db0af2a..848f4c54a 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/MappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/MappingDriverTest.php @@ -13,7 +13,7 @@ class MappingDriverTest extends \Doctrine\Tests\OrmTestCase public function testXmlMapping() { $className = 'Doctrine\Tests\ORM\Mapping\User'; - $xmlDriver = new XmlDriver(__DIR__ . DIRECTORY_SEPARATOR . 'xml', XmlDriver::FILE_PER_CLASS); + $xmlDriver = new XmlDriver(__DIR__ . DIRECTORY_SEPARATOR . 'xml'); $class = new ClassMetadata($className); @@ -38,36 +38,16 @@ class MappingDriverTest extends \Doctrine\Tests\OrmTestCase $this->_testUserClassMapping($class); } - public function testXmlPreloadMode() + public function testXmlGetAllClassNames() { $className = 'Doctrine\Tests\ORM\Mapping\User'; - $xmlDriver = new XmlDriver(__DIR__ . DIRECTORY_SEPARATOR . 'xml', XmlDriver::PRELOAD); + $xmlDriver = new XmlDriver(__DIR__ . DIRECTORY_SEPARATOR . 'xml'); $class = new ClassMetadata($className); - $classNames = $xmlDriver->preload(); + $classNames = $xmlDriver->getAllClassNames(); $this->assertEquals($className, $classNames[0]); - $this->assertEquals(1, count($xmlDriver->getPreloadedElements())); - - $xmlDriver->loadMetadataForClass($className, $class); - - $this->assertEquals(0, count($xmlDriver->getPreloadedElements())); - } - - public function testYamlPreloadMode() - { - $className = 'Doctrine\Tests\ORM\Mapping\User'; - $yamlDriver = new YamlDriver(__DIR__ . DIRECTORY_SEPARATOR . 'yaml', YamlDriver::PRELOAD); - $class = new ClassMetadata($className); - - $classNames = $yamlDriver->preload(); - - $this->assertEquals($className, $classNames[0]); - $this->assertEquals(1, count($yamlDriver->getPreloadedElements())); - - $yamlDriver->loadMetadataForClass($className, $class); - - $this->assertEquals(0, count($yamlDriver->getPreloadedElements())); + $this->assertEquals(1, count($classNames)); } private function _testUserClassMapping($class) diff --git a/tests/Doctrine/Tests/TestInit.php b/tests/Doctrine/Tests/TestInit.php index 1d363d1e0..5be820739 100644 --- a/tests/Doctrine/Tests/TestInit.php +++ b/tests/Doctrine/Tests/TestInit.php @@ -8,9 +8,9 @@ error_reporting(E_ALL | E_STRICT); require_once 'PHPUnit/Framework.php'; require_once 'PHPUnit/TextUI/TestRunner.php'; -require_once __DIR__ . '/../../../lib/Doctrine/Common/IsolatedClassLoader.php'; +require_once __DIR__ . '/../../../lib/Doctrine/Common/ClassLoader.php'; -$classLoader = new \Doctrine\Common\IsolatedClassLoader('Doctrine'); +$classLoader = new \Doctrine\Common\ClassLoader('Doctrine'); $classLoader->register(); set_include_path( diff --git a/tools/sandbox/cli-config.php b/tools/sandbox/cli-config.php index c7ada75f4..1b9e76eef 100644 --- a/tools/sandbox/cli-config.php +++ b/tools/sandbox/cli-config.php @@ -14,14 +14,12 @@ # named "cli-config.php" (this one) in the same directory and uses that by default. # -require_once __DIR__ . '/../../lib/Doctrine/Common/IsolatedClassLoader.php'; +require_once __DIR__ . '/../../lib/Doctrine/Common/ClassLoader.php'; -$classLoader = new \Doctrine\Common\IsolatedClassLoader('Entities'); -$classLoader->setBasePath(__DIR__); +$classLoader = new \Doctrine\Common\ClassLoader('Entities', __DIR__); $classLoader->register(); -$classLoader = new \Doctrine\Common\IsolatedClassLoader('Proxies'); -$classLoader->setBasePath(__DIR__); +$classLoader = new \Doctrine\Common\ClassLoader('Proxies', __DIR__); $classLoader->register(); $config = new \Doctrine\ORM\Configuration(); diff --git a/tools/sandbox/doctrine.php b/tools/sandbox/doctrine.php index a8370623b..902f50196 100644 --- a/tools/sandbox/doctrine.php +++ b/tools/sandbox/doctrine.php @@ -1,9 +1,8 @@ setBasePath(__DIR__ . '/../../lib'); +$classLoader = new \Doctrine\Common\ClassLoader('Doctrine', __DIR__ . '/../../lib'); $classLoader->register(); $cli = new \Doctrine\ORM\Tools\Cli\CliController(); diff --git a/tools/sandbox/index.php b/tools/sandbox/index.php index 2e72e374e..abaef7d2d 100644 --- a/tools/sandbox/index.php +++ b/tools/sandbox/index.php @@ -9,20 +9,22 @@ namespace Sandbox; -use Doctrine\ORM\Configuration, +use Doctrine\Common\ClassLoader, + Doctrine\ORM\Configuration, Doctrine\ORM\EntityManager, Doctrine\Common\Cache\ApcCache, Entities\User, Entities\Address; -require '../../lib/Doctrine/Common/GlobalClassLoader.php'; +require '../../lib/Doctrine/Common/ClassLoader.php'; -// Set up class loading, we could alternatively use several IsolatedClassLoaders. -// You could also use different autoloaders, provided by your favorite framework. -$classLoader = new \Doctrine\Common\GlobalClassLoader(); -$classLoader->registerNamespace('Doctrine', realpath(__DIR__ . '/../../lib')); -$classLoader->registerNamespace('Entities', __DIR__); -$classLoader->registerNamespace('Proxies', __DIR__); -$classLoader->register(); +// Set up class loading. You could use different autoloaders, provided by your favorite framework, +// if you want to. +$doctrineClassLoader = new ClassLoader('Doctrine', realpath(__DIR__ . '/../../lib')); +$doctrineClassLoader->register(); +$entitiesClassLoader = new ClassLoader('Entities', __DIR__); +$entitiesClassLoader->register(); +$proxiesClassLoader = new ClassLoader('Proxies', __DIR__); +$proxiesClassLoader->register(); // Set up caches $config = new Configuration;