Key Takeaways
- To create named array constants at runtime, use the new
define()
function - To bind an object scope to a variable and invoke it, use the new
Closure::call()
function - To use an expression and/or a custom
AssertionError
with the traditionalassert()
, use expectations - PHP 7 supports returning a value from generator functions
- PHP 7 supports delegating from one generator function to another generator function
- For integer division, use the new function called
intdiv()
- To override the session configuration settings in
php.ini
, use the newsession_start
function - To perform a regular expression search and replace using callbacks, use the new function
preg_replace_callback_array()
- PHP 7 is able to generate cryptographically secure integers and bytes
- PHP 7.1 supports converting
Callables
toClosures
- Arrow (=>) functions provide a concise syntax for anonymous functions
PHP 7.x brings several improvements and new features that touch all aspects of the language, including better support for object oriented programming, extensions to classes and interfaces, improvements to the type system, error handling, and more. In this series of articles, we discuss new features across the various PHP 7.x versions.
In the preceding article in this series on PHP 7, we discussed new features in the PHP type system. In this article we explore improvements to functions in PHP 7.
PHP supports several types of functions including user-defined functions, internal functions, variable functions, and anonymous functions.
New Function to define Array Constants
A new function called define()
has been added to PHP 7.0 to define named array constants at runtime. The define() function has the syntax:
bool define ( string $name , mixed $value [, bool $case_insensitive = FALSE ] )
The function parameters are discussed in Table 1.
Table 1. Function define() Parameters
Parameter |
Description |
name |
The name of the constant. The name could be a reserved name but it is not recommended. |
value |
The value of the constant. The value must be a scalar value (integer, float, string, Boolean, or NULL) or an array. |
case_insensitive |
Whether the constant is case insensitive, the default being case sensitive. Case insensitive constants are deprecated in PHP 7.3.0. |
Create a PHP script constant.php and define a constant called CONSTANT
as follows.
define("CONSTANT", "Hello PHP");
You could also use the const
keyword to define a constant:
const CONSTANT_2 = 'Hello php';
const Constant = 'HELLO PHP';
The define()
function can be used to define an array constant:
define("Catalog", ['Oracle Magazine','Java Magazine']);
Array constant values may be output using array element access.
echo Catalog[0]
echo Catalog[1]
The constant.php file is listed:
<?php
define("CONSTANT", "Hello PHP");
echo CONSTANT."<br>";
const CONSTANT_2 = 'Hello php';
echo CONSTANT_2."<br>";
const Constant = 'HELLO PHP';
echo Constant."<br>";
define("Catalog", ['Oracle Magazine','Java Magazine']);
echo Catalog[0]."<br>";
echo Catalog[1]
?>
Run the script to output the constant values defined in it
Hello PHP
Hello php
HELLO PHP
Oracle Magazine
Java Magazine
A globally-defined constant such as TRUE
or FALSE
cannot be redefined. To demonstrate this, create a script const.php that defines a constant TRUE
setting its value to 20
:
<?php
define('TRUE', 20);
echo TRUE;
?>
If you run the script, it will output the value 1
, which is the globally-defined value for TRUE
.
New Function to Bind Object Scope to Closure
A Closure is a class used to represent an anonymous function. PHP 7.0 has introduced a new function Closure::call()
as a short-form of temporarily binding an object scope to a closure and invoking it. To demonstrate this, create a script closure.php and copy the following listing:
<?php
class Hello {
private function getMsg() {
echo "Hello";
}
}
$getMsg = function() {return $this->getMsg();};
echo $getMsg->call(new Hello);
?>
In the above script, Hello is a class with function getMsg()
. The Closure::call()
function is used to create a Hello
instance and bind its scope to a closure that invokes the getMsg
method. Run the script and the Hello
message is output.
Expectations
Before discussing expectations, let’s review traditional assertions. The traditional assert()
method is defined as follows. It will check if a code expectation (what is called an assertion) is FALSE
, and if FALSE
it prints the description message, and aborts the program by default. If an assertionis not FALSE
, assert()
has no effect.
bool assert ( mixed $assertion [, string $description ] )
Assertions are designed to be used for debugging during development and testing and not for runtime operations. The assert()
behavior is configured with assert_options()
or the .ini
file settings.
Traditionally, the first parameter to assert()
shall be a string to be evaluated or a boolean condition to be tested as an assertion. If a string is provided, it is evaluated as PHP code. If a boolean condition is provided, it is converted to a string before being passed to the assertion callback function, if any, defined in assert_options()
.
Assertions have been completely overhauled in PHP 7 and are now called expectations and assert()
is a language construct in PHP 7. Expectations have been added as an enhancement of assert(), with the following syntax:
bool assert ( mixed $assertion [, Throwable $exception ] )
With expectations, the first assert()
parameter may be an expression that returns a value, instead of a string of PHP code to be evaluated or a boolean to be tested. The expression is evaluated and the result used to ascertain if the assertion succeeded. The use of a string as a first argument is deprecated as of PHP 7. assert_options()
are still supported with expectations but are not recommended. Instead, two new configuration php.ini directives should be used, as detailed in table 2.
Table 2. Configuration Directives for Expectations
Configuration Directive |
Type |
Description |
Supported Values |
Default Value |
zend.assertions |
integer |
Configures whether assertion code is to be generated and run. |
1: Generate and run assertion code (development mode) 0: Generate assertion code but not run it at runtime -1: Do not generate assertion code (production mode). Use this setting to use expectations. |
1 |
assert.exception |
|
Throws an AssertionError or Custom exception for failed assertion |
1: throw when the assertion fails, either by throwing the object provided as the exception or by throwing a new AssertionError object if no exception was provided. Use this setting to use expectations. 0: use or generate a Throwable as described above, but only generate a warning rather than throwing an exception or AssertionError |
0 |
With expectations, the second argument may be a Throwable
object instead of a string description. For the Throwable
object or exception to be thrown when an assertion fails, the assert.exception
directive must be set to 1.
Expectations have the following advantages over traditional assertions, which are still supported for backward compatibility:
- An expression may be used for evaluating an assertion, instead of a string or a boolean.
- Using
php.ini
settings, the assertion code may be skipped at runtime, even though generated. Or the assertion code may not even be generated, which is recommended for production use. - Custom exceptions may be thrown. A new class
AssertionError
is added in PHP 7.
As an example of how to use expectations, create a script expectation.php and copy the following listing to the script. The script sets both configuration directives to 1. The assert language construct has the first argument set to true and the second argument set to a custom AssertionError
.
<?php
ini_set('assert.exception', 1);
ini_set('zend.assertions', 1);
assert(true, new AssertionError('Assertion failed.'));
?>
If you run the script, no AssertionError
is thrown.
Next, set the first arg to false
.
assert(false, new AssertionError('Assertion failed.'));
If you run the script again, the expectation fails and throws an AssertonError
.
Uncaught AssertionError: Assertion failed
As an example of using expectations with an expression and custom AssertionError
in assert()
, start with a traditional use of assert() that tests an assertion that a variable has a numeric value. Strings are used both to test the variable and output a message if the assertion fails.
$numValue = '123string';
assert('is_numeric_Value($numValue)' , "Assertion that $numValue is a number failed." );
Next, use an expression as the first argument to assert()
. And use an AssertionError
object to throw a custom error message.
$num = 10;
assert($num > 50 , new AssertionError("Assertion that $num is greater than 50 failed.") );
Return Expressions for Generators
Generator functions are functions that return an iterable object, such as foreach
. Generator functions are simple iterators in that they don’t require the class to implement the Iterator
interface. The syntax for generator functions is the same as for a normal function except that they include one or more yield
statements with each yield yielding a value when the iterator object returned by the generator function is iterated over. A generator function must be invoked as a normal function, providing any required arguments.
PHP 7.0 has added a new feature to the generator function, enabling to specify a return statement after all the yield statements. The value returned by a generator function may be accessed with the getReturn()
function of the object returned by the generator function. The iterator object returned by a generator function should not be confused with the value returned by a generator function. The iterator object returned does not include the return value, it only includes the yielded values. Being able to return a value is useful if a generator function needs to perform some computation and return a final value. Generators may only declare a return type of Generator, Iterator, Traversable
, or iterable
.
To demonstrate how to use this new feature, create a script script gen_return.php defining a generator function with multiple yield statements and invoke the generator function passing 1
as an argument. Use foreach to iterate over its return value and output the yielded values . Finally, output the return value using getReturn()
. The gen_return.php script is listed.
<?php
$gen_return = (function($var) {
$x=$var+2;
yield $var;
yield $x;
$y=$x+2;
return $y;
})(1);
foreach ($gen_return as $value) {
echo $value, PHP_EOL;
}
echo $gen_return->getReturn(), PHP_EOL;
?>
If you run the script, you should get the following output:.
1 3 5
Generators Delegation
PHP 7.0 has added support for generator delegation, which implies that one generator may delegate to another generator, Traversable
object or array using the yield from keyword. To demonstrate generator delegation, create a script gen_yield_from.php and define two generator functions gen($var)
and gen2($var), where gen($var)
delegates to gen2($var)
with the following statement:
yield from gen2($var);
Subsequently iterate over the iterator object returned by gen($var)
in a single foreach
loop. Script gen_yield_from.php is listed:
<?php
function gen($var)
{
yield $var;
$x=$var+2;
yield $x;
yield from gen2($var);
}
function gen2($var)
{
$y=$var+1;
yield $var;
yield $y;
}
foreach (gen(1) as $val)
{
echo $val, PHP_EOL;
}
?>
Run the script to output all the yielded values from both the generator functions:
1 3 1 2
New function for Integer Division
PHP 7.0 has added a new function intdiv()
for integer division. The function returns the integer quotient of the division between two integers and has the following syntax.
int intdiv ( int $dividend , int $divisor )
Create a script int_div.php to use the intdiv()
function. Add some examples of integer division. PHP constants such as PHP_INT_MAX
and PHP_INT_MIN
may be used as args to the function.
<?php
var_dump(intdiv(4, 2));
var_dump(intdiv(5, 3));
var_dump(intdiv(-4, 2));
var_dump(intdiv(-7, 3));
var_dump(intdiv(4, -2));
var_dump(intdiv(5, -3));
var_dump(intdiv(-4, -2));
var_dump(intdiv(-5, -2));
var_dump(intdiv(PHP_INT_MAX, PHP_INT_MAX));
var_dump(intdiv(PHP_INT_MIN, PHP_INT_MIN));
?>
If you run the script, you will get the following output:
nt(2) int(1) int(-2) int(-2) int(-2) int(-1) int(2) int(2) int(1) int(1)
ArithmeticErrors
, if any, are output in the browser. To demonstrate this, include the following function call and run the script again.
var_dump(intdiv(PHP_INT_MIN, -1));
In this case, an ArithmeticError
is generated indicating that division of PHP_INT_MIN
by -1
is not an integer:
Uncaught ArithmeticError: Division of PHP_INT_MIN by -1 is not an integer
Add the following function call to the int_div.php script and rerun the script.
var_dump(intdiv(1, 0));
This time, a DivisionByZeroError
is thrown:
Uncaught DivisionByZeroError: Division by zero
New Session Options
The session_start
function is used to start a new session or resume an existing session. PHP 7.0 has added support for a new parameter called options
, which is an associative array of options that override the session configuration directives settings in php.ini
. These session configuration directives start with “session
.” in php.ini
but the “session.
” prefix should be omitted in the session_start
’s options parameter array supplied as a function argument. In addition to session configuration directives, a new option read_and_close
has been added, which, if set to TRUE
, closes the session after being read, since keeping the session open may be unnecessary. As an example, create a script session_start.php and copy the following listing. The associative array in the example script has a session_start(options)
function call that sets some configuration directives:
<?php
session_start([
'name' => 'PHPSESSID',
'cache_limiter' => 'private',
'use_cookies' => '0'
]);
When you run this script, the session configuration options specified in it will override the session configuration directives defined in php.ini
, if any. The script does not generate any output.
With PHP 7.1 session_start()
returns FALSE
and does not initialize $_SESSION
when it fails to start the session.
New Function to perform Regular Expression Search and Replace Using Callbacks
A new function preg_replace_callback_array()
has been added in PHP 7.0 to perform a regular expression search and replace using callbacks. The function is similar to the preg_replace_callback()
function except that the callbacks are invoked on a per pattern basis. The function has the following syntax:
mixed preg_replace_callback_array ( array $patterns_and_callbacks , mixed $subject [, int $limit = -1 [, int &$count ]] )
It returns an array of strings if the $subject
parameter is an array and a single string if $subject
is a string. The array or string returned is the new subject if any matches are found or the unchanged subject if no matches are found. The function parameters are discussed in Table 3.
Table 3. Function Parameters for preg_replace_callback_array
Parameter |
Type |
Description |
$patterns_and_callbacks |
array |
Specifies an associative array mapping patterns (keys) to callbacks (values) |
$subject |
mixed |
Specifies the string or string array to search and replace |
$limit |
int |
Specifies a limit on maximum replacements for each pattern in each subject string. Defaults to -1 for no limit. |
&$count |
int |
The number of replacements completed is stored in the $count variable. |
To demonstrate this new functionality, create an example script prereg.php and copy the following listing to it. The subject or the example string to search is set to 'AAaaaaa Bbbbb'. The patterns_and_callbacks
arg is set to find the number of matches for ‘A’ and ‘b’.
<?php
$subject = 'AAaaaaa Bbbbb';
preg_replace_callback_array(
[
'~[A]+~i' => function ($match) {
echo strlen($match[0]), ' matches for "A" found', PHP_EOL;
},
'~[b]+~i' => function ($match) {
echo strlen($match[0]), ' matches for "b" found', PHP_EOL;
}
],
$subject
);
?>
If you run the script, the number of matches for ‘A’ and ‘b’ is printed out:
7 matches for "A" found 5 matches for "b" found
New Functions to generate Cryptographically Secure Integers and Bytes
Two new functions have been added to generate cryptographically secure integers and bytes. These functions are discussed in Table 4.
Table 4. New Cryptographic Functions
Function |
Syntax |
Parameter/s |
Return Value |
Description |
random_bytes() |
string random_bytes ( int $length ) |
$length, of type int, is the length of the random string to be returned as bytes. |
Returns a string containing the requested number of cryptographically secure random bytes. |
Generates and returns an arbitrary string containing cryptographic random bytes. |
random_int() |
int random_int ( int $min , int $max ) |
The $min parameter specifies the lower limit for the value to be returned, which must be PHP_INT_MIN or higher. The $max parameter specifies the upper limit for the value to be returned, which must be PHP_INT_MAX or lower. |
A cryptographically secure integer in between $min and $max. |
Generates and returns cryptographic random integers. |
As an example create a script random_int.php to generate cryptographic random integers. First, generate an integer in the range of 1 and 99, then generate another integer in the range of -100 and 0.
<?php
var_dump(random_int(1, 99));
var_dump(random_int(-100, 0));
?>
If you run the script, two integers will be printed out.
int(98) int(-84)
The integers generated are random and if the same script is run again it will generate most likely two different integers.
Next, create an example script random_bytes.php to generate cryptographic random bytes with length of 10. Then, use the bin2hex
function to convert random bytes to an ASCII string containing hexadecimal representation of the string of bytes returned. Copy the following listing to the script:
<?php
$bytes = random_bytes(10);
var_dump(bin2hex($bytes));
?>
un the script to generate cryptographic random bytes:
string(20) "ab9ad4234e7c6ceeb70d"
The list() function modifications
The list()
function is used to assign a list of variables as if they were an array. PHP 7.0 and 7.1 have brought about several changes to the list() function. The list()
function in PHP 7 cannot unpack strings as earlier versions could and returns NULL
if a string is unpacked.
In PHP 5.x, unpacking a string with list()
assigns a variable in list()
with a value from a string. We will first demonstrate unpacking a string with list()
using PHP 5.x. Create a script list.php and copy the following listing to the script.
<?php
$str = "aString";
list($elem) = $str;
var_dump($elem);
The script unpacks a value from a $str
to a list element. Run the script with PHP 5.x and the $elem
value is output as ‘a’. If you run the same script with PHP 7.0, you will get NULL
. Another example of list()
not being able to unpack a string with PHP 7 is the following:
<?php
list($catalog) = "Oracle Magazine";
var_dump($catalog);
?>
If you run the script, you will get an output value of NULL
, as with the previous script.
In fact, in PHP 7.x, if a list()
is to be assigned values from a string, the str_split
function must be used:
<?php
$str = "aString";
list($elem) = str_split($str);
var_dump($elem);
If you run the preceding example,the list element is assigned a value of ‘a’, as expected. With PHP 7.0 a list()
expression cannot be completely empty. As an example run the following listing list.php.
<?php
$info = array('Oracle Magazine', 'January-February 2018', 'Oracle Publishing');
list(, , ) = $info;
echo " \n";
This time, an error message is output indicating that “Cannot use empty list..”. A list could have some of its elements empty. In the following listing one of the list()
elements is empty:
<?php
$info = array('Oracle Magazine', 'January-February 2018', 'Oracle Publishing');
list($name, $edition,) = $info;
echo "$name latest edition is $edition.\n";
?>
If you run the script, no error is generated and a list with 2 non-null elements and one empty element is created.
Oracle Magazine latest edition is January-February 2018.
Another modification is list()
now assigns values to variables in the order they are defined. Previously values were assigned to variables in the reverse order in which they were defined. To demonstrate the earlier behaviour, run the following listing as list.php with PHP 5.x:
<?php
list($a[], $a[], $a[]) = ['A', 2, 3];
var_dump($a);
?>
As you can see inspecting the script output (shown in Figure 1), values are assigned to variables in the reverse order of the one in which they were defined.
Figure 1. Values are assigned in the reverse order
Run the same script again with PHP 7.0 and you will see list() assigns values to variables in the same order they were defined:
array(3) { [0]=> string(1) "A" [1]=> int(2) [2]=> int(3) }
PHP 7.1.0 enables the specification of keys in list to indicate the numerical order of assignment. As an example, create a script list.php with the following listing. Notice all the keys are numerical in this example:
<?php
list(0 => $journal, 1=> $publisher, 2 => $edition) = ['Oracle Magazine', 'Oracle Publishing', 'January February 2018'];
echo "$journal, $publisher, $edition. \n";
?>
If you run the script, you will get the following list of values:
Oracle Magazine, Oracle Publishing, January February 2018.
The numerical order of assignment may be shuffled as shown in the following listing, where index 0 key is listed after index 1.
<?php
list(1 => $journal, 0=> $publisher, 2=>$edition) = ['Oracle Magazine', 'Oracle
Publishing', 'January February 2018'];
echo "$journal, $publisher,$edition \n";
?>
If you run the script, output indicates that variables are assigned a value based on the numerical key and not on the order in which the keys are listed.
Oracle Publishing, Oracle Magazine,January February 2018
The key index may be enclosed in single or double quotes as shown here:
<?php
list('1' => $journal, '0'=> $publisher, '2'=>$edition) = ['Oracle Magazine', 'Oracle
Publishing', 'January February 2018'];
echo "$journal, $publisher,$edition \n";
?>
The preceding script generates the same output as the previous. If one element in list()
is keyed, then all of its elements must be keyed. For example, create a list assignment with some keyed elements and others not keyed.
<?php
list(0 => $journal, 1=> $publisher, 2) = ['Oracle Magazine', 'Oracle Publishing', 'January February 2018'];
echo "$journal, $publisher. \n";
?>
If you run the script, you will get an error message indicating that keyed and unkeyed array entries in assignments cannot be mixed:
Cannot mix keyed and unkeyed array entries in assignments
Negative String Offsets
Starting from PHP 7.1 string manipulation functions such as strpos
and substr
support negative offsets, which are handled as offsets from the end of the string. String indexing with [] and {} also supports negative offsets. For example, “ABC"[-2] would return the letter ‘B’. Now, create a script str-negative-offset.php and copy the following listing to the script:
<?php
echo "ABCDEF"[-1];
echo "<br/>";
echo strpos("aabbcc", "a", -6);
echo "<br/>";
echo strpos("abcdef", "c", -1);
echo "<br/>";
echo strpos("abcdef", "c", -5);
echo "<br/>";
echo substr("ABCDEF", -1);
echo "<br/>";
echo substr("ABCDEF", -2, 5);
echo "<br/>";
echo substr("ABCDEF", -7);
echo "<br/>";
echo substr("ABCDEF", -6);
echo "<br/>";
echo substr("ABCDEF", -5);
echo "<br/>";
echo substr("ABCDEF", 6);
echo "<br/>";
echo substr("abcdef", 1, -3);
echo "<br/>";
echo substr("abcdef", 3, -2);
echo "<br/>";
echo substr("abcdef", 4, -1);
echo "<br/>";
echo substr("abcdef", -5, -2);
?>
The script provides several examples of using negative offsets with different string manipulation functions and [].If you run the script, it will output:
F
0
2
F
EF
ABCDEF
ABCDEF
BCDEF
bc
d
e
bcd
New Function to convert Callables to Closures
Callables are useful to pass functions (user-defined and built-in functions except language constructs) and methods as string variables. For example, the function hello()
could be passed to another function as an argument or returned from a function using the function name as string, i.e., ‘hello’, if the parameter type/return type is callable. Create an example script callable.php and declare function hello()
that outputs a ‘hello’ message. Declare another function with parameter type as callable.
function callFunc(callable $callback) {
$callback();
}
The callFunc(callable) function may invoke hello()
using its name as a string.=:
callFunc("hello");
Alternatively, the built-in mixed call_user_func ( callable $callback [, mixed $... ] )
, which has a callable as its first parameter, could be used to invoke the hello()
function by name:
call_user_func('hello');
The script callable.php is listed below:
<?php
function hello() {
echo 'hello';
}
call_user_func('hello');
function callFunc(callable $callback) {
$callback();
}
echo '<br/>';
callFunc("hello");
If you run the script, =
the hello()
function is invoked by name supplied as a string.
hello
hello
A closure is an object representation of an anonymous function. Why convert a callable to a closure? For several reasons, one of which is performance. The callable type is relatively slow due to the cost of finding out if a function is a callable.
Another disadvantage of using a callable is that only public functions can be used as callables. Instead, converting to a closure within a class does not require the function to be public, e.g., the function may be declared private. As an example of this, create a script hello.php and declare a class Hello
with a method getCallback()
that returns a callback function:
public function getCallback() {
return [$this, 'hello_callback_function'];
}
The callback function is declared public.
public function hello_callback_function($name) { var_dump($name); }
Create an instance of the class to get and invoke the callback function. The hello.php script is as follows:
<?php
class Hello {
public function getCallback() {
return [$this, 'hello_callback_function'];
}
public function hello_callback_function($name) { var_dump($name); }
}
$hello = new Hello();
$callback = $hello-> getCallback();
$callback('Deepak');
If you run the script, you will get the following output :
string(6) "Deepak"
Next, use the Closure::fromCallable
static method to convert the private callback function to a closure.
<?php
class Hello {
public function getClosure() {
return Closure::fromCallable([$this, 'hello_callback_function']);
}
private function hello_callback_function($name) { var_dump($name); }
}
$hello = new Hello();
$closure = $hello-> getClosure();
$closure('Deepak');
If you run the script, you will get the same output :
string(6) "Deepak"
Another reason for converting to a closure is error detection at an early phase rather than at runtime. Consider the same example as before but this time with the callback function name misspelled:
public function getCallback() {
return [$this, 'hello_callback_functio'];
}
If you run the script, an error Call to undefined method Hello::hello_callback_functio()
is thrown when the callback is actually used on the following line:
$callback('Deepak');
Instead, if we convert the callable to a closure, the error Failed to create closure from callable: class 'Hello' does not have a method 'hello_callback_function'
is detected on the following line:
return Closure::fromCallable([$this, 'hello_callback_functio']);
Flag JSON_THROW_ON_ERROR
Error handling for JSON functions json_encode()
and json_decode()
is rather minimal in PHP versions pre-7.3, with the following shortcomings:
- J
son_decode()
returns null on error, but null could also be a valid result value, for example if JSON “null” is decoded. The only method to find if an error occurred is from the global error state withjson_last_error()
orjson_last_error_msg().
Json_encode()
does have an error return value. - The program running is not stopped on error and not even a warning is thrown.
PHP 7.3 has added support for the JSON_THROW_ON_ERROR
flag in json_encode()
and json_decode()
. A new subclass of Exception called JsonException
has also been added to describe an exception resulting for JSON encoding/decoding. If the JSON_THROW_ON_ERROR
flag is supplied to json_encode()
and json_decode()
and a JsonException is thrown, the global error state is not modified. To demonstrate the new JSON_THROW_ON_ERROR
and the new JsonException
create a script json.php, and attempt to use json_decode
to decode an array containing an error when using JSON_THROW_ON_ERROR:.
<?php
try {
$json = '{"a":1,"b":2,"c":3,"d":4,"e":5}';
json_decode("{",false,1,JSON_THROW_ON_ERROR);
}
catch (\JsonException $exception) {
echo $exception->getMessage(); // echoes "Syntax error"
}
?>
If you run the script, you will get the following JsonException
Maximum stack depth exceeded
As an example of using JSON_THROW_ON_ERROR
with json_encode(),
encode an array with NAN as an element value as in the following script:
<?php
try {
$arr = array('a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => NAN);
echo json_encode($arr,JSON_THROW_ON_ERROR);
}
catch (\JsonException $exception) {
echo $exception->getMessage();
}
?>
When the script is run, the following message is output:
Inf and NaN cannot be JSON encoded
New Functions for getting First/Last Key Value from an Array
Getting the first/last key from an array is a common operation and PHP 7.3 has added two new specialised functions:
$key = array_key_first($array);
$key = array_key_last($array);
An example of using these with an associative array and even an empty array is given in the following script:
<?php
// usage of an associative array
$array = ['a' => 'A', 2 => 'B', 'c' => 'C'];
$firstKey =array_key_first($array);
$lastKey = array_key_last($array);
echo assert($firstKey === 'a');
echo "<br/>";
echo $firstKey;
echo "<br/>";
echo $lastKey;
echo "<br/>";
// usage of an empty array
$array = [];
$firstKey = array_key_first($array);
$lastKey = array_key_last($array);
echo "<br/>";
echo assert($firstKey === null);
echo "<br/>";
echo assert($lastKey === null);
?>
The output from the script is as follows:
1
a
c
1
1
Compact function reports undefined variables
The compact()
function has a new feature in PHP 7.3 to report undefined variables. To demonstrate it, run the following script that includes some undefined variables:
<?php
$array1=['a','b','c',];
$var1="var 1";
var_dump(compact($array1,$array2,'var1','var2'));
?>
The following message is output from the script:
Notice: Undefined variable: array2 on line 9
Notice: compact(): Undefined variable: a on line 9
Notice: compact(): Undefined variable: b on line 9
Notice: compact(): Undefined variable: c on line 9
Notice: compact(): Undefined variable: var2 on line 9
Trailing Commas in Function Calls
PHP 7.3 has added support for trailing commas in function calls. Trailing commas are useful in several contexts in which arguments are appended frequently such as in variadic functions (array_merge,
compact, sprintf
). Language constructs unset()
and isset()
also allow trailing commas. An example of using a trailing comma in an unset function call is provided below.
<?php
function funcA($A,$B,$C)
{
unset($A,$B,$C,);
echo $A;
echo $B;
echo $C;
}
$A = 'variable A';
$B = 'variable B';
$C = 'variable C';
funcA($A,$B,$C,);
?>
When the script is run the output is as follows:
Notice: Undefined variable: A
Notice: Undefined variable: B
Notice: Undefined variable: C
The array_merge()
function is another example in which a trailing comma could make it easier to append values. The following script makes use of a trailing comma in a function call to array_merge()
:
<?php
$array1=[1,2,3];
$array2=['A','B','C'];
$array = array_merge(
$array1,
$array2,
['4', '5'],
);
?>
Method calls and closures also allow trailing commas. A method is a function within a class. A closure is an object to represent an anonymous function. Trailing commas are only allowed in function calls and not in/as function declarations. Free standing commas, leading commas, and multiple trailing commas are also prohibited by the language.
Math Function bcscale.php
The bcscale
function with syntax int bcscale ([ int $scale ])
is used to set the default scale factor for all subsequent bc math function calls. BC math functions such as bcadd(), bcdiv()
, and bcsqrt()
are used with arbitrary precision numbers. PHP 7.3 has added support to get the current scale factor with bcscale. Setter bcscale returns the old scale factor. Previously scale was mandatory and bcscale()
always returned TRUE
. As an example of this, the following script sets the default scale factor to 3 and subsequently outputs the current scale factor:
<?php
bcscale(3);
echo bcscale();
?>
The output from the preceding script is 3.
New Function is_countable
PHP 7.3 has added support for a new function is_countable
that returns true if the function argument is an array
type or an instance of Countable
.
bool is_countable(mixed $var)
For example example, is_countable()
could be used to find if a given argument is an array.
echo is_countable(['A', 'B', 3]);
An instance of ArrayIterator
turns out to be countable and is_countable
outputs TRUE since ArrayIterator
implements the Countable interface.
echo is_countable(new ArrayIterator());
is_countable
could be used with if()
to ensure an argument is countable before running any subsequent code. In the following code snippet, an instance of a class A is tested for countability:
class A{}
if (!is_countable(new A())) {
echo "Not countable";
}
The result from the preceding code snippet is FALSE
since class A does not implement Countable
. If the argument to is_countable
is an array, then it would return TRUE
as in the following code snippet shows:
$array=['A', 'B', 3];
if (is_countable($array)) {
var_dump(count($array));
}
All the code snippets in this section are collected into script is_countable.php.
<?php
class A{}
echo is_countable(['A', 'B', 3]);
echo "<br/>";
echo is_countable(new ArrayIterator());
echo "<br/>";
if (!is_countable(new A())) {
echo "Not countable";
}
echo "<br/>";
$array=['A', 'B', 3];
if (is_countable($array)) {
var_dump(count($array));
}
?>
Run the script and the output is as follows.
1
1
Not countable
int(3)
Arrow Functions
PHP 7.4 introduces arrow functions for a more concise syntax for anonymous functions. Arrow functions have the following form:
fn(parameter_list) => expr
Arrow functions have the lowest precedence, which implies that expression to the right of the arrow => is applied before the arrow function. For example, arrow function fn($x) => $x + $y
is equivalent to fn($x) => ($x + $y)
and not to (fn($x) => $x) + $y
. A variable used in the expression declared in the enclosing scope is implicitly captured by-value. As an example, consider the following script that declares an arrow function:
$fn1 = fn($msg) => $msg.' '.$name;
The variable $name
is captured from the enclosing scope automatically and the arrow function is equivalent to:
$fn2 = function ($msg) use ($name) {
return $msg.' '.$name;
};
By-value binding for $x is equivalent to performing use($x)
for every $x
occurrence within an arrow function. The arrow function also declares a parameter $msg
. In the next example, var_export calls the arrow function with a supplied argument and outputs the value 'Hello John':
<?php
$name = "John";
$fn1 = fn($msg) => $msg.' '.$name;
var_export($fn1("Hello"));//'Hello John'
?>
Arrow functions may be nested, as demonstrated by the following script. The outer arrow function captures variable $name
by-value from the enclosing scope and the inner arrow function captures $name
from the outer function:
<?php
$name = "John";
$fn = fn($msg1) => fn($msg2) => $msg1.' '.$msg2.' '.$name;
var_export($fn("Hello")("Hi"));
?>
When the script is run, output shown in Figure 2 is displayed.
Figure 2. Arrow Functions may be nested
Because arrow functions use by-value variable binding, modifying the value of a variable in an arrow function has no effect on the variable declared in the enclosing scope. To demonstrate this, the arrow function in the following script decrements the value of the variable $x from the enclosing block, but it has no effect on the variable $x value which is still the same value 1
:
<?php
$x = 1
$fn = fn() => x--; // Has no effect
$fn();
var_export($x); //Outputs 1
?>
The arrow function supports arbitrary function signatures that may include parameter and return types, default values, variadics, and by-reference variable passing and returning. The following script demonstrates the use of different forms of arrow function signatures. The signature description and output from the script are shown with comments //
.
<?php
$name = "John";
$x = 1;
//Arrow function includes parameter type, return type and default value
$fn = fn(string $msg='Hi'): string => $msg.' '.$name;
//Arrow function includes a variadic.
$fn2 = fn(...$x) => $x;
//The arrow function includes by-reference variable passing
$fn3=fn(&$x) => $x++;
$fn3($x);
echo $x; // 2
var_export($fn("Hello"));//'Hello John'
var_export($fn());//'Hi John'
var_export($fn2(1,2,3)); //array ( 0 => 1, 1 => 2, 2 => 3, )
?>
$this
may be used in an arrow function in object context. If used with an arrow function prefixed with static, $this
cannot be used. To demonstrate this, consider the following script that uses $this
in object context and class context. An error message is output when not used in object context.
<?php
class A {
public function fn1() {
$fn = fn() => var_dump($this);
$fn(); // object(A)#1 (0) { }
$fn = static fn() => var_dump($this);
$fn(); //Uncaught Error: Using $this when not in object context
}
}
$a=new A();
$a->fn1();
Summary
In this fourth and penultimate article in the series on new features in PHP 7, we introduced the new features relating to PHP functions.
In the next and final article in the series we shall discuss some new features relating to arrays, operators, constants, and exception handling.
About the Author
Deepak Vohra is a Sun Certified Java Programmer and Sun Certified Web Component Developer. Deepak has published Java and Java EE related technical articles in WebLogic Developer's Journal, XML Journal, ONJava, java.net, IBM developerWorks, Java Developer’s Journal, Oracle Magazine, and devx. Deepak has published five books on Docker and is a Docker Mentor. Deepak has also published several articles on PHP and a book Ruby on Rails for PHP and Java Developers.
PHP 7.x brings several improvements and new features that touch all aspects of the language, including better support for object oriented programming, extensions to classes and interfaces, improvements to the type system, error handling, and more. In this series of articles, we discuss new features across the various PHP 7.x versions.