rfc:attributes

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
rfc:attributes [2016/04/27 04:15] pierrickrfc:attributes [2017/09/22 13:28] (current) – external edit 127.0.0.1
Line 1: Line 1:
 ====== PHP RFC: Attributes ====== ====== PHP RFC: Attributes ======
-  * Version: 0.9+  * Version: 1.0
   * Date: 2016-04-21   * Date: 2016-04-21
   * Author: Dmitry Stogov, dmitry@zend.com   * Author: Dmitry Stogov, dmitry@zend.com
-  * Status: Under Discussion+  * Status: Declined
   * First Published at: http://wiki.php.net/rfc/attributes   * First Published at: http://wiki.php.net/rfc/attributes
  
Line 14: Line 14:
  
 ==== Attribute Syntax ==== ==== Attribute Syntax ====
-<nowiki>An attribute is a specially formatted text enclosed with "<<" and ">>". Attributes may be applied to functions, classes, interfaces, traits, methods, properties and class constants. In the same way as doc-comments, attributes must be placed before the corresponding definition, but it's possible to define more than one attribute one the same declaration.</nowiki>+<nowiki>An attribute is a specially formatted text enclosed with "<<" and ">>". Attributes may be applied to functions, classes, interfaces, traits, methods, properties and class constants. In the same way as doc-comments, attributes must be placed before the corresponding definition, but it's possible to define more than one attribute on the same declaration.</nowiki>
  
 <code php> <code php>
Line 22: Line 22:
 </code> </code>
  
-Each attribute definition construct may also define one or more named attribute, which may be used with no value, a single value or multiple values. Cf. the EBNF:+Each attribute definition construct may also define one or more named attribute, which may be used with no value, a single value or multiple values. See the EBNF grammar:
  
 <code> <code>
 <attribute> ::= "<<" <namespace-name> [ "(" <value> { "," <value> } ")" ] <attribute> ::= "<<" <namespace-name> [ "(" <value> { "," <value> } ")" ]
                 { "," <namespace-name> [ "(" <value> { "," <value> } ")" ] } ">>".                 { "," <namespace-name> [ "(" <value> { "," <value> } ")" ] } ">>".
-<name>      ::= STRING.+<namespace-name>      ::= STRING.
 <value>     ::= <php-constant> | <php-expression>. <value>     ::= <php-constant> | <php-expression>.
 </code> </code>
Line 67: Line 67:
 } }
 $r = new ReflectionFunction("foo"); $r = new ReflectionFunction("foo");
-$ast = ast\parse_code($r->getAttributes()["foo"][0]);+$ast = ast\parse_code($r->getAttributes()["test"][0]);
 </code> </code>
  
 ==== Reflection ==== ==== Reflection ====
  
-Reflection classes are extended with the getAttributes() method, and returns array of attributes.+Reflection classes are extended with the getAttributes() methods, and return array of attributes.
  
 <code php> <code php>
Line 182: Line 182:
 } }
 </code> </code>
 +
 +[[https://github.com/nikic/php-ast|php-ast]] is also going to be included into core PHP distribution, but this is a subject of another RFC.
  
 ==== Use Cases ==== ==== Use Cases ====
  
-With attributes it's extremely simple to mark some functions with some specific attribute and then perform checks and special handling in extensions.+With attributesit's extremely simple to mark some functions with some specific "flag" and then perform checks and special handling in extensions.
  
 <code php> <code php>
Line 199: Line 201:
 </code> </code>
  
-Attributes may be used to provide an annotation system similar to Doctrine, where each attribute is represented by an object of corresponding class that perform validation and other actions.+Attributes may be used as a base level for an annotation system similar to Doctrine, where each attribute is represented by an object of corresponding class that perform validation and other actions.
  
 <code php> <code php>
Line 266: Line 268:
 ==== Special Attributes ==== ==== Special Attributes ====
  
-Attribute names starting with "__" are reserved for internal purpose. Usage of unknown special attributes leads to compile-time error. Currently, no any special attributes are defined.+<nowiki>Attribute names starting with "__" are reserved for internal purpose. Usage of unknown special attributes leads to compile-time error. Currently, no any special attributes are defined.</nowiki>
  
-==== Criticism and Alternative Approach ====+==== Criticism and Alternative Approaches ==== 
 + 
 +=== Doc-comments ===
  
 Today we are using single doc-comments for any kind of meta-information, and many people don't see a benefit in the introduction of a special syntax. Everything may be grouped together and formatted using another special language. Today we are using single doc-comments for any kind of meta-information, and many people don't see a benefit in the introduction of a special syntax. Everything may be grouped together and formatted using another special language.
Line 309: Line 313:
  
 It might be possible to make PHP parse existing doc-comments and keep information as structured attributes, but we would need to invoke additional parser for each doc-comment; doc-comment may not conform to context-grammar and we have to decide what to do with grammar errors; finally this is going to be another language inside PHP. It might be possible to make PHP parse existing doc-comments and keep information as structured attributes, but we would need to invoke additional parser for each doc-comment; doc-comment may not conform to context-grammar and we have to decide what to do with grammar errors; finally this is going to be another language inside PHP.
 +
 +=== Full Featured Annotation System (like Doctrine) ===
 +
 +This RFC proposes only base PHP attribute functionality. It doesn't define how attributes are validated and used. The full-featured annotation systems may be implemented on top of the base. The following example shows how a real life doc-comment annotation taken from Drupal may be implemented, validated and constructed on top of PHP attributes.
 +
 +<code php>
 +/**
 +  * @Block(
 +  *   id = "system_branding_block",
 +  *   admin_label = @Translation("Site branding")
 +  * )
 +  */
 +
 +<<Drupal(@Block([
 +       "id" => "system_branding_block",
 +       "admin_label" => @Translation("Site branding")
 +]))>>
 +class PageTitleBlock {
 +
 +
 +function TranslateDrupalAttribute($value) {
 +  if ($value instanceof \ast\Node) {
 +    if ($value->kind == 264 && count($value->children) == 1) { // '@'
 +      $a = $value->children[0];
 +      if (is_string($a) && class_exists($a)) {
 +        $value = new $a;
 +      } else if ($a instanceof \ast\Node &&
 +                 $a->kind == 515 && // NAME(ARGS)
 +                 count($a->children) == 2 &&
 +                 is_string($a->children[0]) &&
 +                 class_exists($a->children[0]) &&
 +                 $a->children[1] instanceof \ast\Node &&
 +                 $a->children[1]->kind == 128 &&
 +                 count($a->children[1]->children) == 1) {
 +        $args = $a->children[1]->children[0];
 +        if ($args instanceof ast\Node && $args->kind == 130) {
 +          $obj = new $a->children[0];
 +          foreach ($args->children as $arg) {
 +            if ($arg instanceof ast\Node &&
 +                $arg->kind == 525 &&
 +                count($arg->children) == 2 && 
 +                is_string($arg->children[1])) {
 +              $name = $arg->children[1];
 +              $val = $arg->children[0];
 +              if ($val instanceof ast\Node) {
 +                $obj->{$name} = TranslateDrupalAttribute($val);
 +              } else {
 +                $obj->{$name} = $val;
 +              }
 +            } else {
 +              throw DrupalAnnotationError("...");
 +            }
 +          }
 +        } else {
 +          $name = $a->children[0];
 +          $obj = new $name($args);
 +        }
 +        $value = $obj;
 +      } else {
 +        throw DrupalAnnotationError("...");
 +      }
 +    } else {
 +      throw DrupalAnnotationError("...");
 +    }
 +  }
 +  return $value;
 +}
 +
 +function GetDrupalAnnotations($class_name) {
 +  $reflClass = new \ReflectionClass($class_name);
 +  $attrs = $reflClass->getAttributes();
 +  $ret = [];
 +  foreach ($attrs as $name => $values) {
 +    if ($name == "Drupal") {
 +      foreach ($values as &$value) {
 +        $ret[] = TranslateDrupalAttribute($value);
 +      }
 +    }
 +  }
 +  return $ret;
 +}
 +
 +class Block {}
 +class Translation {
 +  public $text;
 +  function __construct($text) {
 +    $this->text = $text;
 +  }
 +}
 +
 +var_dump(GetDrupalAnnotations("PageTitleBlock"));
 +</code>
 +
 +<code>
 +array(1) {
 +  [0]=>
 +  object(Block)#11 (2) {
 +    ["id"]=>
 +    string(21) "system_branding_block"
 +    ["admin_label"]=>
 +    object(Translation)#12 (1) {
 +      ["text"]=>
 +      string(13) "Site branding"
 +    }
 +  }
 +}
 +</code>
 +
 +=== '@' Prefix in Attribute Names ===
 +
 +'@' symbol may be used in attribute values (as part of PHP expressions) and reused by annotation system for special purpose, but attribute names can't be prefixed with '@' their selves. See the example above.
 +
 +=== Naming (attributes or annotations) ===
 +
 +Different programming languages use different terms for similar features. Some use annotation, some attributes. I prefer name "attributes" because it's used in Hack and makes less fragmentation. It also makes less confusion for external high-level annotation systems (Doctrine, etc).
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
Line 337: Line 456:
   * <del>For each defined attribute getArray() should return a numerically indexed array independently of number of associated values. For attributes without values it should return empty arrays.</del> [INCLUDED]   * <del>For each defined attribute getArray() should return a numerically indexed array independently of number of associated values. For attributes without values it should return empty arrays.</del> [INCLUDED]
   * <del>Attribute names might be namespace qualified e.g. <nowiki><<\Foo\Bar>></nowiki></del> [INCLUDED]   * <del>Attribute names might be namespace qualified e.g. <nowiki><<\Foo\Bar>></nowiki></del> [INCLUDED]
-  * It may be useful to optionally allow some extra special character e.g. <nowiki><<@\Foo\Bar>></nowiki>. This character won't have any special meaning for PHP itself, but higher layer may use this "@" as a flag of special meaning.+  * <del>It may be useful to optionally allow some extra special character e.g. <nowiki><<@\Foo\Bar>></nowiki>. This character won't have any special meaning for PHP itself, but higher layer may use this "@" as a flag of special meaning.</del> [ADDED into criticism section]
   * <del>May be we don't need special functionality for AST in attributes. We may store attribute as a simple strings and then get them through getAttributes() and call ast\parse_code() to get AST (if necessary). Both enabling and disabling native AST support make sense with their profs and cons.</del> [ADDITIONAL VOTING QUESTION]   * <del>May be we don't need special functionality for AST in attributes. We may store attribute as a simple strings and then get them through getAttributes() and call ast\parse_code() to get AST (if necessary). Both enabling and disabling native AST support make sense with their profs and cons.</del> [ADDITIONAL VOTING QUESTION]
 +  * <del>Naming: "Attributes" or "Annotation(s)"?</del> [ADDED into criticism section]
      
 ===== Proposed Voting Choices ===== ===== Proposed Voting Choices =====
-This RFC modifies the PHP language syntax and therefore requires a two-third majority of votes+The voting started on May 10th, 2016 and will close on May 24th, 2016. 
 + 
 +<doodle title="Accept PHP Attributes? (2/3+1 majority required)" auth="dmitry" voteType="single" closed="true"> 
 +   * Yes 
 +   * No 
 +</doodle> 
 + 
 +----
  
-In addition, allow <php-expression> as attribute values using transparent AST representation. (1/2 majority required)+<doodle title="What may be used as attribute value? (simple majority wins)" auth="dmitry" voteType="single" closed="true"> 
 +   * valid PHP expression (internally represented as AST) 
 +   * valid PHP constant (number or string) 
 +</doodle>
  
 ===== Patches and Tests ===== ===== Patches and Tests =====
Line 357: Line 487:
   * [[https://docs.hhvm.com/hack/attributes/introduction|Attributes in Hack]]   * [[https://docs.hhvm.com/hack/attributes/introduction|Attributes in Hack]]
   * [[https://en.wikipedia.org/wiki/Java_annotation|Java Annotation]]   * [[https://en.wikipedia.org/wiki/Java_annotation|Java Annotation]]
 +  * [[https://wiki.php.net/rfc/annotations|Class Metadata RFC]]
 +  * [[https://wiki.php.net/rfc/annotations-in-docblock|Annotations in DocBlock RFC]]
  
-===== Rejected Features ===== 
-Keep this updated with features that were discussed on the mail lists. 
rfc/attributes.1461730520.txt.gz · Last modified: 2017/09/22 13:28 (external edit)