PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php

https://gitlab.com/mario.uriarte/doctrine2.5-tutorial
PHP | 429 lines | 167 code | 52 blank | 210 comment | 19 complexity | 602bc335fe714d6bcd45df9506f3c893 MD5 | raw file
  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\Persistence\Mapping;
  20. use Doctrine\Common\Cache\Cache;
  21. use Doctrine\Common\Util\ClassUtils;
  22. use ReflectionException;
  23. /**
  24. * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
  25. * metadata mapping informations of a class which describes how a class should be mapped
  26. * to a relational database.
  27. *
  28. * This class was abstracted from the ORM ClassMetadataFactory.
  29. *
  30. * @since 2.2
  31. * @author Benjamin Eberlei <kontakt@beberlei.de>
  32. * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
  33. * @author Jonathan Wage <jonwage@gmail.com>
  34. * @author Roman Borschel <roman@code-factory.org>
  35. */
  36. abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
  37. {
  38. /**
  39. * Salt used by specific Object Manager implementation.
  40. *
  41. * @var string
  42. */
  43. protected $cacheSalt = '$CLASSMETADATA';
  44. /**
  45. * @var \Doctrine\Common\Cache\Cache|null
  46. */
  47. private $cacheDriver;
  48. /**
  49. * @var ClassMetadata[]
  50. */
  51. private $loadedMetadata = [];
  52. /**
  53. * @var bool
  54. */
  55. protected $initialized = false;
  56. /**
  57. * @var ReflectionService|null
  58. */
  59. private $reflectionService = null;
  60. /**
  61. * Sets the cache driver used by the factory to cache ClassMetadata instances.
  62. *
  63. * @param \Doctrine\Common\Cache\Cache $cacheDriver
  64. *
  65. * @return void
  66. */
  67. public function setCacheDriver(Cache $cacheDriver = null)
  68. {
  69. $this->cacheDriver = $cacheDriver;
  70. }
  71. /**
  72. * Gets the cache driver used by the factory to cache ClassMetadata instances.
  73. *
  74. * @return \Doctrine\Common\Cache\Cache|null
  75. */
  76. public function getCacheDriver()
  77. {
  78. return $this->cacheDriver;
  79. }
  80. /**
  81. * Returns an array of all the loaded metadata currently in memory.
  82. *
  83. * @return ClassMetadata[]
  84. */
  85. public function getLoadedMetadata()
  86. {
  87. return $this->loadedMetadata;
  88. }
  89. /**
  90. * Forces the factory to load the metadata of all classes known to the underlying
  91. * mapping driver.
  92. *
  93. * @return array The ClassMetadata instances of all mapped classes.
  94. */
  95. public function getAllMetadata()
  96. {
  97. if ( ! $this->initialized) {
  98. $this->initialize();
  99. }
  100. $driver = $this->getDriver();
  101. $metadata = [];
  102. foreach ($driver->getAllClassNames() as $className) {
  103. $metadata[] = $this->getMetadataFor($className);
  104. }
  105. return $metadata;
  106. }
  107. /**
  108. * Lazy initialization of this stuff, especially the metadata driver,
  109. * since these are not needed at all when a metadata cache is active.
  110. *
  111. * @return void
  112. */
  113. abstract protected function initialize();
  114. /**
  115. * Gets the fully qualified class-name from the namespace alias.
  116. *
  117. * @param string $namespaceAlias
  118. * @param string $simpleClassName
  119. *
  120. * @return string
  121. */
  122. abstract protected function getFqcnFromAlias($namespaceAlias, $simpleClassName);
  123. /**
  124. * Returns the mapping driver implementation.
  125. *
  126. * @return \Doctrine\Common\Persistence\Mapping\Driver\MappingDriver
  127. */
  128. abstract protected function getDriver();
  129. /**
  130. * Wakes up reflection after ClassMetadata gets unserialized from cache.
  131. *
  132. * @param ClassMetadata $class
  133. * @param ReflectionService $reflService
  134. *
  135. * @return void
  136. */
  137. abstract protected function wakeupReflection(ClassMetadata $class, ReflectionService $reflService);
  138. /**
  139. * Initializes Reflection after ClassMetadata was constructed.
  140. *
  141. * @param ClassMetadata $class
  142. * @param ReflectionService $reflService
  143. *
  144. * @return void
  145. */
  146. abstract protected function initializeReflection(ClassMetadata $class, ReflectionService $reflService);
  147. /**
  148. * Checks whether the class metadata is an entity.
  149. *
  150. * This method should return false for mapped superclasses or embedded classes.
  151. *
  152. * @param ClassMetadata $class
  153. *
  154. * @return boolean
  155. */
  156. abstract protected function isEntity(ClassMetadata $class);
  157. /**
  158. * Gets the class metadata descriptor for a class.
  159. *
  160. * @param string $className The name of the class.
  161. *
  162. * @return ClassMetadata
  163. *
  164. * @throws ReflectionException
  165. * @throws MappingException
  166. */
  167. public function getMetadataFor($className)
  168. {
  169. if (isset($this->loadedMetadata[$className])) {
  170. return $this->loadedMetadata[$className];
  171. }
  172. // Check for namespace alias
  173. if (strpos($className, ':') !== false) {
  174. list($namespaceAlias, $simpleClassName) = explode(':', $className, 2);
  175. $realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName);
  176. } else {
  177. $realClassName = ClassUtils::getRealClass($className);
  178. }
  179. if (isset($this->loadedMetadata[$realClassName])) {
  180. // We do not have the alias name in the map, include it
  181. return $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
  182. }
  183. $loadingException = null;
  184. try {
  185. if ($this->cacheDriver) {
  186. if (($cached = $this->cacheDriver->fetch($realClassName . $this->cacheSalt)) !== false) {
  187. $this->loadedMetadata[$realClassName] = $cached;
  188. $this->wakeupReflection($cached, $this->getReflectionService());
  189. } else {
  190. foreach ($this->loadMetadata($realClassName) as $loadedClassName) {
  191. $this->cacheDriver->save(
  192. $loadedClassName . $this->cacheSalt,
  193. $this->loadedMetadata[$loadedClassName],
  194. null
  195. );
  196. }
  197. }
  198. } else {
  199. $this->loadMetadata($realClassName);
  200. }
  201. } catch (MappingException $loadingException) {
  202. if (! $fallbackMetadataResponse = $this->onNotFoundMetadata($realClassName)) {
  203. throw $loadingException;
  204. }
  205. $this->loadedMetadata[$realClassName] = $fallbackMetadataResponse;
  206. }
  207. if ($className !== $realClassName) {
  208. // We do not have the alias name in the map, include it
  209. $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
  210. }
  211. return $this->loadedMetadata[$className];
  212. }
  213. /**
  214. * Checks whether the factory has the metadata for a class loaded already.
  215. *
  216. * @param string $className
  217. *
  218. * @return boolean TRUE if the metadata of the class in question is already loaded, FALSE otherwise.
  219. */
  220. public function hasMetadataFor($className)
  221. {
  222. return isset($this->loadedMetadata[$className]);
  223. }
  224. /**
  225. * Sets the metadata descriptor for a specific class.
  226. *
  227. * NOTE: This is only useful in very special cases, like when generating proxy classes.
  228. *
  229. * @param string $className
  230. * @param ClassMetadata $class
  231. *
  232. * @return void
  233. */
  234. public function setMetadataFor($className, $class)
  235. {
  236. $this->loadedMetadata[$className] = $class;
  237. }
  238. /**
  239. * Gets an array of parent classes for the given entity class.
  240. *
  241. * @param string $name
  242. *
  243. * @return array
  244. */
  245. protected function getParentClasses($name)
  246. {
  247. // Collect parent classes, ignoring transient (not-mapped) classes.
  248. $parentClasses = [];
  249. foreach (array_reverse($this->getReflectionService()->getParentClasses($name)) as $parentClass) {
  250. if ( ! $this->getDriver()->isTransient($parentClass)) {
  251. $parentClasses[] = $parentClass;
  252. }
  253. }
  254. return $parentClasses;
  255. }
  256. /**
  257. * Loads the metadata of the class in question and all it's ancestors whose metadata
  258. * is still not loaded.
  259. *
  260. * Important: The class $name does not necesarily exist at this point here.
  261. * Scenarios in a code-generation setup might have access to XML/YAML
  262. * Mapping files without the actual PHP code existing here. That is why the
  263. * {@see Doctrine\Common\Persistence\Mapping\ReflectionService} interface
  264. * should be used for reflection.
  265. *
  266. * @param string $name The name of the class for which the metadata should get loaded.
  267. *
  268. * @return array
  269. */
  270. protected function loadMetadata($name)
  271. {
  272. if ( ! $this->initialized) {
  273. $this->initialize();
  274. }
  275. $loaded = [];
  276. $parentClasses = $this->getParentClasses($name);
  277. $parentClasses[] = $name;
  278. // Move down the hierarchy of parent classes, starting from the topmost class
  279. $parent = null;
  280. $rootEntityFound = false;
  281. $visited = [];
  282. $reflService = $this->getReflectionService();
  283. foreach ($parentClasses as $className) {
  284. if (isset($this->loadedMetadata[$className])) {
  285. $parent = $this->loadedMetadata[$className];
  286. if ($this->isEntity($parent)) {
  287. $rootEntityFound = true;
  288. array_unshift($visited, $className);
  289. }
  290. continue;
  291. }
  292. $class = $this->newClassMetadataInstance($className);
  293. $this->initializeReflection($class, $reflService);
  294. $this->doLoadMetadata($class, $parent, $rootEntityFound, $visited);
  295. $this->loadedMetadata[$className] = $class;
  296. $parent = $class;
  297. if ($this->isEntity($class)) {
  298. $rootEntityFound = true;
  299. array_unshift($visited, $className);
  300. }
  301. $this->wakeupReflection($class, $reflService);
  302. $loaded[] = $className;
  303. }
  304. return $loaded;
  305. }
  306. /**
  307. * Provides a fallback hook for loading metadata when loading failed due to reflection/mapping exceptions
  308. *
  309. * Override this method to implement a fallback strategy for failed metadata loading
  310. *
  311. * @param string $className
  312. *
  313. * @return \Doctrine\Common\Persistence\Mapping\ClassMetadata|null
  314. */
  315. protected function onNotFoundMetadata($className)
  316. {
  317. return null;
  318. }
  319. /**
  320. * Actually loads the metadata from the underlying metadata.
  321. *
  322. * @param ClassMetadata $class
  323. * @param ClassMetadata|null $parent
  324. * @param bool $rootEntityFound
  325. * @param array $nonSuperclassParents All parent class names
  326. * that are not marked as mapped superclasses.
  327. *
  328. * @return void
  329. */
  330. abstract protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents);
  331. /**
  332. * Creates a new ClassMetadata instance for the given class name.
  333. *
  334. * @param string $className
  335. *
  336. * @return ClassMetadata
  337. */
  338. abstract protected function newClassMetadataInstance($className);
  339. /**
  340. * {@inheritDoc}
  341. */
  342. public function isTransient($class)
  343. {
  344. if ( ! $this->initialized) {
  345. $this->initialize();
  346. }
  347. // Check for namespace alias
  348. if (strpos($class, ':') !== false) {
  349. list($namespaceAlias, $simpleClassName) = explode(':', $class, 2);
  350. $class = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName);
  351. }
  352. return $this->getDriver()->isTransient($class);
  353. }
  354. /**
  355. * Sets the reflectionService.
  356. *
  357. * @param ReflectionService $reflectionService
  358. *
  359. * @return void
  360. */
  361. public function setReflectionService(ReflectionService $reflectionService)
  362. {
  363. $this->reflectionService = $reflectionService;
  364. }
  365. /**
  366. * Gets the reflection service associated with this metadata factory.
  367. *
  368. * @return ReflectionService
  369. */
  370. public function getReflectionService()
  371. {
  372. if ($this->reflectionService === null) {
  373. $this->reflectionService = new RuntimeReflectionService();
  374. }
  375. return $this->reflectionService;
  376. }
  377. }