This Year in PHP (2025)
Tuesday, 20 January 2026Now that we are a few weeks into 2026, I figured it would be a good opportunity for me to reflect on my PHP contributions in 2025. It was a pretty big year for me in terms of contributing to the php-src repo.
My first merged commit of the year came on January 4, fixing a pair of misplaced
() in the UPGRADING file for PHP 8.5 (#17360). Not all of my
contributions were that trivial though. By no means is the following an
exhaustive list of my contributions, but it covers the big areas.
gen_stub.php
I spent a lot of time working to clean up and simplify the gen_stub.php script
that is used to maintain the *_arginfo.h files. Given that this script is one
of the few places in the php-src codebase where the code is written in PHP
(rather than C or C++), it is far easier to contribute to, at least for me.
My cleanup changes have generally been aimed at being invisible to end users,
and just improving the script. As a result, they probably had limited impact
(after all, the script functioned the same afterwards) but they were still fun
to work on. That said, I also made a number of improvements to the script that
did change the output in (what I hope was) a positive way, most of them
relating to processing attributes on internal structures:
- After I merged support for attributes on compile-time constants (details
below), I added support for using
gen_stub.phpto process attributes on internal constants (#18735). As a result, deprecated internal constants now include the details of their deprecations; see 3v4l output for an example. - Internally, strings used by PHP are generally interned, which
avoids storing multiple copies of the same string in memory. However, using
string interning comes at the cost of needing to search for the string in the
pool of interned strings when trying to use it. That said, some strings are
so frequently used in the PHP source that they are considered "known" and can
be retrieved with a simple array access for the string's ID. I updated
gen_stub.phpso that when registering attributes for internal structures, if the value of an attribute parameter was a "known" string it would be retrieved by the string ID instead of looking it up by value. More importantly, since the uses of#[\Deprecated]all specify what version of PHP things were deprecated in, by adding known strings for each of the applicable versions I simplified the registration to no longer need to look up the strings each time the extensions were loaded (#19075). - I further simplified the initialization of attribute values by avoiding the use of temporary zvals before the attribute parameters were stored with the attributes (#19141), and by reusing any strings with the same content across multiple attribute uses (#19241).
- One other big change I made, which only helped the generated
*_arginfo.hfiles be more readable, was to further combine preprocessor conditional blocks. For example, some PHP constants are only registered when compiling PHP for Windows. Previously, the arginfo looked a bit like:
#if defined(PHP_WIN32)
// register some constant A
#endif
#if defined(PHP_WIN32)
// register some constant B
#endif
#if defined(PHP_WIN32)
// register some constant C
#endif
and after the change, for constants declared right after each other, the arginfo would look like
#if defined(PHP_WIN32)
// register some constant A
// register some constant B
// register some constant C
#endif
Since #if conditions are evaluated by the preprocessor during compilation, the
resulting binary should be identical before and after this change, but the
code should be a bit easier to read, and might have even had a (negligible)
improvement in compilation times now that the conditions needed to be evaluated
fewer times.
Test cleanup
Also in the first few months of 2025, I continued my work to organize the
Zend/tests directory of the php-src codebase. GitHub limits directory displays
to only showing the first 1,000 entries, meaning that for any additional entries
in the directory, they cannot be easily browsed in the online interface.
In August of 2024, I filed an issue, #15631, when I couldn't even
see tests starting with "enum" (contents are ordered alphabetically); while I
started the cleanup in 2024, I did the majority in 2025, and as of writing,
there are "only" 983 test files or subdirectories in Zend/tests/*, down from
the starting point of over 2,500!
The commits themselves are fairly boring, but are linked from #15631 for those interested.
Karma and Reflection
In March, I successfully requested merge access to the php-src codebase; this access was historically called "Karma" back when PHP development was done with Subversion. In the access request, I volunteered to take over the maintenance of the reflection extension, which did not have a primary maintainer at that point.
Adding myself as the maintainer required multiple steps - the easier was
editing the EXTENSIONS file on the master branch, #18137. The
harder part was adding myself to the .github/CODEOWNERS file so that I would
be added to all reflection-related patches. Since GitHub reads the CODEOWNERS
file from the branch that is targeted by a pull request, the policy for php-src
is to ensure that the CODEOWNERS file is the same across all supported
branches of PHP. This meant that, once I merged my change to add myself as a
code owner for reflection in PHP 8.3 (#18138), I then needed to
upmerge that change to the PHP 8.4 branch, and then from there to the master
branch: 87d7532 and 5c3cff2.
Even before I was the maintainer of the reflection extension, I had already sent a few patches in 2025 to improve it, reducing duplication with #17795, showing the type of object constants used as default properties (#17781, merged to PHP 8.3) and including "final" and "abstract" if applicable when dumping properties (#17827, merged to PHP 8.4).
After becoming a maintainer of the extension, my contributions continued, including improving the output of ReflectionClass with enums, which I had originally started work on back in September 2024 (#15956), and optimizing the building of strings in the various printing functions (#18155).
I also used my merge access to approve and merge patches from other contributors, both to the reflection extension and to other areas of the php-src codebase.
Attributes on constants
In April, I was finally able to merge the implementation of my RFC for adding support for attributes on compile-time constants. I wrote a dedicated blog post about that RFC and the process leading up to getting the patch merged, see "Attributes on Constants".
Of course, code is not always perfect, and there were a few bugs in the merged
implementation that I worked on (e.g. #18463), but there was also
some other follow-up needed - making use of the new functionality to add
#[\Deprecated] attributes to the various deprecated internal constants. After
I added support to gen_stub.php to handle the attributes, I then migrated
most of the stub-declared deprecated constants to use #[\Deprecated] with
a message and a version, requiring some deep dives into the git history to
figure out when and when some things were deprecated. See #18780
for the mass migration.
I skipped some of the contents in that mass migration patch because of how the constants were declared or used. I sent additional patches for
- the ext/hash
MHASH_*constants (#19565) - the ext/intl
U_MULTIPLE_DECIMAL_SEPERATORSconstant (#19564) - the ext/session
SIDconstant (#19566)
Other RFCs
While supporting attributes on constants was probably the biggest change I made, I had five other RFCs in 2025, four of which were successful. I also had a few entries in the mass deprecations RFC.
My dedicated RFCs were
- supporting
neverparameter types - supporting
finalin constructor property promotion - adding the
#[\DelayedTargetValidation]attribute - adding the
FILTER_THROW_ON_FAILUREflag - supporting
#[\Deprecated]on traits
The community declined the never parameters RFC, while accepting the rest.
You can read more about the never parameter types RFC and support for final
in constructor property promotion in my blog post from June,
"A Tale of Two RFCs". I also wrote an
explanation of the #[\DelayedTargetValidation] attribute, and
discuss FILTER_THROW_ON_FAILURE in
my post about out-of-band signaling.
Release Manager
Other than gaining merge access, the biggest change in my role as a PHP contributor came when the PHP community chose me as one of the release managers for PHP 8.5. While I started this role in 2025, it will continue until the end of 2029, when PHP 8.5 reaches its end of life. I am planning to write up a separate blog post about my experience so far.
Looking Ahead
So far in 2026, my merged PHP contributions have only been related to my work as a release manager. But, I have already started work on a few RFCs to hopefully include in PHP 8.6. I plan to remain involved as a contributor at least until the end of 2029 when my role as a release manager ends, so I am not going anywhere.