Use Cases for The __destruct Method in Vanilla PHP

Use Cases for The __destruct Method in Vanilla PHP

Discovering the PHP __destruct Magic Method and a few somewhat practical use cases for implementing it.

If you’ve ever done OOP in PHP you are fully aware of the __constructmethod and the role it plays in setting up class properties, among other tasks. But have you ever given any thought on using the opposite of this function: the __destruct method? Were you aware that such a function existed? Keep reading to find about some use cases I’ve personally implemented in the past.


Save Errors to File

Over the years, I’ve had to create various import scripts. For example, grabbing data from a CSV file and landing it onto a database table. Sometimes what would happen is that my script would stop running because it ran into a row with too many values or a value which couldn’t be inserted properly. These things would hinder my progress because I would then go through a process of elimination to see what the troublesome values were and writing conditions to handle them. It was later that I realized I could come up with a solution whereby the script would continue running and be allowed to finish, while catching and logging any issues that arose along the way. The __destruct method was pivotal in achieving this, see the gist below:

<?php

    namespace App\Classes;

    use Exception;
    use PDO;
    use PDOException;

    abstract class ParentImportScript
    {
        /**
         * @var PDO $pdo
         */
        protected $pdo;

        /**
         * @var array $errorBag
         */
        protected $errorBag;

        /**
         * @var string $errorFileLocation
         */
        protected $errorFileLocation = '/var/www/html/storage/';

        /**
         * @var string $startTime
         */
        protected $startTime;

        /**
         * ChildImportScript constructor.
         * @param $dsn
         * @param $username
         * @param $password
         * @param $options
         */
        public function __construct($dsn, $username, $password, $options = [])
        {
            $this->pdo = new PDO($dsn, $username, $password, $options);
            /*
             * This tells PDO to emit a PDOException should there be
             * any issues with the query statement it executes.
             */
            $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

            /*
             * Making sure to initialize the variable
             * which holds all the errors.
             */
            $this->errorBag = [];

            $this->recordStartTime();

            /*
             * The responsibility of actually running the import is handled
             * by the parent. The child just specifies what data should
             * be imported. The possibility to alter in what specific
             * manner the import should be performed is present
             * however.
             */

            $this->handleImport($this->getImportData());

            /*
             * Lazily simulating the import taking some arbitrary time.
             */
            sleep(10);
        }

        /**
         * @return string
         */
        abstract protected function getTable(): string;

        /**
         * @return array
         */
        abstract protected function getImportData();

        /**
         * @param array $data
         * @return void
         */
        protected function handleImport(array $data)
        {
            foreach ($data as $datum) {
                $columnNames = array_keys($datum);

                $preparedColumnNames = implode(',', $columnNames);

                $preparedValuePlaceholders = implode(',', array_map(function ($column) {
                    return ":$column";
                }, $columnNames));

                $sql = "INSERT INTO {$this->getTable()} ($preparedColumnNames) VALUES ($preparedValuePlaceholders)";

                try {
                    $this->pdo->prepare($sql)->execute($datum);
                } catch (PDOException $e) {
                    $this->addExceptionToErrorBag($e);
                }
            }
        }

        /**
         * @param Exception $e
         */
        protected function addExceptionToErrorBag(Exception $e)
        {
            /*
             * The actual data that is recorded can be
             * changed to fit your needs. Either
             * here or in the child class.
             */
            $this->errorBag[] = [
                'Exception' => get_class($e),
                'Message' => "{$e->getMessage()} {$e->getLine()}",
                'Trace' => $e->getTraceAsString(),
            ];
        }

        private function getRunningScriptName()
        {
            $classNamespacePath = explode('\\', get_class($this));

            return array_pop($classNamespacePath);
        }

        private function recordStartTime()
        {
            $this->startTime = date('Y-m-d H:i:s');

            $sql = "INSERT INTO scripts_table (script_name, started_at) 
                    VALUES ('{$this->getRunningScriptName()}', '{$this->startTime}')";

            $this->pdo->prepare($sql)->execute();
        }

        public function __destruct()
        {
            if (count($this->errorBag) > 0) {
                $className = $this->getRunningScriptName();
                $timestamp = date('Ymd_His');

                $filename = "{$className}_errors_$timestamp.json";

                file_put_contents("$this->errorFileLocation/$filename", json_encode($this->errorBag));
            }

            try {
                $sql = "UPDATE scripts_table SET completed_at = CURRENT_TIMESTAMP 
                        WHERE script_name = '{$this->getRunningScriptName()}'
                        AND started_at = '{$this->startTime}';";

                $this->pdo->prepare($sql)->execute();
            } catch (PDOException $e) {
                $this->addExceptionToErrorBag($e);
            }
        }
    }

php oop laravel symfony php-developers

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

Hire Dedicated PHP Developer

Looking to hire affordable yet experienced PHP developers? **[Hire Dedicated PHP Developer](https://hourlydeveloper.io/hire-dedicated-php-developer/ "Hire Dedicated PHP Developer")**, who can convert your idea to reality, within the stipulated...

Laravel Development Company

Skenix Infotech is a top Laravel Website Development Company with Expert Laravel Developers that provides robust Laravel Development Services at fair costs.

Hire PHP Developer

Looking to develop a PHP based website from scratch or revamp your existing website? **[HourlyDeveloper.io](https://hourlydeveloper.io/ "HourlyDeveloper.io")** has always been an industry leader for companies and business owners looking to hire...

PHP Website Development

Skenix Infotech is a leading PHP Web Development Company that helps companies get results with industry best practices. Get affordable PHP Development Services.

How to Hire Laravel Developers and How Much Does It Cost.

Want to make the most out of the latest Laravel technology? Here is your guide on how to hire the best Laravel developer, and at the right price.