vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyGenerator.php line 291

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