/** * テーブルマイグレートを自動解決する * @param unknown $argDBO * @param unknown $argTable * @return boolean */ public static function resolve($argDBO, $argTblName, $argLastMigrationHash = NULL) { static $executed = array(); // 1プロセス内で同じテーブルに対してのマイグレーションを2度も処理しない if (FALSE === (isset($executed[$argTblName]) && TRUE === $executed[$argTblName])) { $firstMigration = TRUE; if (!isset(ORMapper::$modelHashs[$argTblName])) { // コンソールから強制マイグレーションされる時に恐らくココを通る $nowModel = ORMapper::getModel($argDBO, $argTblName); } // XXX ORMapperとMigrationManagerは循環しているのでいじる時は気をつけて! $modelHash = ORMapper::$modelHashs[$argTblName]; // modelハッシュがmigrationハッシュに含まれていないかどうか $migrationHash = $argLastMigrationHash; if (NULL === $migrationHash) { // 既に見つけているマイグレーションハッシュから定義を取得する $diff = self::_getDiff($argDBO, $argTblName); if (NULL !== self::$_lastMigrationHash) { $migrationHash = self::$_lastMigrationHash; } } debug('$migrationHash=' . $migrationHash); debug('$modelHash=' . $modelHash); // マイグレーションハッシュがある場合は if (NULL !== $migrationHash) { if (FALSE !== strpos($migrationHash, $modelHash)) { // このテーブルはマイグレーション済み $executed[$argTblName] = TRUE; // 現在のテーブル定義と最新のマイグレーションファイル上のテーブルハッシュに差分が無いので何もしない debug('exists migration! ' . $migrationHash); return TRUE; } // 最後に適用している該当テーブルに対してのマイグレーションクラスを読み込んでmodelハッシュを比較する $migrationFilePath = getAutoMigrationPath() . $argDBO->dbidentifykey . '.' . $migrationHash . '.migration.php'; if (TRUE === file_exists($migrationFilePath) && TRUE === is_file($migrationFilePath)) { // 既にテーブルはあるとココで断定 $firstMigration = FALSE; // 直前のマイグレーションクラスをインスタンス化 @(include_once $migrationFilePath); // モデルハッシュが変わっているかどうかを比較 if ($modelHash == $migrationHash::$migrationHash) { // このテーブルはマイグレーション済み $executed[$argTblName] = TRUE; // 現在のテーブル定義と最新のマイグレーションファイル上のテーブルハッシュに差分が無いので何もしない return TRUE; } } } // テーブル定義を取得 $tableDefs = ORMapper::getModelPropertyDefs($argDBO, $argTblName); $describeDef = $tableDefs['describeDef']; $migrationClassDef = PHP_EOL; $migrationClassDef .= PHP_EOL . PHP_TAB . 'public function __construct(){' . PHP_EOL . PHP_TAB . PHP_TAB . str_replace('; ', ';' . PHP_EOL . PHP_TAB . PHP_TAB, $describeDef) . 'return;' . PHP_EOL . PHP_TAB . '}' . PHP_EOL; if (TRUE === $firstMigration) { // create指示を生成 $migrationClassDef .= PHP_EOL . PHP_TAB . 'public function up($argDBO){' . PHP_EOL . PHP_TAB . PHP_TAB . 'return $this->create($argDBO);' . PHP_EOL . PHP_TAB . '}' . PHP_EOL; // drop指示を生成 $migrationClassDef .= PHP_EOL . PHP_TAB . 'public function down($argDBO){' . PHP_EOL . PHP_TAB . PHP_TAB . 'return $this->drop($argDBO);' . PHP_EOL . PHP_TAB . '}' . PHP_EOL; } else { // ALTERかDROP指示を生成 $upAlterDef = '$alter = array(); '; $downAlterDef = '$alter = array(); '; // 差分をフィールドを走査して特定する $lastModel = new $migrationHash(); $beforeDescribes = $lastModel->describes; $describes = array(); $beforeFieldKey = NULL; eval(str_replace('$this->', '$', $describeDef)); // 増えてる減ってるでループの起点を切り替え if (count($describes) >= count($beforeDescribes)) { // フィールドが増えている もしくは数は変わらない foreach ($describes as $feldKey => $propary) { // 最新のテーブル定義に合わせて $alter = NULL; if (!array_key_exists($feldKey, $beforeDescribes)) { // 増えてるフィールドを単純に増やす $alter = 'ADD'; $downAlterDef .= '$alter["' . $feldKey . '"] = array(); '; $downAlterDef .= '$alter["' . $feldKey . '"]["alter"] = "DROP"; '; } elseif (sha1(serialize($propary)) != sha1(serialize($beforeDescribes[$feldKey]))) { // ハッシュ値が違うので新しいフィールド情報でAlterする $alter = 'MODIFY'; // 元に戻すMODYFI $alterDefs = ORMapper::getModelPropertyDefs($argDBO, $argTblName, array($feldKey => $beforeDescribes[$feldKey])); $downAlterDef .= str_replace('$this->describes = array(); ', '', $alterDefs['describeDef']); $downAlterDef .= '$alter["' . $feldKey . '"]["alter"] = "' . $alter . '"; '; } if (NULL === $alter) { // 処理をスキップして次のループへ $beforeFieldKey = $feldKey; continue; } // up生成 $alterDefs = ORMapper::getModelPropertyDefs($argDBO, $argTblName, array($feldKey => $propary)); $upAlterDef .= str_replace('$this->describes = array(); ', '', $alterDefs['describeDef']); $upAlterDef .= '$alter["' . $feldKey . '"]["alter"] = "' . $alter . '"; '; if ('ADD' === $alter) { if (NULL === $beforeFieldKey) { // 先頭にフィールドが増えている $upAlterDef .= '$alter["' . $feldKey . '"]["first"] = TRUE;'; } else { // ADDする箇所の指定 $upAlterDef .= '$alter["' . $feldKey . '"]["after"] = "' . $beforeFieldKey . '";'; } } $beforeFieldKey = $feldKey; } } else { // フィールドが減っている // XXX upとdownがただ増えている時と逆なだけ foreach ($beforeDescribes as $feldKey => $propary) { // 前のテーブル定義に合わせて $alter = NULL; if (!array_key_exists($feldKey, $describes)) { // 減ってるフィールドを単純にARTER DROPする $alter = 'ADD'; $upAlterDef .= '$alter["' . $feldKey . '"] = array(); '; $upAlterDef .= '$alter["' . $feldKey . '"]["alter"] = "DROP"; '; } elseif (sha1(serialize($propary)) != sha1(serialize($describes[$feldKey]))) { // ハッシュ値が違うので新しいフィールド情報でAlterする $alter = 'MODIFY'; // 元に戻すMODYFI $alterDefs = ORMapper::getModelPropertyDefs($argDBO, $argTblName, array($feldKey => $describes[$feldKey])); $upAlterDef .= str_replace('$this->describes = array(); ', '', $alterDefs['describeDef']); $upAlterDef .= '$alter["' . $feldKey . '"]["alter"] = "' . $alter . '"; '; } if (NULL === $alter) { // 処理をスキップして次のループへ $beforeFieldKey = $feldKey; continue; } // down生成 $alterDefs = ORMapper::getModelPropertyDefs($argDBO, $argTblName, array($feldKey => $propary)); $downAlterDef .= str_replace('$this->describes = array(); ', '', $alterDefs['describeDef']); $downAlterDef .= '$alter["' . $feldKey . '"]["alter"] = "' . $alter . '"; '; if ('ADD' === $alter) { if (NULL === $beforeFieldKey) { // 先頭にフィールドが増えている $downAlterDef .= '$alter["' . $feldKey . '"]["first"] = TRUE;'; } else { // ADDする箇所の指定 $downAlterDef .= '$alter["' . $feldKey . '"]["after"] = "' . $beforeFieldKey . '";'; } } $beforeFieldKey = $feldKey; } } // alter指示を生成 $migrationClassDef .= PHP_EOL . PHP_TAB . 'public function up($argDBO){' . PHP_EOL . PHP_TAB . PHP_TAB . str_replace('$this->describes', '$alter', str_replace('; ', ';' . PHP_EOL . PHP_TAB . PHP_TAB, $upAlterDef)) . PHP_EOL . PHP_TAB . PHP_TAB . 'return $this->alter($argDBO, $alter);' . PHP_EOL . PHP_TAB . '}' . PHP_EOL; $migrationClassDef .= PHP_EOL . PHP_TAB . 'public function down($argDBO){' . PHP_EOL . PHP_TAB . PHP_TAB . str_replace('$this->describes', '$alter', str_replace('; ', ';' . PHP_EOL . PHP_TAB . PHP_TAB, $downAlterDef)) . PHP_EOL . PHP_TAB . PHP_TAB . 'return $this->alter($argDBO, $alter);' . PHP_EOL . PHP_TAB . '}' . PHP_EOL; } // 現在の定義でマイグレーションファイルを生成する $migrationClassName = self::_createMigrationClassName($argTblName) . '_' . $modelHash; $migrationClassDef = 'class ' . $migrationClassName . ' extends MigrationBase {' . PHP_EOL . PHP_EOL . PHP_TAB . 'public $tableName = "' . strtolower($argTblName) . '";' . PHP_EOL . PHP_EOL . PHP_TAB . 'public static $migrationHash = "' . $modelHash . '";' . $migrationClassDef . '}'; $path = getAutoMigrationPath() . $argDBO->dbidentifykey . '.' . $migrationClassName . '.migration.php'; @file_put_contents($path, '<?php' . PHP_EOL . PHP_EOL . $migrationClassDef . PHP_EOL . PHP_EOL . '?>'); @chmod($path, 0777); // 生成した場合は、生成環境のマイグレーションが最新で、適用済みと言う事になるので // マイグレーション済みファイルを生成し、新たにマイグレーション一覧に追記する @file_put_contents_e(getAutoMigrationPath() . $argDBO->dbidentifykey . '.all.migrations', $migrationClassName . PHP_EOL, FILE_APPEND); @file_put_contents_e(getAutoMigrationPath() . $argDBO->dbidentifykey . '.dispatched.migrations', $migrationClassName . PHP_EOL, FILE_APPEND); $executed[$argTblName] = TRUE; debug('migration! ' . $migrationClassName); } return TRUE; }
/** * クラス自動生成のキャッシュ */ function generateClassCache($argGeneratedPath, $argIncludePath, $argClassBuffer, $argClassName = '') { $classCheck = $argClassName; if ('' !== $argClassName) { $classCheck = ' && !class_exists(\'' . $argClassName . '\', FALSE)'; } // 先ず先頭に条件文を追加 @file_put_contents_e($argGeneratedPath, '<?php' . sprintf(_getAutoGenerateIncPHPMainBase(), $argIncludePath) . '?>', FILE_PREPEND); // ファイルの終端に処理を追加 @file_put_contents_e($argGeneratedPath, '<?php' . PHP_EOL . 'if(FALSE === $unlink' . $classCheck . '){ ' . PHP_EOL . $argClassBuffer . PHP_EOL . '}' . PHP_EOL . '?>', FILE_APPEND); @chmod($argGeneratedPath, 0777); }