====== PHP RFC: Readonly Variables ====== * Version: 0.1 * Date: 2026-02-22 * Author: Joshua Rüsweg * Status: Draft * Implementation: https://github.com/joshuaruesweg/php-src/pull/1 ===== Introduction ===== PHP has long supported immutable values through ''define()'' and the ''const'' keyword at the global and class scope. However, there is currently no way to declare a **variable** as immutable within a local or functional scope — a pattern that has proven valuable in languages such as JavaScript (''const''), Rust (''let''), and Swift (''let''). This RFC proposes **readonly variables**: the ability to mark a variable as immutable after its initial assignment using the ''readonly'' modifier. Once declared, reassignment results in a fatal error, making developer intent explicit and preventing a class of bugs that are otherwise undetectable without additional tooling. ===== Proposal ===== Immutable variables are declared by prefixing the variable name with the ''readonly'' keyword at the point of assignment. After the initial assignment, any attempt to reassign or pass the variable by reference results in a fatal error. Readonly variables are scoped exactly like regular variables — they follow function, method, and closure boundaries. They are **not** global constants and are not accessible via ''constant()''. **Pass-by-reference is forbidden:** **Compound assignment operators are forbidden:** **Unset removes the ''readonly'' flag:** **Scope and Conditional Initialization** **Edge case — Readonly variables inside loops are limited to a single declaration:** Readonly variables are permitted inside loop bodies, but can only be declared **once**. Since loop variables are exposed to the outer scope after the loop completes, a readonly variable declared inside a loop cannot be re-initialized on subsequent iterations. ==== Examples ==== **Simple example — immutable configuration value:** **Multiple Assignment** Multiple variables can be declared as constant in a single statement by separating them with a comma, following the same syntax as regular variable assignment. Edge case — readonly variables in closures: ===== Backward Incompatible Changes ===== This RFC introduces no breaking changes for existing code. The ''readonly'' keyword in the context of variable declarations is currently a parse error, meaning no valid existing PHP code uses this syntax. ===== Proposed PHP Version(s) ===== Next PHP 8.x ===== RFC Impact ===== ==== To the Ecosystem ==== IDEs, language servers (e.g. Intelephense, PHPStan, Psalm), and static analyzers will need to be updated to recognize the ''readonly'' variable modifier and enforce immutability during static analysis. Auto-formatters and linters may also need updates to handle the new syntax. The benefit is that these tools can now **guarantee** immutability where previously they could only hint at it via docblock annotations such as ''@readonly'' on variables. ==== To Existing Extensions ==== Extensions that inspect or manipulate variable symbols (e.g. via ''zend_hash'' or custom opcache strategies) may need to account for the new ''Z_EXTRA_USER_READONLY_VAR'' flag which is stored in ''(zval).u2.extra''. Extensions that do not interact with variable internals are unaffected. Additionally, there is ''readonly_var_flags'' in the ''zend_op_array''. ==== To SAPIs ==== No impact expected on CLI, the built-in development server, or embedded PHP SAPIs. Readonly variables follow the same lifecycle as regular variables within their respective execution contexts. ===== Future Scope ===== * **Destructuring support** — ''readonly [$a, $b] = [1, 2];'' is not in scope for this RFC but could be added later. * **''readonly'' parameters** — marking function parameters as immutable could be a natural extension of this concept. * **''readonly'' in ''foreach'' loops** - ''foreach ($items as readonly $item)'' * **Typed variables** - Typed variables, which are not immutable (''int $var = 1''). ===== Voting Choices ===== This RFC requires a 2/3 majority to be accepted. * Yes * No * Abstain ===== Patches and Tests ===== A proof-of-concept implementation is available at: https://github.com/joshuaruesweg/php-src/pull/1 ===== Implementation ===== To be filled after merging: - Version merged into - Link to git commit(s) - Link to PHP manual entry ===== References ===== * JavaScript ''const'' declaration: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const * Swift ''let'' declaration: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/thebasics#Declaring-Constants-and-Variables ===== Rejected Features ===== None yet. ===== Changelog ===== * 2026-02-22: Initial draft published.