Remember the C++ idiom I mentioned yesterday? In PHP, it has some unique gotchas.
First, let’s look at a simple C++ RAII example, using objects that do nothing except log their own destruction:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <iostream> #include <string> class LogsDestruction { public: LogsDestruction(std::string name) { this->name = name; } ~LogsDestruction() { std::cout << this->name << std::endl; } private: std::string name; }; int main() { LogsDestruction a("A"); LogsDestruction b("B"); } |
If you build and run this, you can see that objects a and b are destroyed in last-to-first order:
$ CXXFLAGS='-W -Wall -pedantic -std=c++98' make raii g++ -W -Wall -pedantic -std=c++98 raii.cpp -o raii $ ./raii B A
This order is guaranteed by the C++ language spec.
Now let’s see what happens with the equivalent PHP program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?php class LogsDestruction { function __construct($name) { $this->name = $name; } function __destruct() { print $this->name; print "\n"; } } function raii_example() { $a = new LogsDestruction("A"); $b = new LogsDestruction("B"); } raii_example(); ?> |
Running this from either a web request or the PHP command-line interpreter gives:
$ php raii.php A B
Surprise!
This is guaranteed to bite you if you have multiple resources that need guaranteed cleanup in the same block. The only workaround available in the language is to do at most one cleanup per function:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?php /* ... */ function raii_example_inner() { $b = new LogsDestruction("B"); } function raii_example() { $a = new LogsDestruction("A"); raii_example_inner(); } raii_example(); ?> |