Does PHP support the RAII pattern? How?
Asked Answered
R

4

16

Most resources on PHP never touch memory management because the language itself is pretty good at doing this for you. However, in PHP you often end up dealing with external resources which aren't memory -- database handles, sessions, database transactions, etc. These external resources could be managed most cleanly using some form of RAII object.

I initially thought that PHP used a garbage collection scheme similar to the JVM or the CLR, where the concept of a destructor does not exist. (Remember: Everyone thinks about garbage collection the wrong way -- finalizers are not destructors!) There's the special __destruct method, but I thought that was a "finalizer" similar to a Java or C# finalizer. For this reason, you cannot use RAII on the JVM or the CLR (C#'s using blocks get you about 95% of the way there, but that's a bit different...).

However, Google seems to indicate that PHP supports the RAII pattern, though I cannot find verification of this in the PHP docs. Does the language support this and is putting the cleanup logic in __destruct sufficient for accomplishing RAII tasks?

Robertaroberto answered 8/2, 2011 at 21:29 Comment(0)
B
14

This is nearly the same question as Is destructor in PHP predictable? and the answer is the same. PHP uses refcounting, and it promises that the destructor will be called immediately as soon as the refcount goes to zero (generally when the object goes out of scope). So if you create an object and take care not to leak it out of scope, RAII is viable.

Befit answered 8/2, 2011 at 21:37 Comment(4)
Another caveat: when multiple objects leave scope at the same time, the order their destructors are called is officially undefined, and usually in FIFO order (exactly the opposite of what's needed for proper RAII). That's a dealbreaker for my particular use case.Wiper
@Wiper you could artificially add braces to enforce ordering? :)Befit
Braces won't do it - only a function can introduce a new scope. Still possible, I suppose, but that could amount to a lot of boilerplate.Wiper
The "Execute Around" idiom (#342471) looks like it might help. However, I'm worried about what happens when multiple scopes end simultaneously (especially when an exception is thrown) - maybe they will all wind up in FIFO order anyway?Wiper
D
5

PHP uses reference counting, so when you're done with a variable it gets cleaned up immediately. (Unless you create cycles.) That frees up resources promptly so you generally don't need to worry about explicit resource management beyond being careful to not create memory cycles.

If you did want to implement any particular strategy, you can do it by making sure that the resource is only used by one variable. Whenever that variable is pointed away from the resource, the resource should be immediately freed up.

Didymous answered 8/2, 2011 at 21:36 Comment(0)
C
4

The following class ReturnHandler provides automatic invocation of a handler when ReturnHandler's instance goes out of scope. You can have several returns in your function (myfunc) without the need to think of releasing the resource before each of them.

/**
 * Automatically calls a handler before returning from a function. Usage:
 *
 * function myfunc()
 * {
 *  $resource = new Resource();
 *  $rh = new ReturnHandler( function() use ($resource) { $resource->release(); } );
 *  // ...
 *  if(...) {
 *    return; // look, ma, automatic clean up!
 *  }
 * }
 */
class ReturnHandler
{
  private $return_handler;

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

  public function __destruct()
  {
    $handler = $this->return_handler;
    $handler();
  }
}

Here's a test for it:

class ReturnHandlerTest extends PHPUnit_Framework_TestCase
{

  private static function trigger_return_handler(&$var)
  {
    $rh = new ReturnHandler(function() use (&$var) { $var++; } );
  }

  public function test()
  {
    $a = 0;
    $this->assertEquals(0, $a);
    self::trigger_return_handler($a);
    $this->assertEquals(1, $a);
  }
}
Confection answered 13/3, 2013 at 13:55 Comment(1)
I would rather have a type which wraps the resource in question for most uses. But this will work as a quick and dirty solution e.g. if you only have one instance of a given resource used in your program.Robertaroberto
P
1

Slightly offtopic: you can do a using-like pattern with lambdas. Like this:

function WithFile($Name, $Func)
{
    $File = fopen($Name, 'r');
    $r = $Func($File);
    fclose($File);
    return $r;
}

And then use it like this

$FileHeader = WithFile('myfile', function($File) {return fread($File, 16);});

Perfectly deterministic. That said, were there a terser syntax for lambdas...

Patronymic answered 11/11, 2020 at 18:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.