Prism PHP Support

If you follow me on GitHub you may have noticed that I forked and committed quite a bit to the wonderful Prism JS project by Lea Verou. If you don't know what Prism is, it's a lightweight syntax highlighter written in JavaScript. It's what powers the syntax highlighting for my code blocks.

My major contribution to the project was full PHP highlighting support (including 5.3 and 5.4), as well as some improvements to the C-Like language and the inclusion of the CSS extras language. Take a look at the PHP snippet below which is taken from my Titon\Model project.

/**
 * @copyright	Copyright 2010-2013, The Titon Project
 * @license		http://opensource.org/licenses/bsd-license.php
 * @link		http://titon.io
 */
namespace Titon\Model;
use Titon\Common\Base;
use Titon\Common\Traits\Attachable;
use Titon\Model\Relation;
class Model extends Base {
	use Attachable;
	protected $_config = [
		'connection' => 'default',
		'table' => '',
		'prefix' => '',
		'primaryKey' => 'id',
		'displayField' => ['title', 'name', 'id'],
		'entity' => 'Titon\Model\Entity'
	];
	public function addRelation(Relation $relation) {
		$this->_relations[$relation->getAlias()] = $relation;
		$this->attachObject([
			'alias' => $relation->getAlias(),
			'class' => $relation->getModel(),
			'interface' => 'Titon\Model\Model'
		]);
		return $relation;
	}
	public function createTable(array $options = [], $temporary = false) {
		$schema = $this->getSchema();
		$schema->addOptions($options + [
			Dialect::ENGINE => 'InnoDB',
			Dialect::CHARACTER_SET => $this->getDriver()->getEncoding()
		]);
		return (bool) $this->query(Query::CREATE_TABLE)
			->attribute('temporary', $temporary)
			->schema($schema)
			->save();
	}
	public function getPrimaryKey() {
		return $this->cache(__METHOD__, function() {
			$pk = $this->config->primaryKey;
			$schema = $this->getSchema();
			if ($schema->hasColumn($pk)) {
				return $pk;
			}
			if ($pk = $schema->getPrimaryKey()) {
				return $pk['columns'][0];
			}
			return 'id';
		});
	}
}

The language also supports seamless integration with HTML markup. The snippet below is taken from my Titon\Debug project.

<div class="titon-debug">
	<div class="debug-head">
		<abbr title="<?php echo $file; ?>" class="debug-file">
			<?php echo self::parseFile($file) . ':'; ?><!--
			--><span class="debug-line"><?php echo $line; ?></span>
		</abbr>
	</div>
	<?php foreach ($vars as $var) { ?>
		<div class="debug-output">
			<?php if (isset($dump)) {
				echo self::_renderTemplate('table', array('value' => $var));
			} else { ?>
				<pre><code><?php echo \Titon\esc(print_r($var, true)); ?></code></pre>
			<?php } ?>
		</div>
	<?php } ?>
</div>

I hope you all find use in this contribution! Feel free to report any issues or suggestions to me.

Restricting an input field to numeric only

I was always amazed that a feature like this wasn't available in HTML 4 (but is in HTML 5). It would be really cool to restrict an input field to only accept numeric characters (or alphabetic), without the need for JavaScript. But I digress as I had to use JavaScript anyways. I first tried to steer away from using an event, but found it difficult to not allow the letter without it showing up in the input field first, or finding it too strange to regex the non-numeric characters out. So I caved in and used the event handlers. Here's a quick JavaScript snippet that will make an input field only accept numeric characters, a backspace and a tab.

function numericOnly(event) {
	var key = window.event.keyCode || event.keyCode;
	return ((key >= 48 && key <= 57) || (key >= 96 && key <= 105) || (key == 8) || (key == 9));
}

The function will accept the 0-9 range of numbers and all numbers on the numlock keypad. However, this will not work in all locales since some keyboard layouts are different but will work on all basic QWERTY keyboards. Finally, to implement the function, you would use onkeydown, as it checks the event before the character shows up (unlike onkeyup or onkeypress).

<input name="field" onkeydown="return numericOnly(event);" />

There are probably better and more thought out alternatives, so if you have one please post it in the comments. Would be nice to get multiple variations.

AJAX calls in CakePHP: The JSON Response

The controller and HTTP response are now working correctly, so it is time to display the response message to the client. We will achieve this by utilizing jQuery to output our JSON. The plan is to show a success message if the JSON passed or display a list of errors if it has failed. To display the messages we need to create our HTML elements that will hold the responses.

<div id="responseSuccess" class="responseBox" style="display: none"></div>
<ul id="responseError" class="responseBox" style="display: none"></ul>

Before we can write our Javascript to handle the JSON, we need to first inspect the JSON response. Our JSON "data" message can come in 3 possible formats: a simple string, an array containing the error messages, or an object containing the error messages / post data with their key values. Below are a couple examples of how our JSON response could be formatted:

// Single string error
{"success":false,"data":"No username/password","code":-1}
// An array containing the errors
{"success":false,"data":["No username","No password"],"code":-1}
// Errors / Post data and their key
{"success":false,"data":{"username":"No username","password":"No password"},"code":-1}
{"success":true,"data":{"username":"Miles","password":"p4ssw0rd"}}

Now this poses a significant problem. If our response data can be sent as 3 possible formats, we need to write our Javascript to be able to handle all possible variations. We can do this by creating a loop for the arrays/objects and store the values in a custom array, and for the single string just return the message. The following code block can do just that, and all we need to do is place it within the "success" parameter of our AJAX call.

function login() {
	var data = $("#UserAddForm").serialize();
	$.ajax({
		type: "post",
		url: "/ajax/login/",
		data: data,
		dataType: "json",
		success: function(response, status) {
			// Response was a success
			if (response.success) {
				$("#responseSuccess").html(response.data).slideDown();
			// Response contains errors
			} else {
				var errors = [];
				if (typeof(response.data) == ("object" || "array")) {
					$.each(response.data, function(key, value) {
						var text = isNaN(key) ? key + ": " + value : value;
						errors.push("<li>"+ text +"</li>");
					});
				} else {
					errors.push("<li>"+ response.data +"</li>");
				}
				errors = errors.join("\n");
				$("#responseError").html(errors).slideDown();
			}
		}
	});
	return false;
}

Pretty simple right? We can improve on this code by adding a setTimeout() that will remove the response box after 5 seconds. Additionally we can add the AJAX error argument that is called if the AJAX mechanism fails as a whole.

// Remove box after 5 seconds
setTimeout(function() {
	$(".responseBox").slideUp();
}, 5000);
error: function(XMLHttpRequest, textStatus, errorThrown) {
	$("#responseError").html("<li>An unexpected error has occurred.</li>").slideDown();
}

Our Javascript is now complete, but we could improve on it yet again. Say you have multiple AJAX calls that all utilize the same type of callback mechanism. It would be a major pain and waste of time to write all that code over and over again. What we can do is separate the code into 3 separate functions, all handling certain aspects of the logic. Our final code may look something like the following.

/**
 * Fire an AJAX call to login the user
 */
function login() {
	var data = $("#UserAddForm").serialize();
	$.ajax({
		type: "post",
		url: "/ajax/login/",
		data: data,
		dataType: "json",
		success: function(response, status) {
			handleCallback(response, status);
		},
		error: function(XMLHttpRequest, textStatus, errorThrown) {
			handleError(XMLHttpRequest, textStatus, errorThrown);
		}
	});
	return false;
}
/**
 * Handle the AJAX callbacks
 */
function handleCallback(response, status) {
	// Response was a success
	if (response.success) {
		$("#responseSuccess").html(response.data).slideDown();
	// Response contains errors
	} else {
		var errors = [];
		if (typeof(response.data) == ("object" || "array")) {
			$.each(response.data, function(key, value) {
				var text = isNaN(key) ? key + ": " + value : value;
				errors.push("<li>"+ text +"</li>");
			});
		} else {
			errors.push("<li>"+ response.data +"</li>");
		}
		errors = errors.join("\n");
		$("#responseError").html(errors).slideDown();
	}
	// Remove box after 5 seconds
	setTimeout(function() {
		$(".responseBox").slideUp();
	}, 5000);
	return false;
}
/**
 * Handle an AJAX failure
 */
function handleError(XMLHttpRequest, textStatus, errorThrown) {
	$("#responseError").html("<li>An unexpected error has occurred.</li>").slideDown();
}

Now there are multiple ways for your to handle a JSON response, but the approach above is a pretty straight forward implementation of displaying a success or failure message. Be sure that when you write your AJAX requests, that the $.ajax() function has a dataType of json, or your JSON response will completely fail.

AJAX calls in CakePHP: The Controller

Now that you have setup the Javascript and view to properly fire off an AJAX call, it's time to process the call within the Controller. The AJAX call was firing off to AjaxController::login(), so lets begin by creating the action. What we need to do is check to see if the username and password is set (validation), then attempt to login the user by using the AuthComponent. If the login is successful, or even if it fails, we will return a JSON response.

Below would be a basic setup to accomplish what we need to do. However, in your actual application you would apply more strict validation and sanitization, but for the sake of the tutorial I will not.

function login() {
	$this->layout = 'ajax'; // Or $this->RequestHandler->ajaxLayout, Only use for HTML
	$this->autoLayout = false;
	$this->autoRender = false;
	$response = array('success' => false);
	if (!empty($this->data['User']['username']) && !empty($this->data['User']['password'])) {
		if ($this->Auth->login($this->data)) {
			$response['success'] = true;
			$response['data'] = $this->data['User'];
		} else {
			$response['data'] = 'Username/password combo incorrect';
			$response['code'] = 0;
		}
	} else {
		$response['data'] = 'No username/password';
		$response['code'] = -1;
	}
	$this->header('Content-Type: application/json');
	echo json_encode($response);
	return;
}

As an added bonus, I will supply an example of how to achieve the same result above using my AjaxHandlerComponent. The component does all the necessary preparations and output leaving you to write less code.

function login() {
	if ($this->AjaxHandler->valid($this->data['username'], true) && $this->AjaxHandler->valid($this->data['password'], true)) {
		if ($this->Auth->login($this->data)) {
			$this->AjaxHandler->response(true, $this->data);
		} else {
			$this->AjaxHandler->response(false, 'Username/password combo incorrect', 0);
		}
	} else {
		$this->AjaxHandler->response(false, 'No username/password', -1);
	}
	$this->AjaxHandler->respond();
	return;
}

Basically what we need to do, to get a proper JSON response is to build the response array. We create an array with a success variable that would be a true or false. If the success is true that means the core logic that we want completed was a success, else the AJAX logic failed and the user should try again. We also have a data index that would house the error/success message(s), an array of data to use, or anything else you might need. Lastly we have a code index which could be used to differentiate the levels of errors and their importance. Once we have our response array complete, we set the correct headers and respond it back while encoding it with json_encode().

On top of building the response we need to configure the controller a bit. We need to disable the view and layout from rendering or we will receive the "Missing View" errors within Cake, and we also need to remove the layout so that our response is not surrounded by HTML (unless you want to respond with HTML, but we are using JSON). We do this by setting autoLayout and autoRender to false -- furthermore we set the layout to ajax which would be required for HTML responses.

You must be thinking to yourself, "Wow that was easy, we are now done!", but you are wrong! We still have one final step, validating that the request is a legitimate AJAX call. To achieve this we will use the RequestHandler::isAjax() method. Since all of our AJAX calls are housed in the AjaxController, we can place this code in the beforeFilter() so it applies to all actions. If your AJAX actions are within your primary controllers, you will need to add a bit more logic.

I will be using the SecurityComponent in my examples, but you can use die() or something similar in place of blackHole(), like Controller::redirect().

// Must be disabled or AJAX calls fail
$this->Security->validatePost = false;
if (!$this->RequestHandler->isAjax()) {
	$this->Security->blackHole($this, 'You are not authorized to process this request!');
}
// Use the following code in place of blackHole()
// $this->redirect(null, 401, true);

The previous code should blackhole any request that does not contain the XMLHttpRequest header. To further build on this code, we can check to make sure the AJAX request is coming from the current domain, if not then kill it. Our final beforeFilter() should look something like the following.

function beforeFilter() {
	parent::beforeFilter();
	// Must be disabled or AJAX calls fail
	$this->Security->validatePost = false;
	if (!$this->RequestHandler->isAjax()) {
		$this->Security->blackHole($this, 'You are not authorized to process this request!');
	} else {
		if (strpos(env('HTTP_REFERER'), trim(env('HTTP_HOST'), '/')) === false) {
			$this->Security->blackHole($this, 'Invalid referrer detected for this request!');
		}
	}
}

The controller code should now be complete and ready for your amazing AJAX use. We could however improve it a little more by utilizing the RequestHandlerComponent and some of its methods, but ill leave that up to you to adventure into. In the next chapter we will go over the response and how to interact with the JSON.

Code snippets now available

So over the years I have written many small code snippets, functions and what have you, and thought it would be a good idea to release them to you guys. I use most of these snippets on my own projects and applications and are great to be re-usable everywhere. Most, if not all the snippets, will deal with PHP and Javascript, however I have thrown in some CakePHP and jQuery ones.

View all 21 code snippets

Ajax Handler

On top of releasing my code snippets, I have recently made my AjaxHandler component available. The component can be placed in any CakePHP application and then applied to specific controller actions to handle them as Ajax requests. The handler does nearly everything automatic and even responds with the correct data structure and content type.

Check it out and let me know what you think!

Download the Ajax Handler

Toggling all checkboxes with JavaScript

Update You can view the comments for alternative and quicker ways to do this, enjoy!

Say you have a list of messages, and on the left of each message is a checkbox. This checkbox is used for a mass deletion or mass insert function here. Now to make it easier on the user, its nice to add a checkbox outside of the list that either checks them all, or dechecks them all. The following function can achieve that effect, using jQuery or regular JavaScript of course.

// Javascript
function toggleCheckboxes(current, form, field) {
	var val = current.checked;
	var cbs = document.getElementById(form).getElementsByTagName('input');
	var length = cbs.length;
	for (var i=0; i < length; i++) {
		if (cbs[i].name == field +'[]' && cbs[i].type == 'checkbox') {
			cbs[i].checked = val;
		}
	}
}
// jQuery
function toggleCheckboxes(current, form, field) {
	$("#"+ form +" :checkbox[name='"+ field +"[]']").attr("checked", current.checked);
}

Lets set up a quick example of how our form should be and how this function should be used properly. We will begin by adding the form tag with an id (required) and the message checkboxes.

<form action="" method="post" id="messageForm">
<input type="checkbox" name="messages[]" /> Message 1
<input type="checkbox" name="messages[]" /> Message 2
</form>

Let me explain the functions arguments before we finish up. The first argument should always be "this" so it can reference itself, the second argument would be the id of the containing form and the final argument would be the name of the checkboxes input name attribute (without the []). Now with that, we can finish this up by placing the following code anywhere on your page. Enjoy!

<label for="messages"><input type="checkbox" onclick="toggleCheckboxes(this, 'messageForm', 'messages');" /> Toggle all checkboxes</label>

Checking if an element exists

So with my new design, I wanted a way to keep all columns around the same height, especially so the middle doesn't look awkward. To do this, I was going to use JavaScript and get the heights of each column and then calculate. A problem was that some pages do not have the right column, so I had to test to see if the column existed. To do that, all you use is the getElementById().

var el = document.getElementById('column');
if (el != null) {
	// Column exists
}

Easy enough now isn't it. I'm not exactly sure how this is done in jQuery, as I couldn't find an answer in the documentation. It is probably done with some selector and traversing magic. Now, to make things easier, we can package this into a function to be reused over and over.

function elementExists(id) {
	return document.getElementById(id);
}
if (elementExists('column')) {
	// Column exists
}