PHP 5.3 新特性总结

一、前言
早在PHP5的发布在面向对象编程(OOP)和设计方面迈出了很大的进步,它提供了很多新特性,例如类的可见性、类反射等。可以说它为PHP面向对象编程做出了不可磨灭的贡献。PHP V5.3在OOP方面提供了大量的补充,它的发布旨在为开发人员在未来使用 PHP V6 做准备,被誉为 “只缺少原生 Unicode 支持的 PHP V6”,它增加了一些新的特性,删除了一些在未来PHP版本中不会再出现的特性,在做coolPHP的开发中,笔者根据实际使用总结了一些PHP5.3的新特性,这只是茶余饭后的一篇总结罢了...

二、新特性
1、新的魔术方法__callStatic()
了解这个方法之前,有必要了解一下静态方法和成员变量以及PHP5中魔术方法。早在PHP V4中就支持对方法和类成员的静态访问,但是它不能够将方法或者成员指定为专门用于静态访问的属性,比如在PHP V4中

<?php
class Test {
var $name;

function Test(){
$this->name = 'kokko';
}

function static1() {
return '1';
}

function static2() {
// 在PHP5中我们使用self::static1()方式调用动态方法
return Test::static1() . ' and 2';
}
}

//现在我们使用static的方式调用方法,将返回‘1 and 2’
echo Test::static2();
?>

我们可以使用静态的方式调用Test::static2(),而不需要声明addItem为静态访问,而在PHP V5中我们能够将方法和成员变量声明为静态的,我们可以将上述代码改为

<?php
class Test {
static $name = 'kokko';

static function static1() {
return '1';
}
static function static2() {
// 在PHP5中我们使用self::static1()方式调用动态方法
return self::static1() . ' and 2';
}
}

//现在我们使用static的方式调用方法,将返回‘1 and 2’
echo Test::static2();
?>

PHP5中不仅仅提供了对静态变量的定义,还提供了许多Magic Methods(魔术方法),魔术方法的引入提供了(比如__call)重载方法的支持(JAVA开发者很熟悉这个概念,就是一个方法允许多个不同的类型的参数)和多态的支持,极大的丰富了OOP编程方法,使得PHP也可以利用各种设计模式Coding(记得很早就有人曾经问过我Head First设计模式本书中的鸭子能在PHP中实现吗?)。在PHP V5.3中添加了一种姓的魔术方法:__callStatic(),他的工作方式与__call()魔术方法没有太大的区别,__call()它专门用来处理那些没有在类中定义的或者对类不可见的方法的调用而生的,而__callStatic是用来处理静态方法的调用,它可以提供更好的方法重载方式。比如:

class Test{
public static function __callStatic( $name,$args ){
return "静态调用{$name}方法!";
}

public function __call($name,$args){
return "调用{$name}方法";
}
}

echo Test::getItem(1); //输出‘静态调用getItem方法!’
$test = new Test();
$test->getItem(1); //输出‘调用getItem方法!’


2、动态的调用静态方法和成员变量

<?php
class Test
{
public static $name = 'kokko';

public static function doTest()
{
return 'testing...';
}
}

$class = 'Test';
$action = 'doTest';
$name = '$name';

//输出 "testing..."
echo Test::$action();
//输出 'kokko'
echo Test::$name;
//输出 "Parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM, expecting ',' or ';' in E:\eusite\php3\test.php on line 13"
echo $class::$action();
//输出 "Parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM, expecting ',' or ';' in E:\eusite\php3\test.php on line 20"
echo $class::$name;
?>

在PHP以往的版本中是不可以通过可变变量(可以使用某个变量的字符串值指定另一个变量的名称)的方式调用静态方法的,PHP V5.3中添加了动态的调用静态方法的特性,我们现在可以使用$class::$action()的方式调用静态方法了。

3、赋予static关键字新的特性实现延迟静态绑定

<?php
class Test
{
public static $name = 'kokko';

public static function doTest()
{
return self::$name;
}
}

class Test2 extends Test{
public static $name = 'kokko won';
}

//输出kokko
echo Test2::doTest();
?>

上述代码我本来实现输出'kokko won',但是实际上事与愿违,doTest方法中调用的self::$name由于是在Test类中完成的,所以输出的是'kokko',我们要想达到目的只有在子类中覆盖doTest静态方法,在PHP V5.3 中赋予了关键字static的新特性,允许针对当前类进行引用:

<?php
class Test
{
public static $name = 'kokko';

public static function doTest()
{
return static::$name;
}
}

class Test2 extends Test{
public static $name = 'kokko won';
}

//输出kokko won
echo Test2::doTest();
?>


4、新的SPL数据结构类和迭代器类
标准 PHP 库(Standard PHP Library,SPL)是 PHP V5 中新增的接口和类的集合,PHP V5.3继续进行扩展,增加了更多的类,比如SplDoublyLinkedList类以提供双重链接列表支持;SplStack实现堆支持;SplQueue实现队列支持;SplFixedArray实现一个固定大小的数组(PHP中默认的数组是可变大小的),该数据结构是固定大小的,而且不允许非数值型索引(NO $arr['kokko']),它的性能也是非常的快,据说比标准的内置数组快10%至30%,笔者测试了一下,将一个1000元素的数组比较是快20%左右(结果更机器还是有关系的,笔者的本本刚买不久)!另外标准库中还新增了FilesystemIterator和GlobIterator等迭代器类。更重要的一点是在PHP V5.3中不能禁用对SPL的支持,大家应该可以看出点什么来!

<?php
$stack = new SplStack();
$stack->push('kokko');
$stack->push('won');

//count 可以获取堆中元素数目
echo count($stack); // 输出2

//循环输出元素
foreach ( $stack as $item ) {
echo $item."\n";
}

// 出栈,输出won
echo $stack->pop();

//计算当期堆中元素数
echo count($stack); // 输出 1
?>


5、垃圾回收及自动内存释放
我弟弟写C和C++,他看到我写PHP,变量从来不用释放,他说这么写代码真爽,你们就不担心内存泄漏吗?
与JAVA类似,PHP有一个很简单的垃圾收集器,它内部通过一个引用计算器对不再位于内存范围的对象进行垃圾收集管理,当某对象的引用计数器为0时,对象将被当作垃圾收集并从内存中删除,这种方式有一问题,如果两个对象之间存在着相互引用的关系,如“父对象-子对象”,这种情况下,即便父对象被垃圾回收,这些对象的引用计算器没有被收集,因此这些对象的只有等待脚本执行完成之后才能够进行释放,即使对父对象调用 unset()也不会释放在子对象中引用父对象的内存。 (参考http://bugs.php.net/bug.php?id=33595)示例如下:

<?php
class A {
public $b;

function __construct () {
$this->b = new B($this);
}
}

class B {
public $parent;

function __construct ($parent = NULL) {
$this->parent = $parent;
}
}

for ($i = 0 ; $i < 1000000 ; $i++) {
$a = new A();
}
?>

我运行的结果是Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 32 bytes) in E:\eusite\php3\test.php on line 19
我将代码修改如下:

<?php
class A {
function __construct () {
$this->b = new B($this);
}
}

class B {
function __construct ($parent = NULL) {
$this->parent = $parent;
}
}

for ($i = 0 ; $i < 1000000 ; $i++) {
$a = new A();
unset($a);
}

//输出结果是:63,520。
echo number_format(memory_get_usage());
?>

这里我采用手动unset的方式释放父对象内存,表面上看似我手动将对象内存释放,但是如果我们将测试代码改成下面这样:

<?php
class A {
function __construct () {
$this->b = new B($this);
}
}

class B {
function __construct ($parent = NULL) {
$this->parent = $parent;
}
}

for ($i = 0 ; $i < 1000000 ; $i++) {
$a = new A();
unset($a);
echo number_format(memory_get_usage()) . "<br>";
}
?>

输出结果如下:
62,400
62,800
63,176
63,552
...
33,550,992
33,551,368
33,551,744
33,552,120
Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 42 bytes) in E:\eusite\php3\test.php on line 10
你会发现输出值一个比一个大,证明我们在unset对象的时候并没有释放A类中对B类的引用的内存,如若不然,数值的输出应该是一样的。http://bugs.php.net/bug.php?id=33595中提到了一种解决方案:

<?php
class A {
function __construct () {
$this->b = new B($this);
}

function __destruct(){
unset($this->b);
}
}

class B {
function __construct ($parent = NULL) {
$this->parent = $parent;
}
}

for ($i = 0 ; $i < 1000000 ; $i++) {
$a = new A();
$a->__destruct();
unset($a);
echo number_format(memory_get_usage()) . "<br>";
}
?>

输出结果如下:
63,496
63,496
...
63,496
63,496
end
63,496

在上述情况下每当创建 A 类的实例并且该实例随后超出内存范围时,内存不会被释放,因此脚本在内存使用中不断增加,只有为父类创建一个解构函数将直接释放子对象,这种解构器必须在解除父类引用之前进行调用,执行这些代码那就跟写C、C++没啥区别了。
PHP V5.3解决了这个问题,垃圾收集器将检测这些循环引用,并且能够释放它们所占用的内存,因此在执行脚本时 PHP 内存使用情况将保持平稳。当A类的每个引用被删除后,A类中的B类引用也将会被当作垃圾收集。这种循环垃圾收集的方式使得PHP OOP的性能将获得大大的提升,改善内存使用做出不可磨灭的贡献。

6、新添闭包和反射以及lambda函数的支持
最JS前端开发的程序员和Python 和 Ruby开发者,对这两者应该不陌生。比如JavaScript中使用闭包方式构建对象。在PHP V3中添加了闭包和lambda的支持,这意味着函数可以动态创建并作为参数传递给其他方法。

1)闭包
通过闭包,我们可以进一步将逻辑封装到一个指定范围,在很小的范围内在绑定的特定函数。写过Ruby代码的人应该都知道Ruby 中的闭包使用的魔法表现,这里举一个很实用的例子。

<?php
class Logger{
public static $mes = array();

public static function log($code,$string){
self::$mes[] = "[$code]:".$string;
}
}

class Blog{

public function __construct(){
}

public function addComment($id,$data){
$sql = sprintf( "INSERT INTO comments SET blog_id=%d,user_id=%d,content='%s'",$id,$data['user_id'],$data['content']);
Logger::log('database',$sql);
$sql = sprintf( "UPDATE blogs SET comments=comments+1 WHERE id=%d",$id );
Logger::log('database',$sql);
$sql = sprintf( "UPDATE users SET comments=comments+1 WHERE id=%d",$data['user_id'] );
Logger::log('database',$sql);
}
}

$blog = new Blog();
$blog->addComment(1,array(
'user_id' => 1,
'content' => 'hello',
));

print_r(Logger::$mes);
?>

上述代码中,每次记录日志都得调用Logger::log静态方法,一旦Logger::log的参数变化,就必须挨个挨个改变代码,下面改用闭包的方式实现将日志记录的封装到addComment方法的本身范围内。

<?php
class Logger{
public static $mes = array();

public static function log($code,$string){
self::$mes[] = "[$code]:".$string;
}
}

class Blog{

public function __construct(){
}

public function addComment($id,$data){
$code = 'database';
$log = function ($sql) use ($code) { Logger::log($code,$sql); };
$sql = sprintf( "INSERT INTO comments SET blog_id=%d,user_id=%d,content='%s'",$id,$data['user_id'],$data['content']);
$log($sql);
$sql = sprintf( "UPDATE blogs SET comments=comments+1 WHERE id=%d",$id );
$log($sql);
$sql = sprintf( "UPDATE users SET comments=comments+1 WHERE id=%d",$data['user_id'] );
$log($sql);
}
}

$blog = new Blog();
$blog->addComment(1,array(
'user_id' => 1,
'content' => 'hello',
));

print_r(Logger::$mes);
?>

也可以将闭包用在类对象中,类中定义的闭包基本上与在对象外部定义的闭包相同,惟一的不同之处在于通过 $this 变量自动导入对象,我们可以在类闭包中通过$this->name的方式访问类中name成员变量;我们还可以通过将闭包定义为静态闭包,这样与类中定义闭包的区别在于后者将不再将$this变量传入到闭包中。比如:

<?php
class Test{
$name = 'kokko';

function doAct($act){
return function() use $act{
return $act.','.$this->name;
}
}
}

$test = new Test();
echo $test->doAct('hi'); //输出hi,kokko
?>

如果在闭包function前面加上static关键字'return static function() user $act',那么闭包代码将不能访问类Test中的name属性了,通常我们只有在不使用类本身成员属性的情况下才将闭包声明为static静态闭包,因为它不需要将对象应用导入闭包中,可以节省内存。我们可以通过 __invoke() 方法来将类本身用作闭包。

<?php
class Test
{
public function __invoke()
{
return "Hi,I am kokko";
}
}

$test = new Test();
echo $test(); //输出'Hi,I am kokko'
?>


2)lambda函数
俗称匿名函数,是可以随时定义的简单抛弃型函数,函数本身仅存在于定义函数的变量范围内,因此当该变量超出范围时,函数也随之失效,有点类似函数中局部变量的意思。
早在PHP4中我们就可以通过create_function来实现匿名函数了,但是该方法并不太友好,并不适合太多代码的封装,感兴趣的朋友可以去看看PHP的手册相关章节,而PHP V3正式开始了对匿名函数的支持,我们以前实现匿名函数的方式有了很大的变化。

<?php
$av = array("the ", "a ", "that ", "this ");
$fcstr = '$v = $v . "mango";';
array_walk($av, create_function('&$v,$k',$fcstr ));
print_r($av);
?>

假如在create_function函数中代码存在稍微复杂一点的逻辑,比如弄几个if else,这代码就会相当的不便,PHP V5.3中我们可以这么写:

<?php
$av = array("the ", "a ", "that ", "this ");
array_walk($av,function(&$v,$k){
$v = $v.'mango';
});
print_r($av);
?>

代码可阅读性更强,如果代码中出现if、else等判断也不会造成编码的不便。

3)反射
JAVA程序员应该很熟悉反射这个词,现在PHP V5.3也支持对象反射和方法反射,它允许我们对类、接口、函数和方法执行反向工程。我们通过ReflectionMethod 和 ReflectionFunction、 ReflectionClass类中getClosure() 方法可以实现类、函数方法的反向工程。

<?php
class Test{
public $name = 'kokko';

public function setName($name){
$this->name = $name;
}

public function getName(){
return $this->name;
}
}
$class = new ReflectionClass('Test');
$method = $class->getMethod('getName');
$closure = $method->getClosure();
echo $closure(); //output 'kokko'
$class->setName('won');
echo $closure(); //output 'won'
?>

通过反射我们还可以访问类的私有方法,可以内省闭包,将反射 API 能够通过现有函数和方法动态创建闭包,从而为闭包提供强大的支持。

<?php
$closure = function ($x, $y = 1) {};
$m = new ReflectionMethod($closure);
Reflection::export ($m);
//output
/*
Method [ <internal> public method __invoke ] {
- Parameters [2] {
Parameter #0 [ <required> $x ]
Parameter #1 [ <optional> $y ]
}
}
*/
?>


7、goto的支持
PHP V5.3中添加了像VB中的goto的支持直接跳过一段代码执行

<?php
$headers = Array('subject', 'bcc', 'to', 'cc', 'date', 'sender');
$position = 0;

hIterator: {
$c = 0;
echo $headers[$position] . PHP_EOL;

cIterator: {
echo ' ' . $headers[$position][$c] . PHP_EOL;

if(!isset($headers[$position][++$c])) {
goto cIteratorExit;
}
goto cIterator;
}

cIteratorExit: {
if(isset($headers[++$position])) {
goto hIterator;
}
}
}
?>


8、命名空间
PHP 5.3中开始更全面的OOP支持,为了帮助组织大型的代码库,添加namespace关键字支持命名空间。我们可以通过 namespace来声明一个命名空间来更好的组织我们的代码,而不会引起类的命名冲突问题,值得注意的是namespace的声明必须是文件中的第一个命令或输出。

<?php
namespace core;
class app {
}
?>

使用带有名称空间的代码
<coolcode lang="php" download="app.php">
<?php
namespace eu;
include('core/app.php');
class App extends core\App{
}
?>
[/code]
在名称空间内调用函数

<?php
namespace comm;
function doSomething(){
echo "comm.doSomthing";
}
?>


<?php
namespace user;
function doSomething(){
echo "user.doSomthing";
}
?>


<?php
include 'comm.php';
use comm;
doSomething(); // outputs "comm.doSomthing";
?>

<?php
include 'comm.php';
include 'user.php';
comm\doSomething(); // outputs "comm.doSomthing";
user\doSomething(); // outputs "user.doSomthing";
?>
[/code]

9、使用Phar归档
Phar归档的概念来自Java中JAR归档,它允许使用单个文件打包应用程序,这个文件中包含运行应用程序所需的所有东西。关于该特性CoolPHP中还没有涉及到相关的应用,所以也不做更多是示例说明。有兴趣的朋友可以Google一下,应该有不少资料!

原文:[url]http://www.kokkowon.com/archives/902[/url]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值