From db7da9aa69266a78338ebc2f94c5503018825231 Mon Sep 17 00:00:00 2001 From: Jordi Kroon Date: Mon, 22 Jun 2026 21:23:43 +0200 Subject: [PATCH 1/6] improve attribute formatting with better argument parsing --- phpdotnet/phd/Package/Generic/XHTML.php | 145 ++++++++++++++++-- .../generic/attribute_formatting_001.phpt | 4 +- .../generic/attribute_formatting_002.phpt | 40 ++--- .../generic/attribute_formatting_003.phpt | 10 +- .../generic/attribute_formatting_005.phpt | 119 ++++++++++++++ .../generic/data/attribute_formatting_005.xml | 69 +++++++++ 6 files changed, 351 insertions(+), 36 deletions(-) create mode 100644 tests/package/generic/attribute_formatting_005.phpt create mode 100644 tests/package/generic/data/attribute_formatting_005.xml diff --git a/phpdotnet/phd/Package/Generic/XHTML.php b/phpdotnet/phd/Package/Generic/XHTML.php index 1491fb1a..29b99a74 100644 --- a/phpdotnet/phd/Package/Generic/XHTML.php +++ b/phpdotnet/phd/Package/Generic/XHTML.php @@ -1493,23 +1493,150 @@ public function format_modifier_text($value, $tag) { } 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 ' | '; + $trimmed = trim($value); + $namePattern = '\\\\?[A-Za-z_][A-Za-z0-9_\\\\]*'; + + // Full attribute with literal arguments: #[\Name(args)] + if (preg_match('/^(#\[)(' . $namePattern . ')\((.+)\)]$/s', $trimmed, $match)) { + [, $prefix, $name, $args] = $match; + return $prefix . $this->link_attribute_name($name) . '(' . $this->render_attribute_args($args) . ')]'; + } + + // Simple attribute: #[\Name] + if (preg_match('/^#\[(' . $namePattern . ')]$/', $trimmed, $match)) { + $name = $match[1]; + $attribute = strtolower(ltrim($name, "\\")); + $href = $this->getFilename('class.' . $attribute); + $token = '#[' . $name . ']'; + if (!$href) { + return $token; } - return $value; + + return '' . $token . ' '; } - [, $prefix, $name, $suffix] = $match; + // Opening of an attribute followed by child elements: #[\Name( + if (preg_match('/^(#\[)(' . $namePattern . ')\($/', $trimmed, $match)) { + [, $prefix, $name] = $match; + return $prefix . $this->link_attribute_name($name) . '('; + } + + // Separator between attribute arguments + if ($trimmed === '|') { + return ' | '; + } + + return $trimmed === '' ? '' : $value; + } + + private function link_attribute_name(string $name): string { $attribute = strtolower(ltrim($name, "\\")); $href = $this->getFilename('class.' . $attribute); if (!$href) { - return $value; + return $name; + } + + return '' . $name . ''; + } + + private function render_attribute_args(string $args): string { + $args = trim(preg_replace('/\s+/', ' ', $args)); + $parts = $this->split_attribute_args($args); + + if (count($parts) <= 1) { + return $this->link_constants_in_text($parts[0]['value'] ?? ''); + } + + $lines = []; + $prefix = ''; + foreach ($parts as $part) { + $rendered = $this->link_constants_in_text($part['value']); + $line = '    ' . $prefix . $rendered; + if ($part['separator'] === ',') { + $line .= ','; + $prefix = ''; + } elseif ($part['separator'] === '|') { + $prefix = '| '; + } else { + $prefix = ''; + } + $lines[] = $line; + } + + return '
' . implode('
', $lines) . '
'; + } + + /** + * @return list + */ + private function split_attribute_args(string $args): array { + $parts = []; + $current = ''; + $depth = 0; + $inString = false; + $stringChar = ''; + $length = strlen($args); + + for ($i = 0; $i < $length; $i++) { + $ch = $args[$i]; + + if ($inString) { + $current .= $ch; + if ($ch === '\\' && $i + 1 < $length) { + $current .= $args[++$i]; + continue; + } + if ($ch === $stringChar) { + $inString = false; + } + continue; + } + + if ($ch === '"' || $ch === "'") { + $inString = true; + $stringChar = $ch; + $current .= $ch; + continue; + } + + if ($ch === '(' || $ch === '[') { + $depth++; + } elseif ($ch === ')' || $ch === ']') { + $depth--; + } + + if ($depth === 0 && ($ch === '|' || $ch === ',')) { + $parts[] = ['value' => trim($current), 'separator' => $ch]; + $current = ''; + continue; + } + + $current .= $ch; + } + + if (trim($current) !== '') { + $parts[] = ['value' => trim($current), 'separator' => null]; } - return $prefix . '' . $name . '' . $suffix; + return $parts; + } + + private function link_constants_in_text(string $text): string { + $escaped = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'); + + // Link Class::CONST references that appear as literal text + return preg_replace_callback( + '/\\\\?[A-Za-z_][\w\\\\]*::[A-Za-z_][\w]*/', + function (array $match): string { + $constant = $match[0]; + $link = $this->createLink($this->convertConstantNameToId($constant)); + if ($link === null) { + return $constant; + } + return '' . $constant . ''; + }, + $escaped, + ); } public function format_methodsynopsis($open, $name, $attrs, $props) { diff --git a/tests/package/generic/attribute_formatting_001.phpt b/tests/package/generic/attribute_formatting_001.phpt index 5c4c6d2b..76bbf787 100644 --- a/tests/package/generic/attribute_formatting_001.phpt +++ b/tests/package/generic/attribute_formatting_001.phpt @@ -42,7 +42,7 @@ Content:

2. Class methodparameter with known attribute

-
public mysqli::__construct(#[\KnownAttribute]stringnull $password = null)
+
public mysqli::__construct(#[\KnownAttribute] stringnull $password = null)
@@ -54,7 +54,7 @@ Content:

4. Function parameter with known attribute

-
bool password_verify(#[\KnownAttribute]string $password, string $hash)
+
bool password_verify(#[\KnownAttribute] string $password, string $hash)
diff --git a/tests/package/generic/attribute_formatting_002.phpt b/tests/package/generic/attribute_formatting_002.phpt index b5e0d17f..ef6f021f 100644 --- a/tests/package/generic/attribute_formatting_002.phpt +++ b/tests/package/generic/attribute_formatting_002.phpt @@ -50,7 +50,7 @@ Content:

1. Class with unknown attributes

- + #[\UnknownAttribute]
#[\AnotherUnknownAttribute]
class DateTime @@ -61,9 +61,9 @@ Content:

2. Class with known attributes

@@ -80,8 +80,8 @@ Content:

4. Method with known attributes

- @@ -96,8 +96,8 @@ Content:

6. Constructor with known attributes

- @@ -105,7 +105,7 @@ Content:

7. Class, constructor and methods with unknown attributes

- + #[\UnknownAttribute]
#[\AnotherUnknownAttribute]
class DateTime @@ -129,22 +129,22 @@ Content:

8. Class, constructor and methods with known attributes

-
#[\KnownAttribute]
- #[\AnotherKnownAttribute]
+
#[\KnownAttribute]
+ #[\AnotherKnownAttribute]
public ClassName::methodName1()
- @@ -161,8 +161,8 @@ Content:

10. Function with known attributes

- diff --git a/tests/package/generic/attribute_formatting_003.phpt b/tests/package/generic/attribute_formatting_003.phpt index 0506abaa..b1066f9c 100644 --- a/tests/package/generic/attribute_formatting_003.phpt +++ b/tests/package/generic/attribute_formatting_003.phpt @@ -52,7 +52,7 @@ Content:

1. Property/Constant with unknown attributes

- + class ClassName {
/* Properties/Constants */
@@ -70,13 +70,13 @@ Content:

2. Property/Constant with known attributes

- + class ClassName {
/* Properties/Constants */
- #[\KnownAttribute]
- #[\AnotherKnownAttribute]
+ #[\KnownAttribute]
+ #[\AnotherKnownAttribute]
public readonly string @@ -88,7 +88,7 @@ Content:

3. Constant of a class with attribute

- + #[\UnknownAttribute]
class ClassName {
diff --git a/tests/package/generic/attribute_formatting_005.phpt b/tests/package/generic/attribute_formatting_005.phpt new file mode 100644 index 00000000..96e95aa1 --- /dev/null +++ b/tests/package/generic/attribute_formatting_005.phpt @@ -0,0 +1,119 @@ +--TEST-- +Attribute formatting 005 - Attribute with literal parameter arguments +--FILE-- +xmlFile = $xmlFile; + +$format = new TestGenericChunkedXHTML($config, $outputHandler); + +$format->SQLiteIndex( + null, null, + "class.deprecated", + "class.deprecated", + "", "", "", "", "", "", 0, +); +$format->SQLiteIndex( + null, null, + "class.attribute", + "class.attribute", + "", "", "", "", "", "", 0, +); +$format->SQLiteIndex( + null, null, + "attribute.constants.target-function", + "class.attribute", + "", "", "", "", "", "", 0, +); +$format->SQLiteIndex( + null, null, + "attribute.constants.target-method", + "class.attribute", + "", "", "", "", "", "", 0, +); +$format->SQLiteIndex( + null, null, + "attribute.constants.target-class", + "class.attribute", + "", "", "", "", "", "", 0, +); + +$render = new TestRender(new Reader($outputHandler), $config, $format); + +$render->run(); +?> +--EXPECT-- +Filename: attribute-formatting-005.html +Content: +
+
+

1. Attribute with literal named arguments

+
+ + #[\Deprecated(
    since: '8.5',
    message: 'Deprecated since PHP 8.4'
)]

+ final + class Deprecated + {
+ }
+
+ +
+

2. Attribute with single literal positional argument

+
+ + #[\Deprecated('Deprecated since PHP 8.4')]
+ final + class Deprecated + {
+ }
+
+ +
+

3. Unknown attribute with literal argument

+
+ + #[\UnknownAttribute(foo: 'bar')]
+ final + class Deprecated + {
+ }
+
+ +
+

4. Namespaced attribute with literal argument

+
+ + #[\Some\Namespaced\Attribute(value: 42)]
+ final + class Deprecated + {
+ }
+
+ +
+

5. Multi-line attribute with literal class constant arguments

+
+ + #[\Attribute(
    Attribute::TARGET_FUNCTION
    | Attribute::TARGET_METHOD
    | Attribute::TARGET_CLASS
)]

+ final + class Deprecated + {
+ }
+
+ +
+

6. Attribute with mix of known and unknown class constants

+
+ + #[\Attribute(
    Attribute::TARGET_CLASS
    | Unknown::CONST
)]

+ final + class Deprecated + {
+ }
+
+
diff --git a/tests/package/generic/data/attribute_formatting_005.xml b/tests/package/generic/data/attribute_formatting_005.xml new file mode 100644 index 00000000..c296e7fa --- /dev/null +++ b/tests/package/generic/data/attribute_formatting_005.xml @@ -0,0 +1,69 @@ + +
+ 1. Attribute with literal named arguments + + + #[\Deprecated(since: '8.5', message: 'Deprecated since PHP 8.4')] + final + Deprecated + + +
+ +
+ 2. Attribute with single literal positional argument + + + #[\Deprecated('Deprecated since PHP 8.4')] + final + Deprecated + + +
+ +
+ 3. Unknown attribute with literal argument + + + #[\UnknownAttribute(foo: 'bar')] + final + Deprecated + + +
+ +
+ 4. Namespaced attribute with literal argument + + + #[\Some\Namespaced\Attribute(value: 42)] + final + Deprecated + + +
+ +
+ 5. Multi-line attribute with literal class constant arguments + + + + #[\Attribute(Attribute::TARGET_FUNCTION|Attribute::TARGET_METHOD|Attribute::TARGET_CLASS)] + + final + Deprecated + + +
+ +
+ 6. Attribute with mix of known and unknown class constants + + + #[\Attribute(Attribute::TARGET_CLASS | Unknown::CONST)] + final + Deprecated + + +
+
From 1970287c05fe1ce63984aedef1228334fe98bb49 Mon Sep 17 00:00:00 2001 From: Jordi Kroon Date: Mon, 22 Jun 2026 21:30:02 +0200 Subject: [PATCH 2/6] fix spaces in tests --- .../package/generic/attribute_formatting_002.phpt | 10 +++++----- .../package/generic/attribute_formatting_003.phpt | 8 ++++---- .../package/generic/attribute_formatting_005.phpt | 14 +++++++------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/package/generic/attribute_formatting_002.phpt b/tests/package/generic/attribute_formatting_002.phpt index ef6f021f..2fe743b0 100644 --- a/tests/package/generic/attribute_formatting_002.phpt +++ b/tests/package/generic/attribute_formatting_002.phpt @@ -50,7 +50,7 @@ Content:

1. Class with unknown attributes

- + #[\UnknownAttribute]
#[\AnotherUnknownAttribute]
class DateTime @@ -61,7 +61,7 @@ Content:

2. Class with known attributes

- + #[\KnownAttribute]
#[\AnotherKnownAttribute]
class DateTime @@ -105,7 +105,7 @@ Content:

7. Class, constructor and methods with unknown attributes

- + #[\UnknownAttribute]
#[\AnotherUnknownAttribute]
class DateTime @@ -129,7 +129,7 @@ Content:

8. Class, constructor and methods with known attributes

- + #[\KnownAttribute]
#[\AnotherKnownAttribute]
class DateTime @@ -167,4 +167,4 @@ Content:
-
+
\ No newline at end of file diff --git a/tests/package/generic/attribute_formatting_003.phpt b/tests/package/generic/attribute_formatting_003.phpt index b1066f9c..4bf942e2 100644 --- a/tests/package/generic/attribute_formatting_003.phpt +++ b/tests/package/generic/attribute_formatting_003.phpt @@ -52,7 +52,7 @@ Content:

1. Property/Constant with unknown attributes

- + class ClassName {
/* Properties/Constants */
@@ -70,7 +70,7 @@ Content:

2. Property/Constant with known attributes

- + class ClassName {
/* Properties/Constants */
@@ -88,7 +88,7 @@ Content:

3. Constant of a class with attribute

- + #[\UnknownAttribute]
class ClassName {
@@ -102,4 +102,4 @@ Content: }
-
+
\ No newline at end of file diff --git a/tests/package/generic/attribute_formatting_005.phpt b/tests/package/generic/attribute_formatting_005.phpt index 96e95aa1..720b368f 100644 --- a/tests/package/generic/attribute_formatting_005.phpt +++ b/tests/package/generic/attribute_formatting_005.phpt @@ -54,7 +54,7 @@ Content:

1. Attribute with literal named arguments

- + #[\Deprecated(
    since: '8.5',
    message: 'Deprecated since PHP 8.4'
)]

final class Deprecated @@ -65,7 +65,7 @@ Content:

2. Attribute with single literal positional argument

- + #[\Deprecated('Deprecated since PHP 8.4')]
final class Deprecated @@ -76,7 +76,7 @@ Content:

3. Unknown attribute with literal argument

- + #[\UnknownAttribute(foo: 'bar')]
final class Deprecated @@ -87,7 +87,7 @@ Content:

4. Namespaced attribute with literal argument

- + #[\Some\Namespaced\Attribute(value: 42)]
final class Deprecated @@ -98,7 +98,7 @@ Content:

5. Multi-line attribute with literal class constant arguments

- + #[\Attribute(
    Attribute::TARGET_FUNCTION
    | Attribute::TARGET_METHOD
    | Attribute::TARGET_CLASS
)]

final class Deprecated @@ -109,11 +109,11 @@ Content:

6. Attribute with mix of known and unknown class constants

- + #[\Attribute(
    Attribute::TARGET_CLASS
    | Unknown::CONST
)]

final class Deprecated {
}
-
+
\ No newline at end of file From 0bca8dfd590a7313a1ba7c95bc98cad7f181a134 Mon Sep 17 00:00:00 2001 From: Jordi Kroon Date: Mon, 22 Jun 2026 21:54:37 +0200 Subject: [PATCH 3/6] format attribute arguments --- phpdotnet/phd/Package/Generic/XHTML.php | 73 ++++++++++++++++++- .../generic/attribute_formatting_005.phpt | 21 ++++-- .../generic/data/attribute_formatting_005.xml | 11 +++ 3 files changed, 98 insertions(+), 7 deletions(-) diff --git a/phpdotnet/phd/Package/Generic/XHTML.php b/phpdotnet/phd/Package/Generic/XHTML.php index 29b99a74..f9a16b16 100644 --- a/phpdotnet/phd/Package/Generic/XHTML.php +++ b/phpdotnet/phd/Package/Generic/XHTML.php @@ -1544,13 +1544,13 @@ private function render_attribute_args(string $args): string { $parts = $this->split_attribute_args($args); if (count($parts) <= 1) { - return $this->link_constants_in_text($parts[0]['value'] ?? ''); + return $this->render_attribute_arg_value($parts[0]['value'] ?? ''); } $lines = []; $prefix = ''; foreach ($parts as $part) { - $rendered = $this->link_constants_in_text($part['value']); + $rendered = $this->render_attribute_arg_value($part['value']); $line = '    ' . $prefix . $rendered; if ($part['separator'] === ',') { $line .= ','; @@ -1621,6 +1621,75 @@ private function split_attribute_args(string $args): array { return $parts; } + private function render_attribute_arg_value(string $value): string { + $prefix = ''; + if (preg_match('/^([A-Za-z_][A-Za-z0-9_]*)\s*:(?!:)\s*/', $value, $m)) { + $prefix = '' . $m[1] . ': '; + $value = substr($value, strlen($m[0])); + } + + $out = ''; + $buffer = ''; + $length = strlen($value); + for ($i = 0; $i < $length; $i++) { + $ch = $value[$i]; + if ($ch !== '"' && $ch !== "'") { + $buffer .= $ch; + continue; + } + + if ($buffer !== '') { + $out .= $this->format_attribute_non_string_segment($buffer); + $buffer = ''; + } + + $stringChar = $ch; + $literal = $ch; + $i++; + for (; $i < $length; $i++) { + $sc = $value[$i]; + $literal .= $sc; + if ($sc === '\\' && $i + 1 < $length) { + $literal .= $value[++$i]; + continue; + } + if ($sc === $stringChar) { + break; + } + } + $out .= '' . htmlspecialchars($literal, ENT_NOQUOTES, 'UTF-8') . ''; + } + if ($buffer !== '') { + $out .= $this->format_attribute_non_string_segment($buffer); + } + + return $prefix . $out; + } + + private function format_attribute_non_string_segment(string $segment): string { + $linked = $this->link_constants_in_text($segment); + + // Split out ... regions so we don't touch their contents. + $parts = preg_split('/(]*>[^<]*<\/a>)/', $linked, -1, PREG_SPLIT_DELIM_CAPTURE); + foreach ($parts as $i => $part) { + if ($i % 2 === 1) { + continue; + } + $part = preg_replace_callback( + '/\b(true|false|null)\b/i', + fn(array $m) => '' . $m[1] . '', + $part, + ); + $part = preg_replace_callback( + '/(? '' . $m[0] . '', + $part, + ); + $parts[$i] = $part; + } + return implode('', $parts); + } + private function link_constants_in_text(string $text): string { $escaped = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'); diff --git a/tests/package/generic/attribute_formatting_005.phpt b/tests/package/generic/attribute_formatting_005.phpt index 720b368f..185883ca 100644 --- a/tests/package/generic/attribute_formatting_005.phpt +++ b/tests/package/generic/attribute_formatting_005.phpt @@ -55,7 +55,7 @@ Content:

1. Attribute with literal named arguments

- #[\Deprecated(
    since: '8.5',
    message: 'Deprecated since PHP 8.4'
)]
+ #[\Deprecated(
    since: '8.5',
    message: 'Deprecated since PHP 8.4'
)]

final class Deprecated {
@@ -66,7 +66,7 @@ Content:

2. Attribute with single literal positional argument

- #[\Deprecated('Deprecated since PHP 8.4')]
+ #[\Deprecated('Deprecated since PHP 8.4')]
final class Deprecated {
@@ -77,7 +77,7 @@ Content:

3. Unknown attribute with literal argument

- #[\UnknownAttribute(foo: 'bar')]
+ #[\UnknownAttribute(foo: 'bar')]
final class Deprecated {
@@ -88,7 +88,7 @@ Content:

4. Namespaced attribute with literal argument

- #[\Some\Namespaced\Attribute(value: 42)]
+ #[\Some\Namespaced\Attribute(value: 42)]
final class Deprecated {
@@ -109,11 +109,22 @@ Content:

6. Attribute with mix of known and unknown class constants

- + #[\Attribute(
    Attribute::TARGET_CLASS
    | Unknown::CONST
)]

final class Deprecated {
}
+ +
+

7. Attribute with bool, null, int and float literal arguments

+
+ + #[\UnknownAttribute(
    enabled: true,
    fallback: false,
    default: null,
    count: 42,
    ratio: 3.14
)]

+ final + class Deprecated + {
+ }
+
\ No newline at end of file diff --git a/tests/package/generic/data/attribute_formatting_005.xml b/tests/package/generic/data/attribute_formatting_005.xml index c296e7fa..1837b4b4 100644 --- a/tests/package/generic/data/attribute_formatting_005.xml +++ b/tests/package/generic/data/attribute_formatting_005.xml @@ -66,4 +66,15 @@ + +
+ 7. Attribute with bool, null, int and float literal arguments + + + #[\UnknownAttribute(enabled: true, fallback: false, default: null, count: 42, ratio: 3.14)] + final + Deprecated + + +
From 83419a30698dac00561a406af09dbede3b52d942 Mon Sep 17 00:00:00 2001 From: Jordi Kroon Date: Mon, 22 Jun 2026 21:57:53 +0200 Subject: [PATCH 4/6] style: fix whitespace formatting in attribute_formatting_005.phpt Remove trailing whitespace and normalize indentation in test output expectations. Changes are purely formatting corrections to match the expected test output without altering functionality. --- .../package/generic/attribute_formatting_005.phpt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/package/generic/attribute_formatting_005.phpt b/tests/package/generic/attribute_formatting_005.phpt index 185883ca..20667778 100644 --- a/tests/package/generic/attribute_formatting_005.phpt +++ b/tests/package/generic/attribute_formatting_005.phpt @@ -54,7 +54,7 @@ Content:

1. Attribute with literal named arguments

- + #[\Deprecated(
    since: '8.5',
    message: 'Deprecated since PHP 8.4'
)]

final class Deprecated @@ -65,7 +65,7 @@ Content:

2. Attribute with single literal positional argument

- + #[\Deprecated('Deprecated since PHP 8.4')]
final class Deprecated @@ -76,7 +76,7 @@ Content:

3. Unknown attribute with literal argument

- + #[\UnknownAttribute(foo: 'bar')]
final class Deprecated @@ -87,7 +87,7 @@ Content:

4. Namespaced attribute with literal argument

- + #[\Some\Namespaced\Attribute(value: 42)]
final class Deprecated @@ -98,7 +98,7 @@ Content:

5. Multi-line attribute with literal class constant arguments

- + #[\Attribute(
    Attribute::TARGET_FUNCTION
    | Attribute::TARGET_METHOD
    | Attribute::TARGET_CLASS
)]

final class Deprecated @@ -120,11 +120,11 @@ Content:

7. Attribute with bool, null, int and float literal arguments

- + #[\UnknownAttribute(
    enabled: true,
    fallback: false,
    default: null,
    count: 42,
    ratio: 3.14
)]

final class Deprecated {
}
-
\ No newline at end of file +
From 923a6ddabbce76285667ccddd224223de5c0a3f2 Mon Sep 17 00:00:00 2001 From: Jordi Kroon Date: Mon, 22 Jun 2026 22:00:08 +0200 Subject: [PATCH 5/6] fix spaces in tests --- .../package/generic/attribute_formatting_005.phpt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/package/generic/attribute_formatting_005.phpt b/tests/package/generic/attribute_formatting_005.phpt index 20667778..afd1bd3a 100644 --- a/tests/package/generic/attribute_formatting_005.phpt +++ b/tests/package/generic/attribute_formatting_005.phpt @@ -54,7 +54,7 @@ Content:

1. Attribute with literal named arguments

- + #[\Deprecated(
    since: '8.5',
    message: 'Deprecated since PHP 8.4'
)]

final class Deprecated @@ -65,7 +65,7 @@ Content:

2. Attribute with single literal positional argument

- + #[\Deprecated('Deprecated since PHP 8.4')]
final class Deprecated @@ -76,7 +76,7 @@ Content:

3. Unknown attribute with literal argument

- + #[\UnknownAttribute(foo: 'bar')]
final class Deprecated @@ -87,7 +87,7 @@ Content:

4. Namespaced attribute with literal argument

- + #[\Some\Namespaced\Attribute(value: 42)]
final class Deprecated @@ -98,7 +98,7 @@ Content:

5. Multi-line attribute with literal class constant arguments

- + #[\Attribute(
    Attribute::TARGET_FUNCTION
    | Attribute::TARGET_METHOD
    | Attribute::TARGET_CLASS
)]

final class Deprecated @@ -109,7 +109,7 @@ Content:

6. Attribute with mix of known and unknown class constants

- + #[\Attribute(
    Attribute::TARGET_CLASS
    | Unknown::CONST
)]

final class Deprecated @@ -120,7 +120,7 @@ Content:

7. Attribute with bool, null, int and float literal arguments

- + #[\UnknownAttribute(
    enabled: true,
    fallback: false,
    default: null,
    count: 42,
    ratio: 3.14
)]

final class Deprecated From 71af2021101353de896ebf53bb0fe593ae11ce32 Mon Sep 17 00:00:00 2001 From: Jordi Kroon Date: Mon, 22 Jun 2026 22:01:39 +0200 Subject: [PATCH 6/6] fix spaces in tests --- .../package/generic/attribute_formatting_005.phpt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/package/generic/attribute_formatting_005.phpt b/tests/package/generic/attribute_formatting_005.phpt index afd1bd3a..b5d4eca2 100644 --- a/tests/package/generic/attribute_formatting_005.phpt +++ b/tests/package/generic/attribute_formatting_005.phpt @@ -54,7 +54,7 @@ Content:

1. Attribute with literal named arguments

- + #[\Deprecated(
    since: '8.5',
    message: 'Deprecated since PHP 8.4'
)]

final class Deprecated @@ -65,7 +65,7 @@ Content:

2. Attribute with single literal positional argument

- + #[\Deprecated('Deprecated since PHP 8.4')]
final class Deprecated @@ -76,7 +76,7 @@ Content:

3. Unknown attribute with literal argument

- + #[\UnknownAttribute(foo: 'bar')]
final class Deprecated @@ -87,7 +87,7 @@ Content:

4. Namespaced attribute with literal argument

- + #[\Some\Namespaced\Attribute(value: 42)]
final class Deprecated @@ -98,7 +98,7 @@ Content:

5. Multi-line attribute with literal class constant arguments

- + #[\Attribute(
    Attribute::TARGET_FUNCTION
    | Attribute::TARGET_METHOD
    | Attribute::TARGET_CLASS
)]

final class Deprecated @@ -109,7 +109,7 @@ Content:

6. Attribute with mix of known and unknown class constants

- + #[\Attribute(
    Attribute::TARGET_CLASS
    | Unknown::CONST
)]

final class Deprecated @@ -120,7 +120,7 @@ Content:

7. Attribute with bool, null, int and float literal arguments

- + #[\UnknownAttribute(
    enabled: true,
    fallback: false,
    default: null,
    count: 42,
    ratio: 3.14
)]

final class Deprecated