How to Clean After Oneself in PHPHow to Clean After Oneself in PHP

While creating efficient and functional code is crucial, it’s equally important to ensure proper finalization of ressources. There are many situations where it is critical to free a lock, close a connexion or complete a file. In this post, we’ll explore three essential features that help code ensure that it always cleans after oneself, even in case of error: register_shutdown_function(), the object destructor __destruct(), and the command try-catch-finally.

register_shutdown_function()

PHP has been providing the register_shutdown_function() function since early versions. It allows the code to register a function that will be executed when the script finishes execution, regardless of whether it completes successfully or encounters an error. This feature is particularly useful when the cleanup task is difficult to link with the initialisation task. By registering it for the end of the script, it ensures that everything else will happen first.

<?php
function cleanupFunction() {
    // Perform cleanup tasks here
    // Close open files, release resources, etc.
}

register_shutdown_function('cleanupFunction');

// Rest of your PHP code
?>

The registered function is executed after the end of the script. At that point, PHP has already closed the output stream, so it is not possible to display anything to the browser. In particular, echo and print are not working anymore.

Object Destructor __destruct()

In object-oriented programming, the __destruct() method defines what happen when an object is no longer referenced, or when it is explicitly destroyed. This is a powerful mechanism for releasing resources associated with an object: the destructor is automatically called when the object is destroyed.

This may happen at script shutdown, as we have seen with the previous function register_shutdown_function(). Yet, it also happens immediately when an object has no more references pointing to it. This means that a local object, in a method, is immediately destroyed when the method ends.

<?php

class Session {
    public function __construct() {
        // Release resources when the object is destroyed
        print "Started\n";
    }

    public function __destruct() {
        // Release resources when the object is destroyed
        print "Finished\n";
    }
}

function foo() {
  $session = new Session();
  
  // Nothing to do, yet the __destruct is called
}

foo();
// Started
// Finished

?>

By implementing the __destruct() method within a class, the code can encapsulate resource release logic within the object itself.

Exception Handling

Exception handling is a fundamental aspect of writing robust and reliable PHP code. Here, we’ll divert it from its initial purpose to take advantage of a critical feature: finally.

finally is always executed, may there be an exception or not. Hence, after starting the try clause, the finally block is always used. There may be exceptions happening in-between, and these may be caught or not: whatever happens, finally will be called.

And since catch is an optional clause, it is possible to write valid code like this:

<?php
try {
    // Put a file on the disk
    file_put_contents($path, 1);

} finally {
    // Release temporary file, etc.
    unlink($path);
}

?>

This syntax doesn’t block exceptions, which will be transmitted as is, if any. Yet the finally block will always be executed, making it a compulsory step, where anything may be cleaned.

The boy scout rule

Leaving the resources at least as clean as they were before starting execution is also known as the boy scout rules. PHP offers three ways to make sure that the cleaning happens, no matter the amount of error that are happening (though, short of Segmentation fault).

register_shutdown_function() offers a last minut option to clean after all the application has run; the __destruct() method gives a way to clean at method exit and finally offers the same inside a sequence of code.

Lastly, there are PHP functions that take care of such cleaning themselves. For example, tmpfile() provides a temporary file, that will be removed at the end of the script; databases connexions will rollback any opened and unfinished transactions.

Cleaning after one’s code is important.