blob: a18fcb9f1ebabfcccbccdb737d665434634c1ad5 [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
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010042 protected $_migration_enabled = FALSE;
43 protected $_migration_path = NULL;
44 protected $_migration_version = 0;
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070045 protected $_migration_table = 'migrations';
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -070046 protected $_migration_auto_latest = FALSE;
Eric Barnesdd81c432011-11-16 11:07:35 -050047
Phil Sturgeoncb06c652011-05-04 10:50:25 +010048 protected $_error_string = '';
Phil Sturgeon9758d842011-02-07 20:39:00 +000049
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010050 public function __construct($config = array())
Phil Sturgeon9758d842011-02-07 20:39:00 +000051 {
52 # Only run this constructor on main library load
53 if (get_parent_class($this) !== FALSE)
54 {
55 return;
56 }
57
58 foreach ($config as $key => $val)
59 {
60 $this->{'_' . $key} = $val;
61 }
62
63 log_message('debug', 'Migrations class initialized');
64
65 // Are they trying to use migrations while it is disabled?
66 if ($this->_migration_enabled !== TRUE)
67 {
68 show_error('Migrations has been loaded but is disabled or set up incorrectly.');
69 }
70
71 // If not set, set it
Andrey Andreev9b1db792012-03-26 19:45:51 +030072 $this->_migration_path != '' OR $this->_migration_path = APPPATH.'migrations/';
Phil Sturgeon9758d842011-02-07 20:39:00 +000073
74 // Add trailing slash if not set
75 $this->_migration_path = rtrim($this->_migration_path, '/').'/';
76
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010077 // Load migration language
78 $this->lang->load('migration');
79
Phil Sturgeon9758d842011-02-07 20:39:00 +000080 // They'll probably be using dbforge
81 $this->load->dbforge();
82
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070083 // Make sure the migration table name was set.
Cloudmanic Labs, LLC63b61e32011-09-19 09:35:05 -070084 if (empty($this->_migration_table))
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070085 {
Eric Barnesdd81c432011-11-16 11:07:35 -050086 show_error('Migrations configuration file (migration.php) must have "migration_table" set.');
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070087 }
88
Phil Sturgeon9758d842011-02-07 20:39:00 +000089 // If the migrations table is missing, make it
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070090 if ( ! $this->db->table_exists($this->_migration_table))
Phil Sturgeon9758d842011-02-07 20:39:00 +000091 {
92 $this->dbforge->add_field(array(
93 'version' => array('type' => 'INT', 'constraint' => 3),
94 ));
95
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070096 $this->dbforge->create_table($this->_migration_table, TRUE);
Phil Sturgeon9758d842011-02-07 20:39:00 +000097
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070098 $this->db->insert($this->_migration_table, array('version' => 0));
Phil Sturgeon9758d842011-02-07 20:39:00 +000099 }
Eric Barnesdd81c432011-11-16 11:07:35 -0500100
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -0700101 // Do we auto migrate to the latest migration?
Andrey Andreev9b1db792012-03-26 19:45:51 +0300102 if ($this->_migration_auto_latest === TRUE && ! $this->latest())
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -0700103 {
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200104 show_error($this->error_string());
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -0700105 }
Phil Sturgeon9758d842011-02-07 20:39:00 +0000106 }
107
108 // --------------------------------------------------------------------
109
110 /**
111 * Migrate to a schema version
112 *
113 * Calls each migration step required to get to the schema version of
114 * choice
115 *
Andrey Andreev9b1db792012-03-26 19:45:51 +0300116 * @param int Target schema version
Phil Sturgeon9758d842011-02-07 20:39:00 +0000117 * @return mixed TRUE if already latest, FALSE if failed, int if upgraded
118 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100119 public function version($target_version)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000120 {
121 $start = $current_version = $this->_get_version();
122 $stop = $target_version;
123
124 if ($target_version > $current_version)
125 {
126 // Moving Up
127 ++$start;
128 ++$stop;
129 $step = 1;
130 }
Phil Sturgeon9758d842011-02-07 20:39:00 +0000131 else
132 {
133 // Moving Down
134 $step = -1;
135 }
Eric Barnesdd81c432011-11-16 11:07:35 -0500136
Phil Sturgeon9758d842011-02-07 20:39:00 +0000137 $method = $step === 1 ? 'up' : 'down';
138 $migrations = array();
139
140 // We now prepare to actually DO the migrations
141 // But first let's make sure that everything is the way it should be
142 for ($i = $start; $i != $stop; $i += $step)
143 {
Eric Barnesdd81c432011-11-16 11:07:35 -0500144 $f = glob(sprintf($this->_migration_path.'%03d_*.php', $i));
Phil Sturgeon9758d842011-02-07 20:39:00 +0000145
146 // Only one migration per step is permitted
147 if (count($f) > 1)
148 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100149 $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $i);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000150 return FALSE;
151 }
152
153 // Migration step not found
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200154 if (count($f) === 0)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000155 {
156 // If trying to migrate up to a version greater than the last
157 // existing one, migrate to the last one.
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200158 if ($step === 1)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000159 {
160 break;
161 }
162
163 // If trying to migrate down but we're missing a step,
164 // something must definitely be wrong.
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100165 $this->_error_string = sprintf($this->lang->line('migration_not_found'), $i);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000166 return FALSE;
167 }
168
169 $file = basename($f[0]);
170 $name = basename($f[0], '.php');
171
172 // Filename validations
173 if (preg_match('/^\d{3}_(\w+)$/', $name, $match))
174 {
175 $match[1] = strtolower($match[1]);
176
177 // Cannot repeat a migration at different steps
178 if (in_array($match[1], $migrations))
179 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100180 $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $match[1]);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000181 return FALSE;
182 }
183
184 include $f[0];
Eric Barnesdd81c432011-11-16 11:07:35 -0500185 $class = 'Migration_'.ucfirst($match[1]);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000186
187 if ( ! class_exists($class))
188 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100189 $this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000190 return FALSE;
191 }
192
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100193 if ( ! is_callable(array($class, $method)))
Phil Sturgeon9758d842011-02-07 20:39:00 +0000194 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100195 $this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000196 return FALSE;
197 }
198
199 $migrations[] = $match[1];
200 }
201 else
202 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100203 $this->_error_string = sprintf($this->lang->line('migration_invalid_filename'), $file);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000204 return FALSE;
205 }
206 }
207
Eric Barnesdd81c432011-11-16 11:07:35 -0500208 log_message('debug', 'Current migration: '.$current_version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000209
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200210 $version = $i + ($step === 1 ? -1 : 0);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000211
212 // If there is nothing to do so quit
213 if ($migrations === array())
214 {
215 return TRUE;
216 }
217
Eric Barnesdd81c432011-11-16 11:07:35 -0500218 log_message('debug', 'Migrating from '.$method.' to version '.$version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000219
220 // Loop through the migrations
221 foreach ($migrations AS $migration)
222 {
223 // Run the migration class
Eric Barnesdd81c432011-11-16 11:07:35 -0500224 $class = 'Migration_'.ucfirst(strtolower($migration));
Phil Sturgeon9758d842011-02-07 20:39:00 +0000225 call_user_func(array(new $class, $method));
226
227 $current_version += $step;
228 $this->_update_version($current_version);
229 }
230
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100231 log_message('debug', 'Finished migrating to '.$current_version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000232
233 return $current_version;
234 }
235
236 // --------------------------------------------------------------------
237
238 /**
239 * Set's the schema to the latest migration
240 *
Phil Sturgeon9758d842011-02-07 20:39:00 +0000241 * @return mixed true if already latest, false if failed, int if upgraded
242 */
243 public function latest()
244 {
245 if ( ! $migrations = $this->find_migrations())
246 {
Cloudmanic Labs, LLC3d036e32011-11-11 22:46:21 -0800247 $this->_error_string = $this->lang->line('migration_none_found');
Phil Sturgeon9758d842011-02-07 20:39:00 +0000248 return false;
249 }
250
251 $last_migration = basename(end($migrations));
Eric Barnesdd81c432011-11-16 11:07:35 -0500252
Phil Sturgeon9758d842011-02-07 20:39:00 +0000253 // Calculate the last migration step from existing migration
254 // filenames and procceed to the standard version migration
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100255 return $this->version((int) substr($last_migration, 0, 3));
Phil Sturgeon9758d842011-02-07 20:39:00 +0000256 }
257
258 // --------------------------------------------------------------------
259
260 /**
261 * Set's the schema to the migration version set in config
262 *
Phil Sturgeon9758d842011-02-07 20:39:00 +0000263 * @return mixed true if already current, false if failed, int if upgraded
264 */
265 public function current()
266 {
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100267 return $this->version($this->_migration_version);
268 }
269
270 // --------------------------------------------------------------------
271
272 /**
273 * Error string
274 *
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100275 * @return string Error message returned as a string
276 */
277 public function error_string()
278 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100279 return $this->_error_string;
Phil Sturgeon9758d842011-02-07 20:39:00 +0000280 }
281
282 // --------------------------------------------------------------------
283
284 /**
285 * Set's the schema to the latest migration
286 *
Phil Sturgeon9758d842011-02-07 20:39:00 +0000287 * @return mixed true if already latest, false if failed, int if upgraded
288 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100289 protected function find_migrations()
Phil Sturgeon9758d842011-02-07 20:39:00 +0000290 {
291 // Load all *_*.php files in the migrations path
Eric Barnesdd81c432011-11-16 11:07:35 -0500292 $files = glob($this->_migration_path.'*_*.php');
Eric Barnesdd81c432011-11-16 11:07:35 -0500293
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200294 for ($i = 0, $c = count($files); $i < $c; $i++)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000295 {
296 // Mark wrongly formatted files as false for later filtering
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200297 if ( ! preg_match('/^\d{3}_(\w+)$/', basename($files[$i], '.php')))
Phil Sturgeon9758d842011-02-07 20:39:00 +0000298 {
299 $files[$i] = FALSE;
300 }
301 }
Eric Barnesdd81c432011-11-16 11:07:35 -0500302
Phil Sturgeon9758d842011-02-07 20:39:00 +0000303 sort($files);
304
305 return $files;
306 }
307
308 // --------------------------------------------------------------------
309
310 /**
311 * Retrieves current schema version
312 *
Andrey Andreev9b1db792012-03-26 19:45:51 +0300313 * @return int Current Migration
Phil Sturgeon9758d842011-02-07 20:39:00 +0000314 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100315 protected function _get_version()
Phil Sturgeon9758d842011-02-07 20:39:00 +0000316 {
Phil Sturgeond268eda2011-12-31 16:20:11 +0000317 $row = $this->db->select('version')->get($this->_migration_table)->row();
Phil Sturgeon9758d842011-02-07 20:39:00 +0000318 return $row ? $row->version : 0;
319 }
320
321 // --------------------------------------------------------------------
322
323 /**
324 * Stores the current schema version
325 *
Andrey Andreev9b1db792012-03-26 19:45:51 +0300326 * @param int Migration reached
327 * @return void Outputs a report of the migration
Phil Sturgeon9758d842011-02-07 20:39:00 +0000328 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100329 protected function _update_version($migrations)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000330 {
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700331 return $this->db->update($this->_migration_table, array(
Phil Sturgeon9758d842011-02-07 20:39:00 +0000332 'version' => $migrations
333 ));
334 }
335
336 // --------------------------------------------------------------------
337
338 /**
Phil Sturgeon9758d842011-02-07 20:39:00 +0000339 * Enable the use of CI super-global
340 *
Andrey Andreev9b1db792012-03-26 19:45:51 +0300341 * @param $var
Phil Sturgeon9758d842011-02-07 20:39:00 +0000342 * @return mixed
343 */
344 public function __get($var)
345 {
346 return get_instance()->$var;
347 }
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100348}
349
350/* End of file Migration.php */
Andrey Andreev9b1db792012-03-26 19:45:51 +0300351/* Location: ./system/libraries/Migration.php */