PHP7新加入了一些特性,包括类型声明、命名空间的集体声明、匿名类、老式构造函数的摒弃、太空飞船操作符、null合并运算符、uniform变量语法以及一些其他的改动。
从php7开始引入了类型声明,明确的有两类变量可以声明类型:形参、返回值
php7支持的形参类型声明的类型有整型、浮点型、字符串型、布尔类型,可以用在函数形参以及对象的方法形参上。
name('ll');
echo $person->age(30);
echo $person->isAlive(true);
默认情况下,形参类型声明是完全不被限制的,这就意味着我们可以传递一个浮点数给期望得到整型数的方法。例如:
echo $person->age(30.5);
当然,也可以做一些限制,例如:
declare(strict_type=1);
此时如果再传入浮点数给age的话,便会得到一个Uncaught Type Error,这个Fatal错误告诉我们Person::age只接受一个整型而非浮点型。在需要字符串传参的情况下,如果不提供字符串形参的话,也会出现类似的报错:
echo $person->isAlive('true');
执行上边的代码就会生成刚刚的Fatal错误。
还是Person类,稍微修改下:
class Person
{
public function age(float $age) : string
{
return 'Age is '.$age;
}
public function name(string $name) : string
{
return $name;
}
public function isAlive(bool $alive) : string
{
return ($alive) ? 'Yes' : 'No';
}
}
如上所示,返回类型声明使用了data-type语法。
在php中,不必区分命名空间子目录下的类,命名空间只是为类提供了一种逻辑分割,当出现一个命名空间下有很多类且我们要一次性使用多个类的情况时,需要一个一个的将他们声明在代码顶部。例如
use Publisher\Pack\Book;
use Publisher\Pack\Ebook;
use Publisher\Pack\Video;
use Publisher\Pack\Present;
use function Publisher\Pack\getBook;
use function Publisher\Pack\saveBook;
use const Publisher\Pack\COUNT;
use const Publisher\Pack\KEY;
$book = new Book();
$ebook = new Ebook();
$video = new Video();
$present = new Present();
php7引入了批量use声明,有三种模式,分别为非混合模式的use声明,混合模式的use声明,复合模式的use声明
假设命名空间中有多重类型的资源,例如类、函数、常量等,则使用费混合模式的use声明,可以按照类型将他们归类后逐个用use声明。
use Publisher\Pack\{ Book, Ebook, Video, Present };
use function Publisher\Pack\{ getBook, saveBook };
use const Publisher\Pack\{ COUNT, KEY };
use Publisher\Pack\{
Book,
Ebook,
Video,
Present,
function getBook,
function saveBook,
const COUNT,
const KEY
};
举例说明,有一个Book类处于Publisher\Pack\Paper命名空间下,有一个Ebook类位于Publisher\Pack\Electronic命名空间下,还有两个类video和Present位于Publisher\Pack\Media命名空间下,在复合模式下:
use Publisher\Pack\{
Paper\Book,
Electronic\Ebook,
Media\Video,
Media\Present
};
匿名类的声明和使用是同时进行的,它具备其他类所具备的所有功能,只是没有类名而已。不过虽然没有类名,但在php内部,会在内存的引用地址表中为其分配一个全局唯一的名称。例如全局的一个匿名类的名称为class@0x4f6a8d124。
匿名类在继承方面同命名类相同,一样可以继承父类及父类的方法:
class Pack
{
protected $number;
public function __construct()
{
echo 'I am parent construct!';
}
public funtion getNumber() : float
{
return $this->number;
}
}
$number = new class(5) extends Pack
{
public function __construct(float $number)
{
parent::__construct();
$this->number = $number;
}
}
echo $number->getNumber(); // 'I am parent construct' 和 5
上述代码将显示I am parent construct和5。通过使用匿名类继承了父类Pack。同时父类的public、protected属性在匿名类中仍然有效。匿名类同样可以继承接口,方式和继承命名类相同。
class Pack
{
public function pack()
{
echo 'I am an old style constructor!';
}
}
这个方法不推荐使用,目前会产生不推荐的信息,很可能在接下来的版本中废弃掉。
在比较变量的时候非常有用,包括数值(字符串型、整型、浮点型)、数组、对象。
可以书写用于usort、uasort、uksort的回调函数,具体规则如下:
$int1 = 1;
$int2 = 2;
$int3 = 1;
echo $int1 <=> $int3; // 0
echo $int1 <=> $int2; // -1
echo $int2 <=> $int3; // 1
同样的,我们也可以用这个操作符来对比字符串、对象、数组,这些类型的比较都是基于标准的PHP方式。
function normal_sort($a, $b)
{
if ($a == $b) {
return 0;
}
if ($a < $b) {
return -1;
}
return 1;
}
function space_sort($a, $b) : int
{
return $a <=> $b;
}
$normalArray = [1,34,56,67,98,45];
usort($normalArray, 'normal_sort');
foreach ($normal_sort as $k => $v) {
echo $k.' => '.$v.'
';
}
$spaceArray = [1,34,56,67,98,45];
usort($spaceArray, 'space_sort');
foreach ($spaceArray as $k => $v) {
echo $k.' => '.$v.'
';
}
可以看到,太空船操作符更精简并且不需要太多if表达式。
三元运算符一行代码就可以实现if-else的功能:
$post = ($_POST['title']) ? ($_POST['title']) : null;
若$_POST['title']存在,则给变量赋值,不存在则为null,然而如果$_POST、($_POST['title'])不存在或者为null时,php就会抛出Undefined Index错误,为了解决这个问题,一般采用isset函数:
$post = isset($_POST['title']) ? ($_POST['title']) : null;
以便解决报错,但又带来另一个问题--如果我们要在多处进行这样的校验,就需要写很多这样的代码,特别是在php的模板语言时,问题凸显。在php7中,推荐使用合并运算符,在第一个操作数存在时可被直接返回,不然就返回第二操作数:
$post = $_POST['title'] ?? null;
如果存在则返回$_POST['title'],不存在则返回null。合并运算符的另一个好处是可以连续使用:
$title = $_POST['title'] ?? $_GET['title'] ?? 'No POST or GET';
上边这段代码在执行时会首先检查第一操作数是否存在,若存在直接返回,不存在则检查第二操作数。此时第二个合并操作符开始生效,它会检查第二操作数是否存在,若存在则返回,否则返回右边的值。也就是说合并操作符是从左到右执行。
我们常会遇到这样的情况:方法、变量、类名等会被保存在某个变量里:
$objects['class']->name;
在上边的代码中,$objects['class']会先被解析,之后name属性再被解析。就像代码顺序一样,通常由左到右被解析。
再考虑下边一种情况:
$first = ['name' => 'second'];
$second = 'Howdy;
echo $$first['name'];
在php5.x版本中,这段代码会顺利执行,并输出Howdy。然而,这样的输出与前边看到的变量从左到右解析的原则产生了不一致。这是因为$$first会优先被解析。那么,上边代码解析情况就变成了${$first['name']}。很明显,变量的语法不一致,并可能产生混淆。为避免这一情况,php7引入了统一变量语法,如果不使用这种语法,上边的代码会产生notice级别的错误,并且得到不确定的输出内容,为了在php7中实现上述代码,应该在大括号中补充如下:
echo ${$first['name']};
看另外一个例子:
class Pack
{
public title = 'PHP 7';
public $publisher = 'Pack Publisher';
public function getTitle() : string
{
return $this->title;
}
public function getPublisher : string
{
return $this->publisher;
}
}
$methods = ['title' => 'getTitle', 'publisher' => 'getPublisher'];
$object = new Pack();
echo 'Book '.$object->$methods['title']().' is published by '.$object->$methods['publisher']();
上边的代码在php5.x版本中执行的很顺利,并且会输出语预期的结果。然而,在php7环境执行下,会产生Fatal级别的错误。错误主要体现在最后一行,php7会首先尝试解析$object->$methods,之后才会尝试解析['title']等,这不符合预期,修改如下:
echo 'Book '.$object->{$methods['title']}().' is published by '.$object->{$methods['publisher']}();
这样就可以得到预期了。
从5.6开始,常量数组可以使用const关键字来声明:
const STORES = ['en', 'fr', 'ar'];
目前在php7中,常量数组可以通过define函数来初始化
define('STORES', ['en', 'fr', 'ar']);
在php7之前,多个default默认值在switch语句中时被允许的,但在php7中会产生Fatal级别的错误。
在php7之前,要使用session时,必须调用session_start()函数,这个函数并没有参数需要传递,所有session相关的配置都在php.ini文件中。从7开始,可以在调用函数时传递参数选项数组,这些信息将会覆盖php.ini中的session配置。
session_start([
'cookie_lifetime' => 3600,
'read_and_close' => true
]);
实参部分传递的选项数组将优先于php.ini中的session配置而被使用。
在使用serialize和unserialize两个方法分别对对象进行序列化和反序列化。unserialize()函数并不安全,因为它没有任何过滤项,并且可以反序列化任何类型的对象。因此在7中引入了过滤器。默认情况下允许反序列化所有类型的对象:
$result = unserialize($object, ['allowed_classes' => ['Pack', 'Book', 'Ebook']]);