Implemented
- Importing namespaced functions
Proposes to allow importing namespaced functions through a new `use function` sequence.
The proposal is to combine existing keywords to a new sequence that allows importing functions into a namespace. This should make namespaced functions less of a pain to use and discourage placing them in the global namespace. Since functions and classes are in separate namespaces, it is not feasible to use the use keyword for both, as it would likely result in conflicts and overhead. Instead of introducing a new keyword, it would be possible to combine use and function to a sequence. This new use function combo would work as follows:
All of this applies not only to functions, but also to namespaced constants. For consistency, a use const sequence should also be introduced, that does the same thing for constants:
<?php
namespace foo\bar {
function baz() {
return 'foo.bar.baz';
}
function qux() {
return baz();
}
}
namespace {
use function foo\bar\baz, foo\bar\qux;
var_dump(baz());
var_dump(qux());
}
?>
Just like classes, it should be possible to alias imported functions and constants:
<?php
namespace foo\bar {
const baz = 42;
}
namespace {
use const foo\bar\baz;
var_dump(baz);
}
?>
Functions can be treated as an extension of the language, or to be used to create a custom meta-language. An example of this is building a HTML tree, but this could be anything, really.
<?php
namespace {
use function foo\bar as foo_bar;
use const foo\BAZ as FOO_BAZ;
var_dump(foo_bar());
var_dump(FOO_BAZ);
}
?>
A HTML building DSL:
Avoiding noise and clutter is really important to make this usable and readable.
<?php
use function html\div, html\p, html\em;
$html = div(p('Some', em('Text')));
?>
- Constant Scalar Expressions
This RFC proposes adding support for Constant Scalar Expressions with support for constants being operands.
This RFC brings static scalar expressions to the parser. This allows places that only take static values (const declarations, property declarations, function arguments, etc) to also be able to take static expressions.
This can allow for writing far easier to understand code, by allowing for far more expressive code.
The main difference to Anthony's RFC is (apart from a few operators more) that constants can be involved in these scalar operations:
This also means that this is valid code:
<?php
const a = 1;
const b = a?2:100; // here the value of the constant "b" is dependent on the constant "a"
?>
As is this:
<?php
class Foo {
const FOO = 1 + 1;
const BAR = 1 << 1;
const GREETING = "HELLO";
const BAZ = self::GREETING." WORLD!"
}
?>
<?php
const BAR = 1;
function foo($a = 1 + 1, $b = 2 << 3, $c = BAR?10:100) {}
?>
- Remove calls from incompatible context
Calls from incompatible context deprecated in 5.6 (will be removed in next version).
- Dedicated syntax for variadic functions
This RFC introduces a dedicated syntax for variadic functions.
Currently variadic functions are implemented by fetching the function arguments using func_get_args(). The following code sample shows an implementation of a variadic function used to prepare and execute a MySQL query (I'll be making use of this example throughout the RFC):
There are two issues with the above approach:
<?php
class MySQL implements DB {
protected $pdo;
public function query($query) {
$stmt = $this->pdo->prepare($query);
$stmt->execute(array_slice(func_get_args(), 1));
return $stmt;
}
// ...
}
$userData = $db->query('SELECT * FROM users WHERE id = ?', $userID)->fetch();
?>
Firstly, by just looking at the function signature public function query($query) you cannot know that this is actually a variadic function. You'd think that the function can only run a normal query and doesn't support bound parameters.
Secondly, because func_get_args() returns *all* arguments passed to the function you first need to remove the $query parameter using array_slice(func_get_args(), 1).
This RFC proposed to solve these issues by adding a special syntax for variadic functions:
The ...$params syntax indicates that this is a variadic function and that all arguments after $query should be put into the $params array. Using the new syntax both of the issues mentioned above are solved.
<?php
class MySQL implements DB {
public function query($query, ...$params) {
$stmt = $this->pdo->prepare($query);
$stmt->execute($params);
return $stmt;
}
// ...
}
$userData = $db->query('SELECT * FROM users WHERE id = ?', $userID)->fetch();
?>
- phpdbg
Distribute phpdbg with PHP, a PHP debugger
- Argument unpacking
This RFC proposes a syntax for argument unpacking.
This RFC complements the variadics RFC. It introduces a syntax for unpacking arrays and Traversables into argument lists (also known as “splat operator”, “scatter operator” or “spread operator”).
As a usage example, consider a variadic method public function query($query, ...$params). You are provided a $query and an array of $params and want to call the method using these. Currently this is possible using call_user_func_array():
This RFC proposes a syntax for unpacking arguments directly in the call syntax:
<?php
all_user_func_array([$db, 'query'], array_merge(array($query), $params))
?>
<?php
db->query($query, ...$params)
?>
- Expectations
This RFC proposes adding a modern Expectation/Assertion API.
The assertion statement has the prototype:
At execution time, expression will be evaluated, if the result is false, an AssertionException will be thrown. In some cases, expression will be an expensive evaluation that you do not wish to execute in a production environment, assertions can therefore be disabled and enabled via the PHP_INI_ALL configuration setting zend.assertions. Disabling assertions will almost entirely eliminate the performance penalty making them equivalent to an empty statement.
<?php
oid assert (mixed $expression [, mixed $message])
?>
In any case, assertions should never be used to perform tasks required for the code to function, nor should they change the internal state of any object except where that state is used only by other assertions, these are not rules that are enforced by Zend, but are nonetheless the best rules to follow. If an object of a class which extends AssertionException is used for message, it will be thrown if the assertion fails, any other expression will be used as the message for the AssertionException. If no message is provided, the statement will be used as the message in AssertionException. If expression is a constant string, compatibility with the old API is employed, the string is compiled and used as the expression.
PHP programmers tend to document how their code is supposed to work in comments, this is a fine approach for generating automated documentation, but leaves us a little bewildered, and tired of digging through documentation at runtime when things go wrong:
Becomes:
<?php
if ($i % 3 == 0) {
// ...
} else if ($i % 3 == 1) {
// ...
} else { // We know ($i % 3 == 2)
// ...
}
?>
In a development environment, this forces the executor to make you aware of your mistake.
<?php
if ($i % 3 == 0) {
// ...
} else if ($i % 3 == 1) {
// ...
} else {
assert ($i % 3 == 2);
}
?>
Another good example for using assertions might be a switch block with no default case:
The above switch assumes that suit can only be one of four values, to test this assumption add the default case:
<?php
switch ($suit) {
case CLUBS:
/* ... */
break;
case DIAMONDS:
/* ... */
break;
case HEARTS:
/* ... */
break;
case SPADES:
/* ... */
break;
}
?>
The previous example highlights another general area where you should use assertions: place an assertion at any location you assume will not be reached. The statement to use is:
<?php
switch ($suit) {
case CLUBS:
/* ... */
break;
case DIAMONDS:
/* ... */
break;
case HEARTS:
/* ... */
break;
case SPADES:
/* ... */
break;
default:
assert (false, "Unrecognized suit passed through switch: {$suit}");
}
?>
Suppose you have a method that looks like:
<?php
ssert(false)
?>
The above code assumes that one of the iterations results in a return value being passed back to the caller of ::method(), to test this assumption:
<?php
public function method() {
for (/*...*/) {
if (/* ... */)
return true;
}
}
?>
Assertions allow the possibility to perform precondition and postcondition checks:
<?php
public function method() {
for (/*...*/) {
if (/* ... */)
return true;
}
assert(false);
}
?>
Becomes:
<?php
public function setResponseCode($code) {
$this->code = $code;
}
?>
The example above performs a precondition check on the code parameter.
<?php
public function setResponseCode($code) {
assert($code < 550 && $code > 100, "Invalid response code provided: {$code}");
$this->code = $code;
}
?>
- Automatic Property Initialization
This RFC proposes automatic assignments of constructor arguments to properties.
This proposal is an alternative to https://wiki.php.net/rfc/constructor-promotion. It uses a different syntactical approach to shorthand constructors borrowed from the Dart Language, in addition to suggesting variations of that approach.
Instead of writing:
You can just write:
<?php
class Point
{
private $x, $y;
public function __construct($x, $y)
{
$this->x = $x;
$this->y = $y;
}
}
?>
The two snippets are functionally equivalent and achieve the same.
<?php
class Point
{
private $x, $y;
public function __construct($this->x, $this->y);
}
?>
Quoting from the Dart Manual:
The proposal suggests to adopt this functionality to PHP, which is really two features:
allow for $this→foo as constructor arguments
allow for methodless constructors
- Named Parameters
This RFC proposes introducing named parameters.
Named arguments are a way to pass arguments to a function, which makes use of the parameter names rather than the position of the parameters:
The order in which the named arguments are passed does not matter. The above example passes them in the same order as they are declared in the function signature, but any other order is possible too:
<?php
// Using positional arguments:
array_fill(0, 100, 42);
// Using named arguments:
array_fill(start_index => 0, num => 100, value => 42);
?>
It is possible to combine named arguments with normal, positional arguments and it is also possible to specify only some of the optional arguments of a function, irregardless of their order:
<?php
rray_fill(value => 42, num => 100, start_index => 0)
?>
<?php
htmlspecialchars($string, double_encode => false);
// Same as
htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
?>
- Support for anonymous catches
This RFC proposes adding catch-statements without variables, and fully anonymous catches.
With the recent addition of the finally keyword for try-catch statements, PHP is now in line with many other languages such as C#, Python and Ruby (and probably many others). Those languages differ from PHP in that they don't require a variable to bind the exception in, and don't even require specifying a certain Exception class.
Thus, this RFC proposes to add syntax support for:
In this case, we don't need the exception object itself as the exception type is descriptive enough of what happened, and we do not need to log anything because the operation is tried again in 3 seconds. By not specifying a variable, we avoid the impression that we do actually need it. It also helps static analysis tools (e.g. as implemented in IDEs) more accurately discover unused variables.
<?php
try
{
$this->connectToServer();
}
catch (ConnectionLostException)
{
$this->retry(3);
}
?>
We can take this one step further, to also get rid of the class:
<?php
try
{
$this->connectToServer();
}
catch
{
$this->retry(3);
}
?>
- unset(): return bool if the variable has existed
Add to unset a return value if the deletion was successful
The purpose of this RFC is to make unset return something meaningful. People also shouldn't have to wonder why they can't use unset if it isn't as a standalone expression. This removes also an inconsistency: the function's return value is void, states the docs. It is until now the only function (language construct, function-like) (apart from echo, which already has an alternative: print) which has no return value.
Change unset()'s behaviour to:- return true if deletion was successful
- return false if deletion failed (e.g. there was nothing to delete or the deleting function has failed)
This is the way we do it today:
<?php
public function deleteKey ($key) {
if (unset($this->array[$key])) {
// do some cleanup
}
}
?>
But even now we can't be sure if the variable was unset, because the __unset() magic method may not have deleted it. With the patch __unset() can return a value (which is internally casted to bool) and inform the caller of unset() if the deletion was successful.
<?php
public function deleteKey ($key) {
if (isset($this->array[$key])) {
unset($this->array[$key]);
// do some cleanup
}
}
?>
- return true if deletion was successful
- Remove reliance of Zend API
A proposal to remove the use of the Zend API in PHP libraries.
Currently, PHP's interpreter, the Zend Engine, provides access to its internals via the Zend API. This RFC provides a rationale for this access, by removing the Zend API. This RFC does not describe how to remove access, and what to replace it with. That is described separately, in php_native_interface. The goals of this RFC are predicated on achieving the goals of php_native_interface.
- PHP Native Interface
Provide complete alternative to writing libraries interfaces using the Zend API libraries.
This describes the design of phpni, the PHP Native Interface. This design is in early stages.
- Remove any couping between the Zend Engine, extensions and SAPIs.
- Support all major use cases of the Zend API
- embedding within SAPIs
- proving access to C libraries
- providing the ability to rewrite performance sensitive code in C
- embedding within SAPIs
- Significantly simplify creation of extensions
- Allow other PHP implementations to use the same interface.
- This is intended to be a static process. That is, we are replacing the static compilation of extensions with another static process. We do not intend to simplify or support run-time binding, like JNI, JNA or libffi. Instead it is imagined the result will be a single libphplibs.a, which statically links into libphp5.so.
Lets take a simple example. Assume we have a C library XXX, with 3 functions, x, y and z. We'd like to expose this in user space as a class called MyXXX, with methods a and b. We create a file with the signatures of x, y and z:
extensions/xxx/sigs.h
int x (int, int);
void y (char*, int);
void z (char*, int);
We then write our user space code:
extensions/xxx/MyXXX.php
In order to interface between the PHP code and the C functions, a tool will be required to generate code. This tool will obviously be implementation specific. SWIG could be used to create this. Since the libraries would no longer use the Zend API, the tight coupling would be broken. It would now be possible to change major parts of the Zend engine without affecting the operation of any other part of PHP. It would no longer be necessary to know the Zend API to write extensions. Instead, only the API of the C library is necessary, and the interface can be created in PHP user code.
<?php
class MyXXX
{
function __construct ($username)
{
$this->username = $username;
}
function a ($w1, $w2)
{
$foo = \internals\XXX\x ($w1, $w2);
\internals\XXX\y ($this->username, $foo);
}
function b ($m1, $m2)
{
$foo = \internals\XXX\x ($m1, $m2);
\internals\XXX\z ($this->username, $foo);
return $foo;
}
}
?>
- Remove any couping between the Zend Engine, extensions and SAPIs.
- Function call chaining
Function call chaining implementation.
- Enum language structure
A proposal to add the enum language structure
Frequently developers need to produce code like this:
The proposal is that this could be written in a much more concise manner:
<?php
const LOG_LEVEL_DEBUG = 1,
LOG_LEVEL_INFO = 2,
LOG_LEVEL_WARNING = 3,
LOG_LEVEL_ERROR = 4;
// Or
class Tokens {
const T_IF = 258,
T_ELSE = 259,
T_WHILE = 260,
T_DO = 261;
}
?>
The keyword enum is reserved. An enum may be defined wherever a class or interface could be defined, and the same namespacing, naming and referencing restrictions apply.
<?php
enum {
LOG_LEVEL_DEBUG,
LOG_LEVEL_INFO,
LOG_LEVEL_WARNING,
LOG_LEVEL_ERROR
};
// Or
class Tokens {
enum {
T_IF = 258,
T_ELSE,
T_WHILE,
T_DO
};
}
?>
Unlike array declarations, however, a duplicate value causes a fatal error. An enum member may be referenced as EnumName::VALUE just like a class constant. A scalar may be (explicitly or implicitly) cast to an enum member:
<?php
namespace Cards;
enum Suit {
SPADES => '1',
HEARTS => 2,
DIAMONDS, // Will have value 3
CLUBS, // Will have value 4
}
var_dump( Suit::SPADES ); // enum(Cards\Suit)(1)
var_dump( Suit::SPADES == 1 ); // bool(true)
var_dump( Suit::SPADES === 1 ); // bool(true) - because the value is cast to an integer upon declaration
var_dump( Suit::SPADES == '1' ); // bool(true)
var_dump( Suit::SPADES === '1' ); // bool(false)
?>
<?php
// Using declaration of Suit above
var_dump( (Suit)1 ); // enum(Cards\Suit)(1)
var_dump( (Suit)true ); // enum(Cards\Suit)(1)
var_dump( (Suit)('one') ); // null
?>
- Making T_FUNCTION optional for method declarations
A proposal for removing the requirement to use T_FUNCTION in method declarations.
It is proposed that this is valid code:
While technically possible this RFC suggests that the following shall NOT be valid for keeping the code readable:
<?php
class Foo {
const C = 42;
private $var;
public bar() {
echo "Hello World";
}
}
$foo = new Foo();
$foo->bar();
?>
<?php
class Foo {
const C = 42;
private $var;
bar() {
echo "Hello World";
}
}
$foo = new Foo();
$foo->bar();
?>
- CLI options for strict and quiet modes
This RFC proposes a -W option to turn on maximum error reporting, and a -Q option for no error reporting.
- Skipping optional parameters
Skipping optional parameters in function calls
As PHP does not have named parameter support, a very common for function is to have many optional arguments, like this:
If we always use defaults, it's fine. But what if we need ot change $report_errors but don't care about the others? We'd have to find function definition and copy-paste all other defaults into the call, which is annoying, error-prone and may not do what you wanted if some of the defaults change.
<?php
unction create_query($where, $order_by, $join_type='', $execute = false, $report_errors = true) {...
?>
The proposal is to allow skipping optional arguments in a call, thus making them assume default values as they do when they are not provided, like this:
This means that $join_type and $execute are going to use defaults. Of course, if we ever get implementation of named parameters, it may also solve this problem, but until we do, this can be a partial solution.
<?php
reate_query("deleted=0", "name", default, default, /*report_errors*/ true)
?>
- Systemd socket activation
Systemd socket activation support for PHP-FPM
- Add a deprecated modifier for functions
Add a deprecated modifier for functions
This RFC proposes the addition of a “deprecated” modifier for methods and functions giving the ZEND_ACC_DEPRECATED flag to functions, thus throwing an E_DEPRECATED error when they are called.
Results in
<?php
deprecated function myFunction() {
// ...
}
myFunction();
?>
Deprecated: Function myFunction() is deprecated in ... on line 5
- Moving to an AST-based parsing/compilation process
Proposes to improve the current parsing/compilation process by introducing an AST as an intermediary structure
- Scalar Type Casting Magic Methods
This RFC proposes a set of new scalar type casting magic methods for classes.
This RFC and patch introduces 4 new magic methods:
- __toInt() - Called when casting an object explicitly to an integer (or passing it to an internal function which expects an integer)
- __toFloat() - Called when casting an object explicitly to a float (or passing it to an internal function which expects a float)
- __toArray() - Called when casting an object explicitly to an array (or passing it to an internal function which expects an array)
- __toScalar() - Called when using an object in an implicit scalar scope without type information (for example: $obj + 1).
- __toBool() - Called when casting an object explicitly to a boolean (or passing it to an internal function which expects a boolean)
- __toInt() - Called when casting an object explicitly to an integer (or passing it to an internal function which expects an integer)
- Isset/Set Operator
This RFC proposes new operators for handling unset/falsey variables.
?= Will be a new operator that allows the user to set an unset or falsey variable. This handy operator will help avoid dreaded unset variable notices.
??: will be equivalent to the ternary short hand ?: except that it also checks for isset().
<?php
$foo ?= 'default';
// which is functionally equivalent to:
$foo = (isset($foo) && $foo) ? $foo : 'default';
// or
if (!isset($foo) || !$foo) $foo = 'default';
?>
This will be very helpful for echoing default variables in HTML like so:
<?php
// $bar is unset
$foo = $bar ?: 'other'; // Throws undefined notice
$foo = $bar ??: 'other'; // Does NOT throw undefined notice
// ??: is functionally equivalent to:
$foo = (isset($bar) && $foo) ? $bar : $other;
?>
<?php
div class="= $user ??: 'guest' "> ... </div
?>
- Comparison inconsistency
This RFC is to discuss comparison related inconsistency.
- PDO version 1 improvements
This RFC proposes a set of improvements to be made to PDO.
- Tainted variables
This RFC proposes an implementation of variable tainting in PHP.
- Jsonable interface
Feature request to allow support json_(encode|decode) to handle Object instance conversions.
Consider this piece of code:
PHP is able to convert public variables, so the return of this script will be:
<?php
class Person
{
public $name;
protected $age;
private $salary;
public function __construct($name, $age, $salary)
{
$this->name = $name;
$this->age = $age;
$this->salary = $salary;
}
// ...
}
$person = new \Person('Jesus Christ', 32, 10000);
echo json_encode($person);
?>
However, we may want json_(encode|decode) to also export/import protected and private information.
<?php
"name": "Jesus Christ"
?>
A non-intrusive solution should be smooth just like SPL Serializable interface.
It is well known that at this stage, any developer is able to write a piece of code (a method) like the following one to include a similar support:
<?php
interface Jsonable
{
public function __toJson();
?>
By implementing natively Jsonable, it would be simply necessary to:
<?php
class Person implements Jsonable
{
public function __toJson()
{
$str = '{"__CLASS__": "' . get_class($this) . '"';
$reflClass = new \ReflectionClass($this);
foreach ($reflClass->getProperties() as $reflProperty) {
$reflProperty->setAccessible(true);
$value = $reflProperty->getValue($this);
$str .= ', ' . json_encode($reflProperty->getName()) . ': ';
if (is_object($value) && $value instanceof Jsonable) {
$str .= $value->__toJson();
} else if ( ! is_resource($value)) {
$str .= json_encode($value);
}
}
$str .= '}';
return $str;
}
}
?>
It would be even possible to add custom export support, example:
<?php
class Person
{
// ...
public function __toJson()
{
return array('name', 'age', 'salary');
}
}
?>
<?php
class Person
{
// ...
public function __toJson()
{
return array('salary');
}
}
?>
- Object and Array Literals
This RFC proposes first-class, inline object and array literals similar to JSON.
<?php
// new syntax for simple arrays:
$a = [1,2,'three'];
// equivalent to current:
$a = array(1,2,'three');
// associative arrays:
// (examples are equivalent; see discussion)
$a = ['one' => 1, 'two' => 2, 'three' => 'three'];
$a = ['one': 1, 'two': 2, 'three': 'three'];
// equivalent to current:
$a = array('one' => 1, 'two' => 2, 'three' => 'three');
// anonymous object:
// (examples are equivalent; see discussion)
$a = {'one': 1, 'two': 2, 'three': 3};
$a = {'one' => 1, 'two' => 2, 'three' => 3};
// equivalent to:
$a = new \StdClass;
$a->one = 1; $a->two = 2; $a->three = 'three';
// or:
$a = (object)array('one' => 1, 'two' => 2, 'three' => 'three');
// PHP conventions (dynamic keys/values)
$val = 'apple';
$record = {"favourite_fruit": $val};
// true expression:
$record->favourite_fruit == "apple";
$key = "colour";
$record = {$key: "red"};
echo $record->colour; // outputs "red"
$colour = "green";
$vehicle = "truck";
$record = {'notes': "Drives a {$colour} $vehicle."};
echo $record->notes; // outputs "Drives a green truck."
// inline functions:
$creditCard = '5105105105105100';
$doc = {"credit_card_reminder": substr($creditCard, -4)};
echo $doc->credit_card_reminder; // outputs "5100"
// 'invalid' keys:
$obj = {'key with spaces': 'still works'};
echo $obj->{'key with spaces'}; // outputs 'still works'
$doc = {'$set': {"has_logged_in": 'yes'}};
echo $doc->{'$set'}->has_logged_in; // outputs "yes"
?>
- Weak References
This RFC proposes the introduction of Weak References in PHP via a new SPL class.
- Line Markers in PHP
This RFC proposes the introduction of line markers in PHP.
- Loop+Else control structure
This RFC proposes an optional “else” clause that can be used after while, for, and foreach loops.
Proposed syntax will look as follows:
<?php
// "foreach" example (display a list of names)
foreach ($array as $x) {
echo "Name: {$x->name}\n";
} else {
echo "No records found!\n";
}
// "for" example (unset a range of keys from array, then return it, or return null on error)
for ($i = some_very_expensive_and_ugly_looking_calculation($array); $i >= 0; $i--) {
unset($array[$i]);
} else {
return null; // horrific error!
}
return $array;
// "while" example (return true if any $search matches have been removed from $array)
while ($temp = array_search($search, $array)) {
unset($array[$temp]);
} else {
unset($array[$search]); // just because we can
return false;
}
return true;
?>