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 'Behem upgradu vznikly nasledujici chyby'; for ($i = 0; $i < count($this->errors); $i++) { echo '
';
}
$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 ''."\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';
}
}