[DOC-CVS] [phd] master: Add support for constants in attribute formatting (#255)
[email protected] (Jordi Kroon via GitHub) Mon, 22 Jun 2026 16:40:29 +0000
| Newsgroups | php.doc.cvs |
|---|---|
| Message-ID | <[email protected]> |
Author: Jordi Kroon (jordikroon)
Committer: GitHub (web-flow)
Pusher: jordikroon
Date: 2026-06-22T18:40:25+02:00
Commit: https://github.com/php/phd/commit/88859c86dcbc74d7339a0c10d1bc903fcfeeb12f
Raw diff: https://github.com/php/phd/commit/88859c86dcbc74d7339a0c10d1bc903fcfeeb12f.diff
Add support for constants in attribute formatting (#255)
* add support for constants in attribute formatting
Changed paths:
A tests/package/generic/attribute_formatting_004.phpt
A tests/package/generic/data/attribute_formatting_004.xml
M phpdotnet/phd/Package/Generic/XHTML.php
M tests/package/generic/attribute_formatting_001.phpt
M tests/package/generic/attribute_formatting_002.phpt
M tests/package/generic/attribute_formatting_003.phpt
Diff:
diff --git a/phpdotnet/phd/Package/Generic/XHTML.php b/phpdotnet/phd/Package/Generic/XHTML.php
index b5ab2b90..1491fb1a 100644
--- a/phpdotnet/phd/Package/Generic/XHTML.php
+++ b/phpdotnet/phd/Package/Generic/XHTML.php
@@ -1449,12 +1449,7 @@ public function format_fieldsynopsis($open, $name, $attrs) {
public function format_fieldsynopsis_modifier_text($value, $tag) {
$this->cchunk["fieldsynopsis"]["modifier"] = trim($value);
if ($this->getRole() === "attribute") {
- $attribute = trim(strtolower($value), "#[]\\");
- $href = Format::getFilename("class.$attribute");
- if ($href) {
- return '<a href="' . $href . $this->getExt() . '">' .$value. '</a> ';
- }
- return false;
+ return $this->format_attribute_modifier_text($value);
}
return $this->TEXT($value);
}
@@ -1492,15 +1487,31 @@ public function format_methodparam_modifier($open, $name, $attrs, $props) {
public function format_modifier_text($value, $tag) {
if ($this->getRole() === "attribute") {
- $attribute = trim(strtolower($value), "#[]\\");
- $href = Format::getFilename("class.$attribute");
- if ($href) {
- return '<a href="' . $href . $this->getExt() . '">' .$value. '</a> ';
- }
+ return $this->format_attribute_modifier_text($value);
}
return false;
}
+ private function format_attribute_modifier_text(string $value): string {
+ // Anything that is not a leading "#[\Attribute(" / "#[\Attribute]" chunk
+ // e.g. "|" separator between arguments passes through.
+ if (!preg_match('/^(#\[)(.+?)([](])$/', $value, $match)) {
+ if (trim($value) === '|') {
+ return ' | ';
+ }
+ return $value;
+ }
+
+ [, $prefix, $name, $suffix] = $match;
+ $attribute = strtolower(ltrim($name, "\\"));
+ $href = $this->getFilename('class.' . $attribute);
+ if (!$href) {
+ return $value;
+ }
+
+ return $prefix . '<a href="' . $href . $this->getExt() . '">' . $name . '</a>' . $suffix;
+ }
+
public function format_methodsynopsis($open, $name, $attrs, $props) {
if ($open) {
@@ -1909,6 +1920,9 @@ public function format_screen($open, $name, $attrs) {
}
public function format_constant($open, $name, $attrs, $props)
{
+ if ($this->getRole() === "attribute") {
+ return "";
+ }
if ($open) {
if (str_contains($props["innerXml"], '<replaceable')) {
$this->pushRole("constant_group");
diff --git a/tests/package/generic/attribute_formatting_001.phpt b/tests/package/generic/attribute_formatting_001.phpt
index 76bbf787..5c4c6d2b 100644
--- a/tests/package/generic/attribute_formatting_001.phpt
+++ b/tests/package/generic/attribute_formatting_001.phpt
@@ -42,7 +42,7 @@ Content:
<div class="section">
<p class="para">2. Class methodparameter with known attribute</p>
- <div class="constructorsynopsis dc-description"><span class="modifier">public</span> <span class="methodname">mysqli::__construct</span>(<span class="methodparam"><span class="attribute"><a href="file.knownattribute.is.in.html">#[\KnownAttribute]</a> </span><span class="type"><span class="type">string</span><span class="type">null</span></span> <code class="parameter">$password</code><span class="initializer"> = <span class="type">null</span></span></span>)</div>
+ <div class="constructorsynopsis dc-description"><span class="modifier">public</span> <span class="methodname">mysqli::__construct</span>(<span class="methodparam"><span class="attribute">#[<a href="file.knownattribute.is.in.html">\KnownAttribute</a>]</span><span class="type"><span class="type">string</span><span class="type">null</span></span> <code class="parameter">$password</code><span class="initializer"> = <span class="type">null</span></span></span>)</div>
</div>
@@ -54,7 +54,7 @@ Content:
<div class="section">
<p class="para">4. Function parameter with known attribute</p>
- <div class="methodsynopsis dc-description"><span class="type">bool</span> <span class="methodname">password_verify</span>(<span class="methodparam"><span class="attribute"><a href="file.knownattribute.is.in.html">#[\KnownAttribute]</a> </span><span class="type">string</span> <code class="parameter">$password</code></span>, <span class="methodparam"><span class="type">string</span> <code class="parameter">$hash</code></span>)</div>
+ <div class="methodsynopsis dc-description"><span class="type">bool</span> <span class="methodname">password_verify</span>(<span class="methodparam"><span class="attribute">#[<a href="file.knownattribute.is.in.html">\KnownAttribute</a>]</span><span class="type">string</span> <code class="parameter">$password</code></span>, <span class="methodparam"><span class="type">string</span> <code class="parameter">$hash</code></span>)</div>
</div>
</div>
diff --git a/tests/package/generic/attribute_formatting_002.phpt b/tests/package/generic/attribute_formatting_002.phpt
index 6e7aec9d..b5e0d17f 100644
--- a/tests/package/generic/attribute_formatting_002.phpt
+++ b/tests/package/generic/attribute_formatting_002.phpt
@@ -62,8 +62,8 @@ Content:
<p class="para">2. Class with known attributes</p>
<div class="classsynopsis"><div class="classsynopsisinfo">
- <span class="attribute"><a href="file.knownattribute.is.in.html">#[\KnownAttribute]</a> </span><br>
- <span class="attribute"><a href="file.anotherknownattribute.is.in.html">#[\AnotherKnownAttribute]</a> </span><br>
+ <span class="attribute">#[<a href="file.knownattribute.is.in.html">\KnownAttribute</a>]</span><br>
+ <span class="attribute">#[<a href="file.anotherknownattribute.is.in.html">\AnotherKnownAttribute</a>]</span><br>
<span class="modifier">class</span> <strong class="classname">DateTime</strong>
{</div>
}</div>
@@ -80,8 +80,8 @@ Content:
<div class="section">
<p class="para">4. Method with known attributes</p>
- <div class="methodsynopsis dc-description"><span class="attribute"><a href="file.knownattribute.is.in.html">#[\KnownAttribute]</a> </span><br>
- <span class="attribute"><a href="file.anotherknownattribute.is.in.html">#[\AnotherKnownAttribute]</a> </span><br>
+ <div class="methodsynopsis dc-description"><span class="attribute">#[<a href="file.knownattribute.is.in.html">\KnownAttribute</a>]</span><br>
+ <span class="attribute">#[<a href="file.anotherknownattribute.is.in.html">\AnotherKnownAttribute</a>]</span><br>
<span class="modifier">public</span> <span class="methodname">ClassName::methodName</span>()</div>
</div>
@@ -96,8 +96,8 @@ Content:
<div class="section">
<p class="para">6. Constructor with known attributes</p>
- <div class="constructorsynopsis dc-description"><span class="attribute"><a href="file.knownattribute.is.in.html">#[\KnownAttribute]</a> </span><br>
- <span class="attribute"><a href="file.anotherknownattribute.is.in.html">#[\AnotherKnownAttribute]</a> </span><br>
+ <div class="constructorsynopsis dc-description"><span class="attribute">#[<a href="file.knownattribute.is.in.html">\KnownAttribute</a>]</span><br>
+ <span class="attribute">#[<a href="file.anotherknownattribute.is.in.html">\AnotherKnownAttribute</a>]</span><br>
<span class="modifier">public</span> <span class="methodname">ClassName::__construct</span>()</div>
</div>
@@ -130,21 +130,21 @@ Content:
<p class="para">8. Class, constructor and methods with known attributes</p>
<div class="classsynopsis"><div class="classsynopsisinfo">
- <span class="attribute"><a href="file.knownattribute.is.in.html">#[\KnownAttribute]</a> </span><br>
- <span class="attribute"><a href="file.anotherknownattribute.is.in.html">#[\AnotherKnownAttribute]</a> </span><br>
+ <span class="attribute">#[<a href="file.knownattribute.is.in.html">\KnownAttribute</a>]</span><br>
+ <span class="attribute">#[<a href="file.anotherknownattribute.is.in.html">\AnotherKnownAttribute</a>]</span><br>
<span class="modifier">class</span> <strong class="classname">DateTime</strong>
{</div>
<div class="constructorsynopsis dc-description">
- <span class="attribute"><a href="file.knownattribute.is.in.html">#[\KnownAttribute]</a> </span><br>
- <span class="attribute"><a href="file.anotherknownattribute.is.in.html">#[\AnotherKnownAttribute]</a> </span><br>
+ <span class="attribute">#[<a href="file.knownattribute.is.in.html">\KnownAttribute</a>]</span><br>
+ <span class="attribute">#[<a href="file.anotherknownattribute.is.in.html">\AnotherKnownAttribute</a>]</span><br>
<span class="modifier">public</span> <span class="methodname">ClassName::__construct</span>()</div>
- <div class="methodsynopsis dc-description"><span class="attribute"><a href="file.knownattribute.is.in.html">#[\KnownAttribute]</a> </span><br>
- <span class="attribute"><a href="file.anotherknownattribute.is.in.html">#[\AnotherKnownAttribute]</a> </span><br>
+ <div class="methodsynopsis dc-description"><span class="attribute">#[<a href="file.knownattribute.is.in.html">\KnownAttribute</a>]</span><br>
+ <span class="attribute">#[<a href="file.anotherknownattribute.is.in.html">\AnotherKnownAttribute</a>]</span><br>
<span class="modifier">public</span> <span class="methodname">ClassName::methodName1</span>()</div>
- <div class="methodsynopsis dc-description"><span class="attribute"><a href="file.knownattribute.is.in.html">#[\KnownAttribute]</a> </span><br>
- <span class="attribute"><a href="file.anotherknownattribute.is.in.html">#[\AnotherKnownAttribute]</a> </span><br>
+ <div class="methodsynopsis dc-description"><span class="attribute">#[<a href="file.knownattribute.is.in.html">\KnownAttribute</a>]</span><br>
+ <span class="attribute">#[<a href="file.anotherknownattribute.is.in.html">\AnotherKnownAttribute</a>]</span><br>
<span class="modifier">public</span> <span class="methodname">ClassName::methodName2</span>()</div>
}</div>
@@ -161,8 +161,8 @@ Content:
<div class="section">
<p class="para">10. Function with known attributes</p>
- <div class="methodsynopsis dc-description"><span class="attribute"><a href="file.knownattribute.is.in.html">#[\KnownAttribute]</a> </span><br>
- <span class="attribute"><a href="file.anotherknownattribute.is.in.html">#[\AnotherKnownAttribute]</a> </span><br>
+ <div class="methodsynopsis dc-description"><span class="attribute">#[<a href="file.knownattribute.is.in.html">\KnownAttribute</a>]</span><br>
+ <span class="attribute">#[<a href="file.anotherknownattribute.is.in.html">\AnotherKnownAttribute</a>]</span><br>
<span class="type">void</span> <span class="methodname">function_name</span>)</div>
</div>
diff --git a/tests/package/generic/attribute_formatting_003.phpt b/tests/package/generic/attribute_formatting_003.phpt
index 00ce7e77..0506abaa 100644
--- a/tests/package/generic/attribute_formatting_003.phpt
+++ b/tests/package/generic/attribute_formatting_003.phpt
@@ -75,8 +75,8 @@ Content:
{</div>
<div class="classsynopsisinfo classsynopsisinfo_comment">/* Properties/Constants */</div>
<div class="fieldsynopsis">
- <span class="attribute"><a href="file.knownattribute.is.in.html">#[\KnownAttribute]</a> </span><br>
- <span class="attribute"><a href="file.anotherknownattribute.is.in.html">#[\AnotherKnownAttribute]</a> </span><br>
+ <span class="attribute">#[<a href="file.knownattribute.is.in.html">\KnownAttribute</a>]</span><br>
+ <span class="attribute">#[<a href="file.anotherknownattribute.is.in.html">\AnotherKnownAttribute</a>]</span><br>
<span class="modifier">public</span>
<span class="modifier">readonly</span>
<span class="type">string</span>
diff --git a/tests/package/generic/attribute_formatting_004.phpt b/tests/package/generic/attribute_formatting_004.phpt
new file mode 100644
index 00000000..fca533ac
--- /dev/null
+++ b/tests/package/generic/attribute_formatting_004.phpt
@@ -0,0 +1,91 @@
+--TEST--
+Attribute formatting 004 - Attribute with constant arguments
+--FILE--
+<?php
+namespace phpdotnet\phd;
+
+require_once __DIR__ . "/../../setup.php";
+
+$xmlFile = __DIR__ . "/data/attribute_formatting_004.xml";
+
+$config->xmlFile = $xmlFile;
+
+$format = new TestGenericChunkedXHTML($config, $outputHandler);
+
+$format->SQLiteIndex(
+ null, null,
+ "class.attribute",
+ "class.attribute",
+ "", "", "", "", "", "", 0,
+);
+$format->SQLiteIndex(
+ null, null,
+ "attribute.constants.target-class",
+ "class.attribute",
+ "", "", "", "", "", "", 0,
+);
+$format->SQLiteIndex(
+ null, null,
+ "attribute.constants.target-class-constant",
+ "class.attribute",
+ "", "", "", "", "", "", 0,
+);
+$format->SQLiteIndex(
+ null, null,
+ "attribute.constants.target-property",
+ "class.attribute",
+ "", "", "", "", "", "", 0,
+);
+
+$render = new TestRender(new Reader($outputHandler), $config, $format);
+
+$render->run();
+?>
+--EXPECT--
+Filename: attribute-formatting-004.html
+Content:
+<div id="attribute-formatting-004" class="chapter">
+ <div class="section">
+ <p class="para">1. Attribute with one constant argument</p>
+ <div class="classsynopsis"><div class="classsynopsisinfo">
+
+ <span class="attribute">#[<a href="class.attribute.html">\Attribute</a>(<a href="class.attribute.html#attribute.constants.target-class">Attribute::TARGET_CLASS</a>)]</span><br>
+ <span class="modifier">final</span>
+ <span class="modifier">class</span> <strong class="classname">Attribute</strong>
+ {</div>
+ }</div>
+ </div>
+
+ <div class="section">
+ <p class="para">2. Attribute with multiple constant arguments</p>
+ <div class="classsynopsis"><div class="classsynopsisinfo">
+
+ <span class="attribute">#[<a href="class.attribute.html">\Attribute</a>(<a href="class.attribute.html#attribute.constants.target-class-constant">Attribute::TARGET_CLASS_CONSTANT</a> | <a href="class.attribute.html#attribute.constants.target-property">Attribute::TARGET_PROPERTY</a>)]</span><br>
+ <span class="modifier">final</span>
+ <span class="modifier">class</span> <strong class="classname">Attribute</strong>
+ {</div>
+ }</div>
+ </div>
+
+ <div class="section">
+ <p class="para">3. Attribute with unknown constant argument</p>
+ <div class="classsynopsis"><div class="classsynopsisinfo">
+
+ <span class="attribute">#[<a href="class.attribute.html">\Attribute</a>(Attribute::TARGET_UNKNOWN)]</span><br>
+ <span class="modifier">final</span>
+ <span class="modifier">class</span> <strong class="classname">Attribute</strong>
+ {</div>
+ }</div>
+ </div>
+
+ <div class="section">
+ <p class="para">4. Unknown attribute with constant argument</p>
+ <div class="classsynopsis"><div class="classsynopsisinfo">
+
+ <span class="attribute">#[\UnknownAttribute(<a href="class.attribute.html#attribute.constants.target-class">Attribute::TARGET_CLASS</a>)]</span><br>
+ <span class="modifier">final</span>
+ <span class="modifier">class</span> <strong class="classname">Attribute</strong>
+ {</div>
+ }</div>
+ </div>
+</div>
diff --git a/tests/package/generic/data/attribute_formatting_004.xml b/tests/package/generic/data/attribute_formatting_004.xml
new file mode 100644
index 00000000..ef67532d
--- /dev/null
+++ b/tests/package/generic/data/attribute_formatting_004.xml
@@ -0,0 +1,45 @@
+<chapter xml:id="attribute-formatting-004">
+ <section>
+ <para>1. Attribute with one constant argument</para>
+ <classsynopsis class="class">
+ <ooclass>
+ <modifier role="attribute">#[\Attribute(<constant>Attribute::TARGET_CLASS</constant>)]</modifier>
+ <modifier>final</modifier>
+ <classname>Attribute</classname>
+ </ooclass>
+ </classsynopsis>
+ </section>
+
+ <section>
+ <para>2. Attribute with multiple constant arguments</para>
+ <classsynopsis class="class">
+ <ooclass>
+ <modifier role="attribute">#[\Attribute(<constant>Attribute::TARGET_CLASS_CONSTANT</constant>|<constant>Attribute::TARGET_PROPERTY</constant>)]</modifier>
+ <modifier>final</modifier>
+ <classname>Attribute</classname>
+ </ooclass>
+ </classsynopsis>
+ </section>
+
+ <section>
+ <para>3. Attribute with unknown constant argument</para>
+ <classsynopsis class="class">
+ <ooclass>
+ <modifier role="attribute">#[\Attribute(<constant>Attribute::TARGET_UNKNOWN</constant>)]</modifier>
+ <modifier>final</modifier>
+ <classname>Attribute</classname>
+ </ooclass>
+ </classsynopsis>
+ </section>
+
+ <section>
+ <para>4. Unknown attribute with constant argument</para>
+ <classsynopsis class="class">
+ <ooclass>
+ <modifier role="attribute">#[\UnknownAttribute(<constant>Attribute::TARGET_CLASS</constant>)]</modifier>
+ <modifier>final</modifier>
+ <classname>Attribute</classname>
+ </ooclass>
+ </classsynopsis>
+ </section>
+</chapter>