PHP7 的抽象语法树(AST)带来的变化

 更新时间:2016年11月25日 14:55  点击:1372
下面我们来看一篇关于PHP7 的抽象语法树(AST)带来的变化吧,希望这篇文章能够帮助到各位朋友,具体如下。

本文并不会告诉你抽象语法树是什么,这需要你自己去了解,这里只是描述 AST 给 PHP 带来的一些变化。

新的执行过程

PHP7 的内核中有一个重要的变化是加入了 AST。在 PHP5中,从 php 脚本到 opcodes 的执行的过程是:

Lexing:词法扫描分析,将源文件转换成 token 流;
Parsing:语法分析,在此阶段生成 op arrays。
PHP7 中在语法分析阶段不再直接生成 op arrays,而是先生成 AST,所以过程多了一步:

Lexing:词法扫描分析,将源文件转换成 token 流;
Parsing:语法分析,从 token 流生成抽象语法树;
Compilation:从抽象语法树生成 op arrays。
执行时间和内存消耗

从以上的步骤来看,这比之前的过程还多了一步,所以按常理来说这反而会增加程序的执行时间和内存的使用。但事实上内存的使用确实增加了,但是执行时间上却有所降低。

以下结果是使用小(代码大约 100 行)、中(大约 700 行)、大(大约 2800 行)三个脚本分别进行测试得到的,测试脚本: https://gist.github.com/nikic/289b0c7538b46c2220bc .

每个文件编译 100 次的执行时间(注意文章的测试结果时间是 14 年,PHP7 还叫 PHP-NG 的时候):

php-ng php-ast diff
SMALL 0.180s 0.160s -12.5%
MEDIUM 1.492s 1.268s -17.7%
LARGE 6.703s 5.736s -16.9%
单次编译中的内存峰值:

php-ng php-ast diff
SMALL 378kB 414kB +9.5%
MEDIUM 507kB 643kB +26.8%
LARGE 1084kB 1857kB +71.3%
单次编译的测试结果可能并不能代表实际使用的情况,以下是使用 PhpParser 进行完整项目测试得到的结果:

php-ng php-ast diff
TIME 25.5ms 22.8ms -11.8%
MEMORY 2360kB 2482kB +5.1%
测试表明,使用 AST 之后程序的执行时间整体上大概有 10% 到 15% 的提升,但是内存消耗也有增加,在大文件单次编译中增加明显,但是在整个项目执行过程中并不是很严重的问题。

还有注意的是以上的结果都是在没有 Opcache 的情况下,生产环境中打开 Opcache 的情况下,内存的消耗增加也不是很大的问题。

语义上的改变

如果仅仅是时间上的优化,似乎也不是使用 AST 的充足理由。其实实现 AST 并不是基于时间优化上的考虑,而是为了解决语法上的问题。下面来看一下语义上的一些变化。

yield 不需要括号

在 PHP5 的实现中,如果在一个表达式上下文(例如在一个赋值表达式的右侧)中使用 yield,你必须在 yield 申明两边使用括号:

<?php
$result = yield fn();   // 不合法的
$result = (yield fn()); // 合法的
这种行为仅仅是因为 PHP5 的实现方式的限制,在 PHP7 中,括号不再是必须的了。所以下面这些写法也都是合法的:

<?php
$result = yield;
$result = yield $v;
$result = yield $k => $v;
当然了,还得遵循 yield 的应用场景才行。

括号不影响行为

在 PHP5 中, ($foo)['bar'] = 'baz' 和 $foo['bar'] = 'baz' 两个语句的含义不一样。事实上前一种写法是不合法的,你会得到下面这样的错误:

<?php
($foo)['bar'] = 'baz';
# PHP Parse error: Syntax error, unexpected '[' on line 1
但是在 PHP7 中,两种写法表示同样的意思。

同样,如果函数的参数被括号包裹,类型检查存在问题,在 PHP7 中这个问题也得到了解决:

<?php
function func() {
    return [];
}

function byRef(array &$a) {
}

byRef((func()));
以上代码在 PHP5 中不会告警,除非使用 byRef(func()) 的方式调用,但是在 PHP7 中,不管 func() 两边有没有括号都会产生以下错误:

PHP Strict standards:  Only variables should be passed by reference ...
list() 的变化

list 关键字的行为改变了很多。list 给变量赋值的顺序(等号左右同时的顺序)以前是从右至左,现在是从左到右:

<?php
list($array[], $array[], $array[]) = [1, 2, 3];
var_dump($array);

// PHP5: $array = [3, 2, 1]
// PHP7: $array = [1, 2, 3]

# 注意这里的左右的顺序指的是等号左右同时的顺序,
# list($a, $b) = [1, 2] 这种使用中 $a == 1, $b == 2 是没有疑问的。
产生上面变化的原因正是因为在 PHP5 的赋值过程中, 3 会最先被填入数组, 1 最后,但是现在顺序改变了。

同样的变化还有:

<?php
$a = [1, 2];
list($a, $b) = $a;

// PHP5: $a = 1, $b = 2
// PHP7: $a = 1, $b = null + "Undefined index 1"
这是因为在以前的赋值过程中 $b 先得到 2 ,然后 $a 的值才变成 1 ,但是现在 $a 先变成了 1 ,不再是数组,所以 $b 就成了 null 。

list 现在只会访问每个偏移量一次:

<?php
list(list($a, $b)) = $array;

// PHP5:
$b = $array[0][1];
$a = $array[0][0];

// PHP7:
// 会产生一个中间变量,得到 $array[0] 的值
$_tmp = $array[0];
$a = $_tmp[0];
$b = $_tmp[1];
空的 list 成员现在是全部禁止的,以前只是在某些情况下:

<?php
list() = $a;           // 不合法
list($b, list()) = $a; // 不合法
foreach ($a as list()) // 不合法 (PHP5 中也不合法)
引用赋值的顺序

引用赋值的顺序在 PHP5 中是从右到左的,现在时从左到右:

<?php
$obj = new stdClass;
$obj->a = &$obj->b;
$obj->b = 1;
var_dump($obj);

// PHP5:
object(stdClass)#1 (2) {
  ["b"] => &int(1)
  ["a"] => &int(1)
}

// PHP7:
object(stdClass)#1 (2) {
  ["a"] => &int(1)
  ["b"] => &int(1)
}
__clone 方法可以直接调用

现在可以直接使用 $obj->__clone() 的写法去调用 __clone 方法。 __clone 是之前唯一一个被禁止直接调用的魔术方法,之前你会得到一个这样的错误:

Fatalerror:Cannotcall__clone()methodonobjects-use'clone $obj'insteadin...
变量语法一致性

AST 也解决了一些语法一致性的问题,这些问题是在另外一个 RFC 中被提出的: https://wiki.php.net/rfc/uniform_variable_syntax .

在新的实现上,以前的一些语法表达的含义和现在有些不同,具体的可以参照下面的表格:

Expression PHP5 PHP7
$$foo[‘bar’][‘baz’] ${$foo[‘bar’][‘baz’]} ($$foo)[‘bar’][‘baz’]
$foo->$bar[‘baz’] $foo->{$bar[‘baz’]} ($foo->$bar)[‘baz’]
$foo->$bar[‘baz’]() $foo->{$bar[‘baz’]}() ($foo->$bar)[‘baz’]()
Foo::$bar[‘baz’]() Foo::{$bar[‘baz’]}() (Foo::$bar)[‘baz’]()
整体上还是以前的顺序是从右到左,现在从左到右,同时也遵循括号不影响行为的原则。这些复杂的变量写法是在实际开发中需要注意的。

构造函数意思就是在类执行时通过构造函数作为入口进行操作了,我们下面来看一篇关于php构造函数用法吧。


构造函数 和 析构函数

构造函数

void __construct ([ mixed $args [, $... ]] )
PHP 5 允行开发者在一个类中定义一个方法作为构造函数。具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。

Note: 如果子类中定义了构造函数则不会隐式调用其父类的构造函数。要执行父类的构造函数,需要在子类的构造函数中调用 parent::__construct()。如果子类没有定义构造函数则会如同一个普通的类方法一样从父类继承(假如没有被定义为 private 的话)。

Example #1 使用新标准的构造函数

<?php
class BaseClass {
  //我是一个父亲的构造函数
   function __construct() {
       print "In BaseClass constructor<br>";
   }
}
 
//我是一个孩子类
class SubClass extends BaseClass {
   function __construct() {//孩子的构造函数
       parent::__construct(); //我执行一次父亲的构造函数先。
       print "In SubClass constructor<br>";
   }
}
 
//我是个孩子,但是我没有自己的构造函数,所以我自动用我父亲的
class OtherSubClass extends BaseClass {
    // inherits BaseClass's constructor
}
 
//我是个孩子,我有自己的构造函数,抛弃父亲的。所以我不执行父亲的构造函数的打印
class OtherSub2Class extends BaseClass {
    // inherits BaseClass's constructor
    function __construct() {//孩子的构造函数
       print "In OtherSub2Class constructor<br>";
   }
}
 
// In BaseClass constructor
$obj = new BaseClass();
 
// In BaseClass constructor
// In SubClass constructor
$obj = new SubClass();
 
// In BaseClass constructor
$obj = new OtherSubClass();
 
$obj = new OtherSub2Class();
 
?>
为了实现向后兼容性,如果 PHP 5 在类中找不到 __construct() 函数并且也没有从父类继承一个的话,它就会尝试寻找旧式的构造函数,也就是和类同名的函数。因此唯一会产生兼容性问题的情况是:类中已有一个名为 __construct()的方法却被用于其它用途时。

与其它方法不同,当 __construct() 被与父类 __construct() 具有不同参数的方法覆盖时,PHP 不会产生一个 E_STRICT错误信息。

自 PHP 5.3.3 起,在命名空间中,与类名同名的方法不再作为构造函数。这一改变不影响不在命名空间中的类。

Example #2 Constructors in namespaced classes

<?php
namespace Foo;
class Bar {
    public function Bar() {
        // treated as constructor in PHP 5.3.0-5.3.2
        // treated as regular method as of PHP 5.3.3
    }
}
?>
析构函数

void __destruct ( void )
PHP 5 引入了析构函数的概念,这类似于其它面向对象的语言,如 C++。析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。

在 php 文件完全执行完毕,这个类会进入销毁,执行一次析构函数,和构造函数,正好相反,构造函数是在创建类的时候进入执行

Example #3 析构函数示例

<?php
class MyDestructableClass {
   function __construct() {
       print "In constructor<br>";
       $this->name = "MyDestructableClass";
   }
 
   function __destruct() {
       print "Destroying " . $this->name . "<br>";
   }
}
 
$obj = new MyDestructableClass();
echo 'i write something <br>';
 
?>
输出

In constructor
i write something
Destroying MyDestructableClass
和构造函数一样,父类的析构函数不会被引擎暗中调用。要执行父类的析构函数,必须在子类的析构函数体中显式调用 parent::__destruct()。此外也和构造函数一样,子类如果自己没有定义析构函数则会继承父类的。

析构函数即使在使用 exit() 终止脚本运行时也会被调用。在析构函数中调用 exit() 将会中止其余关闭操作的运行。

Note:

析构函数在脚本关闭时调用,此时所有的 HTTP 头信息已经发出。脚本关闭时的工作目录有可能和在 SAPI(如 apache)中时不同。

Note:

试图在析构函数(在脚本终止时被调用)中抛出一个异常会导致致命错误。

时间戳在php中可以精确到秒了,那么我们今天要给各位举例子是关于今天的时间戳计算哦,具体的我们一起来看看这篇关于 PHP获取今天开始和结束的时间戳例子吧.

例子

$t = time();

//开始时间戳

$start = mktime(0,0,0,date("m",$t),date("d",$t),date("Y",$t));

//结束时间戳

$end = mktime(23,59,59,date("m",$t),date("d",$t),date("Y",$t));
mktime 函数的定义和具体用法记录一下

语法

mktime(hour,minute,second,month,day,year,is_dst)

例子 今日、昨日、上周、本月的起始时间戳和结束时间戳

//开始时间截
$start=mktime(0,0,0,date(“m”),date(“d”),date(“Y”));
//结束时间截:
$end=mktime(0,0,0,date(‘m’),date(‘d’)+1,date(‘Y’))-1;
//php获取昨日起始时间戳和结束时间戳
$beginYesterday=mktime(0,0,0,date(‘m’),date(‘d’)-1,date(‘Y’));
$endYesterday=mktime(0,0,0,date(‘m’),date(‘d’),date(‘Y’))-1;
//php获取上周起始时间戳和结束时间戳
$beginLastweek=mktime(0,0,0,date(‘m’),date(‘d’)-date(‘w’)+1-7,date(‘Y’));
$endLastweek=mktime(23,59,59,date(‘m’),date(‘d’)-date(‘w’)+7-7,date(‘Y’));
//php获取本月起始时间戳和结束时间戳
$beginThismonth=mktime(0,0,0,date(‘m’),1,date(‘Y’));
$endThismonth=mktime(23,59,59,date(‘m’),date(‘t’),date(‘Y’));

定义和用法

mktime() 函数返回一个日期的 Unix 时间戳。

参数总是表示 GMT 日期,因此 is_dst 对结果没有影响。

参数可以从右到左依次空着,空着的参数会被设为相应的当前 GMT 值。

 

下面我们来看一篇关于php使用imagick给图片加水印的方法吧,希望这篇文章能够让各位了解到imagick图片加水印的个方法方式。

<?php
$image = new Imagick();
$image->readImage("original.jpg");

$watermark = new Imagick();
$watermark->readImage("/data/mark.png");

// how big are the images?
$iWidth = $image->getImageWidth();
$iHeight = $image->getImageHeight();
$wWidth = $watermark->getImageWidth();
$wHeight = $watermark->getImageHeight();

if ($iHeight < $wHeight || $iWidth < $wWidth) {
    // resize the watermark
    $watermark->scaleImage($iWidth, $iHeight);

    // get new size
    $wWidth = $watermark->getImageWidth();
    $wHeight = $watermark->getImageHeight();
}

// calculate the position
$x = ($iWidth – $wWidth);
$y = ($iHeight – $wHeight);

$image->compositeImage($watermark, imagick::COMPOSITE_OVER, $x, $y);

header("Content-Type: image/" . $image->getImageFormat());
echo $image;
?>

[!--infotagslink--]

相关文章

  • Vue 3.0中jsx语法的使用

    这篇文章主要介绍了Vue 3.0 中 jsx 语法使用,帮助大家更好的理解和使用vue框架,感兴趣的朋友可以了解下...2020-11-13
  • vue Treeselect下拉树只能选择第N级元素实现代码

    这篇文章主要介绍了vue Treeselect下拉树只能选择第N级元素实现代码,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-01
  • Python绘制的爱心树与表白代码(完整代码)

    这篇文章主要介绍了Python绘制的爱心树与表白代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-04-06
  • golang 调用 php7详解及实例

    这篇文章主要介绍了golang 调用 php7详解及实例的相关资料,需要的朋友可以参考下...2017-01-15
  • 带你了解PHP7 性能翻倍的关键

    20岁老牌网页程序语言PHP,最快将在10月底释出PHP 7新版,这是十年来的首次大改版,最大特色是在性能上的大突破,能比前一版PHP 5快上一倍,PHP之父Rasmus Lerdorf表示,甚至能比HHVM虚拟机下的PHP程序性能更快。HHVM 是脸书为自...2015-11-24
  • C语言的基本语法详解

    本篇文章主要讲解C语言 基本语法,这里提供简单的示例和代码来详细讲解C语言的基本语法,开始学习C语言的朋友可以看一下,希望能够给你带来帮助...2021-09-18
  • 基于JS2Image实现圣诞树代码

    马上圣诞节了,作为一名程序猿,如何体现自己独特的过节风格,如何在朋友圈发一张专属自己的祝福照片我觉得很有必要,你们说是不是...2015-12-25
  • C#中方括号[]的语法及作用介绍

    C#中方括号[]可用于数组,索引、属性,更重要的是用于外部DLL类库的引用。...2020-06-25
  • 详解JavaScript语言的基本语法要求

    JavaScript语言的基本语法要求有哪些呐?下面将为大家一一解答: 一、区分大小写 JavaScript语言区分字符大小写,两个字符串相同大小写不同,被认为是不同的字符串。JavaScript语言的关键字也区分大小写,按语法要求应...2015-11-24
  • Java数据结构之哈夫曼树概述及实现

    文中详细讲了关于Java哈夫曼树的概述以及用Java实现的方法,对各位正在学习java数据结构的小伙伴们有很大的帮助哟,需要的朋友可以参考下...2021-05-14
  • 如何利用JavaScript实现二叉搜索树

    这篇文章主要给大家介绍了关于如何利用JavaScript实现二叉搜索树的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-04-03
  • 在C#中使用二叉树实时计算海量用户积分排名的实现详解

    这篇文章主要介绍了在C#中使用二叉树实时计算海量用户积分排名的实现详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • JavaScript的基础语法和数据类型详解

    这篇文章主要介绍了JavaScript的基础语法和数据类型,保姆级的详细教程,万字长文详细的列出了JavaScript的各种语法,建议收藏系列,希望可以有所帮助...2021-09-29
  • C#语法相比其它语言比较独特的地方(一)

    这篇文章主要介绍了C#语法相比其它语言比较独特的地方(一),本文讲解了switch语句可以用来测试string型的对象、多维数组、foreach语句、索引器和Property等内容,需要的朋友可以参考下...2020-06-25
  • C#创建二叉搜索树的方法

    这篇文章主要介绍了C#创建二叉搜索树的方法,涉及C#二叉搜索树的实现技巧,非常具有实用价值,需要的朋友可以参考下...2020-06-25
  • C#基础语法:Base关键字学习笔记

    这篇文章主要介绍了C#基础语法:Base关键字学习笔记,本文讲解了它的一些基础知识以及测试代码,需要的朋友可以参考下...2020-06-25
  • php file_exists 函数与 file_exists语法

    其实于php file_exists 函数与 file_exists语法我们早就讲过了,下面我们来看看一下关于它的使用方法与实例吧 bool file_exists ( string filename ) 如果由 filen...2016-11-25
  • 详细聊聊JavaScript是如何影响DOM树构建的

    DOM (Document Object Model) 的全称是文档对象模型,它可以以一种独立于平台和语言的方式访问和修改一个文档的内容和结构,这篇文章主要给大家介绍了关于JavaScript是如何影响DOM树构建的相关资料,需要的朋友可以参考下...2021-08-06
  • PHP基本语法和数据类型学习笔记

    一篇入门级别的PHP基本语法和数据类型文章,希望对各位php初学者能提供一些帮助哦。 PHP基本语法和数据类型: (1)、PHP基本语法: 1、htm 和 php 混编 ...2016-11-25
  • C++数据结构与算法之哈夫曼树的实现方法

    这篇文章主要介绍了C++数据结构与算法之哈夫曼树的实现方法,简单说明了哈夫曼树的原理,并结合具体实例形式分析了C++实现哈夫曼树的相关操作技巧,需要的朋友可以参考下...2020-04-25