Skip to content

Commit

Permalink
Decouple public API from Doctrine\Persistence\Proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas-grekas committed Jul 13, 2023
1 parent b5987ad commit 54e6f5a
Show file tree
Hide file tree
Showing 48 changed files with 198 additions and 233 deletions.
4 changes: 2 additions & 2 deletions docs/en/reference/unitofwork.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ will still end up with the same reference:
public function testIdentityMapReference(): void
{
$objectA = $this->entityManager->getReference('EntityName', 1);
// check for proxyinterface
$this->assertInstanceOf('Doctrine\Persistence\Proxy', $objectA);
// check entity is not initialized
$this->assertTrue($this->entityManager->isUninitializedObject($objectA));
$objectB = $this->entityManager->find('EntityName', 1);
Expand Down
3 changes: 1 addition & 2 deletions lib/Doctrine/ORM/Cache/DefaultQueryCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\UnitOfWork;
use Doctrine\Persistence\Proxy;

use function array_map;
use function array_shift;
Expand Down Expand Up @@ -345,7 +344,7 @@ private function storeAssociationCache(QueryCacheKey $key, array $assoc, $assocV
$assocIdentifier = $this->uow->getEntityIdentifier($assocValue);
$entityKey = new EntityCacheKey($assocMetadata->rootEntityName, $assocIdentifier);

if (! $assocValue instanceof Proxy && ($key->cacheMode & Cache::MODE_REFRESH) || ! $assocRegion->contains($entityKey)) {
if (! $this->uow->isUninitializedObject($assocValue) && ($key->cacheMode & Cache::MODE_REFRESH) || ! $assocRegion->contains($entityKey)) {
// Entity put fail
if (! $assocPersister->storeEntityCache($assocValue, $entityKey)) {
return null;
Expand Down
8 changes: 8 additions & 0 deletions lib/Doctrine/ORM/EntityManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,14 @@ public function initializeObject($obj)
$this->unitOfWork->initializeObject($obj);
}

/**
* {@inheritDoc}
*/
public function isUninitializedObject($obj): bool

Check warning on line 959 in lib/Doctrine/ORM/EntityManager.php

View check run for this annotation

Codecov / codecov/patch

lib/Doctrine/ORM/EntityManager.php#L959

Added line #L959 was not covered by tests
{
return $this->unitOfWork->isUninitializedObject($obj);

Check warning on line 961 in lib/Doctrine/ORM/EntityManager.php

View check run for this annotation

Codecov / codecov/patch

lib/Doctrine/ORM/EntityManager.php#L961

Added line #L961 was not covered by tests
}

/**
* Factory method to create EntityManager instances.
*
Expand Down
3 changes: 1 addition & 2 deletions lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Query;
use Doctrine\ORM\UnitOfWork;
use Doctrine\Persistence\Proxy;

use function array_fill_keys;
use function array_keys;
Expand Down Expand Up @@ -439,7 +438,7 @@ protected function hydrateRowData(array $row, array &$result)
// PATH B: Single-valued association
$reflFieldValue = $reflField->getValue($parentObject);

if (! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH]) || ($reflFieldValue instanceof Proxy && ! $reflFieldValue->__isInitialized())) {
if (! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH]) || $this->_uow->isUninitializedObject($reflFieldValue)) {
// we only need to take action if this value is null,
// we refresh the entity or its an uninitialized proxy.
if (isset($nonemptyComponents[$dqlAlias])) {
Expand Down
19 changes: 19 additions & 0 deletions lib/Doctrine/ORM/Proxy/InternalProxy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Doctrine\ORM\Proxy;

use Doctrine\Persistence\Proxy;

/**
* @internal
*
* @template T of object
* @template-extends Proxy<T>
*
* @method void __setInitialized(bool $initialized)
*/
interface InternalProxy extends Proxy
{
}
6 changes: 5 additions & 1 deletion lib/Doctrine/ORM/Proxy/Proxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
* Interface for proxy classes.
*
* @deprecated 2.14. Use \Doctrine\Persistence\Proxy instead
*
* @template T of object
* @template-extends BaseProxy<T>
* @template-extends InternalProxy<T>
*/
interface Proxy extends BaseProxy
interface Proxy extends BaseProxy, InternalProxy
{
}
9 changes: 4 additions & 5 deletions lib/Doctrine/ORM/Proxy/ProxyFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
use Doctrine\ORM\UnitOfWork;
use Doctrine\ORM\Utility\IdentifierFlattener;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Proxy;
use ReflectionProperty;
use Symfony\Component\VarExporter\ProxyHelper;
use Symfony\Component\VarExporter\VarExporter;
Expand Down Expand Up @@ -101,7 +100,7 @@ public function __construct(EntityManagerInterface $em, $proxyDir, $proxyNs, $au
$proxyGenerator = new ProxyGenerator($proxyDir, $proxyNs);

if ($em->getConfiguration()->isLazyGhostObjectEnabled()) {
$proxyGenerator->setPlaceholder('baseProxyInterface', Proxy::class);
$proxyGenerator->setPlaceholder('baseProxyInterface', InternalProxy::class);
$proxyGenerator->setPlaceholder('useLazyGhostTrait', Closure::fromCallable([$this, 'generateUseLazyGhostTrait']));
$proxyGenerator->setPlaceholder('skippedProperties', Closure::fromCallable([$this, 'generateSkippedProperties']));
$proxyGenerator->setPlaceholder('serializeImpl', Closure::fromCallable([$this, 'generateSerializeImpl']));
Expand Down Expand Up @@ -131,7 +130,7 @@ public function getProxy($className, array $identifier)

$initializer = $this->definitions[$className]->initializer;

$proxy->__construct(static function (Proxy $object) use ($initializer, $proxy): void {
$proxy->__construct(static function (InternalProxy $object) use ($initializer, $proxy): void {
$initializer($object, $proxy);
});

Expand Down Expand Up @@ -238,13 +237,13 @@ private function createInitializer(ClassMetadata $classMetadata, EntityPersister
/**
* Creates a closure capable of initializing a proxy
*
* @return Closure(Proxy, Proxy):void
* @return Closure(InternalProxy, InternalProxy):void
*
* @throws EntityNotFoundException
*/
private function createLazyInitializer(ClassMetadata $classMetadata, EntityPersister $entityPersister): Closure
{
return function (Proxy $proxy, Proxy $original) use ($entityPersister, $classMetadata): void {
return function (InternalProxy $proxy, InternalProxy $original) use ($entityPersister, $classMetadata): void {
$identifier = $classMetadata->getIdentifierValues($original);
$entity = $entityPersister->loadById($identifier, $original);

Expand Down
3 changes: 1 addition & 2 deletions lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\UnitOfWork;
use Doctrine\Persistence\Proxy;
use ReflectionObject;

use function count;
Expand Down Expand Up @@ -87,7 +86,7 @@ public function dumpIdentityMap(EntityManagerInterface $em)
if ($value === null) {
fwrite($fh, " NULL\n");
} else {
if ($value instanceof Proxy && ! $value->__isInitialized()) {
if ($uow->isUninitializedObject($value)) {

Check warning on line 89 in lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php

View check run for this annotation

Codecov / codecov/patch

lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php#L89

Added line #L89 was not covered by tests
fwrite($fh, '[PROXY] ');
}

Expand Down
64 changes: 30 additions & 34 deletions lib/Doctrine/ORM/UnitOfWork.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@
use Doctrine\ORM\Persisters\Entity\EntityPersister;
use Doctrine\ORM\Persisters\Entity\JoinedSubclassPersister;
use Doctrine\ORM\Persisters\Entity\SingleTablePersister;
use Doctrine\ORM\Proxy\InternalProxy;
use Doctrine\ORM\Utility\IdentifierFlattener;
use Doctrine\Persistence\Mapping\RuntimeReflectionService;
use Doctrine\Persistence\NotifyPropertyChanged;
use Doctrine\Persistence\ObjectManagerAware;
use Doctrine\Persistence\PropertyChangedListener;
use Doctrine\Persistence\Proxy;
use Exception;
use InvalidArgumentException;
use RuntimeException;
Expand Down Expand Up @@ -581,7 +581,7 @@ private function computeSingleEntityChangeSet($entity): void
}

// Ignore uninitialized proxy objects
if ($entity instanceof Proxy && ! $entity->__isInitialized()) {
if ($this->isUninitializedObject($entity)) {
return;
}

Expand Down Expand Up @@ -906,7 +906,7 @@ public function computeChangeSets()

foreach ($entitiesToProcess as $entity) {
// Ignore uninitialized proxy objects
if ($entity instanceof Proxy && ! $entity->__isInitialized()) {
if ($this->isUninitializedObject($entity)) {
continue;
}

Expand All @@ -931,7 +931,7 @@ public function computeChangeSets()
*/
private function computeAssociationChanges(array $assoc, $value): void
{
if ($value instanceof Proxy && ! $value->__isInitialized()) {
if ($this->isUninitializedObject($value)) {
return;
}

Expand Down Expand Up @@ -2172,7 +2172,7 @@ private function ensureVersionMatch(
$entity,
$managedCopy
): void {
if (! ($class->isVersioned && $this->isLoaded($managedCopy) && $this->isLoaded($entity))) {
if (! ($class->isVersioned && ! $this->isUninitializedObject($managedCopy) && ! $this->isUninitializedObject($entity))) {
return;
}

Expand All @@ -2190,16 +2190,6 @@ private function ensureVersionMatch(
throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion);
}

/**
* Tests if an entity is loaded - must either be a loaded proxy or not a proxy
*
* @param object $entity
*/
private function isLoaded($entity): bool
{
return ! ($entity instanceof Proxy) || $entity->__isInitialized();
}

/**
* Sets/adds associated managed copies into the previous entity's association field
*
Expand Down Expand Up @@ -2495,7 +2485,7 @@ static function ($assoc) {
*/
private function cascadePersist($entity, array &$visited): void
{
if ($entity instanceof Proxy && ! $entity->__isInitialized()) {
if ($this->isUninitializedObject($entity)) {
// nothing to do - proxy is not initialized, therefore we don't do anything with it
return;
}
Expand Down Expand Up @@ -2569,13 +2559,13 @@ static function ($assoc) {
}
);

if ($associationMappings) {
$this->initializeObject($entity);
}

$entitiesToCascade = [];

foreach ($associationMappings as $assoc) {
if ($entity instanceof Proxy && ! $entity->__isInitialized()) {
$entity->__load();
}

$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);

switch (true) {
Expand Down Expand Up @@ -2631,9 +2621,7 @@ public function lock($entity, int $lockMode, $lockVersion = null): void
return;
}

if ($entity instanceof Proxy && ! $entity->__isInitialized()) {
$entity->__load();
}
$this->initializeObject($entity);

assert($class->versionField !== null);
$entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
Expand Down Expand Up @@ -2823,7 +2811,6 @@ public function createEntity($className, array $data, &$hints = [])
$unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY];
if (
$unmanagedProxy !== $entity
&& $unmanagedProxy instanceof Proxy
&& $this->isIdentifierEquals($unmanagedProxy, $entity)
) {
// We will hydrate the given un-managed proxy anyway:
Expand All @@ -2832,7 +2819,7 @@ public function createEntity($className, array $data, &$hints = [])
}
}

if ($entity instanceof Proxy && ! $entity->__isInitialized()) {
if ($this->isUninitializedObject($entity)) {
$entity->__setInitialized(true);
} else {
if (
Expand Down Expand Up @@ -2978,8 +2965,7 @@ public function createEntity($className, array $data, &$hints = [])
$hints['fetchMode'][$class->name][$field] === ClassMetadata::FETCH_EAGER &&
isset($hints[self::HINT_DEFEREAGERLOAD]) &&
! $targetClass->isIdentifierComposite &&
$newValue instanceof Proxy &&
$newValue->__isInitialized() === false
$this->isUninitializedObject($newValue)
) {
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
}
Expand Down Expand Up @@ -3377,7 +3363,7 @@ public function registerManaged($entity, array $id, array $data)

$this->addToIdentityMap($entity);

if ($entity instanceof NotifyPropertyChanged && ( ! $entity instanceof Proxy || $entity->__isInitialized())) {
if ($entity instanceof NotifyPropertyChanged && ! $this->isUninitializedObject($entity)) {
$entity->addPropertyChangedListener($this);
}
}
Expand Down Expand Up @@ -3485,7 +3471,7 @@ public function getScheduledCollectionUpdates()
*/
public function initializeObject($obj)
{
if ($obj instanceof Proxy) {
if ($obj instanceof InternalProxy) {
$obj->__load();

return;
Expand All @@ -3496,6 +3482,18 @@ public function initializeObject($obj)
}
}

/**
* Tests if a value is an uninitialized entity.
*
* @param mixed $obj
*
* @psalm-assert-if-true InternalProxy $obj
*/
public function isUninitializedObject($obj): bool
{
return $obj instanceof InternalProxy && ! $obj->__isInitialized();
}

/**
* Helper method to show an object as string.
*
Expand Down Expand Up @@ -3646,13 +3644,11 @@ private function assertThatThereAreNoUnintentionallyNonPersistedAssociations():
*/
private function mergeEntityStateIntoManagedCopy($entity, $managedCopy): void
{
if (! $this->isLoaded($entity)) {
if ($this->isUninitializedObject($entity)) {
return;
}

if (! $this->isLoaded($managedCopy)) {
$managedCopy->__load();
}
$this->initializeObject($managedCopy);

$class = $this->em->getClassMetadata(get_class($entity));

Expand All @@ -3673,7 +3669,7 @@ private function mergeEntityStateIntoManagedCopy($entity, $managedCopy): void
if ($other === null) {
$prop->setValue($managedCopy, null);
} else {
if ($other instanceof Proxy && ! $other->__isInitialized()) {
if ($this->isUninitializedObject($other)) {
// do not merge fields marked lazy that have not been fetched.
continue;
}
Expand Down
5 changes: 0 additions & 5 deletions phpstan-persistence2.neon
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,5 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php

-
message: '/^Call to an undefined method Doctrine\\Persistence\\Proxy::__setInitialized\(\)\.$/'
count: 1
path: lib/Doctrine/ORM/UnitOfWork.php

# Symfony cache supports passing a key prefix to the clear method.
- '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/'
7 changes: 1 addition & 6 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1380,19 +1380,14 @@
<code>$columnList</code>
</PossiblyUndefinedVariable>
</file>
<file src="lib/Doctrine/ORM/Proxy/Proxy.php">
<MissingTemplateParam>
<code>BaseProxy</code>
</MissingTemplateParam>
</file>
<file src="lib/Doctrine/ORM/Proxy/ProxyFactory.php">
<ArgumentTypeCoercion>
<code>$classMetadata</code>
<code>$classMetadata</code>
<code>$classMetadata</code>
</ArgumentTypeCoercion>
<DirectConstructorCall>
<code><![CDATA[$proxy->__construct(static function (Proxy $object) use ($initializer, $proxy): void {
<code><![CDATA[$proxy->__construct(static function (InternalProxy $object) use ($initializer, $proxy): void {
$initializer($object, $proxy);
})]]></code>
</DirectConstructorCall>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

namespace Doctrine\Performance\LazyLoading;

use Doctrine\ORM\Proxy\InternalProxy as Proxy;
use Doctrine\Performance\EntityManagerFactory;
use Doctrine\Performance\Mock\NonProxyLoadingEntityManager;
use Doctrine\Persistence\Proxy;
use Doctrine\Tests\Models\CMS\CmsEmployee;
use Doctrine\Tests\Models\CMS\CmsUser;

Expand Down
Loading

0 comments on commit 54e6f5a

Please sign in to comment.