Search code examples
phpdesign-patternscoding-style

Saving my PDO connection as a global variable


While asking another questions about PDO queries, I've been told that saving my PDO connection object as global to use it in my various functions that call queries to my database is generally bad practice.

Here is how I generally use my PDO object:

function somefunction(){
    global $pdo;

    $statement = $pdo->prepare("some query");
    $statement->execute();
}

The arguments I've read are more about code maintenance and debugging, where it's hard to trace who modified the PDO object and where in the code it would be. Other people just simply reject using global variables for storing the PDO object, but can't really explain why global variables are a bad approach.

However, for a small-medium projects with only one database, is there really a disadvantage to using a global variable? I usually have my connection script and my functions script separately, where the functions script will require_once() the connection script, where my PDO object is created. In this way, my connection is always established and all modifications to the PDO object are done in my connection script.

Is there any fundamental flaws in using this approach?


Solution

  • Is there any fundamental flaws in using this approach?

    The very first thing you have to understand, is that $pdo is a part of storage logic. That means, it should be only used inside classes that do abstract data access, be it a SQL table or a collection.

    Let's look at your code,

    function somefunction(){
        global $pdo;
    
        $statement = $pdo->prepare("some query");
        $statement->execute();
    }
    

    What if you want to switch from MySQL to Mongo/MSSQL/PgSQL, in future? Then you will have to rewrite a lot of code.

    And for each database vendor, you will have to create a separated file with different variable. Just like this

    function somefunction(){
        global $mongo;
        return $mongo->fetch(...);
    }
    

    By using a global state, you end up with mass code duplication, because you cannot pass parameters and thus cannot change function's behavior at runtime.

    Now let's look at this,

    function somefunction($pdo){
        $statement = $pdo->prepare("some query");
        $statement->execute();
    }
    

    Here, $pdo is passed as an argument, thus there's no global state. But the problem still remains, you end up violating the Single-Responsibility Principle

    If you really want something that is maintainable, clean and very readable, you'd better stick with DataMappers. Here's an example,

    $pdo = new PDO(...);
    
    $mapper = new MySQL_DataMapper($pdo);
    $stuff = $mapper->fetchUserById($_SESSION['id'])    
    
    var_dump($stuff); // Array(...)
    
    // The class itself, it should look like this
    class MySQL_DataMapper
    {
        private $table = 'some_table';
    
        private $pdo;
    
        public function __construct($pdo)
        {
            $this->pdo = $pdo;
        }
    
        public function fetchUserById($id)
        {
            $query = "SELECT * FROM `{$this->table}` WHERE `id` =:id";
            $stmt = $this->pdo->prepare($query);
    
            $stmt->execute(array(
               ':id' => $id
            ));
    
            return $stmt->fetch();
        }
    }
    

    Conclusion

    • It doesn't really matter if your project is small or large, you should always avoid global state in all it forms (global variables, static classes, Singletons) - For the sake of code maintainability

    • You have to remember, that $pdo is not a part of your business logic. Its a part of storage logic. That means, before you even start doing something with business logic, like heavy computations, you should really abstract table access (including CRUD operations)

    • The bridge that brings together your data access abstraction and computation logic is usually called Service

    • You should always pass the things function's need as parameters

    • You'd better stop worrying about your code and start thinking about abstraction layers.

    • And finally, before you even start doing any stuff, you'd firstly initialize all your services in bootstrap.php and then start querying storage according to user's input ($_POST or $_GET).

    Just like,

    public function indexAction()
    {
        $id = $_POST['id']; // That could be $this->request->getPost('id')
        $result = $this->dataMapper->fetchById($id);
    
        return print_r($result, true);
    }