Skip to content

Zend: Read string arguments of #[\Deprecated] without constructing it - #24198

Open
ArtUkrainskiy wants to merge 1 commit into
php:masterfrom
ArtUkrainskiy:deprecated-suffix-cache
Open

ArtUkrainskiy wants to merge 1 commit into
php:masterfrom
ArtUkrainskiy:deprecated-suffix-cache

Conversation

@ArtUkrainskiy

@ArtUkrainskiy ArtUkrainskiy commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

First of the two steps in GH-24191.

Emitting the deprecation of a function constructs a \Deprecated object on every call to read its message and since: zend_get_attribute_object(), the constructor through zend_call_function() with named arguments, two property writes, two reads, destruction — about 2,900 of the 5,000 instructions a deprecated call costs, paid whether or not E_DEPRECATED is reported. The constructor is there for parameter handling (named arguments, defaults, type errors, constant expressions), and nearly every attribute has only string literals, which need none of it; this is the string bypass discussed in #11293 that didn't make it into the merge.

This reads message/since straight from the arguments when they are strings or null, positional or named, each at most once; anything else goes through the constructor as before and gets its errors from it, and so does a call with an exception pending (preloading relies on the constructor not running there). Same for #[\NoDiscard]. The message is byte for byte the same.

Instructions per call, release build: ReflectionProperty::setAccessible() 5,010 → 2,772; a deprecated user function with literal arguments 4,641 → 2,516; with a runtime constant as argument (constructor path) 4,831 → 4,976, the cost of trying the fast path first.

What observers see: Deprecated::__construct()/NoDiscard::__construct() are no longer called for literal arguments, so zend_execute_internal hooks don't see them any more; nodiscard/007.phpt and observer_sqlite_create_function.phpt recorded those frames and are updated, 007 now shows both paths. Tests added for the argument forms the fast path accepts and refuses (positional, named, null, duplicate and unknown names, too many arguments), expectations generated on master, and for repeated internal calls through closures, reflection and an error handler. Full test suite on a debug ZTS build with 45 extensions passes; measurements and the differential runs: https://github.com/ArtUkrainskiy/php-src-bench/tree/main/reports/deprecation-cost

Emitting the deprecation of a function constructed a \Deprecated object
on every call to read its message and since arguments: an object, a
constructor call with named arguments, two property writes, two reads
and the destruction, about 2,900 of the 5,000 instructions a deprecated
call costs, paid whether or not E_DEPRECATED is reported. The object
was constructed for the parameter handling of the constructor, named
arguments, defaults, type errors, constant expressions, and nearly
every attribute has only string literals, which need none of it.

Read message and since straight from the arguments when they are
strings or null, positional or named, each at most once; everything
else, a constant expression, another type, a duplicate or unknown name,
goes through the constructor as before, and so does a call with an
exception pending, where the constructor is not run and nothing is
emitted. The same for #[\NoDiscard]. The message is byte for byte the
same; what changes is that the constructor is no longer called for
literal arguments, so an execute_internal observer no longer sees it.

A call to ReflectionProperty::setAccessible() goes from 5,010 to 2,770
instructions, a deprecated user function with literal arguments from
4,640 to 2,520.
@ArtUkrainskiy
ArtUkrainskiy force-pushed the deprecated-suffix-cache branch from c3c9372 to bd47375 Compare October 8, 2026 17:35
@ArtUkrainskiy ArtUkrainskiy changed the title Zend: Build the message of a persistent #[\Deprecated] once Title: Zend: Read string arguments of #[\Deprecated] without constructing it Oct 8, 2026
@ArtUkrainskiy
ArtUkrainskiy marked this pull request as ready for review October 8, 2026 20:14
@ArtUkrainskiy
ArtUkrainskiy requested a review from dstogov as a code owner October 8, 2026 20:14
@ArtUkrainskiy ArtUkrainskiy changed the title Title: Zend: Read string arguments of #[\Deprecated] without constructing it Zend: Read string arguments of #[\Deprecated] without constructing it Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant