1Handling Annotations
2====================
3
4There are several different approaches to handling annotations in PHP. Doctrine Annotations
5maps docblock annotations to PHP classes. Because not all docblock annotations are used
6for metadata purposes a filter is applied to ignore or skip classes that are not Doctrine annotations.
7
8Take a look at the following code snippet:
9
10.. code-block:: php
11
12    namespace MyProject\Entities;
13
14    use Doctrine\ORM\Mapping AS ORM;
15    use Symfony\Component\Validation\Constraints AS Assert;
16
17    /**
18     * @author Benjamin Eberlei
19     * @ORM\Entity
20     * @MyProject\Annotations\Foobarable
21     */
22    class User
23    {
24        /**
25         * @ORM\Id @ORM\Column @ORM\GeneratedValue
26         * @dummy
27         * @var int
28         */
29        private $id;
30
31        /**
32         * @ORM\Column(type="string")
33         * @Assert\NotEmpty
34         * @Assert\Email
35         * @var string
36         */
37        private $email;
38    }
39
40In this snippet you can see a variety of different docblock annotations:
41
42- Documentation annotations such as ``@var`` and ``@author``. These annotations are on a blacklist and never considered for throwing an exception due to wrongly used annotations.
43- Annotations imported through use statements. The statement ``use Doctrine\ORM\Mapping AS ORM`` makes all classes under that namespace available as ``@ORM\ClassName``. Same goes for the import of ``@Assert``.
44- The ``@dummy`` annotation. It is not a documentation annotation and not blacklisted. For Doctrine Annotations it is not entirely clear how to handle this annotation. Depending on the configuration an exception (unknown annotation) will be thrown when parsing this annotation.
45- The fully qualified annotation ``@MyProject\Annotations\Foobarable``. This is transformed directly into the given class name.
46
47How are these annotations loaded? From looking at the code you could guess that the ORM Mapping, Assert Validation and the fully qualified annotation can just be loaded using
48the defined PHP autoloaders. This is not the case however: For error handling reasons every check for class existence inside the AnnotationReader sets the second parameter $autoload
49of ``class_exists($name, $autoload)`` to false. To work flawlessly the AnnotationReader requires silent autoloaders which many autoloaders are not. Silent autoloading is NOT
50part of the `PSR-0 specification <https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md>`_ for autoloading.
51
52This is why Doctrine Annotations uses its own autoloading mechanism through a global registry. If you are wondering about the annotation registry being global,
53there is no other way to solve the architectural problems of autoloading annotation classes in a straightforward fashion. Additionally if you think about PHP
54autoloading then you recognize it is a global as well.
55
56To anticipate the configuration section, making the above PHP class work with Doctrine Annotations requires this setup:
57
58.. code-block:: php
59
60    use Doctrine\Common\Annotations\AnnotationReader;
61    use Doctrine\Common\Annotations\AnnotationRegistry;
62
63    AnnotationRegistry::registerFile("/path/to/doctrine/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php");
64    AnnotationRegistry::registerAutoloadNamespace("Symfony\Component\Validator\Constraint", "/path/to/symfony/src");
65    AnnotationRegistry::registerAutoloadNamespace("MyProject\Annotations", "/path/to/myproject/src");
66
67    $reader = new AnnotationReader();
68    AnnotationReader::addGlobalIgnoredName('dummy');
69
70The second block with the annotation registry calls registers all the three different annotation namespaces that are used.
71Doctrine saves all its annotations in a single file, that is why ``AnnotationRegistry#registerFile`` is used in contrast to
72``AnnotationRegistry#registerAutoloadNamespace`` which creates a PSR-0 compatible loading mechanism for class to file names.
73
74In the third block, we create the actual AnnotationReader instance. Note that we also add "dummy" to the global list of annotations
75for which we do not throw exceptions. Setting this is necessary in our example case, otherwise ``@dummy`` would trigger an exception to
76be thrown during the parsing of the docblock of ``MyProject\Entities\User#id``.
77
78Setup and Configuration
79-----------------------
80
81To use the annotations library is simple, you just need to create a new ``AnnotationReader`` instance:
82
83.. code-block:: php
84
85    $reader = new \Doctrine\Common\Annotations\AnnotationReader();
86
87This creates a simple annotation reader with no caching other than in memory (in php arrays).
88Since parsing docblocks can be expensive you should cache this process by using
89a caching reader.
90
91You can use a file caching reader:
92
93.. code-block:: php
94
95    use Doctrine\Common\Annotations\FileCacheReader;
96    use Doctrine\Common\Annotations\AnnotationReader;
97
98    $reader = new FileCacheReader(
99        new AnnotationReader(),
100        "/path/to/cache",
101        $debug = true
102    );
103
104If you set the debug flag to true the cache reader will check for changes in the original files, which
105is very important during development. If you don't set it to true you have to delete the directory to clear the cache.
106This gives faster performance, however should only be used in production, because of its inconvenience
107during development.
108
109You can also use one of the ``Doctrine\Common\Cache\Cache`` cache implementations to cache the annotations:
110
111.. code-block:: php
112
113    use Doctrine\Common\Annotations\AnnotationReader;
114    use Doctrine\Common\Annotations\CachedReader;
115    use Doctrine\Common\Cache\ApcCache;
116
117    $reader = new CachedReader(
118        new AnnotationReader(),
119        new ApcCache(),
120        $debug = true
121    );
122
123The debug flag is used here as well to invalidate the cache files when the PHP class with annotations changed
124and should be used during development.
125
126.. warning ::
127
128    The AnnotationReader works and caches under the
129    assumption that all annotations of a doc-block are processed at
130    once. That means that annotation classes that do not exist and
131    aren't loaded and cannot be autoloaded (using the AnnotationRegistry) would never be visible and not
132    accessible if a cache is used unless the cache is cleared and the
133    annotations requested again, this time with all annotations
134    defined.
135
136By default the annotation reader returns a list of annotations with numeric indexes. If you want your annotations
137to be indexed by their class name you can wrap the reader in an IndexedReader:
138
139.. code-block:: php
140
141    use Doctrine\Common\Annotations\AnnotationReader;
142    use Doctrine\Common\Annotations\IndexedReader;
143
144    $reader = new IndexedReader(new AnnotationReader());
145
146.. warning::
147
148    You should never wrap the indexed reader inside a cached reader only the other way around. This way you can re-use
149    the cache with indexed or numeric keys, otherwise your code may experience failures due to caching in an numerical
150    or indexed format.
151
152Registering Annotations
153~~~~~~~~~~~~~~~~~~~~~~~
154
155As explained in the Introduction Doctrine Annotations uses its own autoloading mechanism to determine if a
156given annotation has a corresponding PHP class that can be autoloaded. For Annotation Autoloading you have
157to configure the ``Doctrine\Common\Annotations\AnnotationRegistry``. There are three different mechanisms
158to configure annotation autoloading:
159
160-   Calling ``AnnotationRegistry#registerFile($file)`` to register a file that contains one or more Annotation classes.
161-   Calling ``AnnotationRegistry#registerNamespace($namespace, $dirs = null)`` to register that the given namespace
162    contains annotations and that their base directory is located at the given $dirs or in the include path if NULL is passed.
163    The given directories should *NOT* be the directory where classes of the namespace are in, but the base directory
164    of the root namespace. The AnnotationRegistry uses a namespace to directory separator approach to resolve the correct path.
165-   Calling ``AnnotationRegistry#registerLoader($callable)`` to register an autoloader callback. The callback accepts the
166    class as first and only parameter and has to return true if the corresponding file was found and included.
167
168.. note::
169
170    Loaders have to fail silently, if a class is not found even if it matches for example the namespace prefix of that loader.
171    Never is a loader to throw a warning or exception if the loading failed otherwise parsing doc block annotations will become
172    a huge pain.
173
174A sample loader callback could look like:
175
176.. code-block:: php
177
178    use Doctrine\Common\Annotations\AnnotationRegistry;
179    use Symfony\Component\ClassLoader\UniversalClassLoader;
180
181    AnnotationRegistry::registerLoader(function($class) {
182        $file = str_replace("\\", DIRECTORY_SEPARATOR, $class) . ".php";
183
184        if (file_exists("/my/base/path/" . $file)) {
185            // file exists makes sure that the loader fails silently
186            require "/my/base/path/" . $file;
187        }
188    });
189
190    $loader = new UniversalClassLoader();
191    AnnotationRegistry::registerLoader(array($loader, "loadClass"));
192
193
194Ignoring missing exceptions
195~~~~~~~~~~~~~~~~~~~~~~~~~~~
196
197By default an exception is thrown from the AnnotationReader if an annotation was found that:
198
199- Is not part of the blacklist of ignored "documentation annotations".
200- Was not imported through a use statement
201- Is not a fully qualified class that exists
202
203You can disable this behavior for specific names if your docblocks do not follow strict requirements:
204
205.. code-block:: php
206
207    $reader = new \Doctrine\Common\Annotations\AnnotationReader();
208    AnnotationReader::addGlobalIgnoredName('foo');
209
210PHP Imports
211~~~~~~~~~~~
212
213By default the annotation reader parses the use-statement of a php file to gain access to the import rules
214and register them for the annotation processing. Only if you are using PHP Imports you can validate the correct
215usage of annotations and throw exceptions if you misspelled an annotation. This mechanism is enabled by default.
216
217To ease the upgrade path, we still allow you to disable this mechanism. Note however that we will remove this
218in future versions:
219
220.. code-block:: php
221
222    $reader = new \Doctrine\Common\Annotations\AnnotationReader();
223    $reader->setEnabledPhpImports(false);
224