PHP7新特性

PHP7已经出来有一段时间了,据官方测试说明,其性能与PHP5.6相比有很大的提升。PHP7相比之前的版本有了许多改变,不仅底层的引擎被改写优化,而且在语法上也带来了许多变化。可以说PHP7是PHP发展过程中的一个重要里程碑。由于现在使用PHP5版本的开发者比较多,PHP7与之前的版本存在一些差异,部分模块不能无缝过渡到新版本的环境中,所以现在使用PHP5版本的开发者还是很多的,一部分已处于观望中。本次介绍的内容是基于我在官网上了解到的一些PHP7所带来的新特性所总结的知识。

新特性
标量类型声明

我们知道,在之前写PHP的函数或者方法时,能给参数添加类型限制的只能是复杂类型(类,接口,闭包等),而arrayintstringbool等这些普通的标量类型是无法声明的,但PHP7让我们可以做到标量声明。

//PHP7之前
function sum($a, $b)
{
    return $a + $b;
} 
//PHP7
function sum(int $a, int $b)
{
    return $a + $b;
}

在为参数限定了类型情况下,如果传入的参数的类型和声明的类型不一致,就会触发Fatal Error来抛出类型错误。

以下是是在PHP7中可以作为参数声明的类型:

1.class/interface 类或者接口
2.self 自身引用
3.array 数组
4.callable 可调用的函数或者闭包
5.bool 布尔
6.float 浮点型
7.int 整形
8.string 字符串

返回值类型声明

跟很多脚本语言一样,PHP7之前编写的函数或者方法的返回值都是没有类型声明的,它可以任意返回各种类型而不需要声明,虽然这使得代码变得非常灵活,但在一些比较严谨的逻辑中就显得没么健壮。PHP7为函数提供了返回值声明的特性。

//PHP7之前
function sum($a, $b)
{
    return $a + $b;
}
//PHP7
function sum(int $a, int $b): int
{
    return $a + $b;
}

只需要在方法或这函数的参数括号末尾加上一个英文的冒号后接参数的类型即可。返回值可以声明的类型跟参数类型声明一样。

null合并运算符

你是否已经受够了isset($_GET['param']) && !empty($_GET['param'])这样判断,PHP7新出的null合并运算符语法糖让上面的语句变得更加精简,只需要两个?即可。

//PHP7之前
$param = isset($_GET['param']) && !empty($_GET['param']) ? $_GET['param'] : '';
//或者
$param = '';

if (isset($_GET['param']) && !empty($_GET['param'])) {
    $param = $_GET['param'];
}
//PHP7
$param = $_GET['param'] ?? '';
相比以前的写法是不是非常的简单,??运算符会根据一个值来判断它是否是存在且不为NULL,如果是则返回第一个数,否则返回第二个数。

组合比较符

组合比较符用于比较两个量的表达式,可以比较两个量之间的大小关系。

//-1
echo 1 <=> 2;
//0 
echo 2 <=> 2;
//1
echo 3.2 <=> 1.4
//-1
echo 'a' <=> 'c';
//0
echo 'b' <=> 'b';
//1
echo 'c' <=> 'b';
组合表达式返回的有三个值,大于1、等于0和小于1。

define定义常量数组

之前的define只能定义intstringfloatbool等基本数据类型,现在define可以定义数组类型的常量。

define('CONST_ARRAY', [
    'size' => 10,
    'color' => 'red'
]);

匿名类

很巧,前段我想在PHP中使用匿名内部类,但是写了不支持,我以为既然PHP支持匿名函数应该也支持匿名类,哈哈,原来PHP7才支持,匿名类在写java程序的时候用到的非常多,有时我们不想为一个参数专门去声明一个类来适配它,而使用内部类可以直接想使用匿名函数一样方便,直接使用,如果参数类型是接口类型,我们只需要让匿名类实现指定的接口即可。

以下的java代码的内部类示例:

interface IFoo {
    void doSomething();
}

class Bar {
    public void execute(IFoo executor) {
        executor->soSomething();
    }
}

Bar bar = new Bar();

bar.execute(new IFoo {
    @Override
    public doSomething() {
        System.out.println('execute!');
    }
});

PHP7中的内部类的形式也差不多,用class实例化一个匿名类然后根据上下文有必要地继承或者实现具体的抽象参数类型。

$anonymousClass = new class()
{
    public function execute()
    {
        echo 'execute';
    }
};

$anonymousClass->execute();
//输出了execute

interface IFoo
{
    function doSomething();
}

class Bar
{
    public function execute(IFoo $foo)
    {
        $foo->doSomething();
    }
}

$bar = new Bar();

$bar->execute(new class () implements IFoo
{
    function doSomething()
    {
        echo 'execute!';
    }
});
//输出execute!

匿名类没有固定的名字,只在代码运行的过程中生成一个随机的类型。

闭包的绑定 Closure::call()

之前声明一个闭包之后需要为为闭包绑定执行上下文,需要复制闭包然后绑定$this,现在使用call()省去复制步骤,更加方便,性能更好。

PHP7之前

class App
{

    public function execute()
    {
        echo 'App execute';
    }

}

$closure = function() {
    echo $this->execute();
};

$newClosure = Closure::bind($closure, new App());
//或者$newClosure = $closure->bindTo(new App());
$newClosure(); //输出App execute
PHP7

class App
{

    public function execute()
    {
        echo 'App execute Call';
    }

}

$closure = function() {
    echo $this->execute();
};

$closure->call(new App());
//输出App execute Call

unserialize 过滤

可以通过unserialize的过滤参数来设定是否过滤指定的类返回__PHP_Incomplete_Class_Name类的对象,__PHP_Incomplete_Class_Name是一个没有方法的类。具体的参数为allowed_classes,其指向需要过滤的类,默认是true,既可以对所有类都可以完全反序列化

class App
{
    private $controller;
    private $action;

    function __set($name, $value)
    {
        if (property_exists($this, $name)) {
            $this->$name = $value;
        }
    }

    function __get($name)
    {
        return property_exists($this, $name)
            ? $this->$name
            : null;
    }

    function __toString()
    {
        return "controller: {$this->controller}, action: {$this->action}";
    }
}

class App2
{
    private $controller;
    private $action;

    function __set($name, $value)
    {
        if (property_exists($this, $name)) {
            $this->$name = $value;
        }
    }

    function __get($name)
    {
        return property_exists($this, $name)
            ? $this->$name
            : null;
    }

    function __toString()
    {
        return "CONTROLLER: {$this->controller}, ACTION: {$this->action}";
    }
}

$app = new App();
$app2 = new App2();

$app->controller = 'Index';
$app->action = 'actionIndex';

$app2->controller = 'Index2';
$app2->action = 'actionIndex2';

$data = serialize($app);
$data2 = serialize($app2);

$obj = unserialize($data2, ['allowed_classes' => true]);
echo $obj, '<br>';
$obj = unserialize($data, ['allowed_classes' => 'App']);
echo $obj, '<br>';
$obj = unserialize($data2, ['allowed_classes' => 'App2']);
echo $obj, '<br>';
//报错,指定为false,任何类都被过滤,不能直接调用toString
$obj = unserialize($data2, ['allowed_classes' => false]);
echo $obj, '<br>';

命名空间批量导入

有时候需要的函数或者类在不同文件中,其是不同的命名空间下面,我们需要在要因为的php文件头部声明引入命名空间,如果引入的类和函数等非常之多,命名空间声明会很长。PHP7为引入了命名空间批量导入,可以在同一条命名空间声明语句指定具体需要的类、函数和常量等。

//PHP之前
use oopsguy\framwork\core\Controller;
use oopsguy\framework\core\Model;
use oopsguy\framework\core\Config;

use function oopsguy\framework\core\function\func1;
use function oopsguy\framework\core\function\func2;

use const oopsguy\framework\core\constant\CONST_SESSION_KEY;
use const oopsguy\framework\core\constant\CONST_MANAGER_SESSION_KEY;
//PHP7
use oopsguy\framework\core\{Controller, Model, Config};
use function oopsguy\framework\core\function\{func1, func2};
use const oopsguy\framework\core\constant\{CONST_SESSION_KEY, CONST_MANAGER_SESSION_KEY};
新特性使得命名空间的声明语句变得简洁许多,精简了很多代码。

生成器返回表达式

生成器是一个偏的内容,不太被使用的特性,不过这在PHP5.5版本就已经开始有了。以下是官方对生成器的解释:

生成器提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现 Iterator 接口的方式,性能开销和复杂性大大降低。
生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组, 那会使你的内存达到上限,或者会占据可观的处理时间。相反,你可以写一个生成器函数,就像一个普通的自定义函数一样, 和普通函数只返回一次不同的是, 生成器可以根据需要 yield 多次,以便生成需要迭代的值。
其实平时我们在遍历内容比较大的数据时,很消耗内存,但是使用生成器就不一样,它只有在迭代的时候才会取值,不会把遍历的数据全部放入内存中。

生成器Generator实现了迭代器接口Iterator,可以对其进行遍历操作,在PHP7中生成器添加了getReturn接口,可以获取return返回值,而在PHP7之前,在生成器中声明带值的返回会引发Fatal error: Generators cannot return values using "return" in %s on line %d错误。

function generateSerialNo(int $limit)
{
    $i = 0;

    for (; $i < $limit; $i++) {
        yield $i;
    }

    return $i;
}

$gen = generateSerialNo(10);

foreach ($gen as $value) {
    echo $value, ' ';
}

echo $gen->getReturn();

//输出
//0 1 2 3 4 5 6 7 8 9 10

以上时生成一个0$limt-1的序列数字,可以看到最后输出10,其实这个10时返回值,当生成器遍历完之后$i的值已经时10了,跳出了循环。

生成器委托

生成器实现了Iterator接口,说明其可以进行遍历操作,在PHP7中,可以用yield from来把生成器委托到其它生成器。

//生成1到5的数字
function generateOneToFive()
{
    yield from generateOneToThree();
    yield from generateSixFourToFive();
}

//生成1到3的数字
function generateOneToThree()
{
    yield 1;
    yield 2;
    yield 3;
}

//生成4到5的数字
function generateSixFourToFive()
{
    yield 4;
    yield 5;
}

foreach (generateOneToFive() as $num) {
    echo $num . ' ';
}

//输出
//1 2 3 4 5

上边的代码需要生成器生成1-5的数字,主生成器中并没有生成数字的逻辑,而是委托其它生成器完成。

整除函数intdiv

这应该很好了解,整除,返回的是整数部分的模。

echo intdiv(100, 3) , '<br>';
echo intdiv(8, 7) , '<br>';
echo intdiv(10.4, 2.5) , '<br>';
//结果
//33 1 5

Session配置

现在我们可以在session_start时候传入一个数组配置参数来覆盖运行时的php.ini文件的配置参数。

session_start([
    'cache_limiter' => 'private',
    'read_and_close' => true,
]);

list解构

PHP7中,使用list不仅解构数组,还可以解构实现了ArrayAccess接口的对象。

list只能用于数字索引的数组
以下是一个配置类的示例
配置文件config.php内容

<?php 
return [
    'value1', 'value2'
];

配置类

class Config implements ArrayAccess
{
    private $data = [];

    function __construct($file)
    {
        /** @noinspection PhpIncludeInspection */
        $this->data = include $file;
    }

    public function offsetExists($offset)
    {
        return isset($this->data[$offset]);
    }

    public function offsetGet($offset)
    {
        return ($this->offsetExists($offset))
            ? $this->data[$offset]
            : null;
    }

    public function offsetSet($offset, $value)
    {
        $this->data[$offset] = $value;
    }

    public function offsetUnset($offset)
    {
        if ($this->offsetExists($offset))
            unset($this->data[$offset]);
    }
}

$config = new Config('config.php');
echo $config[1] , '<br>';
list($key, $key1) = $config;
echo $key , '<br>';
echo $key1 , '<br>';

Unicode codepoint 转译语法

echo可以接受一个已16进制形式的Unicode codepoint,CodePoint即完整的Unicode的代码,PHP经过解析打印出一个双引号或heredoc包围的 UTF-8 编码格式的字符串。

echo "\u{222}", '
';
echo "\u{444}", '
';
echo "\u{666}", '
';
//输出
//Ȣ
//ф
//٦

IntlChar

这事PHP7新增的一个类,IntlCHar类提供了一些列的工具方法来访问和操作Unicode字符相关的信息,类中定义来大量的常量。这个类依赖ICU库。

echo IntlChar::charName('@') . '
';
echo IntlChar::charName('&') . '
';
//输出
//COMMERCIAL AT
//AMPERSAND

以上只是讲了PHP7特性的大部分,还有其它特性

Expectations 预期

PHP7assert不再是一个函数,而是一个语言结构就像echo一样。

预期增强了之前的assert方法,我们可以在开发或者生产环境中使用断言,其提供了可配置选项,我们可以针对不同的环境来使用不同的策略。

PHP7的断言配置:

zend.assertions
1-生成和执行代码(开发坏境)
0-生成代码但跳过运行时环境
-1-不生成代码(生产环境)

assert.exception
1-断言失败时,抛出异常,没有指定异常则默认抛出AssertException
0-生成一个Throwable抛出警告,而不是抛出错误(兼容PHP5的行为)。

ini_set('zend.assertions', 1);

$dest = true;
assert(false, $dest); //抛出AssertException

//----------------------------
ini_set('assert.exception', 0);
ini_set('zend.assertions', 0);

$dest = true;
assert(false, $dest); //无错误和警告

函数preg_replace_callback_array()
这个新函数能够以数组的形式来传递正则和回掉函数处理匹配工作,比使用preg_replace_callback()精简了不少,特别适合批量匹配正则。

preg_replace_callback_array([
'#\d#i' => function($match) {
echo 'd: ' . $match[0], '
';
},
'#\w#i' => function($match) {
echo 'w: ' . $match[0], '
';
}
], '2d3w4g5t');
输出:

d: 2
d: 3
d: 4
d: 5
w: d
w: w
w: g
w: t
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,681评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,710评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,623评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,202评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,232评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,368评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,795评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,461评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,647评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,476评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,525评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,226评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,785评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,857评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,090评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,647评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,215评论 2 341

推荐阅读更多精彩内容

  • 1. ??运算符(NULL合并运算符)把这个放在第一个说是因为我觉得它很有用。用法:$a = $_GET['a']...
    php红薯阅读 240评论 0 0
  • 类型声明 看代码,一目了然了。 命名空间与use关键字批量声明 非混合模式 混合模式 复合模式 匿名类 匿名类的声...
    DukeAnn阅读 591评论 0 1
  • PHP7带来了巨大的性能的提升,更加方便的语法,但是只有通过深入的了解,才能够有效地利用这些利好。《PHP7新特性...
    yogiman阅读 355评论 0 0
  • 梁先生早盘分析:在这种趋势之上,连续近两个月的时间里每天的早盘分析都在坚持叫大家捏好股票,两个月前很多人都是迷茫的...
    Edwin222222阅读 125评论 0 0
  • 网络阶段# 应用程序如何连接互联网## 1.基于HTTP协议 HTTP:超文本传输协议(Hyper-Text Ma...
    yeshenlong520阅读 329评论 0 2