user1542316 user1542316 - 2 months ago 13
Javascript Question

PHP call_user_func bind thisArg like Javascript call

In Javascript, i can bind

this
to another function and invoke it with
.call
or
.apply


In PHP, i can do that with
call_user_func
or
call_user_func_array
, but how can i bind
$this
into the function?

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


UPDATE:

Thanks @deceze for the
Reflection
idea, I've found these solutions but I don't think it's good for performance (x10 slower than directly call), but much clearly on reading.

I've wrote two functions:

// 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));
}


and replace
call_user_func
to
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

Answer

PHP is not JavaScript: you cannot freely mix functions defined inside and outside classes, switching their context as you call them. Any attempt to use $this the way you described will cause a fatal error: Using $this when not in object context.

Then again, the same effect can be achieved with a simple convention for a classless functions: pass the context they should work with as their first param. Apparently, you'll only be able to use public interface of the context object - but then again, it's the same as with JavaScript. Plus, as a bonus, you'll be able to have a type check with class hinting. For example:

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