Custom PHP Functions: String functions

All of these functions can be found within the Titon Utility library.

It's now time for some PHP that isn't Cake related. This is the first part in a series of "Basic/Common PHP functions that all programmers should know". This series has to deal with manipulating strings. I try to make my functions short, sweet and powerful, and I hope you learn something from them.

Truncate

This function takes a long string and shortens it to a defined length and adds appends an ellipsis (or custom string) to the end. Instead of chopping a word in half (if the limit finished within it), it moves the pointer up to the previous space.

/**
 * Truncates a string to a certain length.
 * 
 * @param string $text
 * @param int $limit
 * @param string $ending
 * @return string
 */
public function truncate($text, $limit = 25, $ending = '...') {
	if (strlen($text) > $limit) {
		$text = strip_tags($text);
		$text = substr($text, 0, $limit);
		$text = substr($text, 0, -(strlen(strrchr($text, ' '))));
		$text = $text . $ending;
	}
	return $text;
}
Shorten

This function works similarly to truncate(), but instead of chopping off the end of the string, it chops out the middle. This is useful for shortening users long names or websites.

/**
 * If a string is too long, shorten it in the middle.
 * 
 * @param string $text
 * @param int $limit
 * @return string
 */
public function shorten($text, $limit = 25) {
	if (strlen($text) > $limit) {
		$pre = substr($text, 0, ($limit / 2));	
		$suf = substr($text, -($limit / 2));	
		$text = $pre .' ... '. $suf;
	}
	return $text;
}
Obfuscate

Now this function doesn't directly scramble the text and confuse the user, instead it scrambles the source code. This is useful for displaying emails or other strings that you don't want to show up directly in your source code.

/**
 * Scrambles the source of a string.
 * 
 * @param string $text
 * @return string
 */
public function obfuscate($text) {
	$length = strlen($text);
	$scrambled = '';
	for ($i = 0; $i < $length; ++$i) {
		$scrambled .= '&#' . ord(substr($text, $i, 1)) . ';';
	}
	return $scrambled;
}
Slugify

This function takes a string and makes it SEO and URL friendly (for example in the address bar for my blog posts). It lowercase's all words, replaces spaces with a dash, removes foreign/illegal characters and follows the guidelines for the best possible SEO. For example "Hello, my name is Miles Johnson!" would convert to "hello-my-name-is-miles-johnson".

/**
 * Rewrite strings to be URL/SEO friendly.
 *
 * @param string $text
 * @return string
 */
public function slugify($text) {
	$text = trim(strtolower($text));
	$text = str_replace(array('-', ' ', '&'), array('_', '-', 'and'), $text);
	$text = preg_replace('/[^a-zA-Z0-9\-_]/is', '', $text);
	$text = urlencode($text);
	return $text;
}
Listing

This final function takes an array of items and turns them into an ordered list with the last item having the word "and" in front of it instead of a comma. Very good for making lists.

/**
 * Creates a comma separated list with the last item having an "and".
 *
 * @param array $items
 * @param string $and
 * @return string
 */
public function listing($items, $and = 'and') {
	if (is_array($items)) {
		$lastItem = array_pop($items);
		$items = implode(', ', $items);
		$items = $items .' '. $and .' '. $lastItem;
	}
	return $items;
}

Now that's all I have for now, and I know you will use this in your code constantly just like I do! Be patient for my next series which will be dealing with basic date/time functions.

Underworld - Rise of the Lycans

I thought it would be time for something, non web related. A quick review (sort of) and my thoughts about Underworld - Rise of the Lycans. Enjoy!

This series has been one of my favorites I thoroughly enjoyed. Albeit this type of movie and genre are the types of movies that spark my interest and inner child. Me and my dad would rent these movies and watch them all the time (no idea why we haven't bought them yet, lol!). So I may seem a little bit bias towards the movie, and its predecessors, but I'm just a fan who enjoys it. It has many likenesses to Van Helsing, which is another favorite of mine.

Now I will agree that the previous two movies left a lot to be asked about the Lycan and Vampire history. You learned a little bit more with the second movie in that you find out who the original Lycan and Vampire are and where they came from. What you didn't know is the details behind the feud (vague references to Lucian and Sonya's love) and how they got to this point. With the Rise of the Lycans, set hundreds of years before the first, you learn everything you need to know about Lucian, Viktor, and the bloodlines.

The next paragraph will be a quick rundown of the plot, spoilers ahead! The movie is set within a castle, that is built into the side of the mountain (very Helms Deepish), which is surrounded by a large field and forest. Lucian is born from a pure breed Lycan (who was slain quickly after), but instead of killing the child, Viktor keeps and raises him. Although he keeps him chained and bound, Viktor later decides to use Lucian to infect other humans, which in return are used as slaves and labor to mine the mountain and guard the castle during the day. A love story is intermingled between Sonya (Viktors daughter) and Lucian. They try to keep it secret but Tanis (a Vampire historian) spots them at one point and tries to use this to his advantage. While Sonya is out protecting a caravan of human nobles (humans pay the vampires for protection), a large group of Lycans ambush the caravan and Lucian knowing of this escapes and saves them (Lucian also meets Raze for the first time here). For his betrayal, Viktor orders Lucian to be tortured and killed. Sonya hears of this news, makes a deal with Tanis so that Lucian can escape in return for a Vampire council seat. Lucian escapes with a few other Lycans and awaits for the arrival of Sonya. Lucian is told that Sonya has been put in prison because Viktor has found out about her love for Lucian. While the other Lycans prepare for battle, Lucian sets off to rescue Sonya. Sonya tells Viktor during their confrontation that she is pregnant with a hybrid. Viktor is furious and sends them off to prison. After their capture and trial, they are sentenced to death. The next scene involves the death of Sonya by sunlight, Lucian grabbing her amulet and attempting to escape in his Lycan form (which we have seen as a flashback in the previous movies). During his escape, Lucian is badly wounded and roars loudly which awakens the pure breed Lycans. This sets the war in motion, with the strong advance of the Lycans on the castle and coven. The castle is overrun by the Lycans, the council was slain and Lucian sets off to kill Viktor. Lucian succeeds in driving a sword through Viktors mouth and throwing him into the river (which made me think wtf? seeing as how hes in the other movies). In the last scenes you see Viktor on board a boat, along with Tanis (presumably the only other survivor). Viktor proceeds to "get into" his coufin, alongside the other elders.

Now that you are hopefully caught up, lets begin. I love watching sequels that piece together previous movies storyline, and this did it beautifully. It showed the events of the birth of Lucian and how it led up to the war between the bloodlines. What I enjoyed most was Viktors character and his power as the Vampire elder. He stays true to the Vampire laws, even when it comes to condemning his daughter to death. Although the first 2/3rds of the movie is about Lucian and Sonya and explains a lot of the previous storyline, you do not see much action or fighting between the bloodlines. It is not until the end when the Lycans commence their attack on the castle, that you see much bloodshed; and boy do you see bloodshed.

The battle upon the castles walls is very reminiscent of LOTR's Helms deep, but why try something different when the previous idea worked so well. You see the Vampires upon the walls unleash arrows and ballistas at the hundreds of Lycans rushing forward. But with little to no effect to the numbers of Lycans, the Vampires are easily overwhelmed on the walls and torn apart. Nearly all the Lycans scale the wall and commence to pounce and shred all the Vampires in sight. During this battle, Raze frees all the remaining imprisoned Lycans, who then join the battle. The Death Dealers are sent into battle, but cannot hold of the large numbers and the ferocity of the Lycans. I always enjoy these types of battles, simply because its something you would never see in a real life.

Now I would say this isnt the greatest movie, but it is a great movie. It brings the storyline together well, has a good story itself, embodies some awesome fight scenes and makes good use of Animatronics and CG. I highly recommend this to anyone who enjoys the Underworld series or movies set in this type of fantasy genre.

Other things I loved about this movie:

  • The Vampires armor - Im a huge fan of medieval settings
  • The Lycans apperance, movements and their ability to tear things apart
  • How the castle was built to protect the Vampires from sunlight

One thing though that is not explained, is the deal that Lucian makes to fake his death (the part of his skin with the tattoo). Hopefully this is explained in another movie that continues after this point.

Method for grabbing named parameters

So I never worked much with named parameters before in CakePHP, but recently I needed a way to filter data. It seemed passing many arguments to the action wasn't the best or correct approach. My initial thought was to find a method that can easily grab the param, but after much searching and looking at the API and Cookbook, no such method exists. The next alternative was doing a long ugly shorthand if statement that looked something like this (assuming our param and URL is /search/filter:username/):

$filter = (isset($this->params['named']['filter'])) ? $this->params['named']['filter'] : '';

Now seeing that CakePHP is in 1.2 Stable, you would assume they would have a simple method to fix this. But no worries, I have created a method that is very easy to use and add to your application. To add it to your application, you would place it in your AppController.

/**
 * Used to get the value of a named param
 * @param mixed $var
 * @param mixed $default
 * @return mixed
 */
public function getNamedParam($var, $default = '') {
	return (isset($this->params['named'][$var])) ? $this->params['named'][$var] : $default;
}

The first argument $var would be the name of the param (e.g., "filter" in our example above) and the second argument $default is the value you can return if the param does not exist. To use the method, simply call it from anywhere in your controller like so:

$filter = $this->getNamedParam('filter');

I have also created a second method for grabbing the value of query string param (Example URL: /search/?filter=username). I hope this has been helpful to someone, it has certainly been helpful and easily readable to me.

/**
 * Used to get the value of a get query
 * @param mixed $var
 * @param mixed $default
 * @return mixed
 */
public function getQueryParam($var, $default = '') {
	return (isset($this->params['url'][$var])) ? $this->params['url'][$var] : $default;
}

XAMPP in Windows: Enabling mod_rewrite

Below I will quickly and easily show you how to enable Apache's mod_rewrite in XAMPP Windows. If you are unclear on what mod_rewrite is, it basically takes a long query string url and shortens it to be SEO and user friendly. Now if you are also a CakePHP user, having mod_rewrite enabled is the best course of action. But it seems that mod_rewrite is not enabled with the initial install of XAMPP. Do not worry, it's extremely easy to enable the module, and here's how it's done.

I'm assuming you have installed the xampp directory into the root or C:/ drive. The first thing to do is to open the configuration file located at C:/xampp/apache/conf/httpd.conf. Locate the text below and remove the # from the beginning of it (# acts as a comment and negates the module from loading).

#LoadModule rewrite_module modules/mod_rewrite.so

The final step is to find all instances of AllowOverride None and replace them with AllowOverride All. Now to get mod_rewrite working, you will need to restart your Apache server. You can simply do this by clicking "Stop/Start" next to the Apache module of your XAMPP Control Panel.

CakePHP 1.2 Stable Released

It seems the good ole developers over at CakePHP have released an update to their 1.2 Final version, a now 1.2 Stable. As always I enjoy and appreciate how quickly the Cake team fixes, rewrites and optimizes the Cake engine. They have come a long way and with the help of the community, the Forge and the Bakery. The best thing is their use of Trac and the ability of the community to report bugs and even suggest features and code to be added. I have many ideas and have even written my own code that hopefully one day I can submit to Trac, and maybe perhaps join the team!

Anyways though, I highly recommend upgrading your Cake to the 1.2 Stable, simply because they found a security flaw with the Security and Auth Component. For more information on that flaw, the announcement, and the download, please click the following links:

Displaying form errors as a list in CakePHP

When a form is submitted in CakePHP, any errors that fail the model validation are displayed after each respective input field. But if you're like me and really dislike that structure and output, and would rather have your errors displayed in a list above the form (an example would be my contact form), then this is how you would achieve it. In the example we will be doing a login for the UsersController (do note that I am not using the AuthComponent, this is just a rough example). Lets begin by creating a file and placing it in your views/elements/errors.ctp folder.

<?php // views/elements/errors.ctp
if (!empty($errors)) { ?>
<div class="errors">
    <h3>There are <?php echo count($errors); ?> error(s) in your submission:</h3>
    <ul>
        <?php foreach ($errors as $field => $error) { ?>
        <li><?php echo $error; ?></li>
        <?php } ?>
    </ul>
</div>
<?php } ?>
Validation in the Model

For any form validation to work, we must declare a validation scheme within the respective model, in our case its the User model. Since this is a login form, the only validation we will need is for a username and password. The rules we will be applying is that both the username and password must not be empty. Once we have the validation setup, the next step is to setup the UsersController.

<?php
class User extends AppModel {
	public $validate = array(
		'username' => array(
			'notEmpty' => array(
				'rule' => 'notEmpty',
				'message' => 'Your username is required'
		)),
		'password' => array(
			'notEmpty' => array(
				'rule' => 'notEmpty',
				'message' => 'Your password is required'
		)),
	);
}
?>
Setting up the Controller

In the login action we will be using the RequestHandlerComponent to check for a form submission (this is a better approach then doing isset/!empty on $this->data). If a post is successful, we will save the form data to the User model to run it against the validation. Lastly, we need to be able to display the errors in the view, so we will set() them to $errors by calling the $this->User->validationErrors. The $errors variable is then passed to the errors.ctp element template, which in turn displays all the errors found.

<?php // controllers/users_controller.php
public function login() {
    if ($this->RequestHandler->isPost()) {
        $this->User->set($this->data);
        if ($this->User->validates()) {
        	if ($this->User->verifyLogin($this->data)) {
				// Process information
			}
        } else {
			// Didn't validate
		}
    }
    $this->set('errors', $this->User->validationErrors);
}
// models/user.php
public function verifyLogin($data) {
	$user = $this->find('first', array(
		'conditions' => array(
			'User.username' => $data['User']['username'],
			'User.password' => md5($data['User']['password'])
		)
	));
	if (!empty($user)) {
		return $user;
	} else {
		$this->invalidate('', 'No user was found with those login credentials');
	}
	return false;
} ?>

The two most important things to note is $validationErrors and the invalidate() method. The $validationErrors property is a list of all the error messages in the form for the corresponding input fields; it's called from each respective model. The method invalidate() is used to trigger a custom error that is not part of the $validate scheme for the model. Now lets look back at the controller where the verifyLogin() method is called. If that method fails (returns false), the login fails and no processing is made. On the other hand if the method returns true, lets continue and log them in.

Displaying my errors in the View

Now that we have the validation set, the errors template created and the form processing complete, the last task is to display the errors in the view. Since the controller is already setting the errors into the view, we just need to include the element, and to do that we will use the $this->element() method within the view. Just place this element in the location you want your errors to display.

<?php echo $this->element('errors', array('errors' => $errors)); ?>

Now if you have tested the form, you will notice that the errors are still showing up after the input field. To fix this, we have to disable the errors on the input by setting it to false.

<?php echo $form->input('username', array('error' => false)); ?>
Conclusion

This was a bit more lengthy then I expected, but I hope it enlightened you on how to use and display form errors. You also received a bit more knowledge on how the invalidate() and invalidFields() method work (hopefully ;)). If you have any questions please comment or send me an email, or take a gander at the CakePHP API and Cookbook.

My `09 Resolutions

A big happy new years to you and everyone else! This last year was kind of a bust for everyone, I even went through some rough times. I actually moved back home literally 5 hours ago (sad I know!) because I recently quit my job, the economy fell and I couldn't afford rent any longer. But there is nothing wrong with that, lets hope that the coming year will bring great things for everyone. And with this new year comes many new years resolutions, to say the least, here are a few of mine!

  • Finish this website (there are things I really want to release, but its not ready!)
  • Work more on special projects
  • Hang out with friends I haven't seen much lately
  • Get my corporation and taxes settled (ugh I know)
  • Write more tutorials and tips
  • Learn CakePHP extensively, its fun!

There are other resolutions made, but like everyone else, a few of them are personal. But I posted the following in hopes that if I slack, someone out there reading them and noticing will be disappointed with me, and I don't want that! Now to keep up with these resolutions and see them through, and the same goes for you! I wish you all a happy new years and good luck with everything your doing and the paths you are taking.