blob: 682d907526fdc3a25bd71256401f7ec98c62fbaf [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';
36
Phil Sturgeoncb06c652011-05-04 10:50:25 +010037 protected $_error_string = '';
Phil Sturgeon9758d842011-02-07 20:39:00 +000038
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010039 public function __construct($config = array())
Phil Sturgeon9758d842011-02-07 20:39:00 +000040 {
41 # Only run this constructor on main library load
42 if (get_parent_class($this) !== FALSE)
43 {
44 return;
45 }
46
47 foreach ($config as $key => $val)
48 {
49 $this->{'_' . $key} = $val;
50 }
51
52 log_message('debug', 'Migrations class initialized');
53
54 // Are they trying to use migrations while it is disabled?
55 if ($this->_migration_enabled !== TRUE)
56 {
57 show_error('Migrations has been loaded but is disabled or set up incorrectly.');
58 }
59
60 // If not set, set it
bubbafoleydae42fa2011-08-28 00:54:24 -050061 $this->_migration_path == '' AND $this->_migration_path = APPPATH . 'migrations/';
Phil Sturgeon9758d842011-02-07 20:39:00 +000062
63 // Add trailing slash if not set
64 $this->_migration_path = rtrim($this->_migration_path, '/').'/';
65
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010066 // Load migration language
67 $this->lang->load('migration');
68
Phil Sturgeon9758d842011-02-07 20:39:00 +000069 // They'll probably be using dbforge
70 $this->load->dbforge();
71
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070072 // Make sure the migration table name was set.
73 if ( (! isset($this->_migration_table)) OR (empty($this->_migration_table)))
74 {
75 show_error('Migrations configuration file (migration.php) must have "migration_table" set.');
76 }
77
Phil Sturgeon9758d842011-02-07 20:39:00 +000078 // If the migrations table is missing, make it
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070079 if ( ! $this->db->table_exists($this->_migration_table))
Phil Sturgeon9758d842011-02-07 20:39:00 +000080 {
81 $this->dbforge->add_field(array(
82 'version' => array('type' => 'INT', 'constraint' => 3),
83 ));
84
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070085 $this->dbforge->create_table($this->_migration_table, TRUE);
Phil Sturgeon9758d842011-02-07 20:39:00 +000086
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070087 $this->db->insert($this->_migration_table, array('version' => 0));
Phil Sturgeon9758d842011-02-07 20:39:00 +000088 }
89 }
90
91 // --------------------------------------------------------------------
92
93 /**
94 * Migrate to a schema version
95 *
96 * Calls each migration step required to get to the schema version of
97 * choice
98 *
99 * @access public
100 * @param $version integer Target schema version
101 * @return mixed TRUE if already latest, FALSE if failed, int if upgraded
102 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100103 public function version($target_version)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000104 {
105 $start = $current_version = $this->_get_version();
106 $stop = $target_version;
107
108 if ($target_version > $current_version)
109 {
110 // Moving Up
111 ++$start;
112 ++$stop;
113 $step = 1;
114 }
115
116 else
117 {
118 // Moving Down
119 $step = -1;
120 }
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100121
Phil Sturgeon9758d842011-02-07 20:39:00 +0000122 $method = $step === 1 ? 'up' : 'down';
123 $migrations = array();
124
125 // We now prepare to actually DO the migrations
126 // But first let's make sure that everything is the way it should be
127 for ($i = $start; $i != $stop; $i += $step)
128 {
129 $f = glob(sprintf($this->_migration_path . '%03d_*.php', $i));
130
131 // Only one migration per step is permitted
132 if (count($f) > 1)
133 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100134 $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $i);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000135 return FALSE;
136 }
137
138 // Migration step not found
139 if (count($f) == 0)
140 {
141 // If trying to migrate up to a version greater than the last
142 // existing one, migrate to the last one.
143 if ($step == 1)
144 {
145 break;
146 }
147
148 // If trying to migrate down but we're missing a step,
149 // something must definitely be wrong.
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100150 $this->_error_string = sprintf($this->lang->line('migration_not_found'), $i);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000151 return FALSE;
152 }
153
154 $file = basename($f[0]);
155 $name = basename($f[0], '.php');
156
157 // Filename validations
158 if (preg_match('/^\d{3}_(\w+)$/', $name, $match))
159 {
160 $match[1] = strtolower($match[1]);
161
162 // Cannot repeat a migration at different steps
163 if (in_array($match[1], $migrations))
164 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100165 $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $match[1]);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000166 return FALSE;
167 }
168
169 include $f[0];
170 $class = 'Migration_' . ucfirst($match[1]);
171
172 if ( ! class_exists($class))
173 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100174 $this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000175 return FALSE;
176 }
177
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100178 if ( ! is_callable(array($class, $method)))
Phil Sturgeon9758d842011-02-07 20:39:00 +0000179 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100180 $this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000181 return FALSE;
182 }
183
184 $migrations[] = $match[1];
185 }
186 else
187 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100188 $this->_error_string = sprintf($this->lang->line('migration_invalid_filename'), $file);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000189 return FALSE;
190 }
191 }
192
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100193 log_message('debug', 'Current migration: ' . $current_version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000194
195 $version = $i + ($step == 1 ? -1 : 0);
196
197 // If there is nothing to do so quit
198 if ($migrations === array())
199 {
200 return TRUE;
201 }
202
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100203 log_message('debug', 'Migrating from ' . $method . ' to version ' . $version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000204
205 // Loop through the migrations
206 foreach ($migrations AS $migration)
207 {
208 // Run the migration class
209 $class = 'Migration_' . ucfirst(strtolower($migration));
210 call_user_func(array(new $class, $method));
211
212 $current_version += $step;
213 $this->_update_version($current_version);
214 }
215
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100216 log_message('debug', 'Finished migrating to '.$current_version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000217
218 return $current_version;
219 }
220
221 // --------------------------------------------------------------------
222
223 /**
224 * Set's the schema to the latest migration
225 *
226 * @access public
227 * @return mixed true if already latest, false if failed, int if upgraded
228 */
229 public function latest()
230 {
231 if ( ! $migrations = $this->find_migrations())
232 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100233 $this->_error_string = $this->line->lang('migration_none_found');
Phil Sturgeon9758d842011-02-07 20:39:00 +0000234 return false;
235 }
236
237 $last_migration = basename(end($migrations));
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100238
Phil Sturgeon9758d842011-02-07 20:39:00 +0000239 // Calculate the last migration step from existing migration
240 // filenames and procceed to the standard version migration
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100241 return $this->version((int) substr($last_migration, 0, 3));
Phil Sturgeon9758d842011-02-07 20:39:00 +0000242 }
243
244 // --------------------------------------------------------------------
245
246 /**
247 * Set's the schema to the migration version set in config
248 *
249 * @access public
250 * @return mixed true if already current, false if failed, int if upgraded
251 */
252 public function current()
253 {
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100254 return $this->version($this->_migration_version);
255 }
256
257 // --------------------------------------------------------------------
258
259 /**
260 * Error string
261 *
262 * @access public
263 * @return string Error message returned as a string
264 */
265 public function error_string()
266 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100267 return $this->_error_string;
Phil Sturgeon9758d842011-02-07 20:39:00 +0000268 }
269
270 // --------------------------------------------------------------------
271
272 /**
273 * Set's the schema to the latest migration
274 *
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100275 * @access protected
Phil Sturgeon9758d842011-02-07 20:39:00 +0000276 * @return mixed true if already latest, false if failed, int if upgraded
277 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100278 protected function find_migrations()
Phil Sturgeon9758d842011-02-07 20:39:00 +0000279 {
280 // Load all *_*.php files in the migrations path
281 $files = glob($this->_migration_path . '*_*.php');
282 $file_count = count($files);
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100283
Phil Sturgeon9758d842011-02-07 20:39:00 +0000284 for ($i = 0; $i < $file_count; $i++)
285 {
286 // Mark wrongly formatted files as false for later filtering
287 $name = basename($files[$i], '.php');
288 if ( ! preg_match('/^\d{3}_(\w+)$/', $name))
289 {
290 $files[$i] = FALSE;
291 }
292 }
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100293
Phil Sturgeon9758d842011-02-07 20:39:00 +0000294 sort($files);
295
296 return $files;
297 }
298
299 // --------------------------------------------------------------------
300
301 /**
302 * Retrieves current schema version
303 *
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100304 * @access protected
305 * @return integer Current Migration
Phil Sturgeon9758d842011-02-07 20:39:00 +0000306 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100307 protected function _get_version()
Phil Sturgeon9758d842011-02-07 20:39:00 +0000308 {
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700309 $row = $this->db->get($this->_migration_table)->row();
Phil Sturgeon9758d842011-02-07 20:39:00 +0000310 return $row ? $row->version : 0;
311 }
312
313 // --------------------------------------------------------------------
314
315 /**
316 * Stores the current schema version
317 *
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100318 * @access protected
319 * @param $migrations integer Migration reached
Phil Sturgeon9758d842011-02-07 20:39:00 +0000320 * @return void Outputs a report of the migration
321 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100322 protected function _update_version($migrations)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000323 {
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700324 return $this->db->update($this->_migration_table, array(
Phil Sturgeon9758d842011-02-07 20:39:00 +0000325 'version' => $migrations
326 ));
327 }
328
329 // --------------------------------------------------------------------
330
331 /**
Phil Sturgeon9758d842011-02-07 20:39:00 +0000332 * Enable the use of CI super-global
333 *
334 * @access public
335 * @param $var
336 * @return mixed
337 */
338 public function __get($var)
339 {
340 return get_instance()->$var;
341 }
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100342}
343
344/* End of file Migration.php */
345/* Location: ./system/libraries/Migration.php */