rfc:declare_vars

PHP RFC: Variable declarations before usage

Introduction

To allow for more strictness and type safety if a user wishes to have it, we need a way to make variable declarations explicit. When enabled, accessing an undeclared variable causes a compile-time error, regardless of whether the user is trying to read or write the variable. This RFC proposes a way to declare variables and to make the process optional for users.

Proposal

Declaring variables

Variables can be declared by using the var keyword:

var $variable;

This initialises the variable with the value NULL. It is equivalent to the following statement:

$variable = null;

Once a variable has been declared, redeclaring it triggers a compile-time error:

var $variable;
var $variable; // Compile error: "Cannot redeclare variable $variable"

When declaring a variable, users may also initialise the variable:

var $variable = 'Initial Value';

Calling unset on a variable removes the declared variable from the current scope:

var $variable;
unset($variable);
var_dump($variable); // Notice: Undefined variable $variable

Requiring variables to be declared before accessing them

With the new declare_vars directive, the engine will throw a compile error when trying to access a variable that hasn't been declared:

declare(declare_vars=1);
$variable = 'value'; // Compile error: "Undeclared variable: $variable"
var_dump($otherVariable); // Compile error: "Undeclared variable: $otherVariable"

This is an alternative to the exception suggested in the Reclassifying engine warnings RFC when reading from an undefined variable. It further disallows implicitly declaring variables, allowing users to switch to this strict mode.

When variable declarations are required, variables can no longer be removed. Calling unset leads to a compile time error:

declare(declare_vars=1);
var $variable;
unset($variable); // Compile error: Cannot unset declared variable

The declare(declare_vars=1) statement must not use block mode. Doing so leads to a compile error:

declare(declare_vars=1) {
}

Dynamic variables

Dynamic variables are evaluated at runtime. They can be declared and accessed as any other variables, but will throw an UndeclaredVariableError when trying to access a dynamic variable that hasn't been declared. Redeclaring a dynamic variable leads to a RedeclaredVariableError, while trying to unset a declared variable triggers an IllegalUnsetError. These errors extend the Error class.

declare(declare_vars=1);
var $variableName = 'foo';
var $$variableName = 'value';
 
var_dump($$variableName); // 'value'
var_dump($$$variableName); // UndeclaredVariableError: Undeclared variable $value
 
var $$variableName; // RedeclaredVariableError: "Cannot redeclare variable $foo"
unset($$variableName); // IllegalUnsetError: "Declared var $foo may not be unset"

Why use a keyword for variable declarations?

The var keyword has been chosen as it already exists, which avoids introducing another keyword to the language (e.g. let). The var keyword is also necessary to differentiate explicit from implicit declarations. Most importantly, the var keyword is necessary since a possible future introduction of typed variables could create an ambiguity in the parser:

require $file;

This could either be a require statement that requires the file stored in $file, or a variable declaration for $file of type require (which is a valid class name in PHP 7). Using a keyword to mark a variable declaration removes this ambiguity.

Backward Incompatible Changes

No BC breaks are introduced by this RFC. The declare_vars directive is optional to prevent breaking existing implicit variable declarations or accessing undefined variables.

Proposed PHP Version(s)

PHP 8.0

Open Issues

None

Future Scope

Thie RFC will be followed by a number of other RFCs to expand the functionality of explicit declarations. These are not part of the RFC; please limit discussion to the scope of this RFC.

Typed variables

In the future, variable declarations may optionally add a type to the variable:

var ?string $string;
var ?int = null;
var My\Object = new My\Object();

Lexical scopes

A future RFC could introduce lexical scopes, allowing to better control where variables can be accessed:

var $unscopedVariable;
 
{
    var $scopedVariable;
}
 
var_dump($unscopedVariable); // NULL
var_dump($scopedVariable); // Compile error: "Undeclared variable $scopedVariable"

Disallow dynamic object properties

The variable declaration logic could also be applied to class properties, which would disallow dynamic properties. This is part of a separate RFC and will use a separate declare switch, as users may want to enforce declaration of variables in code that should also dynamic properties for their class.

Constant variables

A future RFC may add support for constant variables, which can't be reassigned:

const $foo = 'value';
$foo = 'other value'; // Compile error: "Can't assign value to constant variable $foo"

Proposed Voting Choices

A yes/no vote.

Patches and Tests

A prototype implementation is available at https://github.com/sgolemon/php-src/tree/sgolemon.declared-vars (work in progress)

Implementation

After the project is implemented, this section should contain

  1. the version(s) it was merged into
  2. a link to the git commit(s)
  3. a link to the PHP manual entry for the feature
  4. a link to the language specification section (if any)

References

Links to external references, discussions or RFCs

Rejected Features

Keep this updated with features that were discussed on the mail lists.

rfc/declare_vars.txt · Last modified: 2019/09/19 22:35 by alcaeus