Advertisement
PHP

6 CodeIgniter Hacks for the Masters

by

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

Related Posts
  • Code
    Web Development
    Laravel Unwrapped: Session, Auth and CacheLaravel wide retina preview
    Join me as we learn how to use Laravel's component-based system, Illuminate. Additionally, we'll see how to utilize service providers, Laravel's manager system, the Session, Auth, and Cache components, and the Store, Guard, and Repository libraries.Read More…
  • Code
    PHP
    Setting Up a Local Mirror for Composer Packages With SatisComposer retina preview
    Installing all your PHP libraries with Composer is a great way to save time. But larger projects automatically tested and run at each commit to your software version control (SVC) system will take a long time to install all the required packages from the Internet. You want to run your tests as soon as possible through your continuous integration (CI) system so that you have fast feedback and quick reactions on failure. In this tutorial we will set up a local mirror to proxy all your packages required in your project's composer.json file. This will make our CI work much faster, install the packages over the local network or even hosted on the same machine, and make sure we have the specific versions of the packages always available.Read More…
  • Code
    PHP
    Building a Customer Management App Using AngularJS and LaravelLaravel 4 auth retina preview
    When creating a single-page app we should use some kind of framework to do some of the job for us, so we can focus on the actual functionality. AngularJS fits here perfectly, because features like dynamic dependency injection and bi-directional data binding are just great. Sometimes we also require some kind of server. If you've chosen PHP then Laravel may be your best option, as it's easy to work with and pretty powerful.Read More…
  • Code
    PHP
    Creating a Photo Tag Wall With Twilio Picture Messaging & PHPProcedural to oop php retina preview
    Twilio's recently announced Picture Messaging has vastly opened up what we can do with text messaging, now we can attach photos to our text messages and have them get used in different ways. In our case, we are going to build a Photo Tag Wall, which will contain photos linked to tags that will be displayed on a website.Read More…
  • Code
    PHP
    Building a CodeIgniter Web Application From Scratch - Part 1Web app retina preview
    In this series we're going to build a web billboard application from scratch, we're going to use CodeIgniter to handle the back-end service and BackboneJS for the web client. In the first two parts of the series we'll create the back-end service and then the client application in the last two.Read More…
  • Code
    PHP
    Diving Into Symfony 2Symfony2 retina preview
    Frameworks are hot topics in the web-industry and have been for some time. In this vast sea of flavors is Symfony - an extensive PHP framework that follows the ever popular MVC paradigm. Its learning curve is probably a little steeper than its fellow competitors, like CodeIgniter. Don't worry, once it grows on you, you'll feel more powerful than ever and you'll be able to develop fantastic applications. Read More…