summaryrefslogtreecommitdiff
path: root/vendor/doctrine/orm/src/Proxy
diff options
context:
space:
mode:
authorpolo <ordipolo@gmx.fr>2024-08-13 23:45:21 +0200
committerpolo <ordipolo@gmx.fr>2024-08-13 23:45:21 +0200
commitbf6655a534a6775d30cafa67bd801276bda1d98d (patch)
treec6381e3f6c81c33eab72508f410b165ba05f7e9c /vendor/doctrine/orm/src/Proxy
parent94d67a4b51f8e62e7d518cce26a526ae1ec48278 (diff)
downloadAppliGestionPHP-bf6655a534a6775d30cafa67bd801276bda1d98d.zip
VERSION 0.2 doctrine ORM et entités
Diffstat (limited to 'vendor/doctrine/orm/src/Proxy')
-rw-r--r--vendor/doctrine/orm/src/Proxy/Autoloader.php86
-rw-r--r--vendor/doctrine/orm/src/Proxy/DefaultProxyClassNameResolver.php35
-rw-r--r--vendor/doctrine/orm/src/Proxy/InternalProxy.php18
-rw-r--r--vendor/doctrine/orm/src/Proxy/NotAProxyClass.php22
-rw-r--r--vendor/doctrine/orm/src/Proxy/ProxyFactory.php439
5 files changed, 600 insertions, 0 deletions
diff --git a/vendor/doctrine/orm/src/Proxy/Autoloader.php b/vendor/doctrine/orm/src/Proxy/Autoloader.php
new file mode 100644
index 0000000..1013e73
--- /dev/null
+++ b/vendor/doctrine/orm/src/Proxy/Autoloader.php
@@ -0,0 +1,86 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Proxy;
6
7use Closure;
8
9use function file_exists;
10use function ltrim;
11use function spl_autoload_register;
12use function str_replace;
13use function str_starts_with;
14use function strlen;
15use function substr;
16
17use const DIRECTORY_SEPARATOR;
18
19/**
20 * Special Autoloader for Proxy classes, which are not PSR-0 compliant.
21 */
22final class Autoloader
23{
24 /**
25 * Resolves proxy class name to a filename based on the following pattern.
26 *
27 * 1. Remove Proxy namespace from class name.
28 * 2. Remove namespace separators from remaining class name.
29 * 3. Return PHP filename from proxy-dir with the result from 2.
30 *
31 * @psalm-param class-string $className
32 *
33 * @throws NotAProxyClass
34 */
35 public static function resolveFile(string $proxyDir, string $proxyNamespace, string $className): string
36 {
37 if (! str_starts_with($className, $proxyNamespace)) {
38 throw new NotAProxyClass($className, $proxyNamespace);
39 }
40
41 // remove proxy namespace from class name
42 $classNameRelativeToProxyNamespace = substr($className, strlen($proxyNamespace));
43
44 // remove namespace separators from remaining class name
45 $fileName = str_replace('\\', '', $classNameRelativeToProxyNamespace);
46
47 return $proxyDir . DIRECTORY_SEPARATOR . $fileName . '.php';
48 }
49
50 /**
51 * Registers and returns autoloader callback for the given proxy dir and namespace.
52 *
53 * @param Closure(string, string, class-string): void|null $notFoundCallback Invoked when the proxy file is not found.
54 *
55 * @return Closure(string): void
56 */
57 public static function register(
58 string $proxyDir,
59 string $proxyNamespace,
60 Closure|null $notFoundCallback = null,
61 ): Closure {
62 $proxyNamespace = ltrim($proxyNamespace, '\\');
63
64 $autoloader = /** @param class-string $className */ static function (string $className) use ($proxyDir, $proxyNamespace, $notFoundCallback): void {
65 if ($proxyNamespace === '') {
66 return;
67 }
68
69 if (! str_starts_with($className, $proxyNamespace)) {
70 return;
71 }
72
73 $file = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className);
74
75 if ($notFoundCallback && ! file_exists($file)) {
76 $notFoundCallback($proxyDir, $proxyNamespace, $className);
77 }
78
79 require $file;
80 };
81
82 spl_autoload_register($autoloader);
83
84 return $autoloader;
85 }
86}
diff --git a/vendor/doctrine/orm/src/Proxy/DefaultProxyClassNameResolver.php b/vendor/doctrine/orm/src/Proxy/DefaultProxyClassNameResolver.php
new file mode 100644
index 0000000..1345f2e
--- /dev/null
+++ b/vendor/doctrine/orm/src/Proxy/DefaultProxyClassNameResolver.php
@@ -0,0 +1,35 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Proxy;
6
7use Doctrine\Persistence\Mapping\ProxyClassNameResolver;
8use Doctrine\Persistence\Proxy;
9
10use function strrpos;
11use function substr;
12
13/**
14 * Class-related functionality for objects that might or not be proxy objects
15 * at the moment.
16 */
17final class DefaultProxyClassNameResolver implements ProxyClassNameResolver
18{
19 public function resolveClassName(string $className): string
20 {
21 $pos = strrpos($className, '\\' . Proxy::MARKER . '\\');
22
23 if ($pos === false) {
24 return $className;
25 }
26
27 return substr($className, $pos + Proxy::MARKER_LENGTH + 2);
28 }
29
30 /** @return class-string */
31 public static function getClass(object $object): string
32 {
33 return (new self())->resolveClassName($object::class);
34 }
35}
diff --git a/vendor/doctrine/orm/src/Proxy/InternalProxy.php b/vendor/doctrine/orm/src/Proxy/InternalProxy.php
new file mode 100644
index 0000000..7c1d833
--- /dev/null
+++ b/vendor/doctrine/orm/src/Proxy/InternalProxy.php
@@ -0,0 +1,18 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Proxy;
6
7use Doctrine\Persistence\Proxy;
8
9/**
10 * @internal
11 *
12 * @template T of object
13 * @template-extends Proxy<T>
14 */
15interface InternalProxy extends Proxy
16{
17 public function __setInitialized(bool $initialized): void;
18}
diff --git a/vendor/doctrine/orm/src/Proxy/NotAProxyClass.php b/vendor/doctrine/orm/src/Proxy/NotAProxyClass.php
new file mode 100644
index 0000000..689cc3e
--- /dev/null
+++ b/vendor/doctrine/orm/src/Proxy/NotAProxyClass.php
@@ -0,0 +1,22 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Proxy;
6
7use Doctrine\ORM\Exception\ORMException;
8use InvalidArgumentException;
9
10use function sprintf;
11
12final class NotAProxyClass extends InvalidArgumentException implements ORMException
13{
14 public function __construct(string $className, string $proxyNamespace)
15 {
16 parent::__construct(sprintf(
17 'The class "%s" is not part of the proxy namespace "%s"',
18 $className,
19 $proxyNamespace,
20 ));
21 }
22}
diff --git a/vendor/doctrine/orm/src/Proxy/ProxyFactory.php b/vendor/doctrine/orm/src/Proxy/ProxyFactory.php
new file mode 100644
index 0000000..b2d114a
--- /dev/null
+++ b/vendor/doctrine/orm/src/Proxy/ProxyFactory.php
@@ -0,0 +1,439 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Proxy;
6
7use Closure;
8use Doctrine\ORM\EntityManagerInterface;
9use Doctrine\ORM\EntityNotFoundException;
10use Doctrine\ORM\ORMInvalidArgumentException;
11use Doctrine\ORM\Persisters\Entity\EntityPersister;
12use Doctrine\ORM\UnitOfWork;
13use Doctrine\ORM\Utility\IdentifierFlattener;
14use Doctrine\Persistence\Mapping\ClassMetadata;
15use Doctrine\Persistence\Proxy;
16use ReflectionProperty;
17use Symfony\Component\VarExporter\ProxyHelper;
18
19use function array_combine;
20use function array_flip;
21use function array_intersect_key;
22use function assert;
23use function bin2hex;
24use function chmod;
25use function class_exists;
26use function dirname;
27use function file_exists;
28use function file_put_contents;
29use function filemtime;
30use function is_bool;
31use function is_dir;
32use function is_int;
33use function is_writable;
34use function ltrim;
35use function mkdir;
36use function preg_match_all;
37use function random_bytes;
38use function rename;
39use function rtrim;
40use function str_replace;
41use function strpos;
42use function strrpos;
43use function strtr;
44use function substr;
45use function ucfirst;
46
47use const DIRECTORY_SEPARATOR;
48
49/**
50 * This factory is used to create proxy objects for entities at runtime.
51 */
52class ProxyFactory
53{
54 /**
55 * Never autogenerate a proxy and rely that it was generated by some
56 * process before deployment.
57 */
58 public const AUTOGENERATE_NEVER = 0;
59
60 /**
61 * Always generates a new proxy in every request.
62 *
63 * This is only sane during development.
64 */
65 public const AUTOGENERATE_ALWAYS = 1;
66
67 /**
68 * Autogenerate the proxy class when the proxy file does not exist.
69 *
70 * This strategy causes a file_exists() call whenever any proxy is used the
71 * first time in a request.
72 */
73 public const AUTOGENERATE_FILE_NOT_EXISTS = 2;
74
75 /**
76 * Generate the proxy classes using eval().
77 *
78 * This strategy is only sane for development, and even then it gives me
79 * the creeps a little.
80 */
81 public const AUTOGENERATE_EVAL = 3;
82
83 /**
84 * Autogenerate the proxy class when the proxy file does not exist or
85 * when the proxied file changed.
86 *
87 * This strategy causes a file_exists() call whenever any proxy is used the
88 * first time in a request. When the proxied file is changed, the proxy will
89 * be updated.
90 */
91 public const AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED = 4;
92
93 private const PROXY_CLASS_TEMPLATE = <<<'EOPHP'
94<?php
95
96namespace <namespace>;
97
98/**
99 * DO NOT EDIT THIS FILE - IT WAS CREATED BY DOCTRINE'S PROXY GENERATOR
100 */
101class <proxyShortClassName> extends \<className> implements \<baseProxyInterface>
102{
103 <useLazyGhostTrait>
104
105 public function __isInitialized(): bool
106 {
107 return isset($this->lazyObjectState) && $this->isLazyObjectInitialized();
108 }
109
110 public function __serialize(): array
111 {
112 <serializeImpl>
113 }
114}
115
116EOPHP;
117
118 /** The UnitOfWork this factory uses to retrieve persisters */
119 private readonly UnitOfWork $uow;
120
121 /** @var self::AUTOGENERATE_* */
122 private $autoGenerate;
123
124 /** The IdentifierFlattener used for manipulating identifiers */
125 private readonly IdentifierFlattener $identifierFlattener;
126
127 /** @var array<class-string, Closure> */
128 private array $proxyFactories = [];
129
130 /**
131 * Initializes a new instance of the <tt>ProxyFactory</tt> class that is
132 * connected to the given <tt>EntityManager</tt>.
133 *
134 * @param EntityManagerInterface $em The EntityManager the new factory works for.
135 * @param string $proxyDir The directory to use for the proxy classes. It must exist.
136 * @param string $proxyNs The namespace to use for the proxy classes.
137 * @param bool|self::AUTOGENERATE_* $autoGenerate The strategy for automatically generating proxy classes.
138 */
139 public function __construct(
140 private readonly EntityManagerInterface $em,
141 private readonly string $proxyDir,
142 private readonly string $proxyNs,
143 bool|int $autoGenerate = self::AUTOGENERATE_NEVER,
144 ) {
145 if (! $proxyDir) {
146 throw ORMInvalidArgumentException::proxyDirectoryRequired();
147 }
148
149 if (! $proxyNs) {
150 throw ORMInvalidArgumentException::proxyNamespaceRequired();
151 }
152
153 if (is_int($autoGenerate) ? $autoGenerate < 0 || $autoGenerate > 4 : ! is_bool($autoGenerate)) {
154 throw ORMInvalidArgumentException::invalidAutoGenerateMode($autoGenerate);
155 }
156
157 $this->uow = $em->getUnitOfWork();
158 $this->autoGenerate = (int) $autoGenerate;
159 $this->identifierFlattener = new IdentifierFlattener($this->uow, $em->getMetadataFactory());
160 }
161
162 /**
163 * @param class-string $className
164 * @param array<mixed> $identifier
165 */
166 public function getProxy(string $className, array $identifier): InternalProxy
167 {
168 $proxyFactory = $this->proxyFactories[$className] ?? $this->getProxyFactory($className);
169
170 return $proxyFactory($identifier);
171 }
172
173 /**
174 * Generates proxy classes for all given classes.
175 *
176 * @param ClassMetadata[] $classes The classes (ClassMetadata instances) for which to generate proxies.
177 * @param string|null $proxyDir The target directory of the proxy classes. If not specified, the
178 * directory configured on the Configuration of the EntityManager used
179 * by this factory is used.
180 *
181 * @return int Number of generated proxies.
182 */
183 public function generateProxyClasses(array $classes, string|null $proxyDir = null): int
184 {
185 $generated = 0;
186
187 foreach ($classes as $class) {
188 if ($this->skipClass($class)) {
189 continue;
190 }
191
192 $proxyFileName = $this->getProxyFileName($class->getName(), $proxyDir ?: $this->proxyDir);
193 $proxyClassName = self::generateProxyClassName($class->getName(), $this->proxyNs);
194
195 $this->generateProxyClass($class, $proxyFileName, $proxyClassName);
196
197 ++$generated;
198 }
199
200 return $generated;
201 }
202
203 protected function skipClass(ClassMetadata $metadata): bool
204 {
205 return $metadata->isMappedSuperclass
206 || $metadata->isEmbeddedClass
207 || $metadata->getReflectionClass()->isAbstract();
208 }
209
210 /**
211 * Creates a closure capable of initializing a proxy
212 *
213 * @return Closure(InternalProxy, array):void
214 *
215 * @throws EntityNotFoundException
216 */
217 private function createLazyInitializer(ClassMetadata $classMetadata, EntityPersister $entityPersister, IdentifierFlattener $identifierFlattener): Closure
218 {
219 return static function (InternalProxy $proxy, array $identifier) use ($entityPersister, $classMetadata, $identifierFlattener): void {
220 $original = $entityPersister->loadById($identifier);
221
222 if ($original === null) {
223 throw EntityNotFoundException::fromClassNameAndIdentifier(
224 $classMetadata->getName(),
225 $identifierFlattener->flattenIdentifier($classMetadata, $identifier),
226 );
227 }
228
229 if ($proxy === $original) {
230 return;
231 }
232
233 $class = $entityPersister->getClassMetadata();
234
235 foreach ($class->getReflectionProperties() as $property) {
236 if (! $property || isset($identifier[$property->getName()]) || ! $class->hasField($property->getName()) && ! $class->hasAssociation($property->getName())) {
237 continue;
238 }
239
240 $property->setValue($proxy, $property->getValue($original));
241 }
242 };
243 }
244
245 private function getProxyFileName(string $className, string $baseDirectory): string
246 {
247 $baseDirectory = $baseDirectory ?: $this->proxyDir;
248
249 return rtrim($baseDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . InternalProxy::MARKER
250 . str_replace('\\', '', $className) . '.php';
251 }
252
253 private function getProxyFactory(string $className): Closure
254 {
255 $skippedProperties = [];
256 $class = $this->em->getClassMetadata($className);
257 $identifiers = array_flip($class->getIdentifierFieldNames());
258 $filter = ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PRIVATE;
259 $reflector = $class->getReflectionClass();
260
261 while ($reflector) {
262 foreach ($reflector->getProperties($filter) as $property) {
263 $name = $property->name;
264
265 if ($property->isStatic() || (($class->hasField($name) || $class->hasAssociation($name)) && ! isset($identifiers[$name]))) {
266 continue;
267 }
268
269 $prefix = $property->isPrivate() ? "\0" . $property->class . "\0" : ($property->isProtected() ? "\0*\0" : '');
270
271 $skippedProperties[$prefix . $name] = true;
272 }
273
274 $filter = ReflectionProperty::IS_PRIVATE;
275 $reflector = $reflector->getParentClass();
276 }
277
278 $className = $class->getName(); // aliases and case sensitivity
279 $entityPersister = $this->uow->getEntityPersister($className);
280 $initializer = $this->createLazyInitializer($class, $entityPersister, $this->identifierFlattener);
281 $proxyClassName = $this->loadProxyClass($class);
282 $identifierFields = array_intersect_key($class->getReflectionProperties(), $identifiers);
283
284 $proxyFactory = Closure::bind(static function (array $identifier) use ($initializer, $skippedProperties, $identifierFields, $className): InternalProxy {
285 $proxy = self::createLazyGhost(static function (InternalProxy $object) use ($initializer, $identifier): void {
286 $initializer($object, $identifier);
287 }, $skippedProperties);
288
289 foreach ($identifierFields as $idField => $reflector) {
290 if (! isset($identifier[$idField])) {
291 throw ORMInvalidArgumentException::missingPrimaryKeyValue($className, $idField);
292 }
293
294 assert($reflector !== null);
295 $reflector->setValue($proxy, $identifier[$idField]);
296 }
297
298 return $proxy;
299 }, null, $proxyClassName);
300
301 return $this->proxyFactories[$className] = $proxyFactory;
302 }
303
304 private function loadProxyClass(ClassMetadata $class): string
305 {
306 $proxyClassName = self::generateProxyClassName($class->getName(), $this->proxyNs);
307
308 if (class_exists($proxyClassName, false)) {
309 return $proxyClassName;
310 }
311
312 if ($this->autoGenerate === self::AUTOGENERATE_EVAL) {
313 $this->generateProxyClass($class, null, $proxyClassName);
314
315 return $proxyClassName;
316 }
317
318 $fileName = $this->getProxyFileName($class->getName(), $this->proxyDir);
319
320 switch ($this->autoGenerate) {
321 case self::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED:
322 if (file_exists($fileName) && filemtime($fileName) >= filemtime($class->getReflectionClass()->getFileName())) {
323 break;
324 }
325 // no break
326 case self::AUTOGENERATE_FILE_NOT_EXISTS:
327 if (file_exists($fileName)) {
328 break;
329 }
330 // no break
331 case self::AUTOGENERATE_ALWAYS:
332 $this->generateProxyClass($class, $fileName, $proxyClassName);
333 break;
334 }
335
336 require $fileName;
337
338 return $proxyClassName;
339 }
340
341 private function generateProxyClass(ClassMetadata $class, string|null $fileName, string $proxyClassName): void
342 {
343 $i = strrpos($proxyClassName, '\\');
344 $placeholders = [
345 '<className>' => $class->getName(),
346 '<namespace>' => substr($proxyClassName, 0, $i),
347 '<proxyShortClassName>' => substr($proxyClassName, 1 + $i),
348 '<baseProxyInterface>' => InternalProxy::class,
349 ];
350
351 preg_match_all('(<([a-zA-Z]+)>)', self::PROXY_CLASS_TEMPLATE, $placeholderMatches);
352
353 foreach (array_combine($placeholderMatches[0], $placeholderMatches[1]) as $placeholder => $name) {
354 $placeholders[$placeholder] ?? $placeholders[$placeholder] = $this->{'generate' . ucfirst($name)}($class);
355 }
356
357 $proxyCode = strtr(self::PROXY_CLASS_TEMPLATE, $placeholders);
358
359 if (! $fileName) {
360 if (! class_exists($proxyClassName)) {
361 eval(substr($proxyCode, 5));
362 }
363
364 return;
365 }
366
367 $parentDirectory = dirname($fileName);
368
369 if (! is_dir($parentDirectory) && ! @mkdir($parentDirectory, 0775, true)) {
370 throw ORMInvalidArgumentException::proxyDirectoryNotWritable($this->proxyDir);
371 }
372
373 if (! is_writable($parentDirectory)) {
374 throw ORMInvalidArgumentException::proxyDirectoryNotWritable($this->proxyDir);
375 }
376
377 $tmpFileName = $fileName . '.' . bin2hex(random_bytes(12));
378
379 file_put_contents($tmpFileName, $proxyCode);
380 @chmod($tmpFileName, 0664);
381 rename($tmpFileName, $fileName);
382 }
383
384 private function generateUseLazyGhostTrait(ClassMetadata $class): string
385 {
386 $code = ProxyHelper::generateLazyGhost($class->getReflectionClass());
387 $code = substr($code, 7 + (int) strpos($code, "\n{"));
388 $code = substr($code, 0, (int) strpos($code, "\n}"));
389 $code = str_replace('LazyGhostTrait;', str_replace("\n ", "\n", 'LazyGhostTrait {
390 initializeLazyObject as private;
391 setLazyObjectAsInitialized as public __setInitialized;
392 isLazyObjectInitialized as private;
393 createLazyGhost as private;
394 resetLazyObject as private;
395 }
396
397 public function __load(): void
398 {
399 $this->initializeLazyObject();
400 }
401 '), $code);
402
403 return $code;
404 }
405
406 private function generateSerializeImpl(ClassMetadata $class): string
407 {
408 $reflector = $class->getReflectionClass();
409 $properties = $reflector->hasMethod('__serialize') ? 'parent::__serialize()' : '(array) $this';
410
411 $code = '$properties = ' . $properties . ';
412 unset($properties["\0" . self::class . "\0lazyObjectState"]);
413
414 ';
415
416 if ($reflector->hasMethod('__serialize') || ! $reflector->hasMethod('__sleep')) {
417 return $code . 'return $properties;';
418 }
419
420 return $code . '$data = [];
421
422 foreach (parent::__sleep() as $name) {
423 $value = $properties[$k = $name] ?? $properties[$k = "\0*\0$name"] ?? $properties[$k = "\0' . $reflector->name . '\0$name"] ?? $k = null;
424
425 if (null === $k) {
426 trigger_error(sprintf(\'serialize(): "%s" returned as member variable from __sleep() but does not exist\', $name), \E_USER_NOTICE);
427 } else {
428 $data[$k] = $value;
429 }
430 }
431
432 return $data;';
433 }
434
435 private static function generateProxyClassName(string $className, string $proxyNamespace): string
436 {
437 return rtrim($proxyNamespace, '\\') . '\\' . Proxy::MARKER . '\\' . ltrim($className, '\\');
438 }
439}