Setting up cron jobs with Cake shells

This post will primarily be about the process I had to go through to get cron jobs and shells working on Dreamhost. The main problem I kept running into was that the shells would simply not work at all through a cron job; yet they worked when I manually did it through SSH. The weird thing was the source of the console/cake file was being printed in my logs, instead of executed. Below is a quick checklist of things to look out for:

TERM environment variable not set

This isn't really an error, but the TERM variable is used by the server environment and does not affect the shell, even if the error is thrown. Now I may not know much about server maintenance and configuration, but adding the following code to console/cake seemed to do the trick (Hats off to Matt Curry for the tip). And of course, you should change linux to whatever your server is.

TERM=linux
export TERM
Cron environment not PHP 5

This was a weird problem I ran into. It seemed the environment in which my crons ran was PHP 4, so I was receiving unexpected results and would receive this error (it mainly depends where the php folder is located and if its loaded correctly).

console/cake: line 30: exec: php: not found

To fix this, I had to rewrite the cake console file and update it with the path to the PHP folder (path should represent your servers structure). You should only need to do this if you can't upgrade PHP to 5 for some reason.

exec php -q ${LIB}cake.php -working "${APP}" "$@"
# change to
exec /usr/local/php5/bin/php -q ${LIB}cake.php -working "${APP}" "$@"
Making sure your cron has ownership

A simple problem with a simple fix. Since the user that uploaded the console files was different than the user running the cron, I would receive this error:

sh: cake/console/cake: Permission denied

All I had to do was switch the cron user to the user that owns the file, and it fixed the permissions problem.

Making sure the cake file uses unix line endings

This was another pain in the ass that took me forever to get working. My cron environment would throw this error once I fixed the things above (this applies to my Dreamhost server and may not apply to all).

sh: cake/console/cake: /bin/bash^M: bad interpreter: No such file or directory

It required me contacting my host and asking for help on this one. I tried saving all my files as unix and made sure my FTP was not converting them in any way. Still nothing. So my host told me about this SSH command called dos2unix which would convert the file to unix line endings, and wallah, magic! Everything seemed to be working now.

dos2unix cake/console/cake

These are just a few of the minor setbacks I had to deal with when setting up my cron jobs. Most of this really applies to my Dreamhost server, as my Media Temple server worked right away with no configuration. Furthermore, here are some more Dreamhost articles that I used for this problem, that might be of aid:

Editing Your Environment Profile
Make PHP5 the Default in the Shell

Converting a SimpleXML object to an array

This functionality can now be found within the Titon Utility library.

If you have been following my Twitter, you would of heard me complaining about converting a SimpleXML object into an array. I am still having that problem, so if you can get it working correctly (my test so far below), I would be greatly appreciative. If you have never used the SimpleXML object, it can be quite awesome when actually reading an XML document - but once it comes to converting it to something else, it comes straight from the darkest depths of hell. Every property of the object, is also a SimpleXML object, so on and so forth. Each property/object has a method children(), which returns more properties, or attributes() which returns attributes; weirdly enough, children() also return attributes. Furthermore, you can't just echo the object out to get a value, you have to turn it into a string. You can see where this can get quite difficult and confusing, as it always spits out data your not expecting.

After countless hours, I was able to get it to properly convert to an array... about 95% of the time... while keeping attributes and parent/children hierarchy. The only scenario where it doesn't convert properly, is when you have nodes within a node that has attributes (which is kind of rare in my opinion). Here's a small little example:

// Works just fine
<root>
	<node foo="bar">I'm a node!</node>
</root>
// Does not work
<root>
	<node foo="bar">
		<childNode>I'm here to make your life miserable!</childNode>
		<childNode>Me too!</childNode>
	</node>
</root>

Besides that little instance, I am able to properly turn an XML document with attributes, and multiple nodes with the same name, all into a perfectly replicated array. Here is the code I wrote to achieve such an amazing task (sarcasm).

/**
 * Convert a SimpleXML object into an array (last resort).
 *
 * @access public
 * @param object $xml
 * @param boolean $root - Should we append the root node into the array
 * @return array
 */
public function xmlToArray($xml, $root = true) {
	if (!$xml->children()) {
		return (string)$xml;
	}
	$array = array();
	foreach ($xml->children() as $element => $node) {
		$totalElement = count($xml->{$element});
		if (!isset($array[$element])) {
			$array[$element] = "";
		}
		// Has attributes
		if ($attributes = $node->attributes()) {
			$data = array(
				'attributes' => array(),
				'value' => (count($node) > 0) ? xmlToArray($node, false) : (string)$node
				// 'value' => (string)$node (old code)
			);
			foreach ($attributes as $attr => $value) {
				$data['attributes'][$attr] = (string)$value;
			}
			if ($totalElement > 1) {
				$array[$element][] = $data;
			} else {
				$array[$element] = $data;
			}
		// Just a value
		} else {
			if ($totalElement > 1) {
				$array[$element][] = xmlToArray($node, false);
			} else {
				$array[$element] = xmlToArray($node, false);
			}
		}
	}
	if ($root) {
		return array($xml->getName() => $array);
	} else {
		return $array;
	}
}

I know exactly where the problem resides also. Its the value index of the $data array. (The little bastard below).

$data = array('attributes' => array(), 'value' => (string)$node);
// Should be
$data = array('attributes' => array(), 'value' => xmlToArray($node, false));

A simple fix right? Nope! When you do that, it totally breaks... for some reason. The first line of the function (!$xml->children()) gets passed since the element passed does have children, since it has attributes; now I can never understand why attributes count as children when you have attributes(). I tried many different conditionals to get it working, I tried unsetting the attributes (but can't determine the property), and all these other routes... but to no avail. But I digress, seeing as how it works 95% of the time, and the case it doesn't work isn't used that much. However, if you can figure it out, I will be in your debt forever.

The fate of class suffixes in 5.3

Before PHP 5.3 was released, many programmers would suffix their classnames with the package and or module that it pertains to. For example, SessionHelper, UsersController, CommentModel, so on and so forth. But with the recent upgrade to namespaces, are these types of suffixes still required?

// Current Practice
SessionHelper();
// Namespace Practice
\app\libs\helpers\Session();
// Redundant Practice
\app\libs\helpers\SessionHelper();

That's one of many dilemma's I am running into right now when dealing with past practices and theories, and applying them to newer code. Namespaces make the suffix redundant as the package is now part of the class name (unless you are aliasing the namespace). The only deal breaking reason I can think of right now as to keep suffixes; is when you are editing multiple files (or browsing a folder), as it helps identify files from each other. But on the other hand, how many times are you going to have multiple files opened name User? Maybe twice for the model and controller.

I'm mainly posting this entry to get input from everyone else and what you think the approach should be. Right now I'm going down the path of not suffixing my classes, as the similar file name is not that much of a problem or hindrance to me. If you have any convincing arguments, let me hear them (especially since this will apply to my newer scripts)!