Skip to content
Snippets Groups Projects
Forked from Stud.IP / Stud.IP
1002 commits behind the upstream repository.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
DBManager.class.php 6.61 KiB
<?php
# Lifter010: TODO

/*
 * Copyright (C) 2007 - Marcus Lunzenauer <mlunzena@uos.de>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 */

/**
 * This class provides a singleton instance that is used to manage PDO database
 * connections.
 *
 * Example of use:
 * @code
 *   # get hold of the DBManager's singleton
 *   $manager = DBManager::getInstance();
 *
 *   # set PDO connections using a DSN
 *   $manager->setConnection('example',
 *                           'mysql:host=localhost;dbname=example',
 *                           'root', '');
 *   # or an existing instance of PDO
 *   $manager->setConnection('example2', $existingPdo);
 *
 *   # retrieve a PDO connection later in your code
 *   $db = $manager->getConnection("studip");
 *
 *   # or as a shortcut
 *   $db = DBManager::get("studip");
 *
 *   # or even shorter ("studip" is the default key)
 *   $db = DBManager::get();
 *
 *   # and use the connection
 *   $db->query('SELECT * FROM user_info');
 *
 *   # you may even alias connections
 *   $manager->aliasConnection("studip", "studip-slave");
 *
 *   # but this is just sugar for
 *   $studip = $manager->getConnection("studip");
 *   $manager->setConnection("studip-slave", $studip);
 *
 * @endcode
 */
class DBManager
{


    /**
     * the singleton instance
     *
     * @access  private
     * @var     DBManager
     */
    static private $instance;


    /**
     * an array of connections of the singleton instance
     *
     * @access  private
     * @var     array
     */
    private $connections;

    /**
     * @access private
     *
     * @return void
     */
    private function __construct()
    {
        $this->connections = [];
    }


    /**
     * This method returns the singleton instance of this class.
     *
     * @return DBManager  the singleton instance
     */
    static public function getInstance()
    {
        if (is_null(DBManager::$instance)) {
            DBManager::$instance = new DBManager();
        }
        return DBManager::$instance;
    }


    /**
     * This method returns the database connection to the given key. Throws a
     * DBManagerException if there is no such connection.
     *
     * Example usage:
     * @code
     * try {
     *     $db = DBManager::getInstance()->getConnection("foo");
     *     $db->exec($sql);
     * } catch (DBManagerException $e) {
     *     echo "oops";
     * }
     * @endcode
     *
     * @param  string  the key
     *
     * @throw DBManagerException
     *
     * @return PDO     the database connection
     */
    public function getConnection($database)
    {

        if (!isset($this->connections[$database])) {
            throw new DBManagerException('Database connection: "'.$database.
                                         '" does not exist.');
        }

        return $this->connections[$database];
    }


    /**
     * This method maps the specified key to the specified database connection.
     *
     * You can either use an instance of class PDO or specify a DSN (optionally
     * with username/password).
     *
     * The (possibly newly created) connection is configured to throw exceptions
     * and to buffer queries if it is a MySQL connection.
     *
     * Examples:
     * @code
     *    $dbManager = DBManager::getInstance();
     *
     *    // using an existing PDO connection
     *    $existingPdo = new LoggingPDO($dsn);
     *    $dbManager->setConnection('studip', $pdo);
     *
     *    // using a DSN with username and password
     *    $dbManager->setConnection('studip', $dsn , $username, $password);
     * @endcode
     *
     * @param  string      the key
     * @param  string|PDO  either a DSN or an existing PDO connection
     * @param  string      (optional) the connection's username
     * @param  array       (optional) the connection's password
     *
     * @return DBManager this instance, useful for cascading method calls
     */
    public function setConnection($database, $dsnOrConnection, $user = NULL,
                                  $pass = NULL)
    {
        $connection = $dsnOrConnection instanceof PDO
            ? $dsnOrConnection
            : new StudipPDO($dsnOrConnection, $user, $pass);

        $this->configureConnection($connection);
        $this->connections[$database] = $connection;

        return $this;
    }


    // PDO connection should throw exceptions and use buffered queries
    private function configureConnection($connection)
    {

        $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        if ($connection->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql') {
            $connection->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
            // $connection->exec('SET CHARACTER SET latin1');
        }
    }

    /**
     * This method creates an alias for a database connection.
     *
     * This is useful if you want to use different keys but access the same
     * database, e.g. if you want to use master-slave replication in the future
     *
     * @param  string    the old key of the database connection
     * @param  string    the new key of the database connection
     *
     * @return DBManager this instance, useful for cascading method calls
     */
    public function aliasConnection($old, $new)
    {

        if (!isset($this->connections[$old])) {
            throw new DBManagerException('No database found using key: ' . $old);
        }

        $this->connections[$new] = $this->connections[$old];

        return $this;
    }


    /**
     * Shortcut static method to retrieve the database connection for a given
     * key.
     *
     * Example usage:
     * @code
     * // instead of
     * $db = DBManager::getInstance()->getConnection("studip");
     *
     * // this can be shortened to
     * $db = DBManager::get("studip");
     *
     * // or in this case (as "studip" is the default key)
     * $db = DBManager::get();
     * @endcode
     *
     * @param  string  the key
     *
     * @return StudipPDO     the database connection
     */
    static public function get($database = 'studip')
    {
        $manager = DBManager::getInstance();
        return $manager->getConnection($database);
    }
}


/**
 * The DBManager throws this exception if it cannot find a connection using
 * a non existant key.
 */
class DBManagerException extends Exception
{

    /**
     * @param  string   the message of this exception
     *
     * @return void
     */
    public function __construct($message)
    {
        parent::__construct($message);
    }
}