vendor/doctrine/common/src/Proxy/AbstractProxyFactory.php line 111

Open in your IDE?
  1. <?php
  2. namespace Doctrine\Common\Proxy;
  3. use Doctrine\Common\Proxy\Exception\InvalidArgumentException;
  4. use Doctrine\Common\Proxy\Exception\OutOfBoundsException;
  5. use Doctrine\Common\Util\ClassUtils;
  6. use Doctrine\Persistence\Mapping\ClassMetadata;
  7. use Doctrine\Persistence\Mapping\ClassMetadataFactory;
  8. use function class_exists;
  9. use function file_exists;
  10. use function filemtime;
  11. use function in_array;
  12. /**
  13. * Abstract factory for proxy objects.
  14. *
  15. * @deprecated The AbstractProxyFactory class is deprecated since doctrine/common 3.5.
  16. */
  17. abstract class AbstractProxyFactory
  18. {
  19. /**
  20. * Never autogenerate a proxy and rely that it was generated by some
  21. * process before deployment.
  22. */
  23. public const AUTOGENERATE_NEVER = 0;
  24. /**
  25. * Always generates a new proxy in every request.
  26. *
  27. * This is only sane during development.
  28. */
  29. public const AUTOGENERATE_ALWAYS = 1;
  30. /**
  31. * Autogenerate the proxy class when the proxy file does not exist.
  32. *
  33. * This strategy causes a file_exists() call whenever any proxy is used the
  34. * first time in a request.
  35. */
  36. public const AUTOGENERATE_FILE_NOT_EXISTS = 2;
  37. /**
  38. * Generate the proxy classes using eval().
  39. *
  40. * This strategy is only sane for development, and even then it gives me
  41. * the creeps a little.
  42. */
  43. public const AUTOGENERATE_EVAL = 3;
  44. /**
  45. * Autogenerate the proxy class when the proxy file does not exist or
  46. * when the proxied file changed.
  47. *
  48. * This strategy causes a file_exists() call whenever any proxy is used the
  49. * first time in a request. When the proxied file is changed, the proxy will
  50. * be updated.
  51. */
  52. public const AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED = 4;
  53. private const AUTOGENERATE_MODES = [
  54. self::AUTOGENERATE_NEVER,
  55. self::AUTOGENERATE_ALWAYS,
  56. self::AUTOGENERATE_FILE_NOT_EXISTS,
  57. self::AUTOGENERATE_EVAL,
  58. self::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED,
  59. ];
  60. /** @var ClassMetadataFactory */
  61. private $metadataFactory;
  62. /** @var ProxyGenerator the proxy generator responsible for creating the proxy classes/files. */
  63. private $proxyGenerator;
  64. /** @var int Whether to automatically (re)generate proxy classes. */
  65. private $autoGenerate;
  66. /** @var ProxyDefinition[] */
  67. private $definitions = [];
  68. /**
  69. * @param bool|int $autoGenerate
  70. *
  71. * @throws InvalidArgumentException When auto generate mode is not valid.
  72. */
  73. public function __construct(ProxyGenerator $proxyGenerator, ClassMetadataFactory $metadataFactory, $autoGenerate)
  74. {
  75. $this->proxyGenerator = $proxyGenerator;
  76. $this->metadataFactory = $metadataFactory;
  77. $this->autoGenerate = (int) $autoGenerate;
  78. if (! in_array($this->autoGenerate, self::AUTOGENERATE_MODES, true)) {
  79. throw InvalidArgumentException::invalidAutoGenerateMode($autoGenerate);
  80. }
  81. }
  82. /**
  83. * Gets a reference proxy instance for the entity of the given type and identified by
  84. * the given identifier.
  85. *
  86. * @param string $className
  87. * @param array<mixed> $identifier
  88. *
  89. * @return Proxy
  90. *
  91. * @throws OutOfBoundsException
  92. */
  93. public function getProxy($className, array $identifier)
  94. {
  95. $definition = $this->definitions[$className] ?? $this->getProxyDefinition($className);
  96. $fqcn = $definition->proxyClassName;
  97. $proxy = new $fqcn($definition->initializer, $definition->cloner);
  98. foreach ($definition->identifierFields as $idField) {
  99. if (! isset($identifier[$idField])) {
  100. throw OutOfBoundsException::missingPrimaryKeyValue($className, $idField);
  101. }
  102. $definition->reflectionFields[$idField]->setValue($proxy, $identifier[$idField]);
  103. }
  104. return $proxy;
  105. }
  106. /**
  107. * Generates proxy classes for all given classes.
  108. *
  109. * @param ClassMetadata[] $classes The classes (ClassMetadata instances)
  110. * for which to generate proxies.
  111. * @param string $proxyDir The target directory of the proxy classes. If not specified, the
  112. * directory configured on the Configuration of the EntityManager used
  113. * by this factory is used.
  114. *
  115. * @return int Number of generated proxies.
  116. */
  117. public function generateProxyClasses(array $classes, $proxyDir = null)
  118. {
  119. $generated = 0;
  120. foreach ($classes as $class) {
  121. if ($this->skipClass($class)) {
  122. continue;
  123. }
  124. $proxyFileName = $this->proxyGenerator->getProxyFileName($class->getName(), $proxyDir);
  125. $this->proxyGenerator->generateProxyClass($class, $proxyFileName);
  126. $generated += 1;
  127. }
  128. return $generated;
  129. }
  130. /**
  131. * Reset initialization/cloning logic for an un-initialized proxy
  132. *
  133. * @return Proxy
  134. *
  135. * @throws InvalidArgumentException
  136. */
  137. public function resetUninitializedProxy(Proxy $proxy)
  138. {
  139. if ($proxy->__isInitialized()) {
  140. throw InvalidArgumentException::unitializedProxyExpected($proxy);
  141. }
  142. $className = ClassUtils::getClass($proxy);
  143. $definition = $this->definitions[$className] ?? $this->getProxyDefinition($className);
  144. $proxy->__setInitializer($definition->initializer);
  145. $proxy->__setCloner($definition->cloner);
  146. return $proxy;
  147. }
  148. /**
  149. * Get a proxy definition for the given class name.
  150. *
  151. * @param string $className
  152. * @psalm-param class-string $className
  153. *
  154. * @return ProxyDefinition
  155. */
  156. private function getProxyDefinition($className)
  157. {
  158. $classMetadata = $this->metadataFactory->getMetadataFor($className);
  159. $className = $classMetadata->getName(); // aliases and case sensitivity
  160. $this->definitions[$className] = $this->createProxyDefinition($className);
  161. $proxyClassName = $this->definitions[$className]->proxyClassName;
  162. if (! class_exists($proxyClassName, false)) {
  163. $fileName = $this->proxyGenerator->getProxyFileName($className);
  164. switch ($this->autoGenerate) {
  165. case self::AUTOGENERATE_NEVER:
  166. require $fileName;
  167. break;
  168. case self::AUTOGENERATE_FILE_NOT_EXISTS:
  169. if (! file_exists($fileName)) {
  170. $this->proxyGenerator->generateProxyClass($classMetadata, $fileName);
  171. }
  172. require $fileName;
  173. break;
  174. case self::AUTOGENERATE_ALWAYS:
  175. $this->proxyGenerator->generateProxyClass($classMetadata, $fileName);
  176. require $fileName;
  177. break;
  178. case self::AUTOGENERATE_EVAL:
  179. $this->proxyGenerator->generateProxyClass($classMetadata, false);
  180. break;
  181. case self::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED:
  182. if (! file_exists($fileName) || filemtime($fileName) < filemtime($classMetadata->getReflectionClass()->getFileName())) {
  183. $this->proxyGenerator->generateProxyClass($classMetadata, $fileName);
  184. }
  185. require $fileName;
  186. break;
  187. }
  188. }
  189. return $this->definitions[$className];
  190. }
  191. /**
  192. * Determine if this class should be skipped during proxy generation.
  193. *
  194. * @return bool
  195. */
  196. abstract protected function skipClass(ClassMetadata $metadata);
  197. /**
  198. * @param string $className
  199. * @psalm-param class-string $className
  200. *
  201. * @return ProxyDefinition
  202. */
  203. abstract protected function createProxyDefinition($className);
  204. }