Completed
Push — master ( a8fe50...bce26f )
by Maciej
13s
created

lib/Doctrine/ODM/MongoDB/Proxy/ProxyFactory.php (1 issue)

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;
6
7
use Doctrine\Common\EventManager;
8
use Doctrine\Common\NotifyPropertyChanged;
9
use Doctrine\Common\Persistence\Mapping\ClassMetadata as BaseClassMetadata;
10
use Doctrine\Common\Proxy\AbstractProxyFactory;
11
use Doctrine\Common\Proxy\Proxy as BaseProxy;
12
use Doctrine\Common\Proxy\ProxyDefinition;
13
use Doctrine\Common\Proxy\ProxyGenerator;
14
use Doctrine\Common\Util\ClassUtils;
15
use Doctrine\ODM\MongoDB\DocumentManager;
16
use Doctrine\ODM\MongoDB\DocumentNotFoundException;
17
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
18
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory;
19
use Doctrine\ODM\MongoDB\Persisters\DocumentPersister;
20
use Doctrine\ODM\MongoDB\UnitOfWork;
21
use Doctrine\ODM\MongoDB\Utility\LifecycleEventManager;
22
use ReflectionProperty;
23
use function get_class;
24
25
/**
26
 * This factory is used to create proxy objects for documents at runtime.
27
 *
28
 */
29
class ProxyFactory extends AbstractProxyFactory
30
{
31
    /** @var ClassMetadataFactory */
32
    private $metadataFactory;
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
33
34
    /** @var UnitOfWork The UnitOfWork this factory is bound to. */
35
    private $uow;
36
37
    /** @var string The namespace that contains all proxy classes. */
38
    private $proxyNamespace;
39
40
    /** @var EventManager */
41
    private $lifecycleEventManager;
42
43
    /**
44
     * Initializes a new instance of the <tt>ProxyFactory</tt> class that is
45
     * connected to the given <tt>DocumentManager</tt>.
46
     *
47
     * @param DocumentManager $documentManager The DocumentManager the new factory works for.
48
     * @param string          $proxyDir        The directory to use for the proxy classes. It
49
     *                                         must exist.
50
     * @param string          $proxyNamespace  The namespace to use for the proxy classes.
51
     * @param int             $autoGenerate    Whether to automatically generate proxy classes.
52
     */
53 1605
    public function __construct(DocumentManager $documentManager, $proxyDir, $proxyNamespace, $autoGenerate = AbstractProxyFactory::AUTOGENERATE_NEVER)
54
    {
55 1605
        $this->metadataFactory = $documentManager->getMetadataFactory();
56 1605
        $this->uow = $documentManager->getUnitOfWork();
57 1605
        $this->proxyNamespace = $proxyNamespace;
58 1605
        $this->lifecycleEventManager = new LifecycleEventManager($documentManager, $this->uow, $documentManager->getEventManager());
59 1605
        $proxyGenerator = new ProxyGenerator($proxyDir, $proxyNamespace);
60
61 1605
        $proxyGenerator->setPlaceholder('baseProxyInterface', Proxy::class);
62
63 1605
        parent::__construct($proxyGenerator, $this->metadataFactory, $autoGenerate);
64 1605
    }
65
66
    /**
67
     * {@inheritDoc}
68
     */
69
    public function skipClass(BaseClassMetadata $class)
70
    {
71
        /** @var ClassMetadata $class */
72
        return $class->isMappedSuperclass || $class->isQueryResultDocument || $class->getReflectionClass()->isAbstract();
73
    }
74
75
    /**
76
     * {@inheritDoc}
77
     */
78 104
    public function createProxyDefinition($className)
79
    {
80
        /** @var ClassMetadata $classMetadata */
81 104
        $classMetadata     = $this->metadataFactory->getMetadataFor($className);
82 104
        $documentPersister = $this->uow->getDocumentPersister($className);
83 104
        $reflectionId      = $classMetadata->reflFields[$classMetadata->identifier];
84
85 104
        return new ProxyDefinition(
86 104
            ClassUtils::generateProxyClassName($className, $this->proxyNamespace),
87 104
            $classMetadata->getIdentifierFieldNames(),
88 104
            $classMetadata->getReflectionProperties(),
89 104
            $this->createInitializer($classMetadata, $documentPersister, $reflectionId),
90 104
            $this->createCloner($classMetadata, $documentPersister, $reflectionId)
91
        );
92
    }
93
94
    /**
95
     * Generates a closure capable of initializing a proxy
96
     *
97
     *
98
     * @return \Closure
99
     *
100
     * @throws DocumentNotFoundException
101
     */
102 104
    private function createInitializer(
103
        BaseClassMetadata $classMetadata,
104
        DocumentPersister $documentPersister,
105
        ReflectionProperty $reflectionId
106
    ) {
107 104
        if ($classMetadata->getReflectionClass()->hasMethod('__wakeup')) {
108 View Code Duplication
            return function (BaseProxy $proxy) use ($documentPersister, $reflectionId) {
109
                $proxy->__setInitializer(null);
110
                $proxy->__setCloner(null);
111
112
                if ($proxy->__isInitialized()) {
113
                    return;
114
                }
115
116
                $properties = $proxy->__getLazyProperties();
117
118
                foreach ($properties as $propertyName => $property) {
119
                    if (isset($proxy->$propertyName)) {
120
                        continue;
121
                    }
122
123
                    $proxy->$propertyName = $properties[$propertyName];
124
                }
125
126
                $proxy->__setInitialized(true);
127
                $proxy->__wakeup();
128
129
                $id = $reflectionId->getValue($proxy);
130
131
                if ($documentPersister->load(['_id' => $id], $proxy) === null) {
132
                    if (! $this->lifecycleEventManager->documentNotFound($proxy, $id)) {
133
                        throw DocumentNotFoundException::documentNotFound(get_class($proxy), $id);
134
                    }
135
                }
136
137
                if (! ($proxy instanceof NotifyPropertyChanged)) {
138
                    return;
139
                }
140
141
                $proxy->addPropertyChangedListener($this->uow);
142
            };
143
        }
144
145 View Code Duplication
        return function (BaseProxy $proxy) use ($documentPersister, $reflectionId) {
146 35
            $proxy->__setInitializer(null);
147 35
            $proxy->__setCloner(null);
148
149 35
            if ($proxy->__isInitialized()) {
150
                return;
151
            }
152
153 35
            $properties = $proxy->__getLazyProperties();
154
155 35
            foreach ($properties as $propertyName => $property) {
156 12
                if (isset($proxy->$propertyName)) {
157
                    continue;
158
                }
159
160 12
                $proxy->$propertyName = $properties[$propertyName];
161
            }
162
163 35
            $proxy->__setInitialized(true);
164
165 35
            $id = $reflectionId->getValue($proxy);
166
167 35
            if ($documentPersister->load(['_id' => $id], $proxy) === null) {
168 9
                if (! $this->lifecycleEventManager->documentNotFound($proxy, $id)) {
169 8
                    throw DocumentNotFoundException::documentNotFound(get_class($proxy), $id);
170
                }
171
            }
172
173 29
            if (! ($proxy instanceof NotifyPropertyChanged)) {
174 28
                return;
175
            }
176
177 1
            $proxy->addPropertyChangedListener($this->uow);
178 104
        };
179
    }
180
181
    /**
182
     * Generates a closure capable of finalizing a cloned proxy
183
     *
184
     *
185
     * @return \Closure
186
     *
187
     * @throws DocumentNotFoundException
188
     */
189 104
    private function createCloner(
190
        BaseClassMetadata $classMetadata,
191
        DocumentPersister $documentPersister,
192
        ReflectionProperty $reflectionId
193
    ) {
194
        return function (BaseProxy $proxy) use ($documentPersister, $classMetadata, $reflectionId) {
195
            if ($proxy->__isInitialized()) {
196
                return;
197
            }
198
199
            $proxy->__setInitialized(true);
200
            $proxy->__setInitializer(null);
201
202
            $id       = $reflectionId->getValue($proxy);
203
            $original = $documentPersister->load(['_id' => $id]);
204
205
            if ($original === null) {
206
                if (! $this->lifecycleEventManager->documentNotFound($proxy, $id)) {
207
                    throw DocumentNotFoundException::documentNotFound(get_class($proxy), $id);
208
                }
209
            }
210
211
            foreach ($classMetadata->getReflectionClass()->getProperties() as $reflectionProperty) {
212
                $propertyName = $reflectionProperty->getName();
213
214
                if (! $classMetadata->hasField($propertyName) && ! $classMetadata->hasAssociation($propertyName)) {
215
                    continue;
216
                }
217
218
                $reflectionProperty->setAccessible(true);
219
                $reflectionProperty->setValue($proxy, $reflectionProperty->getValue($original));
220
            }
221 104
        };
222
    }
223
}
224