A bit of Behat: clicking on text

So, I’ve been messing about with Behat recently to try some tests of our site. I was given the challenge of testing the ‘Refine By’ functionality of our directory search. I thought it would be easy (it turned out to be, but not until I’d wrapped my head around XPath). Anyway, the refine by options are just bits of text in a label, or even a span in a label:

<ul>
	<li><label><span>Refine Heading</span></label>
		<ul>
			<li><label>Refine criteria</label></li>
		</ul>
	</li>
</ul>

Not great HTML you’ll probably agree, but clicking on the ‘Refine Heading’ or ‘Refine Criteria’ shows and selects criteria with javascript. However, there isn’t an out-of-the-box assertion for this with Behat, so after some fumbling around, I made (most of (some help from General Grecki)) this:


/**
    * Click some text
    *
    * @When /^I click on the text "([^"]*)"$/
    */
    public function iClickOnTheText($text)
    {
        $session = $this->getSession();
        $element = $session->getPage()->find(
            'xpath',
            $session->getSelectorsHandler()->selectorToXpath('xpath', '*//*[text()="'. $text .'"]')
        );
        if (null === $element) {
            throw new \InvalidArgumentException(sprintf('Cannot find text: "%s"', $text));
        }

        $element->click();

    }
Advertisements

PHP Code Sniffer and Lambda functions

Just a quick snippet here. If you’re using PHPCS to check your source code, it’ll barf over this:

<?php
class testClass {
    public function aFunction($array)
    {
        $anotherFunction = function()
        {
            /* do some stuff *
        }
        array_walk($array, $anotherFunction);
    }
}

So, I modified ValidFunctionNameSniff.php. Add this method:

    /**
     *
     * Test to see whether the function name exists, if not, it's a lambda
     * function
     *
     * @param string $functionName
     * @todo Beef this up a bit by testing to see whether or not it's a lambda
     * function, or someone just wrong "function (){ }"
     */
    private function isLambda($functionName)
    {
        // $functionName = function($args) { /** do Stuff **/ }
        if (trim($functionName) === '') {
            return true;
        }
        return false;
    }

Then modify this:

if (PHP_CodeSniffer::isCamelCaps($testMethodName, false, $isPublic, false) === false) {

to this:

if (!$this->isLambda($methodName) && PHP_CodeSniffer::isCamelCaps($testMethodName, false, $isPublic, false) === false) {

and this:

if ($validName === false) {

to this:

if (!$this->isLambda($functionName) && $validName === false) {

et voila. Entire class below:

<?php
/**
 * All rights reserved. No part of this code may be reproduced, modified,
 * amended or retransmitted in any form or by any means for any purpose without
 * prior written consent of Digital Window.
 * You must ensure that this copyright notice remains intact at all times
 *
 * @category DigitalWindow
 * @package phpcs
 * @copyright Copyright (c) Digital Window Ltd 2000-2009. All rights reserved.
 * @version $Id: ValidFunctionNameSniff.php 27739 2010-08-26 15:12:39Z mike.pearce $
 */

/**
 * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
 * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
 * @package phpcs
 */
class Dwin_Sniffs_NamingConventions_ValidFunctionNameSniff extends PHP_CodeSniffer_Standards_AbstractScopeSniff
{

    /**
     * A list of all PHP magic methods.
     *
     * @var array
     */
    private $_magicMethods = array(
                              'construct',
                              'destruct',
                              'call',
                              'callStatic',
                              'get',
                              'set',
                              'isset',
                              'unset',
                              'sleep',
                              'wakeup',
                              'toString',
                              'set_state',
                              'clone',
                             );

    /**
     * A list of all PHP magic functions.
     *
     * @var array
     */
    private $_magicFunctions = array(
                                'autoload',
                               );

    /**
     * Constructs a PEAR_Sniffs_NamingConventions_ValidFunctionNameSniff.
     */
    public function __construct()
    {
        parent::__construct(array(T_CLASS, T_INTERFACE), array(T_FUNCTION), true);

    }//end __construct()

    /**
     * Processes the tokens within the scope.
     *
     * @param PHP_CodeSniffer_File $phpcsFile The file being processed.
     * @param int                  $stackPtr  The position where this token was
     *                                        found.
     * @param int                  $currScope The position of the current scope.
     *
     * @return void
     */
    protected function processTokenWithinScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $currScope)
    {
        $className  = $phpcsFile->getDeclarationName($currScope);
        $methodName = $phpcsFile->getDeclarationName($stackPtr);

        // Is this a magic method. IE. is prefixed with "__".
        if (preg_match('|^__|', $methodName) !== 0) {
            $magicPart = substr($methodName, 2);
            if (in_array($magicPart, $this->_magicMethods) === false) {
                 $error = "Method name \"$className::$methodName\" is invalid; only PHP magic methods should be prefixed with a double underscore";
                 $phpcsFile->addError($error, $stackPtr);
            }

            return;
        }

        // PHP4 constructors are allowed to break our rules.
        if ($methodName === $className) {
            return;
        }

        // PHP4 destructors are allowed to break our rules.
        if ($methodName === '_'.$className) {
            return;
        }

        $methodProps    = $phpcsFile->getMethodProperties($stackPtr);
        $isPublic       = (in_array($methodProps['scope'], array('private', 'protected'))) ? false : true;
        $scope          = $methodProps['scope'];
        $scopeSpecified = $methodProps['scope_specified'];

        // If it's a private method, it must have an underscore on the front.
        if ($isPublic === false && $methodName{0} !== '_') {
            $error = "Private method name \"$className::$methodName\" must be prefixed with an underscore";
            $phpcsFile->addError($error, $stackPtr);
            return;
        }

        // If it's not a private method, it must not have an underscore on the front.
        if ($isPublic === true && $scopeSpecified === true && $methodName{0} === '_') {
            $error = ucfirst($scope)." method name \"$className::$methodName\" must not be prefixed with an underscore";
            $phpcsFile->addError($error, $stackPtr);
            return;
        }

        // If the scope was specified on the method, then the method must be
        // camel caps and an underscore should be checked for. If it wasn't
        // specified, treat it like a public method and remove the underscore
        // prefix if there is one because we cant determine if it is private or
        // public.
        $testMethodName = $methodName;
        if ($scopeSpecified === false && $methodName{0} === '_') {
            $testMethodName = substr($methodName, 1);
        }

        // If it's not a lambda function and doesn't
        if (!$this->isLambda($methodName) && PHP_CodeSniffer::isCamelCaps($testMethodName, false, $isPublic, false) === false) {
            if ($scopeSpecified === true) {
                $error = ucfirst($scope)." method name \"$className::$methodName\" is not in camel caps format";
            } else {
                $error = "Method name \"$className::$methodName\" is not in camel caps format";
            }

            $phpcsFile->addError($error, $stackPtr);
            return;
        }

    }//end processTokenWithinScope()

    /**
     *
     * Test to see whether the function name exists, if not, it's a lambda
     * function
     *
     * @param string $functionName
     * @todo Beef this up a bit by testing to see whether or not it's a lambda
     * function, or someone just wrong "function (){ }"
     */
    private function isLambda($functionName)
    {
        // $functionName = function($args) { /** do Stuff **/ }
        if (trim($functionName) === '') {
            return true;
        }

        return false;
    }

    /**
     * Processes the tokens outside the scope.
     *
     * @param PHP_CodeSniffer_File $phpcsFile The file being processed.
     * @param int                  $stackPtr  The position where this token was
     *                                        found.
     *
     * @return void
     */
    protected function processTokenOutsideScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
    {
        $functionName = $phpcsFile->getDeclarationName($stackPtr);

        // Is this a magic function. IE. is prefixed with "__".
        if (preg_match('|^__|', $functionName) !== 0) {
            $magicPart = substr($functionName, 2);
            if (in_array($magicPart, $this->_magicFunctions) === false) {
                 $error = "Function name \"$functionName\" is invalid; only PHP magic methods should be prefixed with a double underscore";
                 $phpcsFile->addError($error, $stackPtr);
            }

            return;
        }

        // Function names can be in two parts; the package name and
        // the function name.
        $packagePart   = '';
        $camelCapsPart = '';
        $underscorePos = strrpos($functionName, '_');
        if ($underscorePos === false) {
            $camelCapsPart = $functionName;
        } else {
            $packagePart   = substr($functionName, 0, $underscorePos);
            $camelCapsPart = substr($functionName, ($underscorePos + 1));

            // We don't care about _'s on the front.
            $packagePart = ltrim($packagePart, '_');
        }

        // If it has a package part, make sure the first letter is a capital.
        if ($packagePart !== '') {
            if ($functionName{0} === '_') {
                $error = "Function name \"$functionName\" is invalid; only private methods should be prefixed with an underscore";
                $phpcsFile->addError($error, $stackPtr);
                return;
            }

            if ($functionName{0} !== strtoupper($functionName{0})) {
                $error = "Function name \"$functionName\" is prefixed with a package name but does not begin with a capital letter";
                $phpcsFile->addError($error, $stackPtr);
                return;
            }
        }

        // If it doesn't have a camel caps part, it's not valid.
        if (!$this->isLambda($functionName) && trim($camelCapsPart) === '') {
            $error = "Function name \"$functionName\" is not valid; name appears incomplete";
            $phpcsFile->addError($error, $stackPtr);
            return;
        }

        $validName        = true;
        $newPackagePart   = $packagePart;
        $newCamelCapsPart = $camelCapsPart;

        // Every function must have a camel caps part, so check that first.
        if (PHP_CodeSniffer::isCamelCaps($camelCapsPart, false, true, false) === false) {
            $validName        = false;
            $newCamelCapsPart = strtolower($camelCapsPart{0}).substr($camelCapsPart, 1);
        }

        if ($packagePart !== '') {
            // Check that each new word starts with a capital.
            $nameBits = explode('_', $packagePart);
            foreach ($nameBits as $bit) {
                if ($bit{0} !== strtoupper($bit{0})) {
                    $newPackagePart = '';
                    foreach ($nameBits as $bit) {
                        $newPackagePart .= strtoupper($bit{0}).substr($bit, 1).'_';
                    }

                    $validName = false;
                    break;
                }
            }
        }

        if (!$this->isLambda($functionName) && $validName === false) {
            $newName = rtrim($newPackagePart, '_').'_'.$newCamelCapsPart;
            if ($newPackagePart === '') {
                $newName = $newCamelCapsPart;
            } else {
                $newName = rtrim($newPackagePart, '_').'_'.$newCamelCapsPart;
            }

            $error = "Function name \"$functionName\" is invalid; consider \"$newName\" instead";
            $phpcsFile->addError($error, $stackPtr);
        }

    }//end processTokenOutsideScope()

}//end class

PHP: Boolean Not or (int)!

Came across this little snippit today and I’ve never seen it before. My colleague, Ben Spencer, who is a bit of a wizard helped my addled brain understand it. I thought I’d create a small chunk of code to get my head round it as it’s quite useful.


<?php
        echo 'Length: '. 100 ."\n";
        // Prints: Length: 100
        echo 'Length(False): '. (100 - (int)! false) ."\n";
        // Prints: Length(False): 99
        echo 'Length(True): '. (100 - (int)! true) ."\n";
        // Prints: Length(True): 100
        echo 'Length(with int): '. (100 - (int)! 20) ."\n";
        // Prints: Length(with int): 100

Of course, the purists amongst you might rally against this, but I thought it was cool.

#Thimbl

So, I’ve been working on an open source project called Thimbl with some good friends of mine. It’s a Twitter clone that is open source, decentralised and free. The most important of those three is … well – all of them. Some blurb from thimbl.net site:

Thimbl is a Manifesto for the Open Web written in code. The most significant challenge the open web will need to overcome is not technical, it is political. Welcome to thimbl, the free, open source, distributed micro-blogging platform. If you’re tired of being locked in to one micro-blogging platform, or a single social network. Or you’re weary of corporations hi-jacking your updates in the pursuit of money, then thimbl is for you.

http://thimbl.net

One of the original motivating factors that caused us to get off our collective touches and get thimbl written was the Transmediale Open Web Award. This is a joint venture with Mozilla to award the best of a great bunch of Open Web projects funding and a welcome kickstart to build take the project to the next level. For myself and the thimbl team, winning this award and taking thimbl to a more mainstream public is the next step on the road to a more open web and this is nothing but a good thing.

So, after 100’s of applications were submitted to Tramsmediale, these were whittled down by a panel to just three great projects, thimbl being one of those. I really believe we can win this award and show the online world there’s more choice than just twitter or facebook and join the ranks of those trying to do things more openly, like identi.ca and diaspora.

You can help! We’ve got an account on Mozilla’s drumbeat.org and our votes there will be what wins this for the team. We’ll be given supported status and benefit from the great things the Mozilla foundation can do for open projects. Head over to the drumbeat.org site at https://www.drumbeat.org/project/thimbl-decentralized-micro-blogging-platform and start voting for us. If you’re a fan of the open web, or even just a fan of choice, then please help us out and vote!

git-svn and ReviewBoard

The cat in the tree is looking at me
This cat is ON MY BRANCH! Image by pinto_2003

The company I am currently at haven’t yet made the jump to git. It’s understandable; there are a number of large SVN repositories and only one (or two) of the developers know how to use git. So, it doesn’t make sense right now. I’m trying to get git-svn working accurately in my workflow before I try and explain the awesome-ness of git for my colleagues. I’ve run into two problems, one is svn:properties and how git doesn’t support them (except ignore) which is a pain, because it means that if I want to adhere to AWin coding standards and not have PHPUnderControl moan at me, I need to have svn:keywords and svn:EOL properties for each file. The hacky fix is to run a seperate SVN directory on my box, which I add files to, add the properties, then rebase them into git. It’s a pain.

The other problem is ReviewBoard. Well, it’s not such a massive problem now ReviewBoard has ‘post-review’ which allows you to send reviews to RB via the command line. But, sometimes, you just want a standard diff file which you can upload to RB, git diff doesn’t create files that ReviewBoard understands, which is where a little bit of bash scripting comes into play.

I found this: My (work) Git Workflow (You should read it all, it goes into managing branches and stuff, it’s a useful read if you’re just starting out with git-svn, thanks Seth!). Which almost worked. Except, for some reason, it was pumping out massive diff files that git diff wasn’t. So, I had a little bit of a fiddle and found that it uses the git config option svn-remote. Which, for my repo is set to the trunk. Which doesn’t make any sense if I want to run an diff on the branch I’m working on, so, I modified it:

#!/bin/bash
#
# git-svn-diff originally by (http://mojodna.net/2009/02/24/my-work-git-workflow.html)
# modified by mike@mikepearce.net
#
# Generate an SVN-compatible diff against the tip of the tracking branch

# Get the tracking branch (if we're on a branch)
TRACKING_BRANCH=`git svn info | grep URL | sed -e 's/.*\/branches\///'`

# If the tracking branch has 'URL' at the beginning, then the sed wasn't successful and
# we'll fall back to the svn-remote config option
if [[ "$TRACKING_BRANCH" =~ URL.* ]]
then
        TRACKING_BRANCH=`git config --get svn-remote.svn.fetch | sed -e 's/.*:refs\/remotes\///'`
fi

# Get the highest revision number
REV=`git svn find-rev $(git rev-list --date-order --max-count=1 $TRACKING_BRANCH)`

# Then do the diff from the highest revition on the current branch
git diff --no-prefix $(git rev-list --date-order --max-count=1 $TRACKING_BRANCH) $* |
sed -e "s/^+++ .*/&     (working copy)/" -e "s/^--- .*/&        (revision $REV)/" \
-e "s/^diff --git [^[:space:]]*/Index:/" \
-e "s/^index.*/===================================================================/"

You can also see it here: http://gist.github.com/582239

So far, it seems to be working for me. But it’s only until I get the department using git right, RIGHT?

git status on multiple repos

Show_status
git status is now better

Often a lot of the work I do is on services. This means that, in any given day, I’ll perhaps be working on one or more repos. As is the way of git, I commit often to the local working branch and they push to the master at the end of the day, or whenever I’ve got feature, or patch of fix or whatever that needs to go back upstream. First thing in the morning, I’ll also do a pull for each repo I’m going to work on, including dependent repos.

If you’ve got a lot or repos, this quickly gets tedious to cd into each directory, run git status, then git push or git pull, cd back out, then into the next directory, etc etc.

So, I wrote a wee python script called ‘show_status’

Usage: show_status [options]

Show Status is awesome. If you tell it a directory to look in, it'll scan
through all the sub dirs looking for a .git directory. When it finds one it'll
look to see if there are any changes and let you know. It can also push and
pull to/from a remote location (like github.com) (but only if there are no
changes.) Contact mike@mikepearce.net for any support.

Options:
  -h, --help            show this help message and exit
  -d DIRNAME, --dir=DIRNAME
                        The directory to parse sub dirs from
  -v, --verbose         Show the full detail of git status
  -r REMOTE, --remote=REMOTE
                        Push to the master (remotename:branchname)
  -p PULL, --pull=PULL  Pull from the master (remotename:branchname)

Just running ‘show_status’ will always run a non-verbose check on any sub dirs in your current dir, so:

%> show_status
-- Starting git status...
Scanning sub directories of /home/sites/
--/home/sites/gitstatus        : No Changes
--/home/sites/projectone    : No Changes
--/home/sites/project2        : No Changes
Done

You can also pass in a directory that you want to use, this is useful if you wanted to use it in other scripts, or if you’re not in a root directory:

%> show_status -d/home/sites

You can add a pull request, which will pull from a remote master. It’s important to note that it’ll only pull from a remote master if there are no local changes waiting to be committed. This is to force you to manage your merges carefully.

%> show_status -pmikepearce:master

Finally, you can do push requests as well, again, this will only work on repos where there is nothing waiting to be commited:

%> show_status -rmikepearce:master

You can grab the whole thing on github (Git Status), or just clone/fork here: git@github.com:MikePearce/Git-Status.git

The one about HTTP Request Methods and the RESTful API

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.

Kohana: more than just a fork of Codeigniter

For a while now I’ve used EllisLab’s Codeigniter for quite a few projects. More recently, I’ve turned to Concrete5 as it allows both myself and my clients to edit “in-place” resulting in fewer support calls for me. However, I’ve still had a soft spot for Codeigniter as it does, indeed, allow for Rapid Application Development. But I’ve been using is long enough now to understand many of it’s foibles.

I made the mistake of forking my Codeigniter install early on as I wasn’t familiar with the framework and didn’t quite know how to get it to do what I wanted it to. This was a mistake as couldn’t then apply the security patches that EllisLab released, instead, I’d have to make them myself (although, I rarely did).

Recently, a friend of mine put me on to Kohana which is a fork of Codeigniter that is, in my opinion, fricking awesome. Imagine all the things that aren’t quite right in CI and Kohana has fixed them, including being more OO PHP (according to the website, it’s STRICT OO PHP!). It’s secure, lightweight and things you’re used to working with still work. CI removed the $_GET, $_POST and $_COOKIE vars for security, but it was a little irritating, Kohana puts them back. It uses autoloading and appropriately name classes. There are static classes, methods and objects where neccessary and it uses a cascading load heirarchy similar to CI, except this works. It looks for the thing you’re trying to load first in your “application” directory, then your “module” directory and finally, if all else fails in the core “system” directory. Sweet huh? Also, the event handler responds and reacts better as it’s using an observer pattern. More sweetness.

For me, the best part about it is that it just works, out of the box, with a little config, exactly how I want it to. I can access and reference objects, libraries and resources how I would expect without any framework particular nuances ($CI =& get_instance(); anyone)? It makes just dropping into it easy.

I expect that the lessons I’ve learnt while using CI and the fact that it’s fairly similar in structure to CI has helped me get going with Kohana quicker, but it doesn’t feel quite as much like I’m running uphill when developing with the framework.

As an example, I recently wrote an ACL service with a RESTful API for some of our applications at work and I wanted to use Kohana to build a customer portal, I added a new driver for the Auth module and duplicated the ORM auth driver methods with my own ACL methods, using my API instead of ORM (or files, which is the other default option). Everything was written, presented a structured how I would expect a PHP5 application to be and so it took me only about three hours to implment the whole thing. Awesomesauce.

So, in summary, if you’re looking for a new framework, or an alternative to Codeigniter, you’d do much worse than check out Kohana. The Kohana team are busy writing V3 (http://v3.kohanaphp.com/) but it isn’t quite ready yet. This looks to be even more kick-ass.  More streamlined and uses the Zend style directory/filenaming structure. I’ll be checking this out when it’s released.

How many work (non-weekend) days between now and then?

As a developer, it’s not often you get to use a do { … } while(); loop, so, while you do (see what I did there?), it’s time for a celebration!

I needed to find the number of work days between two dates (now and then), I’d seen many, many much longer functions on the web and decided there must be an easier way to do it. So, this is it.

<?

/**
 * @desc    Return how many non-weekend days between two dates
 * @param   timestamp $toDate
 * @param   timestamp $fromDate
 * @author  Mike Pearce <mike@mikepearce.net>
 * @return  integer
 **/
function howManyWorkDays($toDate, $fromDate = FALSE)
{
    // The time RIGHT NOWs
    if (!$fromDate) $fromDate = time();
    $numberOfDays = 0;
    
    // Is it a timestamp? Maybe.
    if (
        is_int($toDate) AND 
        $toDate > $fromDate
    )
    {
        // While the current time is less than the time in the future
        do {
            // Add 1 day to the current time
            $fromDate = strtotime("+1 day", $fromDate);
            
            // If that day is a WEEKDAY, increment.
            if (date("N", $fromDate) < 6)
            {
                $numberOfDays++;
            }
        } while($fromDate < $toDate);
    }
    // Number of work days between now and then
    return $numberOfDays;
    
}

echo "Workdays between until Christmas Day: ". howManyWorkDays(strtotime("25th December"));