Archive for September 2008

ZendCon08 Wrapup

So I kinda had a thing going about blogging during ZendCon but lost it, so I might as well just do a single day wrap up.

So ZendCon turned out to be completely worth it, even if only for the chance to meet in person a lot of the core PHP community and then drink semi-copiously with them.

The talks were good, though most of them were either more basic than I would have liked or really indepth (/me thinks back to Sara's PHP Extension Writing talk, though that was more "few php developers gets this deep" than indepth/complicated).

The uncon was nice, which of course I'd like it given my love for barcamps, refreshes, and generally community driven and adhoc events.

The Yahoo party started out awkward given that one expects an open bar after paying $500+ for tickets to a conference, however from what I gathered it really wasn't so much Yahoo's fault more the hotel's. I won the raffle for the "Day with Rasmus" prize and felt bad 'cause when one of the Yahoo girls came to me and to have me verify my information, I told them my email (which was a gmail account) was correct but if they still have trouble to just google my first and last name since I dominate the first page for my name.

My only problem with ZendCon is that it ended...

refreshBCS October 1st

Since it isn't up on refreshbcs.org yet I'll repost the announcement from the listserv:

Where: Murphy's Law (upstairs) [Google Map]
When: October 1st 7PM (Happy Hour starting at 5)
Speaker: Michelle Greer

We've been quiet for a while but it's time to get this show back on the road. We've got several months worth of great speakers lined up and we're starting this thing off with a bang. Michelle Greer has been kicking ass and taking names in the Austin area for quite some time and she has no plans to stop anytime soon. Come listen to her speak about how she uses social media to institute real-life change in the world around us.

Making Social Media Matter:
How Social Media Turns Everyday Citizens into Superheroes

In an age where anyone can publish anything for the cost of hosting a WordPress blog, the means for spreading ideas has never been easier or cheaper. Learn how people have used social media to mobilize people at rates never seen before in history, and how these tools make it easy for you to bring about positive change for your community.

-> Michelle Greer
-> Web Marketing Strategist
-> http://www.michellesblog.net
-> 512.294.3909

If any of you happen to be around college station on the 1st of October, or can make it, I highly suggest you show up to the refresh meetings. Even if the particular topic isn't your cup of tea (if you are a developer and it's a design topic, etc.) it's a great place to connect with people (obviously from the BCS area) and get educated on the happenings on the other side of the fence.

Ruby on FAILS

Ruby on FAILS sticker

Spoon is both a gentleman AND a scholar.

(And caseysoftware is awesome for the pic)

ZendCon08 Day 1 Wrap Up

Turned out to be a fairly exciting day. I woke up late wondering why my alarms didn't go off, just barely got to lunch and regretted missing Jay Pipes' talk.

Sara Golemon gave an interesting introduction to PHP extension writing. I bow to her vastly superior C-fu.

I already discussed a few talks that I went to yesterday as well.

Ended the night doing a pub crawl with some people (hung out with Jay Pipes at the end which is what caused me to regret missing his talk).

ZendCon08 Day 1 - The Knight Rider Methodology to Software Development

Eli White gave a talk called "The Knight Rider Methodology to Software Development".

He compares software development methodologies to the old TV show Knight Rider, in that the main character, Michael Knight, was average and ordinary except for the fact that he had an applicable toolset for what he needed to do (KITT).

Eli talks about how while locking yourself into a programming language (company wise) is okay in that when you hire you know that the candidate has to know at lease a finite number of languages. But you shouldn't force employees to use locked in tools. If they like their own editor debugger combo, let them use it because they will be faster, the code will still work the same.

He goes over different tools you'll use for different purposes. Editors, debuggers, load testers, etc.

The movie clips are great, which you unfortunately don't get on the downloadable presentation.

ZendCon08 Day 1 - PECL Picks

Elizabeth Smith discussed to a really interesting PECL extension, pecl_http. I can do request pooling!

She also discussed pdo_user that allows you to create PDO drivers in userland (PHP).

Good overview on PECL packages. It makes me sad that they have really cool extensions like the operator overload extension and I can use them because I have to worry about install bases of other users :( Damned shared hosts.

ImagePlane and some simple OOP designs

At Net Perspective we created an image editor in flash that we just released for sale called ImagePlane.

While ImagePlane has already been creatively introduced and documented quite thoroughly, I wanted to go over some decisions concerning the PHP demo.

Currently ImagePlane posts saved data to URL via the HTTP POST method. Currently ImagePlane posts the entire Base64 encoded image (after transformations), but when designing the example class I had to account for the fact that I might have to someday support just a list of transformations (for the server to duplicate rather than receiving the entire image again). I also would have to support more than 1 output engine (despite the fact that I myself would just be using GD).

In designing my interface I like to make a quick list of operations and objects I will be expected to perform (no, this isn't a waterfall document, the list can be broad but I need a general idea of where I'm going):

  1. Set a filename (and by proximity, output directory and type)
  2. Select an output engine (either for performance reasons or simply support)
  3. Constrain image sizes
  4. Set certain image options (JPEG quality, possibly PNG transparency)

Now since I know I needed to support multiple output engines, I'm guaranteed to use the Adapter pattern. So now we have to split up our tasks among two classes.

Since our Adapter classes will each be very different internally (since they have to emulate and mimic features with different libraries) we'll want to keep them as small and clean as possible. Stepping through the list above we can see that 3 and 4 are obviously a best fit for our Adapter class since they deal directly with the actual image processing. All 2 really happens to be is a setOutputEngine(Adapter_Interface $engine) type method, however you can make it fancier by accepting a string for the engine and finding and instantiating the engine manually, but that's implementation side. Item 1 could be construed as belonging with the image processing but in reality it does not. Setting the output location and type is universal to all libraries, infact it really has nothing to do with image processing.

So we have a nice delegation of tasks to our objects we can start building. First things first, after creating our main shell class we create an interface (since things are so wildly different amongst image processing engines there's not much we can replicate so an interface will work better here as opposed to inheriting an abstract class) for our Adapter class.

The adapter class is going to need to do a few things. Specifically its going to need to load from a string (base64 encoded), set a few options (preserveTransparency and setOutQuality, for jpegs), and save the image data.

<?php
interface ImagePlane_Output_Interface
{
	/**
	 * @param string $data base64 encoded image
	 * @return ImagePlane_Output_GD fluent interface
	 */
	public function loadFromString($image);
 
	/**
	 * @param bool $bool
	 * @return ImagePlane_Output_GD fluent interface
	 */
	public function preserveTransparency($bool = true);
 
	/**
	 * Only really used for JPEG
	 *
	 * @param int $quality
	 * @return ImagePlane_Output_GD fluent interface
	 */
	public function setOutQuality($quality);
 
	/**
	 * @param string $outFile output location
	 * @param string $type Image type
	 * @return bool
	 */
	public function save($outFile, $type = ImagePlane::TYPE_JPEG);
}
?>

In our primary class we are probably best splitting up the outfile into 3 separate segments. We should force good filenames by making the user put in the output directory, output name, THEN output type. This way we can easily fix our filenames, check directory problems, and enforce output type (and proper extensions) without having to hack through pathinfo results only to find the user specified some weird extension for a type that we didn't account for. Plus this leaves us open to in the future specifying multiple output files without having to include directories on all them, or specifying multiple output types again without having to retype the entire filename.

<?php
class ImagePlane
{
	/**
	 * ImagePlane is passing in final transformed image via base64 POST
	 */
	const INPUT_POST = 'post';
	/**
	 * CURRENTLY NOT IMPLEMENTED BY IMAGEPLANE
	 */
	const INPUT_TRANSFORMATIONS = 'transformations';
 
	//Image Types
	const TYPE_JPEG = 'jpg';
	const TYPE_PNG = 'png';
	const TYPE_GIF = 'gif';
 
	/**
	 * Output engine
	 *
	 * @var ImagePlane_Output_Interface
	 */
	protected $outputEngine;
 
	/**
	 * Output directory
	 *
	 * @var string
	 */
	protected $outDirectory = null;
 
	/**
	 * Output name (without extension)
	 *
	 * @var string
	 */
	protected $outName = null;
 
	/**
	 * Output type
	 *
	 * @var string
	 */
	protected $outType = null;
 
	/**
	 * Create new ImagePlane instance using a certain output engine
	 *
	 * @param ImagePlane_Output_Interface $outputEngine
	 */
	public function __construct(ImagePlane_Output_Interface $outputEngine)
	{
		$this->setOutputEngine($outputEngine);
	}
 
	/**
	 * Set the image output directory
	 *
	 * @param string $directory
	 * @return ImagePlane fluent interface
	 */
	public function setOutDirectory($directory)
	{
		if( !is_writable($directory) )
		{
			throw new Exception("Output directory is not writeable");
		}
 
		$this->outDirectory = $directory;
 
		return $this;
	}
 
	/**
	 * Get the image output directory
	 *
	 * @return string
	 */
	public function getOutDirectory()
	{
		return rtrim($this->outDirectory, '\\/');
	}
 
	/**
	 * Output filename (without extension)
	 *
	 * The filename is automatically stripped of any characters other than 'A-Z',
	 * '0-9', '_', and '-', and are replaced with a dash.
	 *
	 * @param string $name
	 * @return ImagePlane fluent interface
	 */
	public function setOutName($name)
	{
		$this->outName = preg_replace("/[^[:alnum:]_-]+/", '-', $name);
 
		return $this;
	}
 
	/**
	 * Get the output filename (without extension)
	 *
	 * @return string
	 */
	public function getOutName()
	{
		return $this->outName;
	}
 
	/**
	 * Set output type.
	 *
	 * Valid options are:
	 *  - ImagePlane::TYPE_JPEG
	 *  - ImagePlane::TYPE_PNG
	 *  - ImagePlane::TYPE_GIF
	 *
	 * @param string $type
	 * @return ImagePlane fluent interface
	 */
	public function setOutType($type)
	{
		if( !in_array($type, array(self::TYPE_PNG, self::TYPE_JPEG, self::TYPE_GIF)) )
			throw new Exception("Invalid Type");
 
		$this->outType = $type;
 
		return $this;
	}
 
	/**
	 * Get the output type
	 *
	 * Valid returned types will be:
	 *  - ImagePlane::TYPE_JPEG
	 *  - ImagePlane::TYPE_PNG
	 *  - ImagePlane::TYPE_GIF
	 *
	 * @return string
	 */
	public function getOutType()
	{
		return $this->outType;
	}
 
	/**
	 * Process ImagePlane input
	 *
	 * @param array $data
	 * @param string $type ImagePlane::INPUT_POST (default) or ImagePlane::INPUT_TRANSFORMATIONS
	 * @return bool
	 */
	public function process($data, $type = self::INPUT_POST)
	{
 
	}
 
	/**
	 * Set output engine
	 *
	 * @param ImagePlane_Output_Interface $outputEngine
	 */
	public function setOutputEngine(ImagePlane_Output_Interface $outputEngine)
	{
		$this->outputEngine = $outputEngine;
	}
 
	/**
	 * Get the output engine object
	 *
	 * @return ImagePlane_Output_Interface
	 */
	public function getOutputEngine()
	{
		return $this->outputEngine;
	}
}
?>

You can see that on my set* functions I return the $this variable. This is called the fluent interface, and it allows you to make calls like $object->method()->method()->method(); which makes for compact, and in some cases, semantic code.

You can see that spending just a little bit of time we now have an easily alterable utility class to process uploads from ImagePlane with a very low footprint for the actual end user:

<?php
require_once '../php/imageplane.php';
spl_autoload_register(array('ImagePlane','autoload'));
 
$imagePlane = new ImagePlane(new ImagePlane_Output_GD());
 
$imagePlane->setOutDirectory(dirname(__FILE__))
                ->setOutName("IPOutput")
                ->setOutType(ImagePlane::TYPE_JPEG)
                ->process($_POST);
?>
 

Now obviously this literal code snippet presents security issues but they can be tackled. Firstly the above code snippet can easily be moved inside an if block checking for authentication credentials (like an $acl->isLoggedIn() or similar), as well as checking for tokens via the otherData input variable as described in the documentation.

Stay tuned, hopefully I'll be posting more in the future on OO design and provide more complex design topics in the future.

In the mean time if you want an excellent, classic book, I suggest you grab a copy of Design Patterns: Elements of Reusable Object-Oriented Software by The Gang of Four.

Weird errors and XDebug to save the day!

I had an interesting (and by interesting I mean completely mindbogglingly frustrating) error that I cannot summarize.

To start things off I notice to my dismay visiting a specific page on my website would result in a white screen (as would be expected for a fatal PHP error with error_reporting turned off). Problem is I have the Zend Framework's ErrorHandler plugin setup to load up a view that just var_dumps the error(s) for the development process and obviously I'm not getting this.

Checking the error logs I see... an out of memory issue. Funny thing was no matter how much I upped the limit, the script would always exceed my limit.

So I disabled the ErrorHandler plugin, re triggered the error and checked the logs. Now nothing is showing up in the logs but the script is still white screening.

Using my trusty var_dump I started trudging my way through the code looking for the culprit, I first tried just var_dumping and print_ring the ErrorHandler's errors object and even the ErrorHandler instance itself, but any attempts resulted in my out of memory problem. There really wasn't a specific break point in the code as the code finished execution normally (just with an error, so not really normally eh?).

Eventually I finally just got down on my knees and var_dumped the response object from the Front Controller and the resulting output actually crashed Firefox! One of my initial suspicions, recursion problems, was correct but not in the way you'd think.

It turns out the exception object that was being thrown was either self referencing or something silly because any attempts to print the object resulted in an endless loop. You see, PHP's default var_dump and print_r commands recursively display objects and associative arrays without any depth limits. PHP doesn't even throw a recursion depth limit!

So I installed Xdebug to my live server (an ordeal in and of itself) I was finally able to view the contents of the output (thanks to Xdebug's depth limits) and lo and behold my error message!

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'j.created_at' in 'field list'

Turns out the issue was with an exception that Doctrine threw.

Such a freaking let down.

Desired Hearts T-Shirts - The ‘Unboxing’ Experience Comes To Fashion

So some friends of mine started talking up a new website/company some guy brought to them to work on. Basically it was a Threadless clone but far more upscale, better designs, and socially conscious. They named this experiment "Desired Hearts".

After seeing a few preliminary screen shots and some pictures of the packaging from Roby Fitzhenry, I started getting a bit excited. Then the day finally came when the site launched. I ordered The End Of The World shirt because I thought it looked badass.

The checkout process was a bit bumpy, but considering I jumped on the site a day or two after it opened, I'm going to give them a pass on that one (plus I couldn't duplicate the weird error I got anyways). The only real complaint I have is a lack of an order status/order history page for the accounts and the form fields were a bit messy during the check out.

However none of this was in vain when I finally got my package earlier today.

(You can visit the flickr set directly)

The box came as you see it, no superfluous external packaging, nothing except a USPS shipping label. The box opens up to Gucci-style tissue paper with the Desired Hearts logo on it wrapping the shirt delicately.

I can say that this shirt is of the highest quality, manufacturing wise. The shirt feels amazing, nothing feels cheap, and everything about this shirt feels custom. To sum it up I'm super freaking stoked!

All in all, from start to finish this was an amazingly well executed idea that I can definitely see going places. I mean, seriously, who expects a sexy unboxing experience when you order a shirt?

If you like the idea of Desired Hearts, please Digg this article, upvote this on reddit, or just pass this along to your friends!

SVN Client Too Old (In Ubuntu)?

After upgrading to Subclipse 1.4.0 on my Ubuntu 8.04 laptop, I would try to go in and use the console to update some of my projects. Unfortunately, Ubuntu's Hardy repos don't have Subversion 1.5 so I got this message:

svn: This client is too old to work with working copy '.'; please get a newer Subversion client

Looking for instructions I came across this blog linking to another blog, but horror of horrors the link was dead! Well, thanks to Google's page cache I can put the instructions right here:

For various reasons I won’t go into, you can’t get Subversion 1.5 to build on Ubuntu 8.04. You can however install it from this alternative repository…

First, edit your /etc/apt/sources.list and add:

deb http://ppa.launchpad.net/clazzes.org/ubuntu hardy main

Then update the repo and install:

sudo apt-get update
sudo apt-get install subversion

Now go back into the sources.list and remove (or comment out) that repository:

# deb http://ppa.launchpad.net/clazzes.org/ubuntu hardy main

All done!

Thanks to Seffyroff for ferreting out this info, gleaned from various locations. This reportedly works with both 64-bit and 32-bit versions of Ubuntu.