5

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
user1542316
  • 158
  • 6
  • Why don't you add an argument to that function and pass the variable as argument? Otherwise maybe the 'use' keyword can help you. (http://php.net/functions.anonymous) But I am not really sure. – Lajos Veres Dec 21 '14 at 11:18
  • Possible duplicate of http://stackoverflow.com/questions/27014664/php-equivalent-of-javascript-bind – haim770 Dec 21 '14 at 11:19
  • as i know this is a solution that pass `$this` as argument, just curious is there any possibility working like Javascript – user1542316 Dec 21 '14 at 15:33

2 Answers2

3

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
raina77ow
  • 103,633
  • 15
  • 192
  • 229
  • You *can* rebind `$this` using the Reflection API... but it's not something I'd recommend doing in production since, as you said, PHP is not Javascript. +1 – deceze Dec 21 '14 at 11:56
1

you can use Closure::bind function

<?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();

and result

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