public function testGetTimestampFromFilename() { $test_data = array('2009_01_01_09_31_23.php' => '2009-01-01T09:31:23', '2008_02_27_10_42_59.php' => '2008-02-27T10:42:59', '1973_12_31_16_01_00' => '1973-12-31T16:01:00', '2054_13_01_00_00_00.php' => null, '1922_06_31_10_23_19.php' => null, '2001_02_30_18_23_45.php' => null, '2323_23_23_23_23.php' => null, '3434_34_34_34_43.p' => null); foreach ($test_data as $in => $out) { $this->assertEqual($out, MpmStringHelper::getTimestampFromFilename($in)); } }
/** * @static * @param object $obj a simple object with migration information (from a migration list) * @param string $method * @param array $options * @return mixed */ public static function runMigration(&$obj, $method = 'up', array $options = array()) { // if true, exceptions will not cause the script to exit $forced = isset($options['forced']) ? $options['forced'] : false; // if true, only echo back the SQL to be run $dryrun = isset($options['dryrun']) ? $options['dryrun'] : false; $db_config = MpmDbHelper::get_db_config(); $migrations_table = $db_config->migrations_table; $filename = MpmStringHelper::getFilenameFromTimestamp($obj->timestamp); $classname = 'Migration_' . str_replace('.php', '', $filename); // make sure the file exists; if it doesn't, skip it but display a message $migration_file = MpmListHelper::get_migration_file(MPM_DB_PATH . $filename); if (!$migration_file) { echo "\n\tMigration " . $obj->timestamp . ' (ID ' . $obj->id . ') skipped - file missing.'; return; } // file exists -- run the migration require_once $migration_file; $migration = new $classname(); $msg = "# Performing " . strtoupper($method) . " migration " . $obj->timestamp . ' (ID ' . $obj->id . ')'; if (!empty($migration->info)) { $msg .= "\n# {$migration->info}"; } echo "\n" . $msg . " ... "; MpmSqlLogger::log_to_file("\n" . $msg . "\n"); if ($migration instanceof MpmMigration) { $dbObj = MpmDbHelper::getPdoObj(); } else { $dbObj = MpmDbHelper::getMysqliObj(); } if ($dryrun) { $dbObj->dryrun = true; } $dbObj->beginTransaction(); if ($method == 'down') { $active = 0; } else { $active = 1; } try { $migration->{$method}($dbObj); $sql = "UPDATE `{$migrations_table}` SET `active` = '{$active}' WHERE `id` = {$obj->id}"; $dbObj->internal_exec($sql); } catch (Exception $e) { $dbObj->rollback(); echo "failed!"; echo "\n"; $clw = MpmCommandLineWriter::getInstance(); $clw->writeLine($e->getMessage(), 12); if (!$forced) { echo "\n\n"; // Return with non-0 error code to notify external caller to stop the deployment exit(1); } else { return; } } $dbObj->commit(); echo "done."; }
/** * Determines what action should be performed and takes that action. * * @uses MpmHelpController::displayHelp() * @uses MpmStringHelper::strToCamel() * @uses MpmAutoloadHelper::load() * * @return void */ public function doAction() { if (count($this->arguments) == 0) { return $this->displayHelp(); } else { $controller_name = $this->arguments[0]; $class_name = ucwords(MpmStringHelper::strToCamel('mpm_' . strtolower($controller_name) . '_controller')); try { MpmAutoloadHelper::load($class_name); } catch (Exception $e) { return $this->displayHelp(); } $obj = new $class_name(); return $obj->displayHelp(); } }
/** * Performs a single migration. * * @uses MpmStringHelper::getFilenameFromTimestamp() * @uses MpmDbHelper::getPdoObj() * @uses MpmDbHelper::getMysqliObj() * @uses MpmCommandLineWriter::getInstance() * @uses MpmCommandLineWriter::writeLine() * @uses MPM_DB_PATH * * @param object $obj a simple object with migration information (from a migration list) * @param int &$total_migrations_run a running total of migrations run * @param bool $forced if true, exceptions will not cause the script to exit * * @return void */ public static function runMigration(&$obj, $method = 'up', $forced = false) { $db_config = $GLOBALS['db_config']; $migrations_table = $db_config->migrations_table; $filename = MpmStringHelper::getFilenameFromTimestamp($obj->timestamp); $classname = 'Migration_' . str_replace('.php', '', $filename); // make sure the file exists; if it doesn't, skip it but display a message if (!file_exists(MPM_DB_PATH . $filename)) { echo "\n\tMigration " . $obj->timestamp . ' (ID ' . $obj->id . ') skipped - file missing.'; return; } // file exists -- run the migration echo "\n\tPerforming " . strtoupper($method) . " migration " . $obj->timestamp . ' (ID ' . $obj->id . ')... '; require_once MPM_DB_PATH . $filename; $migration = new $classname(); if ($migration instanceof MpmMigration) { $dbObj = MpmDbHelper::getPdoObj(); } else { $dbObj = MpmDbHelper::getMysqliObj(); } $dbObj->beginTransaction(); if ($method == 'down') { $active = 0; } else { $active = 1; } try { $migration->{$method}($dbObj); $sql = "UPDATE `{$migrations_table}` SET `active` = '{$active}' WHERE `id` = {$obj->id}"; $dbObj->exec($sql); } catch (Exception $e) { $dbObj->rollback(); echo "failed!"; echo "\n"; $clw = MpmCommandLineWriter::getInstance(); $clw->writeLine($e->getMessage(), 12); if (!$forced) { echo "\n\n"; exit; } else { return; } } $dbObj->commit(); echo "done."; }
/** * When part of the class autoloader stack, this method will dynamically try to located the proper class file and include it if the class is instantiated without already existing. * * @throws MpmClassUndefinedException * * @uses MPM_PATH * @uses MpmStringHelper::camelToLower() * * @param string $class_name the name of the class being instantiated * * @return void */ public static function load($class_name) { // already loaded, don't need this method if (class_exists($class_name, false) || interface_exists($class_name, false)) { return; } // where do we store the classes? $class_path = MPM_PATH . '/lib'; // class name is coming to us in camel caps with (possibly) an Mpm prefix... remove prefix and turn into lowercase string with underscores $filename = MpmStringHelper::camelToLower($class_name); if (substr($filename, 0, 4) == 'mpm_') { $filename = substr($filename, 4, strlen($filename)); } $filename .= '.php'; // is it in the class path? if (file_exists($class_path . '/' . $filename)) { require_once $class_path . '/' . $filename; } else { if (file_exists(MPM_PATH . '/config/' . $filename)) { require_once MPM_PATH . '/config/' . $filename; } else { $dir = dir($class_path); while (false != ($file = $dir->read())) { if ($file != '..' && $file != '.' && is_dir($class_path . '/' . $file)) { if (file_exists($class_path . '/' . $file . '/' . $filename)) { require_once $class_path . '/' . $file . '/' . $filename; } } } } } // make sure we've included the class if (false === class_exists($class_name, false)) { if (false === interface_exists($class_name, false)) { throw new MpmClassUndefinedException('Class or interface "' . $class_name . '" does not exist.', E_USER_ERROR); } } return; }
/** * Returns an array of objects which hold data about a migration file (timestamp, file, etc.). * * @uses MPM_DB_PATH * @uses MpmStringHelper::getTimestampFromFilename() * * @param string $sort should either be old or new; determines how the migrations are sorted in the array * * @return array */ public static function getListOfFiles($sort = 'old') { $list = array(); if ($sort == 'new') { $sort_order = 1; } else { $sort_order = 0; } $files = scandir(MPM_DB_PATH, $sort_order); foreach ($files as $file) { $full_file = MPM_DB_PATH . $file; if ($file != 'schema.php' && $file != '.' && $file != '..' && !is_dir($full_file) && stripos($full_file, '.php') !== false) { $timestamp = MpmStringHelper::getTimestampFromFilename($file); if ($timestamp !== null) { $obj = (object) array(); $obj->timestamp = $timestamp; $obj->filename = $file; $obj->full_file = $full_file; $list[] = $obj; } } } return $list; }
/** * Performs a single migration. * * @uses MpmStringHelper::getFilenameFromTimestamp() * @uses MpmDbHelper::getPdoObj() * @uses MpmDbHelper::getMysqliObj() * @uses MpmCommandLineWriter::getInstance() * @uses MpmCommandLineWriter::writeLine() * @uses MPM_DB_PATH * * @param object $obj a simple object with migration information (from a migration list) * @param int &$total_migrations_run a running total of migrations run * @param bool $forced if true, exceptions will not cause the script to exit * * @return void */ public static function runMigration(&$obj, $method = 'up', $forced = false) { $file_timestamp = MpmStringHelper::getFilenameFromTimestamp($obj->timestamp); if ($method == 'up') { $classname = 'Migration_' . $file_timestamp; // make sure the file exists; if it doesn't, skip it but display a message $files = glob(MPM_DB_PATH . $file_timestamp . '*.php'); if (empty($files)) { echo "\n\tMigration " . $obj->timestamp . ' (ID ' . $obj->id . ') skipped - file missing.'; return; } if (count($files) > 1) { echo "\n\tError: Duplicate migration timestamp found! " . $obj->timestamp . ' (ID ' . $obj->id . ')'; exit; } $filename = $files[0]; // file exists -- run the migration echo "\n\tPerforming " . strtoupper($method) . " migration " . $obj->timestamp . ' (ID ' . $obj->id . ')... '; require_once $filename; $migration = new $classname(); } else { //migrate down via stored database objects //fetch object from database //unserialize eval('?>' . $obj->objectstore . '<?'); $down_migration = 'Migration_objectstore_' . $file_timestamp; $migration = new $down_migration(); if (!$migration) { echo "failed!"; $clw = MpmCommandLineWriter::getInstance(); $clw->writeLine("Migration " . $obj->timestamp . ' (ID ' . $obj->id . ') - Object missing or broken', 12); if (!$forced) { echo "\n\n"; exit; } else { return; } } echo "\n\tPerforming " . strtoupper($method) . " migration " . $obj->timestamp . ' (ID ' . $obj->id . ')... '; } if ($migration instanceof MpmMigration) { $dbObj = MpmDbHelper::getPdoObj(); } else { $dbObj = MpmDbHelper::getMysqliObj(); } $dbObj->beginTransaction(); if ($method == 'down') { $active = 0; } else { $active = 1; } try { $migration->{$method}($dbObj); if ($method == 'up') { //fetch object, store in database $string = file_get_contents($filename); $string = preg_replace('/(class Migration_)(\\d{4}(?:_\\d{2}){5})/', '$1objectstore_$2', $string, 1); $query_serial = sprintf(', objectstore="%s" ', $dbObj->real_escape_string($string)); $sql = "UPDATE `mpm_migrations` SET `active` = '{$active}' {$query_serial} WHERE `id` = {$obj->id}"; } else { //delete from database. //Old and new items may need to take same ID space in DB $sql = "DELETE FROM `mpm_migrations` WHERE `id` = {$obj->id}"; } $dbObj->exec($sql); } catch (Exception $e) { $dbObj->rollback(); echo "failed!"; echo "\n"; $clw = MpmCommandLineWriter::getInstance(); $clw->writeLine($e->getMessage(), 12); if (!$forced) { echo "\n\n"; exit; } else { return; } } $dbObj->commit(); echo "done."; }
/** * Returns an array of objects which hold data about a migration file (timestamp, file, etc.). * * @uses MPM_DB_PATH * @uses MpmStringHelper::getTimestampFromFilename() * * @param string $sort should either be old or new; determines how the migrations are sorted in the array * * @return array */ public static function getListOfFiles($sort = 'old') { $list = array(); $exclude_list = array("templates\\/", "schema\\.php\$", "test_data\\.php\$"); $exclude_list_pattern = implode("|", $exclude_list); // SKIP_DOTS (. / ..) suppose to be included by default, but apparently not; $dir_iter = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(MPM_DB_PATH, FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::SKIP_DOTS)); foreach ($dir_iter as $file) { $file_name = $file->getFilename(); // abc.js $file_local_path = $file->getPathName(); // /home/www/dev/jscommon/test/abc.js if (preg_match('/\\.php$/i', $file_name) && !preg_match('/' . $exclude_list_pattern . '/i', $file_name)) { $timestamp = MpmStringHelper::getTimestampFromFilename($file_name); if ($timestamp !== null) { $obj = (object) array(); $obj->timestamp = $timestamp; $obj->filename = $file_name; $obj->full_file = $file_local_path; $list[strtotime($timestamp)] = $obj; } } } // foreach // sort by timestamp if ($sort == 'new') { krsort($list, SORT_NUMERIC); } else { // 'old' ksort($list, SORT_NUMERIC); } return $list; }