3

First excuse my english I'm not a native speaker and sorry if it looks rough, this is the first time that I post on this site. My problem is quite simple I think. Let's say, we have :

class A {

    function foo() {

        function bar ($arg){
            echo $this->baz, $arg;
        }

        bar("world !");

    }

    protected $baz = "Hello ";

}

$qux = new A;

$qux->foo();

In this example, "$this" obviously doesn't refer to my object "$qux".

How should I do to make it reffer to "$qux"?

As might be in JavaScript : bar.bind(this, "world !")

georg
  • 211,518
  • 52
  • 313
  • 390
user3292788
  • 407
  • 1
  • 6
  • 15

4 Answers4

3

PHP doesn't have nested functions, so in your example bar is essentially global. You can achieve what you want by using closures (=anonymous functions), which support binding as of PHP 5.4:

class A {
    function foo() {
        $bar = function($arg) {
            echo $this->baz, $arg;
        };
        $bar->bindTo($this);
        $bar("world !");
    }
    protected $baz = "Hello ";
}

$qux = new A;
$qux->foo();

UPD: however, bindTo($this) doesn't make much sense, because closures automatically inherit this from the context (again, in 5.4). So your example can be simply:

    function foo() {
        $bar = function($arg) {
            echo $this->baz, $arg;
        };
        $bar("world !");
    }

UPD2: for php 5.3- this seems to be only possible with an ugly hack like this:

class A {
    function foo() {
        $me = (object) get_object_vars($this);
        $bar = function($arg) use($me) {
            echo $me->baz, $arg;
        };
        $bar("world !");
    }
    protected $baz = "Hello ";
}

Here get_object_vars() is used to "publish" protected/private properties to make them accessible within the closure.

georg
  • 211,518
  • 52
  • 313
  • 390
  • PHP has nested functions, but is normally is not very wise to use them http://stackoverflow.com/questions/415969/what-are-php-nested-functions-for – Asped Nov 19 '14 at 10:46
  • 1
    @Asped: no. You can write a function inside another one, but that doesn't make it nested. Inner functions are global. – georg Nov 19 '14 at 10:47
  • Ok so I've used closures but by using a variable instead of "$this". I have to consider PHP < 5.4 so I did it with "use". I don't realy know if it's the best practice. – user3292788 Nov 19 '14 at 15:06
  • @user3292788: yes, this seems to be the only way. Added a note to the answer. – georg Nov 19 '14 at 15:16
  • I see but is there a way to pass it by reference ? How would it be if I want to modify the value ? – user3292788 Nov 19 '14 at 18:34
  • @user3292788: then pass `$this` as an argument: `$bar=function($obj){...};` and then `$bar($this)`. No need to use a reference since objects are always passed as pointers. – georg Nov 19 '14 at 19:36
  • Exact I completely forgot it. But in this case, we connot access to a protected element ? – user3292788 Nov 20 '14 at 05:54
  • No, in this code you can't. But if you really want, try Reflection (see http://stackoverflow.com/questions/3475601/accessing-a-protected-member-variable-outside-a-class). – georg Nov 20 '14 at 08:31
2

Actually, $this does refer to $qux when called in that context.

You can't use $this in contexts other than an object method, so if you took something like this:

function test() {
    echo $this->baz;
}

It wouldn't work, no matter what you do.

Madara's Ghost
  • 172,118
  • 50
  • 264
  • 308
0

it is a little strange to to it this way, but if you use a function inside of a function, then of course some closuers are in effect. you can have a similar problem in javascript. I would suggest not to use them at all, but if for some reason you do not want to, then i would try sth like this (not tested):

class A {

    function foo() {

        $that = $this;
        function bar ($arg){
            echo $that->baz, $arg;
        }

        bar("world !");

    }

    protected $baz = "Hello ";

}

$qux = new A;

$qux->foo();

UPDATE if the nested function acts as a global function, then you coud pass your object as a parameter if you need to use it:

function foo() {

        function bar ($o,$arg){
            echo $o->baz, $arg;
        }

        bar($this,"world !");

    }
Asped
  • 3,083
  • 4
  • 31
  • 52
0

If I understand what you're trying to achieve, it could be like this:

<?php
class Foo {

  public $prop;

  public function __construct($prop) {
    $this->prop = $prop;
  }

  public function __call($name, $arguments) {
    if (!isset($this->$name)) {
      throw new InvalidArgumentException("No such property: $name");
    }

    if (! $this->{$name} instanceof Closure) {
      throw new InvalidArgumentException("{$name} is not a closure");
    }

    $c = $this->{$name}->bindTo($this);
    return $c(...$arguments);
  }
}

$f = new Foo("bar");
$f->bar = function ($arg) {
  print_r([$arg, $this->prop]);
};

$f->bar("A");
?>

That way you can essentially monkey-patch any function in a class and call it as if it was local, even including use of $this. Two limitations, however:

  • this seems pointless since Traits are available, except if you are doing adding really dynamically defined on a non-modifiable package
  • although the closure can access properties on $this it is still external to the object, so can only access public properties. Trying to access protected/private properties will fail.
FGM
  • 2,830
  • 1
  • 31
  • 31