-
-
Save mindplay-dk/ebd5e4f7da51da3c4e56232adef41b46 to your computer and use it in GitHub Desktop.
<?php | |
class Route | |
{ | |
public $pattern; | |
public $method; | |
public function __construct($pattern, $method) | |
{ | |
$this->pattern = $pattern; | |
$this->method = $method; | |
} | |
public static function get($pattern) { | |
return new self($pattern, "GET"); | |
} | |
} | |
class Filter | |
{ | |
public $name; | |
public function __construct($name) | |
{ | |
$this->name = $name; | |
} | |
} | |
class UserController | |
{ | |
<< Route::get('^/users/\w+$') >> | |
<< new Filter("membership") >> | |
public function showProfile() | |
{ | |
// ... | |
} | |
} | |
$method = new ReflectionMethod(UserController::class, "showProfile"); | |
var_dump($method->getAnnotations()); // => array(0 => {Route}, 1 => {Filter}) | |
var_dump($method->getAnnotations(Filter::class)); // => array(0 => {Filter}) |
<?php | |
// C# style annotation rules can be implemented entirely in userland | |
class Usage | |
{ | |
public $where; | |
public function __construct($where) { | |
$this->where = $where; | |
} | |
public static $classes = new Usage("classes"); | |
public static $methods = new Usage("methods"); | |
public static $properties = new Usage("properties"); | |
public static $multiple = new Usage("multiple"); | |
public static $inherit = new Usage("inherit"); | |
} | |
class Annotations | |
{ | |
public function getFromClass($class) { | |
// get class reflection | |
// collect annotations from class and recursively from parents | |
// for each annotation | |
// get Usage annotations from the annotation-class | |
// validate usage | |
// validate cardinality (single or multiple) | |
// apply inheritance logic (filter overridden annotations) | |
// return annotations | |
} | |
public function getFromMethod($class, $method) { | |
// ... | |
} | |
public function getFromProperty($class, $property) { | |
// ... | |
} | |
} |
Why this ugly syntax again? I mean "<< >>"? Is it really important to keep compatibilty with HHVM?
There's a lot of languages with native annotations built in such as java, python... Does it look any worse?
@ORM\Column(name="id", type="integer")
@ORM\GeneratedValue(strategy="AUTO")
@ORM\Id()
private $id;
Than this one:
<< new ORM\Column("id", "integer") >>
<< new ORM\GeneratedValue("AUTO") >>
<< new ORM\Id() >>
private $id;
Aren't you (hhvm team in the reality) inventing a wheel inventing new syntax for annotations?
For anyone who hate this syntax, solve this problem and then you can continue argue:
$fn = <<new Annotation()>> function () { /** ... */ };
@fesor there is simple solution for this problem - abandon @
silent operator for error supression.
It's a huge BC break but IMHO using error supression provides more complexity and using exceptions instead would provide clean way for handling errors there is try/catch for that - that would be awesome change in my opinion.
The STFU operator @
should be considered as bad practice, IMHO those usages should be replaced with valid exception handling. Removing STFU could be marked as deprecated and then some users would reflect and refactor for right way of exception handling but also part of users would just blow deprecation errors by adding additional STFU which might cause unproper usage of $ret = @@foo();
.
Maybe I'm just thinking loud and that's all.
I agree <<>> is ugly, I much prefer @ or @@
using @ to suppress errors is super bad in my opinion, I last used that in 2002 when I was young and stupid, I now handle that error instead of sweep it under the rug.
I'm honestly ok with busting BC as if someone is going to upgrade to a new version of PHP, they should expect legacy code to ge a little bruised up in the process and its more of knowing whats going to break before upgrading your system. especially if this makes the cut for PHP 8, I'd like to see a lot of things get disrupted to clean up the API finally.
The syntax is simply
<< {expression} >>
, e.g. any valid php expression that can be evaluated from that context, and the resulting value is simply appended to a list of annotations for the following source code member.This also lets you annotate with simple values, if desired, e.g.
<< 123 >>
or<< "Hello World" >>
or<< [1, 2, 3] >>
etc.Or even complex expressions, e.g.
<< array_map(function ($v) { return $v+1; }, [1, 2, 3]) >>
Or annotating with reusable object instances, e.g.
<< HttpMethod::$GET >>
or flyweight objects via methods, etc.Or (if you must) attach functionality as delegates, e.g.
<< new Validation(function ($a, $b) { return $a < $b; }) >>
. (this is more verbose than the examples in the attributes RFC using dynamic expressions, but it's explicit - the main purpose of annotations is to provide meta-data, not to provide functionality; and the syntax will look nicer/shorter if/when php gets a shorter syntax for anonymous functions.)If somebody wants to decouple implementations from data and interpret the data at run-time, they can do that, e.g. using
<< ["name" => ["value1", "value2"]] >>
which accomplishes the same thing proposed by the attributes RFC - but this has the advantage of relying more directly on php syntax, e.g. with any useful features that might get added to php syntax in the future being available as constructs for creation of annotations as well.In other words, this leverages the entire language, rather than building a small feature on top of the language.
Regarding inheritance and rules about overriding annotations, that does not have to be a thing -
getAnnotations()
can return the annotations of the member you accessed, and you can go to it's parent class and collect more annotations if needed. If you accept only one instance of a specific type of annotation, you can stop walking upwards when you find the instance you're looking for - the rules can be your rules, in your code, logic and rules do not need to be baked into the language. Other soft rules, like the number of annotations allowed of the same type, or the type of members permitted by an annotation, do not need to be enforced or baked-in either. (it's difficult to conceive in advance of all the constraints, rules and conditions somebody might need, and probably better to leave this in userland.)The
getAnnotations()
method takes an optional argument with a class/interface-name, which is compared withinstanceof
against the attributes in the set, maybe with special handling for simple types, e.g.int
,float
,bool
, etc.