LAMP 体系有了新的竞争,但此版本中的特性使 PHP 再次寻衅极限。
2012 年 4 月发布
大约八年前,我为 Oracle 技能网写了一篇名为“您理解 PHP 吗?”的文章。在那篇文章中,我谈到了 PHP 固执的功能优于形式的“Web 问题”办理方法,以及它所具备的让事情变得大略的能力。当时,我们即将发布 PHP 5.0。现在,时隔将近十年之后我们推出全新的 PHP 5.4.0 版本,虽然在这期间发生了很多事情,但也有许多事情根本没变。
没变的一件事情便是生态系统一如以往那样主要。办理 Web 问题不仅仅关乎脚本措辞的选择,它关乎的是周围的全体生态系统。现在,LAMP 体系已盛行近 15 年,仍广受欢迎,但我们开始把稳到其他功能强大的方案。附带 nginx 的 PHP-FPM 已经快速盛行起来,由于从 PHP 5.3 开始大大改进了支持,并在 5.4 中进一步得到简化。体系中的 M(即数据库)部分与 8 年前比较也开始变得极为不同。与将所有内容都只放入 MyISAM 表中比较,各种 NoSQL 办理方案和 MySQL Cluster 供应了一组更丰富的选择。
涌现了多种有趣的技能,因而我们编写了 PHP 扩展来轻松访问这些技能。我最喜好的一个扩展是 libevent,可以用它在 PHP 中编写事宜驱动的高性能运用程序。另一个是 ZeroMQ,这是一个高等套接字库。与 SQLite 不再须要编写另一种原始文件格式和关联的剖析器极为相似,ZeroMQ 也无需因任何情由而利用套接字协议和关联的套接字处理代码。您乃至可以组合利用 libevent 和 ZeroMQ,以得到独立、高性能、事宜驱动的卓越做事器。(如果您对此感兴趣,请拜会此示例。)我还十分喜好 SVM(支持向量机)这一机器学习算法,您不必成为机器学习的爱好者也可提出许多问题。
还有许多扩展在最近几年内已被广泛接管。特殊是,Gearman 变得盛行起来,逐渐成为用户支配的常见体系的一部分。您可以通过 Gearman 分派作业,以便由事情器异步完成这些作业。事情器可以遍布多台做事器,它们乃至可以进一步分派更多的 MapReduce 类型作业。
2004 年发布 PHP 5.0 之后,接下来在 2005 年推出 5.1,此版本新增了 DateTime 实现、PDO 和性能改进。PHP 5.2 于 2006 年发布,引入了改进的内存管理器、JSON 支持和输入筛选。当时,我们动手推动 PHP 6,这是一个极其宏伟的操持,完备重写有关 ICU(Unicode 国际化组件)库的所有内容。事实证明这个操持有些操之过急 — 我们无法使足够多的开拓职员为之愉快,终极只得将准备引入 PHP 6 的各种特性添加到 2009 年发布的 PHP 5.3 中。5.2 与 5.3 版本时隔 3 年,这也意味着 5.3 向 PHP 新增了大量内容:命名空间、后期静态绑定、闭包、垃圾网络、受限 goto、mysqlnd(MySQL 原生驱动程序)、更好的 Windows 性能以及许多其他内容。
事后看来,将此版本称为 PHP 6 可能有一定的道理,但 PHP 6 等同于在 Unicode 方面所做的努力,以至于为此编写了干系书本,因此我们认为如果没有对 Unicode 做出重大改进,就不能发布 PHP 6。我们引入了名为“intl”的 ICU 扩展,它也针对 PHP 5.2 编译,这可让您访问更多的 ICU 功能。mbstring 扩展随韶光不断地改进,这意味着险些任何与 Unicode 干系的问题都有办理方案,只是未明确集成到措辞本身中。
这样在 2012 年推出 PHP 5.4。而且,与上一版本时隔将近 3 年,我们在此期间对一些内容进行了改进。我甘心规复到每年推出一个版本,每个版本包含更少的新特性。
以下是您升级到 5.4 时将看到的紧张特性:
内存和性能改进许多内部构造已变得更小或完备消逝,从而在大型 PHP 运用程序中可节省 20-50% 的内存。通过各种优化使性能提高 10-30%(紧张取决于代码实行的操作),这些优化包括内联各种常见代码路径、将 $GLOBALS 添加到 JIT、“@”操作符运算更快、添加了运行时类/函数/常量缓存、运行时字符串常量现在被拘留、通过预先打算的散列更快地访问常量、空数组速率更快并利用更少内存、unserialize() 和 FastCGI 要求处理速率更快,以及在全体代码中进行更多的内存和性能调度。
例如,早期的一些测试表明,Zend Framework 在 5.4 中运行速率提高 21% 并且内存利用减少 23%,而 Drupal 内存利用减少 50% 并且运行速率大约提高 7%。
TraitTrait 可能是 PHP 5.4 中评论辩论最多的特性 — 将它们视为编译器赞助的复制粘贴。Trait 也是 Scala 的一个特性。其他措辞可能将它们称为“mixin”— 或者这些措辞根本不对它们进行命名,但具有扩展接口机制,许可接口包含其方法的实际实现。
与 mixin 相反,PHP 中的 trait 包括显式冲突办理机制,用于多个 trait 实现相同方法的情形。
trait Singleton {
public static function getInstance() { ... }
}
class A {
use Singleton;
// ...
}
class B extends ArrayObject {
use Singleton;
// ...
}
// Singleton method is now available for both classes
A::getInstance();
B::getInstance();
请拜会 php.net/traits 理解更多示例,包括冲突办理语法、方法优先顺序、可见性以及对 trait 中常量和属性的支持。此外,要详细理解观点理论,您可以阅读 Nathan Schärli 的论文“Trait:行为构建块中的组合类”。
精简数组语法新增的一种大略但非常盛行的语法:
$a = [1, 2, 3];
$b = ['foo' => 'orange', 'bar' => 'apple'];
便是说,您现在不再须要利用“array”关键字来定义数组。
函数数组解除引用新增的另一种常用语法。返回数组的函数调用现在可以直接解除引用:
function fruits() {
return ['apple', 'banana', 'orange'];
}
echo fruits()[0]; // Outputs: apple
实例方法调用与函数数组解除引用干系,您现在可以调用工具实例化方法。与早期版本一样,您当然仍可以链接手法调用,因此您现在可以编写如下代码:
class foo {
public $x = 1;
public function getX() {
return $this->x;
}
public function setX($val) {
$this->x = $val;
return $this;
}
}
$X = (new foo)->setX(20)->getX();
echo $X; // 20
然而,由于可能丢弃实例化的工具,因此,除非您的布局函数实行有用操作,否则您该当在此改用静态方法调用。如果将它与精简数组语法和函数数组解除引用结合利用,我们可以编写某些十分繁芜的代码:
class foo extends ArrayObject {
public function __construct($arr) {
parent::__construct($arr);
}
}
echo (new foo( [1, [4, 5], 3] ))[1][0];
看一眼之后,您可以断定输出是什么吗?在此,我们将二维数组通报到仅返回数组的布局函数。然后,我们选出第二个维度的第一个元素,因此这将输出“4”。
闭包绑定闭包是在 PHP 5.3 中引入的,但在 5.4 中我们改进了闭包与工具的交互办法。例如:
class Foo {
private $prop;
function __construct($prop) {
$this->prop = $prop;
}
public function getPrinter() {
return function() { echo ucfirst($this->prop); };
}
}
$a = new Foo('bar');;
$func = $a->getPrinter();
$func(); // Outputs: Bar
把稳闭包访问 $this->prop 这一私有属性。默认情形下,PHP 中的闭包利用预绑定 — 这意味着闭包内的变量具有定义闭包时所具有的值。可以利用引用将其转换为后绑定。但是,也可以重新绑定闭包:
$a = new Foo('bar');
$b = new Foo('pickle');
$func = $a->getPrinter();
$func(); // Outputs: Bar
$func = $func->bindTo($b);
$func(); // Outputs: Pickle
在此,我们将闭包从 $a 实例重新绑定到 $b 中的实例。如果您不肯望闭包随时访问工具实例,可以将闭包声明为静态:
class Foo {
private $prop;
function __construct($prop) {
$this->prop = $prop;
}
public function getPrinter() {
return static function() { echo ucfirst($this->prop); };
}
}
$a = new Foo('bar');;
$func = $a->getPrinter();
$func(); // Fatal error: Using $this when not in object context
工具即函数有一种新的神奇方法,名为“__invoke”,其用法如下:
class MoneyObject {
private $value;
function __construct($val) {
$this->value = $val;
}
function __invoke() {
return sprintf('$%.2f',$this->value);
}
}
$Money = new MoneyObject(11.02/513);
echo $Money(); // Outputs: $28.65
内置 Web 做事器 (CLI)CLI 做事器是一种小型 Web 做事器实现,可以从命令走运行:
% php -S localhost:8000
PHP 5.4.0 Development Server started at Sun Mar 11 13:27:09 2012
Listening on localhost:8080
Document root is /home/rasmus
Press Ctrl-C to quit.
CLI 做事器不适宜用作生产 Web 做事器;我们将利用它运行一些 PHP 回归测试,其他单元测试机制也可利用它,并且 IDE 也可能利用它。它确实具有一些很有用的特性,用于从命令行进行日常代码调试。默认情形下,它利用当前目录作为 DocumentRoot;它也处理静态文件要求。默认目录索引文件为“index.php”,因此您可以在满含 .php、.css、.jpg 等文件的目录中激活它,它自己就可以运行。对付可能利用 mod_rewrite 将所有要求发送到前端掌握器或路由器的更繁芜运用程序,您可以将此路由器与一个大略的小脚本包装在一起,并启动 CLI 做事器,如下所示:
% php -S localhost:8080 /path/to/router.php
PHP 5.4.0 Development Server started at Sun Mar 11 13:28:01 2012
Listening on localhost:8080
Document root is /tmp/web
Press Ctrl-C to quit.
router.php 脚本可能如下所示:
<?php
if (preg_match('!\.php$!', $_SERVER["REQUEST_URI"])) {
require basename($_SERVER["REQUEST_URI"]);
} else if (strpos($_SERVER["REQUEST_URI"], '.')) {
return false; // serve the requested file as-is.
} else {
Framework::Router($_SERVER["REQUEST_URI"]);
}
此包装器加载直接 .php 要求,将包含“.”的任何其他要求通报到静态文件处理程序,其他所有内容都通报到框架的路由器。您可以如此直接从命令走运行 Drupal 和 Symphony。
原生会话处理程序接口这是一个小而方便的特性,现在可以用它实现会话处理程序接口。现在,您可以仅将会话处理工具的实例通报给 session_set_save_handler(),而不必通报给它六个比较麻烦的函数:
SessionHandler implements SessionHandlerInterface {
public int close ( void )
public int destroy ( string $sessionid )
public int gc ( int $maxlifetime )
public int open ( string $save_path , string $sessionid )
public string read ( string $sessionid )
public int write ( string $sessionid , string $sessiondata )
}
session_set_save_handler(new MySessionHandler);
JsonSerializable 接口现在,您可以通过实现 JsonSerializable 接口来掌握有人考试测验利用 json_encode() 对您的工具进行编码时所发生的情形:
class Foo implements JsonSerializable {
private $data = 'Bar';
public function jsonSerialize() {
return array('data'=>$this->data);
}
}
echo json_encode(new Foo); // Outputs: {"data":"Bar"}
二进制表示法为了与 PHP 的原生十六进制和八进制支持折衷同等,现在也支持二进制表示法:
$mask = 0b010101;
改进了缺点缺点稍有改进。
改进前:
% php -r 'class abc foo'
Parse error: syntax error, unexpected T_STRING, expecting '{'
in Command line code on line 1
改进后:
% php -r 'class abc foo'
Parse error: syntax error, unexpected 'foo' (T_STRING), expecting '{'
in Command line code on line
改进可能不十分明显,但差异是现在已在缺点中显示偏移标记“foo”的值。
数组到字符串转换关照如果您一贯利用 PHP,则可能以随机涌如今页面中“Array”一词结束编程,由于您考试测验直接输出数组。每当将数组直接转换为字符串时,都很有可能涌现缺点,现在有了一个针对这一情形的关照:
$a = [1,2,3];
echo $a;
把稳:数组到字符串转换在 example.php onlLine 2 中
删除的特性末了,我们集中整理了几年来标记为已弃用的多个特性。这些特性包括 allow_call_time_pass_reference、define_syslog_variables、highlight.bg、register_globals、register_long_arrays、magic_quotes、safe_mode、zend.ze1_compatibility_mode、session.bug_compat42、session.bug_compat_warn 以及 y2k_compliance。
除了这些特性之外,magic_quotes 可能是最大的危险。在早期版本中,未考虑因 magic_quotes 出错导致的后果,大略编写且未采纳任何举措使自身免受 SQL 注入攻击的运用程序都通过 magic_quotes 来保护。如果在升级到 PHP 5.4 时未验证已采纳精确的 SQLi 保护方法,则可能导致安全漏洞。
其他改动和特性有一种新的“可调用的”类型提示,用于某方法采取回调作为参数的情形。
htmlspecialchars() 和 htmlentities() 现在可更好地支持亚洲字符,如果未在 php.ini 文件中显式设置 PHP default_charset,这两个函数默认利用 UTF-8 而不是 ISO-8859-1。
<?=(精简回显语法)现在始终可用,无论 short_tags ini 设置的值为何。这该当使模板化系统创建者感到满意。
会话 ID 现在默认通过 /dev/urandom(或等效文件)中的熵天生,而不是与早期版本一样成为必须显式启用的一个选项。
mysqlnd 这一捆绑的 MySQL 原生驱动程序库现在默认用于与 MySQL 通信的各种扩展,除非在编译时通过 ./configure 被显式覆盖。
可能还有 100 个小的改动和特性。从 PHP 5.3 升级到 5.4 该当极为顺畅,但请阅读迁移指南加以确保。如果您从早期版本升级,实行的操作可能稍多一些。请查看以前的迁移指南再开始升级。
PHP 的下一步方案是什么?我们没有对 PHP 进行长期方案。PHP 将随 Web 一起发展。我们不知道 5-10 年内的主要 Web 趋势和技能将是什么,但知道通过我们的不断付出,PHP 必将存在。
短期内,我们通过“internals”邮件列表谈论 PHP 开拓,并且就大特性达成共识时,它将发展为 RFC。您可以在 wiki.php.net/rfc 中找到 RFC。一旦我们表决赞许发布一组极佳的新特性,并且对这些特性进行了精确实现和测试,我们便开始预备推出新版本。
PHP 随 Web 发展并保持稳定的市场份额,在环球所有网站中,大约三分之一的网站都利用它。个中不仅包括一些最大的网站,而且还包括很大一部分最小的网站。我是在最小网站上单独设置 PHP 的:扩展是自然而然的事情,乃至是预期的特色,也是强烈吸引工程师的特色,但缩减不太正常,并且在某些情形下更困难。如果您找到适当的平衡,并且可以将同一代码库用于宿舍出租乃至拥有数十亿美元资产的公司,那么您就真正节制了这种措辞。
Rasmus Lerdorf 因 1995 年创建 PHP 项目而广为人知,多年来,为许多其他开源项目做出了卓越贡献。他在 Yahoo! 担当根本架构师的韶光已超过 7 年,并在 2012 年加入 Etsy。他出生于格陵兰岛,成长于丹麦和加拿大,并在滑铁卢大学得到系统设计工程学位。您可以在 Twitter 上关注 @rasmus。