PHP call_user_func绑定thisArg就像Javascript调用一样

时间:2014-12-21 11:12:49

标签: javascript php

在Javascript中,我可以将this绑定到另一个函数并使用.call.apply

调用它

在PHP中,我可以使用call_user_funccall_user_func_array执行此操作,但如何将$this绑定到函数中?

使用Javascript:

function greet() {
  alert('Hello ' + this.name);
}

function SomeClass() {}
SomeClass.prototype = {
  name: 'John',
  test: function() {
    greet.call(this);
  }
}

var me = new SomeClass();
me.test();  // Hello John

PHP:

function greet() {
  echo 'Hello ' . $this->name;
}

class SomeClass {
  public $name = 'John';

  function test() {
    call_user_func('greet');
  }
}

$me = new SomeClass;
$me->test();  // Fatal error: Using $this when not in object context

更新:

感谢@deceze提出的Reflection想法,我发现了这些解决方案,但我认为它对性能有益(x10比直接调用慢),但很清楚在阅读。

我写了两个函数:

// See also Javascript: Function.prototype.apply()
function function_apply($fn, $thisArg, $argsArray = array()) {
  static $registry;
  if (is_string($fn)) {
    if (!isset($registry[$fn])) {
      $ref = new \ReflectionFunction($fn);
      $registry[$fn] = $ref->getClosure();
    }
    $fn = $registry[$fn];
  }
  return call_user_func_array($fn->bindTo($thisArg), $argsArray);
}

// See also Javascript: Function.prototype.call()
function function_call($fn, $thisArg /*, arg1, arg2 ... */) {
  return function_apply($fn, $thisArg, array_slice(func_get_args(), 2));
}

并将call_user_func替换为function_call

function greet() {
  echo 'Hello ' . $this->name;
}

class SomeClass {
  public $name = 'John';

  function test() {
    function_call('greet', $this);
  }
}

$me = new SomeClass;
$me->test(); // Hello John

2 个答案:

答案 0 :(得分:3)

PHP不是JavaScript:你不能自由地混合内部和外部定义的函数,在调用它们时切换它们的上下文。任何以您描述的方式使用$this的尝试都会导致致命错误:Using $this when not in object context

然后,使用无类函数的简单约定可以实现相同的效果:将它们应该使用的上下文作为第一个参数传递。显然,您只能使用上下文对象的公共接口 - 但话说再次,它与JavaScript相同。另外,作为奖励,您可以通过课堂提示进行类型检查。例如:

function greet(SomeClass $_this) {
  echo 'Hello ' . $_this->name;
}

class SomeClass {
  public $name = 'John';

  function test() {
    call_user_func('greet', $this);
  }
}

$me = new SomeClass;
$me->test(); // Hello John

答案 1 :(得分:1)

你可以使用Closure :: bind函数

<?php

class AAA {

    public function ccc()
    {
        $bbb = new BBB;
        $r = $bbb->abc()[0];
        var_dump($r, Closure::bind($r, $this));
    }

}

class BBB {

    public function abc()
    {
        return [function () {

        }];
    }

}

$aaa = new AAA;
$aaa->ccc();

和结果

object(Closure)#3 (1) {
  ["this"]=>
  object(BBB)#2 (0) {
  }
}
object(Closure)#4 (1) {
  ["this"]=>
  object(AAA)#1 (0) {
  }
}