PageRenderTime 57ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/classes/kohana/migration/manager.php

https://github.com/jmhobbs/K3-Migrate
PHP | 289 lines | 209 code | 43 blank | 37 comment | 10 complexity | fcf976b57be12953a0c8ef9328d73b8d MD5 | raw file
  1. <?php
  2. class Kohana_Migration_Manager {
  3. /**
  4. * @var Config
  5. */
  6. protected $config = null;
  7. protected $database = null;
  8. protected $appliedMigrations = array();
  9. protected $existsMigrations = array();
  10. public function __construct($config)
  11. {
  12. if ( ! is_dir($config->path))
  13. {
  14. throw new Kohana_Exception("Invalid Migrations Path: {$config->path}");
  15. }
  16. $this->config = $config;
  17. if (($database = getenv('ENVIRONMENT')) !== false)
  18. {
  19. $this->database = $database;
  20. }
  21. else
  22. {
  23. $this->database = $this->config->database;
  24. }
  25. $this->loadAppliedMigrations();
  26. $this->loadExistsMigrations();
  27. }
  28. public function enumerateMigrations()
  29. {
  30. return $this->existsMigrations;
  31. }
  32. public function enumerateMigrationsReverse()
  33. {
  34. return array_reverse($this->enumerateMigrations());
  35. }
  36. public function enumerateUpMigrations()
  37. {
  38. $applied = $this->getAppliedVersions();
  39. return array_filter(
  40. $this->enumerateMigrations(),
  41. function ($file) use ($applied)
  42. {
  43. $version = Migration_Manager::migrationNameToVersion($file);
  44. return !in_array($version, $applied);
  45. }
  46. );
  47. }
  48. public function enumerateDownMigrations()
  49. {
  50. $applied = $this->getAppliedVersions();
  51. return array_reverse(
  52. array_filter(
  53. $this->enumerateMigrations(),
  54. function ($file) use ($applied)
  55. {
  56. $version = Migration_Manager::migrationNameToVersion($file);
  57. return in_array($version, $applied);
  58. }
  59. )
  60. );
  61. }
  62. public function getAppliedVersions()
  63. {
  64. return $this->appliedMigrations;
  65. }
  66. protected function loadAppliedMigrations()
  67. {
  68. $version_file = $this->getSchemaVersionFileName();
  69. if (file_exists($version_file))
  70. {
  71. $this->appliedMigrations = file($version_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
  72. }
  73. }
  74. protected function loadExistsMigrations()
  75. {
  76. $files = scandir($this->config->path);
  77. $this->existsMigrations = array_map(
  78. 'Migration_Manager::fileNameToMigrationName',
  79. array_filter(
  80. $files,
  81. 'Migration_Manager::isMigrationFile'
  82. )
  83. );
  84. }
  85. public function getSchemaVersion()
  86. {
  87. $versions = $this->getAppliedVersions();
  88. return count($versions) ? end($versions) : 0;
  89. }
  90. public function setSchemaVersion($version, $deleted = false)
  91. {
  92. $version_file = $this->getSchemaVersionFileName();
  93. if ($deleted)
  94. {
  95. $this->appliedMigrations = array_diff($this->appliedMigrations, array($version));
  96. }
  97. else
  98. {
  99. $this->appliedMigrations[] = $version;
  100. }
  101. sort($this->appliedMigrations);
  102. file_put_contents($version_file, implode(PHP_EOL, $this->appliedMigrations));
  103. }
  104. protected function getSchemaVersionFileName()
  105. {
  106. if ( ! is_dir($this->config->path))
  107. {
  108. mkdir($this->config->path);
  109. }
  110. return rtrim($this->config->path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.'.version-'.$this->database;
  111. }
  112. public function lastSchemaVersion()
  113. {
  114. $migrations = $this->enumerateMigrations();
  115. return self::migrationNameToVersion(end($migrations));
  116. }
  117. public function getOrphansMigrations()
  118. {
  119. return array_diff(
  120. $this->getAppliedVersions(),
  121. array_map(
  122. 'Migration_Manager::migrationNameToVersion',
  123. $this->enumerateMigrations()
  124. )
  125. );
  126. }
  127. /**
  128. * @param string $name
  129. * @return Migration
  130. */
  131. public function getMigrationClass($name)
  132. {
  133. require_once($this->config->path.DIRECTORY_SEPARATOR.$name.'.php');
  134. $class_name = self::migrationNameToClassName($name);
  135. $class = new $class_name();
  136. return $class;
  137. }
  138. public function runMigrationUp($name)
  139. {
  140. $this->getMigrationClass($name)->migrateUp(Database::instance($this->database));
  141. $this->setSchemaVersion(self::migrationNameToVersion($name));
  142. }
  143. public function runMigrationDown($name)
  144. {
  145. $this->getMigrationClass($name)->migrateDown(Database::instance($this->database));
  146. $this->setSchemaVersion(self::migrationNameToVersion($name), true);
  147. }
  148. public function seed()
  149. {
  150. $seed_path = $this->config->path.DIRECTORY_SEPARATOR.'seed.php';
  151. if (is_file($seed_path))
  152. {
  153. require_once($seed_path);
  154. }
  155. else
  156. {
  157. throw new Exception('Seed file does not exist: '.$seed_path);
  158. }
  159. }
  160. public function create($class_name)
  161. {
  162. $name = time().'_'.self::classNameToMigrationName($class_name);
  163. $class = <<<END
  164. <?php defined('SYSPATH') or die('No direct script access.');
  165. class $class_name extends Migration {
  166. public function up()
  167. {
  168. }
  169. public function down()
  170. {
  171. }
  172. }
  173. END;
  174. file_put_contents($this->config->path.DIRECTORY_SEPARATOR.$name.'.php', $class);
  175. return $name.'.php';
  176. }
  177. /**
  178. * Check if the given filename matches the migration file format:
  179. * [timestamp]_[migration_name].php
  180. * @param string $filename
  181. * @return bool
  182. */
  183. public static function isMigrationFile($filename)
  184. {
  185. return (0 != preg_match('/[0-9]+_[a-zA-Z0-9_]+\.php/', basename($filename)));
  186. }
  187. /**
  188. * Convert a file name into a migration name (i.e. strip the extension)
  189. * Example: 1299086729_user_migration.php => 1299086729_user_migration
  190. * @param string $filename
  191. * @return string
  192. */
  193. public static function fileNameToMigrationName($filename)
  194. {
  195. $position = strrpos(strtolower(basename($filename)), '.php');
  196. if ($position !== false)
  197. {
  198. return substr(basename($filename), 0, $position);
  199. }
  200. }
  201. /**
  202. * Convert a migration name into the corresponding class name.
  203. * Example: 1299086729_user_migration => UserMigration
  204. * @param string $migration_name
  205. * @return string
  206. */
  207. public static function migrationNameToClassName($migration_name)
  208. {
  209. return str_replace(
  210. ' ', '',
  211. ucwords(
  212. str_replace(
  213. '_', ' ',
  214. preg_replace('/^[0-9]+_/', '', $migration_name)
  215. )
  216. )
  217. );
  218. }
  219. /**
  220. * Convert a class name to migration name.
  221. * Example: UserMigration => user_migration
  222. * @param string $class_name
  223. * @return string
  224. */
  225. public static function classNameToMigrationName($class_name)
  226. {
  227. preg_match_all('/[A-Z][^A-Z]*/', $class_name, $results);
  228. $name = strtolower(implode('_', $results[0]));
  229. if (strlen($name) == 0)
  230. {
  231. throw new Exception('Invalid class name: '.$class_name);
  232. }
  233. return $name;
  234. }
  235. /**
  236. * Convert a migration (file) name into it's version.
  237. * Example: 1299086729_user_migration => 1299086729
  238. * @param string $migration_name
  239. * @return int
  240. */
  241. public static function migrationNameToVersion($migration_name)
  242. {
  243. $split = explode('_', $migration_name);
  244. return intval($split[0]);
  245. }
  246. }