Home >Backend Development >PHP Tutorial >给猪的鼻子插一根葱_PHP

给猪的鼻子插一根葱_PHP

WBOY
WBOYOriginal
2016-06-01 12:42:59944browse

PHP 具备一些动态语言的特征, 但不彻底. 虽然 PHP 的标志是一头大象, 可这头象的鼻子未免太短, 以致经常够不着东西, 反而象猪了. 本文旨在探讨一种使 PHP 更动态化的方法, 主要是模拟 Javascript 的 prototype 继承. 既然是模拟, 就不是真的能使 PHP 动态起来, 只是插上一根葱, 让它装得更"象"一点.

一. 基本操作
通过 Javascript 的 prototype 动态地为对象添加属性, 我们可以这样:

Object.prototype.greeting = 'Hello'
var o = new Object
alert(o.greeting)

Js 的内置对象 Object 可看作一个"类", 任何 Js "类"都有 prototype 内置对象, 用 PHP 来模拟它可以是:
error_reporting(E_ALL);

class Object
{
     public static $prototype;

     protected function __get($var) {
         if ( isset(self::$prototype->$var) ) {
             return self::$prototype->$var; }}
}

然后我们可以:
Object::$prototype->greeting = 'Hello';
$o = new Object;
echo $o->greeting; // 输出 Hello

这里利用了 PHP 的自动转型特性. 在 PHP 中, 我们要声明一个数组, 并不需要先 $var = array() 然后才做 $var[] = some_value, 直接地使用后者就可以得到一个数组; 同样地直接 $object->var 的时候, $object 就被自动定义为 stdClass 对象. 这就解决了在定义类内静态属性时不能声明 public static $prototype = new stdClass 的问题.

在 Js 中给"类"动态添加方法:
Object.prototype.say = function(word) { alert(word) }
o.say('Hi')

在 PHP 中模拟:
error_reporting(E_ALL);

class Object
{
     public static $prototype;

     protected function __get($var) {
         if ( isset(self::$prototype->$var) ) {
             return self::$prototype->$var; }}

     protected function __call($call, $params) {
         if ( isset(self::$prototype->$call) && is_callable(self::$prototype->$call) ) {
             return call_user_func_array(self::$prototype->$call, $params); }
         else {
             throw new Exception('Call to undefined method: ' . __CLASS__ . "::$call()"); }}
}

这样, 就可以
Object::$prototype->say = create_function('$word', 'echo $word;');
$o->say('Hi');

但是 PHP 的 create_function 返回的结果并不等同于 Js 中的 Function 对象, Js 的 Function 对象是一种闭包(closure), 它可以直接调用宿主的属性, 如
Object.prototype.rock = function() { alert(this.oops) }
o.oops = 'Oops'
o.rock()

但是在 PHP 中我们不可以写
Object::$prototype->rock = create_function('', 'echo $this->oops;');
$o->oops = 'Oops';
$o->rock();

会报告 Fatal error: Using $this when not in object context, 因为 create_function 返回的是匿名的普通函数, 它没有宿主. 为解决这个问题, 我们需要在参数中传入对象本身, 而且不能使用 $this 变量名做参数, 我们暂时用一个 $caller 的变量名:
Object::$prototype->rock = create_function('$caller', 'echo $caller->oops;');
$o->oops = 'Oops';
$o->rock($o);

现在可以了, 可是看上去怪怪的, 一点都不像动态语言. 嗯~, 这根葱还是有点短, 还是不"象".

问题来了:
1. 在调用动态方法时需要传递对象本身, 这算哪门子的面向对象?
2. 我们要在代码中使用 $this, 这才象是在面向对象.

解决方法:
1. 重新写一个函数代替 create_function, 在参数部分挤一个参数 $that 进去作为第一个参数, 在 __call 中向匿名函数传递参数时加入对象本身 $this 作为第一参数.
2. 允许在代码中使用 $this, 我们在代替函数中把 $this 换成 $that.

我们给它添加一个 create_method 函数来代替 create_function
function create_method($args, $code) {
     if ( preg_match('/\$that\b/', $args) ) {
         throw new Exception('Using reserved word \'$that\' as argument'); }
     $args = preg_match('/^\s*$/s', $args) ? '$that' : '$that, '. $args;
     $code = preg_replace('/\$this\b/', '$that', $code);
     return create_function($args, $code); }

$that 作为参数中的"保留字", 当出现在参数部分中将抛出异常.(在 PHP5 的早期暗夜版本中, $that 也曾经是保留字)

相应地, Object 中的 __call 也要作出改动
class Object
{
     public static $prototype;

     protected function __get($var) {
         if ( isset(self::$prototype->$var) ) {
             return self::$prototype->$var; }}

     protected function __call($call, $params) {
         if ( isset(self::$prototype->$call) && is_callable(self::$prototype->$call) ) {
             array_unshift($params, $this); // 这里!
             return call_user_func_array(self::$prototype->$call, $params); }
         else {
             throw new Exception('Call to undefined method: ' . __CLASS__ . "::$call()"); }}
}

现在我们就可以
Object::$prototype->rock = create_method('', 'echo $this->oops;');
$o->oops = 'Oops';
$o->rock();





二. 继承
面向对象的一大特征是继承, 继承最大限度地保留代码重用能力. 但如果直接用上例的 Object 类去创建继承类则会出错, 因为
1. 子类继承的静态属性 $prototype 永远属于父类(不管 $prototype 是标量还是列表, 对象更不消说)
2. 如果子类所继承的方法中有 self 关键字, self 会指向父类而非子类
class Object
{
     public static $prototype;

     protected function __get($var) {
         ... }

     protected function __call($call, $params) {
         ... }
}

class Test extends Object
{
}

Test::$prototype->greeting = 'Hello';
print_r(Object::$prototype);
/* outputs
stdClass Object
(
    [greeting] => Hello
)
*/

Test::$prototype->say = create_method('$word', 'echo $word;');
$o = new Object;
$o->say('Hi');
/* outputs
Hi
*/

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Previous article:关于Zend Optimizer_PHPNext article:ArrayAccess接口介绍_PHP