PHP Archive

1

Extending the CodeIgniter Database Active Record class, the non-hacky way

The past few weeks I’ve been busy working with CodeIgniter. If you don’t know what CodeIgniter is, it’s basically a lightweight and open source PHP web application framework. If you’re working with PHP applications, I suggest you check it out.

One of the best features about CodeIgniter (besides being lightweight and open source) is that you can basically extend all the core classes and substitute them for your own. This makes it a highly customizable framework.

However, one core concept you cannot extend – according to the documentation, are the database classes. I wanted to extend the Active Record database class to include custom functions like insert_delayed() and insert_update() (‘INSERT INTO … ON DUPLICATE KEY UPDATE …’, see MySQL docs here). Previous attempts from other people required altering the system files (aka ‘hacking’), instead of the application files, which is ugly and doesn’t last across framework updates.

However, thanks to the articles I found here and here, I came up with my own solution.

This extension requires 1) a custom Loader class, 2) a custom DB function and 3) a custom DB_active_record class. Using our custom Loader, we load our custom DB function, which in turn is used to load our custom DB_active_record class. Simple, no?

application/core/MY_Loader.php

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
 
class MY_Loader extends CI_Loader {
	public function database($params = '', $return = FALSE, $active_record = NULL)
	{
		// Grab the super object
		$CI =& get_instance();
 
		// Do we even need to load the database class?
		if (class_exists('CI_DB') AND $return == FALSE AND $active_record == NULL AND isset($CI->db) AND is_object($CI->db))
		{
			return FALSE;
		}
 
		// Check if custom DB file exists, else include core one
		if (file_exists(APPPATH.'core/'.config_item('subclass_prefix').'DB'.EXT))
		{
			require_once(APPPATH.'core/'.config_item('subclass_prefix').'DB'.EXT);
		}
		else
		{
			require_once(BASEPATH.'database/DB'.EXT);
		}
 
		if ($return === TRUE)
		{
			return DB($params, $active_record);
		}
 
		// Initialize the db variable. Needed to prevent
		// reference errors with some configurations
		$CI->db = '';
 
		// Load the DB class
		$CI->db =& DB($params, $active_record);
	}
}
 
/* End of file MY_Loader.php */
/* Location: ./application/core/MY_Loader.php */

Copy the contents from system/database/DB.php to application/core/MY_DB.php and look for the following:

require_once(BASEPATH.'database/DB_driver'.EXT);
 
if ( ! isset($active_record) OR $active_record == TRUE)
{
	require_once(BASEPATH.'database/DB_active_rec'.EXT);
 
	if ( ! class_exists('CI_DB'))
	{
		eval('class CI_DB extends CI_DB_active_record { }');
	}
}

Change it to the following:

require_once(BASEPATH.'database/DB_driver'.EXT);
 
if ( ! isset($active_record) OR $active_record == TRUE)
{
	require_once(BASEPATH.'database/DB_active_rec'.EXT);
 
	// get the CI instance
	$CI = & get_instance();
	$prefix = $CI->config->item('subclass_prefix');
 
	if (file_exists(APPPATH.'core/'.$prefix.'DB_active_rec'.EXT))
	{
		require_once(APPPATH.'core/'.$prefix.'DB_active_rec'.EXT);
		if ( ! class_exists('CI_DB'))
		{
			eval('class CI_DB extends '.$prefix.'DB_active_record { }');
		}
	} 
	else 
	{
		if ( ! class_exists('CI_DB'))
		{
			eval('class CI_DB extends CI_DB_active_record { }');
		}
	} 
}

(Thanks to robotslacker.com for the article on this one.)

Then, create your application/core/MY_DB_active_rec.php file:

<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
 
class MY_DB_active_record extends CI_DB_active_record
{
	public function foo()
	{
		echo 'foobar';
	}
}
 
/* End of file MY_DB_active_rec.php */
/* Location: ./application/core/MY_DB_active_rec.php */

And there you have it. A custom CodeIgniter active record database class, the non-hacky way. :) Once I come up with some good functions for ‘INSERT DELAYED’ and ‘INSERT INTO … ON DUPLICATE KEY UPDATE …’, I’ll post them.

2

WordPress fix for MySQL5 strict

If you’ve ever tried installing WordPress on a MySQL5-server where the SQL-mode includes either NO_ZERO_DATE or TRADITIONAL (aka ‘strict’ mode), you will be greeted by a whole lot of errors upon trying to complete the installation.

Firstly, let’s look at MySQL’s definition of the TRADITIONAL SQL-mode:

Make MySQL behave like a “traditional” SQL database system. A simple description of this mode is “give an error instead of a warning” when inserting an incorrect value into a column.

Source

Definition of NO_ZERO_DATE:

In strict mode, do not permit '0000-00-00' as a valid date. You can still insert zero dates with the IGNORE option. When not in strict mode, the date is accepted but a warning is generated. (Added in MySQL 5.0.2)

Source

This pretty much fails the WordPress installation, since a whole lot of database tables aren’t being created, since they include errors according to the server’s SQL standard.

WordPress database error: [Invalid default value for 'comment_date']
CREATE TABLE wp_comments ( ...

WordPress uses quite a bit of ‘zero dates’ (dates consisting of 0000-00-00), which conflicts with the NO_ZERO_DATE constraint, which is included in the TRADITIONAL sql mode.

Most webhosters don’t use these kind of server settings. But for those that do (including this website), I have a very simple solution that fixes it. The only things you are required to do is be extra cautious about updating the WordPress installation (not plugins, only WP itself), since you need to manually add a line of code after each update. Be sure to make backups before updating (just to be safe) and ask your hosting provider if you’re allowed to ‘change the session sql-mode’ .

Note: This fix is for WordPress 3.1. Older versions require a slightly different approach, explained below.

After uploading your WordPress to your (restrictive) server, before installation, open your favorite FTP-client and edit file ‘/wp-includes/wp-db/php’. Find the current line:

$this-&gt;select( $this-&gt;dbname, $this-&gt;dbh );

This should be on or around line number 1065. Simply replace that with the following code:

mysql_query( "SET SESSION SQL_MODE := ''", $this-&gt;dbh );
$this-&gt;select( $this-&gt;dbname, $this-&gt;dbh );

After this, you can safely run the WordPress 3.1 installation and use it to your heart’s content, like you normally would. For WordPress versions older than 3.1, you need to call the ‘SET SESSION SQL_MODE’ query before each call to $this->select() in the code.