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 相关文章推荐
session 的生命周期是多长
Oct 09 PHP
php中unlink()、mkdir()、rmdir()等方法的使用介绍
Dec 21 PHP
dhtmlxTree目录树增加右键菜单以及拖拽排序的实现方法
Apr 26 PHP
PHP程序级守护进程的实现与优化的使用概述
May 02 PHP
PHP中使用Imagick实现各种图片效果实例
Jan 21 PHP
在Windows系统下使用PHP生成Word文档的教程
Jul 03 PHP
php安装php_rar扩展实现rar文件读取和解压的方法
Nov 17 PHP
PHP Class SoapClient not found解决方法
Jan 20 PHP
PHP实现用户异地登录提醒功能的方法【基于thinkPHP框架】
Mar 15 PHP
PHP使用文件锁解决高并发问题示例
Mar 29 PHP
PHP应用跨时区功能的实现方法
Mar 21 PHP
PHP实现数组根据某个字段进行水平合并,横向合并案例分析
Oct 08 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 iconv 函数转gb2312的bug解决方法
2009/10/11 PHP
完美解决:Apache启动问题―(OS 10022)提供了一个无效的参数
2013/06/08 PHP
PHP实现加强版加密解密类实例
2015/07/29 PHP
PHP模糊查询的实现方法(推荐)
2016/09/06 PHP
PHP代码加密的方法总结
2020/03/13 PHP
KindEditor在php环境下上传图片功能集成的方法示例
2020/07/20 PHP
JavaScript/Js脚本处理html元素的自定义属性解析(亲测兼容Firefox与IE)
2013/11/25 Javascript
逐一介绍Jquery data()、Jquery stop()、jquery delay()函数(详)
2015/11/04 Javascript
浅析jQuery Ajax请求参数和返回数据的处理
2016/02/24 Javascript
JavaScript弹出对话框的三种方式
2016/03/23 Javascript
详解jQuery中的deferred对象的使用(一)
2016/05/27 Javascript
Angular 4环境准备与Angular cli创建项目详解
2017/05/27 Javascript
vue项目开发中setTimeout等定时器的管理问题
2018/09/13 Javascript
vue单页应用在页面刷新时保留状态数据的方法
2018/09/21 Javascript
微信小程序结合mock.js实现后台模拟及调试
2019/03/28 Javascript
layui 数据表格 根据值(1=业务,2=机构)显示中文名称示例
2019/10/26 Javascript
JavaScript进阶(一)变量声明提升实例分析
2020/05/09 Javascript
[35:34]Liquid vs Winstrike 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
linux系统使用python获取cpu信息脚本分享
2014/01/15 Python
Python socket C/S结构的聊天室应用实现
2014/11/30 Python
用map函数来完成Python并行任务的简单示例
2015/04/02 Python
使用python 打开文件并做匹配处理的实例
2019/01/02 Python
利用Django提供的ModelForm增删改数据的方法
2019/01/06 Python
Python3.6中Twisted模块安装的问题与解决
2019/04/15 Python
Python如何在循环内使用list.remove()
2020/06/01 Python
python向企业微信发送文字和图片消息的示例
2020/09/28 Python
纯CSS3实现的井字棋游戏
2020/11/25 HTML / CSS
美国购买肉、鸭、家禽、鹅肝和熟食网站:D’Artagnan
2018/11/13 全球购物
意大利咖啡、浓缩咖啡和浓缩咖啡机:illy caffe
2019/03/20 全球购物
SQL注入攻击的种类有哪些
2013/12/30 面试题
《草原的早晨》教学反思
2014/04/08 职场文书
反对四风问题自我剖析材料
2014/09/29 职场文书
2015年大学元旦晚会活动策划书
2014/12/09 职场文书
解放思想大讨论活动总结
2015/05/09 职场文书
个人自我鉴定怎么写?
2019/07/01 职场文书
关于mysql中string和number的转换问题
2022/06/14 MySQL