blob: 06f2f562c43aea6945ea3061996c337810c87332 [file] [log] [blame]
Andrey Andreevc5536aa2012-11-01 17:33:58 +02001<?php
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 */
Andrey Andreevc5536aa2012-11-01 17:33:58 +020027defined('BASEPATH') OR exit('No direct script access allowed');
Phil Sturgeon9758d842011-02-07 20:39:00 +000028
Phil Sturgeon9758d842011-02-07 20:39:00 +000029/**
30 * Migration Class
31 *
32 * All migrations should implement this, forces up() and down() and gives
33 * access to the CI super-global.
34 *
35 * @package CodeIgniter
36 * @subpackage Libraries
37 * @category Libraries
38 * @author Reactor Engineers
39 * @link
40 */
41class CI_Migration {
42
Timothy Warren86611db2012-04-27 10:06:25 -040043 /**
44 * Whether the library is enabled
45 *
46 * @var bool
47 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010048 protected $_migration_enabled = FALSE;
Andrey Andreev56454792012-05-17 14:32:19 +030049
Timothy Warren86611db2012-04-27 10:06:25 -040050 /**
51 * Path to migration classes
52 *
53 * @var string
54 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010055 protected $_migration_path = NULL;
Andrey Andreev56454792012-05-17 14:32:19 +030056
Timothy Warren86611db2012-04-27 10:06:25 -040057 /**
58 * Current migration version
59 *
60 * @var mixed
61 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010062 protected $_migration_version = 0;
Andrey Andreev56454792012-05-17 14:32:19 +030063
Timothy Warren86611db2012-04-27 10:06:25 -040064 /**
65 * Database table with migration info
66 *
67 * @var string
68 */
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070069 protected $_migration_table = 'migrations';
Andrey Andreev56454792012-05-17 14:32:19 +030070
Timothy Warren86611db2012-04-27 10:06:25 -040071 /**
72 * Whether to automatically run migrations
73 *
74 * @var bool
75 */
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -070076 protected $_migration_auto_latest = FALSE;
Eric Barnesdd81c432011-11-16 11:07:35 -050077
Timothy Warren86611db2012-04-27 10:06:25 -040078 /**
79 * Error message
80 *
81 * @var string
82 */
Phil Sturgeoncb06c652011-05-04 10:50:25 +010083 protected $_error_string = '';
Phil Sturgeon9758d842011-02-07 20:39:00 +000084
Timothy Warren86611db2012-04-27 10:06:25 -040085 /**
86 * Initialize Migration Class
87 *
88 * @param array
Andrey Andreev56454792012-05-17 14:32:19 +030089 * @return void
Timothy Warren86611db2012-04-27 10:06:25 -040090 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010091 public function __construct($config = array())
Phil Sturgeon9758d842011-02-07 20:39:00 +000092 {
93 # Only run this constructor on main library load
94 if (get_parent_class($this) !== FALSE)
95 {
96 return;
97 }
98
99 foreach ($config as $key => $val)
100 {
Andrey Andreev56454792012-05-17 14:32:19 +0300101 $this->{'_'.$key} = $val;
Phil Sturgeon9758d842011-02-07 20:39:00 +0000102 }
103
104 log_message('debug', 'Migrations class initialized');
105
106 // Are they trying to use migrations while it is disabled?
107 if ($this->_migration_enabled !== TRUE)
108 {
109 show_error('Migrations has been loaded but is disabled or set up incorrectly.');
110 }
111
112 // If not set, set it
Alex Bilbied261b1e2012-06-02 11:12:16 +0100113 $this->_migration_path !== '' OR $this->_migration_path = APPPATH.'migrations/';
Phil Sturgeon9758d842011-02-07 20:39:00 +0000114
115 // Add trailing slash if not set
116 $this->_migration_path = rtrim($this->_migration_path, '/').'/';
117
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100118 // Load migration language
119 $this->lang->load('migration');
120
Phil Sturgeon9758d842011-02-07 20:39:00 +0000121 // They'll probably be using dbforge
122 $this->load->dbforge();
123
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700124 // Make sure the migration table name was set.
Cloudmanic Labs, LLC63b61e32011-09-19 09:35:05 -0700125 if (empty($this->_migration_table))
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700126 {
Eric Barnesdd81c432011-11-16 11:07:35 -0500127 show_error('Migrations configuration file (migration.php) must have "migration_table" set.');
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700128 }
129
Phil Sturgeon9758d842011-02-07 20:39:00 +0000130 // If the migrations table is missing, make it
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700131 if ( ! $this->db->table_exists($this->_migration_table))
Phil Sturgeon9758d842011-02-07 20:39:00 +0000132 {
133 $this->dbforge->add_field(array(
134 'version' => array('type' => 'INT', 'constraint' => 3),
135 ));
136
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700137 $this->dbforge->create_table($this->_migration_table, TRUE);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000138
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700139 $this->db->insert($this->_migration_table, array('version' => 0));
Phil Sturgeon9758d842011-02-07 20:39:00 +0000140 }
Eric Barnesdd81c432011-11-16 11:07:35 -0500141
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -0700142 // Do we auto migrate to the latest migration?
Andrey Andreev9b1db792012-03-26 19:45:51 +0300143 if ($this->_migration_auto_latest === TRUE && ! $this->latest())
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -0700144 {
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200145 show_error($this->error_string());
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -0700146 }
Phil Sturgeon9758d842011-02-07 20:39:00 +0000147 }
148
149 // --------------------------------------------------------------------
150
151 /**
152 * Migrate to a schema version
153 *
154 * Calls each migration step required to get to the schema version of
155 * choice
156 *
Andrey Andreev9b1db792012-03-26 19:45:51 +0300157 * @param int Target schema version
Phil Sturgeon9758d842011-02-07 20:39:00 +0000158 * @return mixed TRUE if already latest, FALSE if failed, int if upgraded
159 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100160 public function version($target_version)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000161 {
162 $start = $current_version = $this->_get_version();
163 $stop = $target_version;
164
165 if ($target_version > $current_version)
166 {
167 // Moving Up
168 ++$start;
169 ++$stop;
170 $step = 1;
171 }
Phil Sturgeon9758d842011-02-07 20:39:00 +0000172 else
173 {
174 // Moving Down
175 $step = -1;
176 }
Eric Barnesdd81c432011-11-16 11:07:35 -0500177
Phil Sturgeon9758d842011-02-07 20:39:00 +0000178 $method = $step === 1 ? 'up' : 'down';
179 $migrations = array();
180
181 // We now prepare to actually DO the migrations
182 // But first let's make sure that everything is the way it should be
Phil Sturgeonaf6d8502012-06-19 15:30:47 -0500183 for ($i = $start; $i != $stop; $i += $step)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000184 {
Eric Barnesdd81c432011-11-16 11:07:35 -0500185 $f = glob(sprintf($this->_migration_path.'%03d_*.php', $i));
Phil Sturgeon9758d842011-02-07 20:39:00 +0000186
187 // Only one migration per step is permitted
188 if (count($f) > 1)
189 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100190 $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $i);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000191 return FALSE;
192 }
193
194 // Migration step not found
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200195 if (count($f) === 0)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000196 {
197 // If trying to migrate up to a version greater than the last
198 // existing one, migrate to the last one.
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200199 if ($step === 1)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000200 {
201 break;
202 }
203
204 // If trying to migrate down but we're missing a step,
205 // something must definitely be wrong.
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100206 $this->_error_string = sprintf($this->lang->line('migration_not_found'), $i);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000207 return FALSE;
208 }
209
210 $file = basename($f[0]);
211 $name = basename($f[0], '.php');
212
213 // Filename validations
214 if (preg_match('/^\d{3}_(\w+)$/', $name, $match))
215 {
216 $match[1] = strtolower($match[1]);
217
218 // Cannot repeat a migration at different steps
219 if (in_array($match[1], $migrations))
220 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100221 $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $match[1]);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000222 return FALSE;
223 }
224
225 include $f[0];
Eric Barnesdd81c432011-11-16 11:07:35 -0500226 $class = 'Migration_'.ucfirst($match[1]);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000227
228 if ( ! class_exists($class))
229 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100230 $this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000231 return FALSE;
232 }
233
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100234 if ( ! is_callable(array($class, $method)))
Phil Sturgeon9758d842011-02-07 20:39:00 +0000235 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100236 $this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000237 return FALSE;
238 }
239
240 $migrations[] = $match[1];
241 }
242 else
243 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100244 $this->_error_string = sprintf($this->lang->line('migration_invalid_filename'), $file);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000245 return FALSE;
246 }
247 }
248
Eric Barnesdd81c432011-11-16 11:07:35 -0500249 log_message('debug', 'Current migration: '.$current_version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000250
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200251 $version = $i + ($step === 1 ? -1 : 0);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000252
253 // If there is nothing to do so quit
254 if ($migrations === array())
255 {
256 return TRUE;
257 }
258
Eric Barnesdd81c432011-11-16 11:07:35 -0500259 log_message('debug', 'Migrating from '.$method.' to version '.$version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000260
261 // Loop through the migrations
262 foreach ($migrations AS $migration)
263 {
264 // Run the migration class
Eric Barnesdd81c432011-11-16 11:07:35 -0500265 $class = 'Migration_'.ucfirst(strtolower($migration));
Phil Sturgeon9758d842011-02-07 20:39:00 +0000266 call_user_func(array(new $class, $method));
267
268 $current_version += $step;
269 $this->_update_version($current_version);
270 }
271
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100272 log_message('debug', 'Finished migrating to '.$current_version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000273
274 return $current_version;
275 }
276
277 // --------------------------------------------------------------------
278
279 /**
280 * Set's the schema to the latest migration
281 *
Phil Sturgeon9758d842011-02-07 20:39:00 +0000282 * @return mixed true if already latest, false if failed, int if upgraded
283 */
284 public function latest()
285 {
286 if ( ! $migrations = $this->find_migrations())
287 {
Cloudmanic Labs, LLC3d036e32011-11-11 22:46:21 -0800288 $this->_error_string = $this->lang->line('migration_none_found');
Alex Bilbieafee2262012-07-15 18:59:01 +0100289 return FALSE;
Phil Sturgeon9758d842011-02-07 20:39:00 +0000290 }
291
292 $last_migration = basename(end($migrations));
Eric Barnesdd81c432011-11-16 11:07:35 -0500293
Phil Sturgeon9758d842011-02-07 20:39:00 +0000294 // Calculate the last migration step from existing migration
295 // filenames and procceed to the standard version migration
Dumk0dc128cd2012-07-05 03:20:09 +0300296 return $this->version((int) $last_migration);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000297 }
298
299 // --------------------------------------------------------------------
300
301 /**
302 * Set's the schema to the migration version set in config
303 *
Phil Sturgeon9758d842011-02-07 20:39:00 +0000304 * @return mixed true if already current, false if failed, int if upgraded
305 */
306 public function current()
307 {
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100308 return $this->version($this->_migration_version);
309 }
310
311 // --------------------------------------------------------------------
312
313 /**
314 * Error string
315 *
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100316 * @return string Error message returned as a string
317 */
318 public function error_string()
319 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100320 return $this->_error_string;
Phil Sturgeon9758d842011-02-07 20:39:00 +0000321 }
322
323 // --------------------------------------------------------------------
324
325 /**
Dumk05db48272012-07-05 02:58:10 +0300326 * Retrieves list of available migration scripts
Phil Sturgeon9758d842011-02-07 20:39:00 +0000327 *
Dumk05db48272012-07-05 02:58:10 +0300328 * @return array list of migration file paths sorted by version
Phil Sturgeon9758d842011-02-07 20:39:00 +0000329 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100330 protected function find_migrations()
Phil Sturgeon9758d842011-02-07 20:39:00 +0000331 {
332 // Load all *_*.php files in the migrations path
Eric Barnesdd81c432011-11-16 11:07:35 -0500333 $files = glob($this->_migration_path.'*_*.php');
Eric Barnesdd81c432011-11-16 11:07:35 -0500334
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200335 for ($i = 0, $c = count($files); $i < $c; $i++)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000336 {
337 // Mark wrongly formatted files as false for later filtering
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200338 if ( ! preg_match('/^\d{3}_(\w+)$/', basename($files[$i], '.php')))
Phil Sturgeon9758d842011-02-07 20:39:00 +0000339 {
340 $files[$i] = FALSE;
341 }
342 }
Eric Barnesdd81c432011-11-16 11:07:35 -0500343
Phil Sturgeon9758d842011-02-07 20:39:00 +0000344 sort($files);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000345 return $files;
346 }
347
348 // --------------------------------------------------------------------
349
350 /**
351 * Retrieves current schema version
352 *
Andrey Andreev9b1db792012-03-26 19:45:51 +0300353 * @return int Current Migration
Phil Sturgeon9758d842011-02-07 20:39:00 +0000354 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100355 protected function _get_version()
Phil Sturgeon9758d842011-02-07 20:39:00 +0000356 {
Phil Sturgeond268eda2011-12-31 16:20:11 +0000357 $row = $this->db->select('version')->get($this->_migration_table)->row();
Phil Sturgeon9758d842011-02-07 20:39:00 +0000358 return $row ? $row->version : 0;
359 }
360
361 // --------------------------------------------------------------------
362
363 /**
364 * Stores the current schema version
365 *
Andrey Andreev9b1db792012-03-26 19:45:51 +0300366 * @param int Migration reached
367 * @return void Outputs a report of the migration
Phil Sturgeon9758d842011-02-07 20:39:00 +0000368 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100369 protected function _update_version($migrations)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000370 {
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700371 return $this->db->update($this->_migration_table, array(
Phil Sturgeon9758d842011-02-07 20:39:00 +0000372 'version' => $migrations
373 ));
374 }
375
376 // --------------------------------------------------------------------
377
378 /**
Phil Sturgeon9758d842011-02-07 20:39:00 +0000379 * Enable the use of CI super-global
380 *
Andrey Andreev9b1db792012-03-26 19:45:51 +0300381 * @param $var
Phil Sturgeon9758d842011-02-07 20:39:00 +0000382 * @return mixed
383 */
384 public function __get($var)
385 {
386 return get_instance()->$var;
387 }
Andrey Andreev56454792012-05-17 14:32:19 +0300388
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100389}
390
391/* End of file Migration.php */
Andrey Andreev9b1db792012-03-26 19:45:51 +0300392/* Location: ./system/libraries/Migration.php */