1

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

Community
  • 1
  • 1
Gopikrishna S
  • 2,221
  • 4
  • 25
  • 39

2 Answers2

3

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.

Jeff Lambert
  • 24,395
  • 4
  • 69
  • 96
  • does updating `stopper ` in `setStopper` affect the `$stopper` inside the callback? Is everything "by reference" in php? – Gopikrishna S Mar 31 '15 at 16:19
  • Objects are, yes. See [here](http://stackoverflow.com/questions/2715026/are-php5-objects-passed-by-reference) – Jeff Lambert Mar 31 '15 at 16:19
  • Okay then! I will update my code and verify if its running, thanks, and 'll let you know – Gopikrishna S Mar 31 '15 at 16:21
  • Hey, mind if I ask one more question? If I use this as a singleton class, and I execute another php script that gets the instance and sets the stopper, it doesn't actually stop the consume! Can I know why? – Gopikrishna S Mar 31 '15 at 16:39
  • I mean if the whole thing works by reference, and if I understand the working of singleton class, setting the stopper should stop the server right? But it doesn't! The stopper is still `false` even after I call the `setStopper` – Gopikrishna S Mar 31 '15 at 16:43
  • I moved the other question as a new one and as a follow up. Thanks, your's answers the OP, so no more bugging – Gopikrishna S Mar 31 '15 at 17:16
0

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