View file sys/plugins/classes/table_structure.class.php

File size: 10.55Kb
<?php

/**
 * Работа со структурами таблиц.
 * Используется для выявления изменений в структурах таблиц при обновлении движка и формирования соответствующих SQL запросов.
 */
class table_structure {

    protected $_structure = array(); // поля и индексы таблицы
    protected $_properties = array();

    /**
     * загрузка структуры из INI файла
     * @param string $path Путь к файлу со структурой таблицы
     */
    function __construct($path = false) {
        if ($path)
            $this->loadFromIniFile($path);
    }

    /**
     * Очистка данных о структуре загруженной таблицы
     */
    public function clear() {
        $this->_structure = array();
        $this->_properties = array();
    }

    /**
     * загрузка структуры из INI файла
     * @param string $path Путь к файлу со структурой таблицы
     */
    function loadFromIniFile($path) {
        $this->clear();
        $ini = ini::read($path, 1);
        foreach ($ini as $key => $value) {
            if ($key == '~TABLE~PROPERTIES~')
                $this->_properties = $value;
            else
                $this->_structure[$key] = $value;
        }
    }

    /**
     * получение структуры таблицы из подключенной базы
     * @param string $table Имя таблицы
     */
    function loadFromBase($table) {
        $this->clear();
        $table = mysql_real_escape_string($table);
        // получение полей таблицы
        $q = mysql_query("SHOW FULL COLUMNS FROM `$table`");
        while ($result = mysql_fetch_assoc($q)) {
            $structure = array();
            $structure['type'] = $result['Type'];
            if ($result['Default'] == '' && $result['Null'] == 'YES')
                $structure['default_and_null'] = 'DEFAULT NULL';
            elseif ($result['Default'] != '' && $result['Null'] == 'YES')
                $structure['default_and_null'] = "NULL DEFAULT '" . my_esc($result['Default']) . "'";
            elseif ($result['Default'] != '' && $result['Null'] == 'NO')
                $structure['default_and_null'] = "NOT NULL DEFAULT '" . my_esc($result['Default']) . "'";
            else
                $structure['default_and_null'] = 'NOT NULL';
            $structure['ai'] = $result['Extra'] == 'auto_increment' ? 'AUTO_INCREMENT' : '';
            $structure['comment'] = $result['Comment'] ? "COMMENT '" . my_esc($result['Comment']) . "'" : '';
            $this->_structure['`' . $result['Field'] . '`'] = $structure;
        }
        // получение ключей таблицы
        $q = mysql_query("SHOW KEYS FROM `$table`");
        while ($result = mysql_fetch_assoc($q)) {
            $structure = array();
            if ($result['Key_name'] == 'PRIMARY')
                $structure['type'] = 'PRIMARY KEY';
            elseif ($result['Index_type'] == 'FULLTEXT')
                $structure['type'] = 'FULLTEXT KEY `' . my_esc($result['Key_name']) . '`';
            elseif (!$result['Non_unique'])
                $structure['type'] = 'UNIQUE KEY `' . my_esc($result['Key_name']) . '`';
            else
                $structure['type'] = 'KEY `' . my_esc($result['Key_name']) . '`';

            if (isset($this->_structure[$structure['type']]['fields']))
                $this->_structure[$structure['type']]['fields'] .= ", `" . my_esc($result['Column_name']) . "`";
            else
                $this->_structure[$structure['type']]['fields'] = "`" . my_esc($result['Column_name']) . "`";
        }
        // получение свойств таблицы
        $q = mysql_query("SHOW TABLE STATUS LIKE '$table'");
        $properties = mysql_fetch_assoc($q);
        $this->_properties['name'] = $properties['Name'];
        $this->_properties['engine'] = 'ENGINE=' . $properties['Engine'];
        $this->_properties['auto_increment'] = 'AUTO_INCREMENT=' . $properties['Auto_increment'];
        $this->_properties['comment'] = "COMMENT='" . my_esc($properties['Comment']) . "'";
    }

    /**
     * сохранение структуры в INI файл
     * @param string $path
     * @return boolean
     */
    function saveToIniFile($path) {
        return ini::save($path, array_merge($this->_structure, array('~TABLE~PROPERTIES~' => $this->_properties)), true);
    }

    /**
     * получение SQL запроса на создание таблицы
     * @return string SQL запрос на создание таблицы
     */
    function getSQLQueryCreate() {
        $sql = "CREATE TABLE `" . my_esc($this->_properties['name']) . "` (\r\n";
        $structure = array();
        foreach ($this->_structure as $name => $struct) {
            $struct_tmp = array();
            $struct_tmp[] = $name;

            if (isset($struct['fields'])) {
                $struct_tmp[] = '(' . $struct['fields'] . ')';
            } else {
                if ($struct['type'])
                    $struct_tmp[] = $struct['type'];
                if ($struct['default_and_null'])
                    $struct_tmp[] = $struct['default_and_null'];
                if ($struct['ai'])
                    $struct_tmp[] = $struct['ai'];
                if ($struct['comment'])
                    $struct_tmp[] = $struct['comment'];
            }
            $structure[] = implode(' ', $struct_tmp);
        }

        $sql .= implode(",\r\n", $structure);

        $sql .= "\r\n) ";
        $prop_tmp = array();
        if ($this->_properties['engine'])
            $prop_tmp[] = $this->_properties['engine'];
        $prop_tmp[] = 'DEFAULT CHARSET=utf8';
        // $prop_tmp[] = $this -> _properties['auto_increment'];
        if ($this->_properties['comment'])
            $prop_tmp[] = $this->_properties['comment'];

        $sql .= implode(' ', $prop_tmp);
        return $sql;
    }

    /**
     * получение SQL запроса на изменение таблицы
     * @param \table_structure $tStruct_obj Структура целевой таблицы
     * @return string SQL запрос для приведения структуры таблицы к $tStruct_obj
     */
    function getSQLQueryChange($tStruct_obj) {
        $to_add = array(); // добавляем поля (индексы)
        $to_delete = array(); // удаляем поля (индексы)
        $to_edit = array(); // изменяем поля (индексы)

        foreach ($tStruct_obj->_structure as $key => $value) {
            if (!isset($this->_structure[$key])) { // если в старой таблице такого поля не существует, то добавляе в создаваемые
                $to_add[$key] = $value;
                continue;
            }
        }

        foreach ($this->_structure as $key => $value) {
            if (!isset($tStruct_obj->_structure[$key])) { // если в новой таблице такого поля не существует, то добавляе в удаляемые
                $to_delete[$key] = $value;
                continue;
            }
            $new_value = $tStruct_obj->_structure[$key];
            foreach ($value as $key2 => $value2) {
                if ($new_value[$key2] != $value2) {
                    // если хоть один из параметров расходится, то допавляем в изменяемые
                    $to_edit[$key] = $new_value;
                    continue(2);
                }
            }
        }
        // если нет изменений
        if (!$to_add /* && !$to_delete */ && !$to_edit)
            return false;
        $sql = "ALTER TABLE `" . my_esc($this->_properties['name']) . "` \r\n";
        $structure = array();


        /*
          // обработка удаляемых полей (индексов)
          ksort($to_delete);
          foreach ($to_delete as $name => $struct) {
          if (isset($struct['fields'])) {
          if (strpos($name, 'PRIMARY') === 0)
          $structure[] = 'DROP PRIMARY KEY';
          elseif (preg_match('#`(.+?)`#', $name, $m))
          $structure[] = 'DROP INDEX ' . $m[1];
          continue;
          }
          $struct_tmp = array();
          $struct_tmp[] = 'DROP';
          $struct_tmp[] = $name;
          $structure[] = implode(' ', $struct_tmp);
          }
         */

        // обработка изменяемых полей (индексов)
        foreach ($to_edit as $name => $struct) {
            $struct_tmp = array();
            $struct_tmp[] = 'CHANGE';
            $struct_tmp[] = $name;
            $struct_tmp[] = $name;
            // индексы мы менять не можем, но зато мы можем удалить и создать заново
            if (isset($struct['fields'])) {
                if (strpos($name, 'PRIMARY') === 0)
                    $structure[] = 'DROP PRIMARY KEY';
                elseif (preg_match('#`(.+?)`#', $name, $m))
                    $structure[] = 'DROP INDEX ' . $m[1];
                $structure[] = 'ADD ' . $name . ' (' . $struct['fields'] . ')';
                continue;
            }
            if ($struct['type'])
                $struct_tmp[] = $struct['type'];
            if ($struct['default_and_null'])
                $struct_tmp[] = $struct['default_and_null'];
            if ($struct['ai'])
                $struct_tmp[] = $struct['ai'];
            if ($struct['comment'])
                $struct_tmp[] = $struct['comment'];
            $structure[] = implode(' ', $struct_tmp);
        }
        // обработка добавляемых полей (индексов)
        foreach ($to_add as $name => $struct) {
            $struct_tmp = array();
            $struct_tmp[] = 'ADD';
            $struct_tmp[] = $name;

            if (isset($struct['fields'])) {
                $struct_tmp[] = '(' . $struct['fields'] . ')';
            } else {
                if ($struct['type'])
                    $struct_tmp[] = $struct['type'];
                if ($struct['default_and_null'])
                    $struct_tmp[] = $struct['default_and_null'];
                if ($struct['ai'])
                    $struct_tmp[] = $struct['ai'];
                if ($struct['comment'])
                    $struct_tmp[] = $struct['comment'];
            }
            $structure[] = implode(' ', $struct_tmp);
        }

        $sql .= implode(",\r\n", $structure);

        return $sql;
    }
}