Mike Pearce – blog

Namespaces, unit testing and dependency injection (with typehinting)

Posted in Uncategorized by Mike Pearce on May 26, 2010
Bad Medicine by Vermin Inc @ Twitter

Injection Dependency STRAIGHT INTO YOUR OCCIPITAL LOBE!

I’ve struggled with the concept of unit testing and how to deal with dependencies a lot in the past. The solution seemed to be just out of reach. I knew there WAS a solution and not a hacky one either, one that was elegant and worked well. I never really found it. PHPUnit seemed to be well written but not particularly well documented and a lad like myself, one who is a few sandwiches short of a picnic at times, almost gave up.

Almost.

I know and understand the benefits of unit testing and I understand the disadvantages. Unit testing is a weapon that can mite out retribution and vengence if not used properly. There are plenty of posts on the web about the wrong way to unit test and the hazards of them, so I’ll leave you to find them.

Aaaaaanyway, writing your application so that it uses the dependency injection pattern is the best method for making sure it’s uncoupled and modular enough to write some efficient, unit tests (ie: tests that test just a unit of code). Especially when they depend on objects which link to databases, or web services or other crazy stuff that you don’t really want or need to instantiate in your test suite. The problem that I encountered time and time again was when you mocked an object to pass into one test, then you wanted to actually test the REAL version of the object you’d previously mocked or stubbed, you couldn’t. PHP would throw an error:

Code.php:

<?php
    class badger {
    }
    class badger {
    }

%> php code.php
Fatal error: Cannot redeclare class badger

See? It’s a predicament, especially when you want to do some unit testing.

<?php
class weather {
    /**
     * @param string $postcode
     * @return object
     */
    public function getWeatherFromPostcode($postcode) {
        //.. connect to a web service
        $weather = new ThirdPartyWeatherService();
        return $weather->getWeatherAt($postcode);
    }

}

class stuff {

    public function isItRaining($postcode)
    {
        return (
                    $this->weather->getWeatherFromPostcode($postcode)
                          ->precipitation = 'RAIN'
                        ? TRUE
                        : FALSE
               );
    }

    public function injectWeatherObject(weather $o) {
        $this->weather = $o;
    }
}

class doTest extends PHPUnit_Test_Case {

    public function testIsItRaining() {
        $stuff = new stuff();
        $stuff->injectWeatherObject(new weather());
        $this->assertTrue($stuff->isItRaining('RH2 9SS'));
    }
}

The problem with the above code is that, the test may pass or false randomly depending on the weather. Clearly, this is a nightmare! No one thinks that there code is going to behave differently depending on the weather, that’s CRAZY TALK MAN! Well, this code would, especially if the weather is as strange as it is in Reigate.

So, how to get round this: dependency injection and mock objects. See, we had the foresight to inject the weather object into the stuff object. That means we’re halfway there (if you’d just instantiated the weather object in the stuff object, this would be much harder). To workout the solution to this, we need to ask a question: What are we testing?

The answer is, we’re testing the stuff class and, more specifically, the isItRaining() method. So, what do we need to do to test that? Well, for a start, we don’t actually need the full weather object (which might connect to a 3rd party service, which in turn, might be down, or slow or whatever – meaning the test might fail NOT because the unit is broken, but because a dependency is…), we only need to AN object with a ‘precipitation’ property. So, this means we can mock it up. Woo!

Code.php:

class weather {
    /**
     * @param string $postcode
     * @return object
     */
    public function getWeatherFromPostcode($postcode) {
        $w = new stdClass;
        $w->precipitation = 'RAIN'
        return $w;
    }

}

class stuff {

    public function isItRaining($postcode)
    {
        return (
                    $this->weather->getWeatherFromPostcode($postcode)
                          ->precipitation = 'RAIN'
                        ? TRUE
                        : FALSE
               );
    }

    public function injectWeatherObject(weather $o) {
        $this->weather = $o;
    }
}

class doTest extends PHPUnit_Test_Case {

    public function testIsItRaining() {
        $stuff = new stuff();
        $stuff->injectWeatherObject(new weather());
        $this->assertTrue($stuff->isItRaining('RH2 9SS'));
    }
}

Now, we know that it’ll ALWAYS be raining in Reigate (which sucks, but well, what can you do? All in the name of great testing, right?). This test will now pass, unless someone changes isItRaining() and it breaks. But that’s what unit testing is for.

Huzzah, let’s party. Except, we’ve introduced a problem here; what happens if we now weant to test a method in the REAL weather object:

Code.php


class weather {

    public function getTypesOfCloud($type)
    {

        $clouds = array
        (
            'high' =>
                array('Cirrus', 'Cirrocumulus', 'Cirrostratus')
            'medium' =>
                array('Altostratus', 'Altocumulus',)
            'low' =>
                array('Stratocumulus', 'Stratus',)
        );
        if (isset($clouds[$type])) {
            return $clouds[$type];
        }
        else {
            return FALSE;
        }

    }
    /**
     * @param string $postcode
     * @return object
     */
    public function getWeatherFromPostcode($postcode) {
        ..
    }
}
class weather {
    /**
     * @param string $postcode
     * @return object
     */
    public function getWeatherFromPostcode($postcode) {
        $w = new stdClass;
        $w->precipitation = 'RAIN'
        return $w;
    }

}

class stuff {

    public function isItRaining($postcode){ ... }

    public function injectWeatherObject(weather $o) { ... }
}

class doTest extends PHPUnit_Test_Case {

    public function testIsItRaining() { ... }

    public function testGetClouds()
    {
        $weather = new weather();
        $this->assertEquals(
            array('Cirrus', 'Cirrocumulus', 'Cirrostratus'),
            $weather->getTypesOfClouds('high')
        )
}

%> php code.php
Fatal error: Cannot redeclare class weather

OMFG! Now we’ve got two classes called the same thing, this makes it impossible to TEST THEM OMG NOO!

I should mention here that you’ll probably not have all your tests in one file, they’ll probably be split across multiple files and directories. They’re all in one file here to make things easier. However, this doesn’t mean you won’t get the same problem.

The easier thing to do would be to rename the mock weather object to something like ‘weatherMock’, but we can’t because our stuff class expects the object that is injected to be an instance of the ‘weather’ class.

“Well, remove the typehinting dumbass!” I hear you cry, throwing your hands in the air.

I could, but I shan’t. Because I WANT the typehinting there, it’s a conract and I am BOUND by it. I must have a weather object passed in, I can’t have ANY OLD object being passed in now can I? What would happen if someone passed in a ‘cheese’ object? DOES CHEESE HAVE PRECIPITATION?

Anyway, this is how I chose to solve this problem:

Code.php

<?
namespace Weather {
    require_once 'PHPUnit/Framework.php';
    abstract class abstractWeather {
        public function getTypesOfCloud() {}
        public function getWeatherFromPostcode() {}
    }

    class weather extends abstractWeather {

        public function getTypesOfCloud($type)
        {

            $clouds = array
            (
                'high' =>
                    array('Cirrus', 'Cirrocumulus', 'Cirrostratus'),
                'medium' =>
                    array('Altostratus', 'Altocumulus'),
                'low' =>
                    array('Stratocumulus', 'Stratus'),
            );
            if (isset($clouds[$type])) {
                return $clouds[$type];
            }
            else {
                return FALSE;
            }

        }
        /**
         * @param string $postcode
         * @return object
         */
        public function getWeatherFromPostcode($postcode) {
            // Commented as this doesn't actually exist.
            //$weather = new ThirdPartyWeatherService();
            //return $weather->getWeatherAt($postcode);
        }
    }
}
namespace WeatherMock {

    class weather extends \Weather\abstractWeather {

        /**
         * @param string $postcode
         * @return object
         */
        public function getWeatherFromPostcode($postcode) {
            $w = new \stdClass;
            $w->precipitation = 'RAIN';
            return $w;
        }

    }

}

namespace Main {
    class stuff {

        public function IsItRaining($postcode) {
            return (
                        $this->weather->getWeatherFromPostcode($postcode)
                                      ->precipitation = 'RAIN'
                            ? TRUE
                            : FALSE
            );
        }

        public function injectWeatherObject(\Weather\abstractWeather $o) {
            $this->weather = $o;
        }
    }

    class doTest extends \PHPUnit_Framework_TestCase {

        public function testIsItRaining() {
            $stuff = new stuff();
            $stuff->injectWeatherObject(new \WeatherMock\weather());
            $this->assertTrue($stuff->isItRaining('RH2 9SS'));
        }

        public function testGetClouds()
        {
            $weather = new \Weather\weather();
            $this->assertEquals(
                array('Cirrus', 'Cirrocumulus', 'Cirrostratus'),
                $weather->getTypesOfCloud('high')
            );
        }
    }
}

So, the solution is two fold.

  1. I made both the weather classes (real and mock) an extension of the abstractWeather class. This is good, because it means my contract is still in place (if a little more flexible) and I can force people to use the methods prescribed.
  2. I wrapped the abstract class and the real object in a namespace and the mock weather object in another namespace. This means I can have two instances of a class named ‘weather’ as they reside in different namespaces. I could have named the mock weather object as ‘weatherMock’ and not had the namespaces, but adding them makes it much cleaner. If I know I’m using namespaces and not going to get into trouble with conflicting error names, I can be confident that, with a HUGE library of tests, when they’re all run together, I’m not going to get the error (I don’t know if a colleague created a weatherMock() class two days ago for a different part of the application, but for use in the same suite of tests – using namespaces means it doesn’t matter).

Now, you could use interfaces instead of abstract classes, but I prefer the abtract classes as they give you something concrete to work from and, really, if you’re wanting to ride modular, encapsulated code, then you SHOULD be using abstract classes.

The only other thing to remember (which caught me out) is that stdClass lives in the global space, so, whenever instantiating a new stdClass, it must be pre-pended with a forward slase: \stdClass (the same goes for anything that ISN’T in a namespace when you’re working in one: \PHPUnit_Framework_TestCase

Feel free to copy this code and run it in your own environment, it *should* run!

This is not the only, or definitive method of achieving this. But I think it fits my ideal of having an elegant solution and it means the actual code (and not the tests) isn’t mauled about JUST to make the tests fit. Which is where a lot of unit testing falls down.

If you have another method or other ideas for how to get round dependcy and classname conflicts, then I’d love to hear them. Please post in the comments below!

Google: please stop before you do evil…

Posted in Google, Irrelevant by Mike Pearce on May 25, 2010
evil pumpkin by Riv @ flickr

This could be Google (image by Riv @ flickr)

Will Googles new secure search page be evil? You betcha!

If Google make their search and serps pages load on a https (or SSL) URL, then any analytics software (with the exception of their own, natch ;) ) will be next to useless. Why? Because visitors landing on your non-https site from a https site will NOT have a referrer URL. Which means, you’ll have no idea where they came from, what their raw search term was or ANY of the cool stuff associated with a referrer URL.

PPC will still work and things on the querystring URL will be OK, so you’ll know exactly where the users came from. Google will also know everything about your visitors and so their analytics platform will still work as they’ll be able to track you and your underlying click.

This is a big problem and is causing a little bit of worry amongst those companies that compete against Googles analytics platform.

How can this be mitigated? Well, you could make all sites on the web secure. If you go from one SSL site to another, your referrer will be intact (assuming the certs are OK). Google could also append querystring parameters to any outbound click from an organic serp (?referer=google.co.uk&rawSearch=used+badgers).

Who knows? I’m interested to know what Google will do as this, to me, feels like they’re about to do something inherently evil (unless they’re planning on buying ALL companies that do analytics, or use the referrer for anything meaningful!)

Tagged with: , , , , , ,

Digital Economy Act: A letter to my MP

Posted in Irrelevant by Mike Pearce on May 19, 2010
Sam Gyimah

Here he is, Sam Gyimah (Click to visit his site)

Dear Sam Gyimah,
Firstly, I’d like to congratulate you on your recent appointment to MP. Well done. I’m keen to see how you’re able to take the baton from Peter Ainsworth and run with it – I’m expecting some great things!

The second reason for writing this letter to ask that you support the repeal of sections 11 to 18 of the Digital Economy Bill.

As you’re most definitely aware, the bill came to pass on the 8th April 2010. I feel that this Act was rushed through, without proper debate, without proper scrutiny and without discussion by people who actually know what it is they’re talking about (I would hate to think that my Intellectual Property address was being recorded here).

I’m quite sure that, given you’re appointment to the post of East Surrey MP wasn’t made flippantly, you’re the man to represent my views in parliament.

The act has some serious flaws, I for one don’t want my internet cut-off if I leave my wireless network unsecured and someone, without my permission, logs on and downloads the latest Simon Cowell tripe to listen to on their mobile phone, out loud, on the back of a bus (which is the subject for another email, on another day).

What would happen if I ran a cafe which offered free wireless as a means to generate extra custom and someone, using my free wireless, were to download a video of monkey dancing, which had a soundtrack which wasn’t licensed correctly? That’s right, my internet is cut off for someone else’s crime. In fact, it’s only “minor civil offence” as it’s infringement of copyright, NOT MURDERING A KITTEN!

That act holds the account holder liable, not the infringer, which, frankly, is preposterous. It’s like holding the post office responsible for me sending an illegal CD copy of the Teletubbies soundtrack to my nan. I mean, really?

Eric Joyce (the Labour MP for Falkirk West) has setup an all-party group to discuss the outcome of the Digital Economy Bill. Yes, I know he’s the opposition, but, whatever, this is an all-party group. I ask, no, I demand that my government get together and sort the Digital Economy Act out. I believe it has some INCREDIBLE flaws and, frankly, seems to have been written by record company executives.

That’s not the case, is it? Because that would be awful and probably illegal. Let’s agree to put that thought out of our heads Sam.

I probably don’t need to tell you that this is a big deal for me. I don’t often write to my MP as, on the whole, I’m generally happy with the way things are going, but I make my living from the internet so this is a serious issue for me, regardless of how flippant I may sound.

Sam, thanks for your time in reading this email.

Please contact Eric Joyce at joycee@parliament.uk for more information.

Kind regards,

Mike Pearce

The difference between git pull, git fetch and git clone (and git rebase)

Posted in Uncategorized by Mike Pearce on May 18, 2010

Update: So, over a year later and I’ve had some feedback from a colleague (thanks Ben!). Nothing here is drastically wrong, but some clarifications should help!

When I started out with git …

… who am I kidding, I’m still a git n00b. Today, I tweeted about git. I wanted to know what the differene between pull, fetch and clone is. After discovering that, really, 140 characters isn’t enough to answer the questions, I had a play around.

Git Pull

From what I understand, git pull will pull down from a remote whatever you ask (so, whatever trunk you’re asking for) and instantly merge it into the branch you’re in when you make the request. Pull is a high-level request that runs ‘fetch’ then a ‘merge’ by default, or a rebase with ‘–rebase’. You could do without it, it’s just a convenience.

%> git checkout localBranch
%> git pull origin master
%> git branch
master
* localBranch

The above will merge the remote “master” branch into the local “localBranch”.

Git fetch

Fetch is similar to pull, except it won’t do any merging.

 %> git checkout localBranch
 %> git fetch origin remoteBranch
%> git branch
master
* localBranch
remoteBranch
 

So, the fetch will have pulled down the remoteBranch and put it into a local branch called “remoteBranch”. creates a local copy of a remote branch which you shouldn’t manipulate directly; instead create a proper local branch and work on that. ‘git checkout’ has a confusing feature though. If you ‘checkout’ a local copy of a remote branch, it creates a local copy and sets up a merge to it by default.

Git clone

Git clone will clone a repo int a newly created directory. It’s useful for when you’re setting up your local doodah

%> cd newfolder
%> git clone git@github.com:whatever/something.git
%> git branch
 * master
remoteBranch
 

Git clone additionally creates a remote called ‘origin’ for the repo cloned from, sets up a local branch based on the remote’s active branch (generally master), and creates remote-tracking branches for all the branches in the repo

Git rebase

Finally, git rebase is pretty cool. Anything you’ve changed by committing to your current branch but are no in the upstream are saved to a temporary area, so your branch is the same as it was before you started your changes, IE, clean. It then grabs the latest version of the branch from the remote If you do ‘git pull –rebase’, git will pull down the remote changes, rewind your local branch, then replays all your changes over the top of your current branch one by one, until you’re all up to date. Awesome huh?

Finally..

If you get stuck, run ‘git branch -a’ and it will show you exactly what’s going on with your branches. You can see which are remotes and which are local. This is a good headsup before you start to break things! It’s worth remembering that git branches are basically just a pointer, so to be able to work with those commits you need a local branch which points to somewhere from which those commits are reachable.

Thanks to Ben for the extra stuff, clarifications and calling me an idiot when I get git wrong, because I am, as it’s really pretty simple, except for the simple.


	

A referendum every day.

Posted in Irrelevant by Mike Pearce on May 11, 2010
Metabograph, right panel - by A.M Kuchling at Flickr

A radio ... with buttons

Sounds exciting doesn’t it? That’s because it is and it could be easy. This post was written because of @phillprice tweeting this:

Here’s an idea – no taxes just an 090 number to call and we all get to vote on a policy a day.
- @phillprice

But it’s something I’ve thought for a while and discussed with those both cleverer and far more political than me.

Disclaimer: This post is perhaps a little off-topic, but it has computers in it, so it’s staying…

Imagine if you woke up every day, made yourself a nice little cappucino, sat on your Ikea bench at your Ikea table, opened your laptop and voted in a referendum … every day. This is a possibility.

I would love to take part in something like that, direct democracy, making my vote count every day. The kind of things you could vote for range from the trivial, to the hugely important. You logged into a website with your National Insurance number, or other such unique identifier and were presented with a page of questions and radio buttons. You simply selected “Yes” or “No” as a radio button for each question, or whatever the question warranted as an answer and move to the next, when you’re done, click submit. You could be asked:

  • Should the local council paint the lamp posts in the high street, or install new litter bins? (Read more…) (Closes in 3 days)
    • Paint lamp posts
    • Install litter bins
    • Either
  • Should the council raise council tax by 0.1% or 0.2% (Read more …) (Closes in 1 day)
    • 0.1%
    • 0.2%
    • Either
  • Should the UK enter the EU? (Read more …) (Closes in 2 weeks)
    • Yes
    • No

So, a series of question, with answers and a read more link. The link could take you to an impartially authored page describing the pros and cons of the choices. Being online means you could also get the skinny from other websites, visit Wikipedia (if you dare) and discuss it with your friends (if they’re local). The questions remain open for a period of time, giving you breathing space to research the questions you need (you don’t need to research painting lamp posts vs new litter bins, do you?).

This gives the government and the local councillors more time to spend on the bigger issues they tackle without having to worry about every little detail. We’ll be more involved and the country could, for some things, get more of a say in what goes on. The side affects being that perhaps more people will become interested in policitcs and that is always a good thing.

If you didn’t want to vote, then you don’t have to. You don’t now anyway and the decisions are made, but imagine if you could be part of something like this every day. You’d feel more satisfied knowing that you had somewhere to throw your empty double-caff-vanilla-frappacino-vent-latte cup intead of freshly painted lamp posts and that you had something to do with it.

Localised e-voting

Posted in Irrelevant by Mike Pearce on May 8, 2010
"Votes For Women" Canvas Bag, ca. 1920 by Cornell University Library

Really? Them too?

I was having a discussion with @tompoline about how I wouldn’t have had to stay up so late on election night if only someone would introduce e-voting. Now, being a gnarly sysad, the first thing Tom did was to jump on the security aspect of that particular remark.

I think you’ll find that someone will work out a way to hack it, and that would be the end of e-voting.

So, we started discussing why we need evoting and it boils down to not having to count thousands of crosses on thousands of bits of paper, then re-count them all when some grey twat in a suit decides that he should’t have lost his seat, or someone admits to not knowing whether they’d counted 304 votes or 305 and everyone throws their arms in the air and demands a recount.

Aaaaanyway, we decided that the best way to do it and NOT get hacked would be localised e-voting. Imagine, if you will, walking into your polling station, handing the dour old lady the card you had delivered through the post some weeks ago, she reads out your name, someone else crosses it off and hands you a polling card and you step into a booth. See, this is all very familiar, as it’s pretty much what you do now, except the dour old lady and her accomplice didn’t give you a polling card, they just ushered you toward a booth…

… which has a row of large, coloured buttons, each having a label on stating the name and party of each of the candidates in your ward and you PRESS one of the buttons and step out of the booth. Job done. You walk away, proud to have taken part in democracy.

However, behind the scenes, your press of the button has stored your vote on a local computer, stored somewhere in the polling station connected to the buttons by USB, or perhaps Cat-5 or something. There isn’t any way to remote into the box as it’s not on any network (except perhaps the network you had linking all the buttons together). So, to hack this box, someone would have to be on the premises and that is a risk that isn’t particular to e-voting, this can also happen with existing paper ballots.

Anyway, this seems like the ideal method; just as secure as the current method, but a winner could be derived very shortly after the polling stations closed and myself and David Dimbleby could get an early night.

“But what if there’s a fire?” I hear you cry, “or a powercut? Or the machine corrupts itself half way though? WHAT MAN WOULD WE DO THEN?”

voting instructions by muffel

Read this, then ignore

Well, calm yourself. This is where we had an excellent idea. Every time you push the button and the machine digitally stores your vote it also does something slightly more analogue. Once you push the button, a hole punching machine attached to the computer puts a hole in a ream of paper. Or, the computer is hooked to another machine which spits little plastics tokens into candidate corresponding buckets (with secured lids). That way, if the computer becomes unusable, the polling station can fall back to more traditional methods of capturing votes. Either counting platic chips in a bucket, or counting holes on a ream of paper. Both of which would be easier that counting crosses on paper. I mean, they could do this AS WELL as the e-voting, it would still be quicker.

The conversation then degenerated into how you could use iPod touches instead of buttons and make the whole process a bit more interactive, or fun if you will. Pressing on the blue button marked “Conservative Candidate” would have the iDevice ask you a series of questions to ascertain whether you are sober enough to vote. “Are you sure you want to vote Tory? Are you REALLY sure?”.

You could also have the voter answer a series of questions similar to the “Find out who you should vote for!” online quizzes, which ask you a series of questions based on current policies, and at the end say “Your answers indicate your ideals are closest matched to the Liberal Democrat candidate” then, even if you don’t watch the news or read up on the party manifestos, you’re still making an informed decision on who to vote for and not just voting for someone because you like the cut of their jib.

So, technology can help with the democratic process, clearly. Is it just me, or does anyone else think that making a pencil mark next to a name on a piece of paper, before posting it in a back box is a little, well, archaic?

macupgrades.co.uk: a whole jar of awesomesauce

Posted in Uncategorized by Mike Pearce on May 7, 2010
A head assembly on a Seagate hard drive

Not my harddrive - Image by Robert Scoble @ Flickr

I recently purchased a new hard drive for my aging Mac from www.macupgrades.co.uk. Storing everything on my NAS so I have more space on my mac is a pain-in-the-ass, so I decided to get a larger capacity drive so I can store everything locally as well. I placed an order on the Tuesday and paid for the courier delivery as I needed the drive before the weekend.

On the Wednesday I recieved an email stating that the drive had been shipped and would be with me on Friday – awesome. I was very excited, I nearly bought a macchiato to celebrate.

Anyway, the next day, I received an email stating there had been a problem and that the drive hadn’t shipped so wouldn’t be with me until the following Monday. I was pretty pissed off about that, so I emailed www.macupgrades.co.uk and said, I thought the item was in stock and would be delivered by Friday, if I’d known there would be a problem, I’d have ordered another item.

I also asked if I could be refunded the difference between the Courier and Royal Mail delivery as paying for the courier delivery wouldn’t have got me the item on time. I was amazed to recieve this response:

I’m sorry there has been a delay with your item. This was due a collection

issue with our courier company.

We can as a gesture of goodwill offer you the Seagate Momentus 7200 RPM

500GB drive for you at the same price – delivery for Friday.

Or we can progress with the Scorpio for delivery Monday with the difference

on the courier refunded.

Please let us know.

Best Regards

Gavin

Awesomesauce! A better harddrive (I only ordered a 5400) and delivery the next day! Well, I asked if they could send me a drive, any drive, by Friday and lo, when I arrived at work this morning, it was waiting for me. A pre-9am delivery no less.

So, thank you macupgrades.co.uk, your customer service is second to none and I will be a customer for life now.

The one about HTTP Request Methods and the RESTful API

Posted in APIs, Code, PHP by Mike Pearce on May 5, 2010
Nobody Nose The Trouble I've Seen - BrittneyBush

Clearly a RESTful dog (photo by BrittneyBush on Flickr)

What HTTP Methods should I use for what API methods? A quesion which, until I began working on a RESTful API, I’d never needed to ask. I’ve been writing an ACL service which required an API. I thought “I don’t know how to write a RESTful API, this is a good opportunity to learn something you reprobate.” so, these are my collected musing on the subject, based on my recent experience.

I started with POSTing to URIs like /v1/adduser and /v1/getuser, which worked OK, it just didn’t feel right. I read through the O’Reilly book and decided I needed to up my game and make some changes.

You see, at first glance PUT and POST seem the same and, for a large part, they are, they RFC says:

The fundamental difference between the POST and PUT requests is reflected in the different meaning of the Request-URI. The URI in a POST request identifies the resource that will handle the enclosed entity. That resource might be a data-accepting process, a gateway to some other protocol, or a separate entity that accepts annotations. In contrast, the URI in a PUT request identifies the entity enclosed with the request — the user agent knows what URI is intended and the server MUST NOT attempt to apply the request to some other resource. If the server desires that the request be applied to a different URI,

Which is a but dull and dry, like beige. Anyway, when writing a RESTful API, you really want to adhere to the proper use of verbs and methods, it all seems a bit airy fairy until you’re given some context and some examples.

__construct()

Let’s say you’re writing an ACL (Access Control Layer) and the ACL needs to have an API which you can use to ask if a user has access to a resource, to add users, resources and roles, perform delete commands and modify stuff. You’ve also decided that the API should be RESTful as it’s a useful way to learn something new.

Your API is going to be located at: http://api.awes.ome/v1/ as it’s good to version APIs, but that’s for another post. For the rest of this article, I’ll be missing out the hostname and just using the URI, because that’s what REST is all about.

REST URI and methods looks like this:

HTTP_METHOD {version}/{resource}/{modifiers}
{body}

Don’t forget, you’ll only have a {body} depending on the HTTP_METHOD you use (GET, for example, doesn’t have a body), speaking of GET…

GET

So, you’ll need to have the concept of “users” (or, in whatever parlance you’d like, “people” or “persons” if you’re a bit PC or even “accounts”) these are a fixed resource. Something that will always be and can be counted as 1 or n. Now we get right down to it, a user is a  “resource”, possibly not in the context of your application, the ACL, but definately in the context of your API, “resource”‘s should have their own URL, like so:

GET /v1/user/mikepearce

What this URL is saying is this: From version one of the api, get me all the data about the user mikepearce. I’m just getting data, it’s read only and the only filter, or where clause of my “get me..” statement is the username. But it is also the resource, doing this:

GET /v1/user

Is pretty useless, what would it return? Not multiple users as it’s not plural. So, the real resource is /user/mikepearce

If you wanted to get multiple users, then “users” is also a resource (not the use of “IS” not “ARE”, you’re not interested in the ACTUAL users, but the concept of all of them or one of them, see? No? Well, you will; keep reading).

GET /v1/users

That will get me all the users, if I want a filter, I can add it on in multiple ways, again, for another post, but you could do it explicitly like this:

GET /v1/users/active/1/gender/male/newsletter/1

or implicitly, like this (your application should know what to do with each URI segment):

GET /v1/users/1/male/1

DELETE

Delete, this one is easy. If you’re deleting a resource (in this case a user) then you’d send this:

DELETE /v1/user/mikepearce
DELETE /v1/users/active/0/ (although, this is a little dangerous).

What is the difference between PUT and POST?

Ah now, here’s the rub! Essentially it boils down to this:

“The client uses PUT when it’s in charge of deciding which new URI the resource should have. The client uses POST when the server is in charge…” – O’Reillys RESTful web services

See? Easy, right. But that doesn’t quite cover everything. Let’s give you some context and then some more context:

PUT

You want to add users to your ACL, who wouldn’t? Afterall an ACL is useless without users, right? Easy and probably finished a long, long time ago. But I digress:

PUT /v1/users/bobmonkhouse

That will add a user called “bobmonkhouse” to your ACL. You’re “PUTting” something somewhere. Imagine that your service is like a shelf and you’ve got a shelf labelled “users” and your arm is the HTTP PUT method and … yeah, perhaps that analogy is a bit thin. Anyway, simply stated, you’re putting a resource at a location.

“Wait!” you cry, “How do I add email addresses and date of births and stuff to the user!?” you sit down in a huff with your arms crossed and eyeball me.

Well, you would still use put, y’see – PUT has a body, rather like POST does. So, if you wanted to PUT extra data along with the resource you’d do this:

PUT /v1/users/tedrogers
email=tedrogers@threetwoone.co.uk&
dob=1935-07-20&
friend=Dusty%20Bin&
password=j0kersw1ld
 

408 Request Timeout by Ape Lad at Flickr

PUT: LIKE A BASKETBALL IN THE FACE .. THE FACE! (image by Ape Lad)

I’ve URL encoded the body of the PUT, but it could be anything, some of you youngsters might even want to use JSON. From here, you can use the URI to create the resource and the body data to populate it.

Updating a resource, for example if a user wants to change their password, or their email address, is as simple as sending the same request as above. You’re PUTting data onto the server, or PUTing document changes to the server. You should, in all instances, send the entire document. So, even if the user was just changing his email address, you should  still send everything you sent with the original PUT to create the resource.

You don’t have to, but it’s the correct way to do things (also, it means you don’t have to check for the existence of all the fields and do if..else wrapped update statements, you can assume that all the fields are there (although that doesn’t mean you shouldn’t validate them!)).

PUT requests to a URI should be idempotent, that is, PUTing the same thing, multiple times should have exactly the same affect as sending it once. If you make the above PUT request three or four times, it won’t create three of four users called tedrogers, it’ll only create one, then, each subsequent PUT updates the resource, whereas a POST will create the user the first time, then, when you make the request again, depending on how you’ve set up your application will either give you an error stating the user exists, or an internal server error, or something entirely different.

POST

Having read the above, you’re possibly wondering, “What would I want to do with POST? I can do everything with I need with PUT”. Well, maybe you can, but probably you can’t.

As I mentioned above, POSTing the same thing multiple times will result in possible multiple copies of your resource, remember, you’re letting the server decide where to put this resource and if it’s something there CAN be multiples of, such as a blog post, comment or message board post, it will create multiples.

Another use for POST is when you’re authenticating a user on you ACL. As well as the ACL informing you of whether a user can access a resource, it’s also got an authenticate method, letting you send a username and password and receiving a response as to whether the user can log in, for example.

In this instance, you’d send a POST:

POST /v1/authenticate
username=jimbowen&
password=bulls3y3

/v1/authenticate/jimbowen isn’t really a resource, it could be, I guess, but just doesn’t feel like a resource. So, you send the username and password as part of the POST body.

“Why can’t I use GET? I’m not actually asking for anything to be saved or stored on the server.” you ask, having read the all the above and taken it in, go you! The problem with using this:

GET /v1/authenticate/username/jimbowen/password/bulls3y3/

Is that the password is now clearly visible in the URI and will be recorded in the server logs. This is NOT a “good thing”, this is a very bad thing. The content of the POST body isn’t recorded anywhere (unless you actually record it) so is ideal for this kind of request.

__destruct()

So, in conclusion, what have we learnt? GET, DELETE and PUT are the three things you need for adding, reading and deleting resources when the client has control over the resource URI and POST is used for when the server decides what to do with the request, whether to create a resource, or to return a value based on criteria you want to remain hidden.

There are, probably, other (better) uses for POST and please leave some extra examples in the comments for others. The above is really just my observations from creating a RESTful API for my ACL.

Follow

Get every new post delivered to your Inbox.

Join 912 other followers