blob: 2a06aa0111276c83b72b4e50f4c2301a6b1e10b4 [file] [log] [blame]
Andrey Andreevdd4702f2011-12-24 19:33:44 +02001<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
Phil Sturgeon9758d842011-02-07 20:39:00 +00002/**
3 * CodeIgniter
4 *
Phil Sturgeon07c1ac82012-03-09 17:03:37 +00005 * An open source application development framework for PHP 5.2.4 or newer
Phil Sturgeon9758d842011-02-07 20:39:00 +00006 *
Derek Jonesf4a4bd82011-10-20 12:18:42 -05007 * NOTICE OF LICENSE
Eric Barnesdd81c432011-11-16 11:07:35 -05008 *
Derek Jonesf4a4bd82011-10-20 12:18:42 -05009 * Licensed under the Open Software License version 3.0
Eric Barnesdd81c432011-11-16 11:07:35 -050010 *
Derek Jonesf4a4bd82011-10-20 12:18:42 -050011 * This source file is subject to the Open Software License (OSL 3.0) that is
12 * bundled with this package in the files license.txt / license.rst. It is
13 * also available through the world wide web at this URL:
14 * http://opensource.org/licenses/OSL-3.0
15 * If you did not receive a copy of the license and are unable to obtain it
16 * through the world wide web, please send an email to
17 * licensing@ellislab.com so we can send you a copy immediately.
18 *
Phil Sturgeon9758d842011-02-07 20:39:00 +000019 * @package CodeIgniter
20 * @author EllisLab Dev Team
Greg Aker0defe5d2012-01-01 18:46:41 -060021 * @copyright Copyright (c) 2006 - 2012, EllisLab, Inc. (http://ellislab.com/)
Derek Jonesf4a4bd82011-10-20 12:18:42 -050022 * @license http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
Phil Sturgeon9758d842011-02-07 20:39:00 +000023 * @link http://codeigniter.com
Derek Jonesf4a4bd82011-10-20 12:18:42 -050024 * @since Version 3.0
Phil Sturgeon9758d842011-02-07 20:39:00 +000025 * @filesource
26 */
27
Phil Sturgeon9758d842011-02-07 20:39:00 +000028/**
29 * Migration Class
30 *
31 * All migrations should implement this, forces up() and down() and gives
32 * access to the CI super-global.
33 *
34 * @package CodeIgniter
35 * @subpackage Libraries
36 * @category Libraries
37 * @author Reactor Engineers
38 * @link
39 */
40class CI_Migration {
41
Timothy Warren86611db2012-04-27 10:06:25 -040042 /**
43 * Whether the library is enabled
44 *
45 * @var bool
46 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010047 protected $_migration_enabled = FALSE;
Jonathon Hill34c8b9c2012-10-31 14:02:35 -040048
49 /**
50 * Migration numbering style
51 *
52 * @var bool
53 */
54 protected $_migration_style = 'sequential';
Andrey Andreev56454792012-05-17 14:32:19 +030055
Timothy Warren86611db2012-04-27 10:06:25 -040056 /**
57 * Path to migration classes
58 *
59 * @var string
60 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010061 protected $_migration_path = NULL;
Andrey Andreev56454792012-05-17 14:32:19 +030062
Timothy Warren86611db2012-04-27 10:06:25 -040063 /**
64 * Current migration version
65 *
66 * @var mixed
67 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010068 protected $_migration_version = 0;
Andrey Andreev56454792012-05-17 14:32:19 +030069
Timothy Warren86611db2012-04-27 10:06:25 -040070 /**
71 * Database table with migration info
72 *
73 * @var string
74 */
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070075 protected $_migration_table = 'migrations';
Andrey Andreev56454792012-05-17 14:32:19 +030076
Timothy Warren86611db2012-04-27 10:06:25 -040077 /**
78 * Whether to automatically run migrations
79 *
80 * @var bool
81 */
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -070082 protected $_migration_auto_latest = FALSE;
Jonathon Hill34c8b9c2012-10-31 14:02:35 -040083
84 /**
85 * Migration basename regex
86 *
87 * @var bool
88 */
89 protected $_migration_regex = NULL;
Eric Barnesdd81c432011-11-16 11:07:35 -050090
Timothy Warren86611db2012-04-27 10:06:25 -040091 /**
92 * Error message
93 *
94 * @var string
95 */
Phil Sturgeoncb06c652011-05-04 10:50:25 +010096 protected $_error_string = '';
Phil Sturgeon9758d842011-02-07 20:39:00 +000097
Timothy Warren86611db2012-04-27 10:06:25 -040098 /**
99 * Initialize Migration Class
100 *
101 * @param array
Andrey Andreev56454792012-05-17 14:32:19 +0300102 * @return void
Timothy Warren86611db2012-04-27 10:06:25 -0400103 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100104 public function __construct($config = array())
Phil Sturgeon9758d842011-02-07 20:39:00 +0000105 {
106 # Only run this constructor on main library load
107 if (get_parent_class($this) !== FALSE)
108 {
109 return;
110 }
111
112 foreach ($config as $key => $val)
113 {
Andrey Andreev56454792012-05-17 14:32:19 +0300114 $this->{'_'.$key} = $val;
Phil Sturgeon9758d842011-02-07 20:39:00 +0000115 }
116
117 log_message('debug', 'Migrations class initialized');
118
119 // Are they trying to use migrations while it is disabled?
120 if ($this->_migration_enabled !== TRUE)
121 {
122 show_error('Migrations has been loaded but is disabled or set up incorrectly.');
123 }
124
125 // If not set, set it
Alex Bilbied261b1e2012-06-02 11:12:16 +0100126 $this->_migration_path !== '' OR $this->_migration_path = APPPATH.'migrations/';
Phil Sturgeon9758d842011-02-07 20:39:00 +0000127
128 // Add trailing slash if not set
129 $this->_migration_path = rtrim($this->_migration_path, '/').'/';
130
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100131 // Load migration language
132 $this->lang->load('migration');
133
Phil Sturgeon9758d842011-02-07 20:39:00 +0000134 // They'll probably be using dbforge
135 $this->load->dbforge();
136
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700137 // Make sure the migration table name was set.
Cloudmanic Labs, LLC63b61e32011-09-19 09:35:05 -0700138 if (empty($this->_migration_table))
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700139 {
Eric Barnesdd81c432011-11-16 11:07:35 -0500140 show_error('Migrations configuration file (migration.php) must have "migration_table" set.');
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700141 }
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400142
143 // Migration basename regex
144 $this->_migration_regex = $this->_migration_style === 'timestamp' ? '/^\d{14}_(\w+)$/' : '/^\d{3}_(\w+)$/';
145
146 // Make sure a valid migration numbering style was set.
147 if ( ! in_array($this->_migration_style, array('sequential', 'timestamp')))
148 {
149 show_error('An invalid migration numbering style was specified: '.$this->_migration_style);
150 }
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700151
Phil Sturgeon9758d842011-02-07 20:39:00 +0000152 // If the migrations table is missing, make it
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700153 if ( ! $this->db->table_exists($this->_migration_table))
Phil Sturgeon9758d842011-02-07 20:39:00 +0000154 {
155 $this->dbforge->add_field(array(
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400156 'version' => array('type' => 'BIGINT', 'constraint' => 3),
Phil Sturgeon9758d842011-02-07 20:39:00 +0000157 ));
158
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700159 $this->dbforge->create_table($this->_migration_table, TRUE);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000160
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700161 $this->db->insert($this->_migration_table, array('version' => 0));
Phil Sturgeon9758d842011-02-07 20:39:00 +0000162 }
Eric Barnesdd81c432011-11-16 11:07:35 -0500163
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -0700164 // Do we auto migrate to the latest migration?
Andrey Andreev9b1db792012-03-26 19:45:51 +0300165 if ($this->_migration_auto_latest === TRUE && ! $this->latest())
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -0700166 {
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200167 show_error($this->error_string());
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -0700168 }
Phil Sturgeon9758d842011-02-07 20:39:00 +0000169 }
170
171 // --------------------------------------------------------------------
172
173 /**
174 * Migrate to a schema version
175 *
176 * Calls each migration step required to get to the schema version of
177 * choice
178 *
Andrey Andreev9b1db792012-03-26 19:45:51 +0300179 * @param int Target schema version
Phil Sturgeon9758d842011-02-07 20:39:00 +0000180 * @return mixed TRUE if already latest, FALSE if failed, int if upgraded
181 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100182 public function version($target_version)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000183 {
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400184 $current_version = (int) $this->_get_version();
185 $target_version = (int) $target_version;
186
187 $migrations = $this->find_migrations();
188
189 if ($target_version > 0 AND ! isset($migrations[$target_version]))
190 {
191 $this->_error_string = sprintf($this->lang->line('migration_not_found'), $target_version);
192 return FALSE;
193 }
194
Phil Sturgeon9758d842011-02-07 20:39:00 +0000195 if ($target_version > $current_version)
196 {
197 // Moving Up
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400198 $method = 'up';
Phil Sturgeon9758d842011-02-07 20:39:00 +0000199 }
Phil Sturgeon9758d842011-02-07 20:39:00 +0000200 else
201 {
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400202 // Moving Down, apply in reverse order
203 $method = 'down';
204 krsort($migrations);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000205 }
Eric Barnesdd81c432011-11-16 11:07:35 -0500206
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400207 if (empty($migrations))
Phil Sturgeon9758d842011-02-07 20:39:00 +0000208 {
209 return TRUE;
210 }
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400211
212 $previous = FALSE;
Phil Sturgeon9758d842011-02-07 20:39:00 +0000213
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400214 // Validate all available migrations, and run the ones within our target range
215 foreach ($migrations as $number => $file)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000216 {
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400217 // Check for sequence gaps
218 if ($this->_migration_style === 'sequential' AND $previous !== FALSE AND abs($number - $previous) > 1)
219 {
220 $this->_error_string = sprintf($this->lang->line('migration_sequence_gap'), $number);
221 return FALSE;
222 }
223
224 include $file;
225 $class = 'Migration_'.ucfirst(strtolower($this->_get_migration_name(basename($file, '.php'))));
Phil Sturgeon9758d842011-02-07 20:39:00 +0000226
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400227 // Validate the migration file structure
228 if ( ! class_exists($class))
229 {
230 $this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class);
231 return FALSE;
232 }
233 elseif ( ! is_callable(array($class, $method)))
234 {
235 $this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class);
236 return FALSE;
237 }
238
239 $previous = $number;
240
241 // Run migrations that are inside the target range
242 if (
243 ($method === 'up' AND $number > $current_version AND $number <= $target_version) OR
244 ($method === 'down' AND $number <= $current_version AND $number > $target_version)
245 ) {
246 log_message('debug', 'Migrating '.$method.' from version '.$current_version.' to version '.$number);
247 call_user_func(array(new $class, $method));
248 $current_version = $number;
249 $this->_update_version($current_version);
250 }
251 }
252
253 // This is necessary when moving down, since the the last migration applied
254 // will be the down() method for the next migration up from the target
255 if ($current_version <> $target_version)
256 {
257 $current_version = $target_version;
Phil Sturgeon9758d842011-02-07 20:39:00 +0000258 $this->_update_version($current_version);
259 }
260
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100261 log_message('debug', 'Finished migrating to '.$current_version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000262
263 return $current_version;
264 }
265
266 // --------------------------------------------------------------------
267
268 /**
269 * Set's the schema to the latest migration
270 *
Phil Sturgeon9758d842011-02-07 20:39:00 +0000271 * @return mixed true if already latest, false if failed, int if upgraded
272 */
273 public function latest()
274 {
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400275 $migrations = $this->find_migrations();
276
277 if (empty($migrations))
Phil Sturgeon9758d842011-02-07 20:39:00 +0000278 {
Cloudmanic Labs, LLC3d036e32011-11-11 22:46:21 -0800279 $this->_error_string = $this->lang->line('migration_none_found');
Alex Bilbieafee2262012-07-15 18:59:01 +0100280 return FALSE;
Phil Sturgeon9758d842011-02-07 20:39:00 +0000281 }
282
283 $last_migration = basename(end($migrations));
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400284
Phil Sturgeon9758d842011-02-07 20:39:00 +0000285 // Calculate the last migration step from existing migration
286 // filenames and procceed to the standard version migration
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400287 return $this->version($this->_get_migration_number($last_migration));
Phil Sturgeon9758d842011-02-07 20:39:00 +0000288 }
289
290 // --------------------------------------------------------------------
291
292 /**
293 * Set's the schema to the migration version set in config
294 *
Phil Sturgeon9758d842011-02-07 20:39:00 +0000295 * @return mixed true if already current, false if failed, int if upgraded
296 */
297 public function current()
298 {
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100299 return $this->version($this->_migration_version);
300 }
301
302 // --------------------------------------------------------------------
303
304 /**
305 * Error string
306 *
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100307 * @return string Error message returned as a string
308 */
309 public function error_string()
310 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100311 return $this->_error_string;
Phil Sturgeon9758d842011-02-07 20:39:00 +0000312 }
313
314 // --------------------------------------------------------------------
315
316 /**
Dumk05db48272012-07-05 02:58:10 +0300317 * Retrieves list of available migration scripts
Phil Sturgeon9758d842011-02-07 20:39:00 +0000318 *
Dumk05db48272012-07-05 02:58:10 +0300319 * @return array list of migration file paths sorted by version
Phil Sturgeon9758d842011-02-07 20:39:00 +0000320 */
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400321 public function find_migrations()
Phil Sturgeon9758d842011-02-07 20:39:00 +0000322 {
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400323 $migrations = array();
324
Phil Sturgeon9758d842011-02-07 20:39:00 +0000325 // Load all *_*.php files in the migrations path
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400326 foreach (glob($this->_migration_path.'*_*.php') as $file)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000327 {
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400328 $name = basename($file, '.php');
329
330 // Filter out non-migration files
331 if (preg_match($this->_migration_regex, $name))
Phil Sturgeon9758d842011-02-07 20:39:00 +0000332 {
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400333 $number = $this->_get_migration_number($name);
334
335 // There cannot be duplicate migration numbers
336 if (isset($migrations[$number]))
337 {
338 $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $number);
339 show_error($this->_error_string);
340 }
341
342 $migrations[$number] = $file;
Phil Sturgeon9758d842011-02-07 20:39:00 +0000343 }
344 }
Eric Barnesdd81c432011-11-16 11:07:35 -0500345
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400346 ksort($migrations);
347 return $migrations;
348 }
349
350 // --------------------------------------------------------------------
351
352 /**
353 * Extracts the migration number from a filename
354 *
355 * @return int Numeric portion of a migration filename
356 */
357 protected function _get_migration_number($migration)
358 {
359 $parts = explode('_', $migration);
360 return (int) $parts[0];
361 }
362
363 // --------------------------------------------------------------------
364
365 /**
366 * Extracts the migration class name from a filename
367 *
368 * @return string text portion of a migration filename
369 */
370 protected function _get_migration_name($migration)
371 {
372 $parts = explode('_', $migration);
373 array_shift($parts);
374 return implode('_', $parts);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000375 }
376
377 // --------------------------------------------------------------------
378
379 /**
380 * Retrieves current schema version
381 *
Andrey Andreev9b1db792012-03-26 19:45:51 +0300382 * @return int Current Migration
Phil Sturgeon9758d842011-02-07 20:39:00 +0000383 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100384 protected function _get_version()
Phil Sturgeon9758d842011-02-07 20:39:00 +0000385 {
Phil Sturgeond268eda2011-12-31 16:20:11 +0000386 $row = $this->db->select('version')->get($this->_migration_table)->row();
Phil Sturgeon9758d842011-02-07 20:39:00 +0000387 return $row ? $row->version : 0;
388 }
389
390 // --------------------------------------------------------------------
391
392 /**
393 * Stores the current schema version
394 *
Andrey Andreev9b1db792012-03-26 19:45:51 +0300395 * @param int Migration reached
396 * @return void Outputs a report of the migration
Phil Sturgeon9758d842011-02-07 20:39:00 +0000397 */
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400398 protected function _update_version($migration)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000399 {
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700400 return $this->db->update($this->_migration_table, array(
Jonathon Hill34c8b9c2012-10-31 14:02:35 -0400401 'version' => $migration
Phil Sturgeon9758d842011-02-07 20:39:00 +0000402 ));
403 }
404
405 // --------------------------------------------------------------------
406
407 /**
Phil Sturgeon9758d842011-02-07 20:39:00 +0000408 * Enable the use of CI super-global
409 *
Andrey Andreev9b1db792012-03-26 19:45:51 +0300410 * @param $var
Phil Sturgeon9758d842011-02-07 20:39:00 +0000411 * @return mixed
412 */
413 public function __get($var)
414 {
415 return get_instance()->$var;
416 }
Andrey Andreev56454792012-05-17 14:32:19 +0300417
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100418}
419
420/* End of file Migration.php */
Andrey Andreev9b1db792012-03-26 19:45:51 +0300421/* Location: ./system/libraries/Migration.php */