blob: 5d637d44ab9609230cf42b3f1eea3b0d1c8aaf8d [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;
Andrey Andreev56454792012-05-17 14:32:19 +030048
Timothy Warren86611db2012-04-27 10:06:25 -040049 /**
50 * Path to migration classes
51 *
52 * @var string
53 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010054 protected $_migration_path = NULL;
Andrey Andreev56454792012-05-17 14:32:19 +030055
Timothy Warren86611db2012-04-27 10:06:25 -040056 /**
57 * Current migration version
58 *
59 * @var mixed
60 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010061 protected $_migration_version = 0;
Andrey Andreev56454792012-05-17 14:32:19 +030062
Timothy Warren86611db2012-04-27 10:06:25 -040063 /**
64 * Database table with migration info
65 *
66 * @var string
67 */
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070068 protected $_migration_table = 'migrations';
Andrey Andreev56454792012-05-17 14:32:19 +030069
Timothy Warren86611db2012-04-27 10:06:25 -040070 /**
71 * Whether to automatically run migrations
72 *
73 * @var bool
74 */
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -070075 protected $_migration_auto_latest = FALSE;
Eric Barnesdd81c432011-11-16 11:07:35 -050076
Timothy Warren86611db2012-04-27 10:06:25 -040077 /**
78 * Error message
79 *
80 * @var string
81 */
Phil Sturgeoncb06c652011-05-04 10:50:25 +010082 protected $_error_string = '';
Phil Sturgeon9758d842011-02-07 20:39:00 +000083
Timothy Warren86611db2012-04-27 10:06:25 -040084 /**
85 * Initialize Migration Class
86 *
87 * @param array
Andrey Andreev56454792012-05-17 14:32:19 +030088 * @return void
Timothy Warren86611db2012-04-27 10:06:25 -040089 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010090 public function __construct($config = array())
Phil Sturgeon9758d842011-02-07 20:39:00 +000091 {
92 # Only run this constructor on main library load
93 if (get_parent_class($this) !== FALSE)
94 {
95 return;
96 }
97
98 foreach ($config as $key => $val)
99 {
Andrey Andreev56454792012-05-17 14:32:19 +0300100 $this->{'_'.$key} = $val;
Phil Sturgeon9758d842011-02-07 20:39:00 +0000101 }
102
103 log_message('debug', 'Migrations class initialized');
104
105 // Are they trying to use migrations while it is disabled?
106 if ($this->_migration_enabled !== TRUE)
107 {
108 show_error('Migrations has been loaded but is disabled or set up incorrectly.');
109 }
110
111 // If not set, set it
Alex Bilbied261b1e2012-06-02 11:12:16 +0100112 $this->_migration_path !== '' OR $this->_migration_path = APPPATH.'migrations/';
Phil Sturgeon9758d842011-02-07 20:39:00 +0000113
114 // Add trailing slash if not set
115 $this->_migration_path = rtrim($this->_migration_path, '/').'/';
116
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100117 // Load migration language
118 $this->lang->load('migration');
119
Phil Sturgeon9758d842011-02-07 20:39:00 +0000120 // They'll probably be using dbforge
121 $this->load->dbforge();
122
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700123 // Make sure the migration table name was set.
Cloudmanic Labs, LLC63b61e32011-09-19 09:35:05 -0700124 if (empty($this->_migration_table))
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700125 {
Eric Barnesdd81c432011-11-16 11:07:35 -0500126 show_error('Migrations configuration file (migration.php) must have "migration_table" set.');
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700127 }
128
Phil Sturgeon9758d842011-02-07 20:39:00 +0000129 // If the migrations table is missing, make it
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700130 if ( ! $this->db->table_exists($this->_migration_table))
Phil Sturgeon9758d842011-02-07 20:39:00 +0000131 {
132 $this->dbforge->add_field(array(
133 'version' => array('type' => 'INT', 'constraint' => 3),
134 ));
135
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700136 $this->dbforge->create_table($this->_migration_table, TRUE);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000137
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700138 $this->db->insert($this->_migration_table, array('version' => 0));
Phil Sturgeon9758d842011-02-07 20:39:00 +0000139 }
Eric Barnesdd81c432011-11-16 11:07:35 -0500140
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -0700141 // Do we auto migrate to the latest migration?
Andrey Andreev9b1db792012-03-26 19:45:51 +0300142 if ($this->_migration_auto_latest === TRUE && ! $this->latest())
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -0700143 {
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200144 show_error($this->error_string());
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -0700145 }
Phil Sturgeon9758d842011-02-07 20:39:00 +0000146 }
147
148 // --------------------------------------------------------------------
149
150 /**
151 * Migrate to a schema version
152 *
153 * Calls each migration step required to get to the schema version of
154 * choice
155 *
Andrey Andreev9b1db792012-03-26 19:45:51 +0300156 * @param int Target schema version
Phil Sturgeon9758d842011-02-07 20:39:00 +0000157 * @return mixed TRUE if already latest, FALSE if failed, int if upgraded
158 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100159 public function version($target_version)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000160 {
161 $start = $current_version = $this->_get_version();
162 $stop = $target_version;
163
164 if ($target_version > $current_version)
165 {
166 // Moving Up
167 ++$start;
168 ++$stop;
169 $step = 1;
170 }
Phil Sturgeon9758d842011-02-07 20:39:00 +0000171 else
172 {
173 // Moving Down
174 $step = -1;
175 }
Eric Barnesdd81c432011-11-16 11:07:35 -0500176
Phil Sturgeon9758d842011-02-07 20:39:00 +0000177 $method = $step === 1 ? 'up' : 'down';
178 $migrations = array();
179
180 // We now prepare to actually DO the migrations
181 // But first let's make sure that everything is the way it should be
Phil Sturgeonaf6d8502012-06-19 15:30:47 -0500182 for ($i = $start; $i != $stop; $i += $step)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000183 {
Eric Barnesdd81c432011-11-16 11:07:35 -0500184 $f = glob(sprintf($this->_migration_path.'%03d_*.php', $i));
Phil Sturgeon9758d842011-02-07 20:39:00 +0000185
186 // Only one migration per step is permitted
187 if (count($f) > 1)
188 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100189 $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $i);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000190 return FALSE;
191 }
192
193 // Migration step not found
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200194 if (count($f) === 0)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000195 {
196 // If trying to migrate up to a version greater than the last
197 // existing one, migrate to the last one.
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200198 if ($step === 1)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000199 {
200 break;
201 }
202
203 // If trying to migrate down but we're missing a step,
204 // something must definitely be wrong.
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100205 $this->_error_string = sprintf($this->lang->line('migration_not_found'), $i);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000206 return FALSE;
207 }
208
209 $file = basename($f[0]);
210 $name = basename($f[0], '.php');
211
212 // Filename validations
213 if (preg_match('/^\d{3}_(\w+)$/', $name, $match))
214 {
215 $match[1] = strtolower($match[1]);
216
217 // Cannot repeat a migration at different steps
218 if (in_array($match[1], $migrations))
219 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100220 $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $match[1]);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000221 return FALSE;
222 }
223
224 include $f[0];
Eric Barnesdd81c432011-11-16 11:07:35 -0500225 $class = 'Migration_'.ucfirst($match[1]);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000226
227 if ( ! class_exists($class))
228 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100229 $this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000230 return FALSE;
231 }
232
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100233 if ( ! is_callable(array($class, $method)))
Phil Sturgeon9758d842011-02-07 20:39:00 +0000234 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100235 $this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000236 return FALSE;
237 }
238
239 $migrations[] = $match[1];
240 }
241 else
242 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100243 $this->_error_string = sprintf($this->lang->line('migration_invalid_filename'), $file);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000244 return FALSE;
245 }
246 }
247
Eric Barnesdd81c432011-11-16 11:07:35 -0500248 log_message('debug', 'Current migration: '.$current_version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000249
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200250 $version = $i + ($step === 1 ? -1 : 0);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000251
252 // If there is nothing to do so quit
253 if ($migrations === array())
254 {
255 return TRUE;
256 }
257
Eric Barnesdd81c432011-11-16 11:07:35 -0500258 log_message('debug', 'Migrating from '.$method.' to version '.$version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000259
260 // Loop through the migrations
261 foreach ($migrations AS $migration)
262 {
263 // Run the migration class
Eric Barnesdd81c432011-11-16 11:07:35 -0500264 $class = 'Migration_'.ucfirst(strtolower($migration));
Phil Sturgeon9758d842011-02-07 20:39:00 +0000265 call_user_func(array(new $class, $method));
266
267 $current_version += $step;
268 $this->_update_version($current_version);
269 }
270
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100271 log_message('debug', 'Finished migrating to '.$current_version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000272
273 return $current_version;
274 }
275
276 // --------------------------------------------------------------------
277
278 /**
279 * Set's the schema to the latest migration
280 *
Phil Sturgeon9758d842011-02-07 20:39:00 +0000281 * @return mixed true if already latest, false if failed, int if upgraded
282 */
283 public function latest()
284 {
285 if ( ! $migrations = $this->find_migrations())
286 {
Cloudmanic Labs, LLC3d036e32011-11-11 22:46:21 -0800287 $this->_error_string = $this->lang->line('migration_none_found');
Alex Bilbieafee2262012-07-15 18:59:01 +0100288 return FALSE;
Phil Sturgeon9758d842011-02-07 20:39:00 +0000289 }
290
291 $last_migration = basename(end($migrations));
Eric Barnesdd81c432011-11-16 11:07:35 -0500292
Phil Sturgeon9758d842011-02-07 20:39:00 +0000293 // Calculate the last migration step from existing migration
294 // filenames and procceed to the standard version migration
Dumk0dc128cd2012-07-05 03:20:09 +0300295 return $this->version((int) $last_migration);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000296 }
297
298 // --------------------------------------------------------------------
299
300 /**
301 * Set's the schema to the migration version set in config
302 *
Phil Sturgeon9758d842011-02-07 20:39:00 +0000303 * @return mixed true if already current, false if failed, int if upgraded
304 */
305 public function current()
306 {
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100307 return $this->version($this->_migration_version);
308 }
309
310 // --------------------------------------------------------------------
311
312 /**
313 * Error string
314 *
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100315 * @return string Error message returned as a string
316 */
317 public function error_string()
318 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100319 return $this->_error_string;
Phil Sturgeon9758d842011-02-07 20:39:00 +0000320 }
321
322 // --------------------------------------------------------------------
323
324 /**
Dumk05db48272012-07-05 02:58:10 +0300325 * Retrieves list of available migration scripts
Phil Sturgeon9758d842011-02-07 20:39:00 +0000326 *
Dumk05db48272012-07-05 02:58:10 +0300327 * @return array list of migration file paths sorted by version
Phil Sturgeon9758d842011-02-07 20:39:00 +0000328 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100329 protected function find_migrations()
Phil Sturgeon9758d842011-02-07 20:39:00 +0000330 {
331 // Load all *_*.php files in the migrations path
Eric Barnesdd81c432011-11-16 11:07:35 -0500332 $files = glob($this->_migration_path.'*_*.php');
Eric Barnesdd81c432011-11-16 11:07:35 -0500333
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200334 for ($i = 0, $c = count($files); $i < $c; $i++)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000335 {
336 // Mark wrongly formatted files as false for later filtering
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200337 if ( ! preg_match('/^\d{3}_(\w+)$/', basename($files[$i], '.php')))
Phil Sturgeon9758d842011-02-07 20:39:00 +0000338 {
339 $files[$i] = FALSE;
340 }
341 }
Eric Barnesdd81c432011-11-16 11:07:35 -0500342
Phil Sturgeon9758d842011-02-07 20:39:00 +0000343 sort($files);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000344 return $files;
345 }
346
347 // --------------------------------------------------------------------
348
349 /**
350 * Retrieves current schema version
351 *
Andrey Andreev9b1db792012-03-26 19:45:51 +0300352 * @return int Current Migration
Phil Sturgeon9758d842011-02-07 20:39:00 +0000353 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100354 protected function _get_version()
Phil Sturgeon9758d842011-02-07 20:39:00 +0000355 {
Phil Sturgeond268eda2011-12-31 16:20:11 +0000356 $row = $this->db->select('version')->get($this->_migration_table)->row();
Phil Sturgeon9758d842011-02-07 20:39:00 +0000357 return $row ? $row->version : 0;
358 }
359
360 // --------------------------------------------------------------------
361
362 /**
363 * Stores the current schema version
364 *
Andrey Andreev9b1db792012-03-26 19:45:51 +0300365 * @param int Migration reached
366 * @return void Outputs a report of the migration
Phil Sturgeon9758d842011-02-07 20:39:00 +0000367 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100368 protected function _update_version($migrations)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000369 {
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700370 return $this->db->update($this->_migration_table, array(
Phil Sturgeon9758d842011-02-07 20:39:00 +0000371 'version' => $migrations
372 ));
373 }
374
375 // --------------------------------------------------------------------
376
377 /**
Phil Sturgeon9758d842011-02-07 20:39:00 +0000378 * Enable the use of CI super-global
379 *
Andrey Andreev9b1db792012-03-26 19:45:51 +0300380 * @param $var
Phil Sturgeon9758d842011-02-07 20:39:00 +0000381 * @return mixed
382 */
383 public function __get($var)
384 {
385 return get_instance()->$var;
386 }
Andrey Andreev56454792012-05-17 14:32:19 +0300387
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100388}
389
390/* End of file Migration.php */
Andrey Andreev9b1db792012-03-26 19:45:51 +0300391/* Location: ./system/libraries/Migration.php */