Hi internals,
I'd like to propose a new language feature for PHP: Readonly Variables.
PHP currently has no way to declare a variable as immutable within a local or functional scope. This RFC proposes the readonly modifier for variables, which prevents reassignment after the initial declaration and results in an error on any attempt to do so.
Key behaviors:
- Reassignment and compound assignment operators are forbidden
- Pass-by-reference is forbidden
- unset() removes the readonly flag, allowing re-declaration
- Readonly variables inside loops are permitted but limited to a single declaration
- Scoping follows regular variable rules (function, method, closure boundaries)
The RFC is available at:
https://wiki.php.net/rfc/readonly-variables
I'm looking forward to your feedback and discussion.
Cheers,
Joshua Rüsweg
Hi internals,
I'd like to propose a new language feature for PHP: Readonly Variables.
PHP currently has no way to declare a variable as immutable within a local or functional scope. This RFC proposes the readonly modifier for variables, which prevents reassignment after the initial declaration and results in an error on any attempt to do so.
Key behaviors:
- Reassignment and compound assignment operators are forbidden
- Pass-by-reference is forbidden
- unset() removes the readonly flag, allowing re-declaration
- Readonly variables inside loops are permitted but limited to a single declaration
- Scoping follows regular variable rules (function, method, closure boundaries)
The RFC is available at:
https://wiki.php.net/rfc/readonly-variablesI'm looking forward to your feedback and discussion.
Cheers,
Joshua Rüsweg
Hi Joshua,
This is a very interesting proposal, but I feel like you haven't
explained all your choices. I have some questions:
- Why the use of readonly keyword? Why not const or some new keyword,
e.g. locked. - Why does unset not remove the variable completely? If you want a
feature to only unlock the variable then it probably needs a new
keyword, e.g. unlock - What exactly does this mean: "Readonly variables inside loops are
permitted but limited to a single declaration"? What does a single
declaration look like? - How will this impact optimizer and JIT?
- You said that readonly variables cannot be used with the global
keyword. But what about readonly variables in global scope? Will they
still behave like a typical global variable? - What exactly counts as a variable declaration? When is it a valid
syntax to use this keyword? I don't think PHP had the concept of
variable declaration until now. - What about compound variables such as arrays and objects?
I would prefer starting with readonly parameters as I feel that would
bring the most value.
I think it would also be worthwhile to investigate a simpler syntax
for define().
Regards,
Kamil
Hi internals,
I'd like to propose a new language feature for PHP: Readonly Variables.
PHP currently has no way to declare a variable as immutable within a local or functional scope. This RFC proposes the readonly modifier for variables, which prevents reassignment after the initial declaration and results in an error on any attempt to do so.
Key behaviors:
- Reassignment and compound assignment operators are forbidden
- Pass-by-reference is forbidden
- unset() removes the readonly flag, allowing re-declaration
- Readonly variables inside loops are permitted but limited to a single declaration
- Scoping follows regular variable rules (function, method, closure boundaries)
The RFC is available at:
https://wiki.php.net/rfc/readonly-variablesI'm looking forward to your feedback and discussion.
Cheers,
Joshua RüswegHi Joshua,
This is a very interesting proposal, but I feel like you haven't
explained all your choices. I have some questions:
- Why the use of readonly keyword? Why not const or some new keyword,
e.g. locked.- Why does unset not remove the variable completely? If you want a
feature to only unlock the variable then it probably needs a new
keyword, e.g. unlock- What exactly does this mean: "Readonly variables inside loops are
permitted but limited to a single declaration"? What does a single
declaration look like?- How will this impact optimizer and JIT?
- You said that readonly variables cannot be used with the global
keyword. But what about readonly variables in global scope? Will they
still behave like a typical global variable?- What exactly counts as a variable declaration? When is it a valid
syntax to use this keyword? I don't think PHP had the concept of
variable declaration until now.- What about compound variables such as arrays and objects?
I would prefer starting with readonly parameters as I feel that would
bring the most value.
I think it would also be worthwhile to investigate a simpler syntax
fordefine().Regards,
Kamil
In addition to Kamil's questions, I'm concerned about the "taking a
reference is forbidden" and "pass-by-reference is forbidden"
requirements. Objects are references, whether or not using the & operator.
According to this RFC, both of these would be forbidden:
readonly $a = new Foo();
$b = $a;
and:
function modify(Foo $value): void {}
readonly $a = new Foo();
modify($a);
I said "according to this RFC," but the RFC doesn't mention object
behavior with readonly at all, which I think might be an oversight. The
only place it hints at allowing objects is in the examples with new PDO().
Is the idea to allow objects (and arrays) similar to how JavaScript
handles these with const? That is, the objects and arrays themselves
are mutable, but the variable name cannot be reassigned to a new object
or array?
Cheers,
Ben
Hi
Am 2026-02-24 01:45, schrieb Ben Ramsey:
Objects are references, whether or not using the
&operator.
This is false. Quoting from the PHP documentation at
https://www.php.net/manual/en/language.oop5.references.php:
One of the key-points of PHP OOP that is often mentioned is that
"objects are passed by references by default". This is not completely
true. […] In PHP, an object variable […] contains an object identifier
which allows object accessors to find the actual object.
Is the idea to allow objects (and arrays) similar to how JavaScript
handles these withconst? That is, the objects and arrays themselves
are mutable, but the variable name cannot be reassigned to a new object
or array?
The proposed semantics in the RFC are very clearly modeled after the
existing behavior of readonly properties in PHP: Immutability of
identity, which would be “cannot be reassigned”. I'd like to note here
that arrays in PHP are values, thus they are immutable when stored in a
readonly property (see https://3v4l.org/vS0ij). I expect the same from
this RFC, but having array-semantics spelled out explicitly + an example
certainly makes sense for clarity.
Best regards
Tim Düsterhus
Hi Ben,
According to this RFC, both of these would be forbidden:
readonly $a = new Foo();
$b = $a;
As Tim already clarified, there is a common misconception about how PHP handles objects internally. In PHP, objects are not passed by reference in the sense of the & operator, but via an object identifier. This means that $b = $a simply copies the object identifier, not a reference to the variable itself, and is therefore perfectly valid:
<?php
readonly $a = new Foo();
$b = $a;
The readonly flag only prevents re-assignment of $a itself. The & operator, on the other hand, would create a true reference to the variable $a, which would allow $a to be modified indirectly through $b, effectively circumventing the readonly flag. This is why taking a reference of a readonly variable is forbidden.
I hope this clears up the concern.
and:
function modify(Foo $value): void {}
readonly $a = new Foo();
modify($a);I said "according to this RFC," but the RFC doesn't mention object behavior with readonly at all, which I think might be an oversight. The only place it hints at allowing objects is in the examples with
new PDO().Is the idea to allow objects (and arrays) similar to how JavaScript handles these with
const? That is, the objects and arrays themselves are mutable, but the variable name cannot be reassigned to a new object or array?
Regarding your question about object and array behavior: you are correct that the RFC did not explicitly document this, which was indeed an oversight on my part.
As already mentioned above, the variable binding is immutable, meaning $a cannot be reassigned to a different object, but the internal state of the object can still be modified. This is also consistent with how the readonly attribute behaves for class properties:
<?php
readonly $a = new Foo();
$a->value = "foo"; // Valid: modifying the object's internal state is allowed
$a = new Foo(); // Error: Cannot re-assign readonly variable
For arrays, however, the behavior differs from JavaScript. In PHP, arrays are value types with copy-on-write semantics, not reference types. This means that a readonly array is fully immutable, as neither the array itself nor its elements can be modified after the initial assignment:
<?php
readonly $arr = [1, 2, 3];
$arr[] = 4; // Error: Cannot re-assign readonly variable
$arr[0] = 99; // Error: Cannot re-assign readonly variable
I have added explicit examples for both objects and arrays to the RFC to make this behavior clear. I appreciate you flagging this.
(I apologize for the duplicate email, Ben. The first attempt was sent from an incorrect address and did not go through properly.)
Cheers,
Joshua Rüsweg
Hi internals,
I'd like to propose a new language feature for PHP: Readonly Variables.
PHP currently has no way to declare a variable as immutable within a
local or functional scope. This RFC proposes the readonly modifier for
variables, which prevents reassignment after the initial declaration and
results in an error on any attempt to do so.The RFC is available at:
https://wiki.php.net/rfc/readonly-variablesI'm looking forward to your feedback and discussion.
Cheers,
Joshua RüswegHi Joshua,
This is a very interesting proposal, but I feel like you haven't
explained all your choices. I have some questions:
- Why the use of readonly keyword? Why not const or some new keyword,
e.g. locked.I would prefer starting with readonly parameters as I feel that would
bring the most value.
I think it would also be worthwhile to investigate a simpler syntax
fordefine().
Hi Kamil, Joshua,
I wrote on this topic in the past:
https://news-web.php.net/php.internals/126909
and I think we already cannot use readonly and final for parameter, as that
conflicts with constructor parameter promotion:
if we ever decide to use the final modifier for a
parameter, it would not be possible.
it is already not possible to use the readonly modifier for a parameter
because of this
I think that using const keyword might be one of the best options.
--
Alex
Hello Kamil,
- Why the use of readonly keyword? Why not const or some new keyword,
e.g. locked.
I spent a long time thinking about the right keyword. In an early stage of development I chose const, as it is already used in this way in JavaScript, for example. However, the further I developed the feature and the more feedback I gathered from others, the more I gravitated towards readonly. The readonly keyword is already used in PHP for class properties and serves exactly the same purpose: declaring variables as immutable. Reusing readonly for variables is therefore consistent with existing PHP semantics. The const keyword, on the other hand, has fundamentally different semantics in PHP. A const declaration at the top level defines a globally accessible constant, and inside a class it defines a class constant. This is quite different from a locally scoped immutable variable, and using const for this feature would likely cause confusion for PHP developers who are already familiar with its existing meaning. Introducing an entirely new keyword for this purpose, such as locked, would in my opinion be counterproductive, as it could be unintuitive for developers who are already familiar with readonly and its meaning in PHP.
- Why does unset not remove the variable completely? If you want a
feature to only unlock the variable then it probably needs a new
keyword, e.g. unlock
Thank you for pointing that out, as I apparently expressed myself ambiguously in the RFC. unset() behaves exactly as it does for regular variables: it removes the variable entirely. The only additional effect is that the readonly flag is cleared along with it, meaning the variable name can subsequently be reused with a new assignment, without the readonly flag. I have updated the RFC to make this behavior more explicit.
Regarding the idea of a dedicated unlock mechanism: I don't think this is necessary, and I honestly don't see a practical use case for it. If a variable needs to be modified after its initial assignment, it simply should not be declared as readonly in the first place.
- What exactly does this mean: "Readonly variables inside loops are
permitted but limited to a single declaration"? What does a single
declaration look like?
A readonly variable declared inside a loop is exposed to the outer scope, just like any other variable in PHP. This means that on the second iteration, PHP would attempt to re-assign an already initialized readonly variable, which results in a fatal error. A "single declaration" therefore means that the readonly variable can only be initialized once throughout the entire execution of the loop, not once per iteration.
I hope this clarifies the behavior. Please let me know if you have any further questions.
- How will this impact optimizer and JIT?
Thank you for raising this point. I have not yet investigated the impact on the optimizer and JIT, as the current implementation is an initial proof of concept intended to gather feedback from the community. I will definitely look into this before the voting phase and investigate whether and how readonly variables can be leveraged for potential optimizations. I will report back with my findings.
- You said that readonly variables cannot be used with the global
keyword. But what about readonly variables in global scope? Will they
still behave like a typical global variable?
I am not entirely sure what you mean by "typical global variable behavior", so I will try to address what I believe you are asking.
If your question is whether a readonly variable declared in the global scope can be imported into a function via the global statement, the answer is no. Importing a readonly variable via global is forbidden and results in an error.
If you are asking whether readonly variables declared in the global scope are automatically accessible in function scopes, the answer is also no. Readonly variables follow the same scoping rules as regular variables, meaning they are not automatically available inside functions, just as regular variables are not.
If you meant something else entirely, please feel free to clarify and I will be happy to address it.
- What exactly counts as a variable declaration? When is it a valid
syntax to use this keyword? I don't think PHP had the concept of
variable declaration until now.
By "declaration" I simply mean a regular variable assignment prefixed with the readonly keyword:
<?php
readonly $foo = "bar”;
You are right that PHP has not had a formal concept of variable declaration until now. The readonly keyword in this context is purely a modifier on a standard assignment statement, signaling that the variable cannot be re-assigned after its initial value has been set. Any valid assignment expression can be used as the initial value, including object instantiation, function calls, or scalar values.
- What about compound variables such as arrays and objects?
The behavior differs depending on the type of value assigned to a readonly variable.
For arrays, the entire array is immutable. This means that neither the array itself nor its elements can be modified after the initial assignment:
<?php
readonly $arr = [1, 2, 3];
$arr[] = 4; // Error: Cannot re-assign readonly variable
$arr[0] = 99; // Error: Cannot re-assign readonly variable
For objects, the behavior is consistent with how readonly works for class properties: the variable binding is immutable, meaning the object reference cannot be replaced with a different object. However, the internal state of the object itself can still be modified:
<?php
readonly $obj = new stdClass();
$obj->value = "changed"; // Valid: modifying the object's internal state is allowed
$obj = new stdClass(); // Error: Cannot re-assign readonly variable
I have added examples for both cases to the RFC to make this behavior explicit.
I would prefer starting with readonly parameters as I feel that would
bring the most value.
I find the idea of readonly parameters interesting and have already given it some thought. However, it is out of scope for this RFC, as I wanted to keep the initial proposal focused and manageable. I have already mentioned it in the Future Scope section of the RFC as a potential natural extension that could be addressed in a separate RFC in the future.
I think it would also be worthwhile to investigate a simpler syntax
fordefine().
It is worth noting that PHP already provides two ways to define global constants: define() and the const keyword. Both serve a different purpose than readonly variables, as they are globally accessible and not scoped to a local or functional context. See: https://3v4l.org/1r6gk
Cheers,
Joshua Rüsweg
I would prefer starting with readonly parameters as I feel that would
bring the most value.I find the idea of readonly parameters interesting and have already
given it some thought. However, it is out of scope for this RFC, as I
wanted to keep the initial proposal focused and manageable. I have
already mentioned it in the Future Scope section of the RFC as a
potential natural extension that could be addressed in a separate RFC
in the future.
A slight tangent here, but what would readonly parameters do?
If you pass something by value, then already you cannot modify the value in the caller. If the value is large, it's already kept efficient by copy-on-write.
If you pass by reference, then presumably you want to modify the value in the caller.
If the value is an object, then as already discussed, its internals are mutable either way so you gain little, but the function cannot change the identity in the caller anyway.
So I don't see what readonly parameters would even accomplish. The thing they accomplish in JS or Java or C simply doesn't apply in PHP.
I'm still undecided at the moment on readonly variables.
--Larry Garfield
I would prefer starting with readonly parameters as I feel that would
bring the most value.I find the idea of readonly parameters interesting and have already
given it some thought. However, it is out of scope for this RFC, as I
wanted to keep the initial proposal focused and manageable. I have
already mentioned it in the Future Scope section of the RFC as a
potential natural extension that could be addressed in a separate RFC
in the future.A slight tangent here, but what would readonly parameters do?
If you pass something by value, then already you cannot modify the value in the caller. If the value is large, it's already kept efficient by copy-on-write.
If you pass by reference, then presumably you want to modify the value in the caller.
If the value is an object, then as already discussed, its internals are mutable either way so you gain little, but the function cannot change the identity in the caller anyway.
So I don't see what readonly parameters would even accomplish. The thing they accomplish in JS or Java or C simply doesn't apply in PHP.
I'm still undecided at the moment on readonly variables.
--Larry Garfield
A readonly parameter would mean that it would be impossible to reuse
the variable within the function for something else. It's not uncommon
for a function to preprocess the argument and still make use of the
original value. A readonly modifier would prevent accidental
overwrite.
function foo(string|array $data) {
if (is_string($data) ) {
$data = [$data]; // Original argument lost
}
// ....
return is_string($data) ? $result : [$result];
)
Thanks for the replies.
- What exactly does this mean: "Readonly variables inside loops are
permitted but limited to a single declaration"? What does a single
declaration look like?A readonly variable declared inside a loop is exposed to the outer scope, just like any other variable in PHP. This means that on the second iteration, PHP would attempt to re-assign an already initialized readonly variable, which results in a fatal error. A "single declaration" therefore means that the readonly variable can only be initialized once throughout the entire execution of the loop, not once per iteration.
So does that mean that it can only be used within a loop if that loop
only has a single iteration or that the assignment is guarded by some
condition?
$var = null;
while(true) {
if ($var === null) {
readonly $var = 42;
}
}
- You said that readonly variables cannot be used with the global
keyword. But what about readonly variables in global scope? Will they
still behave like a typical global variable?I am not entirely sure what you mean by "typical global variable behavior", so I will try to address what I believe you are asking.
If your question is whether a readonly variable declared in the global scope can be imported into a function via theglobalstatement, the answer is no. Importing a readonly variable viaglobalis forbidden and results in an error.
If you are asking whether readonly variables declared in the global scope are automatically accessible in function scopes, the answer is also no. Readonly variables follow the same scoping rules as regular variables, meaning they are not automatically available inside functions, just as regular variables are not.
If you meant something else entirely, please feel free to clarify and I will be happy to address it.
I meant using $GLOBALS.
readonly $var = 42;
function foo() {
$GLOBALS['foo'] = 'bar';
}
foo();
var_dump($foo);
- What exactly counts as a variable declaration? When is it a valid
syntax to use this keyword? I don't think PHP had the concept of
variable declaration until now.By "declaration" I simply mean a regular variable assignment prefixed with the readonly keyword:
<?php readonly $foo = "bar”;You are right that PHP has not had a formal concept of variable declaration until now. The readonly keyword in this context is purely a modifier on a standard assignment statement, signaling that the variable cannot be re-assigned after its initial value has been set. Any valid assignment expression can be used as the initial value, including object instantiation, function calls, or scalar values.
Right, but if PHP users have never thought about variable declaration,
then it might be confusing for them to understand when it is legal to
use this keyword. For example, you said that array destructuring is
not supported but for me it's a regular variable assignment. It seems
that assignment within a loop is also not allowed, but that doesn't
seem to be clearly specified in the RFC.
while(readonly $arr = $pdoStmt->fetch()) {} // Is this allowed
assuming it only iterates once?
foreach($arr as readonly $element) {} // This is forbidden, correct?
for(readonly $foo; ; ) {} // Is this allowed?
if(readonly $val = foobar()) {} // This is a regular variable
assignment, is this allowed?
What about array vivification?
readonly $arr[] = 42;
Or a declaration with a warning:
readonly $i++;
What about a slightly more complex variable assignment?
$arr = [readonly $var = 42];
// or
readonly $var1 = $var2 = 42;
// or
foobar(readonly $baz = 42);
// or
$foo = 'bar';
readonly $$foo = 42;
Regards,
Kamil