Annotations Parser


    It is the first time that an annotations parser component is written in C for the PHP world. is a general purpose component that provides ease of parsing and caching annotations in PHP classes to be used in applications.

    Annotations are read from docblocks in classes, methods and properties. An annotation can be placed at any position in the docblock:

    An annotation has the following syntax:

    1. /**
    2. * @Annotation-Name
    3. * @Annotation-Name(param1, param2, ...)
    4. */

    Also, an annotation can be placed at any part of a docblock:

    1. <?php
    2. /**
    3. * This a property with a special feature
    4. *
    5. * @SpecialFeature
    6. *
    7. * More comments
    8. *
    9. * @AnotherSpecialFeature(true)
    10. */

    The parser is highly flexible, the following docblock is valid:

    1. <?php
    2. /**
    3. * This a property with a special feature @SpecialFeature({
    4. someParameter='the value', false
    5. }) More comments @AnotherSpecialFeature(true) @MoreAnnotations
    6. **/

    However, to make the code more maintainable and understandable it is recommended to place annotations at the end of the docblock:

    Factory

    1. <?php
    2. use Phalcon\Annotations\Adapter\Memory as MemoryAdapter;
    3. $reader = new MemoryAdapter();
    4. // .....

    However you can also utilize the factory method to achieve the same thing:

    1. <?php
    2. use Phalcon\Annotations\Factory;
    3. $options = [
    4. 'prefix' => 'annotations',
    5. 'lifetime' => '3600',
    6. 'adapter' => 'memory', // Load the Memory adapter
    7. ];
    8. $annotations = Factory::load($options);

    The Factory loader provides more flexibility when dealing with instantiating annotations adapters from configuration files.

    A reflector is implemented to easily get the annotations defined on a class using an object-oriented interface:

    1. <?php
    2. use Phalcon\Annotations\Adapter\Memory as MemoryAdapter;
    3. $reader = new MemoryAdapter();
    4. // Reflect the annotations in the class Example
    5. $reflector = $reader->get('Example');
    6. // Read the annotations in the class' docblock
    7. $annotations = $reflector->getClassAnnotations();
    8. // Traverse the annotations
    9. // Print the annotation name
    10. echo $annotation->getName(), PHP_EOL;
    11. // Print the number of arguments
    12. echo $annotation->numberArguments(), PHP_EOL;
    13. // Print the arguments
    14. print_r($annotation->getArguments());
    15. }

    The annotation reading process is very fast, however, for performance reasons it is recommended to store the parsed annotations using an adapter. Adapters cache the processed annotations avoiding the need of parse the annotations again and again.

    was used in the above example. This adapter only caches the annotations while the request is running and for this reason the adapter is more suitable for development. There are other adapters to swap out when the application is in production stage.

    Types of Annotations

    Annotations may have parameters or not. A parameter could be a simple literal (strings, number, boolean, null), an array, a hashed list or other annotation:

    Let’s pretend we’ve created the following controller and you want to create a plugin that automatically starts the cache if the last action executed is marked as cacheable. First off all, we register a plugin in the Dispatcher service to be notified when a route is executed:

    1. <?php
    2. use Phalcon\Mvc\Dispatcher as MvcDispatcher;
    3. use Phalcon\Events\Manager as EventsManager;
    4. $di['dispatcher'] = function () {
    5. $eventsManager = new EventsManager();
    6. // Attach the plugin to 'dispatch' events
    7. $eventsManager->attach(
    8. 'dispatch',
    9. new CacheEnablerPlugin()
    10. );
    11. $dispatcher = new MvcDispatcher();
    12. $dispatcher->setEventsManager($eventsManager);
    13. return $dispatcher;
    14. };

    CacheEnablerPlugin is a plugin that intercepts every action executed in the dispatcher enabling the cache if needed:

    1. <?php
    2. use Phalcon\Events\Event;
    3. use Phalcon\Mvc\Dispatcher;
    4. use Phalcon\Plugin;
    5. /**
    6. * Enables the cache for a view if the latest
    7. * executed action has the annotation @Cache
    8. */
    9. class CacheEnablerPlugin extends Plugin
    10. {
    11. /**
    12. * This event is executed before every route is executed in the dispatcher
    13. */
    14. public function beforeExecuteRoute(Event $event, Dispatcher $dispatcher)
    15. {
    16. // Parse the annotations in the method currently executed
    17. $annotations = $this->annotations->getMethod(
    18. $dispatcher->getControllerClass(),
    19. );
    20. // Return normally if the method doesn't have a 'Cache' annotation
    21. if (!$annotations->has('Cache')) {
    22. return true;
    23. }
    24. // The method has the annotation 'Cache'
    25. $annotation = $annotations->get('Cache');
    26. // Get the lifetime
    27. $lifetime = $annotation->getNamedParameter('lifetime');
    28. $options = [
    29. 'lifetime' => $lifetime,
    30. ];
    31. // Check if there is a user defined cache key
    32. if ($annotation->hasNamedParameter('key')) {
    33. $options['key'] = $annotation->getNamedParameter('key');
    34. }
    35. // Enable the cache for the current method
    36. $this->view->cache($options);
    37. }
    38. }

    Now, we can use the annotation in a controller:

    1. <?php
    2. use Phalcon\Mvc\Controller;
    3. class NewsController extends Controller
    4. {
    5. public function indexAction()
    6. {
    7. }
    8. /**
    9. * This is a comment
    10. *
    11. * @Cache(lifetime=86400)
    12. */
    13. public function showAllAction()
    14. {
    15. $this->view->article = Articles::find();
    16. }
    17. /**
    18. * This is a comment
    19. *
    20. * @Cache(key='my-key', lifetime=86400)
    21. */
    22. public function showAction($slug)
    23. {
    24. $this->view->article = Articles::findFirstByTitle($slug);
    25. }

    You can use annotations to tell the ACL which controllers belong to the administrative areas:

    Annotations Adapters

    This component makes use of adapters to cache or no cache the parsed and processed annotations thus improving the performance or providing facilities to development/testing:

    The Phalcon\Annotations\AdapterInterface interface must be implemented in order to create your own annotations adapters or extend the existing ones.