2012年11月16号PHP5.5的第一个alpha版发布了,里面包含了一些期待已久的特性,以及从其他语言借鉴而来的新特性。本文会浅析这些新特性,使大家对此有所了解。
一,Generators-生成器
使用过Python的人对此并不陌生,生成器的定义是:“生成器是一个函数,它生成一个值的序列,以便在迭代中适用”。
可以简单的理解为一种简单的迭代器的生成方案。(相对于实现PHP中的迭代器,生成器更为便捷)

  1. function countdown(n){
  2. while(n>0){
  3. yield n ;
  4. }
  5. }
  6. foreach(countdown(10) as $val){
  7. //do something with $val
  8. }

当调用countdonw(10)时,函数不会马上执行,而返回一个迭代器对象,该对象实现了Iterator的借口,从而可以在foreach中被遍历。每当PHP调用Iterator::next()函数时,生成器函数将不断执行语句,直至遇到yield语句位置,并返回当前Iterator::current()的值。迭代器的原型如下:

  1. final class Generator implements Iterator {
  2. void rewind();
  3. bool valid();
  4. mixed current();
  5. mixed key();
  6. void next();
  7.  
  8. mixed send(mixed $value);
  9. }

其中:
通常情况下,rewind()方法是不允许调用的,除非generator处于初始状态,因为generator通常用于对数据的一次性迭代,rewind()一个已经开始调用的generator会抛出一个异常;
valid()方法可以用以检测,迭代器是否已经结束;
current()方法返回当前的值或者为null;
key()方法返回当前值的键值,如果未指定,则返回一个从0开始的自增值;
next()函数用来继续调用迭代器对象;
sent()函数用来对迭代器表达式赋值,开始新的迭代;

迭代器的语法:

  1. yield $key=>$val ; //将变量$val的值及其键值$key,压入序列
  2. yield $val ; //将变量$val的值及自动生成自增键值,压入序列
  3. yield null ; //将值null及自动生成的自增键值,压入序列

敏捷的同学可能已经注意到,迭代器可以用来方便的处理过去需要将整个文件读入数组然后,逐行处理的情况,从而节省大量内存,进一步的研究,留给大家去尝试。

二,coroutines-协程
这个概念同样来源于Python,而且直接跟迭代器Generators有关。当如下定义一个函数时:

  1. function receiver(){
  2. while(true){
  3. n = yield ;
  4. echo "Got {$n}" ;
  5. }
  6. }

就定义了一个协程。执行该函数时,会响应发送个它的值。其行为也非常类似于生成器。如下调用该函数:

  1. $r = receiver() ;
  2. $r->send(1) ; //输出 Got 1
  3. $r->send(2) ; //输出 Got 2

回想到上面介绍的Generator对象的send()方法,用来为yield表达式赋值,并且进行下一次的迭代。与Python的协程不太相同的是,PHP的协程并不需要事先通过调用next()方法,从而执行到第一个yield表达式,因此不用先调用$r->next();再为当前的表达式赋值。(Python中还可以通过装饰器来包装协程来自动完成该步骤)
协程与生成器可以方便的建立一个处理管道,从而方便的处理一些特定的问题。例如:

  1. function find_files($target){
  2. while(true){
  3. list($dir, $pattern) = yield ;
  4. foreach(glob("{$dir}/*.php") as $file){
  5. if(preg_match($pattern, $file)){
  6. $target->send($file) ;
  7. }
  8. }
  9. }
  10. }
  11.  
  12. function open_file($target){
  13. while(true){
  14. $file = yield ;
  15. if($fileHandle = fopen($file, 'r')){
  16. $target->send($fileHandle) ;
  17. }
  18. }
  19. }
  20.  
  21. function read_line($target){
  22. while(true){
  23. $fileHandle = yield ;
  24. if(false !== $line = fread($fileHandle)){
  25. $target->send($line) ;
  26. }
  27. }
  28. }
  29.  
  30. function grep($pattern, $target){
  31. while(true){
  32. $line = yield ;
  33. if(preg_match($pattern, $line)){
  34. $target->send($line) ;
  35. }
  36. }
  37. }
  38.  
  39. function printer(){
  40. while(true){
  41. $line = yield ;
  42. print("%s\n", $line) ;
  43. }
  44. }
  45.  
  46. $finder = find_files(open_file(read_line(grep('#php#', printer())))) ;
  47. $finder->send('/home/jerry', '#wow#') ;

该例子重写了一个经典的Python协程的例子,完全可以向大家展示协程的管道式编程。只要不断的给协程灌入数据(不断调用$finder->send()),就会得到想要的结果,是不是很炫。

三,password hash api – 新的密码哈希API
上半年,国内网站爆出的暴库事件,想必大家还记忆犹新。PHP程序员,似乎想来对于密码加密问题,关心不多。简单的md5方式,然后放到数据库中,非常容易被暴力破解。而且这种方式只能称得上是一种签名,并不能起到加密的效果。虽然PHP提供了强大的crypt(),但是由于其操作复杂,而且某些算法会消耗较长的cpu时间,并不为大家所用。
现在好了,官方提供了简单易用的密码哈希API,为大家进行密码加密提供了非常便捷的方法。虽然该函数只是对crypt()的很薄的封装,但是已经非常好的在便捷性与安全性上寻得一个平衡点。新增了如下四个函数:

  1. string password_hash(string $password, int $algo, array $options = array()) //该函数返回密码的哈希,第二个参数$algo标识哈希使用的算法,第三个参数$options是哈希算法可能用到的参数;
  2. bool password_verify($password, $hash) //该函数用来校验,原始密码与哈希值,该哈希值应该是password_hash或者crypt()返回的;
  3. bool password_needs_rehash(string $hash, int $algo, array $options = array()) //该函数用来校验,哈希与提供的算法及算法参数是否一致;
  4. array password_get_info(string $hash) //该函数返回哈希的算法及算法参数
  5.  
  6. 算法参数
  7. PASSWORD_BCRYPT //使用CRYPT_BLOWFISH算法返回一个哈希
  8. PASSWORD_DEFAULT = PASSWORD_BCRYPT //默认的哈希算法,如果省略$algo,会默认使用该算法,而且以后可能会随PHP版本升级

示例如下:

  1. $password = 'php' ;
  2. $hash = password_hash($password, PASSWORD_BCRYPT, array('cost'=>7, 'salt'=>'easysalt')) ;
  3. if(password_verify($pasword, $hash)){
  4. //match
  5. }else{
  6. //not match
  7. }

cost的取值范围是4~31,salt值可以省略,这样会默认生成一个。

该函数将PHP的易用性表达的很好,为了程序员的使用方便,即便是一层很薄的crypt()的封装,都可能让日后PHP的程序更加牢固。

四,在foreach语句中适用list
list函数用来将数组元素拆包,现在官方将便利性更进一步,直接可以在foreach中使用list了。

  1. $users = array(
  2. array('Foo', 'Bar'),
  3. array('Baz', 'Qux');
  4. );
  5.  
  6. foreach ($users as list($firstName, $lastName)) {
  7. echo "First name: $firstName, last name: $lastName. ";
  8. }

例子来源于官方文档,大家应该一看就明白了。

五,字符串与数组元素新的索引方式
该功能已经出现在了PHP5.4中,可以看下官方的例子:

  1. function getArray() {
  2. return array(1, 2, 3);
  3. }
  4.  
  5. // on PHP 5.4
  6. $secondElement = getArray()[1];
  7.  
  8. // previously
  9. $tmp = getArray();
  10. $secondElement = $tmp[1];
  11.  
  12. // or
  13. list(, $secondElement) = getArray();

这种索引方式,我们在javascript中应该见过,现在PHP也支持该语法了,还要感谢鸟哥的贡献,为大家提供了非常便捷的语法糖。

六,支持在try/catch块中使用finally关键字
同样在其他语言中,早已经实现了该关键字,主要用来对异常处理中做一些cleanup工作。可以看下面的例子:

  1. function foo(){
  2. try{
  3. throw new Exception("MM coming...") ;
  4. } catch {
  5. echo "haha, i catch you" ;
  6. } finally {
  7. echo "no matter what happened , i always here for you";
  8. }
  9. }

该语法同样由鸟哥实现,可以看他的这篇文章做进一步的了解。

七,empty()函数参数扩大为任意表达式
过去empty()与isset()只接受变量做为参数,如果直接如下写:

  1. if(isset(get_parsed_parameters())){
  2. //do something
  3. }

会抛出 “Can’t use function return value in write context” 的异常,现在方便了,终于由可以方便的按照直觉去写代码了,想必这项改动会方便很多新手,减少学习过程中的疑惑与挫败感。

总之,PHP5.5的新特性,生成器与协程是一项比较大的进步,其他方面也进一步的方便了开发者的使用,为大家的开发提供了更多的便利。

PHP5.5 alpha 的新特性
标签: