2012年11月16号PHP5.5的第一个alpha版发布了,里面包含了一些期待已久的特性,以及从其他语言借鉴而来的新特性。本文会浅析这些新特性,使大家对此有所了解。
一,Generators-生成器
使用过Python的人对此并不陌生,生成器的定义是:“生成器是一个函数,它生成一个值的序列,以便在迭代中适用”。
可以简单的理解为一种简单的迭代器的生成方案。(相对于实现PHP中的迭代器,生成器更为便捷)
- function countdown(n){
- while(n>0){
- yield n ;
- }
- }
- foreach(countdown(10) as $val){
- //do something with $val
- }
当调用countdonw(10)时,函数不会马上执行,而返回一个迭代器对象,该对象实现了Iterator的借口,从而可以在foreach中被遍历。每当PHP调用Iterator::next()函数时,生成器函数将不断执行语句,直至遇到yield语句位置,并返回当前Iterator::current()的值。迭代器的原型如下:
- final class Generator implements Iterator {
- void rewind();
- bool valid();
- mixed current();
- mixed key();
- void next();
- mixed send(mixed $value);
- }
其中:
通常情况下,rewind()方法是不允许调用的,除非generator处于初始状态,因为generator通常用于对数据的一次性迭代,rewind()一个已经开始调用的generator会抛出一个异常;
valid()方法可以用以检测,迭代器是否已经结束;
current()方法返回当前的值或者为null;
key()方法返回当前值的键值,如果未指定,则返回一个从0开始的自增值;
next()函数用来继续调用迭代器对象;
sent()函数用来对迭代器表达式赋值,开始新的迭代;
迭代器的语法:
- yield $key=>$val ; //将变量$val的值及其键值$key,压入序列
- yield $val ; //将变量$val的值及自动生成自增键值,压入序列
- yield null ; //将值null及自动生成的自增键值,压入序列
敏捷的同学可能已经注意到,迭代器可以用来方便的处理过去需要将整个文件读入数组然后,逐行处理的情况,从而节省大量内存,进一步的研究,留给大家去尝试。
二,coroutines-协程
这个概念同样来源于Python,而且直接跟迭代器Generators有关。当如下定义一个函数时:
- function receiver(){
- while(true){
- n = yield ;
- echo "Got {$n}" ;
- }
- }
就定义了一个协程。执行该函数时,会响应发送个它的值。其行为也非常类似于生成器。如下调用该函数:
- $r = receiver() ;
- $r->send(1) ; //输出 Got 1
- $r->send(2) ; //输出 Got 2
回想到上面介绍的Generator对象的send()方法,用来为yield表达式赋值,并且进行下一次的迭代。与Python的协程不太相同的是,PHP的协程并不需要事先通过调用next()方法,从而执行到第一个yield表达式,因此不用先调用$r->next();再为当前的表达式赋值。(Python中还可以通过装饰器来包装协程来自动完成该步骤)
协程与生成器可以方便的建立一个处理管道,从而方便的处理一些特定的问题。例如:
- function find_files($target){
- while(true){
- list($dir, $pattern) = yield ;
- foreach(glob("{$dir}/*.php") as $file){
- if(preg_match($pattern, $file)){
- $target->send($file) ;
- }
- }
- }
- }
- function open_file($target){
- while(true){
- $file = yield ;
- if($fileHandle = fopen($file, 'r')){
- $target->send($fileHandle) ;
- }
- }
- }
- function read_line($target){
- while(true){
- $fileHandle = yield ;
- if(false !== $line = fread($fileHandle)){
- $target->send($line) ;
- }
- }
- }
- function grep($pattern, $target){
- while(true){
- $line = yield ;
- if(preg_match($pattern, $line)){
- $target->send($line) ;
- }
- }
- }
- function printer(){
- while(true){
- $line = yield ;
- print("%s\n", $line) ;
- }
- }
- $finder = find_files(open_file(read_line(grep('#php#', printer())))) ;
- $finder->send('/home/jerry', '#wow#') ;
该例子重写了一个经典的Python协程的例子,完全可以向大家展示协程的管道式编程。只要不断的给协程灌入数据(不断调用$finder->send()),就会得到想要的结果,是不是很炫。
三,password hash api – 新的密码哈希API
上半年,国内网站爆出的暴库事件,想必大家还记忆犹新。PHP程序员,似乎想来对于密码加密问题,关心不多。简单的md5方式,然后放到数据库中,非常容易被暴力破解。而且这种方式只能称得上是一种签名,并不能起到加密的效果。虽然PHP提供了强大的crypt(),但是由于其操作复杂,而且某些算法会消耗较长的cpu时间,并不为大家所用。
现在好了,官方提供了简单易用的密码哈希API,为大家进行密码加密提供了非常便捷的方法。虽然该函数只是对crypt()的很薄的封装,但是已经非常好的在便捷性与安全性上寻得一个平衡点。新增了如下四个函数:
- string password_hash(string $password, int $algo, array $options = array()) //该函数返回密码的哈希,第二个参数$algo标识哈希使用的算法,第三个参数$options是哈希算法可能用到的参数;
- bool password_verify($password, $hash) //该函数用来校验,原始密码与哈希值,该哈希值应该是password_hash或者crypt()返回的;
- bool password_needs_rehash(string $hash, int $algo, array $options = array()) //该函数用来校验,哈希与提供的算法及算法参数是否一致;
- array password_get_info(string $hash) //该函数返回哈希的算法及算法参数
- 算法参数
- PASSWORD_BCRYPT //使用CRYPT_BLOWFISH算法返回一个哈希
- PASSWORD_DEFAULT = PASSWORD_BCRYPT //默认的哈希算法,如果省略$algo,会默认使用该算法,而且以后可能会随PHP版本升级
示例如下:
- $password = 'php' ;
- $hash = password_hash($password, PASSWORD_BCRYPT, array('cost'=>7, 'salt'=>'easysalt')) ;
- if(password_verify($pasword, $hash)){
- //match
- }else{
- //not match
- }
cost的取值范围是4~31,salt值可以省略,这样会默认生成一个。
该函数将PHP的易用性表达的很好,为了程序员的使用方便,即便是一层很薄的crypt()的封装,都可能让日后PHP的程序更加牢固。
四,在foreach语句中适用list
list函数用来将数组元素拆包,现在官方将便利性更进一步,直接可以在foreach中使用list了。
- $users = array(
- array('Foo', 'Bar'),
- array('Baz', 'Qux');
- );
- foreach ($users as list($firstName, $lastName)) {
- echo "First name: $firstName, last name: $lastName. ";
- }
例子来源于官方文档,大家应该一看就明白了。
五,字符串与数组元素新的索引方式
该功能已经出现在了PHP5.4中,可以看下官方的例子:
- function getArray() {
- return array(1, 2, 3);
- }
- // on PHP 5.4
- $secondElement = getArray()[1];
- // previously
- $tmp = getArray();
- $secondElement = $tmp[1];
- // or
- list(, $secondElement) = getArray();
这种索引方式,我们在javascript中应该见过,现在PHP也支持该语法了,还要感谢鸟哥的贡献,为大家提供了非常便捷的语法糖。
六,支持在try/catch块中使用finally关键字
同样在其他语言中,早已经实现了该关键字,主要用来对异常处理中做一些cleanup工作。可以看下面的例子:
- function foo(){
- try{
- throw new Exception("MM coming...") ;
- } catch {
- echo "haha, i catch you" ;
- } finally {
- echo "no matter what happened , i always here for you";
- }
- }
该语法同样由鸟哥实现,可以看他的这篇文章做进一步的了解。
七,empty()函数参数扩大为任意表达式
过去empty()与isset()只接受变量做为参数,如果直接如下写:
- if(isset(get_parsed_parameters())){
- //do something
- }
会抛出 “Can’t use function return value in write context” 的异常,现在方便了,终于由可以方便的按照直觉去写代码了,想必这项改动会方便很多新手,减少学习过程中的疑惑与挫败感。
总之,PHP5.5的新特性,生成器与协程是一项比较大的进步,其他方面也进一步的方便了开发者的使用,为大家的开发提供了更多的便利。