blob: 840cefe0877e08692fe2688f48013d5e0d8f5236 [file] [log] [blame]
Phil Sturgeon9758d842011-02-07 20:39:00 +00001<?php defined('BASEPATH') OR exit('No direct script access allowed');
2/**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP 5.1.6 or newer
6 *
7 * @package CodeIgniter
8 * @author EllisLab Dev Team
9 * @copyright Copyright (c) 2006 - 2011, EllisLab, Inc.
10 * @license http://codeigniter.com/user_guide/license.html
11 * @link http://codeigniter.com
12 * @since Version 1.0
13 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
19 * Migration Class
20 *
21 * All migrations should implement this, forces up() and down() and gives
22 * access to the CI super-global.
23 *
24 * @package CodeIgniter
25 * @subpackage Libraries
26 * @category Libraries
27 * @author Reactor Engineers
28 * @link
29 */
30class CI_Migration {
31
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010032 protected $_migration_enabled = FALSE;
33 protected $_migration_path = NULL;
34 protected $_migration_version = 0;
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070035 protected $_migration_table = 'migrations';
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -070036 protected $_migration_auto_latest = FALSE;
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070037
Phil Sturgeoncb06c652011-05-04 10:50:25 +010038 protected $_error_string = '';
Phil Sturgeon9758d842011-02-07 20:39:00 +000039
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010040 public function __construct($config = array())
Phil Sturgeon9758d842011-02-07 20:39:00 +000041 {
42 # Only run this constructor on main library load
43 if (get_parent_class($this) !== FALSE)
44 {
45 return;
46 }
47
48 foreach ($config as $key => $val)
49 {
50 $this->{'_' . $key} = $val;
51 }
52
53 log_message('debug', 'Migrations class initialized');
54
55 // Are they trying to use migrations while it is disabled?
56 if ($this->_migration_enabled !== TRUE)
57 {
58 show_error('Migrations has been loaded but is disabled or set up incorrectly.');
59 }
60
61 // If not set, set it
bubbafoleydae42fa2011-08-28 00:54:24 -050062 $this->_migration_path == '' AND $this->_migration_path = APPPATH . 'migrations/';
Phil Sturgeon9758d842011-02-07 20:39:00 +000063
64 // Add trailing slash if not set
65 $this->_migration_path = rtrim($this->_migration_path, '/').'/';
66
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010067 // Load migration language
68 $this->lang->load('migration');
69
Phil Sturgeon9758d842011-02-07 20:39:00 +000070 // They'll probably be using dbforge
71 $this->load->dbforge();
72
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070073 // Make sure the migration table name was set.
Cloudmanic Labs, LLC63b61e32011-09-19 09:35:05 -070074 if (empty($this->_migration_table))
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070075 {
76 show_error('Migrations configuration file (migration.php) must have "migration_table" set.');
77 }
78
Phil Sturgeon9758d842011-02-07 20:39:00 +000079 // If the migrations table is missing, make it
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070080 if ( ! $this->db->table_exists($this->_migration_table))
Phil Sturgeon9758d842011-02-07 20:39:00 +000081 {
82 $this->dbforge->add_field(array(
83 'version' => array('type' => 'INT', 'constraint' => 3),
84 ));
85
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070086 $this->dbforge->create_table($this->_migration_table, TRUE);
Phil Sturgeon9758d842011-02-07 20:39:00 +000087
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070088 $this->db->insert($this->_migration_table, array('version' => 0));
Phil Sturgeon9758d842011-02-07 20:39:00 +000089 }
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -070090
91 // Do we auto migrate to the latest migration?
92 if ( $this->_migration_auto_latest == TRUE )
93 {
94 if ( ! $this->latest() )
95 {
96 show_error($this->error_string());
97 }
98 }
Phil Sturgeon9758d842011-02-07 20:39:00 +000099 }
100
101 // --------------------------------------------------------------------
102
103 /**
104 * Migrate to a schema version
105 *
106 * Calls each migration step required to get to the schema version of
107 * choice
108 *
109 * @access public
110 * @param $version integer Target schema version
111 * @return mixed TRUE if already latest, FALSE if failed, int if upgraded
112 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100113 public function version($target_version)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000114 {
115 $start = $current_version = $this->_get_version();
116 $stop = $target_version;
117
118 if ($target_version > $current_version)
119 {
120 // Moving Up
121 ++$start;
122 ++$stop;
123 $step = 1;
124 }
125
126 else
127 {
128 // Moving Down
129 $step = -1;
130 }
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100131
Phil Sturgeon9758d842011-02-07 20:39:00 +0000132 $method = $step === 1 ? 'up' : 'down';
133 $migrations = array();
134
135 // We now prepare to actually DO the migrations
136 // But first let's make sure that everything is the way it should be
137 for ($i = $start; $i != $stop; $i += $step)
138 {
139 $f = glob(sprintf($this->_migration_path . '%03d_*.php', $i));
140
141 // Only one migration per step is permitted
142 if (count($f) > 1)
143 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100144 $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $i);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000145 return FALSE;
146 }
147
148 // Migration step not found
149 if (count($f) == 0)
150 {
151 // If trying to migrate up to a version greater than the last
152 // existing one, migrate to the last one.
153 if ($step == 1)
154 {
155 break;
156 }
157
158 // If trying to migrate down but we're missing a step,
159 // something must definitely be wrong.
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100160 $this->_error_string = sprintf($this->lang->line('migration_not_found'), $i);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000161 return FALSE;
162 }
163
164 $file = basename($f[0]);
165 $name = basename($f[0], '.php');
166
167 // Filename validations
168 if (preg_match('/^\d{3}_(\w+)$/', $name, $match))
169 {
170 $match[1] = strtolower($match[1]);
171
172 // Cannot repeat a migration at different steps
173 if (in_array($match[1], $migrations))
174 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100175 $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $match[1]);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000176 return FALSE;
177 }
178
179 include $f[0];
180 $class = 'Migration_' . ucfirst($match[1]);
181
182 if ( ! class_exists($class))
183 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100184 $this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000185 return FALSE;
186 }
187
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100188 if ( ! is_callable(array($class, $method)))
Phil Sturgeon9758d842011-02-07 20:39:00 +0000189 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100190 $this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000191 return FALSE;
192 }
193
194 $migrations[] = $match[1];
195 }
196 else
197 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100198 $this->_error_string = sprintf($this->lang->line('migration_invalid_filename'), $file);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000199 return FALSE;
200 }
201 }
202
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100203 log_message('debug', 'Current migration: ' . $current_version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000204
205 $version = $i + ($step == 1 ? -1 : 0);
206
207 // If there is nothing to do so quit
208 if ($migrations === array())
209 {
210 return TRUE;
211 }
212
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100213 log_message('debug', 'Migrating from ' . $method . ' to version ' . $version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000214
215 // Loop through the migrations
216 foreach ($migrations AS $migration)
217 {
218 // Run the migration class
219 $class = 'Migration_' . ucfirst(strtolower($migration));
220 call_user_func(array(new $class, $method));
221
222 $current_version += $step;
223 $this->_update_version($current_version);
224 }
225
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100226 log_message('debug', 'Finished migrating to '.$current_version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000227
228 return $current_version;
229 }
230
231 // --------------------------------------------------------------------
232
233 /**
234 * Set's the schema to the latest migration
235 *
236 * @access public
237 * @return mixed true if already latest, false if failed, int if upgraded
238 */
239 public function latest()
240 {
241 if ( ! $migrations = $this->find_migrations())
242 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100243 $this->_error_string = $this->line->lang('migration_none_found');
Phil Sturgeon9758d842011-02-07 20:39:00 +0000244 return false;
245 }
246
247 $last_migration = basename(end($migrations));
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100248
Phil Sturgeon9758d842011-02-07 20:39:00 +0000249 // Calculate the last migration step from existing migration
250 // filenames and procceed to the standard version migration
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100251 return $this->version((int) substr($last_migration, 0, 3));
Phil Sturgeon9758d842011-02-07 20:39:00 +0000252 }
253
254 // --------------------------------------------------------------------
255
256 /**
257 * Set's the schema to the migration version set in config
258 *
259 * @access public
260 * @return mixed true if already current, false if failed, int if upgraded
261 */
262 public function current()
263 {
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100264 return $this->version($this->_migration_version);
265 }
266
267 // --------------------------------------------------------------------
268
269 /**
270 * Error string
271 *
272 * @access public
273 * @return string Error message returned as a string
274 */
275 public function error_string()
276 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100277 return $this->_error_string;
Phil Sturgeon9758d842011-02-07 20:39:00 +0000278 }
279
280 // --------------------------------------------------------------------
281
282 /**
283 * Set's the schema to the latest migration
284 *
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100285 * @access protected
Phil Sturgeon9758d842011-02-07 20:39:00 +0000286 * @return mixed true if already latest, false if failed, int if upgraded
287 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100288 protected function find_migrations()
Phil Sturgeon9758d842011-02-07 20:39:00 +0000289 {
290 // Load all *_*.php files in the migrations path
291 $files = glob($this->_migration_path . '*_*.php');
292 $file_count = count($files);
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100293
Phil Sturgeon9758d842011-02-07 20:39:00 +0000294 for ($i = 0; $i < $file_count; $i++)
295 {
296 // Mark wrongly formatted files as false for later filtering
297 $name = basename($files[$i], '.php');
298 if ( ! preg_match('/^\d{3}_(\w+)$/', $name))
299 {
300 $files[$i] = FALSE;
301 }
302 }
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100303
Phil Sturgeon9758d842011-02-07 20:39:00 +0000304 sort($files);
305
306 return $files;
307 }
308
309 // --------------------------------------------------------------------
310
311 /**
312 * Retrieves current schema version
313 *
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100314 * @access protected
315 * @return integer Current Migration
Phil Sturgeon9758d842011-02-07 20:39:00 +0000316 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100317 protected function _get_version()
Phil Sturgeon9758d842011-02-07 20:39:00 +0000318 {
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700319 $row = $this->db->get($this->_migration_table)->row();
Phil Sturgeon9758d842011-02-07 20:39:00 +0000320 return $row ? $row->version : 0;
321 }
322
323 // --------------------------------------------------------------------
324
325 /**
326 * Stores the current schema version
327 *
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100328 * @access protected
329 * @param $migrations integer Migration reached
Phil Sturgeon9758d842011-02-07 20:39:00 +0000330 * @return void Outputs a report of the migration
331 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100332 protected function _update_version($migrations)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000333 {
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700334 return $this->db->update($this->_migration_table, array(
Phil Sturgeon9758d842011-02-07 20:39:00 +0000335 'version' => $migrations
336 ));
337 }
338
339 // --------------------------------------------------------------------
340
341 /**
Phil Sturgeon9758d842011-02-07 20:39:00 +0000342 * Enable the use of CI super-global
343 *
344 * @access public
345 * @param $var
346 * @return mixed
347 */
348 public function __get($var)
349 {
350 return get_instance()->$var;
351 }
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100352}
353
354/* End of file Migration.php */
355/* Location: ./system/libraries/Migration.php */