1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Doctrine\ODM\MongoDB\Proxy; |
6
|
|
|
|
7
|
|
|
use Doctrine\Common\NotifyPropertyChanged; |
8
|
|
|
use Doctrine\Common\Persistence\Mapping\ClassMetadata as BaseClassMetadata; |
9
|
|
|
use Doctrine\Common\Proxy\AbstractProxyFactory; |
10
|
|
|
use Doctrine\Common\Proxy\Proxy as BaseProxy; |
11
|
|
|
use Doctrine\Common\Proxy\ProxyDefinition; |
12
|
|
|
use Doctrine\Common\Proxy\ProxyGenerator; |
13
|
|
|
use Doctrine\Common\Util\ClassUtils; |
14
|
|
|
use Doctrine\ODM\MongoDB\DocumentManager; |
15
|
|
|
use Doctrine\ODM\MongoDB\DocumentNotFoundException; |
16
|
|
|
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; |
17
|
|
|
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory; |
18
|
|
|
use Doctrine\ODM\MongoDB\Persisters\DocumentPersister; |
19
|
|
|
use Doctrine\ODM\MongoDB\UnitOfWork; |
20
|
|
|
use Doctrine\ODM\MongoDB\Utility\LifecycleEventManager; |
21
|
|
|
use ReflectionProperty; |
22
|
|
|
use function assert; |
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; |
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 LifecycleEventManager */ |
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
|
1584 |
|
public function __construct(DocumentManager $documentManager, $proxyDir, $proxyNamespace, $autoGenerate = AbstractProxyFactory::AUTOGENERATE_NEVER) |
54
|
|
|
{ |
55
|
1584 |
|
$this->metadataFactory = $documentManager->getMetadataFactory(); |
56
|
1584 |
|
$this->uow = $documentManager->getUnitOfWork(); |
57
|
1584 |
|
$this->proxyNamespace = $proxyNamespace; |
58
|
1584 |
|
$this->lifecycleEventManager = new LifecycleEventManager($documentManager, $this->uow, $documentManager->getEventManager()); |
59
|
1584 |
|
$proxyGenerator = new ProxyGenerator($proxyDir, $proxyNamespace); |
60
|
|
|
|
61
|
1584 |
|
$proxyGenerator->setPlaceholder('baseProxyInterface', Proxy::class); |
62
|
|
|
|
63
|
1584 |
|
parent::__construct($proxyGenerator, $this->metadataFactory, $autoGenerate); |
64
|
1584 |
|
} |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* {@inheritDoc} |
68
|
|
|
*/ |
69
|
|
|
public function skipClass(BaseClassMetadata $class) |
70
|
|
|
{ |
71
|
|
|
assert($class instanceof ClassMetadata); |
72
|
|
|
return $class->isMappedSuperclass || $class->isQueryResultDocument || $class->getReflectionClass()->isAbstract(); |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* {@inheritDoc} |
77
|
|
|
*/ |
78
|
97 |
|
public function createProxyDefinition($className) |
79
|
|
|
{ |
80
|
|
|
/** @var ClassMetadata $classMetadata */ |
81
|
97 |
|
$classMetadata = $this->metadataFactory->getMetadataFor($className); |
82
|
97 |
|
$documentPersister = $this->uow->getDocumentPersister($className); |
83
|
97 |
|
$reflectionId = $classMetadata->reflFields[$classMetadata->identifier]; |
84
|
|
|
|
85
|
97 |
|
return new ProxyDefinition( |
86
|
97 |
|
ClassUtils::generateProxyClassName($className, $this->proxyNamespace), |
87
|
97 |
|
$classMetadata->getIdentifierFieldNames(), |
88
|
97 |
|
$classMetadata->getReflectionProperties(), |
89
|
97 |
|
$this->createInitializer($classMetadata, $documentPersister, $reflectionId), |
90
|
97 |
|
$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
|
97 |
|
private function createInitializer( |
103
|
|
|
BaseClassMetadata $classMetadata, |
104
|
|
|
DocumentPersister $documentPersister, |
105
|
|
|
ReflectionProperty $reflectionId |
106
|
|
|
) { |
107
|
97 |
|
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
|
31 |
|
$proxy->__setInitializer(null); |
147
|
31 |
|
$proxy->__setCloner(null); |
148
|
|
|
|
149
|
31 |
|
if ($proxy->__isInitialized()) { |
150
|
|
|
return; |
151
|
|
|
} |
152
|
|
|
|
153
|
31 |
|
$properties = $proxy->__getLazyProperties(); |
154
|
|
|
|
155
|
31 |
|
foreach ($properties as $propertyName => $property) { |
156
|
12 |
|
if (isset($proxy->$propertyName)) { |
157
|
|
|
continue; |
158
|
|
|
} |
159
|
|
|
|
160
|
12 |
|
$proxy->$propertyName = $properties[$propertyName]; |
161
|
|
|
} |
162
|
|
|
|
163
|
31 |
|
$proxy->__setInitialized(true); |
164
|
|
|
|
165
|
31 |
|
$id = $reflectionId->getValue($proxy); |
166
|
|
|
|
167
|
31 |
|
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
|
25 |
|
if (! ($proxy instanceof NotifyPropertyChanged)) { |
|
|
|
|
174
|
24 |
|
return; |
175
|
|
|
} |
176
|
|
|
|
177
|
1 |
|
$proxy->addPropertyChangedListener($this->uow); |
178
|
97 |
|
}; |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* Generates a closure capable of finalizing a cloned proxy |
183
|
|
|
* |
184
|
|
|
* |
185
|
|
|
* @return \Closure |
186
|
|
|
* |
187
|
|
|
* @throws DocumentNotFoundException |
188
|
|
|
*/ |
189
|
97 |
|
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
|
97 |
|
}; |
222
|
|
|
} |
223
|
|
|
} |
224
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.