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