[doc-en] master: Document attribute behavior for class loading and errors (#6043)

From: Date: Sun, 08 Nov 2026 16:47:46 +0000
Subject: [doc-en] master: Document attribute behavior for class loading and errors (#6043)
Groups: php.doc.cvs 
Request: Send a blank email to doc-cvs+get-23922@lists.php.net to get a copy of this message
Author: Jérôme Tamarelle (GromNaN) Committer: GitHub (web-flow) Pusher: lacatoire Date: 2026-11-08T17:47:43+01:00 Commit: https://github.com/php/doc-en/commit/c89ca8abb0d92a3ba4c2fb9720f61d47f07515d1 Raw diff: https://github.com/php/doc-en/commit/c89ca8abb0d92a3ba4c2fb9720f61d47f07515d1.diff Document attribute behavior for class loading and errors (#6043) * Document attribute class loading with IS_INSTANCEOF When IS_INSTANCEOF is used, PHP autoloads the filter class and every attribute class on the element, and propagates errors thrown while loading them. * Document ReflectionAttribute::getArguments() The arguments are constant expressions evaluated when the method is called. Evaluation can autoload classes, errors are propagated, and positional and named arguments use different array keys. * Document ReflectionAttribute::getName() The name is resolved when the attribute is declared and returned as a fully qualified class name without a leading backslash. The attribute class does not need to exist. * Document ReflectionAttribute::newInstance() errors Cover a class that cannot be loaded, a class without the #[Attribute] marker, a repeated attribute without IS_REPEATABLE, an invalid target, and errors thrown by the constructor. * Document that getAttributes() does not autoload without IS_INSTANCEOF The name filter is a case-insensitive string comparison against the fully qualified class name, and the class is not checked for existence. * Clarify attribute name resolution Only attribute names are resolved to classes. The arguments are evaluated and passed to the constructor when an instance is requested. * Document attribute inheritance and promoted parameters Class attributes are not inherited, while attributes on inherited members are returned when the member is reflected. An attribute on a promoted constructor parameter is returned on both the parameter and the property. * Update language/attributes.xml --------- Co-authored-by: Louis-Arnaud <la.catoire@gmail.com> Changed paths: M language-snippets.ent M language/attributes.xml M reference/reflection/reflectionattribute.xml M reference/reflection/reflectionattribute/getarguments.xml M reference/reflection/reflectionattribute/getname.xml M reference/reflection/reflectionattribute/newinstance.xml Diff: diff --git a/language-snippets.ent b/language-snippets.ent index 7cf8ff649fa6..1693ca100919 100644 --- a/language-snippets.ent +++ b/language-snippets.ent @@ -2570,9 +2570,34 @@ to be references, then they must be references in the passed argument list.'> Filter the results to include only <classname>ReflectionAttribute</classname> instances for attributes matching this class name. </simpara> + <simpara> + Unless <constant>ReflectionAttribute::IS_INSTANCEOF</constant> is passed in + <parameter>flags</parameter>, no attribute class is autoloaded. The name is + compared case-insensitively with the fully qualified class name of each + attribute, and the class is not checked for existence. + </simpara> </listitem> </varlistentry>'> +<!ENTITY reflection.getattributes.is-instanceof.note '<note xmlns="http://docbook.org/ns/docbook"> + <simpara> + When <constant>ReflectionAttribute::IS_INSTANCEOF</constant> is used, the + class name specified by <parameter>name</parameter> is autoloaded before + any attribute is examined. If it cannot be loaded, the error or exception + thrown while loading it is propagated. If the class does not exist, an + <exceptionname>Error</exceptionname> is thrown. + </simpara> + <simpara> + PHP also autoloads the class of every attribute declared on the element + being reflected, including attributes that do not match the filter, because + it uses <literal>instanceof</literal> to filter them. An attribute whose class + does not exist is ignored. If an error or exception is thrown while loading + an attribute class, for example because a parent class is missing or its + file contains a syntax error, it is propagated, even if the attribute does + not match the filter. + </simpara> +</note>'> + <!ENTITY reflection.getattributes.param.flags '<varlistentry xmlns="http://docbook.org/ns/docbook"> <term><parameter>flags</parameter></term> <listitem> @@ -2588,6 +2613,7 @@ to be references, then they must be references in the passed argument list.'> The only other option available, is to use <constant>ReflectionAttribute::IS_INSTANCEOF</constant>, which will instead use <literal>instanceof</literal> for filtering. </simpara> + &reflection.getattributes.is-instanceof.note; </listitem> </varlistentry>'> diff --git a/language/attributes.xml b/language/attributes.xml index 81f1796c71b3..7b294b72b9f1 100644 --- a/language/attributes.xml +++ b/language/attributes.xml @@ -111,12 +111,12 @@ executeAction($copyAction); expressions. Both positional and named argument syntax are supported. </para> - <para> - Attribute names and their arguments are resolved to a class, and the arguments - are passed to its constructor when an instance of the attribute is requested + <simpara> + An attribute name is resolved to a class, and the arguments are evaluated and + passed to the constructor when an instance of the attribute is requested through the Reflection API. Therefore, it is recommended to introduce a class for each attribute. - </para> + </simpara> <example> <title>Attribute Syntax</title> @@ -188,6 +188,22 @@ class AnotherThing occurs at that point. </para> + <simpara> + Class attributes are not inherited. Reflecting a subclass does not return + attributes declared on its parent class, its interfaces, or its traits. + Attributes on a property, method, or class constant are returned when that + member is reflected, including inherited members and trait members. + </simpara> + + <simpara> + An attribute declared on a + <link linkend="language.oop5.decon.constructor.promotion">promoted constructor + parameter</link> is returned by <function>getAttributes</function> on both the + <classname>ReflectionParameter</classname> and the corresponding + <classname>ReflectionProperty</classname>. An internal attribute that + cannot target one of them is only returned on the other. + </simpara> + <example> <title>Reading Attributes using Reflection API</title> diff --git a/reference/reflection/reflectionattribute.xml b/reference/reflection/reflectionattribute.xml index f938d74fd9f7..ee0d26170fcb 100644 --- a/reference/reflection/reflectionattribute.xml +++ b/reference/reflection/reflectionattribute.xml @@ -91,6 +91,7 @@ the given class or a subclass of it, or implements the given interface, instead of only matching the exact class name. </simpara> + &reflection.getattributes.is-instanceof.note; </listitem> </varlistentry> diff --git a/reference/reflection/reflectionattribute/getarguments.xml b/reference/reflection/reflectionattribute/getarguments.xml index c10999b3965d..9d2c3732f8b6 100644 --- a/reference/reflection/reflectionattribute/getarguments.xml +++ b/reference/reflection/reflectionattribute/getarguments.xml @@ -3,7 +3,7 @@ <refentry xml:id="reflectionattribute.getarguments" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink"> <refnamediv> <refname>ReflectionAttribute::getArguments</refname> - <refpurpose>Gets arguments passed to attribute</refpurpose> + <refpurpose>Gets the arguments passed to the attribute</refpurpose> </refnamediv> <refsect1 role="description"> @@ -12,9 +12,17 @@ <modifier>public</modifier> <type>array</type><methodname>ReflectionAttribute::getArguments</methodname> <void/> </methodsynopsis> - <para> - Gets arguments passed to attribute. - </para> + <simpara> + Gets the arguments passed to the attribute. + </simpara> + <simpara> + The arguments are constant expressions and are evaluated when this method + is called. Evaluating an argument may autoload a class referenced by the + expression, for example through a class constant, an enum case, or an + object created with <literal>new</literal>. Reading the arguments does not + require the attribute class to exist or validate them against the + constructor. + </simpara> </refsect1> @@ -25,9 +33,19 @@ <refsect1 role="returnvalues"> &reftitle.returnvalues; - <para> - The arguments passed to attribute. - </para> + <simpara> + The arguments passed to the attribute, with integer keys for positional + arguments and string keys for named arguments. + </simpara> + </refsect1> + + <refsect1 role="errors"> + &reftitle.errors; + <simpara> + If evaluating an argument throws an error or exception, it is propagated. + This can happen when the argument references a class that cannot be loaded, + or a constant that is not defined. + </simpara> </refsect1> </refentry> diff --git a/reference/reflection/reflectionattribute/getname.xml b/reference/reflection/reflectionattribute/getname.xml index df4e9ae018ff..904aa4c7c2a0 100644 --- a/reference/reflection/reflectionattribute/getname.xml +++ b/reference/reflection/reflectionattribute/getname.xml @@ -3,7 +3,7 @@ <refentry xml:id="reflectionattribute.getname" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink"> <refnamediv> <refname>ReflectionAttribute::getName</refname> - <refpurpose>Gets attribute name</refpurpose> + <refpurpose>Gets the name of the attribute</refpurpose> </refnamediv> <refsect1 role="description"> @@ -12,9 +12,15 @@ <modifier>public</modifier> <type>string</type><methodname>ReflectionAttribute::getName</methodname> <void/> </methodsynopsis> - <para> - Gets the attributes name. - </para> + <simpara> + Gets the name of the attribute. + </simpara> + <simpara> + The name is resolved when the attribute is declared, using the imports and + namespace in effect at that point. The returned name is the fully qualified + class name of the attribute, without a leading backslash. The attribute class + does not need to exist for the name to be returned. + </simpara> </refsect1> diff --git a/reference/reflection/reflectionattribute/newinstance.xml b/reference/reflection/reflectionattribute/newinstance.xml index 3af067b1d399..f57f2af779d3 100644 --- a/reference/reflection/reflectionattribute/newinstance.xml +++ b/reference/reflection/reflectionattribute/newinstance.xml @@ -31,12 +31,22 @@ <refsect1 role="errors"> &reftitle.errors; + <simpara> + Throws an <exceptionname>Error</exceptionname> if the attribute class cannot + be loaded, or if the class is not declared with + <literal>#[Attribute]</literal>. + </simpara> <simpara> Throws an <exceptionname>Error</exceptionname> if the attribute is applied - to a target it cannot be used on. For internal attributes, this happens - here instead of at compile time when the declaration uses + to a target it cannot be used on, or if it is applied more than once and is + not declared with <constant>Attribute::IS_REPEATABLE</constant>. For + internal attributes, target validation happens here instead of at compile + time when the declaration uses <link linkend="class.delayedtargetvalidation">#[\DelayedTargetValidation]</link>. </simpara> + <simpara> + Errors and exceptions thrown by the attribute constructor are propagated. + </simpara> </refsect1> <refsect1 role="changelog">

« previous php.doc.cvs (#23922) next »