Try Tuts+ Premium, Get Cash Back!
CodeIgniter Hacks

6 CodeIgniter Hacks for the Masters

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.

Disclaimer

  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 object types. This enables IDE features such as auto-complete, or previewing documentation.

There are two more side effects of this hack. First, 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.

The Hack

All we need to do is add a PHP 5 style autoloader function.

Add this code to the bottom of system/application/config/config.php:

<?php
// ...

function __autoload($class) {
	if (file_exists(APPPATH."models/".strtolower($class).EXT)) {
		include_once(APPPATH."models/".strtolower($class).EXT);
	}
}
?>

If you are interested in applying this hack for controllers too, you can use this code instead:

<?php
// ...

function __autoload($class) {
	if (file_exists(APPPATH."models/".strtolower($class).EXT)) {
		include_once(APPPATH."models/".strtolower($class).EXT);
	} else if (file_exists(APPPATH."controllers/".strtolower($class).EXT)) {
		include_once(APPPATH."controllers/".strtolower($class).EXT);
	}
}
?>

Any time you try to use a class that is not defined, this __autoload function is called first. It takes care of loading the class file.

2. Prevent Model-Controller Name Collision

The Goal

Normally, you can not have the same class name for a Model and a Controller. Let’s say you created a model name Post:

class Post extends Model {

	// ...

}

Now you can not have a URL like this:


http://www.mysite.com/post/display/13

The reason is because that would require you to also have a Controller class named ‘Post.’ Creating such a class would result in a fatal PHP error.

But with this hack, it will become possible. And the Controller for that URL will look like this:

// application/controllers/post.php

class Post_controller extends Controller {

	// ...

}

Note the ‘_controller’ suffix.

The Hack

To get around this issue, normally most people add the ‘_model’ suffix to the Model class names (eg. Post_model). Model objects are created and referenced all over the application, so it might seem a bit silly to have all of these names with ‘_model’ floating around. I think it is better to add a suffix to the Controllers instead, since they are almost never referenced by their class names in your code.

First we need to extend the Router class. Create this file: “application/libraries/MY_Router.php”

class MY_Router extends CI_Router {
	var $suffix = '_controller';

	function MY_Router() {
		parent::CI_Router();
	}

	function set_class($class) {
		$this->class = $class . $this->suffix;
	}

	function controller_name() {

		if (strstr($this->class, $this->suffix)) {
			return str_replace($this->suffix, '', $this->class);
		}
		else {
			return $this->class;
		}

	}
}

Now edit “system/codeigniter/CodeIgniter.php” line 153:

if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->controller_name().EXT))

Same file, line 158:

include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->controller_name().EXT);

Next, edit: “system/libraries/Profiler.php”, line 323:

$output .= "
".$this->CI->router->controller_name()."/".$this->CI->router->fetch_method()."
";

That is all. Keep in mind that with this hack you are required to put the ‘_controller’ suffix on all of your controller class names. But not in the file names or the URL’s.

3. Form Validation for Unique Values

The Goal

CodeIgniter has a nice Form_validation class. It comes with several validation rules:

These are useful, but there is an important one missing from this list: to check for unique values. For example, most user registration forms need to check that the username is not already taken, or the e-mail address is not already in the system.

With this hack, you will be able add this validation rule to your form submission handler very easily:

$this->form_validation->set_rules('username', 'Username',
		'required|alpha_numeric|min_length[6]|unique[User.username]');

Note the last part that says “unique[User.username].” This new validation rule is just called “unique,” and takes a parameter inside the square brackets, which is “tablename.fieldname”. So it will check the “username” column of the “User” table to make sure the submitted value does not already exist.

Similarly, you can check for duplicate e-mails:

$this->form_validation->set_rules('email', 'E-mail',
		'required|valid_email|unique[User.email]');

And your application can respond with proper error messages:

The Hack

This might be considered more of an extension than a hack. Nevertheless, we are going to take a core CodeIgniter library and improve it.

Create: “application/libraries/MY_Form_validation.php”

class MY_Form_validation extends CI_Form_validation {

	function unique($value, $params) {

		$CI =& get_instance();
		$CI->load->database();

		$CI->form_validation->set_message('unique',
			'The %s is already being used.');

		list($table, $field) = explode(".", $params, 2);

		$query = $CI->db->select($field)->from($table)
			->where($field, $value)->limit(1)->get();

		if ($query->row()) {
			return false;
		} else {
			return true;
		}

	}
}

Now you can use the unique validation rule.

4. Running CodeIgniter from the Command Line

The Goal

Just like the title says, our goal is to be able to run CodeIgniter applications from the command line. This is necessary for building cron jobs, or running more intensive operations so you don’t have the resource limitations of a web script, such as maximum execution time.

This is what it looks like on my local Windows machine:

The above code would be like calling this URL:


http://www.mysite.com/hello/world/foo

The Hack

Create a “cli.php” file at the root of your CodeIgniter folder:

if (isset($_SERVER['REMOTE_ADDR'])) {
	die('Command Line Only!');
}

set_time_limit(0);

$_SERVER['PATH_INFO'] = $_SERVER['REQUEST_URI'] = $argv[1];

require dirname(__FILE__) . '/index.php';

If you are on a Linux environment and want to make this script self executable, you can add this as the first line in cli.php:

#!/usr/bin/php

If you want a specific controller to be command line only, you can block web calls at the controller constructor:

class Hello extends Controller {

	function __construct() {
		if (isset($_SERVER['REMOTE_ADDR'])) {
			die('Command Line Only!');
		}
		parent::Controller();
	}

	// ...

}

5. Adding Doctrine ORM to CodeIgniter

The Goal

Doctrine is a popular Object Relational Mapper for PHP. By adding it to CodeIgniter, you can have a very powerful Model layer in your framework.

The Hack

Just installing Doctrine is not very “hacky” per se, as we can just add it as a plug-in. However, once added, your Model classes will need to extend the Doctrine base classes, instead of the CodeIgniter Model class. This will completely change the way the Model layer works in the framework. The objects you create will have database persistence and also will able to have database relationships with other objects.

Follow these steps:

  1. Create folder: application/plugins
  2. Create folder: application/plugins/doctrine
  3. Download Doctrine (1.2 as of this article)
  4. Copy the “lib” folder from Doctrine to: “application/plugins/doctrine”
  5. Create “application/plugins/doctrine_pi.php”
// system/application/plugins/doctrine_pi.php

// load Doctrine library
require_once APPPATH.'/plugins/doctrine/lib/Doctrine.php';

// load database configuration from CodeIgniter
require_once APPPATH.'/config/database.php';

// this will allow Doctrine to load Model classes automatically
spl_autoload_register(array('Doctrine', 'autoload'));

// 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'] .
		'://' . $db[$connection_name]['username'] .
		':' . $db[$connection_name]['password'].
		'@' . $db[$connection_name]['hostname'] .
		'/' . $db[$connection_name]['database'];

	Doctrine_Manager::connection($dsn,$connection_name);
}

// CodeIgniter's Model class needs to be loaded
require_once BASEPATH.'/libraries/Model.php';

// telling Doctrine where our models are located
Doctrine::loadModels(APPPATH.'/models');

Next, edit “application/config/autoload.php” to autoload this Doctrine plugin

$autoload['plugin'] = array('doctrine');

Also make sure you have your database configuration in “application/config/database.php”.

That is all. Now you can create Doctrine Models within your CodeIgniter application. Read my tutorials on this subject for more information.

6. Running Multiple Sites

The Goal

This hack will make it possible for you to run multiple sites from a single install of CodeIgniter. Each website will have its own application folder, but they will all share the same system folder.

The Hack

Install CodeIgniter anywhere on the server. It doesn’t need to be under a website folder. Then take the application folder out of the system folder. And make additional copies of it, as seen in the image above, for every website you want to run. You can place those application folders anywhere, like under each separate website folders.

Now copy the index.php file to the root of each website folder, and edit it as follows:

At line 26, put the full path to the system folder:

	$system_folder = dirname(__FILE__) . '../codeigniter/system';

At line 43, put the full path to the application folder:

	$application_folder = dirname(__FILE__) . '../application_site1';

Now you can have independent websites using separate application folders, but sharing the same system folder.

There is a similar implementation in the CodeIgniter User Guide you can read also.

7. Allowing All File Types for Uploads

The Goal

When using the Upload library in CodeIgniter, you must specify which file types are allowed.

$this->load->library('upload');

$this->upload->set_allowed_types('jpg|jpeg|gif|png|zip');

If you do not specify any file types, you will receive an error message from CodeIgniter: “You have not specified any allowed file types.”

So, by default, there is no way to allow all file types to be uploaded. We need to do small hack to get around this limitation. After that we will be able to allow all file types by setting it to ‘*’.

$this->load->library('upload');

$this->upload->set_allowed_types('*');

The Hack

For this hack we are going to modify the Upload class behavior.

Create file: application/libraries/My_Upload.php

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');
			return FALSE;
		}

		if (in_array("*", $this->allowed_types))
		{
			return TRUE;
		}

		$image_types = array('gif', 'jpg', 'jpeg', 'png', 'jpe');

		foreach ($this->allowed_types as $val)
		{
			$mime = $this->mimes_types(strtolower($val));

			// Images get some additional checks
			if (in_array($val, $image_types))
			{
				if (getimagesize($this->file_temp) === FALSE)
				{
					return FALSE;
				}
			}

			if (is_array($mime))
			{
				if (in_array($this->file_type, $mime, TRUE))
				{
					return TRUE;
				}
			}
			else
			{
				if ($mime == $this->file_type)
				{
					return TRUE;
				}
			}
		}

		return FALSE;

	}

}

Conclusion

I hope some of these are useful to you. If not, 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 you know any other cool hacks or modifications, let us know in the comments. Thank you!

Ready to take your skills to the next level, and start profiting from your scripts and components? Check out our sister marketplace, CodeCanyon.

CodeCanyon

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://ideecadeauquebec.com Matt

    You also can cronjob codeigniter with Lynx for example, it’s a text web browser so you can call the URL you wish and do it as a cron job.

    That is usefull in some case.
    But your way is better :)

  • Dexter

    That hack that allows you to load models with the php 5 syntax only works if you have your controllers in the base “models” directory. If you have any subdirectories, it fails.

  • michal

    foa, Burak, thanks a lot for great CI series. very appreciated.
    I was wondering though, how to make an apache rule to rewrite url when I am using multiple applications sharing one CI core (ie. hack no.6).
    TIA.
    best,

  • http://thecodeabode.blogspot.com Ben Kitzelman

    For anyone who is interested – I’ve posted up a project starter on my blog – a dev ready incorporation of the following technologies:

    - ExtJS : client side JS library,
    - CodeIgniter : presentation + domain tier,
    - Doctrine : ORM data layer framework

    Some features of this project starter are:
    - CodeIgniter Models have been replaced with Doctrine Records
    - Doctrine is loaded into CI as a plugin
    - RoR type before and after filters….
    - Doctrine transactions automatically wrapped around every action at execution time (ATOMIC db updates)

    Basic Role based security (I think Redux may be in there as well?)
    Simply extract, hook up the database.php config file and viola…. You can start coding your layouts, views and models. Probably a few things to iron out – but enjoy!

    Hope it helps

    GET IT AT: http://thecodeabode.blogspot.com/2010/06/php-codeigniter-doctrine-and-extjs.html

  • tilmankoester

    Re #5:

    // CodeIgniter's Model class needs to be loaded  
    require_once BASEPATH.'/libraries/Model.php';
    

    Why? What do you use the ci Model class for?

  • http://puworlddiscovery.info/ Homer Corlee

    This is really among the far better content articles of those who I’ve got continue reading that subject matter as of late. Good do the job.

  • Gazelle

    The unique function is a great extension but it will fail, if you update an existing record.

    It should take into consideration the id of the row being processed.

    • marceloabelda

      Yes sir, you’re right.

  • http://www.rebatesense.com RebateSense

    Burak – great tut, especially the Command Line hack. Very handy!

  • http://www.jsxtech.com Jaspal Singh

    Good Hacks, sure, give it a try.
    Thanks for sharing.

  • jhoney

    Really Good Hacks, it help me out alot…

    Thanks & waiting for some others….

  • http://maxdegterev.name/ Max

    A bit of improvement to the third hack

    function unique($value, $params) {
    $this->CI->load->database();

    $this->CI->form_validation->set_message(‘unique’, ‘The %s is already being used.’);

    list($table, $field) = explode(“.”, $params, 2);

    $row = $this->CI->db->
    select($field)->
    where($field, $value)->
    limit(1)->
    get($table)->
    row();

    if ($row) {
    return FALSE;
    } else {
    return TRUE;
    }
    }

  • http://www.geeks-online.de Geek

    You do not need the include_once function. include or require is enough because when your autoload function is called you know that the class has not been loaded yet. Using require_once or include_once is just a performance waste here.

  • Iulian

    I saw that phpandstuff.com its not online anymore.
    Any clue?
    Thanks.
    PS : Great tutorials

  • V

    For #3, how can I check if username exists?

  • http://bluefuton.com christophe

    Re: tip 2. To avoid controller and model name collision easily, just make your model names singular and your controller names plural (model Post and controller Posts). This fits well with RESTful routing conventions too (http://www.example.com/posts/32).

  • http://www.marcomonteiro.net Marco Monteiro

    This is awesome… Even If I already knew most of these hacks… This is still a great compilation. Cheers!

  • Jubic

    The 3rd tip doesn’t seem to work anymore.

  • Shadow Caster

    #1 Thanks for this! It’s going to save me doing quite a lot of unnecessary coding.

  • Reconix

    Ah man, just gave me that burst of enthusiasm to do and do some thing in codeigniter again !

    Thanks, even years later !

  • anonimo

    The hacks no work in current version of codeigniter

  • http://yablog.org.ua yAnTar

    Hack with adding suffix to controller great. I needed that.
    Thanks.

  • http://yablog.org.ua/ yAnTar

    Second hack is helpful for me.
    Thanks.

  • http://bertcompton.wordpress.com/ Gray Powell

    That is really a awesome and binifit product of details. I’m pleased that you generally assigned this useful details with us. Please keep us recommended like this. Thank you for referring to about design Development.

  • Ahmed Samy

    impressive :D

  • zeeHtaa

    Would be great if there could come an update of this tutorials for a more recent version of Codeigniter.

  • http://rizki.info/ rizki ms

    really good tutorials..thanks

  • Atish Mahadik

    Its really awesome, I am fresher in CI, so its really helpful to me. Thanks to all CI seniors & requesting you all CI Seniors to post updates or new things which will helpful to fresher like me.

    Its hearty request please support me to build my carrier in CI

    My email id is atishmahadik@rediffmail.com

    Atish Mahadik

  • ya vo