php - Access outer class from an anonymous callbac

2019-08-13 02:29发布

问题:

I have a code like this:

class Server {
  private $stopper;

  public function setStopper() { $this->stopper = TRUE; }

  public function startServer() {
    $consumer = new Consumer();
    $consumer->onConsume(function($data) {
      global $consumer;
      // some processing
      if( ?? ) { // how to access stopper here??
         $consumer->stop();
         // also how to access stopServer() here??
      }
    });
    $consumer->consume();
  }

  public function stopServer() { ... }

}

This code is in a file that should run forever unless the setStopper() is called. So far I have set_time_limit to stop the code after a while. But I need to implement setStopper way so I can stop the server whenever needed, not "after a while".

I need this because, the onConsume is connected to a streaming API and runs the anonymous call back whenever new data is available and I don't want kill the php app on timeout due to some lock issues. I want to gracefully stop the server.

Can anyone please tell how to access the stopper or stopServer inside the callback? Can I use following syntax?

...(function($data) use ($this) {...

I also thought of storing the class value inside callback, but the setStopper is called dynamically and the value might not be updated!

Is there a better way to handle this situation?

Follow Up: php - Dynamically update Singleton class' value

回答1:

You can create a Closure around the $consumer object as well as the lexical object $this (if you're using PHP < 5.4, you need to rename $this to something else, because you cannot use($this)):

 $self = $this;
 // You may not need to do this, I cannot remember off-hand whether 
 // closures have access to private variables or not
 $stopper = $this->stopper;
 $consumer->onConsume(function($data) use($consumer, $self, $stopper) {
  if( $stopper ) {
     $consumer->stop();
     $self->stopServer();
  }
});

See Example #3 on the linked to manual page.

I should also note here for completeness that if this is a long-lived process, then the objects being referenced inside the closure will hang around long after the function exits. For instance:

function makeAdder($x) {
    return function($n) use($x) {
        return $x + $n;
    };
}

$adder = makeAdder(5);
echo $adder(2); // Output 7
echo $adder(5); // Output 10
echo $adder(4); // Output 9

This is a classic example of a closure. Normally, once the makeAdder function returns its inner variable $x will fall out of scope and be ready for garbage collection. Since it is however bound inside the anonymous function's scope, it will hang around indefinitely (until the script's termination) or the reference to the containing scope is also released (i.e. via unset($adder)). This means that once your function is called, extra references to $consumer, $this and $stopper will hang around until the class instance itself is destroyed.

Not being aware of this can lead to some serious performance issues.



回答2:

the same problem here i use output buffers from ob_start/ob_end_flush and one function i have should be dynamic (however parameters i push into should insert them in an array for later use for parsing buffers using $buffer) in the parser associated to ob_start at a time i have these lines of code from one array full of data :

if(!empty($this->__b2))
        array_filter ($this->__b2,function($var)use(**&$buffer**){
                $buffer = preg_replace("/<\/body>/i", $var.'</body>', $buffer);
        });

I use a only one class singleton ,and i use "::" a lot. How you see in my case array_filter was out of order without &$buffer