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类
Jul 15 PHP
怎样在UNIX系统下安装MySQL
Oct 09 PHP
php zlib压缩和解压缩swf文件的代码
Dec 30 PHP
PHP 文件上传全攻略
Apr 28 PHP
php阻止页面后退的方法分享
Feb 17 PHP
php递归方法实现无限分类实例代码
Feb 28 PHP
CI框架Session.php源码分析
Nov 03 PHP
PHP永久登录、记住我功能实现方法和安全做法
Apr 27 PHP
php 利用array_slice函数获取随机数组或前几条数据
Sep 30 PHP
PHP+Apache环境中如何隐藏Apache版本
Nov 24 PHP
PHP _construct()函数讲解
Feb 03 PHP
laravel实现Auth认证,登录、注册后的页面回跳方法
Sep 30 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
《忧国的莫里亚蒂》先导宣传图与STAFF公开
2020/03/04 日漫
3.从实例开始
2006/10/09 PHP
php session_start()关于Cannot send session cache limiter - headers already sent错误解决方法
2009/11/27 PHP
php 使用GD库为页面增加水印示例代码
2014/03/24 PHP
php实现最简单的MVC框架实例教程
2014/09/08 PHP
PHP实现的通过参数生成MYSQL语句类完整实例
2016/04/11 PHP
PHP中关键字interface和implements详解
2017/06/14 PHP
php7基于递归实现删除空文件夹的方法示例
2017/06/15 PHP
laravel配置Redis多个库的实现方法
2019/04/10 PHP
php pdo连接数据库操作示例
2019/11/18 PHP
event对象的方法 兼容多浏览器
2009/06/27 Javascript
JavaScript解析URL参数示例代码
2013/08/12 Javascript
ionic 上拉菜单(ActionSheet)实例代码
2016/06/06 Javascript
RequireJS简易绘图程序开发
2016/10/28 Javascript
jQuery中元素选择器(element)简单用法示例
2018/05/14 jQuery
微信小程序表单验证form提交错误提示效果
2020/06/19 Javascript
koa源码中promise的解读
2018/11/13 Javascript
Vue 之孙组件向爷组件通信的实现
2019/04/23 Javascript
jquery.tagsinput.js实现记录checkbox勾选的顺序
2019/09/21 jQuery
javascript二维数组和对象的深拷贝与浅拷贝实例分析
2019/10/26 Javascript
基于JavaScript判断两个对象内容是否相等
2020/01/10 Javascript
JS简单表单验证功能完整示例
2020/01/26 Javascript
vue 实现把路由单独分离出来
2020/08/13 Javascript
js实现自定义滚动条的示例
2020/10/27 Javascript
[02:18]DOTA2英雄基础教程 育母蜘蛛
2014/01/20 DOTA
python批量爬取下载抖音视频
2019/06/17 Python
Python3 A*寻路算法实现方式
2019/12/24 Python
OpenCV Python实现拼图小游戏
2020/03/23 Python
python按照list中字典的某key去重的示例代码
2020/10/13 Python
开发人员所需要知道的HTML5性能分析面面观
2012/07/05 HTML / CSS
canvas拼图功能实现代码示例
2018/11/21 HTML / CSS
乌克兰香水和化妆品网站:Notino.ua
2018/03/26 全球购物
一篇.NET面试题
2014/09/29 面试题
学雷锋月活动总结
2014/04/25 职场文书
2015年护士节活动策划方案
2015/05/04 职场文书
阿里云服务器Ubuntu 20.04上安装Odoo 15
2022/05/20 Servers