PageRenderTime 59ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Doctrine/DBAL/Migrations/Configuration/Configuration.php

https://github.com/304/Zend_Framework_Skeleton
PHP | 504 lines | 337 code | 24 blank | 143 comment | 3 complexity | b25641eceb370140116ac5ec69162e2a MD5 | raw file
  1. <?php
  2. /*
  3. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  4. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  5. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  6. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  7. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  8. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  9. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14. *
  15. * This software consists of voluntary contributions made by many individuals
  16. * and is licensed under the LGPL. For more information, see
  17. * <http://www.doctrine-project.org>.
  18. */
  19. namespace Doctrine\DBAL\Migrations\Configuration;
  20. use Doctrine\DBAL\Connection,
  21. Doctrine\DBAL\Migrations\MigrationException,
  22. Doctrine\DBAL\Migrations\Version,
  23. Doctrine\DBAL\Migrations\OutputWriter,
  24. Doctrine\DBAL\Schema\Table,
  25. Doctrine\DBAL\Schema\Column,
  26. Doctrine\DBAL\Types\Type;
  27. /**
  28. * Default Migration Configurtion object used for configuring an instance of
  29. * the Migration class. Set the connection, version table name, register migration
  30. * classes/versions, etc.
  31. *
  32. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
  33. * @link www.doctrine-project.org
  34. * @since 2.0
  35. * @author Jonathan H. Wage <jonwage@gmail.com>
  36. */
  37. class Configuration
  38. {
  39. /**
  40. * Name of this set of migrations
  41. *
  42. * @var string
  43. */
  44. private $name;
  45. /**
  46. * Flag for whether or not the migration table has been created
  47. *
  48. * @var bool
  49. */
  50. private $migrationTableCreated = false;
  51. /**
  52. * Connection instance to use for migrations
  53. *
  54. * @var Connection
  55. */
  56. private $connection;
  57. /**
  58. * OutputWriter instance for writing output during migrations
  59. *
  60. * @var OutputWriter
  61. */
  62. private $outputWriter;
  63. /**
  64. * The migration table name to track versions in
  65. *
  66. * @var string
  67. */
  68. private $migrationsTableName = 'doctrine_migration_versions';
  69. /**
  70. * The path to a directory where new migration classes will be written
  71. *
  72. * @var string
  73. */
  74. private $migrationsDirectory;
  75. /**
  76. * Namespace the migration classes live in
  77. *
  78. * @var string
  79. */
  80. private $migrationsNamespace;
  81. /**
  82. * Array of the registered migrations
  83. *
  84. * @var array
  85. */
  86. private $migrations = array();
  87. /**
  88. * Construct a migration configuration object.
  89. *
  90. * @param Connection $connection A Connection instance
  91. * @param OutputWriter $outputWriter A OutputWriter instance
  92. */
  93. public function __construct(Connection $connection, OutputWriter $outputWriter = null)
  94. {
  95. $this->connection = $connection;
  96. if ($outputWriter === null) {
  97. $outputWriter = new OutputWriter();
  98. }
  99. $this->outputWriter = $outputWriter;
  100. }
  101. /**
  102. * Validation that this instance has all the required properties configured
  103. *
  104. * @return void
  105. * @throws MigrationException
  106. */
  107. public function validate()
  108. {
  109. if ( ! $this->migrationsNamespace) {
  110. throw MigrationException::migrationsNamespaceRequired();
  111. }
  112. if ( ! $this->migrationsDirectory) {
  113. throw MigrationException::migrationsDirectoryRequired();
  114. }
  115. }
  116. /**
  117. * Set the name of this set of migrations
  118. *
  119. * @param string $name The name of this set of migrations
  120. */
  121. public function setName($name)
  122. {
  123. $this->name = $name;
  124. }
  125. /**
  126. * Returns the name of this set of migrations
  127. *
  128. * @return string $name The name of this set of migrations
  129. */
  130. public function getName()
  131. {
  132. return $this->name;
  133. }
  134. /**
  135. * Returns the OutputWriter instance
  136. *
  137. * @return OutputWriter $outputWriter The OutputWriter instance
  138. */
  139. public function getOutputWriter()
  140. {
  141. return $this->outputWriter;
  142. }
  143. /**
  144. * Returns a timestamp version as a formatted date
  145. *
  146. * @param string $version
  147. * @return string $formattedVersion The formatted version
  148. */
  149. public function formatVersion($version)
  150. {
  151. return sprintf('%s-%s-%s %s:%s:%s',
  152. substr($version, 0, 4),
  153. substr($version, 4, 2),
  154. substr($version, 6, 2),
  155. substr($version, 8, 2),
  156. substr($version, 10, 2),
  157. substr($version, 12, 2)
  158. );
  159. }
  160. /**
  161. * Returns the Connection instance
  162. *
  163. * @return Connection $connection The Connection instance
  164. */
  165. public function getConnection()
  166. {
  167. return $this->connection;
  168. }
  169. /**
  170. * Set the migration table name
  171. *
  172. * @param string $tableName The migration table name
  173. */
  174. public function setMigrationsTableName($tableName)
  175. {
  176. $this->migrationsTableName = $tableName;
  177. }
  178. /**
  179. * Returns the migration table name
  180. *
  181. * @return string $migrationsTableName The migration table name
  182. */
  183. public function getMigrationsTableName()
  184. {
  185. return $this->migrationsTableName;
  186. }
  187. /**
  188. * Set the new migrations directory where new migration classes are generated
  189. *
  190. * @param string $migrationsDirectory The new migrations directory
  191. */
  192. public function setMigrationsDirectory($migrationsDirectory)
  193. {
  194. $this->migrationsDirectory = $migrationsDirectory;
  195. }
  196. /**
  197. * Returns the new migrations directory where new migration classes are generated
  198. *
  199. * @return string $migrationsDirectory The new migrations directory
  200. */
  201. public function getMigrationsDirectory()
  202. {
  203. return $this->migrationsDirectory;
  204. }
  205. /**
  206. * Set the migrations namespace
  207. *
  208. * @param string $migrationsNamespace The migrations namespace
  209. */
  210. public function setMigrationsNamespace($migrationsNamespace)
  211. {
  212. $this->migrationsNamespace = $migrationsNamespace;
  213. }
  214. /**
  215. * Returns the migrations namespace
  216. *
  217. * @return string $migrationsNamespace The migrations namespace
  218. */
  219. public function getMigrationsNamespace()
  220. {
  221. return $this->migrationsNamespace;
  222. }
  223. /**
  224. * Register migrations from a given directory. Recursively finds all files
  225. * with the pattern VersionYYYYMMDDHHMMSS.php as the filename and registers
  226. * them as migrations.
  227. *
  228. * @param string $path The root directory to where some migration classes live.
  229. * @return $migrations The array of migrations registered.
  230. */
  231. public function registerMigrationsFromDirectory($path)
  232. {
  233. $path = realpath($path);
  234. $path = rtrim($path, '/');
  235. $files = glob($path . '/Version*.php');
  236. $versions = array();
  237. foreach ($files as $file) {
  238. require_once($file);
  239. $info = pathinfo($file);
  240. $version = substr($info['filename'], 7);
  241. $class = $this->migrationsNamespace . '\\' . $info['filename'];
  242. $versions[] = $this->registerMigration($version, $class);
  243. }
  244. return $versions;
  245. }
  246. /**
  247. * Register a single migration version to be executed by a AbstractMigration
  248. * class.
  249. *
  250. * @param string $version The version of the migration in the format YYYYMMDDHHMMSS.
  251. * @param string $class The migration class to execute for the version.
  252. */
  253. public function registerMigration($version, $class)
  254. {
  255. $version = (string) $version;
  256. $class = (string) $class;
  257. if (isset($this->migrations[$version])) {
  258. throw MigrationException::duplicateMigrationVersion($version, get_class($this->migrations[$version]));
  259. }
  260. $version = new Version($this, $version, $class);
  261. $this->migrations[$version->getVersion()] = $version;
  262. ksort($this->migrations);
  263. return $version;
  264. }
  265. /**
  266. * Register an array of migrations. Each key of the array is the version and
  267. * the value is the migration class name.
  268. *
  269. *
  270. * @param array $migrations
  271. * @return void
  272. */
  273. public function registerMigrations(array $migrations)
  274. {
  275. $versions = array();
  276. foreach ($migrations as $version => $class) {
  277. $versions[] = $this->registerMigration($version, $class);
  278. }
  279. return $versions;
  280. }
  281. /**
  282. * Get the array of registered migration versions.
  283. *
  284. * @return array $migrations
  285. */
  286. public function getMigrations()
  287. {
  288. return $this->migrations;
  289. }
  290. /**
  291. * Returns the Version instance for a given version in the format YYYYMMDDHHMMSS.
  292. *
  293. * @param string $version The version string in the format YYYYMMDDHHMMSS.
  294. * @return Version $version
  295. * @throws MigrationException $exception Throws exception if migration version does not exist.
  296. */
  297. public function getVersion($version)
  298. {
  299. if ( ! isset($this->migrations[$version])) {
  300. throw MigrationException::unknownMigrationVersion($version);
  301. }
  302. return $this->migrations[$version];
  303. }
  304. /**
  305. * Check if a version exists.
  306. *
  307. * @param string $version
  308. * @return bool $exists
  309. */
  310. public function hasVersion($version)
  311. {
  312. return isset($this->migrations[$version]) ? true : false;
  313. }
  314. /**
  315. * Check if a version has been migrated or not yet
  316. *
  317. * @param Version $version
  318. * @return bool $migrated
  319. */
  320. public function hasVersionMigrated(Version $version)
  321. {
  322. $this->createMigrationTable();
  323. $version = $this->connection->fetchColumn("SELECT version FROM " . $this->migrationsTableName . " WHERE version = ?", array($version->getVersion()));
  324. return $version !== false ? true : false;
  325. }
  326. /**
  327. * Returns all migrated versions from the versions table, in an array.
  328. *
  329. * @return array $migrated
  330. */
  331. public function getMigratedVersions()
  332. {
  333. $this->createMigrationTable();
  334. $ret = $this->connection->fetchAll("SELECT version FROM " . $this->migrationsTableName);
  335. $versions = array();
  336. foreach ($ret as $version) {
  337. $versions[] = current($version);
  338. }
  339. return $versions;
  340. }
  341. /**
  342. * Returns the current migrated version from the versions table.
  343. *
  344. * @return bool $currentVersion
  345. */
  346. public function getCurrentVersion()
  347. {
  348. $this->createMigrationTable();
  349. $result = $this->connection->fetchColumn("SELECT version FROM " . $this->migrationsTableName . " ORDER BY version DESC LIMIT 1");
  350. return $result !== false ? (string) $result : '0';
  351. }
  352. /**
  353. * Returns the total number of executed migration versions
  354. *
  355. * @return integer $count
  356. */
  357. public function getNumberOfExecutedMigrations()
  358. {
  359. $this->createMigrationTable();
  360. $result = $this->connection->fetchColumn("SELECT COUNT(version) FROM " . $this->migrationsTableName);
  361. return $result !== false ? $result : 0;
  362. }
  363. /**
  364. * Returns the total number of available migration versions
  365. *
  366. * @return integer $count
  367. */
  368. public function getNumberOfAvailableMigrations()
  369. {
  370. return count($this->migrations);
  371. }
  372. /**
  373. * Returns the latest available migration version.
  374. *
  375. * @return string $version The version string in the format YYYYMMDDHHMMSS.
  376. */
  377. public function getLatestVersion()
  378. {
  379. $versions = array_keys($this->migrations);
  380. $latest = end($versions);
  381. return $latest !== false ? (string) $latest : '0';
  382. }
  383. /**
  384. * Create the migration table to track migrations with.
  385. *
  386. * @return bool $created Whether or not the table was created.
  387. */
  388. public function createMigrationTable()
  389. {
  390. $this->validate();
  391. if ($this->migrationTableCreated) {
  392. return false;
  393. }
  394. $schema = $this->connection->getSchemaManager()->createSchema();
  395. if ( ! $schema->hasTable($this->migrationsTableName)) {
  396. $columns = array(
  397. 'version' => new Column('version', Type::getType('string'), array('length' => 14)),
  398. );
  399. $table = new Table($this->migrationsTableName, $columns);
  400. $table->setPrimaryKey(array('version'));
  401. $this->connection->getSchemaManager()->createTable($table);
  402. $this->migrationTableCreated = true;
  403. return true;
  404. }
  405. return false;
  406. }
  407. /**
  408. * Returns the array of migrations to executed based on the given direction
  409. * and target version number.
  410. *
  411. * @param string $direction The direction we are migrating.
  412. * @param string $to The version to migrate to.
  413. * @return array $migrations The array of migrations we can execute.
  414. */
  415. public function getMigrationsToExecute($direction, $to)
  416. {
  417. if ($direction === 'down') {
  418. $allVersions = array_reverse(array_keys($this->migrations));
  419. $classes = array_reverse(array_values($this->migrations));
  420. $allVersions = array_combine($allVersions, $classes);
  421. } else {
  422. $allVersions = $this->migrations;
  423. }
  424. $versions = array();
  425. $migrated = $this->getMigratedVersions();
  426. foreach ($allVersions as $version) {
  427. if ($this->shouldExecuteMigration($direction, $version, $to, $migrated)) {
  428. $versions[$version->getVersion()] = $version;
  429. }
  430. }
  431. return $versions;
  432. }
  433. /**
  434. * Check if we should execute a migration for a given direction and target
  435. * migration version.
  436. *
  437. * @param string $direction The direction we are migrating.
  438. * @param Version $version The Version instance to check.
  439. * @param string $to The version we are migrating to.
  440. * @param array $migrated Migrated versions array.
  441. * @return void
  442. */
  443. private function shouldExecuteMigration($direction, Version $version, $to, $migrated)
  444. {
  445. if ($direction === 'down') {
  446. if ( ! in_array($version->getVersion(), $migrated)) {
  447. return false;
  448. }
  449. return $version->getVersion() > $to ? true : false;
  450. } else if ($direction === 'up') {
  451. if (in_array($version->getVersion(), $migrated)) {
  452. return false;
  453. }
  454. return $version->getVersion() <= $to ? true : false;
  455. }
  456. }
  457. }