Skip to content

Commit

Permalink
Drop doctrine/common proxies in favor of ProxyManager
Browse files Browse the repository at this point in the history
  • Loading branch information
alcaeus committed Nov 9, 2018
1 parent 4b59521 commit 4681d8b
Show file tree
Hide file tree
Showing 34 changed files with 467 additions and 473 deletions.
22 changes: 22 additions & 0 deletions UPGRADE-2.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
* The `setRetryConnect` and `setRetryQuery` methods have been dropped without
replacement. You should implement proper error handling instead of simply
re-running queries or connection attempts.
* The `AUTOGENERATE_ALWAYS` and `AUTOGENERATE_NEVER` generation strategies for
proxy objects have been removed. Use `AUTOGENERATE_EVAL` and
`AUTOGENERATE_FILE_NOT_EXISTS` instead. This does not affect hydrator or
collection generation strategies.

## Cursor changes

Expand Down Expand Up @@ -169,6 +173,24 @@ or annotations. To migrate away from YAML mappings, first update to MongoDB ODM
without replacement. You should register a class loader in the
`AnnotationRegistry` instead.

## Proxy objects

The proxy implementation no longer relies on Doctrine proxies but rather
the [Proxy Manager](https:/ocramius/ProxyManager) library by
ocramius. If you are checking for proxies, the following changed:
* Proxies no longer implement `Doctrine\ODM\MongoDB\Proxy\Proxy` or
any other Doctrine proxy interface. To check whether a returned object is a
proxy, check for the `ProxyManager\Proxy\GhostObjectInterface` interface.
* The `__load` method has been replaced by `initializeProxy`.
* The `__isInitialized` method has been replaced by `isProxyInitialized`.
* To resolve the original class name for a proxy object, you can no longer use
the `Doctrine\Common\Util\ClassUtils` class. Instead, fetch the class name
resolver from the document manager:
```php
$dm->getClassNameResolver()->getRealClass($className);
$dm->getClassNameResolver()->getClass($object);
```

## Repository

* The `Doctrine\ODM\MongoDB\DocumentRepository` class has been renamed to
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"doctrine/common": "^2.9",
"doctrine/instantiator": "^1.1",
"mongodb/mongodb": "^1.2.0",
"ocramius/proxy-manager": "^2.2",
"symfony/console": "^3.4|^4.1"
},
"require-dev": {
Expand Down
2 changes: 0 additions & 2 deletions docs/en/cookbook/blending-orm-and-mongodb-odm.rst
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,8 @@ Later we can retrieve the entity and lazily load the reference to the document i
$order = $em->find(Order::class, $order->getId());
// Instance of an uninitialized product proxy
$product = $order->getProduct();
// Initializes proxy and queries the database
echo "Order Title: " . $product->getTitle();
If you were to print the `$order` you would see that we got back regular PHP objects:
Expand Down
79 changes: 66 additions & 13 deletions lib/Doctrine/ODM/MongoDB/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
use Doctrine\ODM\MongoDB\Repository\DocumentRepository;
use Doctrine\ODM\MongoDB\Repository\GridFSRepository;
use Doctrine\ODM\MongoDB\Repository\RepositoryFactory;
use InvalidArgumentException;
use ProxyManager\Configuration as ProxyManagerConfiguration;
use ProxyManager\Factory\LazyLoadingGhostFactory;
use ProxyManager\FileLocator\FileLocator;
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
use ProxyManager\GeneratorStrategy\FileWriterGeneratorStrategy;
use ReflectionClass;
use function trim;

Expand All @@ -34,13 +40,6 @@
*/
class Configuration
{
/**
* Array of attributes for this configuration instance.
*
* @var array
*/
private $attributes = [];

/**
* Never autogenerate a proxy/hydrator/persistent collection and rely that
* it was generated by some process before deployment. Copied from
Expand Down Expand Up @@ -72,6 +71,25 @@ class Configuration
*/
public const AUTOGENERATE_EVAL = 3;

/**
* Array of attributes for this configuration instance.
*
* @var array
*/
private $attributes = [];

/** @var ProxyManagerConfiguration|null */
private $proxyManagerConfiguration;

/** @var int */
private $autoGenerateProxyClasses = self::AUTOGENERATE_EVAL;

public function __construct()
{
$this->proxyManagerConfiguration = new ProxyManagerConfiguration();
$this->setAutoGenerateProxyClasses(self::AUTOGENERATE_FILE_NOT_EXISTS);
}

/**
* Adds a namespace under a certain alias.
*/
Expand Down Expand Up @@ -154,15 +172,22 @@ public function setMetadataCacheImpl(Cache $cacheImpl) : void
*/
public function setProxyDir(string $dir) : void
{
$this->attributes['proxyDir'] = $dir;
$this->getProxyManagerConfiguration()->setProxiesTargetDir($dir);

// Recreate proxy generator to ensure its path was updated
if ($this->autoGenerateProxyClasses !== self::AUTOGENERATE_FILE_NOT_EXISTS) {
return;
}

$this->setAutoGenerateProxyClasses($this->autoGenerateProxyClasses);
}

/**
* Gets the directory where Doctrine generates any necessary proxy class files.
*/
public function getProxyDir() : ?string
{
return $this->attributes['proxyDir'] ?? null;
return $this->getProxyManagerConfiguration()->getProxiesTargetDir();
}

/**
Expand All @@ -171,26 +196,44 @@ public function getProxyDir() : ?string
*/
public function getAutoGenerateProxyClasses() : int
{
return $this->attributes['autoGenerateProxyClasses'] ?? self::AUTOGENERATE_ALWAYS;
return $this->autoGenerateProxyClasses;
}

/**
* Sets an int flag that indicates whether proxy classes should always be regenerated
* during each script execution.
*
* @throws InvalidArgumentException If an invalid mode was given.
*/
public function setAutoGenerateProxyClasses(int $mode) : void
{
$this->attributes['autoGenerateProxyClasses'] = $mode;
$this->autoGenerateProxyClasses = $mode;
$proxyManagerConfig = $this->getProxyManagerConfiguration();

switch ($mode) {
case self::AUTOGENERATE_FILE_NOT_EXISTS:
$proxyManagerConfig->setGeneratorStrategy(new FileWriterGeneratorStrategy(
new FileLocator($proxyManagerConfig->getProxiesTargetDir())
));

break;
case self::AUTOGENERATE_EVAL:
$proxyManagerConfig->setGeneratorStrategy(new EvaluatingGeneratorStrategy());

break;
default:
throw new InvalidArgumentException('Invalid proxy generation strategy given - only AUTOGENERATE_FILE_NOT_EXISTS and AUTOGENERATE_EVAL are supported.');
}
}

public function getProxyNamespace() : ?string
{
return $this->attributes['proxyNamespace'] ?? null;
return $this->getProxyManagerConfiguration()->getProxiesNamespace();
}

public function setProxyNamespace(string $ns) : void
{
$this->attributes['proxyNamespace'] = $ns;
$this->getProxyManagerConfiguration()->setProxiesNamespace($ns);
}

public function setHydratorDir(string $dir) : void
Expand Down Expand Up @@ -410,4 +453,14 @@ public function getPersistentCollectionGenerator() : PersistentCollectionGenerat
}
return $this->attributes['persistentCollectionGenerator'];
}

public function buildGhostObjectFactory() : LazyLoadingGhostFactory
{
return new LazyLoadingGhostFactory(clone $this->getProxyManagerConfiguration());
}

public function getProxyManagerConfiguration() : ProxyManagerConfiguration
{
return $this->proxyManagerConfiguration;
}
}
35 changes: 20 additions & 15 deletions lib/Doctrine/ODM/MongoDB/DocumentManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory;
use Doctrine\ODM\MongoDB\Mapping\MappingException;
use Doctrine\ODM\MongoDB\Proxy\ProxyFactory;
use Doctrine\ODM\MongoDB\Proxy\ClassNameResolver;
use Doctrine\ODM\MongoDB\Proxy\Factory\ProxyFactory;
use Doctrine\ODM\MongoDB\Proxy\Factory\StaticProxyFactory;
use Doctrine\ODM\MongoDB\Query\FilterCollection;
use Doctrine\ODM\MongoDB\Repository\RepositoryFactory;
use InvalidArgumentException;
Expand Down Expand Up @@ -139,6 +141,9 @@ class DocumentManager implements ObjectManager
*/
private $filterCollection;

/** @var ClassNameResolver */
private $classNameResolver;

/**
* Creates a new Document that operates on the given Mongo connection
* and uses the given Configuration.
Expand Down Expand Up @@ -174,13 +179,9 @@ protected function __construct(?Client $client = null, ?Configuration $config =
$this->unitOfWork = new UnitOfWork($this, $this->eventManager, $this->hydratorFactory);
$this->hydratorFactory->setUnitOfWork($this->unitOfWork);
$this->schemaManager = new SchemaManager($this, $this->metadataFactory);
$this->proxyFactory = new ProxyFactory(
$this,
$this->config->getProxyDir(),
$this->config->getProxyNamespace(),
$this->config->getAutoGenerateProxyClasses()
);
$this->proxyFactory = new StaticProxyFactory($this);
$this->repositoryFactory = $this->config->getRepositoryFactory();
$this->classNameResolver = new ClassNameResolver($this->config);
}

/**
Expand Down Expand Up @@ -263,26 +264,30 @@ public function getSchemaManager() : SchemaManager
return $this->schemaManager;
}

/** Returns the class name resolver which is used to resolve real class names for proxy objects. */
public function getClassNameResolver() : ClassNameResolver
{
return $this->classNameResolver;
}

/**
* Returns the metadata for a class.
*
* @internal Performance-sensitive method.
*
* @param string $className The class name.
*
* @return ClassMetadata
*/
public function getClassMetadata($className)
public function getClassMetadata($className) : ClassMetadata
{
return $this->metadataFactory->getMetadataFor(ltrim($className, '\\'));
return $this->metadataFactory->getMetadataFor($className);
}

/**
* Returns the MongoDB instance for a class.
*/
public function getDocumentDatabase(string $className) : Database
{
$className = ltrim($className, '\\');
$className = $this->classNameResolver->getRealClass($className);

if (isset($this->documentDatabases[$className])) {
return $this->documentDatabases[$className];
Expand Down Expand Up @@ -314,7 +319,7 @@ public function getDocumentDatabases() : array
*/
public function getDocumentCollection(string $className) : Collection
{
$className = ltrim($className, '\\');
$className = $this->classNameResolver->getRealClass($className);

/** @var ClassMetadata $metadata */
$metadata = $this->metadataFactory->getMetadataFor($className);
Expand Down Expand Up @@ -349,7 +354,7 @@ public function getDocumentCollection(string $className) : Collection
*/
public function getDocumentBucket(string $className) : Bucket
{
$className = ltrim($className, '\\');
$className = $this->classNameResolver->getRealClass($className);

/** @var ClassMetadata $metadata */
$metadata = $this->metadataFactory->getMetadataFor($className);
Expand Down Expand Up @@ -570,7 +575,7 @@ public function getReference(string $documentName, $identifier) : object
return $document;
}

$document = $this->proxyFactory->getProxy($class->name, [$class->identifier => $identifier]);
$document = $this->proxyFactory->getProxy($class, $identifier);
$this->unitOfWork->registerManaged($document, $identifier, []);

return $document;
Expand Down
20 changes: 3 additions & 17 deletions lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
use Doctrine\ODM\MongoDB\Event\PreLoadEventArgs;
use Doctrine\ODM\MongoDB\Events;
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
use Doctrine\ODM\MongoDB\Proxy\Proxy;
use Doctrine\ODM\MongoDB\Types\Type;
use Doctrine\ODM\MongoDB\UnitOfWork;
use ProxyManager\Proxy\GhostObjectInterface;
use const DIRECTORY_SEPARATOR;
use function array_key_exists;
use function chmod;
Expand Down Expand Up @@ -444,26 +444,12 @@ public function hydrate(object $document, array $data, array $hints = []) : arra
}
}

if ($document instanceof Proxy) {
$document->__isInitialized__ = true;
$document->__setInitializer(null);
$document->__setCloner(null);
if ($document instanceof GhostObjectInterface) {
$document->setProxyInitializer(null);
}

$data = $this->getHydratorFor($metadata->name)->hydrate($document, $data, $hints);

if ($document instanceof Proxy) {
// lazy properties may be left uninitialized
$properties = $document->__getLazyProperties();
foreach ($properties as $propertyName => $property) {
if (isset($document->$propertyName)) {
continue;
}

$document->$propertyName = $properties[$propertyName];
}
}

// Invoke the postLoad lifecycle callbacks and listeners
if (! empty($metadata->lifecycleCallbacks[Events::postLoad])) {
$metadata->invokeLifecycleCallbacks(Events::postLoad, $document, [new LifecycleEventArgs($document, $this->dm)]);
Expand Down
10 changes: 5 additions & 5 deletions lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
use Doctrine\Instantiator\InstantiatorInterface;
use Doctrine\ODM\MongoDB\Id\AbstractIdGenerator;
use Doctrine\ODM\MongoDB\LockException;
use Doctrine\ODM\MongoDB\Proxy\Proxy;
use Doctrine\ODM\MongoDB\Types\Type;
use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
use InvalidArgumentException;
use LogicException;
use ProxyManager\Proxy\GhostObjectInterface;
use ReflectionClass;
use ReflectionProperty;
use function array_filter;
Expand Down Expand Up @@ -1457,10 +1457,10 @@ public function getIdentifierObject(object $document)
*/
public function setFieldValue(object $document, string $field, $value) : void
{
if ($document instanceof Proxy && ! $document->__isInitialized()) {
if ($document instanceof GhostObjectInterface && ! $document->isProxyInitialized()) {
//property changes to an uninitialized proxy will not be tracked or persisted,
//so the proxy needs to be loaded first.
$document->__load();
$document->initializeProxy();
}

$this->reflFields[$field]->setValue($document, $value);
Expand All @@ -1473,8 +1473,8 @@ public function setFieldValue(object $document, string $field, $value) : void
*/
public function getFieldValue(object $document, string $field)
{
if ($document instanceof Proxy && $field !== $this->identifier && ! $document->__isInitialized()) {
$document->__load();
if ($document instanceof GhostObjectInterface && $field !== $this->identifier && ! $document->isProxyInitialized()) {
$document->initializeProxy();
}

return $this->reflFields[$field]->getValue($document);
Expand Down
5 changes: 5 additions & 0 deletions lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ public function setConfiguration(Configuration $config) : void
$this->config = $config;
}

public function getMetadataFor($className)
{
return parent::getMetadataFor($this->dm->getClassNameResolver()->getRealClass($className));
}

/**
* Lazy initialization of this stuff, especially the metadata driver,
* since these are not needed at all when a metadata cache is active.
Expand Down
Loading

0 comments on commit 4681d8b

Please sign in to comment.