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

2 Answers 2

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
Sign up to request clarification or add additional context in comments.

1 Comment

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
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) {
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.