PageRenderTime 53ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

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

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