Heim > Backend-Entwicklung > PHP-Tutorial > 给猪的鼻子插一根葱_PHP

给猪的鼻子插一根葱_PHP

WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWB
Freigeben: 2016-06-01 12:42:59
Original
988 Leute haben es durchsucht

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

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

Object.prototype.greeting = 'Hello'
var o = new Object
alert(o.greeting)
Nach dem Login kopieren

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; }}
}
Nach dem Login kopieren

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

这里利用了 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')
Nach dem Login kopieren

在 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()"); }}
}
Nach dem Login kopieren

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

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

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

会报告 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);
Nach dem Login kopieren

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

问题来了:
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); }
Nach dem Login kopieren

$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()"); }}
}
Nach dem Login kopieren

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





二. 继承
面向对象的一大特征是继承, 继承最大限度地保留代码重用能力. 但如果直接用上例的 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
*/
Nach dem Login kopieren

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage