comparison vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyGenerator.php @ 0:c75dbcec494b

Initial commit from drush-created site
author Chris Cannam
date Thu, 05 Jul 2018 14:24:15 +0000
parents
children 5311817fb629
comparison
equal deleted inserted replaced
-1:000000000000 0:c75dbcec494b
1 <?php
2 /*
3 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14 *
15 * This software consists of voluntary contributions made by many individuals
16 * and is licensed under the MIT license. For more information, see
17 * <http://www.doctrine-project.org>.
18 */
19
20 namespace Doctrine\Common\Proxy;
21
22 use Doctrine\Common\Persistence\Mapping\ClassMetadata;
23 use Doctrine\Common\Proxy\Exception\InvalidArgumentException;
24 use Doctrine\Common\Proxy\Exception\UnexpectedValueException;
25 use Doctrine\Common\Util\ClassUtils;
26
27 /**
28 * This factory is used to generate proxy classes.
29 * It builds proxies from given parameters, a template and class metadata.
30 *
31 * @author Marco Pivetta <ocramius@gmail.com>
32 * @since 2.4
33 */
34 class ProxyGenerator
35 {
36 /**
37 * Used to match very simple id methods that don't need
38 * to be decorated since the identifier is known.
39 */
40 const PATTERN_MATCH_ID_METHOD = '((public\s+)?(function\s+%s\s*\(\)\s*)\s*{\s*return\s*\$this->%s;\s*})i';
41
42 /**
43 * The namespace that contains all proxy classes.
44 *
45 * @var string
46 */
47 private $proxyNamespace;
48
49 /**
50 * The directory that contains all proxy classes.
51 *
52 * @var string
53 */
54 private $proxyDirectory;
55
56 /**
57 * Map of callables used to fill in placeholders set in the template.
58 *
59 * @var string[]|callable[]
60 */
61 protected $placeholders = [
62 'baseProxyInterface' => Proxy::class,
63 'additionalProperties' => '',
64 ];
65
66 /**
67 * Template used as a blueprint to generate proxies.
68 *
69 * @var string
70 */
71 protected $proxyClassTemplate = '<?php
72
73 namespace <namespace>;
74
75 /**
76 * DO NOT EDIT THIS FILE - IT WAS CREATED BY DOCTRINE\'S PROXY GENERATOR
77 */
78 class <proxyShortClassName> extends \<className> implements \<baseProxyInterface>
79 {
80 /**
81 * @var \Closure the callback responsible for loading properties in the proxy object. This callback is called with
82 * three parameters, being respectively the proxy object to be initialized, the method that triggered the
83 * initialization process and an array of ordered parameters that were passed to that method.
84 *
85 * @see \Doctrine\Common\Persistence\Proxy::__setInitializer
86 */
87 public $__initializer__;
88
89 /**
90 * @var \Closure the callback responsible of loading properties that need to be copied in the cloned object
91 *
92 * @see \Doctrine\Common\Persistence\Proxy::__setCloner
93 */
94 public $__cloner__;
95
96 /**
97 * @var boolean flag indicating if this object was already initialized
98 *
99 * @see \Doctrine\Common\Persistence\Proxy::__isInitialized
100 */
101 public $__isInitialized__ = false;
102
103 /**
104 * @var array properties to be lazy loaded, with keys being the property
105 * names and values being their default values
106 *
107 * @see \Doctrine\Common\Persistence\Proxy::__getLazyProperties
108 */
109 public static $lazyPropertiesDefaults = [<lazyPropertiesDefaults>];
110
111 <additionalProperties>
112
113 <constructorImpl>
114
115 <magicGet>
116
117 <magicSet>
118
119 <magicIsset>
120
121 <sleepImpl>
122
123 <wakeupImpl>
124
125 <cloneImpl>
126
127 /**
128 * Forces initialization of the proxy
129 */
130 public function __load()
131 {
132 $this->__initializer__ && $this->__initializer__->__invoke($this, \'__load\', []);
133 }
134
135 /**
136 * {@inheritDoc}
137 * @internal generated method: use only when explicitly handling proxy specific loading logic
138 */
139 public function __isInitialized()
140 {
141 return $this->__isInitialized__;
142 }
143
144 /**
145 * {@inheritDoc}
146 * @internal generated method: use only when explicitly handling proxy specific loading logic
147 */
148 public function __setInitialized($initialized)
149 {
150 $this->__isInitialized__ = $initialized;
151 }
152
153 /**
154 * {@inheritDoc}
155 * @internal generated method: use only when explicitly handling proxy specific loading logic
156 */
157 public function __setInitializer(\Closure $initializer = null)
158 {
159 $this->__initializer__ = $initializer;
160 }
161
162 /**
163 * {@inheritDoc}
164 * @internal generated method: use only when explicitly handling proxy specific loading logic
165 */
166 public function __getInitializer()
167 {
168 return $this->__initializer__;
169 }
170
171 /**
172 * {@inheritDoc}
173 * @internal generated method: use only when explicitly handling proxy specific loading logic
174 */
175 public function __setCloner(\Closure $cloner = null)
176 {
177 $this->__cloner__ = $cloner;
178 }
179
180 /**
181 * {@inheritDoc}
182 * @internal generated method: use only when explicitly handling proxy specific cloning logic
183 */
184 public function __getCloner()
185 {
186 return $this->__cloner__;
187 }
188
189 /**
190 * {@inheritDoc}
191 * @internal generated method: use only when explicitly handling proxy specific loading logic
192 * @static
193 */
194 public function __getLazyProperties()
195 {
196 return self::$lazyPropertiesDefaults;
197 }
198
199 <methods>
200 }
201 ';
202
203 /**
204 * Initializes a new instance of the <tt>ProxyFactory</tt> class that is
205 * connected to the given <tt>EntityManager</tt>.
206 *
207 * @param string $proxyDirectory The directory to use for the proxy classes. It must exist.
208 * @param string $proxyNamespace The namespace to use for the proxy classes.
209 *
210 * @throws InvalidArgumentException
211 */
212 public function __construct($proxyDirectory, $proxyNamespace)
213 {
214 if ( ! $proxyDirectory) {
215 throw InvalidArgumentException::proxyDirectoryRequired();
216 }
217
218 if ( ! $proxyNamespace) {
219 throw InvalidArgumentException::proxyNamespaceRequired();
220 }
221
222 $this->proxyDirectory = $proxyDirectory;
223 $this->proxyNamespace = $proxyNamespace;
224 }
225
226 /**
227 * Sets a placeholder to be replaced in the template.
228 *
229 * @param string $name
230 * @param string|callable $placeholder
231 *
232 * @throws InvalidArgumentException
233 */
234 public function setPlaceholder($name, $placeholder)
235 {
236 if ( ! is_string($placeholder) && ! is_callable($placeholder)) {
237 throw InvalidArgumentException::invalidPlaceholder($name);
238 }
239
240 $this->placeholders[$name] = $placeholder;
241 }
242
243 /**
244 * Sets the base template used to create proxy classes.
245 *
246 * @param string $proxyClassTemplate
247 */
248 public function setProxyClassTemplate($proxyClassTemplate)
249 {
250 $this->proxyClassTemplate = (string) $proxyClassTemplate;
251 }
252
253 /**
254 * Generates a proxy class file.
255 *
256 * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class Metadata for the original class.
257 * @param string|bool $fileName Filename (full path) for the generated class. If none is given, eval() is used.
258 *
259 * @throws UnexpectedValueException
260 */
261 public function generateProxyClass(ClassMetadata $class, $fileName = false)
262 {
263 preg_match_all('(<([a-zA-Z]+)>)', $this->proxyClassTemplate, $placeholderMatches);
264
265 $placeholderMatches = array_combine($placeholderMatches[0], $placeholderMatches[1]);
266 $placeholders = [];
267
268 foreach ($placeholderMatches as $placeholder => $name) {
269 $placeholders[$placeholder] = isset($this->placeholders[$name])
270 ? $this->placeholders[$name]
271 : [$this, 'generate' . $name];
272 }
273
274 foreach ($placeholders as & $placeholder) {
275 if (is_callable($placeholder)) {
276 $placeholder = call_user_func($placeholder, $class);
277 }
278 }
279
280 $proxyCode = strtr($this->proxyClassTemplate, $placeholders);
281
282 if ( ! $fileName) {
283 $proxyClassName = $this->generateNamespace($class) . '\\' . $this->generateProxyShortClassName($class);
284
285 if ( ! class_exists($proxyClassName)) {
286 eval(substr($proxyCode, 5));
287 }
288
289 return;
290 }
291
292 $parentDirectory = dirname($fileName);
293
294 if ( ! is_dir($parentDirectory) && (false === @mkdir($parentDirectory, 0775, true))) {
295 throw UnexpectedValueException::proxyDirectoryNotWritable($this->proxyDirectory);
296 }
297
298 if ( ! is_writable($parentDirectory)) {
299 throw UnexpectedValueException::proxyDirectoryNotWritable($this->proxyDirectory);
300 }
301
302 $tmpFileName = $fileName . '.' . uniqid('', true);
303
304 file_put_contents($tmpFileName, $proxyCode);
305 @chmod($tmpFileName, 0664);
306 rename($tmpFileName, $fileName);
307 }
308
309 /**
310 * Generates the proxy short class name to be used in the template.
311 *
312 * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class
313 *
314 * @return string
315 */
316 private function generateProxyShortClassName(ClassMetadata $class)
317 {
318 $proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace);
319 $parts = explode('\\', strrev($proxyClassName), 2);
320
321 return strrev($parts[0]);
322 }
323
324 /**
325 * Generates the proxy namespace.
326 *
327 * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class
328 *
329 * @return string
330 */
331 private function generateNamespace(ClassMetadata $class)
332 {
333 $proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace);
334 $parts = explode('\\', strrev($proxyClassName), 2);
335
336 return strrev($parts[1]);
337 }
338
339 /**
340 * Generates the original class name.
341 *
342 * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class
343 *
344 * @return string
345 */
346 private function generateClassName(ClassMetadata $class)
347 {
348 return ltrim($class->getName(), '\\');
349 }
350
351 /**
352 * Generates the array representation of lazy loaded public properties and their default values.
353 *
354 * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class
355 *
356 * @return string
357 */
358 private function generateLazyPropertiesDefaults(ClassMetadata $class)
359 {
360 $lazyPublicProperties = $this->getLazyLoadedPublicProperties($class);
361 $values = [];
362
363 foreach ($lazyPublicProperties as $key => $value) {
364 $values[] = var_export($key, true) . ' => ' . var_export($value, true);
365 }
366
367 return implode(', ', $values);
368 }
369
370 /**
371 * Generates the constructor code (un-setting public lazy loaded properties, setting identifier field values).
372 *
373 * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class
374 *
375 * @return string
376 */
377 private function generateConstructorImpl(ClassMetadata $class)
378 {
379 $constructorImpl = <<<'EOT'
380 /**
381 * @param \Closure $initializer
382 * @param \Closure $cloner
383 */
384 public function __construct($initializer = null, $cloner = null)
385 {
386
387 EOT;
388 $toUnset = [];
389
390 foreach ($this->getLazyLoadedPublicProperties($class) as $lazyPublicProperty => $unused) {
391 $toUnset[] = '$this->' . $lazyPublicProperty;
392 }
393
394 $constructorImpl .= (empty($toUnset) ? '' : ' unset(' . implode(', ', $toUnset) . ");\n")
395 . <<<'EOT'
396
397 $this->__initializer__ = $initializer;
398 $this->__cloner__ = $cloner;
399 }
400 EOT;
401
402 return $constructorImpl;
403 }
404
405 /**
406 * Generates the magic getter invoked when lazy loaded public properties are requested.
407 *
408 * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class
409 *
410 * @return string
411 */
412 private function generateMagicGet(ClassMetadata $class)
413 {
414 $lazyPublicProperties = array_keys($this->getLazyLoadedPublicProperties($class));
415 $reflectionClass = $class->getReflectionClass();
416 $hasParentGet = false;
417 $returnReference = '';
418 $inheritDoc = '';
419
420 if ($reflectionClass->hasMethod('__get')) {
421 $hasParentGet = true;
422 $inheritDoc = '{@inheritDoc}';
423
424 if ($reflectionClass->getMethod('__get')->returnsReference()) {
425 $returnReference = '& ';
426 }
427 }
428
429 if (empty($lazyPublicProperties) && ! $hasParentGet) {
430 return '';
431 }
432
433 $magicGet = <<<EOT
434 /**
435 * $inheritDoc
436 * @param string \$name
437 */
438 public function {$returnReference}__get(\$name)
439 {
440
441 EOT;
442
443 if ( ! empty($lazyPublicProperties)) {
444 $magicGet .= <<<'EOT'
445 if (array_key_exists($name, $this->__getLazyProperties())) {
446 $this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]);
447
448 return $this->$name;
449 }
450
451
452 EOT;
453 }
454
455 if ($hasParentGet) {
456 $magicGet .= <<<'EOT'
457 $this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]);
458
459 return parent::__get($name);
460
461 EOT;
462 } else {
463 $magicGet .= <<<'EOT'
464 trigger_error(sprintf('Undefined property: %s::$%s', __CLASS__, $name), E_USER_NOTICE);
465
466 EOT;
467 }
468
469 $magicGet .= " }";
470
471 return $magicGet;
472 }
473
474 /**
475 * Generates the magic setter (currently unused).
476 *
477 * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class
478 *
479 * @return string
480 */
481 private function generateMagicSet(ClassMetadata $class)
482 {
483 $lazyPublicProperties = $this->getLazyLoadedPublicProperties($class);
484 $hasParentSet = $class->getReflectionClass()->hasMethod('__set');
485
486 if (empty($lazyPublicProperties) && ! $hasParentSet) {
487 return '';
488 }
489
490 $inheritDoc = $hasParentSet ? '{@inheritDoc}' : '';
491 $magicSet = <<<EOT
492 /**
493 * $inheritDoc
494 * @param string \$name
495 * @param mixed \$value
496 */
497 public function __set(\$name, \$value)
498 {
499
500 EOT;
501
502 if ( ! empty($lazyPublicProperties)) {
503 $magicSet .= <<<'EOT'
504 if (array_key_exists($name, $this->__getLazyProperties())) {
505 $this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]);
506
507 $this->$name = $value;
508
509 return;
510 }
511
512
513 EOT;
514 }
515
516 if ($hasParentSet) {
517 $magicSet .= <<<'EOT'
518 $this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]);
519
520 return parent::__set($name, $value);
521 EOT;
522 } else {
523 $magicSet .= " \$this->\$name = \$value;";
524 }
525
526 $magicSet .= "\n }";
527
528 return $magicSet;
529 }
530
531 /**
532 * Generates the magic issetter invoked when lazy loaded public properties are checked against isset().
533 *
534 * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class
535 *
536 * @return string
537 */
538 private function generateMagicIsset(ClassMetadata $class)
539 {
540 $lazyPublicProperties = array_keys($this->getLazyLoadedPublicProperties($class));
541 $hasParentIsset = $class->getReflectionClass()->hasMethod('__isset');
542
543 if (empty($lazyPublicProperties) && ! $hasParentIsset) {
544 return '';
545 }
546
547 $inheritDoc = $hasParentIsset ? '{@inheritDoc}' : '';
548 $magicIsset = <<<EOT
549 /**
550 * $inheritDoc
551 * @param string \$name
552 * @return boolean
553 */
554 public function __isset(\$name)
555 {
556
557 EOT;
558
559 if ( ! empty($lazyPublicProperties)) {
560 $magicIsset .= <<<'EOT'
561 if (array_key_exists($name, $this->__getLazyProperties())) {
562 $this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]);
563
564 return isset($this->$name);
565 }
566
567
568 EOT;
569 }
570
571 if ($hasParentIsset) {
572 $magicIsset .= <<<'EOT'
573 $this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]);
574
575 return parent::__isset($name);
576
577 EOT;
578 } else {
579 $magicIsset .= " return false;";
580 }
581
582 return $magicIsset . "\n }";
583 }
584
585 /**
586 * Generates implementation for the `__sleep` method of proxies.
587 *
588 * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class
589 *
590 * @return string
591 */
592 private function generateSleepImpl(ClassMetadata $class)
593 {
594 $hasParentSleep = $class->getReflectionClass()->hasMethod('__sleep');
595 $inheritDoc = $hasParentSleep ? '{@inheritDoc}' : '';
596 $sleepImpl = <<<EOT
597 /**
598 * $inheritDoc
599 * @return array
600 */
601 public function __sleep()
602 {
603
604 EOT;
605
606 if ($hasParentSleep) {
607 return $sleepImpl . <<<'EOT'
608 $properties = array_merge(['__isInitialized__'], parent::__sleep());
609
610 if ($this->__isInitialized__) {
611 $properties = array_diff($properties, array_keys($this->__getLazyProperties()));
612 }
613
614 return $properties;
615 }
616 EOT;
617 }
618
619 $allProperties = ['__isInitialized__'];
620
621 /* @var $prop \ReflectionProperty */
622 foreach ($class->getReflectionClass()->getProperties() as $prop) {
623 if ($prop->isStatic()) {
624 continue;
625 }
626
627 $allProperties[] = $prop->isPrivate()
628 ? "\0" . $prop->getDeclaringClass()->getName() . "\0" . $prop->getName()
629 : $prop->getName();
630 }
631
632 $lazyPublicProperties = array_keys($this->getLazyLoadedPublicProperties($class));
633 $protectedProperties = array_diff($allProperties, $lazyPublicProperties);
634
635 foreach ($allProperties as &$property) {
636 $property = var_export($property, true);
637 }
638
639 foreach ($protectedProperties as &$property) {
640 $property = var_export($property, true);
641 }
642
643 $allProperties = implode(', ', $allProperties);
644 $protectedProperties = implode(', ', $protectedProperties);
645
646 return $sleepImpl . <<<EOT
647 if (\$this->__isInitialized__) {
648 return [$allProperties];
649 }
650
651 return [$protectedProperties];
652 }
653 EOT;
654 }
655
656 /**
657 * Generates implementation for the `__wakeup` method of proxies.
658 *
659 * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class
660 *
661 * @return string
662 */
663 private function generateWakeupImpl(ClassMetadata $class)
664 {
665 $unsetPublicProperties = [];
666 $hasWakeup = $class->getReflectionClass()->hasMethod('__wakeup');
667
668 foreach (array_keys($this->getLazyLoadedPublicProperties($class)) as $lazyPublicProperty) {
669 $unsetPublicProperties[] = '$this->' . $lazyPublicProperty;
670 }
671
672 $shortName = $this->generateProxyShortClassName($class);
673 $inheritDoc = $hasWakeup ? '{@inheritDoc}' : '';
674 $wakeupImpl = <<<EOT
675 /**
676 * $inheritDoc
677 */
678 public function __wakeup()
679 {
680 if ( ! \$this->__isInitialized__) {
681 \$this->__initializer__ = function ($shortName \$proxy) {
682 \$proxy->__setInitializer(null);
683 \$proxy->__setCloner(null);
684
685 \$existingProperties = get_object_vars(\$proxy);
686
687 foreach (\$proxy->__getLazyProperties() as \$property => \$defaultValue) {
688 if ( ! array_key_exists(\$property, \$existingProperties)) {
689 \$proxy->\$property = \$defaultValue;
690 }
691 }
692 };
693
694 EOT;
695
696 if ( ! empty($unsetPublicProperties)) {
697 $wakeupImpl .= "\n unset(" . implode(', ', $unsetPublicProperties) . ");";
698 }
699
700 $wakeupImpl .= "\n }";
701
702 if ($hasWakeup) {
703 $wakeupImpl .= "\n parent::__wakeup();";
704 }
705
706 $wakeupImpl .= "\n }";
707
708 return $wakeupImpl;
709 }
710
711 /**
712 * Generates implementation for the `__clone` method of proxies.
713 *
714 * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class
715 *
716 * @return string
717 */
718 private function generateCloneImpl(ClassMetadata $class)
719 {
720 $hasParentClone = $class->getReflectionClass()->hasMethod('__clone');
721 $inheritDoc = $hasParentClone ? '{@inheritDoc}' : '';
722 $callParentClone = $hasParentClone ? "\n parent::__clone();\n" : '';
723
724 return <<<EOT
725 /**
726 * $inheritDoc
727 */
728 public function __clone()
729 {
730 \$this->__cloner__ && \$this->__cloner__->__invoke(\$this, '__clone', []);
731 $callParentClone }
732 EOT;
733 }
734
735 /**
736 * Generates decorated methods by picking those available in the parent class.
737 *
738 * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class
739 *
740 * @return string
741 */
742 private function generateMethods(ClassMetadata $class)
743 {
744 $methods = '';
745 $methodNames = [];
746 $reflectionMethods = $class->getReflectionClass()->getMethods(\ReflectionMethod::IS_PUBLIC);
747 $skippedMethods = [
748 '__sleep' => true,
749 '__clone' => true,
750 '__wakeup' => true,
751 '__get' => true,
752 '__set' => true,
753 '__isset' => true,
754 ];
755
756 foreach ($reflectionMethods as $method) {
757 $name = $method->getName();
758
759 if (
760 $method->isConstructor() ||
761 isset($skippedMethods[strtolower($name)]) ||
762 isset($methodNames[$name]) ||
763 $method->isFinal() ||
764 $method->isStatic() ||
765 ( ! $method->isPublic())
766 ) {
767 continue;
768 }
769
770 $methodNames[$name] = true;
771 $methods .= "\n /**\n"
772 . " * {@inheritDoc}\n"
773 . " */\n"
774 . ' public function ';
775
776 if ($method->returnsReference()) {
777 $methods .= '&';
778 }
779
780 $methods .= $name . '(' . $this->buildParametersString($class, $method, $method->getParameters()) . ')';
781 $methods .= $this->getMethodReturnType($method);
782 $methods .= "\n" . ' {' . "\n";
783
784 if ($this->isShortIdentifierGetter($method, $class)) {
785 $identifier = lcfirst(substr($name, 3));
786 $fieldType = $class->getTypeOfField($identifier);
787 $cast = in_array($fieldType, ['integer', 'smallint']) ? '(int) ' : '';
788
789 $methods .= ' if ($this->__isInitialized__ === false) {' . "\n";
790 $methods .= ' return ' . $cast . ' parent::' . $method->getName() . "();\n";
791 $methods .= ' }' . "\n\n";
792 }
793
794 $invokeParamsString = implode(', ', $this->getParameterNamesForInvoke($method->getParameters()));
795 $callParamsString = implode(', ', $this->getParameterNamesForParentCall($method->getParameters()));
796
797 $methods .= "\n \$this->__initializer__ "
798 . "&& \$this->__initializer__->__invoke(\$this, " . var_export($name, true)
799 . ", [" . $invokeParamsString . "]);"
800 . "\n\n return parent::" . $name . '(' . $callParamsString . ');'
801 . "\n" . ' }' . "\n";
802 }
803
804 return $methods;
805 }
806
807 /**
808 * Generates the Proxy file name.
809 *
810 * @param string $className
811 * @param string $baseDirectory Optional base directory for proxy file name generation.
812 * If not specified, the directory configured on the Configuration of the
813 * EntityManager will be used by this factory.
814 *
815 * @return string
816 */
817 public function getProxyFileName($className, $baseDirectory = null)
818 {
819 $baseDirectory = $baseDirectory ?: $this->proxyDirectory;
820
821 return rtrim($baseDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . Proxy::MARKER
822 . str_replace('\\', '', $className) . '.php';
823 }
824
825 /**
826 * Checks if the method is a short identifier getter.
827 *
828 * What does this mean? For proxy objects the identifier is already known,
829 * however accessing the getter for this identifier usually triggers the
830 * lazy loading, leading to a query that may not be necessary if only the
831 * ID is interesting for the userland code (for example in views that
832 * generate links to the entity, but do not display anything else).
833 *
834 * @param \ReflectionMethod $method
835 * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class
836 *
837 * @return boolean
838 */
839 private function isShortIdentifierGetter($method, ClassMetadata $class)
840 {
841 $identifier = lcfirst(substr($method->getName(), 3));
842 $startLine = $method->getStartLine();
843 $endLine = $method->getEndLine();
844 $cheapCheck = (
845 $method->getNumberOfParameters() == 0
846 && substr($method->getName(), 0, 3) == 'get'
847 && in_array($identifier, $class->getIdentifier(), true)
848 && $class->hasField($identifier)
849 && (($endLine - $startLine) <= 4)
850 );
851
852 if ($cheapCheck) {
853 $code = file($method->getDeclaringClass()->getFileName());
854 $code = trim(implode(' ', array_slice($code, $startLine - 1, $endLine - $startLine + 1)));
855
856 $pattern = sprintf(self::PATTERN_MATCH_ID_METHOD, $method->getName(), $identifier);
857
858 if (preg_match($pattern, $code)) {
859 return true;
860 }
861 }
862
863 return false;
864 }
865
866 /**
867 * Generates the list of public properties to be lazy loaded, with their default values.
868 *
869 * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class
870 *
871 * @return mixed[]
872 */
873 private function getLazyLoadedPublicProperties(ClassMetadata $class)
874 {
875 $defaultProperties = $class->getReflectionClass()->getDefaultProperties();
876 $properties = [];
877
878 foreach ($class->getReflectionClass()->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
879 $name = $property->getName();
880
881 if (($class->hasField($name) || $class->hasAssociation($name)) && ! $class->isIdentifier($name)) {
882 $properties[$name] = $defaultProperties[$name];
883 }
884 }
885
886 return $properties;
887 }
888
889 /**
890 * @param ClassMetadata $class
891 * @param \ReflectionMethod $method
892 * @param \ReflectionParameter[] $parameters
893 *
894 * @return string
895 */
896 private function buildParametersString(ClassMetadata $class, \ReflectionMethod $method, array $parameters)
897 {
898 $parameterDefinitions = [];
899
900 /* @var $param \ReflectionParameter */
901 foreach ($parameters as $param) {
902 $parameterDefinition = '';
903
904 if ($parameterType = $this->getParameterType($class, $method, $param)) {
905 $parameterDefinition .= $parameterType . ' ';
906 }
907
908 if ($param->isPassedByReference()) {
909 $parameterDefinition .= '&';
910 }
911
912 if (method_exists($param, 'isVariadic') && $param->isVariadic()) {
913 $parameterDefinition .= '...';
914 }
915
916 $parameters[] = '$' . $param->getName();
917 $parameterDefinition .= '$' . $param->getName();
918
919 if ($param->isDefaultValueAvailable()) {
920 $parameterDefinition .= ' = ' . var_export($param->getDefaultValue(), true);
921 }
922
923 $parameterDefinitions[] = $parameterDefinition;
924 }
925
926 return implode(', ', $parameterDefinitions);
927 }
928
929 /**
930 * @param ClassMetadata $class
931 * @param \ReflectionMethod $method
932 * @param \ReflectionParameter $parameter
933 *
934 * @return string|null
935 */
936 private function getParameterType(ClassMetadata $class, \ReflectionMethod $method, \ReflectionParameter $parameter)
937 {
938
939 // We need to pick the type hint class too
940 if ($parameter->isArray()) {
941 return 'array';
942 }
943
944 if ($parameter->isCallable()) {
945 return 'callable';
946 }
947
948 if (method_exists($parameter, 'hasType') && $parameter->hasType() && $parameter->getType()->isBuiltin()) {
949 return (string) $parameter->getType();
950 }
951
952 try {
953 $parameterClass = $parameter->getClass();
954
955 if ($parameterClass) {
956 return '\\' . $parameterClass->getName();
957 }
958 } catch (\ReflectionException $previous) {
959 throw UnexpectedValueException::invalidParameterTypeHint(
960 $class->getName(),
961 $method->getName(),
962 $parameter->getName(),
963 $previous
964 );
965 }
966
967 return null;
968 }
969
970 /**
971 * @param \ReflectionParameter[] $parameters
972 *
973 * @return string[]
974 */
975 private function getParameterNamesForInvoke(array $parameters)
976 {
977 return array_map(
978 function (\ReflectionParameter $parameter) {
979 return '$' . $parameter->getName();
980 },
981 $parameters
982 );
983 }
984
985 /**
986 * @param \ReflectionParameter[] $parameters
987 *
988 * @return string[]
989 */
990 private function getParameterNamesForParentCall(array $parameters)
991 {
992 return array_map(
993 function (\ReflectionParameter $parameter) {
994 $name = '';
995
996 if (method_exists($parameter, 'isVariadic') && $parameter->isVariadic()) {
997 $name .= '...';
998 }
999
1000 $name .= '$' . $parameter->getName();
1001
1002 return $name;
1003 },
1004 $parameters
1005 );
1006 }
1007
1008 /**
1009 * @Param \ReflectionMethod $method
1010 *
1011 * @return string
1012 */
1013 private function getMethodReturnType(\ReflectionMethod $method)
1014 {
1015 if (! (method_exists($method, 'hasReturnType') && $method->hasReturnType())) {
1016 return '';
1017 }
1018
1019 $returnType = $method->getReturnType();
1020
1021 if ($returnType->isBuiltin()) {
1022 return ': ' . $returnType;
1023 }
1024
1025 $nameLower = strtolower((string) $returnType);
1026
1027 if ('self' === $nameLower) {
1028 return ': \\' . $method->getDeclaringClass()->getName();
1029 }
1030
1031 if ('parent' === $nameLower) {
1032 return ': \\' . $method->getDeclaringClass()->getParentClass()->getName();
1033 }
1034
1035 return ': \\' . (string) $returnType;
1036 }
1037 }