jmacedo/php-simple-annotation

Yet another PHP annotation implementation. A simple and direct implementation without any complexity.

2.0.1 2020-08-17 19:58 UTC

This package is auto-updated.

Last update: 2024-05-18 12:27:33 UTC


README

Latest Stable Version Total Downloads Latest Unstable Version License Build Status Scrutinizer Code Quality Code Coverage Code Intelligence Status

Yet another PHP annotation implementation. A simple and direct implementation without any complexity.

Requirements

  • PHP >= 7.4.*
  • ext-json
  • ext-mbstring

Installation

composer require jmacedo/php-simple-annotation

Usage

Consider the following class for the purpose of this documentation:

namespace Examples;

/**
 * Class MyAnnotatedClass
 *
 * @package Examples
 * @metadata {"PI": 3.14, "name": "The name", "genre": "The genre", "age": "The age"}
 * @PI 3.14
 * @list ['item 1', 'item 2', 'item 3']
 * @uselessClass true
 */
class MyAnnotatedClass
{
    /**
     * @value 3.14
     * @var double
     * @description Math constant
     */
    const PI = 3.14;

    /** @var string */
    public $name;

    /** @var string */
    protected $genre;

    /** @var int */
    private $age;

    /**
     * MyAnnotatedClass constructor.
     *
     * @param string $name
     * @param string $genre
     * @param int $age
     */
    public function __construct(string $name = '', string $genre = '', int $age = 0)
    {
        $this->name = $name;
        $this->genre = $genre;
        $this->age = $age;
    }

    /**
     * A method that says "Hello!".
     *
     * @methodName sayHello
     * @emptyAnnotation
     */
    public function sayHello()
    {
        echo 'Hello!';
    }
}

You can instantiate the Annotation class in two ways:

  • First:
use SimpleAnnotation\Annotation;
use Examples\MyAnnotatedClass;

$annotation = new Annotation(MyAnnotatedClass::class);
// Or
$annotation = new Annotation('Examples\MyAnnotatedClass');
  • Second:
use SimpleAnnotation\Annotation;
use Examples\MyAnnotatedClass;

$myAnnotatedClass = new MyAnnotatedClass();
$annotation = new Annotation($myAnnotatedClass);

To get class annotations:

$classAnnotations = $annotation->getClassAnnotations();

var_dump($classAnnotations);
object(SimpleAnnotation\AnnotationParsed)[6]
  private 'properties' => 
    array (size=6)
      'package' => string 'Examples' (length=8)
      'metadata' => 
        object(stdClass)[5]
          public 'PI' => float 3.14
          public 'name' => string 'The name' (length=8)
          public 'genre' => string 'The genre' (length=9)
          public 'age' => string 'The age' (length=7)
      'PI' => float 3.14
      'list' => 
        array (size=3)
          0 => string 'item 1' (length=6)
          1 => string 'item 2' (length=6)
          2 => string 'item 3' (length=6)
      'uselessClass' => boolean true
      'anotherStringValue' => string 'my string' (length=9)

The AnnotationParsed class implements __get and __set magic methods, so to access the annotations keys, just access the properties:

echo "PI value is:" . $classAnnotations->PI;
echo "PI is also available in: " . $classAnnotations->metadata->PI;

To get methods annotations:

$methodsAnnotations = $annotation->getMethodsAnnotations();

var_dump($methodsAnnotations);
array (size=2)
  '__construct' => 
    object(SimpleAnnotation\AnnotationParsed)[9]
      private 'properties' => 
        array (size=1)
          'param' => 
            array (size=3)
              0 => string 'string $name' (length=12)
              1 => string 'string $genre' (length=13)
              2 => string 'int $age' (length=8)
  'sayHello' => 
    object(SimpleAnnotation\AnnotationParsed)[10]
      private 'properties' => 
        array (size=2)
          'methodName' => string 'sayHello' (length=8)
          'emptyAnnotation' => boolean true

For a specific method:

$sayHelloAnnotations = $annotation->getMethodAnnotations('sayHello');

var_dump($sayHelloAnnotations);
object(SimpleAnnotation\AnnotationParsed)[11]
  private 'properties' => 
    array (size=2)
      'methodName' => string 'sayHello' (length=8)
      'emptyAnnotation' => boolean true

To get properties annotations:

$propertiesAnnotations = $annotation->getPropertiesAnnotations();

var_dump($propertiesAnnotations);
array (size=3)
  'name' => 
    object(SimpleAnnotation\AnnotationParsed)[12]
      private 'properties' => 
        array (size=1)
          'var' => string 'string' (length=6)
  'genre' => 
    object(SimpleAnnotation\AnnotationParsed)[13]
      private 'properties' => 
        array (size=1)
          'var' => string 'string' (length=6)
  'age' => 
    object(SimpleAnnotation\AnnotationParsed)[14]
      private 'properties' => 
        array (size=1)
          'var' => string 'int' (length=3)

For a specific property:

$nameAnnotations = $annotation->getPropertyAnnotations('name');

var_dump($nameAnnotations);
object(SimpleAnnotation\AnnotationParsed)[7]
  private 'properties' => 
    array (size=1)
      'var' => string 'string' (length=6)

And that's it

Just instantiate the SimpleAnnotation\Annotation class, call the methods and use the annotations values of the class you want to.

Cache

By default, there is no cache in use, but there is a cache handler implemented that can be used, if you want.

This cache handler uses a file approach and had good results in some simple benchmarks I have made:

$annotation = new Annotation(MyAnnotatedClass::class);
$start = microtime(true);
for ($i = 0; $i < $numberOfTimes; $i++) {
    $classAnnotation = $annotation->getClassAnnotations();
    $methodsAnnotations = $annotation->getMethodsAnnotations();
    $propertiesAnnotations = $annotation->getPropertiesAnnotations();
}
$end = microtime(true);
echo 'Time elapsed without cache: ' . ($end - $start) . "\n";

$cache = new SimpleAnnotation\Concerns\Cache\FileCache(__DIR__ . DIRECTORY_SEPARATOR . 'test.txt');
$annotation2 = new Annotation(MyAnnotatedClass::class, $cache);
$start2 = microtime(true);
for ($i = 0; $i < $numberOfTimes; $i++) {
    $classAnnotation = $annotation2->getClassAnnotations();
    $methodsAnnotations = $annotation2->getMethodsAnnotations();
    $propertiesAnnotations = $annotation2->getPropertiesAnnotations();
}
$end2 = microtime(true);
echo 'Time elapsed with cache: ' . ($end2 - $start2) . "\n";

For $numberOfTimes=100:

Time elapsed without cache: 0.011738777160645
Time elapsed with cache: 0.00053501129150391

For $numberOfTimes=10000:

Time elapsed without cache: 1.1012320518494
Time elapsed with cache: 0.055597066879272

For $numberOfTimes=100000:

Time elapsed without cache: 11.216305971146
Time elapsed with cache: 0.53948402404785

The concrete implementation of \SimpleAnnotation\Concerns\ParsedAnnotation must implement the interface \JsonSerializable to the SimpleAnnotation\Concerns\Cache\FileCache implementation work well.

If you use the default implementation of this library, you don't have to worry about it.