annotations.rst 10 KB

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