950 lines
28 KiB
PHP
950 lines
28 KiB
PHP
<?php
|
|
|
|
use KupShop\KupShopBundle\Config;
|
|
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
|
|
use KupShop\KupShopBundle\Util\Database\QueryHint;
|
|
|
|
class Upgrade
|
|
{
|
|
use DatabaseCommunication;
|
|
|
|
public const VERBOSE_YES = true;
|
|
public const VERBOSE_NO = false;
|
|
|
|
public const LOCAL_UPGRADES_YES = true;
|
|
public const LOCAL_UPGRADES_NO = false;
|
|
|
|
public $path = [];
|
|
|
|
protected $priority;
|
|
|
|
// aktualni nastaveni vybraneho upgrade
|
|
public $upgradeIni = [];
|
|
// loguje chyby
|
|
public $errors = [];
|
|
// loguje zpravy
|
|
public $notes = [];
|
|
|
|
protected $verbose = false;
|
|
protected static $instanceCache = [];
|
|
|
|
private $useLocalUpgrades;
|
|
|
|
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
public function __construct($verbose = self::VERBOSE_NO, $useLocalUpgrades = self::LOCAL_UPGRADES_YES)
|
|
{
|
|
global $cfg;
|
|
|
|
$this->path['upgrades_dir'] = dirname(__DIR__).'/upgrade/list/';
|
|
$this->path['local_upgrades_dir'] = $cfg['Path']['web_root'].'upgrade/';
|
|
$this->verbose = $verbose;
|
|
$this->useLocalUpgrades = $useLocalUpgrades;
|
|
|
|
return true;
|
|
}
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// prohleda slozku a najde vsechny dostupne upgrade
|
|
|
|
public function loadFileNamesFromFolder($folder)
|
|
{
|
|
$files = [];
|
|
|
|
$dp = @opendir($folder);
|
|
if ($dp) {
|
|
while ($fileThis = readdir($dp)) {
|
|
if (!is_dir($fileThis)) {
|
|
// pokud odpovida jmeno souboru tvaru upgradu
|
|
if (preg_match('/^(.+)\\.php$/i', $fileThis, $matches)) {
|
|
$files[] = [
|
|
'file' => $folder.$fileThis,
|
|
'date' => $matches[1],
|
|
];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sort($files);
|
|
|
|
return $files;
|
|
}
|
|
|
|
public function getUpgrades()
|
|
{
|
|
$shared_files = $this->loadFileNamesFromFolder($this->path['upgrades_dir']);
|
|
if ($this->useLocalUpgrades) {
|
|
$local_files = $this->loadFileNamesFromFolder($this->path['local_upgrades_dir']);
|
|
$shared_files = array_merge($shared_files, $local_files);
|
|
}
|
|
|
|
try {
|
|
$finder = ServiceContainer::getService(\KupShop\KupShopBundle\Util\System\BundleFinder::class);
|
|
$files = $finder->getBundlesPath('Resources/upgrade/');
|
|
|
|
foreach ($files as $file) {
|
|
if ($this->loadFileNamesFromFolder($file) != null) {
|
|
$shared_files = array_merge($shared_files, $this->loadFileNamesFromFolder($file));
|
|
}
|
|
}
|
|
} catch (Throwable $e) {
|
|
echo 'Failed loading bundle upgrades: '.$e->getMessage();
|
|
}
|
|
|
|
return $shared_files;
|
|
}
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
public function checkTableExists($tableName, $dbName = null)
|
|
{
|
|
$change = true;
|
|
|
|
$db = $dbName ? 'IN '.$dbName : '';
|
|
$SQL = sqlQuery("SHOW TABLES {$db} LIKE '".getTableName($tableName, false)."'");
|
|
|
|
if (sqlNumRows($SQL) > 0) {
|
|
$change = false;
|
|
}
|
|
|
|
return $change;
|
|
}
|
|
|
|
public function checkDatabaseExists($tableName)
|
|
{
|
|
$change = true;
|
|
|
|
$SQL = sqlQuery("SHOW DATABASES LIKE '".getTableName($tableName, false)."'");
|
|
|
|
if (sqlNumRows($SQL) > 0) {
|
|
$change = false;
|
|
}
|
|
|
|
return $change;
|
|
}
|
|
|
|
public function checkTableIsEmpty($tableName)
|
|
{
|
|
$change = true;
|
|
|
|
if ($this->checkTableExists($tableName)) {
|
|
return true;
|
|
}
|
|
|
|
$SQL = sqlQuery('SELECT * FROM '.getTableName($tableName, false));
|
|
|
|
if (sqlNumRows($SQL) > 0) {
|
|
$change = false;
|
|
}
|
|
|
|
return $change;
|
|
}
|
|
|
|
public function checkColumnExists($table, $column)
|
|
{
|
|
$change = true;
|
|
|
|
try {
|
|
$SQL = sqlQuery('SHOW FIELDS FROM '.getTableName($table));
|
|
|
|
while (($row = sqlFetchArray($SQL)) !== false) {
|
|
if ($row['Field'] == $column) {
|
|
$change = false;
|
|
break;
|
|
}
|
|
}
|
|
} catch (Exception $e) {
|
|
// pass
|
|
}
|
|
|
|
return $change;
|
|
}
|
|
|
|
public function checkColumnIsEmpty($tableName, $columnName)
|
|
{
|
|
$change = true;
|
|
|
|
$SQL = sqlQuery('SELECT * FROM '.getTableName($tableName).' WHERE '.$columnName);
|
|
|
|
if (sqlNumRows($SQL) > 0) {
|
|
$change = false;
|
|
}
|
|
|
|
return $change;
|
|
}
|
|
|
|
public function checkHasPrimaryKey(string $table)
|
|
{
|
|
return !$this->checkConstraintExists($table, 'PRIMARY');
|
|
}
|
|
|
|
public function checkConstraintExists($table, $name)
|
|
{
|
|
$change = true;
|
|
|
|
// $SQL = sqlQuery("SELECT *
|
|
// FROM information_schema.KEY_COLUMN_USAGE
|
|
// WHERE CONSTRAINT_SCHEMA='".$GLOBALS['cfg']['Connection']['database']."' AND CONSTRAINT_NAME='$name' AND TABLE_NAME='".getTableName($table, false)."'");
|
|
|
|
$SQL = sqlQuery("SHOW KEYS FROM {$table} WHERE key_name='{$name}'");
|
|
|
|
if (sqlNumRows($SQL) > 0) {
|
|
$change = false;
|
|
}
|
|
|
|
return $change;
|
|
}
|
|
|
|
public function checkIsInnoDB($tableName)
|
|
{
|
|
$change = true;
|
|
|
|
$SQL = sqlFetchAssoc(sqlQuery("SHOW TABLE STATUS WHERE Name = '".getTableName($tableName, false)."'"));
|
|
|
|
if ($SQL['Engine'] == 'InnoDB') {
|
|
$change = false;
|
|
}
|
|
|
|
return $change;
|
|
}
|
|
|
|
public function checkFulltextConstraintExists($table, $name)
|
|
{
|
|
$change = true;
|
|
|
|
$SQL = sqlQuery("SELECT 1
|
|
FROM information_schema.STATISTICS
|
|
WHERE table_schema='".$GLOBALS['cfg']['Connection']['database']."' AND INDEX_NAME='{$name}' AND index_type='FULLTEXT' AND TABLE_NAME='".getTableName($table, false)."'");
|
|
|
|
if (sqlNumRows($SQL) > 0) {
|
|
$change = false;
|
|
}
|
|
|
|
return $change;
|
|
}
|
|
|
|
public function checkConstraintTable($table, $name)
|
|
{
|
|
$change = true;
|
|
|
|
$query = "SELECT 1
|
|
FROM information_schema.TABLE_CONSTRAINTS
|
|
WHERE CONSTRAINT_SCHEMA='".$GLOBALS['cfg']['Connection']['database']."' AND CONSTRAINT_NAME='{$name}' AND TABLE_NAME='".getTableName($table, false)."'";
|
|
if (sqlNumRows(sqlQuery($query)) > 0) {
|
|
$change = false;
|
|
}
|
|
|
|
return $change;
|
|
}
|
|
|
|
public function checkConstraintRule($table, $name, $delete = null, $update = null)
|
|
{
|
|
$change = true;
|
|
|
|
$query = "SELECT 1
|
|
FROM information_schema.REFERENTIAL_CONSTRAINTS
|
|
WHERE CONSTRAINT_SCHEMA='".$GLOBALS['cfg']['Connection']['database']."' AND CONSTRAINT_NAME='{$name}' AND TABLE_NAME='".getTableName($table, false)."'";
|
|
|
|
if ($delete) {
|
|
$query .= "AND DELETE_RULE='{$delete}'";
|
|
}
|
|
|
|
if ($update) {
|
|
$query .= "AND UPDATE_RULE='{$update}'";
|
|
}
|
|
|
|
if (sqlNumRows(sqlQuery($query)) > 0) {
|
|
$change = false;
|
|
}
|
|
|
|
return $change;
|
|
}
|
|
|
|
public function checkConstraintWithColumnExists($table, $name, $column)
|
|
{
|
|
$change = true;
|
|
|
|
$SQL = sqlQuery("SELECT 1
|
|
FROM information_schema.KEY_COLUMN_USAGE
|
|
WHERE TABLE_SCHEMA='".$GLOBALS['cfg']['Connection']['database']."'
|
|
AND CONSTRAINT_NAME='{$name}'
|
|
AND TABLE_NAME='".getTableName($table, false)."'
|
|
AND COLUMN_NAME='{$column}'");
|
|
|
|
if (sqlNumRows($SQL) > 0) {
|
|
$change = false;
|
|
}
|
|
|
|
return $change;
|
|
}
|
|
|
|
public function checkForeignKeyExists($table, $column)
|
|
{
|
|
$change = true;
|
|
|
|
$SQL = sqlQuery("SELECT 1
|
|
FROM information_schema.KEY_COLUMN_USAGE
|
|
WHERE TABLE_SCHEMA='{$GLOBALS['cfg']['Connection']['database']}'
|
|
AND TABLE_NAME='".getTableName($table, false)."'
|
|
AND COLUMN_NAME='{$column}'
|
|
AND REFERENCED_COLUMN_NAME IS NOT NULL");
|
|
|
|
if (sqlNumRows($SQL) > 0) {
|
|
$change = false;
|
|
}
|
|
|
|
return $change;
|
|
}
|
|
|
|
public function checkIfTriggerExists($triggerName, $table = null)
|
|
{
|
|
$change = true;
|
|
|
|
if (!$table) {
|
|
$table = str_replace('trigger_', '', $triggerName);
|
|
}
|
|
|
|
$SQL = sqlQuery('SELECT 1
|
|
FROM INFORMATION_SCHEMA.TRIGGERS
|
|
WHERE TRIGGER_NAME = :trigger
|
|
AND EVENT_OBJECT_SCHEMA = DATABASE()
|
|
AND EVENT_OBJECT_TABLE = :table',
|
|
['table' => $table, 'trigger' => $triggerName]);
|
|
if (sqlNumRows($SQL) > 0) {
|
|
$change = false;
|
|
}
|
|
|
|
return $change;
|
|
}
|
|
|
|
public function checkColumnType($table, $column, $type, $dbName = null)
|
|
{
|
|
$change = true;
|
|
$type = sqlFormatInput($type);
|
|
$dbName = $dbName ?? $GLOBALS['cfg']['Connection']['database'];
|
|
$SQL = sqlQuery("SELECT *
|
|
FROM information_schema.COLUMNS
|
|
WHERE TABLE_SCHEMA='".$dbName."'
|
|
AND TABLE_NAME='".getTableName($table, false)."'
|
|
AND COLUMN_NAME='{$column}'
|
|
AND COLUMN_TYPE COLLATE UTF8_GENERAL_CI LIKE '{$type}'");
|
|
|
|
if (sqlNumRows($SQL) > 0) {
|
|
$change = false;
|
|
}
|
|
|
|
return $change;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use self::checkEnumOptions
|
|
*/
|
|
public function checkEnumExists($table, $column, $value, &$values = '')
|
|
{
|
|
$change = true;
|
|
|
|
$type = returnSqlResult("SELECT COLUMN_TYPE
|
|
FROM information_schema.COLUMNS
|
|
WHERE TABLE_SCHEMA='".$GLOBALS['cfg']['Connection']['database']."'
|
|
AND TABLE_NAME='".getTableName($table, false)."'
|
|
AND COLUMN_NAME='{$column}'");
|
|
|
|
if (empty($type)) {
|
|
throw new \Exception("Can not find column {$table}:{$column}");
|
|
}
|
|
|
|
if (strstr($type, "'{$value}'") !== false) {
|
|
$change = false;
|
|
}
|
|
|
|
$values = $type;
|
|
|
|
return $change;
|
|
}
|
|
|
|
public function checkEnumOptions(string $table, string $column, array $values)
|
|
{
|
|
$enums = '';
|
|
if (empty($values)) {
|
|
throw new \Exception('No value');
|
|
}
|
|
$this->checkEnumExists($table, $column, 'bžet', $enums);
|
|
foreach ($values as $value) {
|
|
if (strstr($enums, "'{$value}'") === false) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function updateEnumOptions($table, $column, $values, $default = '')
|
|
{
|
|
$enums = '';
|
|
$this->checkEnumExists($table, $column, 'bžet', $enums);
|
|
|
|
foreach ($values as $value) {
|
|
if (strstr($enums, "'{$value}'") === false) {
|
|
$enums = str_replace(')', ",'{$value}')", $enums);
|
|
}
|
|
}
|
|
|
|
if ($default) {
|
|
$default = " DEFAULT '{$default}'";
|
|
}
|
|
|
|
sqlQuery("ALTER TABLE {$table} CHANGE `{$column}` `{$column}` {$enums} NOT NULL {$default};");
|
|
}
|
|
|
|
public function checkColumnIsNull($table, $column, $null)
|
|
{
|
|
$change = true;
|
|
|
|
$SQL = sqlQuery("SELECT 1
|
|
FROM information_schema.COLUMNS
|
|
WHERE TABLE_SCHEMA='".$GLOBALS['cfg']['Connection']['database']."'
|
|
AND TABLE_NAME='".getTableName($table, false)."'
|
|
AND COLUMN_NAME='{$column}'
|
|
AND IS_NULLABLE='".($null ? 'YES' : 'NO')."'");
|
|
|
|
if (sqlNumRows($SQL) > 0) {
|
|
$change = false;
|
|
}
|
|
|
|
return $change;
|
|
}
|
|
|
|
public function checkColumnCollation($table, $column, $collation)
|
|
{
|
|
$change = true;
|
|
|
|
$SQL = sqlQuery("SELECT 1
|
|
FROM information_schema.COLUMNS
|
|
WHERE TABLE_SCHEMA='".$GLOBALS['cfg']['Connection']['database']."'
|
|
AND TABLE_NAME='".getTableName($table, false)."'
|
|
AND COLUMN_NAME='{$column}'
|
|
AND COLLATION_NAME='{$collation}'");
|
|
|
|
if (sqlNumRows($SQL) > 0) {
|
|
$change = false;
|
|
}
|
|
|
|
return $change;
|
|
}
|
|
|
|
public function checkColumnDefault($table, $column, $default)
|
|
{
|
|
$change = true;
|
|
|
|
$SQL = sqlQuery("SHOW FIELDS FROM {$table}
|
|
WHERE Field=:column
|
|
AND `Default`=:default", ['default' => $default, 'column' => $column]);
|
|
|
|
if (sqlNumRows($SQL) > 0) {
|
|
$change = false;
|
|
}
|
|
|
|
return $change;
|
|
}
|
|
|
|
public function checkIndexNameExists($table, $name)
|
|
{
|
|
return sqlNumRows(sqlQuery("SHOW INDEXES FROM {$table} WHERE key_name=:name", ['name' => $name])) == 0;
|
|
}
|
|
|
|
public function checkIndexOnColumnExists($table, $column)
|
|
{
|
|
return sqlNumRows(sqlQuery("SHOW INDEXES FROM {$table} WHERE column_name=:column", ['column' => $column])) == 0;
|
|
}
|
|
|
|
public function checkModule($module, $submodule = null)
|
|
{
|
|
return findModule($module, $submodule);
|
|
}
|
|
|
|
public function checkDatabaseEncoding($collation)
|
|
{
|
|
return $collation != returnSQLResult('SELECT default_character_set_name FROM information_schema.SCHEMATA
|
|
WHERE schema_name = :database', ['database' => $GLOBALS['cfg']['Connection']['database']]);
|
|
}
|
|
|
|
public function checkTableEncoding($table, $collation)
|
|
{
|
|
return $collation != returnSQLResult('SELECT CCSA.character_set_name FROM information_schema.`TABLES` T,
|
|
information_schema.`COLLATION_CHARACTER_SET_APPLICABILITY` CCSA
|
|
WHERE CCSA.collation_name = T.table_collation
|
|
AND T.table_schema = :database
|
|
AND T.table_name = :table', ['database' => $GLOBALS['cfg']['Connection']['database'], 'table' => $table]);
|
|
}
|
|
|
|
public function checkFunctionExists($name)
|
|
{
|
|
return sqlNumRows(sqlQuery('SHOW FUNCTION STATUS WHERE name=:name AND db=:db', ['name' => $name, 'db' => Config::get()['Connection']['database']])) === 0;
|
|
}
|
|
|
|
public function checkProcedureExists($name)
|
|
{
|
|
return sqlNumRows(sqlQuery('SHOW PROCEDURE STATUS WHERE name=:name AND db=:db', ['name' => $name, 'db' => Config::get()['Connection']['database']])) === 0;
|
|
}
|
|
|
|
public function getTableInformationSchema(string $tableName): array|false
|
|
{
|
|
$database = Config::get()['Connection']['database'];
|
|
|
|
return sqlQueryBuilder()->select('*')
|
|
->from('information_schema.TABLES')
|
|
->andWhere(\Query\Operator::equals([
|
|
'TABLE_SCHEMA' => $database,
|
|
'TABLE_NAME' => $tableName,
|
|
]))->execute()->fetchAssociative();
|
|
}
|
|
|
|
// Posledni pouzita verze = 38
|
|
public function checkDataMigration($version)
|
|
{
|
|
return Settings::getDefault()->user_rights_version == $version - 1;
|
|
}
|
|
|
|
public function checkPrimaryKey($table, $column)
|
|
{
|
|
$return = sqlQueryBuilder()->select('COLUMN_KEY')->from('information_schema.COLUMNS')
|
|
->where(\Query\Operator::equals([
|
|
'TABLE_SCHEMA' => $GLOBALS['cfg']['Connection']['database'],
|
|
'COLUMN_NAME' => $column,
|
|
'TABLE_NAME' => $table,
|
|
'COLUMN_KEY' => 'PRI',
|
|
]))
|
|
->execute()->fetchOne();
|
|
|
|
return $return === false;
|
|
}
|
|
|
|
public function commitDataMigration($version)
|
|
{
|
|
$settings = Settings::getDefault();
|
|
$settings->user_rights_version = $version;
|
|
$settings->saveValue('user_rights_version', $version);
|
|
\Settings::clearCache();
|
|
}
|
|
|
|
public function loadIniFile($upgradeName)
|
|
{
|
|
// sestavit cestu k souboru
|
|
$src = $this->path['upgrades_dir'].'/'.$upgradeName;
|
|
|
|
if (!file_exists($src)) {
|
|
return false;
|
|
}
|
|
|
|
$this->upgradeIni = @parse_ini_file($src, true);
|
|
|
|
// kdyz neexistuje nastaveni souboru, pouzit defaukt
|
|
if (!array_key_exists('file', $this->upgradeIni['UpgradeSettings'])) {
|
|
$this->upgradeIni['UpgradeSettings']['file'] = str_replace('.ini', '', $upgradeName);
|
|
}
|
|
|
|
return $this->upgradeIni;
|
|
}
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
public function includeUpgradeScript($upgradeName)
|
|
{
|
|
// sestavit cestu k souboru
|
|
$src = $this->path['upgrades_dir'].'/'.$upgradeName;
|
|
|
|
// ukoncit, kdy soubor neexistuje
|
|
// ukoncit, kdy neni nalezeno nejdrive nastaveni
|
|
if (!file_exists($src)
|
|
|| count($this->upgradeIni['UpgradeSettings']) == 0
|
|
) {
|
|
return false;
|
|
}
|
|
include_once $src;
|
|
}
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
public function addError($msg)
|
|
{
|
|
$this->errors[] = $msg;
|
|
}
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
public function addNote($msg)
|
|
{
|
|
$this->notes[] = $msg;
|
|
}
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
public function noChanges()
|
|
{
|
|
$this->notes[] = 'Nebylo zapotrebi zmen';
|
|
}
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
public function upgradeOK()
|
|
{
|
|
echo ' + '.get_class($this).'::??????: OK';
|
|
}
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
public function printResult($global = true)
|
|
{
|
|
if (count($this->errors) > 0) {
|
|
echo '<strong>Behem upgradu vznikly nasledujici chyby</strong>';
|
|
|
|
for ($i = 0; $i < count($this->errors); $i++) {
|
|
echo '<li>'.$this->errors[$i].'</li>';
|
|
}
|
|
|
|
$this->errors = [];
|
|
}
|
|
|
|
if (!$global && count($this->notes) > 0) {
|
|
echo join(', ', $this->notes);
|
|
|
|
$this->notes = [];
|
|
}
|
|
}
|
|
|
|
public function run()
|
|
{
|
|
// Sorry za tohle, musím kvůli tomu že v buildu se spouští bez databáze ale musí proběhnout alespoň prvních pár migrací bez DB
|
|
try {
|
|
QueryHint::routeToMaster();
|
|
} catch (\Throwable) {
|
|
// pass
|
|
}
|
|
|
|
// nacte seznam ini souboru
|
|
$upgradesList = $this->getUpgrades();
|
|
|
|
// kontrola delky zpracovani skriptu
|
|
controlTimeLimit();
|
|
ini_set('memory_limit', '1G');
|
|
|
|
if ($this->verbose) {
|
|
echo '<pre>';
|
|
}
|
|
|
|
$this->loadUpgrades($upgradesList);
|
|
|
|
$this->orderUpgrades($upgradesList);
|
|
|
|
// -----------------------------------------------------
|
|
// projet a spustit vsechny nalezene upgrady
|
|
foreach ($upgradesList as $index => $upgrade) {
|
|
$className = get_class($upgrade['instance']);
|
|
if ($this->verbose) {
|
|
if ($index > 0) {
|
|
echo '---------------------------------------------------------'."\n";
|
|
}
|
|
|
|
echo 'Upgrade #'.($index + 1)." - {$className} ({$upgrade['date']})\n";
|
|
}
|
|
|
|
// inicializuje tridu
|
|
$time = microtime(true);
|
|
/** @var Upgrade $upgrade */
|
|
$upgrade = $upgrade['instance'];
|
|
$upgrade->setVerbose($this->verbose);
|
|
try {
|
|
$upgrade->upgrade();
|
|
} catch (Exception $exception) {
|
|
echo "Upgrade error: {$className}\n";
|
|
throw $exception;
|
|
}
|
|
if ($this->verbose) {
|
|
echo ' => '.round((microtime(true) - $time) * 1000).'ms'."\n";
|
|
}
|
|
|
|
// kontrola delky zpracovani skriptu
|
|
controlTimeLimit();
|
|
}
|
|
|
|
if ($this->verbose) {
|
|
echo '</pre><div id="end"></div>'."\n";
|
|
}
|
|
}
|
|
|
|
public function setVerbose($verbose)
|
|
{
|
|
$this->verbose = $verbose;
|
|
}
|
|
|
|
public function upgrade()
|
|
{
|
|
throw new LogicException('class Upgrade: method upgrade() not defined');
|
|
}
|
|
|
|
public function getShopDir()
|
|
{
|
|
global $cfg;
|
|
|
|
return realpath($cfg['Path']['web_root']).'/';
|
|
}
|
|
|
|
public function loadUpgrades(&$upgradesList)
|
|
{
|
|
foreach ($upgradesList as $index => &$upgrade) {
|
|
// Load class if not already loaded
|
|
if (!isset(static::$instanceCache[$upgrade['file']])) {
|
|
// nacist upgrade - trik jak ziskat nactenou classu
|
|
$classes = get_declared_classes();
|
|
|
|
include_once $upgrade['file'];
|
|
|
|
$diff = array_diff(get_declared_classes(), $classes);
|
|
$className = reset($diff);
|
|
|
|
if (!class_exists($className)) {
|
|
echo '!!! Chyba - chybi trida pro upgrade: '.$upgrade['file'];
|
|
continue;
|
|
}
|
|
|
|
/* @var Upgrade $u */
|
|
static::$instanceCache[$upgrade['file']] = new $className();
|
|
}
|
|
|
|
$u = static::$instanceCache[$upgrade['file']];
|
|
$upgrade['priority'] = $u->getPriority();
|
|
$upgrade['instance'] = $u;
|
|
|
|
// Upgrades from egnine/upgrade/list has higher priority
|
|
if ($upgrade['priority'] == null) {
|
|
if (strpos($upgrade['file'], 'upgrade/list') !== false) {
|
|
$upgrade['priority'] = -100;
|
|
} else {
|
|
$upgrade['priority'] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public function orderUpgrades(&$upgradesList)
|
|
{
|
|
usort($upgradesList, ['Upgrade', 'prioritySort']);
|
|
}
|
|
|
|
// sort by priority and date
|
|
public function prioritySort($a, $b)
|
|
{
|
|
$order = ['priority' => 'asc', 'date' => 'asc'];
|
|
$t = [true => -1, false => 1];
|
|
$r = true;
|
|
$k = 1;
|
|
foreach ($order as $key => $value) {
|
|
$k = ($value === 'asc') ? 1 : -1;
|
|
$r = ($a[$key] < $b[$key]);
|
|
if ($a[$key] !== $b[$key]) {
|
|
return $t[$r] * $k;
|
|
}
|
|
}
|
|
|
|
return $t[$r] * $k;
|
|
}
|
|
|
|
/**
|
|
* @return int
|
|
*/
|
|
public function getPriority()
|
|
{
|
|
return $this->priority;
|
|
}
|
|
|
|
/**
|
|
* @throws ReflectionException
|
|
*/
|
|
public function ensureDbExists(string $db): bool
|
|
{
|
|
try {
|
|
sqlGetConnection()->connect();
|
|
|
|
sqlQuery("USE `{$db}`");
|
|
|
|
return false;
|
|
} catch (Exception $e) {
|
|
sqlGetConnection()->close();
|
|
}
|
|
|
|
$reflectionClass = new ReflectionClass(\Doctrine\DBAL\Connection::class);
|
|
try {
|
|
$reflectionProperty = $reflectionClass->getProperty('_params');
|
|
} catch (ReflectionException $e) {
|
|
$reflectionProperty = $reflectionClass->getProperty('params');
|
|
}
|
|
$reflectionProperty->setAccessible(true);
|
|
$params = $reflectionProperty->getValue(sqlGetConnection());
|
|
unset($params['dbname']);
|
|
$reflectionProperty->setValue(sqlGetConnection(), $params);
|
|
|
|
sqlGetConnection()->connect();
|
|
|
|
$params['dbname'] = $db;
|
|
$reflectionProperty->setValue(sqlGetConnection(), $params);
|
|
|
|
sqlQuery("CREATE DATABASE IF NOT EXISTS `{$db}`");
|
|
|
|
sqlQuery("USE `{$db}`");
|
|
|
|
return true;
|
|
}
|
|
|
|
protected function isAllowed()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
protected function addPrivilegeToPrivilegedAdmins(?string $conditionPrivilege = null, string $newPrivilege)
|
|
{
|
|
$qb = sqlQueryBuilder()->select('id, privilege')->from('admins');
|
|
if (isset($conditionPrivilege)) {
|
|
$qb->where('privilege LIKE :privilege')
|
|
->setParameter('privilege', '%'.$conditionPrivilege.'%');
|
|
}
|
|
foreach ($qb->execute() as $user) {
|
|
$privilegeArr = explode('|', $user['privilege']);
|
|
if (isset($conditionPrivilege) && !in_array($conditionPrivilege, $privilegeArr)) {
|
|
continue;
|
|
}
|
|
|
|
// pridavam pravo vsem krom adminum z pravem ALL_RIGHTS
|
|
if (!isset($conditionPrivilege) && in_array('ALL_RIGHTS', $privilegeArr)) {
|
|
continue;
|
|
}
|
|
|
|
$privilegeArr[] = $newPrivilege;
|
|
$privilege = join('|', $privilegeArr);
|
|
$this->updateSQL('admins', ['privilege' => $privilege], ['id' => $user['id']]);
|
|
}
|
|
}
|
|
}
|
|
|
|
class UpgradeNew extends Upgrade
|
|
{
|
|
public function upgrade()
|
|
{
|
|
$changed = false;
|
|
|
|
$methods = get_class_methods($this);
|
|
|
|
if (!$this->isAllowed()) {
|
|
$this->addNote('Preskoceno');
|
|
$methods = [];
|
|
}
|
|
|
|
foreach ($methods as $method) {
|
|
if (!preg_match('/^check(Rightfulness)?_(.+)$/i', $method, $matches)) {
|
|
continue;
|
|
}
|
|
|
|
$name = $matches[2];
|
|
$checkRightfulness = $matches[0];
|
|
|
|
if (method_exists($this, "makeChanges_{$name}")) {
|
|
$makeChanges = "makeChanges_{$name}";
|
|
} elseif (method_exists($this, "upgrade_{$name}")) {
|
|
$makeChanges = "upgrade_{$name}";
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
// jestlize je opravnene udelat upgrade, tak provest
|
|
$time = microtime(true);
|
|
$change = $this->$checkRightfulness();
|
|
|
|
if (is_null($change)) {
|
|
echo 'Method "'.$checkRightfulness.'" in class "'.get_class($this).'" returns NULL instead of boolean! You might have missed the word "return".'.PHP_EOL;
|
|
}
|
|
|
|
if ($this->verbose && round((microtime(true) - $time) * 1000) > 10) {
|
|
echo ' ! '.$name.': '.$this->getComment($makeChanges).' checking took '.round((microtime(true) - $time) * 1000).'ms)'."\n";
|
|
}
|
|
|
|
if ($change) {
|
|
$changed = true;
|
|
|
|
$comment = $this->getComment($makeChanges) ?: get_class($this).'::'.$makeChanges;
|
|
echo ' + '.$comment.': ';
|
|
|
|
flush();
|
|
|
|
$time = microtime(true);
|
|
$this->$makeChanges();
|
|
|
|
$this->printResult(false);
|
|
|
|
echo ' ('.round((microtime(true) - $time) * 1000).'ms)';
|
|
|
|
echo "\n";
|
|
}
|
|
}
|
|
|
|
if (!$changed) {
|
|
$this->noChanges();
|
|
$this->printResult();
|
|
}
|
|
}
|
|
|
|
public function getComment($method)
|
|
{
|
|
$c = new ReflectionClass($this);
|
|
$m = $c->getMethod($method);
|
|
$s = $m->getDocComment();
|
|
$s = str_replace('/*', '', $s);
|
|
$s = str_replace('*/', '', $s);
|
|
$s = str_replace('*', '', $s);
|
|
|
|
return trim($s);
|
|
}
|
|
|
|
public function checkSetOptions($table, $column, $values)
|
|
{
|
|
$this->checkEnumExists($table, $column, 'bžet', $set);
|
|
|
|
$set = preg_match_all("/'([^']+)'/", $set, $matches);
|
|
|
|
if (array_diff($values, $matches[1])) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function updateSetOptions($table, $column, $values)
|
|
{
|
|
$typesString = array_map(fn ($value) => "'{$value}'", $values);
|
|
|
|
sqlQuery("ALTER TABLE {$table} MODIFY COLUMN {$column} SET(".join(',', $typesString).');');
|
|
}
|
|
|
|
public function upgradeOK()
|
|
{
|
|
$this->notes[] = 'OK';
|
|
}
|
|
}
|