Completed
Push — master ( 7b9f4b...1cd743 )
by Andreas
13s queued 10s
created

ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB\Proxy\Factory;
6
7
use Closure;
8
use Doctrine\Common\NotifyPropertyChanged;
9
use Doctrine\ODM\MongoDB\DocumentManager;
10
use Doctrine\ODM\MongoDB\DocumentNotFoundException;
11
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
12
use Doctrine\ODM\MongoDB\Persisters\DocumentPersister;
13
use Doctrine\ODM\MongoDB\UnitOfWork;
14
use Doctrine\ODM\MongoDB\Utility\LifecycleEventManager;
15
use ProxyManager\Factory\LazyLoadingGhostFactory;
16
use ProxyManager\Proxy\GhostObjectInterface;
17
use ReflectionProperty;
18
use function array_filter;
19
use function count;
20
21
/**
22
 * This factory is used to create proxy objects for documents at runtime.
23
 */
24
class StaticProxyFactory implements ProxyFactory
25
{
26
    /** @var UnitOfWork The UnitOfWork this factory is bound to. */
27
    private $uow;
28
29
    /** @var LifecycleEventManager */
30
    private $lifecycleEventManager;
31
32
    /** @var LazyLoadingGhostFactory */
33
    private $proxyFactory;
34
35 1651
    public function __construct(DocumentManager $documentManager)
36
    {
37 1651
        $this->uow                   = $documentManager->getUnitOfWork();
38 1651
        $this->lifecycleEventManager = new LifecycleEventManager($documentManager, $this->uow, $documentManager->getEventManager());
39 1651
        $this->proxyFactory          = $documentManager->getConfiguration()->buildGhostObjectFactory();
40 1651
    }
41
42 98
    public function getProxy(ClassMetadata $metadata, $identifier) : GhostObjectInterface
43
    {
44 98
        $documentPersister = $this->uow->getDocumentPersister($metadata->getName());
0 ignored issues
show
Consider using $metadata->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
45
46
        $ghostObject = $this
47 98
            ->proxyFactory
48 98
            ->createProxy(
49 98
                $metadata->getName(),
0 ignored issues
show
Consider using $metadata->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
50 98
                $this->createInitializer($metadata, $documentPersister),
51
                [
52 98
                    'skippedProperties' => $this->skippedFieldsFqns($metadata),
53
                ]
54
            );
55
56 98
        $metadata->setIdentifierValue($ghostObject, $identifier);
57
58 98
        return $ghostObject;
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     *
64
     * @param ClassMetadata[] $classes
65
     */
66
    public function generateProxyClasses(array $classes) : int
67
    {
68
        $concreteClasses = array_filter($classes, static function (ClassMetadata $metadata) : bool {
69
            return ! ($metadata->isMappedSuperclass || $metadata->isQueryResultDocument || $metadata->getReflectionClass()->isAbstract());
70
        });
71
72
        foreach ($concreteClasses as $metadata) {
73
            $this
74
                ->proxyFactory
75
                ->createProxy(
76
                    $metadata->getName(),
77
                    static function () {
78
                        // empty closure, serves its purpose, for now
79
                    },
80
                    $this->skippedFieldsFqns($metadata)
81
                );
82
        }
83
84
        return count($concreteClasses);
85
    }
86
87 98
    private function createInitializer(
88
        ClassMetadata $metadata,
89
        DocumentPersister $documentPersister
90
    ) : Closure {
91
        return function (
92
            GhostObjectInterface $ghostObject,
93
            string $method,
94
            // we don't care
95
            array $parameters,
96
            // we don't care
97
            & $initializer,
98
            array $properties // we currently do not use this
99
        ) use (
100 33
            $metadata,
101 33
            $documentPersister
102
        ) : bool {
103 33
            $originalInitializer = $initializer;
104 33
            $initializer         = null;
105 33
            $identifier          = $metadata->getIdentifierValue($ghostObject);
106
107 33
            if (! $documentPersister->load(['_id' => $identifier], $ghostObject)) {
108 9
                $initializer = $originalInitializer;
109
110 9
                if (! $this->lifecycleEventManager->documentNotFound($ghostObject, $identifier)) {
111 8
                    throw DocumentNotFoundException::documentNotFound($metadata->getName(), $identifier);
0 ignored issues
show
Consider using $metadata->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
112
                }
113
            }
114
115 27
            if ($ghostObject instanceof NotifyPropertyChanged) {
116 1
                $ghostObject->addPropertyChangedListener($this->uow);
117
            }
118
119 27
            return true;
120 98
        };
121
    }
122
123
    /**
124
     * @return string[]
125
     */
126 98
    private function skippedFieldsFqns(ClassMetadata $metadata) : array
127
    {
128 98
        $idFieldFqcns = [];
129
130 98
        foreach ($metadata->getIdentifierFieldNames() as $idField) {
131 98
            $idFieldFqcns[] = $this->propertyFqcn($metadata->getReflectionProperty($idField));
132
        }
133
134 98
        return $idFieldFqcns;
135
    }
136
137 98
    private function propertyFqcn(ReflectionProperty $property) : string
138
    {
139 98
        if ($property->isPrivate()) {
140 50
            return "\0" . $property->getDeclaringClass()->getName() . "\0" . $property->getName();
141
        }
142
143 54
        if ($property->isProtected()) {
144 22
            return "\0*\0" . $property->getName();
145
        }
146
147 32
        return $property->getName();
148
    }
149
}
150