Burak Guzel on Dec 29th 2009 with 89 comments

CodeIgniter is a simple and powerful open source web application framework for PHP. Today, we’ll do some core “hacks” to this framework to change and improve its functionality. In the process, you’ll gain a better understanding of the intricacies of CodeIgniter.

1. It is not recommended to apply these hacks to an existing project.

Since they change some of CodeIgniter’s core functionality, it can break the existing code.
2. As of this writing, CodeIgniter 1.7.2 is the latest stable release. These

hacks are not guaranteed to work for future (or past) releases.
3. Even though CodeIgniter is designed to be PHP 4 compatible, some of

these hacks are not. So you will need a server with PHP 5 installed.
4. When you make any changes to the files inside the system folder, you

should document it somewhere for future reference. Next time you upgrade CodeIgniter (even though they do not release updates very often), you may need to reapply those changes.

1. Autoloading Models PHP 5 Style
The Goal

On the left side, you see the regular way of loading a model in CodeIgniter, from within a Controller. After this hack, we will be able to create objects directly. The code is cleaner, and your IDE will be able to recognize the

Add this code to the bottom of system/application/config/config.EXT)."models/". 4. The Hack All we need to do is add a PHP 5 style autoloader function.EXT)..strtolower($class). <?php // .strtolower($class). you are no longer required to extend the Model class: And you no longer have to add a require_once call before you do model class inheritance.strtolower($class). 7. or previewing documentation. <?php 2. } } ?> If you are interested in applying this hack for controllers too.object types.php: 1. 3. you can use this code instead: ."models/"."models/".EXT)) { include_once(APPPATH. First. } 9. There are two more side effects of this hack. function __autoload($class) { if (file_exists(APPPATH. 6.strtolower($class)."models/". // . This enables IDE features such as auto-complete.. ?> } if (file_exists(APPPATH. function __autoload($class) { 5.EXT)) { include_once(APPPATH. 8...

. 9.strtolower($class).. 8.EXT)) { include_once(APPPATH.. 3.. 6.EXT). you can not have the same class name for a Model and a Controller.EXT)) { include_once(APPPATH."models/"."controllers/". 10.strtolower($class).strtolower($class). 7. 4.EXT)) { include_once(APPPATH. } else if (file_exists(APPPATH."controllers/". } else if (file_exists(APPPATH. 5.EXT). // . <?php 2.strtolower($class). ?> } if (file_exists(APPPATH.com/post/display/13 . } Now you can not have a URL like this: 1..EXT).strtolower($class). function __autoload($class) { if (file_exists(APPPATH.strtolower($class). Prevent Model-Controller Name Collision The Goal Normally.1. 4. It takes care of loading the class file.EXT)) { include_once(APPPATH.EXT). this __autoload function is called first."models/".com/post/display/13 http://www. class Post extends Model { 2. 3."controllers/".."controllers/".. } // .mysite.mysite.strtolower($class)."models/". <?php // . http://www."models/". 2.. } 11. Let’s say you created a model name Post: 1. } } ?> Any time you try to use a class that is not defined. function __autoload($class) { 5. class Post extends Model { // .strtolower($class).

it will become possible. I think it is better to add a suffix to the Controllers instead. First we need to extend the Router class. since they are almost never referenced by their class names in your code. } 12.. Post_model). 3. 13. The Hack To get around this issue. 5. 6. 11.. 5.. class Post_controller extends Controller { 4. 7. But with this hack. function MY_Router() { parent::CI_Router(). And the Controller for that URL will look like this: 1..The reason is because that would require you to also have a Controller class named ‘Post. // application/controllers/post. class MY_Router extends CI_Router { 2. } Note the ‘_controller’ suffix. $this->suffix. 10. 9. } // . // application/controllers/post.php 2. 6.’ Creating such a class would result in a fatal PHP error. Model objects are created and referenced all over the application. 4. so it might seem a bit silly to have all of these names with ‘_model’ floating around. 3. var $suffix = '_controller'. 7. function controller_name() { . } 8.php class Post_controller extends Controller { // .php” 1. normally most people add the ‘_model’ suffix to the Model class names (eg. Create this file: “application/libraries/MY_Router. function set_class($class) { $this->class = $class .

} else { return $this->class. } function controller_name() { if (strstr($this->class.$RTR>controller_name(). Next. include(APPPATH. ''.$this->CI->router>fetch_method(). $this->suffix. $this->suffix)) { return str_replace($this->suffix.'controllers/'. 83.$RTR>controller_name().$RTR->fetch_directory(). edit: “system/libraries/Profiler. 16. ''. 21. .">".$RTR>controller_name(). 20."</div> 3.php”. $this->CI->router->controller_name(). 4. if ( ! file_exists(APPPATH.14. $this->class). } } } Now edit “system/codeigniter/CodeIgniter. font-weight: normal.EXT)) if ( ! file_exists(APPPATH. line 323: 1. } } if (strstr($this->class. padding: 4px 0pt.EXT).php” line 153: 1. <div style="color: rgb(153. 17."/".'controllers/'. 22.$RTR->controller_name().$RTR->fetch_directory().'controllers/'.$RTR->fetch_directory().EXT)) Same file. include(APPPATH.EXT). line 158: 1. $output . 0). $this->suffix)) { return str_replace($this->suffix.= " 2. 18.'controllers/'. function MY_Router() { parent::CI_Router(). } class MY_Router extends CI_Router { var $suffix = '_controller'. 15. ". 19.$RTR->fetch_directory(). } function set_class($class) { $this->class = $class . } else { return $this->class. $this->class).

3. or the e-mail address is not already in the system.$this->CI->router->fetch_method()."/". It comes with several validation rules: These are useful. But not in the file names or the URL’s. Form Validation for Unique Values The Goal CodeIgniter has a nice Form_validation class.$this->CI->router->controller_name(). That is all. For example.= " ". .$output . but there is an important one missing from this list: to check for unique values. Keep in mind that with this hack you are required to put the ‘_controller’ suffix on all of your controller class names. most user registration forms need to check that the username is not already taken." ".

Note the last part that says “unique[User. 'E-mail'.username]').” and takes a parameter inside the square brackets. you will be able add this validation rule to your form submission handler very easily: 1.email]').email]').” This new validation rule is just called “unique. which is “tablename. 'required|alpha_numeric|min_length[6]|unique[User. 'required|valid_email|unique[User. $this->form_validation->set_rules('username'. $this->form_validation->set_rules('username'. 2. 'required|alpha_numeric|min_length[6]|unique[User.username]').username].With this hack. And your application can respond with proper error messages: . Similarly. you can check for duplicate e-mails: 1. So it will check the “username” column of the “User” table to make sure the submitted value does not already exist.fieldname”. $this->form_validation->set_rules('email'. 'required|valid_email|unique[User. 'Username'. $this->form_validation->set_rules('email'. 2. 'E-mail'. 'Username'.

class MY_Form_validation extends CI_Form_validation { 2. 6. 3. . 7. Create: “application/libraries/MY_Form_validation. $CI->load->database().php” 1. Nevertheless. $params) { 5. 8. $CI =& get_instance(). we are going to take a core CodeIgniter library and improve it. 4. $CI->form_validation->set_message('unique'. function unique($value.The Hack This might be considered more of an extension than a hack.

2). $params. This is necessary for building cron jobs. Running CodeIgniter from the Command Line The Goal Just like the title says. list($table. $value)->limit(1)->get(). 13. 12. $query = $CI->db->select($field)->from($table) ->where($field. 19. 22. $value)->limit(1)->get().9. 10.". } else { return true. or running more intensive operations so you don’t have the resource limitations of a web script. 21. our goal is to be able to run CodeIgniter applications from the command line. $params. if ($query->row()) { return false. 20. 16. 23. 15. 17. } } list($table. 'The %s is already being used. 'The %s is already being used. 2). } } } Now you can use the unique validation rule. 14. } class MY_Form_validation extends CI_Form_validation { function unique($value. This is what it looks like on my local Windows machine: . $field) = explode(". $field) = explode(". $CI->form_validation->set_message('unique'. $query = $CI->db->select($field)->from($table) ->where($field.'). $params) { $CI =& get_instance(). 4.').". 11. 18. such as maximum execution time. $CI->load->database(). if ($query->row()) { return false. } else { return true.

http://www. if (isset($_SERVER['REMOTE_ADDR'])) { 2. 6.com/hello/world/foo The Hack Create a “cli. .php: 1. 8. '/index. function __construct() { if (isset($_SERVER['REMOTE_ADDR'])) { 4. 5.mysite. } set_time_limit(0). die('Command Line Only!'). 3.php” file at the root of your CodeIgniter folder: 1. } 4. If you are on a Linux environment and want to make this script self executable.php'. require dirname(__FILE__) . $_SERVER['PATH_INFO'] = $_SERVER['REQUEST_URI'] = $argv[1]. set_time_limit(0). you can block web calls at the controller constructor: 1.php'. 3. you can add this as the first line in cli.com/hello/world/foo http://www. $_SERVER['PATH_INFO'] = $_SERVER['REQUEST_URI'] = $argv[1]. '/index. if (isset($_SERVER['REMOTE_ADDR'])) { die('Command Line Only!'). 7.mysite. require dirname(__FILE__) . 9.The above code would be like calling this URL: 1. class Hello extends Controller { 2. #!/usr/bin/php #!/usr/bin/php If you want a specific controller to be command line only.

8. } parent::Controller(). However. your Model classes will need to extend the Doctrine base classes. } } die('Command Line Only!'). This will . 11. Adding Doctrine ORM to CodeIgniter The Goal Doctrine is a popular Object Relational Mapper for PHP. you can have a very powerful Model layer in your framework. The Hack Just installing Doctrine is not very “hacky” per se.... By adding it to CodeIgniter. as we can just add it as a plug-in. } 5.5. 6. once added. parent::Controller(). 9. instead of the CodeIgniter Model class. 12.. 7. } // . } // . 10. class Hello extends Controller { function __construct() { if (isset($_SERVER['REMOTE_ADDR'])) { die('Command Line Only!').

19. 8. Copy the “lib” folder from Doctrine to: “application/plugins/doctrine” 5.'/plugins/doctrine/lib/Doctrine. Create “application/plugins/doctrine_pi.'/config/database. 17. $db[$connection_name]['hostname'] . 9.php'. // we load our database connections into Doctrine_Manager 13. '://' . // this will allow Doctrine to load Model classes automatically 10. 'autoload')). 26. 28. Download Doctrine (1. 18. require_once BASEPATH. Follow these steps: 1. ':' . // first we must convert to dsn format $dsn = $db[$connection_name]['dbdriver'] . 16. '@' .php'. 12. // system/application/plugins/doctrine_pi. // this loop allows us to use multiple connections later on 14. // load database configuration from CodeIgniter 7. require_once APPPATH.php” 1. 11.completely change the way the Model layer works in the framework. $db[$connection_name]['password']. 3. 20.$connection_name).'/libraries/Model.php 2. 21. Create folder: application/plugins/doctrine 3. $db[$connection_name]['username'] . } 25. // telling Doctrine where our models are located . // CodeIgniter's Model class needs to be loaded 27. Doctrine_Manager::connection($dsn. 22. foreach ($db as $connection_name => $db_values) { 15. $db[$connection_name]['database']. '/' . 23. 24. spl_autoload_register(array('Doctrine'.2 as of this article) 4. // load Doctrine library 4. 6. The objects you create will have database persistence and also will able to have database relationships with other objects. 29.php'. Create folder: application/plugins 2. 5. require_once APPPATH.

30.php'. // system/application/plugins/doctrine_pi.php // load Doctrine library require_once APPPATH. That is all. but they will all share the same system folder. 6. $autoload['plugin'] = array('doctrine'). Read my tutorials on this subject for more information.$connection_name). Doctrine::loadModels(APPPATH.'/models').php” to autoload this Doctrine plugin 1. Doctrine_Manager::connection($dsn. // this will allow Doctrine to load Model classes automatically spl_autoload_register(array('Doctrine'. '://' . Each website will have its own application folder. ':' . Running Multiple Sites The Goal This hack will make it possible for you to run multiple sites from a single install of CodeIgniter. $db[$connection_name]['hostname'] . } // CodeIgniter's Model class needs to be loaded require_once BASEPATH. 'autoload')). $autoload['plugin'] = array('doctrine'). '@' .'/config/database. // telling Doctrine where our models are located Doctrine::loadModels(APPPATH. Also make sure you have your database configuration in “application/config/database.php'. $db[$connection_name]['database']. // we load our database connections into Doctrine_Manager // this loop allows us to use multiple connections later on foreach ($db as $connection_name => $db_values) { // first we must convert to dsn format $dsn = $db[$connection_name]['dbdriver'] . .php'. $db[$connection_name]['password']. // load database configuration from CodeIgniter require_once APPPATH. edit “application/config/autoload.'/plugins/doctrine/lib/Doctrine.'/models'). '/' . Now you can create Doctrine Models within your CodeIgniter application.php”. $db[$connection_name]['username'] . Next.'/libraries/Model.

'. like under each separate website folders.” So. Now copy the index. 7. You can place those application folders anywhere. At line 43.. $system_folder = dirname(__FILE__) ./codeigniter/system'. We need to do small hack to get around this limitation.. 3. you must specify which file types are allowed. Now you can have independent websites using separate application folders. there is no way to allow all file types to be uploaded. Allowing All File Types for Uploads The Goal When using the Upload library in CodeIgniter. $this->load->library('upload'). $this->upload->set_allowed_types('jpg|jpeg|gif|png|zip')./application_site1'. but sharing the same system folder./application_site1'. 2.. . There is a similar implementation in the CodeIgniter User Guide you can read also.The Hack Install CodeIgniter anywhere on the server. It doesn’t need to be under a website folder. And make additional copies of it.. $application_folder = dirname(__FILE__) . $this->upload->set_allowed_types('jpg|jpeg|gif|png|zip'). for every website you want to run. '. put the full path to the system folder: 1. put the full path to the application folder: 1.php file to the root of each website folder. you will receive an error message from CodeIgniter: “You have not specified any allowed file types. by default. After that we will be able to allow all file types by setting it to ‘*’. $system_folder = dirname(__FILE__) . and edit it as follows: At line 26. Then take the application folder out of the system folder. as seen in the image above. $application_folder = dirname(__FILE__) ./codeigniter/system'. 1. If you do not specify any file types. $this->load->library('upload'). '. '.

Create file: application/libraries/My_Upload. 12. 'jpe'). 30. 13. } $image_types = array('gif'. if (is_array($mime)) . 21. 5. class MY_Upload extends CI_Upload { 2. 11. $this->allowed_types)) { return TRUE. 23. } } 20. 17. 31. foreach ($this->allowed_types as $val) { $mime = $this->mimes_types(strtolower($val)). 25. 8. if (in_array("*". 22. 19. 'jpeg'. $this->load->library('upload'). } 7. 15. 'png'. 2. 14. 24. $this->load->library('upload'). 16. 3. $this->upload->set_allowed_types('*'). 'jpg'. 18. function is_allowed_filetype() { if (count($this->allowed_types) == 0 OR ! is_array($this>allowed_types)) { $this->set_error('upload_no_file_types'). 3. 9.php 1.1. 27. return FALSE. 26. 28. 4. 6. 29. The Hack For this hack we are going to modify the Upload class behavior. $image_types)) { if (getimagesize($this->file_temp) === FALSE) { return FALSE. $this->upload->set_allowed_types('*'). 10. // Images get some additional checks if (in_array($val.

'jpeg'. 46. $mime. $image_types)) { if (getimagesize($this->file_temp) === FALSE) { return FALSE. 42. 48. 50. 45. 41. 36. } 33. return FALSE. 40. } if (in_array("*". 35. 'png'. TRUE)) { return TRUE. 39. } } else { if ($mime == $this->file_type) { return TRUE. 37. 34. foreach ($this->allowed_types as $val) { $mime = $this->mimes_types(strtolower($val)). 49.32. } } . // Images get some additional checks if (in_array($val. 47. 44. 43. 'jpe'). $this->allowed_types)) { return TRUE. 51. 'jpg'. { if (in_array($this->file_type. 38. } } } return FALSE. } $image_types = array('gif'. } class MY_Upload extends CI_Upload { function is_allowed_filetype() { if (count($this->allowed_types) == 0 OR ! is_array($this->allowed_types)) { $this->set_error('upload_no_file_types').

If not. } } } return FALSE. Thank you! . If you know any other cool hacks or modifications. they are still interesting to know and can help you learn more about the internal workings of a framework and some of the core PHP language features.if (is_array($mime)) { if (in_array($this->file_type. TRUE)) { return TRUE. let us know in the comments. $mime. } } Conclusion I hope some of these are useful to you. } } else { if ($mime == $this->file_type) { return TRUE.

Sign up to vote on this title
UsefulNot useful