Tag Archives: programming - Page 3

CakePHP mod_rewrite optimization

CakePHP is great, but the default installation has a performance issue that is very easy to resolve, and should be fixed in any production installation of a CakePHP application. The solution is very simple.

In the default install, there is a file in the webroot called .htaccess. It contains mod_rewrite rules, and you have to set you vhost up to allow overrides. This introduces a significant overhead to each request, as apache has to search for applicable htaccess files and merge their settings into the core apache config every request. On a production application this can become significant, especially if you get a significant amount of traffic.

Read more »

CakePHP Shells, and Cron Jobs

As part of the same project that resulted in my last posting, I needed to write several processes that would be managed by cron. In the past, at the Yahoo contract, we had come up with a way to do this involving a customized version of the basic CakePHP index.php file. However, his required us to place the cron code on the main controllers, and extra protections to ensure that the code was inaccessible from the main web interface. I was never thrilled with the solution, but it was the best we had at the time.

So for this project, I looked into the concept of ‘Shells’, which are basically command-line interfaces to a CakePHP application. It quickly became obvious that this was the best way to go about handling the cron jobs for a CakePHP application. It allows you to place all your sensitive cron code completely out of reach of the web facing part of the application, increasing security and decreasing complexity. It’s really quite simple as well.

The process is basically outlined in this section of the CakePHP manual, and should be simple enough to follow. The end result is code like this:

class BillingShell extends Shell {
	var $uses = array('OrderLineItem', 'Division');
 
	function main() {
		echo "Please specify which billing task to run. Currently, the only choice is 'daily'.\n\n";
	}
 
	function daily() {
		date_default_timezone_set('America/Denver');
		App::Import('Component','Cbg');
		$this->Cbg = new CbgComponent();
 
		$this->create_invoices();
		$this->process_invoices();
	}
 
	// Other functions not shown
}

And you can run it as a cron like this:

# m h  dom mon dow   command
*/5 * * * * /full/path/to/cakeshell billing daily -app /full/path/to/app

Automatic DB Field Encryption in CakePHP

UPDATE: I’ve posted an improved version of this behavior here

I’ve written the following behavior for a project I recently completed in Cake, and I thought it would be worth sharing:

class CryptableBehavior extends ModelBehavior {
	var $settings = array();
 
	function setup(&$model, $settings) {
		if (!isset($this->settings[$model->alias])) {
			$this->settings[$model->alias] = array(
				'fields' => array()
			);
		}
 
		$this->settings[$model->alias] = array_merge($this->settings[$model->alias], $settings);
	}
 
	function beforeFind(&$model, $queryData) {
		foreach ($this->settings[$model->alias]['fields'] AS $field) {
			if (isset($queryData['conditions'][$model->alias.'.'.$field])) {
				$queryData['conditions'][$model->alias.'.'.$field] = $this->encrypt($queryData['conditions'][$model->alias.'.'.$field]);
			}
		}
		return $queryData;
	}
 
	function afterFind(&$model, $results, $primary) {
		foreach ($this->settings[$model->alias]['fields'] AS $field) {
			if ($primary) {
				foreach ($results AS $key => $value) {
					if (isset($value[$model->alias][$field])) {
						$results[$key][$model->alias][$field] = $this->decrypt($value[$model->alias][$field]);
					}
				}
			} else {
				if (isset($results[$field])) {
					$results[$field] = $this->decrypt($results[$field]);
				}
			}
		}
 
		return $results;
	}
 
	function beforeSave(&$model) {
		foreach ($this->settings[$model->alias]['fields'] AS $field) {
			if (isset($model->data[$model->alias][$field])) {
				$model->data[$model->alias]['cleartext_'.$field] = $model->data[$model->alias][$field];
				$model->data[$model->alias][$field] = $this->encrypt($model->data[$model->alias][$field]);
			}
		}
		return true;
	}
 
	public function encrypt($data) {
		if ($data !== '') {
			return base64_encode(mcrypt_encrypt(Configure::read('Cryptable.cipher'), Configure::read('Cryptable.key'), $data, 'cbc', Configure::read('Cryptable.iv')));
		} else {
			return '';
		}
	}
 
	public function decrypt($data, $data2 = null) {
		if (is_object($data)) {
			unset($data);
			$data = $data2;
		}
 
		if ($data != '') {
			return trim(mcrypt_decrypt(Configure::read('Cryptable.cipher'), Configure::read('Cryptable.key'), base64_decode($data), 'cbc', Configure::read('Cryptable.iv')));
		} else {
			return '';
		}
	}
}

All you need to do is add three lines to your bootstrap, and then load the behavior in any model you want to use it.

Here are the lines for your bootstrap:

Configure::write('Cryptable.cipher', 'rijndael-192');
Configure::write('Cryptable.key','random key string here');
Configure::write('Cryptable.iv', base64_decode('base64 encoded IV here')); // Create with mcrypt_create_iv with the appropriate size for your cipher

Here’s an example of how to load it in your model:

	var $actsAs = array(
		'Cryptable' => array(
			'fields' => array(
				'password'
			)
		)
	);

If you need to encrypt or decrypt a field outside of the normal find methods, you can simply call those methods on the model, passing in the string that needs worked on.

Download Cyptable Behavior

CakePHP + FirePHP

I was poking through the addons for Firebug today, and liked the look of FirePHP, so I started looking into how I could use it. As many of you know, I’m a big fan of CakePHP as a framework, so I looked up how to integrate the two together and make them work nicely. I found this guide on the Cake Bakery, and followed it with a few modifications, so here they are. Read more »

CakePHP: Paginate with Deep Associations

Got some CakePHP models with deep associations that you need to pull into the data that you display when you paginate them? This used to be a royal pain in the butt, and pretty much required custom pagination, but no more!

Behold:

$this->paginate = array(
    'Customer' => array(
        'contain' => array(
            'Order(id)',
            'Order.AccessCode(id)',
            'ShippingAddress(city,state)'
        )
    )
);
$this->set('customers', $this->paginate('Customer'));

All you need to do for this to work? Make sure you have your associations defined, and add the Containable behavior to each of the models in question. It works flawlessly, and can go as deep as you need, and retrieve just the fields you need. I haven’t tested sorting on deep relationships yet, but I don’t think that works, because of the way Cake does its queries. But still, this is better than nothing.