:: Archive for the 'Web Development' Category ::

ObservableTrait – Experiment with PHP 5.4

Posted on March 3rd, 2012 in PHP, Web Development by Greg

PHP 5.4 was released this week, and one of the biggest new capabilities from a language perspective is traits, called mixins in some other languages. Traits are basically a way of creating a set of reusable methods and properties and adding them to class, almost like copy paste. In a way this is like inheritance, but a class can use multiple traits to add multiple capabilities. Whereas with inheritance you say something like ‘a Dog is an Animal’, with traits you say ‘a Dog can be observed’.

To try out traits I took inspiration from JavaScript, a language that I’ve been using quite a bit recently. Client-side javascript for complex applications is very event driven and accordingly makes heavy use of event listeners – both DOM events and custom events. One approach that I’ve been impressed with is Sencha’s Observable class/mixin that allows classes to easily fire events and notify any listeners without tightly coupling the listeners and the observed.

While PHP is significantly less event driven (user input only comes at the beginning of a script), I thought that implementing a similar Observable mechanism would be a good way to try out the new feature. I’ve added the trait to my growing library of utility classes on Github. Here’s how it works:

1. A class uses the ObservableTrait and then fires events whenever an important event occurs:

<?php
require_one('gUtils/ObservableTrait.php');
Class Dog {
	use gUtils\ObservableTrait;
	public $name;
 
	public function __construct($name) {
		$this->name = $name;
	}
 
	public function bark() {
		echo "woof woof\n";
		$this->fireEvent('barked', $this);
	}
 
	public function rollOver() {
		echo '(' . $this->name . " rolls over)\n";
		$this->fireEvent('didTrick', $this, 'roll over');
	}
 
	public function shake() {
		echo '(' . $this->name . " shakes hands)\n";
		$this->fireEvent('didTrick', $this, 'shake hands');
	}
}

The calls to fireEvent include the name of the event, and any additional parameters that should be passed to any listener.

2. On instantiation of an object, listeners can be attached to the object to be notified when particular events occur:

<?php
require('Dog.php');
$fido = new Dog('Fido');
$listeners = $fido->addListeners([
	'barked' => function($dog) {
		echo 'Shhh ' . $dog->name . "\n";
	},
	'didTrick' => function($dog, $trickName){
		echo 'Good dog, ' . $dog->name . " - I love it when you {$trickName}\n";
	}
]);
$fido->bark(); // Shhh Fido
$fido->rollOver(); // Good dog, Fido - I love it when you roll over
$fido->shake(); // Good dog, Fido - I love it when you shake hands
$fido->stopEvents();
echo "stopped listening for a while\n";
 
$fido->bark();
$fido->bark();
$fido->shake();
$fido->bark();
$fido->bark();
$fido->resumeEvents();
echo "listening again\n";
$fido->bark(); // Shhh Fido
$fido->removeListener($listeners[0]);
echo "removed bark listener\n";
$fido->bark();
$fido->rollOver(); // Good dog, Fido - I love it when you roll over

And that’s about it – you can:

  • Add Events
  • Add Listeners (one at a time or multiple)
  • Remove Listeners (one at a time or multiple)
  • Stop throwing events
  • Resume throwing events
  • Remove all listeners

So take a look and let me know what you think…


InputValidator – Simple Input Validation for PHP

Posted on December 26th, 2011 in PHP, Web Development by Greg

I recently needed a library for validating form input and everything seemed either verbose or required a huge number of files and libraries to be included. Here’s what I was looking for:

  • Easy to include in projects without having to change include paths, mess with auto loaders, or include other frameworks
  • Something that minimized the amount of validation code that needed to be written, preferably with a fluent interface
  • Ability to generate error messages that can be shown to the end user
  • Ability to transform data to data types needed by the rest of the script
  • Capability to validate against arbitrary regexes
  • Capability to validate against closures or other external functions

I couldn’t find exactly what I was looking for, so I decided to write it myself. I added the result, InputValidator, to my gUtils collection of PHP classes on Github. InputValidator is a standalone script for validating user input from forms or other sources.

Everything is in a single file, making it easy to include in any (PHP 5.3+) project. Here’s some example usage:

<?
require('gUtils/InputValidator.php');
// this could be from $_POST
$data = array(
  'name' => 'Greg Neustaetter',
  'email' => 'greg@emailaddress.com',
  'website' => 'http://www.gregphoto.net',
  'favoriteNumber' => 'xyz',
  'date' => '11/11/2011'
);
 
// pass in the data to be validated
$v = new gUtils\InputValidator($data);
 
// validate each field
$v->field('name')->required()->length(3,50);
$v->field('email', 'Email Address')->required()->email();
$v->field('website')->url();
$v->field('favoriteNumber', 'Your favorite number')->intRange(0,100)->toInt();
$v->field('date')->toDateTime('m/d/Y')->after(time()); // after the current time
 
if(!$v->allValid()) {
    echo '<pre>';
    print_r($v->getErrors()); // returns an array of errors indexed by field
    echo '<pre>';
    exit();
} 
$data = $v->getValues();  // returns an array of values indexed by field
$name = $v->get('name'); // get the value of name
echo $v->escape('name'); // escape the value of name for output with htmlspecialchars

This script would print the following error array:

Array(
    'favoriteNumber' => Your favorite number must be an integer between 0 and 100
    'date' => Date cannot be before 12/24/2011
)

In addition to validation the library includes a few filters to manipulate data and cast it to formats needed by your code. Take a look at the documentation on the Github wiki for a list of the 20 validators and the 8 filters.

Let me know if you find it useful or if you have any feedback…

Playing mp3s with SoundManager 2

Posted on November 23rd, 2008 in Music, Web Development by Greg

My friend Michael Ross is a musician and he sent me an email with a couple of the songs he recorded recently. I asked him if he would like me to put together a simple web page for his songs so that he didn’t have to send out emails with large attachments, and a week or so later we had a new site up and running for him – www.michaelrossmusic.net. He wanted a page that listed the albums, had links to download mp3 songs, and a way for people to listen to the songs without downloading them. I had used SoundManager 2 previously to have mp3s play on a page, so I knew that it would be a good fit.

I wanted to be able to build a simple page with regular links to the mp3 files and for JavaScript to do the magic of making the songs playable in the browser – this post explains how I made it happen. SoundManager 2 is a JavaScript library built on top of Flash – the music plays through Flash (which has much better audio support than browsers), but everything is controlled via JavaScript. I also used Prototype as a framework to build out the functionality.

Here’s what I did:

  1. Created a simple page with links to mp3 files
  2. Added a SoundManager handler so that as soon as it loads it instantiates PagePlayer, a JavaScript class I created
  3. PagePlayer takes a CSS selector as input (and optionally an options object) and identifies all of the links that match the selector – ‘a.mp3Link’ in this case (all links with the class ‘mp3Link’).
  4. For each of the identified links, PagePlayer creates a SoundManager sound object using the link to the mp3, adds a play button just prior to the song link, creates event handlers for the play button and sound object, and stores the song in a hash
  5. The event handlers take care of all of the logic: playing songs, making sure that only a single song is playing, changing the state of the button from play to stop

Here’s a current snapshot of the script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
PagePlayer = Class.create({
	songCount: 0,
	songs: new Hash(),
	nowPlaying: null,
	options: null,
 
	initialize: function(selector, options) {
		this.options = {
			imgPath: 'images/',
			imgPlay: 'control_play.png',
			imgStop: 'control_stop.png',
			mp3LinkTitle: 'Download song'
		};
		Object.extend(this.options, options || {});
		$$(selector).each(this.addSong,this);
	},
 
	addSong: function(mp3Link) {
		mp3Link.title = this.options.mp3LinkTitle;
		var songId = mp3Link.identify();
		var soundObj = soundManager.createSound({
			id: songId,
			url: mp3Link.href,
			onfinish: this.finishHandler.bind(this,songId)
		});
		var controlBtn = new Element('img',{
			src: this.options.imgPath + this.options.imgPlay,
			className: 'audioControl'
		});
		mp3Link.insert({before: controlBtn});
		controlBtn.observe('click', this.toggleHandler.bind(this,songId));
 
		this.songs.set(songId, {
			controlBtn: controlBtn,
			soundObj: soundObj
		});
 
		this.songCount++;
	},
 
	toggleHandler: function(songId) {
		var oldSong = this.nowPlaying;
		var newSong = this.songs.get(songId);
 
		if(oldSong) {
			oldSong.soundObj.stop();
			oldSong.controlBtn.src = this.options.imgPath + this.options.imgPlay;
			if(oldSong === newSong) {
				this.nowPlaying = null;
				return;
			}
		}
 
		newSong.controlBtn.src = this.options.imgPath + this.options.imgStop;
		newSong.soundObj.play();
		this.nowPlaying = newSong;
	},
 
	finishHandler: function() {
		this.nowPlaying.controlBtn.src = this.options.imgPath + this.options.imgPlay;
		this.nowPlaying = null;
	}
});

And on the page it is started up as follows:

1
2
3
4
5
6
7
8
<script src="js/soundmanager2-nodebug-jsmin.js"></script>
<script src="js/pagePlayer.js"></script>
<script>
	soundManager.url = '';
	soundManager.onload = function() {
		new PagePlayer('a.mp3Link');
	};
</script>

It’s not the prettiest code, but it gets the job done in 1.58 KB of code and works at least in recent versions of Firefox, IE, Safari, and Chrome. So, if you need to add some mp3s to a page, check out SoundManager 2 – it’s easy to use, well documented, and super flexible…

Yelp for the iPhone

Posted on December 3rd, 2007 in Web Development by Greg

I just put together my first simple website for my new toy – my iPhone. It is a simple iPhone-specific interface for Yelp, my favorite source for restaurant and other local reviews. They have a ton of reviews for just about every local restaurant so I find myself using it all the time to find new restaurants to go to or to find more info about restaurants I already know. I could access the website just fine already on my phone, but it was a bit crowded and difficult to navigate on a small screen, so I decided it take this as a challenge to build my first iPhone web interface.

And yes, I’m probably about the 58th person to build a Yelp interface for the iPhone, but I was looking for something I’d actually use and learn from!

Yelp provides a REST search API that allows you to pass in a location and a search term and they return a JSON object with a list of matching businesses, their ratings, and the business info. I used the Zend Framework and the Prototype JavaScript library to put it together. I could have done a pure JavaScript implementation using script hacks, but figured it’d be better to have the PHP code serve as a basic proxy for the Yelp web service.

Try it out!

Announcing Gregphoto_Image – a PHP5/GD2 Thumbnail Class

Posted on February 3rd, 2007 in PHP, Web Development by Greg

I put together a simple, lightweight PHP class for generating thumbnail images. The class is compatible with PHP5 and uses the GD2 extension (included by default with PHP5) to create JPEG, PNG, and GIF thumbnails. I’ve setup a Google Code project for it: Gregphoto_Image where you can checkout the source from SVN and file bugs.

The class has the following basic features:

  • Ability to read JPEG, PNG, or GIF images
  • Ability to output JPEG, PNG, or GIF images
  • 4 modes of thumbnail creation
    • MAX_HEIGHT – you specify a maximum height and the dimensions are calculated based off of the height
    • MAX_WIDTH – you specify a maximum width and the dimensions are calculated based off of the width
    • BEST_FIT – you specify a maximum height and width and the dimensions are calculated so that the thumbnail
      is as large as possible without exceeding the maximum height or width
    • EXACT – you specify a maximum height and width and these are directly used. Causes distortion if the
      chosen aspect ratio is different from the aspect ratio of the image
  • Renders/saves images in their input format by default, but allows changing the format. For example, input a GIF but output a PNG
  • Fully documented object oriented code
  • Fluent interface for creating thumbnails with a minimal amount of code

The class is licensed under the MIT license, which basically means it can be used and modified by anyone – for personal or commercial use.

You can Download it from the project page on Google Code. You can view examples of it running on my site – the examples are checked into SVN and can be viewed on the project site. You can also view the docs.

Example usage:

1
2
3
4
5
6
7
require('path/to/Gregphoto_Image.php');
$image = new Gregphoto_Image('path/to/sample/image.jpg');
$image->setMaxHeight(200);
$image->setMaxWidth(200);
$image->setJpegQuality(90);
$image->resize(Gregphoto_Image::BEST_FIT);
$image->showThumbnail();
1
2
$image = new Gregphoto_Image('../images/fan.jpg');
$image->setMaxHeight(200)->setJpegQuality(90)->resize()->showThumbnail();
1
2
3
4
5
6
7
8
require('path/to/Gregphoto_Image.php');
$image = new Gregphoto_Image('path/to/image.jpg');
$image->setMaxHeight(200);
$image->setMaxWidth(200);
$image->setJpegQuality(90);
$image->setOutputType(IMAGETYPE_PNG);
$image->resize(Gregphoto_Image::BEST_FIT);
$image->saveThumbnail('path/to/thumbnail.png');

Enjoy!