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 相关文章推荐
PHP 强制下载文件代码
Oct 24 PHP
php中经典方法实现判断多维数组是否为空
Oct 23 PHP
PHP设计模式之命令模式的深入解析
Jun 13 PHP
php使用pack处理二进制文件的方法
Jul 03 PHP
php时间戳格式化显示友好的时间函数分享
Oct 21 PHP
php+html5使用FormData对象提交表单及上传图片的方法
Feb 11 PHP
thinkPHP下ueditor的使用方法详解
Dec 26 PHP
PHP 输出缓冲控制(Output Control)详解
Aug 25 PHP
PHP接口继承及接口多继承原理与实现方法详解
Oct 18 PHP
PHP操作MongoDB实现增删改查功能【附php7操作MongoDB方法】
Apr 24 PHP
PHP递归遍历文件夹去除注释并压缩php源代码的方法示例
May 23 PHP
php实现每日签到功能
Nov 29 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开发中的安全防范知识详解
2013/06/06 PHP
Windows下的PHP安装pear教程
2014/10/24 PHP
PHP爬虫之百万级别知乎用户数据爬取与分析
2016/01/22 PHP
php通过pecl方式安装扩展的实例讲解
2018/02/02 PHP
php格式文件打开的四种方法
2018/02/24 PHP
PHP CURL使用详解
2019/03/21 PHP
javascript让setInteval里的函数参数中的this指向特定的对象
2010/01/31 Javascript
AppBaseJs 类库 网上常用的javascript函数及其他js类库写的
2010/03/04 Javascript
asp.net下使用jquery 的ajax+WebService+json 实现无刷新取后台值的实现代码
2010/09/19 Javascript
IE中鼠标经过option触发mouseout的解决方法
2015/01/29 Javascript
JavaScript Sort 的一个错误用法示例
2015/03/20 Javascript
javascript模拟评分控件实现方法
2015/05/13 Javascript
jQuery实现放大镜效果实例代码
2016/03/17 Javascript
JS实现消息来时让网页标题闪动效果的方法
2016/04/20 Javascript
js编写一个简单的产品放大效果代码
2016/06/27 Javascript
bootstrap选项卡使用方法解析
2017/01/11 Javascript
angularJS 发起$http.post和$http.get请求的实现方法
2017/05/18 Javascript
Angular 4依赖注入学习教程之组件服务注入(二)
2017/06/04 Javascript
JS模拟实现哈希表及应用详解
2018/05/04 Javascript
通过jquery toggleClass()属性制作文章段落更改背景颜色
2018/05/21 jQuery
微信小程序按钮去除边框线分享页面功能
2018/08/27 Javascript
JS与jQuery判断文本框还剩多少字符可以输入的方法
2018/09/01 jQuery
微信小程序实现图片滚动效果示例
2018/12/05 Javascript
JavaScript中this的全面解析及常见实例
2019/05/14 Javascript
Vue组件生命周期运行原理解析
2020/11/25 Vue.js
python f-string式格式化听语音流程讲解
2019/06/18 Python
Pytorch 搭建分类回归神经网络并用GPU进行加速的例子
2020/01/09 Python
python GUI库图形界面开发之PyQt5 MDI(多文档窗口)QMidArea详细使用方法与实例
2020/03/05 Python
Python+Dlib+Opencv实现人脸采集并表情判别功能的代码
2020/07/01 Python
公共汽车、火车和飞机票的通用在线预订和销售平台:INFOBUS
2019/11/30 全球购物
Unix里面如何在后台运行程序
2016/10/14 面试题
《太阳》教学反思
2014/02/21 职场文书
大学生工作自荐书
2014/06/16 职场文书
三好学生主要事迹怎么写
2015/11/03 职场文书
my.ini优化mysql数据库性能的十个参数(推荐)
2021/05/26 MySQL
MySQL如何修改字段类型和字段长度
2022/06/10 MySQL