← All posts

RFC Merged into PHP Core: Object Property Writes on Constants (PHP 8.6)

Aug 13, 2026 · 5 min read · — views
The php/php-src repository on GitHub showing the merged commit allowing direct mutation of objects stored in constants

A change I proposed for PHP has been merged into php-src and ships in PHP 8.6: you can now write to a property of an object stored in a constant. My two previous PHP core posts were bug fixes. This one is different — it changes what the language allows, and that means a formal RFC and a vote by the PHP internals group. The vote passed 17 yes, 2 no, 6 abstain, and the implementation merged on 13 August 2026.

TL;DR

  • The change: CONFIG->debug = true; is now legal, for global and class constants alike.
  • Why: a constant's binding is immutable, but the object it holds was always mutable — PHP was treating those as the same question.
  • Still rejected: rebinding a constant, and dimension writes such as ARR[0] = 9.
  • The BC note: some code that used to fail at compile time now fails at runtime instead.
  • The process: an RFC, a discussion on the internals mailing list, a vote, then review of the implementation.
The php/php-src repository on GitHub showing the merged commit "Allow direct mutation of objects stored in constants"
Merged into php-src and shipping in PHP 8.6.

What was the problem?

A PHP constant can hold an object. But until PHP 8.6, writing to a property of that object was a fatal error:

<?php
const CONFIG = new stdClass;

CONFIG->debug = true;
// Fatal error: Cannot use temporary expression in write context

Compare that with the same code using an ordinary variable:

<?php
$config = new stdClass;
$config->debug = true;  // fine, and always has been

Nobody finds the second snippet surprising. $config holds a handle to an object. Writing to a property changes the object, not the variable.

A constant is the same thing with one additional rule: you cannot rebind it. CONFIG = new stdClass; is a parse error, and it still is in PHP 8.6. But whether the binding is fixed and whether the object can change are two different questions, and PHP was answering both with "no".

Christophe Coevoet reported this as issue #10497 in February 2023. It stayed open for about three and a half years.

What can you do in PHP 8.6?

Property writes through a constant reference now work, for global constants and class constants alike:

<?php
const CONFIG = new stdClass;

CONFIG->debug = true;    // global constant

class Cls {
    const CONFIG = CONFIG;
}

Cls::CONFIG->level++;    // class constant

What is still not allowed?

Rebinding the constant, and anything that would write into a copy instead of through a handle:

<?php
const ARR = [1, 2, 3];

ARR[0] = 9;             // Fatal error — still rejected
ARR[] = 4;              // Fatal error — still rejected
CONFIG = new stdClass;  // Parse error — rebinding remains impossible

Arrays in PHP are values, not handles. ARR[0] = 9 would write into a copy and then discard it, so the assignment would silently do nothing. Keeping it an error is the honest answer.

What about backward compatibility?

Code that previously failed at compile time now compiles. That is a slightly bigger shift than it first sounds: a file containing TRUE->prop = 1 used to fail to compile entirely and never executed a single line. In PHP 8.6 it compiles, runs up to that write, and raises an Error there instead.

Enum cases are class constants, so they are affected too. Writing to an enum case property now raises the usual readonly-property Error at runtime rather than failing at compile time. Enum case properties remain immutable — only the timing and the type of the error changed.

What does it actually take to get an RFC through?

This is the part that differs most from my earlier contributions. A bug fix is a conversation about whether the code is correct. A language change is a conversation about whether the language should work that way at all, and it is held in public with people who have been arguing about PHP's semantics for far longer than I have.

So the code was the smallest part of the work. The rest was the RFC itself: writing up the reasoning clearly enough that someone who has never thought about this can follow it, taking it to the internals mailing list, and answering the objections that came back — the array case above got a lot of that attention. Then the vote, and only after all of that, review of the actual implementation.

Review was where the real value showed up. Ilija Tovilo reviewed the patch and reworked parts of the implementation, and is a co-author on the merged commit. That is the whole point of doing this in the open, and it is a large part of why the process is deliberately slow: the pull request was opened in January and merged in August.

If you want the more typical, smaller-scale version of contributing, I have written about fixing a NAN Reflection bug in php-src and about how PR #20902 added ReflectionConstant::inNamespace(). Those are a bug fix and a small API addition — no RFC required.

Frequently asked questions

What is a PHP RFC?

An RFC is the formal proposal process for changing the PHP language. You write the proposal on the PHP wiki, discuss it on the internals mailing list, and then it goes to a vote by the people with voting rights on php-src. A language change needs a two-thirds majority to pass.

Do I need to be a PHP maintainer to write an RFC?

No. But you do need to be prepared to explain the idea in writing, defend it publicly, and have someone review the implementation before it lands.

Does this change make constants mutable?

No. The constant binding is still immutable — you cannot point it at a different value. What changed is that PHP no longer blocks you from mutating the object the constant refers to, which is the same thing you have always been able to do through a variable.

Why are array constants treated differently from object constants?

Because arrays are value types in PHP and objects are handle types. Writing to an array element through a constant would operate on a copy, so the write would have no visible effect. Rather than allow a silently useless assignment, it stays an error.

Will this break my existing code?

Almost certainly not, but the failure mode moved. Code that was already broken — writing to a property of something that is not a mutable object — used to stop the whole file from compiling. Now it compiles and raises an Error when execution reaches it.

How long did the whole thing take?

The pull request was opened on 11 January 2026 and merged on 13 August 2026 — roughly seven months, including the discussion and the vote. The original issue had been open since February 2023.

Key takeaways

  • PHP 8.6 allows property writes on objects held in constants, for global and class constants.
  • The constant binding stays immutable; only the referenced object can change.
  • Dimension writes on constants remain errors, because arrays are values rather than handles.
  • Enum case property writes now fail at runtime instead of compile time, but remain forbidden.
  • Changing a language is mostly writing, discussing and defending — the patch is the easy part.

Thanks to Ilija Tovilo for the reviews and for co-authoring the merged implementation, and to everyone on internals who took the time to argue about it.

Reading: the RFC, the pull request, and the merged commit. Thinking about your own PHP contribution? I'm @khaledalam on GitHub.

Comments

    Comments are reviewed before they appear.