您好,欢迎访问一九零五行业门户网

给猪的鼻子插一根葱_PHP

php 具备一些动态语言的特征, 但不彻底. 虽然 php 的标志是一头大象, 可这头象的鼻子未免太短, 以致经常够不着东西, 反而象猪了. 本文旨在探讨一种使 php 更动态化的方法, 主要是模拟 javascript 的 prototype 继承. 既然是模拟, 就不是真的能使 php 动态起来, 只是插上一根葱, 让它装得更象一点.
一. 基本操作
通过 javascript 的 prototype 动态地为对象添加属性, 我们可以这样:
object.prototype.greeting = 'hello'var o = new objectalert(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);/* outputsstdclass object( [greeting] => hello)*/test::$prototype->say = create_method('$word', 'echo $word;');$o = new object;$o->say('hi');/* outputshi*/
其它类似信息

推荐信息