first commit
This commit is contained in:
949
class/class.Upgrade.php
Normal file
949
class/class.Upgrade.php
Normal file
@@ -0,0 +1,949 @@
|
||||
<?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';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user