blob: f89391c0d72f42b124c8ecee9aa5e0576ee27b37 [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 *
5 * An open source application development framework for PHP 5.1.6 or newer
6 *
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
Derek Jonesf4a4bd82011-10-20 12:18:42 -050021 * @copyright Copyright (c) 2006 - 2011, EllisLab, Inc. (http://ellislab.com/)
22 * @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
28// ------------------------------------------------------------------------
29
30/**
31 * Migration Class
32 *
33 * All migrations should implement this, forces up() and down() and gives
34 * access to the CI super-global.
35 *
36 * @package CodeIgniter
37 * @subpackage Libraries
38 * @category Libraries
39 * @author Reactor Engineers
40 * @link
41 */
42class CI_Migration {
43
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010044 protected $_migration_enabled = FALSE;
45 protected $_migration_path = NULL;
46 protected $_migration_version = 0;
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070047 protected $_migration_table = 'migrations';
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -070048 protected $_migration_auto_latest = FALSE;
Eric Barnesdd81c432011-11-16 11:07:35 -050049
Phil Sturgeoncb06c652011-05-04 10:50:25 +010050 protected $_error_string = '';
Phil Sturgeon9758d842011-02-07 20:39:00 +000051
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010052 public function __construct($config = array())
Phil Sturgeon9758d842011-02-07 20:39:00 +000053 {
54 # Only run this constructor on main library load
55 if (get_parent_class($this) !== FALSE)
56 {
57 return;
58 }
59
60 foreach ($config as $key => $val)
61 {
62 $this->{'_' . $key} = $val;
63 }
64
65 log_message('debug', 'Migrations class initialized');
66
67 // Are they trying to use migrations while it is disabled?
68 if ($this->_migration_enabled !== TRUE)
69 {
70 show_error('Migrations has been loaded but is disabled or set up incorrectly.');
71 }
72
73 // If not set, set it
Eric Barnesdd81c432011-11-16 11:07:35 -050074 $this->_migration_path == '' AND $this->_migration_path = APPPATH.'migrations/';
Phil Sturgeon9758d842011-02-07 20:39:00 +000075
76 // Add trailing slash if not set
77 $this->_migration_path = rtrim($this->_migration_path, '/').'/';
78
Phil Sturgeon96bd33b2011-05-04 01:30:36 +010079 // Load migration language
80 $this->lang->load('migration');
81
Phil Sturgeon9758d842011-02-07 20:39:00 +000082 // They'll probably be using dbforge
83 $this->load->dbforge();
84
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070085 // Make sure the migration table name was set.
Cloudmanic Labs, LLC63b61e32011-09-19 09:35:05 -070086 if (empty($this->_migration_table))
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070087 {
Eric Barnesdd81c432011-11-16 11:07:35 -050088 show_error('Migrations configuration file (migration.php) must have "migration_table" set.');
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070089 }
90
Phil Sturgeon9758d842011-02-07 20:39:00 +000091 // If the migrations table is missing, make it
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070092 if ( ! $this->db->table_exists($this->_migration_table))
Phil Sturgeon9758d842011-02-07 20:39:00 +000093 {
94 $this->dbforge->add_field(array(
95 'version' => array('type' => 'INT', 'constraint' => 3),
96 ));
97
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -070098 $this->dbforge->create_table($this->_migration_table, TRUE);
Phil Sturgeon9758d842011-02-07 20:39:00 +000099
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700100 $this->db->insert($this->_migration_table, array('version' => 0));
Phil Sturgeon9758d842011-02-07 20:39:00 +0000101 }
Eric Barnesdd81c432011-11-16 11:07:35 -0500102
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -0700103 // Do we auto migrate to the latest migration?
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200104 if ($this->_migration_auto_latest === TRUE AND ! $this->latest())
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -0700105 {
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200106 show_error($this->error_string());
Cloudmanic Labs, LLCd1ba8f72011-09-18 12:23:00 -0700107 }
Phil Sturgeon9758d842011-02-07 20:39:00 +0000108 }
109
110 // --------------------------------------------------------------------
111
112 /**
113 * Migrate to a schema version
114 *
115 * Calls each migration step required to get to the schema version of
116 * choice
117 *
118 * @access public
119 * @param $version integer Target schema version
120 * @return mixed TRUE if already latest, FALSE if failed, int if upgraded
121 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100122 public function version($target_version)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000123 {
124 $start = $current_version = $this->_get_version();
125 $stop = $target_version;
126
127 if ($target_version > $current_version)
128 {
129 // Moving Up
130 ++$start;
131 ++$stop;
132 $step = 1;
133 }
Phil Sturgeon9758d842011-02-07 20:39:00 +0000134 else
135 {
136 // Moving Down
137 $step = -1;
138 }
Eric Barnesdd81c432011-11-16 11:07:35 -0500139
Phil Sturgeon9758d842011-02-07 20:39:00 +0000140 $method = $step === 1 ? 'up' : 'down';
141 $migrations = array();
142
143 // We now prepare to actually DO the migrations
144 // But first let's make sure that everything is the way it should be
145 for ($i = $start; $i != $stop; $i += $step)
146 {
Eric Barnesdd81c432011-11-16 11:07:35 -0500147 $f = glob(sprintf($this->_migration_path.'%03d_*.php', $i));
Phil Sturgeon9758d842011-02-07 20:39:00 +0000148
149 // Only one migration per step is permitted
150 if (count($f) > 1)
151 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100152 $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $i);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000153 return FALSE;
154 }
155
156 // Migration step not found
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200157 if (count($f) === 0)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000158 {
159 // If trying to migrate up to a version greater than the last
160 // existing one, migrate to the last one.
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200161 if ($step === 1)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000162 {
163 break;
164 }
165
166 // If trying to migrate down but we're missing a step,
167 // something must definitely be wrong.
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100168 $this->_error_string = sprintf($this->lang->line('migration_not_found'), $i);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000169 return FALSE;
170 }
171
172 $file = basename($f[0]);
173 $name = basename($f[0], '.php');
174
175 // Filename validations
176 if (preg_match('/^\d{3}_(\w+)$/', $name, $match))
177 {
178 $match[1] = strtolower($match[1]);
179
180 // Cannot repeat a migration at different steps
181 if (in_array($match[1], $migrations))
182 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100183 $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $match[1]);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000184 return FALSE;
185 }
186
187 include $f[0];
Eric Barnesdd81c432011-11-16 11:07:35 -0500188 $class = 'Migration_'.ucfirst($match[1]);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000189
190 if ( ! class_exists($class))
191 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100192 $this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000193 return FALSE;
194 }
195
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100196 if ( ! is_callable(array($class, $method)))
Phil Sturgeon9758d842011-02-07 20:39:00 +0000197 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100198 $this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000199 return FALSE;
200 }
201
202 $migrations[] = $match[1];
203 }
204 else
205 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100206 $this->_error_string = sprintf($this->lang->line('migration_invalid_filename'), $file);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000207 return FALSE;
208 }
209 }
210
Eric Barnesdd81c432011-11-16 11:07:35 -0500211 log_message('debug', 'Current migration: '.$current_version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000212
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200213 $version = $i + ($step === 1 ? -1 : 0);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000214
215 // If there is nothing to do so quit
216 if ($migrations === array())
217 {
218 return TRUE;
219 }
220
Eric Barnesdd81c432011-11-16 11:07:35 -0500221 log_message('debug', 'Migrating from '.$method.' to version '.$version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000222
223 // Loop through the migrations
224 foreach ($migrations AS $migration)
225 {
226 // Run the migration class
Eric Barnesdd81c432011-11-16 11:07:35 -0500227 $class = 'Migration_'.ucfirst(strtolower($migration));
Phil Sturgeon9758d842011-02-07 20:39:00 +0000228 call_user_func(array(new $class, $method));
229
230 $current_version += $step;
231 $this->_update_version($current_version);
232 }
233
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100234 log_message('debug', 'Finished migrating to '.$current_version);
Phil Sturgeon9758d842011-02-07 20:39:00 +0000235
236 return $current_version;
237 }
238
239 // --------------------------------------------------------------------
240
241 /**
242 * Set's the schema to the latest migration
243 *
244 * @access public
245 * @return mixed true if already latest, false if failed, int if upgraded
246 */
247 public function latest()
248 {
249 if ( ! $migrations = $this->find_migrations())
250 {
Cloudmanic Labs, LLC3d036e32011-11-11 22:46:21 -0800251 $this->_error_string = $this->lang->line('migration_none_found');
Phil Sturgeon9758d842011-02-07 20:39:00 +0000252 return false;
253 }
254
255 $last_migration = basename(end($migrations));
Eric Barnesdd81c432011-11-16 11:07:35 -0500256
Phil Sturgeon9758d842011-02-07 20:39:00 +0000257 // Calculate the last migration step from existing migration
258 // filenames and procceed to the standard version migration
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100259 return $this->version((int) substr($last_migration, 0, 3));
Phil Sturgeon9758d842011-02-07 20:39:00 +0000260 }
261
262 // --------------------------------------------------------------------
263
264 /**
265 * Set's the schema to the migration version set in config
266 *
267 * @access public
268 * @return mixed true if already current, false if failed, int if upgraded
269 */
270 public function current()
271 {
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100272 return $this->version($this->_migration_version);
273 }
274
275 // --------------------------------------------------------------------
276
277 /**
278 * Error string
279 *
280 * @access public
281 * @return string Error message returned as a string
282 */
283 public function error_string()
284 {
Phil Sturgeoncb06c652011-05-04 10:50:25 +0100285 return $this->_error_string;
Phil Sturgeon9758d842011-02-07 20:39:00 +0000286 }
287
288 // --------------------------------------------------------------------
289
290 /**
291 * Set's the schema to the latest migration
292 *
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100293 * @access protected
Phil Sturgeon9758d842011-02-07 20:39:00 +0000294 * @return mixed true if already latest, false if failed, int if upgraded
295 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100296 protected function find_migrations()
Phil Sturgeon9758d842011-02-07 20:39:00 +0000297 {
298 // Load all *_*.php files in the migrations path
Eric Barnesdd81c432011-11-16 11:07:35 -0500299 $files = glob($this->_migration_path.'*_*.php');
Eric Barnesdd81c432011-11-16 11:07:35 -0500300
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200301 for ($i = 0, $c = count($files); $i < $c; $i++)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000302 {
303 // Mark wrongly formatted files as false for later filtering
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200304 if ( ! preg_match('/^\d{3}_(\w+)$/', basename($files[$i], '.php')))
Phil Sturgeon9758d842011-02-07 20:39:00 +0000305 {
306 $files[$i] = FALSE;
307 }
308 }
Eric Barnesdd81c432011-11-16 11:07:35 -0500309
Phil Sturgeon9758d842011-02-07 20:39:00 +0000310 sort($files);
311
312 return $files;
313 }
314
315 // --------------------------------------------------------------------
316
317 /**
318 * Retrieves current schema version
319 *
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100320 * @access protected
321 * @return integer Current Migration
Phil Sturgeon9758d842011-02-07 20:39:00 +0000322 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100323 protected function _get_version()
Phil Sturgeon9758d842011-02-07 20:39:00 +0000324 {
Phil Sturgeond268eda2011-12-31 16:20:11 +0000325 $row = $this->db->select('version')->get($this->_migration_table)->row();
Phil Sturgeon9758d842011-02-07 20:39:00 +0000326 return $row ? $row->version : 0;
327 }
328
329 // --------------------------------------------------------------------
330
331 /**
332 * Stores the current schema version
333 *
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100334 * @access protected
335 * @param $migrations integer Migration reached
Phil Sturgeon9758d842011-02-07 20:39:00 +0000336 * @return void Outputs a report of the migration
337 */
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100338 protected function _update_version($migrations)
Phil Sturgeon9758d842011-02-07 20:39:00 +0000339 {
Cloudmanic Labs, LLC539dcb02011-09-18 12:08:56 -0700340 return $this->db->update($this->_migration_table, array(
Phil Sturgeon9758d842011-02-07 20:39:00 +0000341 'version' => $migrations
342 ));
343 }
344
345 // --------------------------------------------------------------------
346
347 /**
Phil Sturgeon9758d842011-02-07 20:39:00 +0000348 * Enable the use of CI super-global
349 *
350 * @access public
351 * @param $var
352 * @return mixed
353 */
354 public function __get($var)
355 {
356 return get_instance()->$var;
357 }
Phil Sturgeon96bd33b2011-05-04 01:30:36 +0100358}
359
360/* End of file Migration.php */
Andrey Andreevdd4702f2011-12-24 19:33:44 +0200361/* Location: ./system/libraries/Migration.php */