首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

PHP 7 入门:数组、运算符、常量及异常处理的改进

本文要点

  • PHP 7.0添加了空合并运算符(??),如果第一个操作数存在且其值不为NULL,则返回第一个操作数,否则返回第二个操作数。PHP 7.4还增加了对空合并赋值的支持。
  • PHP 7.0添加了一个新的比较运算符(<=>)来比较表达式。
  • PHP 7.0增加了对Unicode codepoint转义语法的支持,可以将十六进制格式转换为相应的UTF-8编码格式。
  • 即使是从同一命名空间导入,use语句也可以对类、函数和常量进行分组。
  • PHP 7.1添加了一个短数组语法,可用于解包(unpacking)或析构(destructuring )数组。
  • PHP 7.1增加了对类常量可见性的支持,这些常量可以声明为共有(public)、受保护(protected)和私有(private)。
  • PHP 7支持在try/catch语句的同一catch块中指定多个异常。
  • 在PHP 7.0.0中,关键字可用作标识符。
  • PHP 7.3引入了灵活的Heredoc和Nowdoc语法,以提高可读性。
  • PHP 7.3在数组和list()析构中增加了对引用赋值的支持。

这是介绍PHP 7.x新特性系列文章的最后一篇,在本文中,我们将讨论其对数组、运算符、常量和异常处理的改进。

空合并运算符

isset函数可用于确定一个变量是否被设置成非NULL值。通常,isset会与PHP的三元运算符结合使用,如下例所示。在这里,如果设置了GET请求的参数name ,isset则返回true,在这种情况下,变量的值被赋给变量$name,否则$name被设置成常量字符串值:

代码语言:javascript
复制
$name = isset($_GET['name']) ? $_GET['name'] : 'Deepak';
echo "Hello " . htmlspecialchars($name)."<br>";

在PHP 7.0中已经添加了空合并运算符(null coalescing operator??),它可用于简化此类操作。实际上,如果其第一个操作数存在且其值不为NULL,它将返回第一个操作数,否则返回第二个操作数。前面的示例可以使用??重写为:

代码语言:javascript
复制
$name = $_GET['name'] ?? 'Deepak';

空合并运算符可以通过链式调用返回第一个定义了的值:

代码语言:javascript
复制
$name = $_GET['name'] ?? $_POST['name'] ?? 'Deepak';
echo "Hello " . htmlspecialchars($name)."<br>";

现在,创建一个脚本ternary.php,其中包括下面所有的示例:

代码语言:javascript
复制
<?php
$name = $_GET['name'] ?? 'Deepak';
echo "Hello " . htmlspecialchars($name)."<br>";
$name = isset($_GET['name']) ? $_GET['name'] : 'Deepak';
echo "Hello " . htmlspecialchars($name)."<br>";
$name = $_GET['name'] ?? $_POST['name'] ?? 'Deepak';
echo "Hello " . htmlspecialchars($name)."<br>";
?>

如果不带请求参数运行脚本,那么所有示例都将输出最后指定的值:

代码语言:javascript
复制
Hello Deepak
Hello Deepak
Hello Deepak

如果带有请求参数运行脚本,比如name=JohnSmith,那么所有的示例都会输出在$_GET['name']中接收到的请求参数:

代码语言:javascript
复制
Hello JohnSmith
Hello JohnSmith
Hello JohnSmith

新的比较运算符

PHP 7.0中添加了一个新的比较运算符(<=>),如果第一个表达式小于第二个表达式,则返回-1;如果两个表达式相同,则返回0;如果第一个表达式大于第二个表达式,则返回1。 PHP的类型比较规则可用于执行比较。为了证明这一点,创建一个脚本compare.php来比较整数、浮点数和字符串的值:

代码语言:javascript
复制
<?php
// 整数
echo 1 <=> 1; 
echo "<br>";
echo 1 <=> 0; 
echo "<br>";
echo 5 <=> 10; 
echo "<br>";
//浮点数
echo 1.0 <=> 1.5;
echo "<br>";
echo 1.0 <=> 1.0; 
echo "<br>";
echo 0 <=> 1.0; 
echo "<br>";
//字符串
echo "a" <=> "a";
echo "<br>";
echo "a" <=> "c"; 
echo "<br>";
echo "c" <=> "a";
echo "<br>";
?>

如果运行脚本,将会得到如下的比较结果:

代码语言:javascript
复制
0
1
-1
-1
0
-1
0
-1
1

从十六进制格式到UTF-8的Unicode codepoint转换

PHP 7.0添加了对Unicode codepoint转义语法的支持,该语法采用十六进制格式并返回相应的UTF-8编码格式。例如,在Unicode中,ĒU+0112表示,其中前导0可以省略。要尝试Unicode codepoint转义语法,创建一个脚本unicode.php。将如下代码复制到该脚本中:

代码语言:javascript
复制
<?php
echo "\u{0124}";
echo "\u{112}";
echo "\u{13B}";
echo "\u{13B}";
echo "\u{014C}";
?>

如果运行脚本,将输出UTF-8字符串ĤĒĻĻŌ。

允许对“use”语句的别名命名空间进行分组

在PHP 7.0之前,从同一命名空间导入的每个类、函数和常量都必须使用单独的use语句指定。在PHP 7.0中,即使是从同一个命名空间导入的类、函数和常量,也可以在同一个use语句下分组。另外,从PHP 7开始,分组导入时允许使用尾随逗号。

举例来说,创建一个脚本catalog.php并声明一些属于同一命名空间的类、函数和常量,如下所示:

代码语言:javascript
复制
<?php
namespace Catalog;
class ClassA{
function hello(){
return "Hello from classA";
}
}
class ClassB{
function hello(){
return "Hello from classB";
}
}
class ClassC{
function hello(){
return "Hello from classC";
}
}
 
function fn_a(){
return "Message from fn_a()";
}
function fn_b(){
return "Message from fn_b()";
}
function fn_c(){
return "Message from fn_c()";
}
define("Catalog\ConstA", 1);
define("Catalog\ConstB", 2);
define("Catalog\ConstC", 3);
?>

如你所见,虽然使用define()声明的常量必须指定其全限定名,但对于用const声明的常量,这一点并不适用。创建另一个脚本group-namespace.php并导入在catalog.php中定义的类、函数和常量。该脚本包含catalog.phprequire语句。类、函数和常量使用以下方式进行分组导入:

代码语言:javascript
复制
<?php
require('catalog.php');
use Catalog\{ClassA as A, ClassB as B, ClassC as C,};
use function  Catalog\{fn_a, fn_b, fn_c,};
use const Catalog\{ConstA, ConstB, ConstC,Const1};
$a = new A();
echo $a->hello();
echo "<br/>";
$b = new B();
echo $b->hello();
echo "<br/>";
$c = new C();
echo $c->hello();
echo "<br/>";
echo fn_a();
echo "<br/>";
echo fn_b();
echo "<br/>";
echo fn_c();
echo "<br/>";
echo ConstA;
echo "<br/>";
echo ConstB;
echo "<br/>";
echo ConstC;
?>

运行group-namespace.php脚本以访问分组导入的类、函数和常量并输出它们的值。

代码语言:javascript
复制
Hello from classA
Hello from classB
Hello from classC
Message from fn_a()
Message from fn_b()
Message from fn_c()
1
2
3

用于析构数组赋值的短数组语法

前面我们提到过,PHP 7中已经不再支持使用list()解包(unpacking)字符串。无论如何,list()仍继续支持对数组进行解包或析构以赋值给变量。在PHP 7.1中,添加了一种短数组语法来解包或析构数组。为了演示短数组语法的用法,创建一个脚本array_syntax.php,并为不同的“杂志”(magazine)创建一个二维数组,并为每种“杂志”分配一个id:

代码语言:javascript
复制
$catalog = [
	[1, 'Oracle Magazine'],
	[2, 'Java Magazine'],
	[3, 'PHP Magazine'],
];

要使用list()$catalog数组析构或解包为$id$journal_name,可以使用如下语法:

代码语言:javascript
复制
list($id1, $journal_name_1) = $catalog[0];
list($id2, $journal_name_2) = $catalog[1];
list($id3, $journal_name_3) = $catalog[2];

另外,也可以使用新的数组析构语法,如下所示:

代码语言:javascript
复制
[$id1, $journal_name_1] = $catalog[0];
[$id2, $journal_name_2] = $catalog[1];
[$id3, $journal_name_3] = $catalog[2];

list()函数可以在foreach()构造中使用,如下例所示:

代码语言:javascript
复制
foreach ($catalog as list($id, $journal_name)) {
 	echo "Journal $id is $journal_name";
echo "<br/>";
}

与之等效的,使用数组语法[]foreach如下所示:

代码语言:javascript
复制
foreach ($catalog as [$id, $journal_name]) {
	echo "Journal $id is $journal_name";
echo "<br/>";
}

下面列出了完整的array_syntax.php脚本:

代码语言:javascript
复制
<?php
$catalog = [
	[1, 'Oracle Magazine'],
	[2, 'Java Magazine'],
	[3, 'PHP Magazine'],
];
 echo "list() syntax";
echo "<br/>";
list($id1, $journal_name_1) = $catalog[0];
list($id2, $journal_name_2) = $catalog[1];
list($id3, $journal_name_3) = $catalog[2];
echo "Journal $id1 is $journal_name_1";
echo "<br/>";
echo "Journal $id2 is $journal_name_2";
echo "<br/>";
echo "Journal $id3 is $journal_name_3";
echo "<br/>";
echo "[] syntax";
echo "<br/>";
[$id1, $journal_name_1] = $catalog[0];
[$id2, $journal_name_2] = $catalog[1];
[$id3, $journal_name_3] = $catalog[2];
echo "Journal $id1 is $journal_name_1";
echo "<br/>";
echo "Journal $id2 is $journal_name_2";
echo "<br/>";
echo "Journal $id3 is $journal_name_3";
echo "<br/>";
echo "list() syntax";
echo "<br/>";
foreach ($catalog as list($id, $journal_name)) {
 	echo "Journal $id is $journal_name";
echo "<br/>";
}
echo "[] syntax";
echo "<br/>";
foreach ($catalog as [$id, $journal_name]) {
	echo "Journal $id is $journal_name";
echo "<br/>";
}
?>

如果运行脚本,你将看到新的短数组语法执行与list()相同的数组拆包并输出相同的值,如下所示:

代码语言:javascript
复制
list() syntax
Journal 1 is Oracle Magazine
Journal 2 is Java Magazine
Journal 3 is PHP Magazine
[] syntax
Journal 1 is Oracle Magazine
Journal 2 is Java Magazine
Journal 3 is PHP Magazine
list() syntax
Journal 1 is Oracle Magazine
Journal 2 is Java Magazine
Journal 3 is PHP Magazine
[] syntax
Journal 1 is Oracle Magazine
Journal 2 is Java Magazine
Journal 3 is PHP Magazine

与此相关,array_column函数将返回输入数组的单个列的值。在以下语法中,该列由$column_key标识

代码语言:javascript
复制
array array_column ( array $input , mixed $column_key [, mixed $index_key = NULL ] )

PHP 7.0.0添加了对输入参数作为对象数组的支持。为了演示这一点,创建一个脚本array_column.php, 并声明一个带有两个字段$title$editionCatalog类。创建两个Catalog实例,并为它们设置值。然后创建一个包含两个Catalog对象的对象数组:

代码语言:javascript
复制
$catalogs = array($Catalog1, $Catalog2);

最后,使用array_column()函数从对象数组中获取两个字段的值:

代码语言:javascript
复制
print_r(array_column($catalogs, 'title'));
print_r(array_column($catalogs, 'edition'));

array_column.php脚本如下所示:

代码语言:javascript
复制
<?php
class Catalog
{
	public $title;
	public $edition;
}
$Catalog1 = new Catalog();
$Catalog1->title = 'Oracle Magazine';
$Catalog1->edition = 'January-February2018';
 
$Catalog2 = new Catalog();
$Catalog2->title = 'Java Magazine';
$Catalog2->edition = 'March-April2018';
$catalogs = array($Catalog1, $Catalog2);
print_r(array_column($catalogs, 'title'));
print_r(array_column($catalogs, 'edition'));
?>

如果运行这个脚本,它将会输出两个Catalog对象的字段值:

代码语言:javascript
复制
Array ( [0] => Oracle Magazine [1] => Java Magazine ) Array ( [0] => January-February2018 [1] => March-April2018 )

支持类常量可见性

PHP 7.1增加了对类常量可见性的支持,这意味着可以将常量声明为公有(public)、受保护(protected)和私有(private)。只要声明公有常量的类是可访问的,公有常量就可以访问。受保护常量可以在同一类及其子类中访问。私有常量只能在同一类中访问。为了演示类常量可见性的用法,创建一个脚本constants.php,并声明一个constants类。在类中声明四个常量:一个没有访问修饰符,第二个带有public访问修饰符,第三个带有protected修饰符,第四个带有private访问修饰符:

代码语言:javascript
复制
const A = 'A';
	public const B = 2;
	protected const C = 'C';
	private const D = 4;

类常量的默认可见性是public。现在定义三个函数:带有公有访问修饰符的fn_a()、带有私有访问修饰符的fn_b()、带受保护访问修饰符的fn_c()。每个函数都输出先前定义的四个常量的值:

代码语言:javascript
复制
echo constants::A;
echo constants::B;
echo constants::C;
echo constants::D;

从 fn_a() 调用 fn_b() 。

$this->fn_b();

从 fn_b() 调用函数 fn_c() 。

$this->fn_c();

为了说明类中声明的所有常量都可以从同一个类中访问,而不管其所使用的可见性或访问修饰符如何,创建一个类常量实例并调用fn_a()函数,该函数反过来调用fn_b(),后者又调用fn_c()

代码语言:javascript
复制
$constants=new constants();
$constants->fn_a();

为了说明私有常量在声明它们的同一个类中是可访问的,受保护常量只能从子类和同一个声明类中访问,声明一个类ClassA,并在函数fn_a()中输出每个常量的值:

代码语言:javascript
复制
class ClassA{
public function fn_a(){
echo constants::A;
echo constants::B;
echo constants::C; 
echo constants::D;
}

最后,为了说明公有和受保护的常量可以从子类中访问,而私有常量不能,声明constants类的一个子类并在函数fn_d()中输出每个常量的值:

代码语言:javascript
复制
class ClassB extends constants{
public function fn_d(){
echo constants::A;
echo constants::B;
echo constants::C;
echo constants::D;
}

constants.php脚本如下所示:

代码语言:javascript
复制
<?php
class constants
{
	const A = 'A';
	public const B = 2;
	protected const C = 'C';
	private const D = 4;
public function fn_a(){
echo constants::A;
echo "<br/>";
echo constants::B;
echo "<br/>";
echo constants::C;
echo "<br/>";
echo constants::D;
echo "<br/>";
$this->fn_b();
}
private function fn_b(){
echo constants::A;
echo "<br/>";
echo constants::B;
echo "<br/>";
echo constants::C;
echo "<br/>";
echo constants::D;
echo "<br/>";
$this->fn_c();
}
protected function fn_c(){
echo constants::A;
echo "<br/>";
echo constants::B;
echo "<br/>";
echo constants::C;
echo "<br/>";
echo constants::D;
echo "<br/>";
}
}
class ClassA{
public function fn_a(){
echo constants::A;
echo "<br/>";
echo constants::B;
echo "<br/>";
//echo constants::C; Uncaught Error: Cannot access protected const constants::C 
echo "<br/>";
//echo constants::D;Uncaught Error: Cannot access private const constants::D
echo "<br/>";
}
}
class ClassB extends constants{
public function fn_d(){
echo constants::A;
echo "<br/>";
echo constants::B;
echo "<br/>";
echo constants::C;
echo "<br/>";
//echo constants::D;Uncaught Error: Cannot access private const constants::D
echo "<br/>";
}
 }
$constants=new constants();
$constants->fn_a();
$classA=new ClassA();
$classA->fn_a();
$classB=new ClassB();
$classB->fn_d();
?>

如果你尝试运行该脚本,如下所示的echo语句将生成这样的错误:Uncaught Error: Cannot access protected const constants::C.

代码语言:javascript
复制
class ClassA{
	public function fn_a(){
…
   echo constants::C; 
…
	}
..
}

作为一个受保护的常量,constants::C不能从任何非constants派生的类中访问。现在,注释掉该语句并重新运行脚本。如下语句将导致脚本生成另一个错误Uncaught Error: Cannot access private const constants::D

代码语言:javascript
复制
class ClassA{
 	public function fn_a(){
 …
	echo constants::D; 
…
	}
…
}

作为一个私有常量,constants::D不能从任何其他类中访问。注释掉该语句,并再次运行脚本。脚本现在会生成另一个错误Uncaught Error: Cannot access private const constants::D

代码语言:javascript
复制
class ClassB extends constants{
public function fn_d(){
 ...
echo constants::D; 
…
}
…}

constants::D是一个私有常量,不能从子类中访问它。注释掉该语句,并重新运行脚本。现在,可以得到如下输出:

代码语言:javascript
复制
A
2
C
4
A
2
C
4
A
2
C
4
A
2




A
2
C

每个catch块包含多个异常

现在,可以在try/catch语句的同一个catch块中指定多个异常,多个异常使用管道字符“|”分隔。如果需要以相同的方式处理多个异常,那么该特性会非常有用。为了演示多异常catch块的用法,创建一个脚本multi-catch-exception.php,并将如下代码复制到其中。该脚本声明了两个自定义的异常类和一个try/catch语句,该语句在另一个类(MultiCatch)的test()函数中,且该函数在catch块中声明了多个异常:

代码语言:javascript
复制
try {
        	throw new CustomException_2();
    	} catch (CustomException | CustomException_2 $e) {
            var_dump(get_class($e));
    	}

脚本multi-catch-exception.php如下所示:

代码语言:javascript
复制
<?php
class CustomException extends Exception { }
class CustomException_2 extends Exception { }
class MultiCatch {
	public function test() {
    	try {
        	throw new CustomException_2();
    	} catch (CustomException | CustomException_2 $e) {
            var_dump(get_class($e));
        }
	}
}
$multiCatch = new MultiCatch;
$multiCatch->test();
?>

如果运行该脚本,在try块中抛出的异常将会在catch块中被捕获,如下所示:

代码语言:javascript
复制
string(17) "CustomException_2" 

改进了扩展加载语法

php.ini中可用的扩展加载语法已经得到了改进。共享扩展不再需要.dll(在Windows上)和.so(在Unix上)后缀。例如,可以用如下方式指定MySQL数据库和Oracle数据库的扩展:

代码语言:javascript
复制
extension=mysqli
extension=oci8_12c

关键字作为标识符

在PHP 7.0.0中,关键字可以用作类、接口和特征(trait)的属性、常量和方法名称。为了演示这一点,创建一个脚本reserved_restriction.php,并将以下代码复制到该脚本中。该脚本使用保留关键字(intiterable)来声明变量名。它还声明了一个常量null(关键字)和一个true(关键字)函数。

代码语言:javascript
复制
<?php
class Catalog {
	public $int = 'hello ' . 'php';
	public $iterable = '';
	const null = 'null';
	function true() {
	}
}
$catalog=new Catalog();
 $catalog->true();
?>

如果运行该脚本,不会输出错误信息。使用关键字作为标识符可能的一个例外是,不能将常量命名为class。为了演示这一点,在前面的脚本中添加以下代码:

代码语言:javascript
复制
const class=1;

如果运行该脚本,将生成以下错误信息:

A class constant must not be called 'class'; it is reserved for class name fetching.

灵活的Heredoc和Nowdoc语法

让我们先回顾一下HeredocNowdoc语法。Heredoc类似于双引号字符串,用开始和结束标记代替引号。使用heredoc,在<<<开始运算符之后,可以指定一个任意标识符,后跟一个换行符。随后是一个字符串,并且以相同的标识符结束引号。NowdocHeredoc相似,不同之处在于开始标记放在单引号''中,并且在Nowdoc内不进行任何解析。

PHP 7.3引入了灵活的HeredocNowdoc语法,以提高可读性,并进行了如下的改进:

  1. 结束标记不需要后跟分号(”;“)。
  2. 结束标记不需要后跟换行符。
  3. 结束标记可以用制表符或空格缩进。制表符和空格不能混用。文档中的字符串文本可以缩进到与结束标记的缩进级别相同或更高。
  4. 结束标识符由与开始标识符相同的连续独立标记标识。

接下来,我们将用几个例子来演示下新的语法。但首先回顾一下旧语法:

print <<<EOT

Heredoc is similar to double-quoted string, with start and end markers replacing quotes.

EOT;

Heredoc也可以赋值给变量:

代码语言:javascript
复制
<?php
class A {
	public $a = <<<EOT
An example of heredoc as a variable value.
EOT;
}
?>

下面是一个使用新语法的Heredoc示例。

代码语言:javascript
复制
<?php
$str = <<<EOD
	The heredoc string	
EOD;
echo <<<EOT
    	The heredoc string	line 1
   	The heredoc string	line 2
  	The heredoc string	line 3
  	EOT
?>

相反,下面的脚本并不是一个有效的Heredoc语法,会生成这样的错误:Parse error: Invalid indentation - tabs and spaces cannot be mixed.

代码语言:javascript
复制
<?php
{
 	echo <<<END
	        	Heredoc text
            	END;
}
?>

下面是Nowdoc老语法的一个示例。

print <<<'EOT'

Nowdoc is similar to heredoc except that the start marker is enclosed in a single quote '' and no parsing is done inside a nowdoc.

EOT;

新Nowdoc语法的示例如下所示。

代码语言:javascript
复制
<?php
$str = <<<EOD
	The heredoc string	
EOD;
echo <<<'EOT'
    	The nowdoc string	line 1
   	The nowdoc string	line 2
  	The nowdoc string	line 3
  	'EOT' 
?>

由于在nowdoc中不进行任何解析,因此下面示例的nowdoc字符串中包含冗余代码:

代码语言:javascript
复制
<?php
$str = <<<'EOD'
The heredoc  text.
EOD;
class A
{
	var $a;
	var $b;
	function __construct()
	{
        $this->a = 'A';
        $this->b = array('B1', 'B2', 'B3');
	}
}
$A = new A();
$c = 'C';
echo <<<'EOT'
  	Value of variable is "$c". Value of a variable from a class A is  "$A->a".
 	Value of an array element from class A is "{$A->b[2]}".
	Unicode  for 'B' is U+0042
   EOT
?>

由于未执行任何解析,因此前面的脚本会生成如下的输出。

Value of variable is "$c". Value of a variable from a class A is "$A->a". Value of an array element from class A is "{$A->b[2]}". Unicode for 'B' is U+0042

如前所述,heredoc和nowdoc主体缩进级别必须至少与结束标记的缩进级别相同。为了证明这一点,运行如下脚本。

代码语言:javascript
复制
<?php
echo <<<'EOT'
  	Line 1
 	Line 2
	Line 3
 	EOT
?>

在这种情况下,会产生以下错误:

Invalid body indentation level (expecting an indentation level of at least 5

支持数组析构中的引用赋值

PHP 7.3增加了对数组和list()析构中引用赋值的支持。首先让我们回顾一下什么是数组/列表(array/list)析构中的赋值。在下面的脚本中,将对数组进行析构,并将其元素值赋给一个列表:

代码语言:javascript
复制
<?php
list($a[], $a[], $a[]) = ['A', 2, 3];
var_dump($a);
?>

var_dump语句生成如下输出:

array(3) { [0]=> string(1) "A" [1]=> int(2) [2]=> int(3) }

现在,让我们来看一个新语法的示例:

list(&$a, [$b, &$c]) = $d

在本例中,列表元素$a$c是通过引用赋值的。例如,创建以下脚本,其中$array[1]元素通过引用$bvariable赋值。这意味着,如果为$b赋了一个新值,则新值也将赋给$array[1]

代码语言:javascript
复制
<?php
$array = ['A', 2];
list($a, &$b) = $array;
echo $a;
echo "<br/>";
echo $b;
echo "<br/>";
echo $array[1];
$b='b';
echo "<br/>";
echo $array[1];

该脚本的输出如下:

代码语言:javascript
复制
A
2
2
b

如果不通过引用赋值而运行相同的脚本,则输出将不同。

list($a, $b) = $array;

使用上述赋值的输出如下:

代码语言:javascript
复制
A
2
2
2

最后,让我们考虑一个数组析构中引用赋值的示例。在下面的脚本中,数组元素通过引用变量$b被赋值。如果$b的值被更改了,那么数组元素的值也会随之改变。

代码语言:javascript
复制
<?php
$array = [1, &$b];
 
$b='B';
echo $array[0];
echo "<br/>";
echo $array[1];
$b=2;
echo "<br/>";
echo $array[1];

运行该脚本,将会得到如下的输出:

代码语言:javascript
复制
1
B
2

Instanceof接受字面量

让我们首先回顾一下instanceof运算符。在下面的脚本中,instanceof用于确定对象是否为类A的实例:

代码语言:javascript
复制
<?php
class A{}
$obj = new A();
echo ($obj instanceof A);
?>

如果运行该脚本,将输出1。

PHP 7.3添加了对将字面量用作第一个操作数的支持。在下面的脚本中,instanceof的第一个操作数是一个字符串字面量:

代码语言:javascript
复制
<?php
class A{}
echo ('Hello PHP' instanceof A); 
?>

如果运行该脚本,将输出FALSE。如果第一个操作数是一个字面量,instanceof的输出总是FALSE

空合并赋值

我们前面讨论过在PHP 7.0中引入的空合并运算符??。PHP 7.4采用空合并运算符??进一步添加了对空合并赋值的支持。比如,考虑以下情况。使用isset()来确定是否设置了数组键,如果没有设置,则为其设置一个值

代码语言:javascript
复制
if (!isset($a['4'])) {
    $a['4'] = setDefault();
}

下面的脚本演示了如何对具有相同条件设置的数组键运用空合并赋值:

代码语言:javascript
复制
<?php
$a = array('1' => 'one', '2' => 'two', '3' => 'three');
$a['4'] ??= setDefault();
function setDefault()
{ 
    return 'four';
}
var_dump($a);//array(4) { [1]=> string(3) "one" [2]=> string(3) "two" [3]=> string(5) "three" [4]=> string(4) "four" }
?>

数字字面量分隔符

具有多个数字的数字字面量可能会由于长度的关系而变得无法识别,这可能会使调试变得相当困难。PHP 7.4引入了下划线作为数字字面量的分隔符,以提高代码的可读性。下面的脚本在不同类型的变量中使用了数字分隔符“_”。

代码语言:javascript
复制
$_1=10_;       // 用在末尾
$_2=1__2;       // 用在下划线后
$_3=5_.0; 1._0; // 用在小数点后
$_4=0x_123;     //用在x后
$_5=0b_111;     //用在b后

使用下划线作为数字字面量分隔符的唯一要求是它必须出现在两个数字之间。具体地说,它不能用在数字的末尾,也不能出现在另一个下划线或小数点旁边。变量名仍然可以以下划线开头。下面列举以错误方式将"_"用作数字字面量分隔符的所有示例:

代码语言:javascript
复制
$_1=10_;       //  用在末尾
$_2=1__2;       // 用在下划线旁
$_3=5_.0; 1._0; // 用在小数点后
$_4=0x_123;     // 用在x后
$_5=0b_111;     // 用在b后
$_6=2_e3; 2e_3; // 用在e旁

在词法分析期间,数字字面量中的下划线将会被删除。

用于数组内解包的扩展运算符

在函数签名中,已经支持用由三个连续点(…)表示的扩展运算符对参数进行解包。PHP 7.4增加了对扩展操作符的支持,以解包数组的元素。数组中支持的扩展运算符主要有如下特征:

  • 实现Traversable的数组和对象可以与扩展运算符一起使用。
  • 扩展运算符可以在数组的任何位置使用,在元素之前或之后,甚至是连续使用都可以。
  • 它可以与数组语法(array())和短语法([])一起使用。
  • 函数返回的数组可以用score运算符解包。
  • 数组不能通过引用解包。如果要解包的数组元素是通过引用存储的,那么解包之后,它们还将继续通过引用存储。
  • 不支持字符串键。

下面的脚本演示了扩展操作符的使用。数组元素...$cd使用了扩展操作符。...getArr()对函数返回的数组进行解包。

代码语言:javascript
复制
<?php
$cd = ['c', 'd'];
$af = ['a', 'b', ...$cd,'e','f'];
var_dump($af);  
function getArr() {
  return ['c', 'd'];
}
$af = ['a', 'b',...getArr(), 'e','f'];  
var_dump($af); 
?>

每个var_dump语句输出均为:

array(6) { [0]=> string(1) "a" [1]=> string(1) "b" [2]=> string(1) "c" [3]=> string(1) "d" [4]=> string(1) "e" [5]=> string(1) "f" }

为了验证字符串键不能与扩展操作符一起使用,请运行如下脚本:

代码语言:javascript
复制
<?php
$cd = array("c" => "c","d" => "d");
$af = ['a', 'b', ...$cd,'e','f'];
var_dump($af);

将会显示如下的错误信息:

Uncaught Error: Cannot unpack array with string keys

不再推荐使用花括号语法来访问数组元素

PHP 7.4 不推荐使用花括号来访问数组元素和字符串偏移量。无论如何,花括号语法只具有有限的功能。例如,它不能用于创建数组或将元素推入数组,也不能用于列表赋值。以下脚本仍可以使用,并能生成预期的输出字符串(1)“ a”。

代码语言:javascript
复制
<?php
$arr = ['a', 'b', 'c'];
var_dump($arr{0});

但它也会显示一条警告信息:

Deprecated: Array and string offset access syntax with curly braces is deprecated

总结

在关于PHP 7系列的五篇文章中,我们已经按照特性类别分组的形式探讨了PHP 7.x中显著的新特性。在第一篇文章《PHP 7入门:OOP改进》中,我们设置了运行PHP 7.x脚本的环境,并介绍了与面向对象编程相关的改进。在第二篇文章《PHP 7 :类和接口的增强》中,我们介绍了对类和接口的改进。在第三篇文章《PHP 7:类型的新特性》中,我们介绍了PHP类型系统的改进。在第四篇《PHP 7:函数改进》中,我们介绍了与函数相关的改进。在本系列的最后一篇文章中,我们将介绍之前文章中没有涉及的改进,其中包括对数组、运算符、常量和异常处理的改进。

PHP 8.0预计将于2020年12月初发布,并会提供一组新特性,但在此之前,仍需要学习使用PHP 7.x。

作者介绍

Deepak Vohra是一位Sun认证的Java程序员和Sun认证的Web组件开发人员。Deepak 在 WebLogic Developer’s Journal、XML Journal、ONJava、java.net、IBM developerWorks、Java Developer’s Journal、Oracle Magazine 和 devx 上都发表过Java 和Java EE相关的技术文章。Deepak还是一名Docker导师,已出版了五本关于Docker的书籍。Deepak还发表了几篇关于PHP的文章,以及一本面向PHP和Java开发人员的Ruby on Rails书籍。

原文链接:

https://www.infoq.com/articles/php-7-array-operators/

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/hpjgR2sLTTFbTA2qcxfu
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券