Source for file DBA.class.php

Documentation is available at DBA.class.php

  1. /**
  2.  * Класс DBA.
  3.  *
  4.  * @package energine
  5.  * @subpackage core
  6.  * @author 1m.dm
  7.  * @copyright ColoCall 2006
  8.  * @version $Id: fsource_energine_core_frameworkDBA.class.php.html,v 1.1 2007/09/17 14:32:33 pavka Exp $
  9.  */
  10.  
  11. require_once 'core/framework/SystemConfig.class.php';
  12.  
  13. /**
  14.  * Database Abstraction Layer.
  15.  *
  16.  * @package energine
  17.  * @subpackage core
  18.  * @abstract
  19.  */
  20. abstract class DBA extends SystemConfig {
  21.  
  22.     /**
  23.      * @access protected
  24.      * @var PDO экземпляр класса PDO (PHP Data Objects)
  25.      */
  26.     protected $pdo;
  27.  
  28.     /**
  29.      * @access protected
  30.      * @var string последний запрос к БД
  31.      */
  32.     protected $lastQuery;
  33.  
  34.     /**
  35.      * @access protected
  36.      * @var mixed результат последнего запроса к БД
  37.      */
  38.     protected $lastResult;
  39.  
  40.     /*
  41.      * Типы полей таблиц БД:
  42.      */
  43.  
  44.     /**
  45.      * Целое число
  46.      */
  47.     const COLTYPE_INTEGER = 'INT';
  48.  
  49.     /**
  50.      * Число с плавающей точкой
  51.      */
  52.     const COLTYPE_FLOAT = 'FLOAT';
  53.  
  54.     /**
  55.      * Дата
  56.      */
  57.     const COLTYPE_DATE = 'DATE';
  58.  
  59.     /**
  60.      * Время
  61.      */
  62.     const COLTYPE_TIME = 'TIME';
  63.  
  64.     /**
  65.      * Timestamp
  66.      */
  67.     const COLTYPE_TIMESTAMP = 'TIMESTAMP';
  68.  
  69.     /**
  70.      * Дата и время
  71.      */
  72.     const COLTYPE_DATETIME = 'DATETIME';
  73.  
  74.     /**
  75.      * Строка
  76.      */
  77.     const COLTYPE_STRING = 'VARCHAR';
  78.  
  79.     /**
  80.      * Типы строк только для внутреннего использования. Без комментариев :)
  81.      */
  82.     const COLTYPE_STRING1 = 'STRING';
  83.     const COLTYPE_STRING2 = 'VAR_STRING';
  84.  
  85.     /**
  86.      * Текст
  87.      */
  88.     const COLTYPE_TEXT = 'TEXT';
  89.  
  90.     /**
  91.      * Бинарные данные
  92.      */
  93.     const COLTYPE_BLOB = 'BLOB';
  94.  
  95.     /**
  96.      * Ошибки
  97.      */
  98.     const ERR_BAD_REQUEST = 'ERR_DATABASE_ERROR';
  99.  
  100.     /**
  101.      * Первичный индекс
  102.      *
  103.      */
  104.     const PRIMARY_INDEX = 'PRI';
  105.  
  106.     /**
  107.      * Уникальный индекс
  108.      *
  109.      */
  110.     const UNIQUE_INDEX = 'UNI';
  111.  
  112.     /**
  113.      * Индекс
  114.      *
  115.      */
  116.     const INDEX = 'MUL';
  117.  
  118.     /**
  119.      * Конструктор класса.
  120.      *
  121.      * @access public
  122.      * @param string $dsn Data Source Name для подключения к БД
  123.      * @param string $username имя пользователя
  124.      * @param string $password пароль
  125.      * @param array $driverOptions специфические параметры драйвера БД
  126.      * @return void 
  127.      */
  128.     public function __construct($dsn$username$passwordarray $driverOptions{
  129.         parent::__construct();
  130.         try {
  131.             $this->pdo = new PDO($dsn$username$password$driverOptions);
  132.         }
  133.         catch (PDOException $e{
  134.             throw new SystemException($e->getMessage()SystemException::ERR_DB);
  135.         }
  136.         $this->pdo->query('SET NAMES utf8');
  137.     }
  138.  
  139.     /**
  140.      * Выполняет SELECT-запрос к БД.
  141.      *
  142.      * Если количество аргументов метода больше 1, тогда $query трактуется
  143.      * как строка формата подобно функции printf, а дополнительные аргументы
  144.      * экранируются и помещаются на место меток (placeholder) строки $query.
  145.      *
  146.      * Возвращает в результате:
  147.      *     1. Массив вида
  148.      *            array(
  149.      *                rowID => array(fieldName => fieldValue, ...)
  150.      *            )
  151.      *        если запрос исполнился успешно и вернул какие-либо строки;
  152.      *     2. true, если запрос исполнился успешно, но не вернул ни одной строки;
  153.      *     3. false, если при выполнении запроса произошла ошибка.
  154.      *
  155.      * @access public
  156.      * @param string $query SELECT-запрос к БД
  157.      * @param mixed $var, ...
  158.      * @return mixed 
  159.      * @see printf()
  160.      */
  161.     public function selectRequest($query{
  162.         if (!is_string($query|| strlen($query== 0{
  163.             return false;
  164.         }
  165.  
  166.         $result false;
  167.  
  168.         $query $this->constructQuery(func_get_args());
  169.         $this->lastQuery $query;
  170.         $res $this->pdo->query($query);
  171.  
  172.         if (!($res instanceof PDOStatement)) {
  173.             $errorInfo $this->pdo->errorInfo();
  174.             throw new SystemException(self::ERR_BAD_REQUESTSystemException::ERR_DBarray($this->getLastRequest()$errorInfo[2]));
  175.         }
  176.  
  177.         $result array();
  178.         $rowCount 0;
  179.         while ($row $res->fetch(PDO::FETCH_ASSOC)) {
  180.             $fieldNum 0;
  181.             foreach ($row as $fieldName => $fieldValue{
  182.                 $fieldMeta $res->getColumnMeta($fieldNum);
  183.                 if (isset($fieldMeta['native_type'])) {
  184.                     if ($fieldMeta['native_type'== self::COLTYPE_DATETIME ||
  185.                         $fieldMeta['native_type'== self::COLTYPE_DATE{
  186.                         $fieldValue convertDatetimeToTimestamp($fieldValue);
  187.                     }
  188.                     elseif (in_array($fieldMeta['native_type']array(self::COLTYPE_STRING1self::COLTYPE_STRING2))) {
  189.                         $fieldValue = stripslashes($fieldValue);
  190.                     }
  191.                 }
  192.                 else {
  193.                     if ($fieldMeta['len'== 1{
  194.                         $fieldValue (intval($fieldValue== false true);
  195.                     }
  196.                 }
  197.                 $result[$rowCount][$fieldName$fieldValue;
  198.                 $fieldNum++;
  199.             }
  200.             $rowCount++;
  201.         }
  202.  
  203.         if (empty($result)) {
  204.             $result true;
  205.         }
  206.  
  207.         $this->lastResult $result;
  208.         return $result;
  209.     }
  210.  
  211.     /**
  212.      * Выполняет модифицирующую (INSERT, UPDATE, DELETE) операцию в БД.
  213.      *
  214.      * Если количество аргументов метода больше 1, тогда $query трактуется
  215.      * как строка формата подобно функции printf, а дополнительные аргументы
  216.      * экранируются и помещаются на место меток (placeholder) строки $query.
  217.      *
  218.      * Возвращает в результате:
  219.      *     1. Последний сгенерированный ID для поля типа AUTO_INCREMENT, или
  220.      *     2. true, если запрос выполнен успешно;
  221.      *     2. false, в случае неудачи.
  222.      *
  223.      * @access public
  224.      * @param string $query 
  225.      * @return mixed 
  226.      * @see printf()
  227.      */
  228.     public function modifyRequest($query{
  229.         if (!is_string($query|| strlen($query== 0{
  230.             return false;
  231.         }
  232.  
  233.         $result false;
  234.  
  235.         $query $this->constructQuery(func_get_args());
  236.         $this->lastQuery $query;
  237.         $res $this->pdo->query($query);
  238.  
  239.         if (!($res instanceof PDOStatement)) {
  240.             $errorInfo $this->pdo->errorInfo();
  241.             throw new SystemException(self::ERR_BAD_REQUESTSystemException::ERR_DBarray($this->getLastRequest()$errorInfo[2]));
  242.         }
  243.  
  244.         $result = intval($this->pdo->lastInsertId());
  245.  
  246.         if ($result == 0{
  247.             $result true;
  248.         }
  249.  
  250.         $this->lastResult $result;
  251.         return $result;
  252.     }
  253.  
  254.     /**
  255.      * Ставит кавычки вокруг входной строки (если необходимо) и экранирует
  256.      * специальные символы внутри входной строки.
  257.      *
  258.      * @access public
  259.      * @param string $string 
  260.      * @return string 
  261.      */
  262.     public function quote($string{
  263.         return $this->pdo->quote($string);
  264.     }
  265.  
  266.     /**
  267.      * Возвращает последний запрос к БД.
  268.      *
  269.      * @access public
  270.      * @return string 
  271.      */
  272.     public function getLastRequest({
  273.         return $this->lastQuery;
  274.     }
  275.  
  276.     /**
  277.      * Возвращает результат последнего запроса к БД.
  278.      *
  279.      * @access public
  280.      * @return mixed 
  281.      */
  282.     public function getLastResult({
  283.         return $this->lastResult;
  284.     }
  285.     /**
  286.      * Возвращает последнюю ошибку
  287.      *
  288.      * @return string 
  289.      * @access public
  290.      */
  291.  
  292.     public function getLastError({
  293.         return $this->pdo->errorInfo();
  294.     }
  295.     /**
  296.      * Стартует транзакцию.
  297.      *
  298.      * @access public
  299.      * @return boolean 
  300.      */
  301.     public function beginTransaction({
  302.         return $this->pdo->beginTransaction();
  303.     }
  304.  
  305.     /**
  306.      * Выполняет (commit) транзакцию.
  307.      *
  308.      * @access public
  309.      * @return boolean 
  310.      */
  311.     public function commit({
  312.         return $this->pdo->commit();
  313.     }
  314.  
  315.     /**
  316.      * Откатывает транзакцию.
  317.      *
  318.      * @access public
  319.      * @return boolean 
  320.      */
  321.     public function rollback({
  322.         return $this->pdo->rollBack();
  323.     }
  324.  
  325.     /**
  326.      * Возвращает информацию о колонках таблицы $tableName в виде массива:
  327.      *     array(
  328.      *         'columnName' => array(
  329.      *             'type' => тип колонки,
  330.      *             'length' => длина,
  331.      *             'nullable' => принимает ли значение NULL?,
  332.      *             'key' => описание ключа колонки (если есть),
  333.      *             'default' => значение по-умолчанию,
  334.      *             'index'=> тип индекса
  335.      *         )
  336.      *     )
  337.      *
  338.      * @access public
  339.      * @param string $tableName 
  340.      * @return array 
  341.      */
  342.     public function getColumnsInfo($tableName{
  343.         $res $this->selectRequest("SHOW COLUMNS FROM `$tableName`");
  344.         if (!is_array($res)) {
  345.             return false;
  346.         }
  347.  
  348.         $result false;
  349.  
  350.         foreach ($res as $row{
  351.             $name $row['Field'];
  352.             $type = strtoupper($row['Type']);
  353.             $length false;
  354.             $nullable (strtolower($row['Null']== 'yes' true false);
  355.             $key $row['Key'];
  356.             $index $key;
  357.             $default (empty($row['Default']))?false:$row['Default'];
  358.  
  359.             // получаем тип и размер поля
  360.             preg_match('/([A-Z]+)(\(([0-9]+)(,[0-9]+)?\))?/'$type$matches);
  361.             if (count($matches>= 2{
  362.                 $type $matches[1];
  363.                 if (isset($matches[3])) {
  364.                     $length = intval($matches[3]);
  365.                 }
  366.             }
  367.             $type $this->convertType($type);
  368.  
  369.             // получаем информацию о ключе поля
  370.             switch ($key{
  371.                 case 'PRI':
  372.                     $fk $this->getForeignKeyInfo($tableName$name);
  373.                     $key ($fk == false true $fk);
  374.                     break;
  375.                 case 'MUL':
  376.                     $key $this->getForeignKeyInfo($tableName$name);
  377.                     break;
  378.                 default:
  379.                     $key false;
  380.             }
  381.  
  382.             $result[$name= compact('length''nullable''default''key''type' 'tableName''index');
  383.         }
  384.  
  385.         return $result;
  386.     }
  387.  
  388.     /**
  389.      * Возвращает информацию о внешнем ключе поля $fieldName таблицы $tableName
  390.      * в виде массива
  391.      *     array(
  392.      *         'tableName' => имя таблицы,
  393.      *         'fieldName' => имя поля
  394.      *     )
  395.      * или false, если $tableName.$fieldName не является первичным ключем.
  396.      *
  397.      * @access private
  398.      * @param string $tableName имя таблицы
  399.      * @param string $fieldName имя поля
  400.      * @return mixed 
  401.      */
  402.     private function getForeignKeyInfo($tableName$fieldName{
  403.         /*
  404.         $res = $this->selectRequest("SHOW TABLE STATUS LIKE '$tableName'");
  405.         $fkinfos = explode(';', $res[0]['Comment']);
  406.         foreach ($fkinfos as $fkinfo) {
  407.             if (preg_match('/\(`([^`]+)`\) REFER `([^`]+)\/([^`]+)`(\(`([^`]+)`\))?/', $fkinfo, $matches) && count($matches) >= 4) {
  408.                 $matches[4] = (isset($matches[4]) ? $matches[5] : $matches[1]);
  409.                 if ($fieldName == $matches[1]) {
  410.                     $result = array('tableName' => $matches[3], 'fieldName' => $matches[4]);
  411.                     return $result;
  412.                 }
  413.             }
  414.         }
  415.         return false;
  416.         */
  417.  
  418.         $res $this->selectRequest("SHOW CREATE TABLE $tableName");
  419.         $fkinfos = explode(","$res[0]['Create Table']);
  420.         foreach ($fkinfos as $fkinfo{
  421.             if (preg_match("/FOREIGN KEY \(`$fieldName`\) REFERENCES `([^`]+)` \(`([^`]+)`\)/"$fkinfo$matches)/* && sizeof($matches) >= 4*/{
  422.                 $result array('tableName' => $matches[1]'fieldName' => $matches[2]);
  423.                 return $result;
  424.             }
  425.         }
  426.         return false;
  427.     }
  428.  
  429.     /**
  430.      * Конвертирует тип данных из описания БД (MySQL) в наш, системный тип.
  431.      *
  432.      * @access private
  433.      * @param string $mysqlType 
  434.      * @return string 
  435.      */
  436.     private function convertType($mysqlType{
  437.         $result $mysqlType;
  438.         switch ($mysqlType{
  439.             case 'TINYINT':
  440.             case 'MEDIUM':
  441.             case 'SMALLINT':
  442.             case 'INT':
  443.             case 'BIGINT':
  444.                 $result self::COLTYPE_INTEGER;
  445.                 break;
  446.             case 'FLOAT':
  447.             case 'DOUBLE':
  448.             case 'DECIMAL':
  449.             case 'NUMERIC':
  450.                 $result self::COLTYPE_FLOAT;
  451.                 break;
  452.             case 'DATE':
  453.                 $result self::COLTYPE_DATE;
  454.                 break;
  455.             case 'TIME':
  456.                 $result self::COLTYPE_TIME;
  457.                 break;
  458.             case 'TIMESTAMP':
  459.                 $result self::COLTYPE_TIMESTAMP;
  460.                 break;
  461.             case 'DATETIME':
  462.                 $result self::COLTYPE_DATETIME;
  463.                 break;
  464.             case 'VARCHAR':
  465.             case 'CHAR':
  466.                 $result self::COLTYPE_STRING;
  467.                 break;
  468.             case 'TEXT':
  469.             case 'TINYTEXT':
  470.             case 'MEDIUMTEXT':
  471.             case 'LONGTEXT':
  472.                 $result self::COLTYPE_TEXT;
  473.                 break;
  474.             case 'BLOB':
  475.             case 'TINYBLOB':
  476.             case 'MEDIUMBLOB':
  477.             case 'LONGBLOB':
  478.                 $result self::COLTYPE_BLOB;
  479.                 break;
  480.             default// не используется
  481.         }
  482.         return $result;
  483.     }
  484.  
  485.     /**
  486.      * Возвращает для таблицы $tableName имя таблицы с переводами,
  487.      * если такая существует. В противном случае возвращает false.
  488.      *
  489.      * @access public
  490.      * @param string $tableName 
  491.      * @return mixed 
  492.      */
  493.     public function getTranslationTablename($tableName{
  494.         $tableName .= '_translation';
  495.         $res $this->selectRequest("SHOW TABLES LIKE '$tableName'");
  496.         return (empty($res|| $res === truefalse $tableName;
  497.     }
  498.  
  499.     /**
  500.      * Формирует строку запроса к БД.
  501.      *
  502.      * @access private
  503.      * @param array $args массив аргументов, переданных в методы selectRequest и modifyRequest
  504.      * @return string 
  505.      * @see DBA::selectRequest()
  506.      * @see DBA::modifyRequest()
  507.      */
  508.     private function constructQuery(array $args{
  509.         if (sizeof($args1{
  510.             $query = array_shift($args)// отбрасываем первый аргумент $query
  511.             foreach ($args as &$arg{
  512.                 $arg $this->pdo->quote($arg);
  513.             }
  514.             array_unshift($args$query);
  515.             $query = call_user_func_array('sprintf'$args);
  516.         }
  517.         else {
  518.             $query $args[0];
  519.         }
  520.         return $query;
  521.     }
  522. }

Documentation generated on Mon, 17 Sep 2007 13:26:58 +0300 by phpDocumentor 1.4.0a2