-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Add Attributes based on attributes_v2 RFC. #5394
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
7bbc1db
to
6bf867a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks very good to me! It's flexible, a bit error prone (when forgetting imports), but extremely powerful.
It has potentially to finally kill docblocks, so that's a big 👍 from me :)
I'm missing nested attributes, but I think those could be added at a later point in time.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Please keep pull request discussions focused on implementation concerns. If you have general comments on an RFC, please make them on the PHP internals mailing list instead. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I apologize for offtopic, but is there a build in CI for Windows (https://windows.php.net/downloads/snaps/) with this PR or somewhere else? Or it makes sense to just wait for the merge? // I can’t compile it at the moment |
@SerafimArts latest CI build targeting windows is at https://ci.appveyor.com/project/php/php-src/builds/32298449 |
@Ocramius I meant binaries (artifacts) |
DescriptionIt seems that the validation of Test script<?php
declare(strict_types=1);
class Test {
public string $name;
public function __construct(string $name) {
$this->name = $name;
}
}
<<Test('test')>>
function test(): void {}
$attrs = (new ReflectionFunction('test'))->getAttributes();
echo $attrs[0]->newInstance()->name . PHP_EOL; Expected behavior
Actual behavior
|
@moliata validation only happens when you call |
I'm not sure if I correctly understand you but I am using |
@moliata ah sorry, weird that i didn't see that, i only saw the getAttributes line. |
@beberlei after doing some tests, it seems I can trigger the check only if the class has some kind of an attribute. For example, let's reuse the same test script I used in my bug report and add something like this: <?php
declare(strict_types=1);
// Adding any other than the <<PhpAttribute>> attribute to
// the class makes the check work and raises a fatal error
<<TestAttribute>>
class Test {
public string $name;
public function __construct(string $name) {
$this->name = $name;
}
}
<<Test('test')>>
function test(): void {}
$attrs = (new ReflectionFunction('test'))->getAttributes();
echo $attrs[0]->newInstance()->name . PHP_EOL; EDIT: fixed a TYPO |
|
||
echo "\n"; | ||
|
||
<<A1(self::FOO, C1::BAR)>> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a bit of ambiguity here when it comes to cases like this:
class Test {
public function method() {
return new <<A(self::class)>> class {};
}
}
Here self
could intuitively both refer to Test
and to the anonymous class.
More ambiguous cases are:
<<A(self::class)>>
trait T {}
In traits, self
generally refers to the using class, not the trait. It's not clear how this is supposed to work.
$fn = <<A(self::class)>> function() {};
$fn->bindTo(null, X::class);
In closures, self
refers to the bound scope of the closure. Again, it's not entirely obvious what the result is.
The behavior for these cases doesn't seem to be specified in the RFC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW I expect that just adding tests with current behavior will yield something that is pretty reasonable. Making the scope the scope of the entity on which the attribute is declared is necessary to make things like accessing private class constants work (which it probably should?) so there might not be much real choice here.
Zend/zend_attributes.h
Outdated
zend_class_entry *zend_ce_php_compiler_attribute; | ||
|
||
typedef void (*zend_attributes_internal_validator)(zval *attribute, int target); | ||
HashTable zend_attributes_internal_validators; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be part of compiler_globals or so.
Zend/zend_language_parser.y
Outdated
@@ -227,16 +227,16 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); | |||
/* Token used to force a parse error from the lexer */ | |||
%token T_ERROR | |||
|
|||
%type <ast> top_statement namespace_name name statement function_declaration_statement | |||
%type <ast> top_statement namespace_name name statement annotated_statement function_declaration_statement |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit of terminology mix here with annotations vs attributes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Naming things is hard... would attributed_statement
be a better fit? In addition there are annotated_class_statement
and annotated_parameter
that should also be renamed. Any suggestions as far as new names are concerned?
@kooldev done. |
@beberlei You need to adjust |
3bf85a2
to
4cc905c
Compare
@nikic We are ready for final review (hopefully :)) The only thing that is not adressed from your previous comments is moving internal_validators global HashTable to compiler_globals. |
<<Entity("imported class")>> | ||
<<ORM\Entity("imported namespace")>> | ||
<<\Doctrine\ORM\Mapping\Entity("absolute from namespace")>> | ||
<<\Entity("import absolute from global")>> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to be complete, there's also <<namespace\Entity()>>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant this one a bit more literally: namespace\Entity
, with the namespace
keyword is a namespace-relative name.
RETURN_THROWS(); | ||
} | ||
|
||
if (!zend_get_attribute_str(ce->attributes, ZEND_STRL("phpattribute"))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might want to add a ZEND_ACC_ATTRIBUTE flag instead of this check.
Open todos are:
|
@beberlei All builds failed due to use of typed properties in |
@kooldev You might want to not touch the zend_declare_property_ex and zend_declare_typed_property APIs at all, and instead add an additional lower-level zend_declare_attributed_property. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. Should be fine to land once CI green.
@nikic How about providing additional functions to do attribute registration and automatic HashTable initialization in a unified way? I implemented it that way in my attributes PR to avoid making changes to existing function signatures. I would like to add these function in ZEND_API zend_attribute *zend_add_class_attribute(zend_class_entry *ce, zend_string *name, uint32_t argc);
ZEND_API zend_attribute *zend_add_function_attribute(zend_function *func, zend_string *name, uint32_t argc);
ZEND_API zend_attribute *zend_add_parameter_attribute(zend_function *func, uint32_t offset, zend_string *name, uint32_t argc);
ZEND_API zend_attribute *zend_add_property_attribute(zend_class_entry *ce, zend_property_info *info, zend_string *name, uint32_t argc);
ZEND_API zend_attribute *zend_add_class_constant_attribute(zend_class_entry *ce, zend_class_constant *c, zend_string *name, uint32_t argc); And provide implementations like that in static void attribute_ptr_dtor(zval *v)
{
zend_attribute_free((zend_attribute *) Z_PTR_P(v), 1);
}
ZEND_API zend_attribute *zend_add_class_attribute(zend_class_entry *ce, zend_string *name, uint32_t argc)
{
zend_bool persistent = (ce->type == ZEND_INTERNAL_CLASS);
if (ce->attributes == NULL) {
ce->attributes = pemalloc(sizeof(HashTable), persistent);
zend_hash_init(ce->attributes, 8, NULL, attribute_ptr_dtor, persistent);
}
zend_attribute *attr = pemalloc(ZEND_ATTRIBUTE_SIZE(argc), persistent);
attr->name = zend_string_dup(name, persistent);
attr->lcname = zend_string_tolower_ex(attr->name, persistent);
attr->offset = 0;
attr->argc = argc;
zend_hash_next_index_insert_ptr(ce->attributes, attr);
return attr;
} |
@kooldev That looks reasonable to me. Will probably make the compiler code a bit uglier...
I'd instead assert that the input has the right persistence type. |
Co-authored-by: Martin Schröder <[email protected]>
20aceb5
to
8697ccf
Compare
Register Attributes Without Changes to ZEND APIs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LG assuming CI green.
CI is green, the Azure failure is unrelated. |
Merged in a7908c2 |
RFC: https://wiki.php.net/rfc/attributes_v2
References:
@:
: https://github.com/kooldev/php-src/pull/2Opcache\Jit
attribute: [Opcache] Add Opcache\Jit attribute example beberlei/php-src#7