Key Takeaways
- PHP 8.1 simplifies the syntax for creating a callable to
AnyCallableExpression(...)
. - PHP 8.0 introduces named function parameters in addition to positional arguments.
- PHP 8.1 introduces Fibers as interruptible functions to facilitate multi-tasking.
- PHP 8 adds new standard library functions, including
__toString()
, and sets new requirements on the use of magic methods. - Private methods may be inherited and reimplemented without any restrictions, except for private final constructors that must be kept as such.
This article is part of the article series "PHP 8.x". You can subscribe to receive notifications about new articles in this series via RSS. PHP continues to be one of the most widely used scripting languages on the web with 77.3% of all the websites whose server-side programming language is known using it according to w3tech. PHP 8 brings many new features and other improvements, which we shall explore in this article series. |
PHP 8.0 adds support for several functions- and methods-related features, some of which are an improvement of existing features, while others are completely new features. The enhanced callable syntax in PHP 8.1 can be used to create anonymous functions from a callable. Named function arguments may be used along with positional arguments with the added benefit that named arguments are not ordered and can convey meaning by their name. Fibers are interruptible functions that add support for multitasking.
Inheritance on private methods is redefined
Object inheritance is a programming paradigm that is used by most object-oriented languages including PHP. It makes it possible to override public and protected methods, and class properties and constants defined in a class from any class that extends it. In PHP, public methods cannot be reimplemented with a more restrictive access such as by making a public
method private
. To demonstrate this, consider a class B that extends class A and reimplements a public method from A.
<?php
class A{
public function sortArray():string{
return "Class A method";
}
}
class B extends A{
private function sortArray():string{
return "Class B method";
}
}
$b=new B();
echo $b->sortArray();
When run, the script generates an error message:
Fatal error: Access level to B::sortArray() must be public (as in class A)
public method cannot be reimplemented.
On the contrary, private methods defined in a class are not inherited and can be reimplemented in a class that extends it. As an example, class B extends class A in the following script and reimplements a private method from A.
<?php
class A{
private function sortArray():string{
return "Class A method";
}
}
class B extends A{
private function sortArray(int $a):string{
return "Class B method";
}
}
Prior to PHP 8.0, two restrictions applied to private method redeclaration in an extending class: the final
and static
modifiers were not allowed to be changed. If a private
method was declared final
, an extending class was not allowed to redeclare the method. If a private method was declared static, it was to be kept static in an extending class. And, if a private method did not have the static modifier, an extending class was not allowed to add a static modifier. Both restrictions have been lifted in PHP 8. The following script runs ok in PHP 8.
<?php
class A {
private final static function sortArray():string{
return "Class A method";
}
}
class B extends A {
private function sortArray(int $a):string{
return "Class B method";
}
}
The only private method restriction in PHP 8 is to enforce private final
constructors, which are sometimes used to disable the constructor when using static factory methods as a substitute.
<?php
class A {
private final function __construct(){
}
}
class B extends A {
private final function __construct(){
}
}
The script generates error message:
Fatal error: Cannot override final method A::__construct()
A variadic argument may replace any number of function arguments
In PHP 8, a single variadic argument may replace any number of function arguments. Consider the following script in which class B extends class A and replaces the three-argument function sortArray
with a single variadic argument.
<?php
class A {
public function sortArray(array $arrayToSort, string $sortType, int $arraySize) {
if ($sortType == "asc") {
sort($arrayToSort);
foreach ($arrayToSort as $key => $val) {
echo "$key = $val ";
}
} elseif ($sortType == "desc") {
rsort($arrayToSort);
foreach ($arrayToSort as $key => $val) {
echo "$key = $val ";
}
}
}
}
class B extends A {
public function sortArray(...$multiple) {
$arrayToSort= $multiple[0];
$sortType=$multiple[1];
if ($sortType == "asc") {
sort($arrayToSort);
foreach ($arrayToSort as $key => $val) {
echo "$key = $val ";
}
} elseif ($sortType == "desc") {
rsort($arrayToSort);
foreach ($arrayToSort as $key => $val) {
echo "$key = $val ";
}
}
}
}
The sortArray
function in class B may be called using multiple arguments.
$sortType="asc";
$arrayToSort=array("B", "A", "f", "C");
$arraySize=4;
$b=new B();
$b->sortArray($arrayToSort,$sortType,$arraySize);
The output is as follows:
0 = A 1 = B 2 = C 3 = f
Simplified Callable Syntax
A callable is a PHP expression that can be called, such as an instance method, a static method, or an invocable object. A callable can be used to create a short-form expression for a method call, for example. In PHP 8.1, there is new callable syntax available:
AVariableCallableExpression(...)
The AVariableCallableExpression represents a variable callable expression. The ellipses … is included in the syntax.
Why a new callable syntax? Let’s recall what the traditional callable syntax looks like with some examples:
$f1 = 'strlen'(...);
$f2 = [$someobj, 'somemethod'](...);
$f3 = [SomeClass::class, 'somestaticmethod'](...);
This has two issues:
- The syntax involves strings and arrays
- The scope is not maintained at the point at which the callable is created.
To demonstrate this, consider the following script for sorting an array in which the getSortArrayMethod()
method returns a callable for the sortArray()
method with return [$this, 'sortArray']
.
<?php
class Sort {
private array $arrayToSort;
private string $sortType;
public function __construct($arrayToSort,$sortType)
{
$this->arrayToSort = $arrayToSort;
$this->sortType = $sortType;
}
public function getSortArrayMethod() {
return [$this, 'sortArray'];
}
private function sortArray() {
if ($this->sortType == "Asc") {
sort($this->arrayToSort);
foreach ($this->arrayToSort as $key => $val) {
echo "$key = $val ";
}
} elseif ($this->sortType == "Desc") {
rsort($this->arrayToSort);
foreach ($this->arrayToSort as $key => $val) {
echo "$key = $val ";
}
} else {
shuffle($this->arrayToSort);
foreach ($this->arrayToSort as $key => $val) {
echo "$key = $val ";
}
}
}
}
$sortType="Asc";
$arrayToSort=array("B", "A", "f", "C");
$sort = new Sort($arrayToSort,$sortType);
$c = $sort->getSortArrayMethod();
$c();
The script generates an error message:
Fatal error: Uncaught Error: Call to private method Sort::sortArray()
from global scope
Using Closure::fromCallable([$this, 'sortArray'])
instead of [$this, 'sortArray']
would fix the scope issue, but using the Closure::fromCallable
method makes the call verbose. The new callable syntax fixes both the scope and syntax verbosity issues. With the new callable syntax, the function becomes:
public function getSortArrayMethod() {
return $this->sortArray(...);
}
The array gets sorted with output:
0 = A 1 = B 2 = C 3 = f
The new syntax can be combined with the traditional syntax involving strings and arrays to fix the scope issue. The scope at which the callable is created is kept unchanged.
public function getSortArrayMethod() {
return [$this, 'sortArray'](...);
}
The new callable syntax may be used with static methods as well, as demonstrated by the following script that includes a static function.
<?php
class Sort {
private array $arrayToSort;
private string $sortType;
public function __construct($arrayToSort,$sortType)
{
$this->arrayToSort = $arrayToSort;
$this->sortType = $sortType;
}
public function getStaticMethod() {
return Sort::aStaticFunction(...);
}
private static function aStaticFunction() {
}
}
$sortType="Asc";
$arrayToSort=array("B", "A", "f", "C");
$sort = new Sort($arrayToSort,$sortType);
$cStatic=$sort->getStaticMethod();
$cStatic();
The output is the same as before:
0 = A 1 = B 2 = C 3 = f
The following are equivalent ways for calling a method:
return $this->sortArray(...);
return Closure::fromCallable([$this, 'sortArray']);
return [$this, 'sortArray'](...);
The following are equivalent ways for calling a static method:
return Sort::aStaticFunction(...);
return [Sort::class, 'aStaticFunction'](...);
return Closure::fromCallable([Sort::class, 'aStaticFunction']);
The new callable syntax may be used even if a function declares parameters.
<?php
class Sort {
private array $arrayToSort;
private string $sortType;
public function __construct($arrayToSort,$sortType)
{
$this->arrayToSort = $arrayToSort;
$this->sortType = $sortType;
}
public function getSortArrayMethod() {
return $this->sortArray(...);
}
private function sortArray(int $a,string $b) {
if ($this->sortType == "Asc") {
sort($this->arrayToSort);
foreach ($this->arrayToSort as $key => $val) {
echo "$key = $val ";
}
} elseif ($this->sortType == "Desc") {
rsort($this->arrayToSort);
foreach ($this->arrayToSort as $key => $val) {
echo "$key = $val ";
}
} else {
shuffle($this->arrayToSort);
foreach ($this->arrayToSort as $key => $val) {
echo "$key = $val ";
}
}
}
}
A callable must be called with its arguments if the method declares any.
$sortType="Asc";
$arrayToSort=array("B", "A", "f", "C");
$sort = new Sort($arrayToSort,$sortType);
$c = $sort->getSortArrayMethod();
$c(1,"A");
Simplified Syntax can be used with any PHP Callable expression
The simplified callable syntax may be used with any PHP callable expression. The callable syntax is not supported with the new operator for object creation because the callable syntax AVariableCallableExpression(...) does not have provision to specify constructor args, which could be required. The following is an example that is not supported:
$sort = new Sort(...);
An error message is generated:
Fatal error: Cannot create Closure for new expression
The following script demonstrates the full range of callable expressions that are supported.
<?php
class Sort {
private array $arrayToSort;
private string $sortType;
public function __construct($arrayToSort,$sortType)
{
$this->arrayToSort = $arrayToSort;
$this->sortType = $sortType;
}
public function getSortArrayMethod() {
return $this->sortArray(...);
}
public function getStaticMethod() {
return Sort::aStaticFunction(...);
}
public static function aStaticFunction() {
}
public function sortArray(int $a,string $b) {
if ($this->sortType == "Asc") {
sort($this->arrayToSort);
foreach ($this->arrayToSort as $key => $val) {
echo "$key = $val ";
}
} elseif ($this->sortType == "Desc") {
rsort($this->arrayToSort);
foreach ($this->arrayToSort as $key => $val) {
echo "$key = $val ";
}
} else {
shuffle($this->arrayToSort);
foreach ($this->arrayToSort as $key => $val) {
echo "$key = $val ";
}
}
}
public function __invoke() {}
}
$sortType="Asc";
$arrayToSort=array("B", "A", "f", "C");
$classStr = 'Sort';
$staticmethodStr = 'aStaticFunction';
$c1 = $classStr::$staticmethodStr(...);
$methodStr = 'sortArray';
$sort = new Sort($arrayToSort,$sortType);
$c2 = strlen(...);
$c3 = $sort(...); // invokable object
$c4 = $sort->sortArray(...);
$c5 = $sort->$methodStr(...);
$c6 = Sort::aStaticFunction(...);
$c7 = $classStr::$staticmethodStr(...);
// traditional callable using string, array
$c8 = 'strlen'(...);
$c9 = [$sort, 'sortArray'](...);
$c10 = [Sort::class, 'aStaticFunction'](...);
$c11 = $sort->getSortArrayMethod();
$c11(1,"A");
$cStatic=$sort->getStaticMethod();
$cStatic();
Trailing comma and optional/required arguments order
Another new feature in PHP 8.0 is support for adding a trailing comma at the end of the list of parameters to a function to improve readability. Any trailing comma is ignored. A trailing comma may not always be useful, but could be useful if the parameters list is long or if parameter names are long, making it suitable to list them vertically. A trailing comma is also supported in closure use lists.
PHP 8.0 deprecates declaring optional arguments before required arguments. Optional arguments declared before required arguments are implicitly required.
The following script demonstrates the required parameters implicit order in addition to the use of trailing comma.
<?php
function trailing_comma_example(
$the_very_first_arg_of_this_function,
$the_second_arg_of_this_function,
$the_third_arg_of_this_function = 1,
$the_fourth_arg_of_this_function,
$the_last_arg_of_this_function,
){ echo $the_third_arg_of_this_function; }
trailing_comma_example(1,2,null,3,4)
?>
The output is as follows:
Deprecated: Optional parameter $the_third_arg_of_this_function declared before required parameter $the_last_arg_of_this_function is implicitly treated as a required parameter
Nullable parameters are not considered optional parameters and may be declared before required parameters using the $param = null
form or the explicit nullable type, as in the following script:
<?php
class A {}
function fn1(A $a = null, $b) {}
function fn2(?A $a, $b) {}
fn1(new A,1);
fn2(null,1);
?>
Named Function Parameters and Arguments
PHP 8.0 adds support for named function parameters and arguments besides already supported positional parameters and arguments. Named arguments are passed in a function call with the following syntax:
Argument_name:Argument_value
Some of the benefits of named arguments are the following:
- Function parameters may be given a meaningful name to make them self-documenting
- Arguments are order-independent when passed by name
- Default values may be skipped arbitrarily.
In the following script, the array_hashtable
function declares named parameters. The function may be passed argument values with or without argument names. When positional arguments are passed, the function parameters declaration order is used. When named arguments are passed, any arbitrary order may be used.
<?php
function array_hashtable($key1,$key2,$key3,$key4,$key5){
echo $key1.' '.$key2.' '.$key3.' '.$key4.' '.$key5;
echo "<br>";
}
// Using positional arguments:
array_hashtable(0, 10, 50, 20, 25);
// Using named arguments:
array_hashtable(key2: 0, key5: 25, key1: 10, key4: 50, key3: 20);
?>
The output is:
0 10 50 20 25
10 0 20 50 25
Named arguments and positional arguments may be used in the same function call. The mixed arguments call is used with the same example function array_hashtable
.
<?php
function array_hashtable($key1,$key2,$key3,$key4,$key5){
echo $key1.' '.$key2.' '.$key3.' '.$key4.' '.$key5;
echo "<br>";
}
// Using mixed arguments:
array_hashtable(0, 10, 50, key5: 25, key4: 20);
?>
Output is :
0 10 50 20 25
Notice that named arguments are used only after positional arguments. The following script reverses the order and uses positional arguments after named arguments:
<?php
function array_hashtable($key1,$key2,$key3,$key4,$key5){
echo $key1.' '.$key2.' '.$key3.' '.$key4.' '.$key5;
echo "<br>";
}
// Using mixed arguments:
array_hashtable(0, 10, key3: 25, 50, key5: 20);
?>
The script generates an error message:
Fatal error: Cannot use positional argument after named argument
Declaring optional arguments before required arguments is deprecated even with named arguments, as demonstrated by the following script:
<?php
function array_hashtable($key1=0,$key2=10,$key3=20,$key4,$key5){
echo $key1.' '.$key2.' '.$key3.' '.$key4.' '.$key5;
echo "<br>";
}
// Using mixed arguments:
array_hashtable(1,2,key3: 25, key4: 1,key5: 20);
?>
The output includes the deprecation messages:
Deprecated: Optional parameter $key1 declared before required parameter $key5 is implicitly treated as a required parameter
Deprecated: Optional parameter $key2 declared before required parameter $key5 is implicitly treated as a required parameter
Deprecated: Optional parameter $key3 declared before required parameter $key5 is implicitly treated as a required parameter
When using optional named parameters after required named parameters, named arguments may be used to skip over one or more optional parameters in a function call, as in the script:
<?php
function array_hashtable($key1,$key2,$key3=20,$key4=50,$key5=10){
echo $key1.' '.$key2.' '.$key3.' '.$key4.' '.$key5;
echo "<br>";
}
// Using mixed arguments:
array_hashtable(key1:1, key2:2,key4: 25);
?>
The output is:
1 2 20 25 10
You may call a function with only a subset of its optional arguments, regardless of their order.
<?php
function array_hashtable($key1=0,$key2=10,$key3=20,$key4=50,$key5=10){
echo $key1.' '.$key2.' '.$key3.' '.$key4.' '.$key5;
echo "<br>";
}
// Using mixed arguments:
array_hashtable(1,2,key4: 25);
?>
Output is as follows:
1 2 20 25 10
Even when calling a function with a subset of its optional arguments, positional arguments cannot be used after named arguments, as demonstrated by script:
<?php
function array_hashtable($key1=0,$key2=10,$key3=20,$key4=50,$key5=10){
echo $key1.' '.$key2.' '.$key3.' '.$key4.' '.$key5;
echo "<br>";
}
// Using mixed arguments:
array_hashtable(1,2,key4: 25,5);
?>
The following error message is produced:
Fatal error: Cannot use positional argument after named argument
PHP 8.1 improves on the named arguments feature by supporting named arguments after unpacking the arguments, as in the script:
<?php
function array_hashtable($key1,$key2,$key3=30,$key4=40,$key5=50){
echo $key1.' '.$key2.' '.$key3.' '.$key4.' '.$key5;
echo "<br>";
}
echo array_hashtable(...[10, 20], key5: 40);
echo array_hashtable(...['key2' => 2, 'key1' => 2], key4: 50);
?>
The output is as follows:
10 20 30 40 40
2 2 30 50 50
However, a named argument must not overwrite an earlier argument as demonstrated by script:
<?php
function array_hashtable($key1,$key2,$key3=30,$key4=40,$key5=50){
echo $key1.' '.$key2.' '.$key3.' '.$key4.' '.$key5;
echo "<br>";
}
echo array_hashtable(...[10, 20], key2: 40);
?>
Output is as follows:
Fatal error: Uncaught Error: Named parameter $key2 overwrites previous argument.
Non-static method cannot be called statically
Prior to PHP 8.0, if you called a non-static method in a static context, or statically, you only got a deprecation message. With 8.0 you now get an error message. Also, $this
is undefined in a static context. To demonstrate this, consider the following script in which a non-static method aNonStaticMethod()
is called with static syntax A::aNonStaticMethod()
.
<?php
class A
{
function aNonStaticMethod()
{
}
}
A::aNonStaticMethod();
If you run the script, you would get an error message:
Uncaught Error: Non-static method A::aNonStaticMethod() cannot be called statically
Fibers
PHP 8.1 adds support for multi-tasking with Fibers. A Fiber is an interruptible function with a stack of its own. A Fiber may be suspended from anywhere in the call stack, and resumed later. The new Fiber class is a final class that supports the following public methods:
Method |
Description |
|
Constructor to create a new Fiber instance. The parameter is the callable to invoke when starting the fiber. Arguments given to |
|
Starts the fiber. The method returns when the fiber suspends or terminates. A variadic list of arguments is provided to the callable used when constructing the fiber. A mixed value is returned from the first suspension point or |
resume(mixed $value = null): mixed |
Resumes the fiber, returning the given mixed value from Fiber::suspend(). Returns when the fiber suspends or terminates. The returned mixed value is actually returned from the next suspension point or NULL if the fiber returns. Throws a FiberError if the fiber has not started, is running, or has terminated. |
throw(Throwable $exception): mixed |
Throws the given exception into the fiber from |
getReturn(): mixed |
Gets the mixed return value of the fiber callback. |
isStarted(): bool |
Returns bool |
isSuspended(): bool |
Returns bool |
isRunning(): bool |
Returns bool |
isTerminated(): bool |
Returns bool |
static suspend(mixed $value = null): mixed |
Suspends the fiber. Returns execution to the call to Throws FiberError if not within a fiber (i.e., if called from the main thread). |
static getCurrent(): ?Fiber |
Returns the currently executing fiber instance or |
A Fiber may be started only once, but may be suspended and resumed multiple times. The following script demonstrates multitasking by using a Fiber to perform different types of sorts on an array. The Fiber is suspended after each sort, and resumed later to perform a different type of sort.
<?php
$fiber = new Fiber(function (array $arr): void {
sort($arr);
foreach ($arr as $key => $val) {
echo "$key = $val ";
}
echo "<br/>";
Fiber::suspend();
rsort($arr);
foreach ($arr as $key => $val) {
echo "$key = $val ";
}
echo "<br/>";
Fiber::suspend();
shuffle($arr);
foreach ($arr as $key => $val) {
echo "$key = $val ";
}
});
$arrayToSort=array("B", "A", "f", "C");
$value = $fiber->start($arrayToSort);
$fiber->resume();
$fiber->resume();
?>
The output is as follows:
0 = A 1 = B 2 = C 3 = f
0 = f 1 = C 2 = B 3 = A
0 = C 1 = f 2 = A 3 = B
If the Fiber is not resumed after first suspension, only one type of sort is made, which could be implemented by commenting out the two calls to resume().
//$fiber->resume();
//$fiber->resume();
Output is the result from the first sort:
0 = A 1 = B 2 = C 3 = f
Stringable interface and __toString()
PHP 8.0 introduces a new interface called Stringabl
e that provides only one method __toString()
. The __toString()
method if provided in a class would implicitly implement the Stringable
interface. Consider the class A that provides a __toString()
method.
<?php
class A {
public function __toString(): string {
return " ";
}
}
echo (new A() instanceof Stringable);
The script returns 1 from the type check for Stringable
.
The reverse is however not true. If a class implements the Stringable interface, it must explicitly provide the __toString() method as the method is not added automatically, as in:
<?php
class A implements Stringable {
public function __toString(): string { }
}
New standard library functions
PHP 8 introduces a number of new functions belonging to its standard library.
The str_contains
function returns a bool
to indicate if the string given as the first argument contains the string given as the second argument. The following script returns false
:
<?php
if (str_contains('haystack', 'needle')) {
echo true;
} else {
echo false;
}
And the following script returns 1, or true:
<?php
if (str_contains('haystack', 'hay')) {
echo true;
}else {
echo "false";
}
The str_starts_with function returns a bool to indicate if the string given as the first argument starts with the string given as the second argument. The following script returns false.
<?php
if (str_contains('haystack', 'hay')) {
echo true;
}else {
echo "false";
}
And the following script returns 1, or true.
<?php
if (str_starts_with('haystack', 'needle')) {
echo true;
} else {
echo false;
}
The str_ends_with
function returns a bool
to indicate if the string given as the first argument ends with the string given as the second argument. The following script returns false
.
<?php
if (str_starts_with('haystack', 'needle')) {
echo true;
} else {
echo false;
}
And the following script returns 1
, or true
.
<?php
if (str_starts_with('haystack', 'hay')) {
echo true;
} else {
echo false;
}
The fdiv
function divides two numbers and returns a float
value, as demonstrated by the script:
<?php
var_dump(fdiv(1.5, 1.3));
var_dump(fdiv(10, 2));
var_dump(fdiv(5.0, 0.0));
var_dump(fdiv(-2.0, 0.0));
var_dump(fdiv(0.0, 0.0));
var_dump(fdiv(5.0, 1.0));
var_dump(fdiv(10.0, 2));
The output is:
float(1.1538461538461537)
float(5)
float(INF)
float(-INF)
float(NAN)
float(5)
float(5)
The fdatasync
function, aliased to fsync
on Windows, synchronizes data to a stream on a file. To demonstrate its use, create an empty file test.txt in the scripts
directory that contains PHP scripts to run. Run the script:
<?php
$file = 'test.txt';
$stream = fopen($file, 'w');
fwrite($stream, 'first line of data');
fwrite($stream, "\r\n");
fwrite($stream, 'second line of data');
fwrite($stream, 'third line of data');
fdatasync($stream);
fclose($stream);
Subsequently, open the test.txt file to find the text:
first line of data
second line of data
third line of data
The array_is_list
function returns a bool to indicate whether a given array is a list. The array must start at 0, the keys must be all consecutive integer keys and in the correct order. The following script demonstrates the array_is_list
function:
<?php
echo array_is_list([]);
echo array_is_list(['1', 2, 3]);
echo array_is_list([0 => 'a', 'b']);
echo array_is_list([1 => 'a', 'b']); // false
echo array_is_list([1 => 'a', 0 => 'b']); // false
echo array_is_list([0 => 'a', 'b' => 'b']); // false
echo array_is_list([0 => 'a', 2 => 'b']); // false
The output is:
1
1
1
Magic methods must have correct signatures
Magic methods are special methods in PHP to override default actions They include the following methods, of which the constructor method __construct()
may be the most familiar:
__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __serialize(), __unserialize(), __toString(), __invoke(), __set_state(), __clone(), and __debugInfo().
As of PHP 8.0 the signature of the magic method definitions must be correct, which implies that if type declarations are used in method parameters or return type, they must be identical to that in the documentation. The new __toString()
method must declare string
as return type. To demonstrate declare return type as int
:
<?php
class A {
public function __toString(): int {
}
}
An error message is generated:
Fatal error: A::__toString(): Return type must be string when declared
However, functions that don’t declare a return type by definition such as the constructor function must not declare a return type, not even a void
return type. The following script is an example:
<?php
class A {
public function __construct():void
{
}
}
The script returns an error message:
Fatal error: Method A::__construct() cannot declare a return type
All magic methods with a few exceptions, e.g. __construct()
, must be declared with public visibility. To demonstrate this, declare __callStatic
with private
visibility.
<?php
class A {
private static function __callStatic(string $name, array $arguments) {}
}
A warning message is output:
Warning: The magic method A::__callStatic() must have public visibility
Even though it is ok to omit mixed return types, the method signature must be the same. For example, in the following script, class A
declares __callStatic
without specifying its return type, while the class B
defines its first parameter as an int:
<?php
class A {
public static function __callStatic(string $name, array $arguments) {}
class B {
public static function __callStatic(int $name, array $arguments) {}
}
An error message is output:
Fatal error: B::__callStatic(): Parameter #1 ($name) must be of type string when declared
Return Type Compatibility with Internal Classes
With PHP 8.1 most internal methods, which are the methods in internal classes, have “tentatively” started to declare a return type. Tentatively implies that while in 8.1 only a Deprecation notice is raised, in version 9.0 an error condition message shall be output. Thus, any extending class must declare a return type that is compatible with the internal class, or a deprecation notice is issued. To demonstrate this, extend the internal class Directory and redeclare the function read() without a return type:
<?php
class A extends Directory {
public function read() { }
}
The script generates a deprecation notice:
Deprecated: Return type of A::read() should either be compatible with Directory::read(): string|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
The following script, however is OK:
<?php
class A extends Directory {
public function read():string { return ""; }
}
Adding the #[\ReturnTypeWillChange]
attribute attribute suppresses the deprecation notice:
<?php
class A extends Directory {
#[\ReturnTypeWillChange]
public function read() { }
}
The \SensitiveParameter attribute
While stack traces for exceptions that include detailed information about method parameters are quite useful for debugging, you may not want to output parameter values for some sensitive parameters such as those associated with passwords and credentials. PHP 8.2 introduces a new attribute called \SensitiveParameter so that, if a method parameter is annotated with the \SensitiveParameter
attribute, the parameter’s value is not output in an exception stack trace.
To demonstrate this, consider the following script in which the function f1
has the $password
parameter associated with the \SensitiveParameter
attribute.
<?php
function f1(
$param1 = 1,
#[\SensitiveParameter] $password = “s@5f_u7”,
$param3 = null
) {
throw new \Exception('Error');
}
The function throws an Exception
just to demonstrate the \SensitiveParameter
feature. Call the function:
f1(param3: 'a');
Notice that the exception stack trace does not include the value for the $password
parameter, and instead has Object(SensitiveParameterValue)
listed.
Stack trace: #0 : f1(1, Object(SensitiveParameterValue), 'a') #1 {main}
Built-in functions deprecation/enhancement
The built-in functions utf8_encode()
and utf8_decode()
have often been misunderstood because their names imply encoding/decoding just about any string. Actually the functions are for encoding/decoding only ISO8859-1, aka “Latin-1”, strings. Additionally, the error messages they generate are not descriptive enough for debugging. PHP 8.2 has deprecated these functions. The following script makes use of them:
<?php
$string_to_encode = "\x7A\x6A\xdB";
$utf8_string = utf8_encode($string_to_encode);
echo bin2hex($utf8_string), "\n";
$utf8_string = "\x6A\x6B\xD3\xCB";
$decoded_string = utf8_decode($utf8_string);
echo bin2hex($decoded_string), "\n";
With PHP 8.2, a deprecation notice is output:
Deprecated: Function utf8_encode() is deprecated
Deprecated: Function utf8_decode() is deprecated
In PHP 8.2, the functions iterator_count
and iterator_to_array
accept all iterables. The iterator_to_array()
function copies the elements of an iterator into an array. The iterator_count()
function counts the elements of an array. These functions accept an $iterator
as the first argument. In PHP 8.2, the type of the $iterator
parameter has been widened from Traversable
to Traversable|array
so that any arbitrary iterable value is accepted.
The following script demonstrates their use with both arrays
and Traversables
.
<?php
$a=array('1'=>'one', 'two', 'three', 'four');
$iterator = new ArrayIterator($a);
var_dump(iterator_to_array($iterator, true));
var_dump(iterator_to_array($a, true));
var_dump(iterator_count($iterator));
var_dump(iterator_count($a));
The output is as follows:
array(4) { [1]=> string(3) "one" [2]=> string(3) "two" [3]=> string(5) "three" [4]=> string(4) "four" }
array(4) { [1]=> string(3) "one" [2]=> string(3) "two" [3]=> string(5) "three" [4]=> string(4) "four" }
int(4)
int(4)
Summary
In this article in the PHP 8 series, we discussed the new features related to functions and methods, the most salient being named function parameters/arguments, a simplified callable syntax, and interruptible functions called Fibers.
In the next article in the series, we will cover new features for PHP’s type system.
This article is part of the article series "PHP 8.x". You can subscribe to receive notifications about new articles in this series via RSS. PHP continues to be one of the most widely used scripting languages on the web with 77.3% of all the websites whose server-side programming language is known using it according to w3tech. PHP 8 brings many new features and other improvements, which we shall explore in this article series. |