PHP5新特性: 更加面向对象化的PHP


Posted in PHP onNovember 18, 2006

PHP处理对象部分的内核完全重新开发过,提供更多功能的同时也提高了性能。在以前版本的php中,处理对象和处理基本类型(数字,字符串)的方式是一样的。这种方式的缺陷是:当将对象赋值给一个变量时,或者通过参数传递对象时,对象将被完全拷贝一份。在新的版本里,上述操作将传递引用(可以把引用理解成对象的标识符),而非值。

很多PHP程序员可能甚至没有察觉到老的对象处理方式。事实上,大多数的php应用都可以很好地运行。或者仅仅需要很少的改动。

私有和受保护成员
PHP5引入了私有和受保护成员变量的概念。我们可以用它来定义类成员的可见性。

例子
受保护成员可以被子类访问, 而私有成员只能被类本身访问。

<?php
class MyClass {
   private $Hello = "Hello, World!\n";
   protected $Bar = "Hello, Foo!\n";
   protected $Foo = "Hello, Bar!\n";

   function printHello() {
       print "MyClass::printHello() " . $this->Hello;
       print "MyClass::printHello() " . $this->Bar;
       print "MyClass::printHello() " . $this->Foo;
   }
}

class MyClass2 extends MyClass {
   protected $Foo;

   function printHello() {
       MyClass::printHello();                          /* Should print */
       print "MyClass2::printHello() " . $this->Hello; /* Shouldn't print out anything */
       print "MyClass2::printHello() " . $this->Bar;  /* Shouldn't print (not declared)*/
       print "MyClass2::printHello() " . $this->Foo;  /* Should print */
   }
}

$obj = new MyClass();
print $obj->Hello;  /* Shouldn't print out anything */
print $obj->Bar;    /* Shouldn't print out anything */
print $obj->Foo;    /* Shouldn't print out anything */
$obj->printHello(); /* Should print */

$obj = new MyClass2();
print $obj->Hello;  /* Shouldn't print out anything */
print $obj->Bar;    /* Shouldn't print out anything */
print $obj->Foo;    /* Shouldn't print out anything */
$obj->printHello();
?> 

私有方法和受保护方法
PHP5也引入了私有方法和受保护方法的概念。

例子:
<?php
class Foo {
   private function aPrivateMethod() {
       echo "Foo::aPrivateMethod() called.\n";
   }

   protected function aProtectedMethod() {
       echo "Foo::aProtectedMethod() called.\n";
       $this->aPrivateMethod();
   }
}

class Bar extends Foo {
   public function aPublicMethod() {
       echo "Bar::aPublicMethod() called.\n";
       $this->aProtectedMethod();
   }
}

$o = new Bar;
$o->aPublicMethod();
?> 

以前的不使用类的老代码,没有访问修饰符(public, protected, private)的代码可以不经改动运行。

抽象类和抽象方法
Php5也引入了抽象类和抽象方法的概念。抽象方法只是声明了方法的签名并不提供它的实现。包含抽象方法的类必须被声明成抽象类。

例子:
<?php
abstract class AbstractClass {
   abstract public function test();
}

class ImplementedClass extends AbstractClass {
   public function test() {
       echo "ImplementedClass::test() called.\n";
   }
}

$o = new ImplementedClass;
$o->test();
?> 

抽象类不能被实例化。以前的不使用抽象类的老代码可以不经改动运行。

接口
Php5引入了接口。一个类可以实现多个接口。

例子:
<?php
interface Throwable {
   public function getMessage();
}

class MyException implements Throwable {
   public function getMessage() {
       // ...
   }
}
?> 

以前的不使用接口的老代码可以不经改动运行

 

类的型别提示

PHP5依然是弱类型的,不过在定义函数参数时,可以使用类的型别提示来声明期望传入的对象类型

Example
<?php
interface Foo {
   function a(Foo $foo);
}

interface Bar {
   function b(Bar $bar);
}

class FooBar implements Foo, Bar {
   function a(Foo $foo) {
       // ...
   }

   function b(Bar $bar) {
       // ...
   }
}

$a = new FooBar;
$b = new FooBar;

$a->a($b);
$a->b($b);
?> 

和其他强类型语言一样,php5类的型别提示在运行期间检查而非编译期间检查。即:

<?php
function foo(ClassName $object) {
   // ...
}
?> 

和下面的代码是一样的:

<?php
function foo($object) {
   if (!($object instanceof ClassName)) {
       die("Argument 1 must be an instance of ClassName");
   }
}
?> 

这个语法只适用于类,不适用于内建类型。 

Final

PHP 5 引入了final关键字来声明final成员和final方法。final成员和final方法不能被子类覆盖。

Example
<?php
class Foo {
   final function bar() {
       // ...
   }
}
?> 

更进一步,可以把类声明成final。将类声明成final可以阻止这个类被继承。final类里面的方法缺省地都是final的,无需再声明一次。

Example
<?php
final class Foo {
   // class definition
}

// the next line is impossible
// class Bork extends Foo {}
?> 

属性不能定义成为final.

以前的不使用final的老代码可以不经改动运行.

对象克隆
Php4没有提供一种机制来让用户自己定义复制构造子(copy constructor)控制对象的复制过程。Php4做二进制的拷贝,因而很精确地复制了对象的所有属性。

精确地复制对象的所有属性可能并不是我们一直想要的。有个例子可以很好地说明我们确实需要复制构造子:比如一个GTK Window的对象 a。 a持有它所需要的全部资源。当复制的这个GTK Window到对象b时候,我们更希望b持有新的资源对象。再举个例子:对象a包含了一个对象c, 当你把对象a 复制到对象c的时候。我们可能更希望对象b包含一个新的对象c的copy, 而不是一个对象c的引用。(译者注:这里所说的就是浅克隆和深克隆。)

对象的复制是通过clone这个关键字达到的(Clone调用被克隆对象的__clone()方法)。对象的__clone方法不能够直接被调用。

<?php
$copy_of_object = clone $object;
?> 

当developer创建对象的一份拷贝的时候,php5将会检查 __clone()方法是否存在。如果不存在,那么它就会呼叫缺省的__clone()方法,复制对象的所有属性。如果__clone()方法已经定义过,那么_clone()方法就会负责设置新对象的属性。为了方便起见,Engine会缺省地复制所有的属性。所以在__clone()方法中,只需要覆盖那些需要更改的属性就可以了。如下:
Example
<?php
class MyCloneable {
   static $id = 0;

   function MyCloneable() {
       $this->id = self::$id++;
   }

   function __clone() {
       $this->address = "New York";
       $this->id = self::$id++;
   }
}

$obj = new MyCloneable();

$obj->name = "Hello";
$obj->address = "Tel-Aviv";

print $obj->id . "\n";

$obj_cloned = clone $obj;

print $obj_cloned->id . "\n";
print $obj_cloned->name . "\n";
print $obj_cloned->address . "\n";
?> 

统一构造函数
Php5允许开发者声明一个类的构造方法。拥有构造方法的类在每次创建新的对象的时候都会呼叫这个方法,因此构造方法适合对象在被使用之前的初始化工作

Php4中,构造方法的名称和类的名称一样。考虑到从子类构造方法呼叫父类构造方法的情况是非常普遍的,而将类从一个继承体系中搬迁引起的父类变更就常常导致需要更改类的构造方法,php4的做法显然是不太合理的。

Php5引入了一个声明构建函数的标准方法: __construct().如下:

Example
<?php
class BaseClass {
   function __construct() {
       print "In BaseClass constructor\n";
   }
}

class SubClass extends BaseClass {
   function __construct() {
       parent::__construct();
       print "In SubClass constructor\n";
   }
}

$obj = new BaseClass();
$obj = new SubClass();
?> 

为保持向后的兼容性,如果php5不能够找到 __construct(),它会寻找老式的构造方法,即与类同名的方法。简单的说,只有当老代码里包含了一个__construct()方法的时候,才存在一个兼容性的问题。

析构方法
对于面向对象的编程来说,可以定义析构方法是非常有用的一个功能。析构方法可以用来记录调试信息,关闭数据库连接等等一些清除收尾的工作。Php4中没有析构方法,尽管php4已经支持可以注册一个函数以便请求结束的时候被调用。

Php5引进的析构方法的概念和其他面向对象的语言(比如java)是一致的。当指向这个对象的最后一个引用被销毁的时候,析构方法被调用,调用完成后释放内存。注意:析构方法不接受任何参数。

Example
<?php
class MyDestructableClass {
   function __construct() {
       print "In constructor\n";
       $this->name = "MyDestructableClass";
   }

   function __destruct() {
       print "Destroying " . $this->name . "\n";
   }
}

$obj = new MyDestructableClass();
?> 

和构建方法一样,父类的析构方法也不会被隐含调用。子类可以在自己的析构方法通过调用parent::__destruct()来显式地调用它。

Constants
Php5引入了class级别的常量。 

<?php
class Foo {
   const constant = "constant";
}

echo "Foo::constant = " . Foo::constant . "\n";
?> 

老的没有使用const的代码仍然正常运行。

Exceptions
Php4没有异常控制。Php5引入了和其它语言(java)相似的异常控制模式。应该注意的是php5里面支持捕捉全部异常,但是不支持finally子句。

在catch语句块里面,可以重新抛出异常。也可以有多个catch语句,在这种情况下,被捕捉到的异常从上往下依次比较和catch语句比较异常,第一个类型匹配的catch语句将会被执行。如果一直搜索到底还没有发现匹配的catch子句,则寻找下一个try/catch语句。最后不能捕捉的异常将被显示出来。如果异常被捕捉,那么程序会接着catch语句块的下面开始执行。

Example
<?php
class MyException {
   function __construct($exception) {
       $this->exception = $exception;
   }

   function Display() {
       print "MyException: $this->exception\n";
   }
}

class MyExceptionFoo extends MyException {
   function __construct($exception) {
       $this->exception = $exception;
   }

   function Display() {
       print "MyException: $this->exception\n";
   }
}

try {
   throw new MyExceptionFoo('Hello');
}
catch (MyException $exception) {
   $exception->Display();
}
catch (Exception $exception) {
   echo $exception;
}
?> 

上面的例子表明可以定义一个并不继承自 Exception的异常类,但是,最好还是从Exception继承并定义自己的异常。这是因为系统内建的Exception类能够收集到很多有用的信息, 而不继承它的异常类是得不到这些信息的。下面的php代码模仿了系统内建Exception类。每个属性后面都加了注释。每个属性都有一个getter,由于这些getter方法经常被系统内部处理调用,所以这些方法被标明了final。

Example
<?php
class Exception {
   function __construct(string $message=NULL, int code=0) {
       if (func_num_args()) {
           $this->message = $message;
       }
       $this->code = $code;
       $this->file = __FILE__; // of throw clause
       $this->line = __LINE__; // of throw clause
       $this->trace = debug_backtrace();
       $this->string = StringFormat($this);
   }

   protected $message = 'Unknown exception';  // exception message
   protected $code = 0; // user defined exception code
   protected $file;    // source filename of exception
   protected $line;    // source line of exception

   private $trace;      // backtrace of exception
   private $string;    // internal only!!

   final function getMessage() {
       return $this->message;
   }
   final function getCode() {
       return $this->code;
   }
   final function getFile() {
       return $this->file;
   }
   final function getTrace() {
       return $this->trace;
   }
   final function getTraceAsString() {
       return self::TraceFormat($this);
   }
   function _toString() {
       return $this->string;
   }
   static private function StringFormat(Exception $exception) {
       // ... a function not available in PHP scripts
       // that returns all relevant information as a string
   }
   static private function TraceFormat(Exception $exception) {
       // ... a function not available in PHP scripts
       // that returns the backtrace as a string
   }
}
?> 

如果我们定义的一异常类都是继承自Exception基类

无兼容性问题。老的代码不会受到这一特性的影响。

Dereferencing objects returned from functions
Php4中不能再次引用函数返回的对象以进一步呼叫返回对象的方法,而php5是可以的。

<?php
class Circle {
   function draw() {
       print "Circle\n";
   }
}

class Square {
   function draw() {
       print "Square\n";
   }
}

function ShapeFactoryMethod($shape) {
   switch ($shape) {
       case "Circle": 
           return new Circle();
       case "Square": 
           return new Square();
   }
}

ShapeFactoryMethod("Circle")->draw();
ShapeFactoryMethod("Square")->draw();
?> 

静态成员变量能够被初始化。
Example
<?php
class foo {
   static $my_static = 5;
   public $my_prop = 'bla';
}

print foo::$my_static;
$obj = new foo;
print $obj->my_prop;
?> 

静态方法
PHP 5 引入了静态方法,可以在不实例化类的情况下呼叫静态方法。

Example
<?php
class Foo {
   public static function aStaticMethod() {
       // ...
   }
}

Foo::aStaticMethod();
?> 

伪变量$this不能够在静态方法方法中使用。

instanceof
Php5引入了instanceof关键字,允许用它来测试一个对象是一个类的实例,或者是一个派生类的实例,或者实现了某个接口

Example
<?php
class baseClass { }

$a = new baseClass;

if ($a instanceof baseClass) {
   echo "Hello World";
}
?> 

Static function variables
现在,静态变量在编译阶段处理。因此程序员可以通过引用为静态变量赋值。这可以改善性能,不过,不能够使用对静态变量的间接引用了。

按引用传递的函数参数现在也可以设置缺省值了。

Example
<?php
function my_function(&$var = null) {
   if ($var === null) {
       die("$var needs to have a value");
   }
}
?> 

__autoload()
__autoload() 拦截函数在一个未声明的类被初始化的时候自动调用。该类的名字会被自动传递给__autoload()函数。而__autoload()也只有这么唯一的一个参数。

Example
<?php
function __autoload($className) {
   include_once $className . ".php";
}

$object = new ClassName;
?> 

可重载的方法呼叫和属性访问
方法呼叫和属性访问都能够通过__call, __get() and __set()方法重载。

Example: __get() and __set()
<?php
class Setter {
   public $n;
   public $x = array("a" => 1, "b" => 2, "c" => 3);

   function __get($nm) {
       print "Getting [$nm]\n";

       if (isset($this->x[$nm])) {
           $r = $this->x[$nm];
           print "Returning: $r\n";
           return $r;
       } else {
           print "Nothing!\n";
       }
   }

   function __set($nm, $val) {
       print "Setting [$nm] to $val\n";

       if (isset($this->x[$nm])) {
           $this->x[$nm] = $val;
           print "OK!\n";
       } else {
           print "Not OK!\n";
       }
   }
}

$foo = new Setter();
$foo->n = 1;
$foo->a = 100;
$foo->a++;
$foo->z++;
var_dump($foo);
?> 

Example: __call()
<?php
class Caller {
   private $x = array(1, 2, 3);

   function __call($m, $a) {
       print "Method $m called:\n";
       var_dump($a);
       return $this->x;
   }
}

$foo = new Caller();
$a = $foo->test(1, "2", 3.4, true);
var_dump($a);
?> 

迭代
当和foreach一起使用对象的时候,迭代的方式被重载过了。缺省的行为是迭代类的所有属性。

Example
<?php
class Foo {
   public $x = 1;
   public $y = 2;
}

$obj = new Foo;

foreach ($obj as $prp_name => $prop_value) {
   // using the property
}
?> 

一个类的所有对象都能够被迭代浏览到, 如果这个类实现了一个空的接口:Traversable. 换句话说,实现了Traversable接口的类可以和foreach一起使用。

接口 IteratorAggregate 和Iterator允许指定类的对象在代码中如何迭代。IteratorAggregate接口有一个方法:getIterator() 必须返回一个数组

Example
<?php
class ObjectIterator implements Iterator {

   private $obj;
   private $num;

   function __construct($obj) {
       $this->obj = $obj;
   }
   function rewind() {
       $this->num = 0;
   }
   function valid() {
       return $this->num < $this->obj->max;
   }
   function key() {
       return $this->num;
   }
   function current() {
       switch($this->num) {
           case 0: return "1st";
           case 1: return "2nd";
           case 2: return "3rd";
           default: return $this->num."th";
       }
   }
   function next() {
       $this->num++;
   }
}

class Object implements IteratorAggregate {

   public $max = 3;

   function getIterator() {
       return new ObjectIterator($this);
   }

$obj = new Object;

// this foreach ...
foreach($obj as $key => $val) {
   echo "$key = $val\n";
}

// matches the following 7 lines with the for directive.
$it = $obj->getIterator();
for($it->rewind(); $it->hasMore(); $it->next) {
   $key = $it->current();
   $val = $it->key();
   echo "$key = $val\n";
}
unset($it);
?> 

新的__toString方法
可以通过覆盖__toString方法来控制对象到字符串的转换。

Example
<?php
class Foo {
   function __toString() {
       return "What ever";
   }
}

$obj = new Foo;

echo $obj; // call __toString()
?> 

Reflection API
Php5引入了全套的反射API,以支持对类,接口,函数,方法的反向工程。

它也提供了API以从程序中提取注释文档。反射API的详细资料参考此处:http://sitten-polizei.de/php/reflection_api/docs/language.reflection.html

Example
<?php
class Foo {
   public $prop;
   function Func($name) {
       echo "Hello $name";
   }
}

reflection_class::export('Foo');
reflection_object::export(new Foo);
reflection_method::export('Foo', 'func');
reflection_property::export('Foo', 'prop');
reflection_extension::export('standard');
?> 

新内存管理机制
Php5有一个全新的内存管理机制,使得它在多线程的环境下可以更有效地运行。在分配和释放内存时,不再使用mutex锁定/解除锁定

PHP 相关文章推荐
模拟OICQ的实现思路和核心程序(一)
Oct 09 PHP
PHP获取当前文件所在目录 getcwd()函数
May 13 PHP
php站内搜索并高亮显示关键字的实现代码
Dec 29 PHP
php提示Call-time pass-by-reference has been deprecated in的解决方法[已测]
May 06 PHP
php cc攻击代码与防范方法
Oct 18 PHP
PHP获取当前url的具体方法全面解析
Nov 26 PHP
windows服务器中检测PHP SSL是否开启以及开启SSL的方法
Apr 25 PHP
PHP生成随机密码类分享
Jun 25 PHP
php通过修改header强制图片下载的方法
Mar 24 PHP
php中define用法实例
Jul 30 PHP
PHP中递归的实现实例详解
Nov 14 PHP
ThinkPHP框架实现的邮箱激活功能示例
Jun 15 PHP
Windows中安装Apache2和PHP4权威指南
Nov 18 #PHP
Win2003下APACHE+PHP5+MYSQL4+PHPMYADMIN 的简易安装配置
Nov 18 #PHP
Windows下的PHP5.0详解
Nov 18 #PHP
一些关于PHP的知识
Nov 17 #PHP
PHP4和PHP5共存于一系统
Nov 17 #PHP
Apache2 httpd.conf 中文版
Nov 17 #PHP
php环境配置 php5 mysql5 apache2 phpmyadmin安装与配置
Nov 17 #PHP
You might like
PHP XML数据解析代码
2010/05/26 PHP
简单的php写入数据库类代码分享
2011/07/26 PHP
php安装swoole扩展的方法
2015/03/19 PHP
Laravel 5框架学习之模型、控制器、视图基础流程
2015/04/08 PHP
使用ThinkPHP生成缩略图及显示
2017/04/27 PHP
关于IFRAME 自适应高度的研究
2006/07/20 Javascript
javascript读取xml
2006/11/04 Javascript
Jquery + Ajax调用webService实例代码(asp.net)
2010/08/27 Javascript
DIV菜单层实现代码
2010/11/19 Javascript
使用jquery操作session方法分享
2015/01/22 Javascript
javascript实现的淘宝旅行通用日历组件用法实例
2015/08/03 Javascript
jquery实现滑动特效代码
2015/08/10 Javascript
JavaScript下的时间格式处理函数Date.prototype.format
2016/01/27 Javascript
Angularjs中date过滤器失效的问题及解决方法
2018/07/06 Javascript
VeeValidate 的使用场景以及配置详解
2019/01/11 Javascript
JavaScript面试技巧之数组的一些不low操作
2019/03/22 Javascript
vc6编写python扩展的方法分享
2014/01/17 Python
python和shell实现的校验IP地址合法性脚本分享
2014/10/23 Python
Python和Perl绘制中国北京跑步地图的方法
2016/03/03 Python
python中类和实例如何绑定属性与方法示例详解
2017/08/18 Python
Python3实现将本地JSON大数据文件写入MySQL数据库的方法
2018/06/13 Python
BP神经网络原理及Python实现代码
2018/12/18 Python
对pandas写入读取h5文件的方法详解
2018/12/28 Python
基于Python实现用户管理系统
2019/02/26 Python
python 实现检验33品种数据是否是正态分布
2019/12/09 Python
PyTorch中反卷积的用法详解
2019/12/30 Python
python实现爱奇艺登陆密码RSA加密的方法示例详解
2020/05/27 Python
全球最大运动品牌的男装、女装和童装官方库存商:A&A Sports
2021/01/17 全球购物
提高EJB性能都有哪些技巧
2012/03/25 面试题
幼教毕业生自我鉴定
2014/01/12 职场文书
安全生产先进个人材料
2014/02/06 职场文书
离职报告格式
2014/11/04 职场文书
运动会加油稿20字
2014/11/15 职场文书
教师节老师寄语
2015/05/28 职场文书
2016年学校爱国卫生月活动总结
2016/04/06 职场文书
python基于tkinter制作m3u8视频下载工具
2021/04/24 Python