====== PHP RFC: Annotations 2.0 ====== * Version: 0.1 * Date: 2019-02-05 * Author: MichaƂ Brzuchalski * Status: Obsolete * First Published at: http://wiki.php.net/rfc/annotations_v2 ===== Introduction ===== Annotation is a form of syntactic metadata that can be added to source code. Annotations can be embedded in and read using reflection mechanism. Annotations are known from Java or so-called Attributes in C# and can be retained by VM at run-time and read via reflection. Annotations can be placed in classes, methods, properties and functions. PHP offers only a single form of such metadata - doc-comments. In userland, there exist some annotation reader libraries like [[https://www.doctrine-project.org/projects/annotations.html|Doctrine Annotations]] which is widely used for eg. to express object-relational mapping metadata. ===== Proposal ===== Proposal of this RFC is to provide annotations with ''@'' prefix in the place the're declared like: [@AnnotationName("value", true)] [SimpleAnnotation] class Foo {} Which can be used to annotate classes, properties, methods and functions. They look similar to new object instantiation but there is a wide range of inhibitions. Annotations can have named and/or unnamed parameters, they're name can be imported using ''use'' statement same way as class names but the place they're declared must use ''@'' prefix. The same as instantiating new object parenthesis is not obligatory if there are no parameters to pass. Unnamed parameters must exist at the beginning of parameters list and they must meet constructor parameter requirements. use ORM\Entity; use ORM\Id; use ORM\Column; [@Entity] [@Table("foo")] class Foo { [@Id] [@Column("id", "uuid")] private $id; } use MVC\Route; class FooController { [@Route("/api/foo", ["POST"], "foo_create")] public function create(Request $request): Response { // specific implementation } } ==== Built-in annotations ==== First, there are several that inform compilation: === [Compiled] === Function scoped annotation indicating if a function should be JIT compiled. === [SupressWarnings] === Function or statement scoped annotation indicating if error supression should be applied. ==== Meta-Annotations ==== Next, meta-annotations are annotations that can be applied to other annotations. For example, these meta-annotations are used for annotation configuration: === [@Annotation] === Annotation classes have to contain a ''@Annotation''. [@Annotation] class MyAnnotation { // some code } === [@Target] === ''@Target'' annotation indicates the kinds of the class element or a function which an annotation type is applicable. Then you could define one or more targets: * ''CLASS'' allowed before the class declaration * ''PROPERTY'' allowed before class property declaration * ''METHOD'' allowed before the class method declaration * ''FUNCTION'' allowed before the function declaration * ''ALL'' allowed in all cases, also default value * ''ANNOTATION'' allowed before annotation class declaration === [@Repeatable] === ''@Repeatable'' annotation indicates the annotation may be repeated multiple times when annotating. === [@Inherited] === ''@Inherited'' annotation can be used as meta-annotation on the other user-defined annotation classes. When such user-defined annotations are used on superclass, they are automatically inherited to subclasses. [@Annotation] class MyAnnotation {} [@Annotation] [@Inherited] class MyInheritedAnnotation {} [@MyAnnotation] [@MyInheritedAnnotation] class Foo {} class Bar extends Foo {} $refl = new ReflectionClass(Bar::class); $classAnnotations = $refl->getAnnotations(); // will include @MyInheritedAnnotation only ==== Custom annotations ==== Annotation type declarations are similar to normal class declarations. Each property declaration defines an element of the annotation type. Property types are restricted to primitives or another annotation type. Declaring custom annotations: namespace Example; [@Annotation] [@Target("class")] class MyAnnotation { [@Required] public string $myProperty; public array $myArrayProperty = []; public MyEmbededAnnotation $myEmbeded; } [@Annotation] [@Target(["class", "annotation"])] class MyEmbededAnnotation { } [@Annotation] [@Target("property")] class MyPropertyAnnotation { } @Annotation @Target("method") class MyMethodAnnotation { public string $value; public function __construct(string $value) { $this->value = $value; } } Using annotations use Example\MyAnnotation; use Example\MyEmbededAnnotation; [@MyAnnotation([ "myProperty" => "value", "myArrayProperty" = > [1, 3.14, true, "string", DIRECTORY_SEPARATOR], )] class Foo { [@MyPropertyAnnotation] private $property; [@MyMethodAnnotation("value")] public function bar() {} } Reading annotations $refl = new ReflectionClass(Foo::class); $classAnnotations = $refl->getAnnotations(); $propertyAnnotations = $refl->getProperty('property')->getAnnotations(); $methodAnnotations = $refl->getMethod('foo')->getAnnotations(); ===== Benefits ===== ==== Caching ==== Annotations are cached with source code in OPCache. Which is different than userland implementations which are stored cached outside of source code. This means they don't need invoking reread after cache invalidation. ==== Live with the code ==== Provided annotations live with the code so it's easy to enable/disable behaviour of components which use their metadata. For eg. ''@Route'' in MVC style application is metadata for controller method which is used by the routing component. Which means any changes to the controller implementation or it's routing metadata lives in one place and therefore can be for eg. commented out with the controller method. ==== IDE support ==== Provided annotations can be verified in IDE. The IDE can also provide auto-completion support or a sort of validation. ===== Critique ===== TBD ===== Backward Incompatible Changes ===== None. ===== Proposed PHP Version(s) ===== Proposed version is next PHP 7.x or PHP 8. ===== RFC Impact ===== ==== To SAPIs ==== None. ==== To Existing Extensions ==== None. ==== To Opcache ==== Probably yes. ===== Proposed Voting Choices ===== As this is a language change, a 2/3 majority is required. The vote is a straight Yes/No vote for accepting the RFC and merging the patch. ===== Patches and Tests ===== TBD. ===== Implementation ===== TBD. ===== References ===== TBD