Skip to content

Recent Articles

15
Jun

Personal Development

It has been more then a year since I wrote about a new challenge. It’s been almost a year since I started working for Egeniq as a Junior Software Engineer and looking back it has been one of the best decisions in my life.

I write this sitting inside the WWDC convention center, something I couldn’t have dreamed of a year ago. The things I have experienced in the past year have been amazing and the personal development has been so steep it’s hard to imagine where I was a year ago.

As a PHP developer I joined Egeniq to start developing mobile applications. Starting here I knew I was going to learn iOS, and learning a new language is always exciting but also the progress I made in PHP has been astonishing. I learned writing proper API’s, working agile and working for big, big customers. The first major project I worked on was the RTLNieuws365 news application. I was involved in the original brainstorm sessions, all the way to making overtime to get the product finished on time (albeit only 1 day we had to do this). It was the first time I had actually seen a proper interaction design, a proper functional design and a proper technical design. We were responsible for most of the main parts, we set up the API, the application and the server infrastructure. I personally was involved in creating a huge part of the API and the application itself. Working alongside my colleagues, who I revered before joining Egeniq, and still do. The things that I now feel are ‘standard’ in application development, were a totally new concept a year ago. I cannot imagine going back.

As a software engineer at Egeniq we have multiple privileges, alongside the development devices we recieve (we work at home), I get to go to conferences. Where I saw Ivo for the first time on the first ever conference I been to, the conferences I visited last year is quite a list for me.

Starting with PFCongres, a 1-day PHP conference, onto the Apple TechTalks in London (Which me and Peter were fortunate to get into). After that we had Droidcon 2011 in Amsterdam and to finish it off I helped organize MDevCon 2012, which, according to its attendees was one of the best mobile conferences in the Netherlands to date.

Offcourse, last but definitely not least I am writing this from Moscone Center in San Francisco at the WWDC. I can only hope that the coming year brings as much as the last one did. But that definitely isn’t going to be the easiest thing in the world.

I also met great a lot people, off course my colleagues Felix, Allen and Lineke. Whom have helped me progress the way I have. Peter and Ivo, who have been amazing in supporting me during my first year at Egeniq and of whom I have the privilege to learn from everyday. Some of the biggest names in iOS development (Mike Lee, Matt Gemmell, Jeff LaMarche) and a lot of other people in the community. To all the people I met this year, thank you. Thank you for your knowledge, your puns and even just the time we chit-chatted. You all inspired me.

Going for another year, here are a few of my personal goals:

  • Become a speaker
  • Attend at least 3 conferences in different areas of expertise
  • Pickup android development
  • Increase my knowledge for both PHP and iOS
  • Write more (technical) blog posts

I hope, that one year from now I look back at this list, I can tick em all off. And maybe added some more accomplishments.

A year ago I quoted the late Steve Jobs when switching jobs.

If today were the last day of my life. Would I want to do what I am about to do today?

Yes.

24
Feb

jQuery clearInput plugin

While working a web-application once we tried to use input field placeholder values to clarify input types. Which was a hassle, we ended up settling for a script downloaded from somewhere, which we still had to modify anyway..

Based on that experience I decided to create a small plugin that would everything we wanted (and just a bit more) based on jQuery.
It is free to download at https://github.com/thijsdamen/jquery-clearinput, if you like it, please give me kudos :)

Plugin works awesome for form fields with default values. For example a search input field.

My first ‘official’ jQuery plugin and can’t say I am unpleased with it. If you think it can be improved, please fork it and create a pull request.

6
Feb

The little things that count

This is to long for a tweet but most likely to short for a blog post. But still very much wanted to share.

For testing purposes I had to create a new test application on Twitter, so using my special super-secret development account I register a new app, enter all the credentials, url, captcha and submit only to see that I miss-formatted the URL (apparently you are required to enter http://) but more importantly, they did recognize that the captcha was correct and did not show it again! You go twitter! This should be done on ‘o so many’ more websites. Wonderful!!

2 steps later I see that the application is created, I see the consumer key & secret, and a little button “We see that you have not generated an access token for your currently logged in account, do you want to do this now”. This was so awesome! I was already hating the fact that I had to setup a complete authorization process just to get my token and secret, but everything was just provided :)

All in all, super experience twitter! Thumbs up

22
Nov

Droidcon 2011 day 1

Today I have been to my first android conference! While the conference isn’t over yet (tomorrow day 2) I wanted to write down some of my experiences.

Early on the foggy morning we (me and Allen Ding) arrived at the conference, and while I had some issues with the ticket (it ended up in my spam folder) saying my name was enough to get me in and recieve my t-shirt (luckily this one isn’t one size fits all ;)). The venue itself looked awesome and there was a cool vibe in the building. With the opening statement underway by an overly enthusiastic presenter (which was nice) it became clear that unfortunately the “Barcamp” sessions weren’t going to be much more then 20 minute sale-pitches. Atleast there was the keynote done by Nick Butcher which was a good and funny presentation about the new Ice Cream Sandwich (4.0) version of android and the new message that google wants to get out there

It’s not OK to just make OK apps, make more awesome apps instead

Something I felt that was also stressed at the Apple Tech Talk conference earlier November in London. There are so many apps on the markets these days that just being OK isn’t going to do it anymore. Unfortunatly this could not be said for the android application made specifically for the event, the app loaded initial data over network, without an obvious activity indicator and then had talks listed with an empty summary, which still forced the user to go to the website (which wasn’t mobile friendly).

After the Barcamp and proper lunch the first real talks we’re going to get started, attending first the talk by Joris de Sutter being marketed as a talk that will cover “End-user hierarchy of smartphone needs and how this reflects the success of an app” and “Where to find inspiration for great apps that end-users need (and hopefully want to pay for).”. Unfortunately it did not deliver both. It started with a nice overview of the phones a user buys and in what price range (mainly stats from Belgium but ok) he then had his own version of the “Pyramid of Need” which according to him ment that only 60% of the people that buy smartphones use it to make phone calls and only 20% actually download apps. He admitted himself these were made-up figures but obviously this was done without any sense of the subject. He then talked more about people that do not actually reach the marketplace, if they don’t reach the marketplace, how can they be my target audience?

Being quite disappointed after this talk, and I could feel more around me had the same feeling we went onto the next talk which should cover “a few lessons learnt while developing a real-life Android application”. While some parts of this talk coud’ve been interesting for some people the end really made me feel that the person giving the talk wasn’t someone I’d ever want to learn from. In a real-life example of the Efteling Post app the following request was made to the developer

The application was made in portrait mode, but our customers tell us that for devices with a physical keyboard the screen doesn’t rotate while typing

What the presenter then presented as the solution to this problem blew my mind. Instead of building a liquid layout that would stretch the width of the screen, in order to keep using the exact same design images, the choice was made to add black bars to both sides. Causing the user to loose almost 60% (on some devices) of their screen size and are left with an application with a to small font to actually read what your typing in the first place. Going back to Nick’s point, this is not more awesome. Another let down in a day that was riddled with confusion.

Last but not least we went to a talk by Matthias Kaäppler “Kick-starting Android Application Development”. The talk delivered what it promised, and with an awesome presentation made using prezi.com it finally showed some actual good development stuff done for the android.

We ended the day with some demo’s of application made by people in the audience and finally an awesome barbecue, free beer and hamburgers! All standing outside near local fires made in bins, the atmosphere was awesome!

So the conclusion of the day has to be that the level of the talks are a bit disappointing, there is either not enough content, good content or actual facts and it is easy to spot people who do this more then once or people just trying their hand on presenting for the first time.

I am hoping that tomorrow will bring more talks of a better quality!

30
May

New Adventure – New Challenge

For the past year and a half I have been working for HPU, their an amazing company with some amazing people. But in the past few months something started itching inside me, I was not getting the challenge I wanted to get, I was not gaining the knowledge I wanted to gain. I felt like I was repeating the same trick and coming home, it was not often that I actually felt satisfied with what I was doing.

So that for me was a sign that I should move on. Inspired by Steve jobs Stanford Commencement Speech

If today were the last day of my life. Would I want to do what I am about to do today?

I decided I should be looking for a new adventure, a new challenge that would inspire me to become a better developer then I am today, everyday. In this process I wasn’t ‘actively’ looking, I wasn’t going online every day searching for a new opportunity. I knew that with patience, I would find my dream job. This happened on May 4th, there was an small blog post which read: “Wanted: Talented Software Engineer” and the very first heading was exactly this: “We’re Looking For A Jedi Apprentice”.

I replied with my heart beat raised, had the first interview over skype (I know right, how awesome is that!), was invited to have a conversation in Utrecht and now finally

Coming July first I will be working for Egeniq!!!

I am so excited that even now a few weeks later my heart still raises when I think of the awesome stuff that I will be doing in a month from now. I know that I will be learning so much, that I will be experiencing so much and above all, I will be enjoying myself so much more working with the people and the projects over at egeniq.

Egeniq is a company that specialises in mobile development. It’s a young company founded October 2010, but they already have made some impressive applications. Also the founders have years of experience in developing applications.

So I’ll just end with a, probably inappropriate line that’s been going trough my head the last few days:

“fuck yeah”

18
May

Revisited: PHP Thumbnails

This is a revisit of my earlier post How To: PHP Thumbnails – Resize images with PHP

After having used the createImage functionality myself for quite some time I decided to revisit it. Make it more “OO” and portable. The class itself worked as advertised and simply made an resized image from an existing image. The reasons to revisit tho are that the code itself isn’t consistent, there is no clear naming convention and the working of it would be better of if all the classes were seperated.

Starting with this concept and looking over the code already created I figured there would be use of the Factory Pattern, an Image class would recieve the file it would work with, and search for possible classes to handle that file. Also, almost all of the parameters were pulled from the constructor and can now be set using general getters & setters. All setters return the current object so it is a fluent interface.

So, let’s take a look at the code, We will have multiple files for this one, let’s start with the Base class

Image.php

<?php
namespace Library;

/**
* Abstract class Image.
* Uses factory pattern to intatntiate child objcets
*/
abstract class Image {

    /**
     * The file
     */
    protected $_file = null;

    /**
     * the image type
     */
    protected $_type = null;

    /**
     * image name
     */
    protected $_name = null;

    /**
     * image extension
     */
    protected $_extension;

    /**
     * image data
     */
    protected $_image = null;

    /**
     * image quality
     */
    protected $_quality = 100;

    /**
     * Image height
     */
    protected $_height = null;

    /**
     * Image width
     */
    protected $_width = null;

    /**
     * Image max height
     */
    protected $_maxHeight = null;

    /**
     * Image max width
     */
    protected $_maxWidth = null;

    /**
     * Image current Height
     */
    protected $_curHeight = null;

    /**
     * Image current Width
     */
    protected $_curWidth = null;

    /**
     * Destination folder
     */
    protected $_destination = null;

    /**
     * Factory Method
     * Accepts a file string and defines the type and instantiates the corresponding
     * child class
     *
     * @param string $file
     */
    public static function factory($file = null) {
    	/* Checks whether a file is given */
        if (null === $file) {
        	throw new \InvalidArgumentException("Image factory requires a file, none given");
        }

        /* check if the file itself exists */
        if (!file_exists($file) && !is_dir($file)) {
        	throw new \Exception("Image file with name '" . $file . "' does not exist or is a directory");
        }

        /* check if file is an image */
		if (!($type = exif_imagetype($file))) {
			throw new \Exception("File '" . $file . "' is not a image");
		}

		/* get the mime type, for a list of possible types: http://php.net/manual/en/function.image-type-to-mime-type.php */
		$mimetype = image_type_to_mime_type($type);

		/* Remove the image/application in the front */
		$type = end(explode('/', $mimetype));

		/* And remove the "-" and "." */
		$type = str_replace("-", "", $type);
                $type = str_replace(".", "", $type);

		/* Capatalize it */
		$type = strtoupper($type);

		/* check if the class file exists */
		if (!file_exists($type . ".php")) {
			throw new \Exception("Class file for '" . $type . "' could not be found.");
		}

		include $type . ".php";
		$classname = __NAMESPACE__ . "\\" . $type;
		return new $classname($file);
    }

    /**
     * Constructor, recieves path to file and filetype
     */
    protected function __construct($file, $type) {
        /* Checks whether a file is given */
        if (null === $file) {
        	throw new \InvalidArgumentException("Image constructor requires a file, none given");
        }

        /* check if the file itself exists */
        if (!file_exists($file) && !is_dir($file)) {
        	throw new \Exception("Image file with name '" . $file . "' does not exist or is a directory");
        }

		if (!exif_imagetype($file)) {
			throw new \Exception("File '" . $file . "' is not a image");
		}

		/**
		 * Settings some basic values here. such as path to file.
		 * Sets current destionation / extension / name / current width and height all based on
		 * the current file
		 */
		$this->_file = $file;
		$fileinfo = pathinfo($file);
		$this->_destination = realpath($fileinfo['dirname']) . DIRECTORY_SEPARATOR;
		$this->_name = $fileinfo['filename'];
		$this->_extension = '.' . $fileinfo['extension'];
    	$this->_type = $type;
    	list($this->_curWidth, $this->_curHeight) = getimagesize($this->_file);
    }

    /**
     * returns the file
     */
    protected function getFile() {
    	return $this->_file;
    }

    /**
     * returns the image
     */
    protected function getImage() {
    	return $this->_image;
    }

    /**
     * Returns the image name
     */
    public function getName() {
    	return $this->_name;
    }

    /**
     * Sets the image name
     */
    public function setName($name) {
    	if (!is_string($name) || empty($name)) {
    		throw new \Exception("Name must be a string and not empty, '" . $name . "' given");
    	}
    	$this->_name = $name;
    	return $this;
    }

    /**
     * gets the extension
     */
    protected function getExtension() {
    	return $this->_extension;
    }

    /**
     * Returns the image quality
     */
    public function getQuality() {
    	return $this->_quality;
    }

    /**
     * Sets the image quality
     */
    public function setQuality($quality) {
		if (!is_numeric($quality)) {
			throw new \Exception("Quality must be number, '".$quality."' given");
		}
		if ($quality < 0 || $quality > 100) {
			throw new \Exception("Quality must be between 0 and 100");
		}
    	$this->_quality = $quality;
    	return $this;
    }

    /**
     * Gets the height
     */
    public function getHeight() {
		return $this->_height;
	}

	/**
	 * Sets the height
	 */
	public function setHeight($height) {
		$this->_height = $height;
	}

	 /**
     * Gets the width
     */
	public function getWidth() {
		return $this->_width;
	}

	/**
	 * Sets the width
	 */
	public function setWidth($width) {
		$this->_width = $width;
	}

	/**
	 * return the image max height
	 */
	public function getMaxHeight() {
		return $this->_maxHeight;
	}

	/**
	 * sets the image max height
	 */
	public function setMaxHeight($maxHeight) {
		if (!is_numeric($maxHeight)) {
			throw new \Exception("Max Height must be number, '".$maxHeight."' given");
		}
		$this->_maxHeight = $maxHeight;
    	return $this;
	}

	/**
	 * Returns the image max width
	 */
	public function getMaxWidth() {
		return $this->_maxWidth;
	}

	/**
	 * sets the image max width
	 */
	public function setMaxWidth($maxWidth) {
		if (!is_numeric($maxWidth)) {
			throw new \Exception("Max Width must be number, '".$maxWidth."' given");
		}
		$this->_maxWidth = $maxWidth;
    	return $this;
	}

    /**
     * Gets the current height
     */
    public function getCurHeight() {
		return $this->_curHeight;
	}

	/**
	 * Sets the current height
	 */
	public function setCurHeight($curHeight) {
		$this->_curHeight = $curHeight;
		return $this;
	}

	 /**
     * Gets the current width
     */
	public function getCurWidth() {
		return $this->_curWidth;
	}

	/**
	 * Sets the current width
	 */
	public function setCurWidth($curWidth) {
		$this->_curWidth = $curWidth;
		return $this;
	}

	public function getDestination() {
		return $this->_destination;
	}

	public function setDestination($destination) {
		if (!is_dir($destination)) {
			throw new \Exception("Destination must be an (existing) folder, '".$destination."' given");
		}
		if (!is_writable($destination)) {
			throw new \Exception("Cannot write to '".$destination."'");
		}
		$this->_destination = rtrim($destination, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
    	return $this;
	}

	/**
	 * Creates a black image with the original or specified height/width of the image
	 * @param $width = maximum width of the image
	 * @param $height = maximum height of the image
	 */
	protected function createImage() {

		if ($this->_maxWidth === null && $this->_maxHeight === null) {
			/* No calculation needed, using original sizes */
			$this->_width = $this->_curWidth;
			$this->_height = $this->_curHeight;
			$this->_image = imagecreatetruecolor($this->_width, $this->_height);
			return $this;
		}

		/* Calculations wether this is a landscape or portrait image */
		if (($this->_curWidth / $this->_maxWidth) > ($this->_curHeight / $this->_maxHeight)) {
			/* Landscape */
			if ($this->_curWidth > $this->_maxWidth) {
				$this->_width = intval($this->_maxWidth);
				$this->_height = intval($this->_curHeight * ($this->_maxWidth / $this->_curWidth));
			} else {
				$this->_width = $this->_curWidth;
				$this->_height = $this->_curHeight;
			}
		} else {
			/* Portrait */
			if ($this->_curHeight > $this->_maxHeight) {
				$this->_height = intval($this->_maxHeight);
				$this->_width = intval($this->_curWidth * ($this->_maxHeight / $this->_curHeight));
			} else {
				$this->_width = $this->_curWidth;
				$this->_height = $this->_curHeight;
			}
		}

		/* Create the black image with the new dimension */
		$this->_image = imagecreatetruecolor($this->_width, $this->_height);
		return $this;
	}

	/**
     * Saves the image to disk
     */
    abstract function save();

    /**
     * echos the image
     */
    abstract function show();
}

From top to bottom things to mention are:
Namespace! Change this to your own library. You can remove it. But you have to keep in mind that __NAMESPACE__ is used in the factory method, you will have to refactor that as well

The class is abstract! that means you cannot do:
$image = new Image($filename);
You are required to extend it.

The factory function, it takes a filename. Does some safety checks and defines the image mime-type. Using this mime-type it will look if it can find a class to handle it. (for example: image/jpeg) would need a file and class named “JPEG.php” (note that to support e.g. “vnd.microsoft.icon” the dots and dashes are removed, note: using preg_replace doesn’t become faster then using str_replace before it you need to replace atleast 4 chars source. note2: haven’t tested this myself and the source is very old).

The mime-type is searched for using exef_imagetype which returns a constant value for its type (1 to 17) and image_type_to_mime_type is used to return the mime-type. According to php.net exif_imagetype is significantly faster then getimagesize. I did some testing using XDebug and over 10000 iterations the difference is a about 10%. It took getimagesize() 4.5 seconds (rounded) to find the imagetype upto 10000 times and it took exif_imagetype only 4.1 seconds.

The constructor, it will be called from it’s children with a path to the file, after doing some more safety checks we use php’s pathinfo function to get a lot of information from this file.

in the setDestination function we use a little trick. Someone can use setDestionation in 2 different ways.

<?php
//1
$image->setDestination("folder");
//2
$image->setDestination("folder/");

In order to accomodate both we simply remove all directory separators from the right side of the string and place 1 behind it. This way we know that there is always 1, and only 1 directory separator on the right side of the file.

in the createImage function we do the same as we did in in calcImgSize(), we create a black square (that is what imagecreatetruecolor actually does) based on the maximum height and width we were told

Finnaly we define 2 abstract functions, this means that classes extending this class are required to implement these functions. Namely: save() and show(). We used to only be able to save an image, now with some modification to how the destination of the new file is reached, we will still be able to do this. New tho is the fact that we will be able to just echo the image itself. This is usefull if we were for example to make a thumb.php file which could be used to dynamically load an image in the right size in a webpage. Maybe I will write an example of this later.

Now, let’s look at the child classes. I will look only at the PNG class since it has a little modification to it.

PNG.php

<?php
namespace Library;

class PNG extends Image {

	const MIME_TYPE = "image/png";

	public function __construct($filename) {
		parent::__construct($filename, self::MIME_TYPE);
                /* make sure the correct quality is used */
                $this->setQuality(0);
	}

	/**
	 * Create a true color image based on source
	 */
	protected function create($maxHeight = null, $maxWidth = null) {
		if (null !== $maxHeight) {
			$this->setMaxHeight($maxHeight);
		}
		if (null !== $maxWidth) {
			$this->setMaxWidth($maxWidth);
		}

		$this->createImage();
		$image = imagecreatefrompng($this->getFile());
		imagecopyresampled($this->getImage(), $image, 0, 0, 0, 0, $this->getWidth(), $this->getHeight(), $this->getCurHeight(), $this->getCurWidth());
		imagedestroy($image);
	}

	/**
	 * Shows the image
	 */
	public function show($maxHeight = null, $maxWidth = null) {
		$this->create($maxHeight, $maxWidth);
		header("Content-type: " . self::MIME_TYPE);
		imagepng($this->getImage());
	}

	/**
	 * Saves the image
	 */
	public function save($maxHeight = null, $maxWidth = null) {
		$this->create($maxHeight, $maxWidth);
		if (imagepng($this->getImage(), $this->getDestination() . $this->getName() . $this->getExtension(), $this->getQuality())) {
			return $this;
		}
	}

	/**
	 * Override parent setQuality, Quality works different for PNGs
	 */
    public function setQuality($quality) {
		if (!is_numeric($quality)) {
			throw new \Exception("Quality must be number, '".$quality."' given");
		}
		if ($quality < 0 || $quality > 100) {
			throw new \Exception("Quality must be between 0 and 100");
		}
	    $quality = ($quality - 100) / 11.11;
	    $quality = round(abs($quality));
    	parent::setQuality($quality);
    	return $this;
    }
}
?>

Ok, again let’s go over it from top to bottom. We start out by declaring the namespace again.
We extend our superclass Image, and define our mime type as a class constant.

In our constructor we call our superclass constructor including our mimetype.

The create function actually creates our new image. It takes a height and width parameter. Calls our parents createImage function to create the black image square and we fill that with our file.
We simply destroy the left over resource.

the show function simply set’s the correct content-type header and display our image.
The save function does not display but saves it, returns itself so it can be directory shown afterwards if required.

This is the basic, this can be used for JPEGs, PNGs, GIFs, BMPs everything. Small modifications are needed to the mime-type and also some of the functions (imagepng becomes imagejpeg etc. etc.).
But the reason I show you the PNG one is because it substitutes some of it’s parent functionality.
We have a function setQuality() inside our superclass but we overide it , since as we know from our previous code that the quality for a PNG is not based on a scale from 0 to 100, it is based on a scale from 9 to 0 (worst to best)

Now, to see it in action:

<?php
use Library\Image as Image;
require("Image.php");

try {
	$file = "image.png";

	// Image with be an isntance of PNG class
	$image = Image::factory($file);

	$image->setName("big")
	      //No need to use 0-9 here. the class itself knows what we want!
		  ->setQuality(90)
		  //We want to save it to the folder files
		  ->setDestination('files')
		  //save it with 1024 x 1024 dimensions
		  ->save(1024, 1024)
		  //Change the name, we will just chain it. since we want 3 versions of the file
		  ->setName("Medium")
		  //save it with 512 x 512 dimensions
		  ->save(512, 512)
		  //And the last name change
		  ->setName("small")
		  //save it with 256 x 256 dimensions
		  ->save(256, 256)
		  //And let's just show it
		  ->show(256, 256);
} catch (Exception $e) {
	echo "<pre>";
	print_r($e);
	echo "</pre>";
}
?>

This is an extreme case, where you want the same image in 3 different dimensions and in the end show the final image. As you can see you can chain all the commands together. The following would probably be more likely

<?php
$file = "image.png";
$image = Image::factory($file);
$image->setName("New Name")->save(1024, 1024);

I hope that was all pretty clear for the most of you, if you are actually using this I’d appreciate a comment. If there are any bugs or maybe bad design changes on my part. Please let me know, I am still learning every day, and these modification should show off a bit of the changes I have made over the past years.

11
Jan

Small & Quick Update

Just wanted everyone to know I’ll be going to PHPBenelux Conference this year, and I am especially interested in the Talk from Rob Allen (one of my all-time heroes).

I hope to have a full report of the event when I’m back.

Also, I was just working on a HTML Ajax crawlable by google page I thought I’d share for now.

check it out here: http://thijsdamen.nl/crawlable/

28
Dec

How To: PHP Thumbnails – Resize images with PHP

I have revisited this subject here: Revisited: PHP Thumbnails

We all have been in situation where we allow users to upload images for content items or profile pictures. They’ll just upload huge images without considering bandwidth and disk space. Soon you have hundreds images above 2MB in size cluttering your precious harddisk space and taking up all of the bandwidth with just a few pageviews.

So how do you conquer this problem? Well, resize the images automatically. Keeping their ratio and quality but reducing their size significantly.

I assume you have some knowledge with HTML and know how to upload a file. Therefor in these examples I use the file example.png and assume its in the images/ folder.

First the class that we will use to resize the image

<?php
/**
 * Class createImage
 *
 * @author Thijs Damen
 * @desc Creates a resized image from an existing image
 *
 */
class createImage {

	/*Folder where the current image can be found*/
	private $RootFolder;

	/*Folder relative from RootFolder to write the image to*/
	private $dstFolder;

	/*Name of the image*/
	private $ImageName;

	/*Image Extension*/
	private $ImageExtention;

	/*Maximum width for image*/
	private $img_maxWidth;
	/*Maximum height for image*/
	private $img_maxHeight;

	/*Current width of the image*/
	private $img_curWidth;
	/*Current height of the image*/
	private $img_curHeight;

	/*New width of the image*/
	private $img_newWidth;

	/*New height of the image*/
	private $img_newHeight;

	/*Quality of the new image*/
	private $imgQuality;

	/*Thumb of the image*/
	private $thumb;

	/*
	 * Constructor
	 * Recieves all parameters and immediatly creates the new image
	 */
	public function __construct($rootfolder, $imagename, $dstfolder, $maxwidth, $maxheight, $quality = 100) {
		$this->RootFolder 		= 	$rootfolder;
		$this->ImageName		=	$imagename;
		$this->dstFolder		= 	$dstfolder;
									//Neat trick that will get everything from the last . as a string (aka. extension)
		$this->ImageExtention 	=   end(explode(".", $imagename));
		$this->img_maxWidth		= 	$maxwidth;
		$this->img_maxHeight	=	$maxheight;
		$this->imgQuality		=	$quality;

		$this->calcImgSize();
	}

	/*
	 * Smart function that will calculate the new width and height of the image
	 */
	private function calcImgSize() {
		$src = getimagesize($this->RootFolder.$this->ImageName);
		$this->img_curWidth 	= 	$src[0];
		$this->img_curHeight	= 	$src[1];

		//Some calculation to determine wether the layout it landscape or portrait
		if(($this->img_curWidth / $this->img_maxWidth) > ($this->img_curHeight / $this->img_maxHeight)) {
			// landscape mode so recalculate based on width
			if ($this->img_curWidth > $this->img_maxWidth) {
				$this->img_newWidth = intval($this->img_maxWidth);
				$this->img_newHeight = intval($this->img_curHeight * ($this->img_maxWidth / $this->img_curWidth));
			} else {
				$this->img_newWidth = $this->img_curWidth;
				$this->img_newHeight = $this->img_curHeight;
			}
		} else {
			// portrait mode so recaulculate on height
			if ($this->img_curHeight > $this->img_maxHeight) {
				$this->img_newHeight = intval($this->img_maxHeight);
				$this->img_newWidth = intval($this->img_curWidth * ($this->img_maxHeight / $this->img_curHeight));
			} else {
				$this->img_newWidth = $this->img_curWidth;
				$this->img_newHeight = $this->img_curHeight;
			}
		}
		//GD Function that returns an image identifier based on width and height
		$this->thumb = imagecreatetruecolor($this->img_newWidth, $this->img_newHeight);
		if (!$this->createImage()) {
			return false;
		}
	}

	/*
	 * Create the new image based on extension
	 */
	private function CreateImage() {
		/*
		 * Switch the extension. so we know what type of image we are dealing with
		 * All of these will use their respecitve create function (imagecreatefrom)
		 * Create a copy from it and write it to the disk
		 */
		switch ($this->ImageExtention) {
			case "jpg":
			case "jpeg":
				header("Content: image/jpeg");
				$imgcreate = imagecreatefromjpeg($this->RootFolder.$this->ImageName);
				$imgcopy = imagecopyresampled($this->thumb, $imgcreate, 0,0,0,0, $this->img_newWidth, $this->img_newHeight, $this->img_curWidth, $this->img_curHeight);
				return imagejpeg($this->thumb,$this->RootFolder.$this->dstFolder.$this->ImageName,$this->imgQuality);
			break;
			case "png":
				header("Content: image/png");
				$imgcreate = imagecreatefrompng($this->RootFolder.$this->ImageName);
				$imgcopy = imagecopyresampled($this->thumb, $imgcreate, 0,0,0,0, $this->img_newWidth, $this->img_newHeight, $this->img_curWidth, $this->img_curHeight);
				/*
				 *  This is a special case. Where imagejpeg and imagegif take their quality parameter as 0 to 100.
				 *  0 being the worst and 100 being best. imagepng expects 0 to 9, 0 being the best. 9 being the worst.
				 *  So we have to do some calculation for the quality
				 */
				$quality = ($this->imgQuality - 100) / 11.11;
				$this->imgQuality = round(abs($quality));
				return imagepng($this->thumb,$this->RootFolder.$this->dstFolder.$this->ImageName, $this->imgQuality);
			break;
			case "gif":
				header("Content: image/gif");
				$imgcreate = imagecreatefromgif($this->RootFolder.$this->ImageName);
				$imgcopy =  imagecopyresampled($this->thumb, $imgcreate, 0,0,0,0, $this->img_newWidth, $this->img_newHeight, $this->img_curWidth, $this->img_curHeight);
				return imagegif($this->thumb,$this->RootFolder.$this->dstFolder.$this->ImageName,$this->imgQuality);
			break;
		}
	}
}

How to use this class? Simple:

I have an image in the images/ folder named image.png. It is 4096×3072 pixels in size (and 1.4MB big) currently and I want to resize it to a maximum of 512×512.

new createImage("images/", "image.png", "thumbs/", 512, 512, 80);

This will now create a image in images/thumbs/, still named image.png of size: 512*384 with a little less quality then the original, but little chance you will actually notice the difference (also, it is only 56KB big, which is 2500% smaller(!) then the original).

The best part about this. I can do the same with my JPEGs and GIFs without a hassle. look

new createImage("images/","image.jpg", "thumbs/", 512, 512, 80);

And this will do exactly the same just for the JPG extension. So instead of having to remember 3 different functions, I can just use a single class which holds all the logic for me.

There are some prerequisites. The destFolder has to be writable and you have to have the GD library installed. Also, it might be necessary to temporarily increase the memory limit of php.

ini_set('memory_limit', '256M');

I hope this helps someone with resizing their images in a good way. Keeping proportions and reducing image size significantly.

20
Dec

How To: Create your own Content Distribution Network

There are many Content Distribution Networks (CDN) aka Content Delivery Networks out there so why create your own? I for one argue that most CDNs out there don’t host enough code.

Here’s a list that google supports:

Chrome Frame
Dojo
Ext Core
jQuery
jQuery UI
MooTools
Prototype
script.aculo.us
SWFObject
Yahoo! User Interface Library (YUI)
WebFont Loader

This may seem as a long list, but what if you use jQuery plugins like jQuery validate or Fancybox often?

Why not create your own CDN system to host the code you use the most (You can even choose to use googleapis for all the libraries that are available and use your own CDN for the ones that are not). Personally, I prefer that option.
Another benefit of hosting your own CDN is that if you create web applications using a in-house made CMS you can also host its icon-sets and other frequently accessed static images.

The reason’s to use a CDN are simple and straight-forward:

Using a CDN will save you bandwich
Using a CDN will greatly increase your scripts load speed

So how do you create your own CDN?

#1 Setup the domain

If it is possible create a new domain name and disable the use of cookies. Why? RavelRumba explain it in this post. If you can not afford, or do not want to pay for a new domain you can consider creating a subdomain and then making sure cookies are only saved on the www.* domain.

#2 Figure out a filetree structure

Your filetree is very important when setting up your CDN. Yes, you can simply put every file you need in the root directory of the webserver but this is clumbersome and after some time will become confusing.
Google uses the following syntax:

http://ajax.googleapis.com/ajax/libs/library/version/filename
or
http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.js

You can also use this syntax or use a more simplified version. Which I personally prefer since the code you should host on your CDN often does not come with that many versions.

http://yourwebsite.com/library/filename

http://yourwebsite.com/library/plugins/pluginname/filename

http://yourwebsite.com/plugins/pluginname/filename

http://yourwebsite.com/images/imagename

I then use the library folder to host specific libraries (and their specific plugins), the plugins folder to host plugins (such as modernizr) and the image folder to host my frequently used images.

#3 Making it perform

As said before, you should disable cookies on the domain of your choice.

Because you are using a CDN you can use a custom .htaccess file to optimise the content distrubution. Set far-future (the content is static anyway) expire headers and deflate any content delivered.

.htaccess example (mostly stripped from the –awesome- html5boilerplate created by the, maybe even more awesome Paul Irish

#Set proper content-types for your files
# Proper svg serving.
AddType     image/svg+xml              svg svgz
AddEncoding gzip                       svgz           

# webfonts
AddType application/vnd.ms-fontobject  eot
AddType font/truetype                  ttf
AddType font/opentype                  otf
AddType font/woff                      woff          

# assorted types
AddType image/vnd.microsoft.icon       ico
AddType image/webp                     webp
AddType text/cache-manifest            manifest
AddType text/x-component               htc
AddType application/x-chrome-extension crx

# gzip compression.
<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE text/html text/plain text/css application/json
  AddOutputFilterByType DEFLATE text/javascript application/javascript application/x-javascript
  AddOutputFilterByType DEFLATE text/xml application/xml text/x-component
  <FilesMatch "\.(ttf|otf|eot|svg)$" >
    SetOutputFilter DEFLATE
  </FilesMatch>
</IfModule>

<IfModule mod_expires.c>
  Header set Cache-Control "public"
  ExpiresActive on

# Perhaps better to whitelist expires rules? Perhaps.
  ExpiresDefault                          "access plus 1 month"

# cache.manifest needs re-requests in FF 3.6 (thx Remy ~Introducing HTML5)
  ExpiresByType text/cache-manifest       "access plus 0 seconds"

# favicon (cannot be renamed)
  ExpiresByType image/vnd.microsoft.icon  "access plus 1 week" 

# media: images, video, audio
  ExpiresByType image/gif                 "access plus 1 month"
  ExpiresByType image/png                 "access plus 1 month"
  ExpiresByType image/jpg                 "access plus 1 month"
  ExpiresByType image/jpeg                "access plus 1 month"
  ExpiresByType video/ogg                 "access plus 1 month"
  ExpiresByType audio/ogg                 "access plus 1 month"
  ExpiresByType video/mp4                 "access plus 1 month"
  ExpiresByType video/webm                "access plus 1 month"

# webfonts
  ExpiresByType font/truetype             "access plus 1 month"
  ExpiresByType font/opentype             "access plus 1 month"
  ExpiresByType font/woff                 "access plus 1 month"
  ExpiresByType image/svg+xml             "access plus 1 month"
  ExpiresByType application/vnd.ms-fontobject "access plus 1 month"

# css and javascript
  ExpiresByType text/css                  "access plus 1 week"
  ExpiresByType application/javascript    "access plus 1 week"
  ExpiresByType text/javascript           "access plus 1 week"
</IfModule>

# Since we're sending far-future expires, we don't need ETags for
# static content.
FileETag None

# use utf-8 encoding for anything served text/plain or text/html
AddDefaultCharset utf-8

# force utf-8 for a number of file formats
AddCharset utf-8 .html .css .js .xml .json .rss

#4 All done

It’s that simple to create a very basic CDN. Obviously there are other more advanced topics we could discuss about how to optimize the distribution of content, but this is in essence a CDN that does what it promises.

17
Dec

Hello Blog

 ('&%:9]!~}|z2Vxwv-,POqponl$Hjig%eB@@>}=<M:9wv6WsU2T|nm-,jcL(I&%$#"
 `CB]V?Tx<uVtT`Rpo3NlF.Jh++FdbCBA@?]!~|4XzyTT43Qsqq(Lnmkj"Fhg${z@>

or HELLO WORLD!

Starting this blog thanks to Scott Hanselman amazing talk at Wintellect’s Devscovery where he says that Every Developer Needs a Blog. Well, I consider myself a developer so here’s my blog.

For my very first blog post I’ll just leave you with a function that I myself use atleast 15 times a day. Now I know it is bad practice to be outputting data to the screen (instead of using actual logging functions), But this one is just to good.

<?php
function print_pre($array) {
    echo '<pre>';
    print_r($array);
    echo '</pre>';
}

Simple, but effective.