annotations.rst 10 KB

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