Вход Регистрация
Файл: contao-3.5.8/system/modules/core/library/Contao/Database.php
Строк: 693
<?php

/**
 * Contao Open Source CMS
 *
 * Copyright (c) 2005-2016 Leo Feyer
 *
 * @license LGPL-3.0+
 */

namespace Contao;


/**
 * Abstract parent class to handle database communication
 *
 * The class is responsible for connecting to the database, listing tables and
 * fields, handling transactions and locking tables. It also creates the related
 * DatabaseStatement and DatabaseResult objects.
 *
 * Usage:
 *
 *     $db   = Database::getInstance();
 *     $stmt = $db->prepare("SELECT * FROM tl_user WHERE id=?");
 *     $res  = $stmt->execute(4);
 *
 * @property string $error The last error message
 *
 * @author Leo Feyer <https://github.com/leofeyer>
 */
abstract class Database
{

    
/**
     * Object instances (Singleton)
     * @var array
     */
    
protected static $arrInstances = array();

    
/**
     * Connection configuration
     * @var array
     */
    
protected $arrConfig = array();

    
/**
     * Connection ID
     * @var resource
     */
    
protected $resConnection;

    
/**
     * Disable autocommit
     * @var boolean
     */
    
protected $blnDisableAutocommit false;

    
/**
     * Cache
     * @var array
     */
    
protected $arrCache = array();

    
/**
     * List tables query
     * @var string
     */
    
protected $strListTables "SHOW TABLES FROM `%s`";


    
/**
     * Establish the database connection
     *
     * @param array $arrConfig A configuration array
     *
     * @throws Exception If a connection cannot be established
     */
    
protected function __construct(array $arrConfig)
    {
        
$this->arrConfig $arrConfig;
        
$this->connect();

        if (!
is_resource($this->resConnection) && !is_object($this->resConnection))
        {
            throw new 
Exception(sprintf('Could not connect to database (%s)'$this->error));
        }
    }


    
/**
     * Close the database connection if it is not permanent
     */
    
public function __destruct()
    {
        if (!
$this->arrConfig['dbPconnect'])
        {
            
$this->disconnect();

            
// Unset the reference (see #4772)
            
$strKey md5(implode(''$this->arrConfig));
            unset(static::
$arrInstances[$strKey]);
        }
    }


    
/**
     * Prevent cloning of the object (Singleton)
     */
    
final public function __clone() {}


    
/**
     * Return an object property
     *
     * @param string $strKey The property name
     *
     * @return string|null The property value
     */
    
public function __get($strKey)
    {
        if (
$strKey == 'error')
        {
            return 
$this->get_error();
        }

        return 
null;
    }


    
/**
     * Instantiate the Database object (Factory)
     *
     * @param array $arrCustom A configuration array
     *
     * @return Database The Database object
     */
    
public static function getInstance(array $arrCustom=null)
    {
        
$arrConfig = array
        (
            
'dbDriver'   => Config::get('dbDriver'),
            
'dbHost'     => Config::get('dbHost'),
            
'dbUser'     => Config::get('dbUser'),
            
'dbPass'     => Config::get('dbPass'),
            
'dbDatabase' => Config::get('dbDatabase'),
            
'dbPconnect' => Config::get('dbPconnect'),
            
'dbCharset'  => Config::get('dbCharset'),
            
'dbPort'     => Config::get('dbPort'),
            
'dbSocket'   => Config::get('dbSocket'),
            
'dbSqlMode'  => Config::get('dbSqlMode')
        );

        if (
is_array($arrCustom))
        {
            
$arrConfig array_merge($arrConfig$arrCustom);
        }

        
// Sort the array before generating the key
        
ksort($arrConfig);
        
$strKey md5(implode(''$arrConfig));

        if (!isset(static::
$arrInstances[$strKey]))
        {
            
$strClass 'Database\' . str_replace(' ', '_', ucwords(str_replace('_', ' ', strtolower($arrConfig['dbDriver']))));
            static::$arrInstances[$strKey] = new $strClass($arrConfig);
        }

        return static::$arrInstances[$strKey];
    }


    /**
     * Prepare a query and return a DatabaseStatement object
     *
     * @param string $strQuery The query string
     *
     * @return DatabaseStatement The DatabaseStatement object
     */
    public function prepare($strQuery)
    {
        return $this->createStatement($this->resConnection, $this->blnDisableAutocommit)->prepare($strQuery);
    }


    /**
     * Execute a query and return a DatabaseResult object
     *
     * @param string $strQuery The query string
     *
     * @return DatabaseResult|object The DatabaseResult object
     */
    public function execute($strQuery)
    {
        return $this->prepare($strQuery)->execute();
    }


    /**
     * Execute a raw query and return a DatabaseResult object
     *
     * @param string $strQuery The query string
     *
     * @return DatabaseResult|object The DatabaseResult object
     */
    public function query($strQuery)
    {
        return $this->createStatement($this->resConnection, $this->blnDisableAutocommit)->query($strQuery);
    }


    /**
     * Auto-generate a FIND_IN_SET() statement
     *
     * @param string  $strKey     The field name
     * @param mixed   $varSet     The set to find the key in
     * @param boolean $blnIsField If true, the set will not be quoted
     *
     * @return string The FIND_IN_SET() statement
     */
    public function findInSet($strKey, $varSet, $blnIsField=false)
    {
        if (is_array($varSet))
        {
            $varSet = implode('
,', $varSet);
        }

        return $this->find_in_set($strKey, $varSet, $blnIsField);
    }


    /**
     * Return all tables of a database as array
     *
     * @param string  $strDatabase The database name
     * @param boolean $blnNoCache  If true, the cache will be bypassed
     *
     * @return array An array of table names
     */
    public function listTables($strDatabase=null, $blnNoCache=false)
    {
        if ($strDatabase === null)
        {
            $strDatabase = $this->arrConfig['
dbDatabase'];
        }

        if (!$blnNoCache && isset($this->arrCache[$strDatabase]))
        {
            return $this->arrCache[$strDatabase];
        }

        $arrReturn = array();
        $objTables = $this->query(sprintf($this->strListTables, $strDatabase));

        while ($objTables->next())
        {
            $arrReturn[] = current($objTables->row());
        }

        $this->arrCache[$strDatabase] = $arrReturn;

        return $this->arrCache[$strDatabase];
    }


    /**
     * Determine if a particular database table exists
     *
     * @param string  $strTable    The table name
     * @param string  $strDatabase The optional database name
     * @param boolean $blnNoCache  If true, the cache will be bypassed
     *
     * @return boolean True if the table exists
     */
    public function tableExists($strTable, $strDatabase=null, $blnNoCache=false)
    {
        if ($strTable == '')
        {
            return false;
        }

        return in_array($strTable, $this->listTables($strDatabase, $blnNoCache));
    }


    /**
     * Return all columns of a particular table as array
     *
     * @param string  $strTable   The table name
     * @param boolean $blnNoCache If true, the cache will be bypassed
     *
     * @return array An array of column names
     */
    public function listFields($strTable, $blnNoCache=false)
    {
        if (!$blnNoCache && isset($this->arrCache[$strTable]))
        {
            return $this->arrCache[$strTable];
        }

        $this->arrCache[$strTable] = $this->list_fields($strTable);

        return $this->arrCache[$strTable];
    }


    /**
     * Determine if a particular column exists
     *
     * @param string  $strField   The field name
     * @param string  $strTable   The table name
     * @param boolean $blnNoCache If true, the cache will be bypassed
     *
     * @return boolean True if the field exists
     */
    public function fieldExists($strField, $strTable, $blnNoCache=false)
    {
        if ($strField == '' || $strTable == '')
        {
            return false;
        }

        foreach ($this->listFields($strTable, $blnNoCache) as $arrField)
        {
            if ($arrField['
name'] == $strField && $arrField['type'] != 'index')
            {
                return true;
            }
        }

        return false;
    }


    /**
     * Determine if a particular index exists
     *
     * @param string  $strName    The index name
     * @param string  $strTable   The table name
     * @param boolean $blnNoCache If true, the cache will be bypassed
     *
     * @return boolean True if the index exists
     */
    public function indexExists($strName, $strTable, $blnNoCache=false)
    {
        if ($strName == '' || $strTable == '')
        {
            return false;
        }

        foreach ($this->listFields($strTable, $blnNoCache) as $arrField)
        {
            if ($arrField['
name'] == $strName && $arrField['type'] == 'index')
            {
                return true;
            }
        }

        return false;
    }


    /**
     * Return the field names of a particular table as array
     *
     * @param string  $strTable   The table name
     * @param boolean $blnNoCache If true, the cache will be bypassed
     *
     * @return array An array of field names
     */
    public function getFieldNames($strTable, $blnNoCache=false)
    {
        $arrNames = array();
        $arrFields = $this->listFields($strTable, $blnNoCache);

        foreach ($arrFields as $arrField)
        {
            if ($arrField['
type'] != 'index')
            {
                $arrNames[] = $arrField['
name'];
            }
        }

        return $arrNames;
    }


    /**
     * Check whether a field value in the database is unique
     *
     * @param string  $strTable The table name
     * @param string  $strField The field name
     * @param mixed   $varValue The field value
     * @param integer $intId    The ID of a record to exempt
     *
     * @return boolean True if the field value is unique
     */
    public function isUniqueValue($strTable, $strField, $varValue, $intId=null)
    {
        $strQuery = "SELECT * FROM $strTable WHERE $strField=?";

        if ($intId !== null)
        {
            $strQuery .= " AND id!=?";
        }

        $objUnique = $this->prepare($strQuery)
                          ->limit(1)
                          ->execute($varValue, $intId);

        return $objUnique->numRows ? false : true;
    }


    /**
     * Return the IDs of all child records of a particular record (see #2475)
     *
     * @author Andreas Schempp
     *
     * @param mixed   $arrParentIds An array of parent IDs
     * @param string  $strTable     The table name
     * @param boolean $blnSorting   True if the table has a sorting field
     * @param array   $arrReturn    The array to be returned
     * @param string  $strWhere     Additional WHERE condition
     *
     * @return array An array of child record IDs
     */
    public function getChildRecords($arrParentIds, $strTable, $blnSorting=false, $arrReturn=array(), $strWhere='')
    {
        if (!is_array($arrParentIds))
        {
            $arrParentIds = array($arrParentIds);
        }

        if (empty($arrParentIds))
        {
            return $arrReturn;
        }

        $arrParentIds = array_map('
intval', $arrParentIds);
        $objChilds = $this->query("SELECT id, pid FROM " . $strTable . " WHERE pid IN(" . implode('
,', $arrParentIds) . ")" . ($strWhere ? " AND $strWhere" : "") . ($blnSorting ? " ORDER BY " . $this->findInSet('pid', $arrParentIds) . ", sorting" : ""));

        if ($objChilds->numRows > 0)
        {
            if ($blnSorting)
            {
                $arrChilds = array();
                $arrOrdered = array();

                while ($objChilds->next())
                {
                    $arrChilds[] = $objChilds->id;
                    $arrOrdered[$objChilds->pid][] = $objChilds->id;
                }

                foreach (array_reverse(array_keys($arrOrdered)) as $pid)
                {
                    $pos = (int) array_search($pid, $arrReturn);
                    array_insert($arrReturn, $pos+1, $arrOrdered[$pid]);
                }

                $arrReturn = $this->getChildRecords($arrChilds, $strTable, $blnSorting, $arrReturn, $strWhere);
            }
            else
            {
                $arrChilds = $objChilds->fetchEach('
id');
                $arrReturn = array_merge($arrChilds, $this->getChildRecords($arrChilds, $strTable, $blnSorting, $arrReturn, $strWhere));
            }
        }

        return $arrReturn;
    }


    /**
     * Return the IDs of all parent records of a particular record
     *
     * @param integer $intId    The ID of the record
     * @param string  $strTable The table name
     *
     * @return array An array of parent record IDs
     */
    public function getParentRecords($intId, $strTable)
    {
        $arrReturn = array();

        // Currently supports a nesting-level of 10
        $objPages = $this->prepare("SELECT id, @pid:=pid FROM $strTable WHERE id=?" . str_repeat(" UNION SELECT id, @pid:=pid FROM $strTable WHERE id=@pid", 9))
                         ->execute($intId);

        while ($objPages->next())
        {
            $arrReturn[] = $objPages->id;
        }

        return $arrReturn;
    }


    /**
     * Change the current database
     *
     * @param string $strDatabase The name of the target database
     *
     * @return boolean True if the database was changed successfully
     */
    public function setDatabase($strDatabase)
    {
        return $this->set_database($strDatabase);
    }


    /**
     * Begin a transaction
     */
    public function beginTransaction()
    {
        $this->begin_transaction();
    }


    /**
     * Commit a transaction
     */
    public function commitTransaction()
    {
        $this->commit_transaction();
    }


    /**
     * Rollback a transaction
     */
    public function rollbackTransaction()
    {
        $this->rollback_transaction();
    }


    /**
     * Lock one or more tables
     *
     * @param array $arrTables An array of table names to be locked
     */
    public function lockTables($arrTables)
    {
        $this->lock_tables($arrTables);
    }


    /**
     * Unlock all tables
     */
    public function unlockTables()
    {
        $this->unlock_tables();
    }


    /**
     * Return the table size in bytes
     *
     * @param string $strTable The table name
     *
     * @return integer The table size in bytes
     */
    public function getSizeOf($strTable)
    {
        return $this->get_size_of($strTable);
    }


    /**
     * Return the next autoincrement ID of a table
     *
     * @param string $strTable The table name
     *
     * @return integer The autoincrement ID
     */
    public function getNextId($strTable)
    {
        return $this->get_next_id($strTable);
    }


    /**
     * Return a universal unique identifier
     *
     * @return string The UUID string
     */
    public function getUuid()
    {
        return $this->get_uuid();
    }


    /**
     * Connect to the database server and select the database
     */
    abstract protected function connect();


    /**
     * Disconnect from the database
     */
    abstract protected function disconnect();


    /**
     * Return the last error message
     *
     * @return string The error message
     */
    abstract protected function get_error();


    /**
     * Auto-generate a FIND_IN_SET() statement
     *
     * @param string  $strKey     The field name
     * @param mixed   $varSet     The set to find the key in
     * @param boolean $blnIsField If true, the set will not be quoted
     *
     * @return string The FIND_IN_SET() statement
     */
    abstract protected function find_in_set($strKey, $varSet, $blnIsField=false);


    /**
     * Return a standardized array with the field information
     *
     * * name:       field name (e.g. my_field)
     * * type:       field type (e.g. "int" or "number")
     * * length:     field length (e.g. 20)
     * * precision:  precision of a float number (e.g. 5)
     * * null:       NULL or NOT NULL
     * * default:    default value (e.g. "default_value")
     * * attributes: attributes (e.g. "unsigned")
     * * index:      PRIMARY, UNIQUE or INDEX
     * * extra:      extra information (e.g. auto_increment)
     *
     * @param string $strTable The table name
     *
     * @return array An array with the field information
     *
     * @todo Support all kind of keys (e.g. FULLTEXT or FOREIGN)
     */
    abstract protected function list_fields($strTable);


    /**
     * Change the current database
     *
     * @param string $strDatabase The name of the target database
     *
     * @return boolean True if the database was changed successfully
     */
    abstract protected function set_database($strDatabase);


    /**
     * Begin a transaction
     */
    abstract protected function begin_transaction();


    /**
     * Commit a transaction
     */
    abstract protected function commit_transaction();


    /**
     * Rollback a transaction
     */
    abstract protected function rollback_transaction();


    /**
     * Lock one or more tables
     *
     * @param array $arrTables An array of table names
     */
    abstract protected function lock_tables($arrTables);


    /**
     * Unlock all tables
     */
    abstract protected function unlock_tables();


    /**
     * Return the table size in bytes
     *
     * @param string $strTable The table name
     *
     * @return integer The table size in bytes
     */
    abstract protected function get_size_of($strTable);


    /**
     * Return the next autoincrement ID of a table
     *
     * @param string $strTable The table name
     *
     * @return integer The autoincrement ID
     */
    abstract protected function get_next_id($strTable);


    /**
     * Return a universal unique identifier
     *
     * @return string The UUID string
     */
    abstract protected function get_uuid();


    /**
     * Create a DatabaseStatement object
     *
     * @param resource $resConnection        The connection ID
     * @param boolean  $blnDisableAutocommit If true, autocommitting will be disabled
     *
     * @return DatabaseStatement The DatabaseStatement object
     */
    abstract protected function createStatement($resConnection, $blnDisableAutocommit);


    /**
     * Execute a query and do not cache the result
     *
     * @param string $strQuery The query string
     *
     * @return DatabaseResult|object The DatabaseResult object
     *
     * @deprecated Use Database::execute() instead
     */
    public function executeUncached($strQuery)
    {
        return $this->execute($strQuery);
    }


    /**
     * Always execute the query and add or replace an existing cache entry
     *
     * @param string $strQuery The query string
     *
     * @return DatabaseResult|object The DatabaseResult object
     *
     * @deprecated Use Database::execute() instead
     */
    public function executeCached($strQuery)
    {
        return $this->execute($strQuery);
    }
}
Онлайн: 1
Реклама