Stop of dupicating code

OK, I think I have this straight?

First I have my Database Class:

class Database extends PDO
{
    public function __construct()
    {
        try {
            $db_options = [
                PDO::ATTR_EMULATE_PREPARES => false,
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
            ];

            $dsn = 'mysql:host=' . DATABASE_HOST . ';dbname=' . DATABASE_NAME . ';charset=utf8mb4';
            parent::__construct($dsn, DATABASE_USERNAME, DATABASE_PASSWORD, $db_options);
        } catch (PDOException $e) {
            // Handle the exception or re-throw it to be handled by another class
            throw $e;
        }
    }
}


Then My Errorhandler Class:

class ErrorHandler
{
    public function handlePDOException(PDOException $e)
    {
        error_log('PDO Error: ' . $e->getMessage());
        echo 'An error occurred while connecting to the database.';
    }

    public function handleGeneralException(Exception $e)
    {
        error_log('General Error: ' . $e->getMessage());
        echo 'An error occurred.';
    }

    public function withErrorHandling(callable $function)
    {
        try {
            return $function();
        } catch (PDOException $e) {
            $this->handlePDOException($e);
        } catch (Exception $e) {
            $this->handleGeneralException($e);
        }
    }
}

Then as an example the following →

class UserRepository
{
    private Database $database;
    private ErrorHandler $errorHandler;

    public function __construct(Database $database, ErrorHandler $errorHandler)
    {
        $this->database = $database;
        $this->errorHandler = $errorHandler;
    }

    public function fetchUserById($id)
    {
        return $this->errorHandler->withErrorHandling(function () use ($id) {
            $stmt = $this->database->prepare("SELECT * FROM users WHERE id = :id");
            $stmt->execute(['id' => $id]);
            return $stmt->fetch(PDO::FETCH_ASSOC);
        });
    }
}

Creating instances of the classes

$database = new Database();
$errorHandler = new ErrorHandler();
$userRepository = new UserRepository($database, $errorHandler);

OK I think I have a better way in handing the Database Class and the ErrorHandler class

class ErrorHandler
{
    public function handlePDOException(PDOException $e)
    {
        error_log('PDO Error: ' . $e->getMessage());
        echo 'An error occurred while connecting to the database.';
    }

    public function handleGeneralException(Exception $e)
    {
        error_log('General Error: ' . $e->getMessage());
        echo 'An error occurred.';
    }
}

The Database Class -

<?php

namespacePhotoTech;

use PDO;
use PDOException;

class Database {

    private PDO $_connection;
    private static ?Database $_instance = null;
    private ErrorHandler $errorHandler;

    protected static function getInstance(ErrorHandler $errorHandler): Database
    {
        if (!self::$_instance) {
            self::$_instance = new self($errorHandler);
        }
        return self::$_instance;
    }

    public static function pdo(ErrorHandler $errorHandler): PDO
    {
        $db = static::getInstance($errorHandler);
        return $db->getConnection();
    }

    private function __construct(ErrorHandler $errorHandler) {
        $this->errorHandler = $errorHandler;
        try {
            $db_options = [
                PDO::ATTR_EMULATE_PREPARES => false,
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
            ];
            $this->_connection = new PDO('mysql:host=' . DATABASE_HOST . ';dbname=' . DATABASE_NAME . ';charset=utf8mb4', DATABASE_USERNAME, DATABASE_PASSWORD, $db_options);
        } catch (PDOException $e) {
            $this->errorHandler->handlePDOException($e);
        } catch (Exception $e) {
            $this->errorHandler->handleGeneralException($e);
        }
    }

    private function __clone() {}

    protected function getConnection(): PDO
    {
        return $this->_connection;
    }
}

Example of using the classes

<?php

namespace PhotoTech;

use PDO;
use Exception;
use JetBrains\PhpStorm\Pure;
use DateTime;
use DateTimeZone;

class LoginRepository {

    private PDO $pdo;
    private string $table = 'admins'; // Replace with your actual table name

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

    public function verify_credentials($username, $password): bool
    {
        $sql = "SELECT id, password FROM " . $this->table . " WHERE username =:username LIMIT 1";
        $user = $this->retrieve_credentials($sql, $username);
        if ($user && password_verify($password, $user['password'])) {
            session_regenerate_id(); // prevent session fixation attacks
            $_SESSION['user_id'] = $user['id'];
            return true;
        }

        return false;
    }


    protected function retrieve_credentials(string $sql, string $username): ?array
    {
        $stmt = $this->pdo->prepare($sql);

        $stmt->execute([ 'username' => $username]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        return $result !== false ? $result : null;
    }

    public function store_token_in_database($user_id, $token) {
        $sql = "UPDATE ". $this->table . " SET token=:token WHERE id=:id";
        $stmt= $this->pdo->prepare($sql);
        $stmt->execute(['token' => $token, 'id' => $user_id]);
        return true;
    }

}


Then simply call the instances of them:

<?php
require_once "../assets/config/config.php";
require_once "../vendor/autoload.php";

use PhotoTech\Database;
use PhotoTech\LoginRepository;
use PhotoTech\ErrorHandler;

$errorHandler = new ErrorHandler();
$database = Database::pdo($errorHandler);
$loginRepository = new LoginRepository($database);

// ...

Ultimately, you want to be coding to an Interface. Here is a definition:

A PHP interface defines a contract which a class must fulfill. If a PHP class is a blueprint for objects, an interface is a blueprint for classes. Any class implementing a given interface can be expected to have the same behavior in terms of what can be called, how it can be called, and what will be returned.

Next, have you heard of “Composition over Inheritance”? Learn it. It is important. Short version in regards to your code, do not extend PDO.

In your use, you are just re-inventing what PHP already does, and quite well at at. I already told you about set_exemption_handler. Learn it and use it. * There is case for using an ErrorHandler Class, but this is not it.

In your next Database class, you did a complete switch from extending PDO to using Static. I will leave it to you to learn about the pros/cons of a Static DB class.

I just released the Database component of a Framework I am writing. You can use it as an example of coding to an Interface. It also adheres to the SOLID Principles. (Learn it if you don’t know it). The Database module comes out of the box with a working SQLite example. You can “install” it with a simple composer command. I would suggest you install it stand-alone and try it that way.

composer require krubio/perfect-database

You can read the code here: GitHub - benanamen/perfect-database: Database Connection Classes

So Composition over Inheritance would be something like this

<?php

// ErrorHandler.php
namespace PhotoTech;

use Exception;
use PDOException;

class ErrorHandler
{
    public function handlePDOException(PDOException $e): void
    {
        error_log('PDO Error: ' . $e->getMessage());
        echo 'An error occurred while connecting to the database.';
    }

    public function handleGeneralException(Exception $e): void
    {
        error_log('General Error: ' . $e->getMessage());
        echo 'An error occurred.';
    }
}

and the Database Class

<?php

// Database.php
namespace PhotoTech;

use Exception;
use PDO;
use PDOException;

class Database
{
    private ErrorHandler $errorHandler;

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

    public function createPDO(): ?PDO
    {
        try {
            $db_options = [
                PDO::ATTR_EMULATE_PREPARES => false,
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
            ];
            return new PDO('mysql:host=' . DATABASE_HOST . ';dbname=' . DATABASE_NAME . ';charset=utf8mb4', DATABASE_USERNAME, DATABASE_PASSWORD, $db_options);
        } catch (PDOException $e) {
            $this->errorHandler->handlePDOException($e);
            return null;
        } catch (Exception $e) {
            $this->errorHandler->handleGeneralException($e);
            return null;
        }
    }
}

So the Database class contains an instance of the ErrorHandler class as a private property, which it can use to delegate error handling to the ErrorHandler instance using its public methods. This approach provides more flexibility and decoupling between classes. So if I wanted to I could use a different or better error handler?

So I am trying to simplify things and I got away from using static variables and methods and came up with this →

<?php /** @noinspection ALL */

namespace PhotoTech;

use Exception;
use JetBrains\PhpStorm\Pure;
use DateTime;
use DateTimeZone;
use PDO;

class CMS
{
    protected string $table = "cms"; // Table Name:
    protected array $db_columns = ['id', 'category', 'user_id', 'thumb_path', 'image_path', 'Model', 'ExposureTime', 'Aperture', 'ISO', 'FocalLength', 'author', 'heading', 'content', 'data_updated', 'date_added'];
    public $id;
    public $user_id;
    public $page;
    public $category;
    public $thumb_path;
    public $image_path;
    public $Model;
    public $ExposureTime;
    public $Aperture;
    public $ISO;
    public $FocalLength;
    public $author;
    public $heading;
    public $content;
    public $date_updated;
    public $date_added;

    protected PDO $pdo;

    /*
 * Construct the data for the CMS
 */
    public function __construct(PDO $pdo, array $args = [])
    {
        $this->pdo = $pdo;

        // Caution: allows private/protected properties to be set
        foreach ($args as $k => $v) {
            if (property_exists($this, $k)) {
                $v = $this->filterwords($v);
                $this->$k = $v;
                $this->params[$k] = $v;
                $this->objects[] = $v;
            }
        }
    } // End of construct method:

    /*
     * Create a short description of content and place a link button that I call 'more' at the end of the
     * shorten content.
     */
    #[Pure] public function intro($content = "", $count = 100): string
    {
        return substr($content, 0, $count) . "...";
    }

    public function setImagePath($image_path) {
        $this->image_path = $image_path;

    }


    public function countAllPage($category = 'home')
    {
        $sql = "SELECT count(id) FROM " . $this->table . " WHERE category=:category";
        $stmt = $this->pdo->prepare($sql);

        $stmt->execute(['category' => $category ]);
        return $stmt->fetchColumn();

    }

    public function page($perPage, $offset, $page = "index", $category = "home"): array
    {
        $sql = 'SELECT * FROM ' . $this->table . ' WHERE page =:page AND category =:category ORDER BY id DESC, date_added DESC LIMIT :perPage OFFSET :blogOffset';
        $stmt = $this->pdo->prepare($sql); // Prepare the query:
        $stmt->execute(['page' => $page, 'perPage' => $perPage, 'category' => $category, 'blogOffset' => $offset]); // Execute the query with the supplied data:
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }


    protected function filterwords($text) {
        $filterWords = array('Gosh Darn It');
        $filterCount = sizeof($filterWords);
        for ($i = 0; $i < $filterCount; $i++) {
            $text = preg_replace_callback('/\b' . $filterWords[$i] . '\b/i', function($matches){return str_repeat('*', strlen($matches[0]));}, $text);
        }
        return $text;
    }
    

} // End of class:

The only thing now I am kind of confused about is error handling but I will figure it out.

Found this statement about Composition over Inheritance that explains it pretty good for me I think →

“Composition over Inheritance” is a software development principle that recommends the use of object composition rather than class inheritance to achieve code reuse and modular design. In PHP, it means that instead of creating a subclass that inherits from a base class, you create a new class that contains an instance of the base class as a property and delegates calls to that instance.

And I’m just starting to understand the approach of polymorphism and how an interface allows for more flexibility and extensible code.

Thanks for the help.

I think I solved the error handler as I checkout what you have been pounding into my head →

<?php
// ErrorHandler.php
namespace PhotoTech;

use Exception;
use PDOException;
use Throwable;

class ErrorHandler
{
    public function handleException(Throwable $e): void
    {
        if ($e instanceof PDOException) {
            error_log('PDO Error: ' . $e->getMessage());
            echo 'An error occurred while connecting to the database.';
        } else {
            error_log('General Error: ' . $e->getMessage());
            echo 'An error occurred.';
        }
    }
}

So much simpler an this just go to my error long in my config folder and doesn’t display the error which I don’t care and like you said it’s only for hackers.

// Set error reporting level
error_reporting(E_ALL);

// Disable display of errors
ini_set('display_errors', '0');

// Enable error logging
ini_set('log_errors', '1');

// Set the path for the error log file
ini_set('error_log', __DIR__ . '/error_log/error_log_file.log');

and when I create a new instance

$errorHandler = new ErrorHandler();

// Register the exception handler method
set_exception_handler([$errorHandler, 'handleException']);
$database = new Database($errorHandler);
$pdo = $database->createPDO(); // I kind of like this as if I just want to do a quick query here no prob
$args = [];
$cms = new CMS($pdo, $args);

Now I just have to understand this better and get my head wrapped around interfaces.

I just have to say I love using interfaces as you can easily modify and organized you code better

<?php
// ErrorHandlerInterface.php
namespace PhotoTech;

use Throwable;

interface ErrorHandlerInterface {
    public function handleException(Throwable $e): void;
}
<?php
// ErrorHandler.php
namespace PhotoTech;

use Throwable;

class ErrorHandler implements ErrorHandlerInterface {
    public function handleException(Throwable $e): void {
        error_log('Error: ' . $e->getMessage());
        echo 'An error occurred.';
    }
}

It’s amazing how small the code gets →

<?php
// DatabaseInterface.php
namespace PhotoTech;

use PDO;

interface DatabaseInterface {
    public function createPDO(): ?PDO;
}

<?php

// Database.php
namespace PhotoTech;

use Throwable;
use PDO;


class Database implements DatabaseInterface
{
    private ErrorHandler $errorHandler;

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

    public function createPDO(): ?PDO
    {
        try {
            $db_options = [
                PDO::ATTR_EMULATE_PREPARES => false,
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
            ];
            return new PDO('mysql:host=' . DATABASE_HOST . ';dbname=' . DATABASE_NAME . ';charset=utf8mb4', DATABASE_USERNAME, DATABASE_PASSWORD, $db_options);
        } catch (Throwable $e) {
            $this->errorHandler->handleException($e);
            error_log("An error occurred while creating the PDO connection: " . $e->getMessage());
            exit;
        }
    }

}

and implementing is pretty straight forward

$errorHandler = new ErrorHandler();
$database = new Database($errorHandler);

$pdo = $database->createPDO();

$args = [];
$cms = new CMS($pdo, $args);

Glad to see you coming along. There is still more to go.

I’ll pose a question to you. What do you think will happen if you get rid of the try/catch in your Database class? Before you try it, think about it. If you understand what you just learned about you should be able to know.

I haven’t tried it, but I would guess that is where Dependency Injection comes into play, forcing a coder to use try/catch all over the place. I would imagine large websites would use much larger Error Handlers and it would be easy for them just to implement a much larger Error Handler Interface. As for me, I just using it for a small/medium websites. Using DI would be so much easier than hunting around for all that code needed to be changed.

No, that is not it. I don’t want to give it away for you. Consider this, what exactly does set_exemption handler do and what are you doing in contrast to that?

Well, the only thing I can think of is that If I remove the try-catch block in the Database class, any exception that occurs while creating the PDO connection will not be caught within the class. You would have to do it on the top level like a stack of books? The set_exception_handler sets the Sets a user-defined exception handler function (coming from php.net) meaning if not caught by the try/catch?. If I wanted to use that error handler on another class I would have to create an instance or in this case use Dependency Injection. (I’m probably not wording it right). So basically you’re creating your own error handler in this case or how it behaves?

Correct me if I wrong in thinking this, but using interfaces only implements on how the class should be structured and it a good way to make sure that class that uses the other class uses the same structure those make for more organized code?

You have the general idea on Interfaces. I wouldn’t wouldn’t be so concerned with that right now, especially since you are the only one working on the code.

I gave you a couple references already. Try those out and see what you can gain from that.

What I was hoping you would see is that you are using set_exemption_handler but you are still handling errors in the class itself at the same time. Move the error handling out of the Database class. The Database class should only be responsible for one thing, creating the DB connection. Remember “Separation Of Concerns”?

Like you said the Database class, its primary responsibility is to create and manage the database connection. However, error handling is an inevitable part of creating a database connection, as various exceptions may occur during the process. Instead of completely handling the errors within the Database class, I inject an ErrorHandler (or any other class implementing the ErrorHandlerInterface) to deal with the error handling aspect. This way, I maintain the Separation of Concerns by delegating the error handling task to a separate class. So, The try-catch block itself is just a mechanism for controlling the flow of the program when an exception occurs. How I handle the error within the catch block determines whether the code adheres to the Separation of Concerns principle or not. So if I wanted to do away with the try/catch block I would have to use the set_exception_handler. I think I’m grasping the concept? Though like you said I concentrating on redoing my website though I do have to say I’m see a lot of green checks an no duplicate code warnings which I used to scratch my head on :thinking:. Thanks for the help, I like coding from the ground up and try to avoid using libraries or frameworks. It makes it easier to code when you know the reason why.

Yes.

No. Php will take care of that all by itself. You only set set_exemption_handler in a global file used by the app, functions.php for example. Php will take care of sending the exception to your custom handler all by itself. I would really encourage you to look at the examples I already I gave you. You should notice I do not have have or call any error handling in the DB connection code.

There are only a couple instances when you should be using a Try/Catch. Your DB connection is not one of them. What I have been trying to tell you is completely get rid of your try/catches for the connection code. set_exemption_handler will catch the exceptions for you automatically.

Here is an example custom exception handler called by set_exemption_handler

function custom_exception(object $exception): void
{
    header('HTTP/1.1 500 Internal Server Error', TRUE, 500);
    require BASEDIR . '/resources/views/partials/header.php';
    echo '<div class="danger col-md-12"><b>500 Internal Server Error</b>';

    $error_msg = 'DATE: ' . MYSQL_DATETIME_TODAY . "\nERROR: " . $exception->getMessage() . "\nFILE: " . $exception->getFile() . ' on line ' . $exception->getLine() . "\n\nSTACK TRACE\n" . $exception->getTraceAsString() . "\n";

    if (EMAIL_ERROR) {
        echo '<br>Admin has been notified';
        error_log($error_msg, 1, ADMIN_EMAIL_TO, 'From:' . ADMIN_EMAIL_FROM);
    } else {
        echo '<br>Admin has not been notified';
    }

    // Write error to log
    if (LOG_ERROR) {
        echo '<br>Error has been logged';
        error_log("$error_msg\r\n", 3, ERROR_LOG_PATH);
    } else {
        echo '<br>Error has not been logged';
    }

    echo '</div>';

    if (DEBUG) {
        echo '<div class="danger col-md-12"><b>Error Message:</b>';
        echo '<pre>';
        echo '<br>Exception Code: ' . $exception->getCode() . '<br>';
        echo $exception->getMessage();
        echo '<br>FILE: ' . $exception->getFile();
        echo '<br>on line ' . $exception->getLine();
        echo '</pre>';
        echo '</div>';

        echo '<div class="danger"><b>Stack Trace:</b><br>';
        echo '<pre>';
        echo $exception->getTraceAsString();
        echo '</pre>';
        echo '</div>';
    }
    require BASEDIR . '/resources/views/partials/footer.php';
}

So I could just do the following 🤦

<?php
// ErrorHandler.php
namespace PhotoTech;

use Exception;
use PDOException;
use Throwable;

class ErrorHandler
{
    public function handleException(Throwable $e): void
    {
        if ($e instanceof PDOException) {
            error_log('PDO Error: ' . $e->getMessage());
        } else {
            error_log('General Error: ' . $e->getMessage());
        }
    }
}
<?php
// Database.php
namespace PhotoTech;

use PDO;

class Database
{
    private string $dsn;
    private string $username;
    private string $password;
    private array $options;

    public function __construct()
    {
        // Set your database credentials and options here
        $this->dsn = 'mysql:host=localhost;dbname=your_database;charset=utf8mb4';
        $this->username = 'your_username';
        $this->password = 'your_password';
        $this->options = [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false,
        ];
    }

    public function createPDO(): PDO
    {
        // No try/catch block, relying on the registered exception handler
        $pdo = new PDO($this->dsn, $this->username, $this->password, $this->options);
        return $pdo;
    }
}

then finally

use PhotoTech\ErrorHandler;
use PhotoTech\Database;
use PhotoTech\CMS;
use PhotoTech\Pagination;

$errorHandler = new ErrorHandler();

// Register the exception handler method
set_exception_handler([$errorHandler, 'handleException']);

$database = new Database();
$pdo = $database->createPDO();
$args = [];
$cms = new CMS($pdo, $args);```

Thank You and yes I really should look at your examples.  I will when I have  spare time.

If you do that first it will probably save you a lot of time and trial & error.

Sponsor our Newsletter | Privacy Policy | Terms of Service