Saturday, 31 January 2015

CFML: the evolution of functionality

G'day:
Right, enough Lucee stuff for the time being. CFML. I still remember CFML.

As you all know, I am very unhappy about how Adobe and Railo had dealt with porting some residual tag-only functionality from tags to a script-sensible implementation.

I'm going to work through why they're both wrong and why I'm right in this article. Hey, I might as well lay my cards out, right?

<cfsavecontent>

Lucee: what to tell your boss about this whole RailoLucee thing

G'day:
I'm sure the Lucee bods will come out with their own whitepaper on this shortly, but here are my thoughts / analsyes.

Lucee: Discourse vs Google group (clarification)

G'day:
Just to clarify the situation with which forums people should be using for general Lucee chatter: there are currently two different forums that have been variously promoted:


The reason for this is that during launch, there were some teething problems with Discourse, so the Lucee people decided to run with Google until the Discourse issues were ironed out, and some further testing was done.

Unfortunately this messaging didn't get across to all the parties who were communicating about the forums, and the louder of the two messages directed people to Discourse. This is where the confusion arose from.

I am a card-carrying member of Lucee

G'day:
I've just signed-up for Lucee Membership, at "Supporter" level (this is the lowly US$9/month option... that's six quid / €8). I currently don't have anything to show for it except for a receipt, so that's not very exciting.


But I do feel good that I'm putting my money where my mouth is, and am supporting the project. So if you're a former-Railo / new-Lucee community member... get on board. If an unpaid blogger can pony up $9/month, so can people who actually used the product to earn their living! I also think it's really good that our language / platform has an association, so we can work together to make it better.

--
Adam

Friday, 30 January 2015

Lucee: "Open Source" doesn't mean "free"

G'day:
There was some irksome feedback re the Lucee release last night. This mostly centred around feigned (if not "feigned" then just "pig ignorant") horror that Lucee Association Switzerland has the gall to charge a membership fee. And somehow conflating that with the concept of "Open Source" and positioning Lucee as some sort of Bilderberg Group-esque evil cabal.

Well... settle the f*** down, will you?

Thursday, 29 January 2015

Lucee launch: thanks

G'day:
I was just thinking about this evening's Lucee launch. I'd just like to say that Micha gave an excellent presentation this evening, especially for a fella who was giving his first presentation ever. And also in a language that isn't his primary one. And not just some flippant "I fancy giving a presentation at a conference" sort of affair, but annoucing the release of something quite significant and controversial in our wee community, and career defining for him personally. No pressure then, eh?

Also I'd like to say thanks to Alex for providing the venue, beer, and general logistics for the thing. Nice one mate.

And to Mark Drew who sat there in the front row fielding all the questions coming in from a variety of online channels - the online meeting message feed, Twitter, IRC and my blog, and relaying them to Micha so he could answer them all.

I was out of the loop for a while during the presentation - unavoidable family stuff - and Brad stepped up and also fielded all the questions coming into my blog, interpreting, relaying, and intuiting answers on the fly.

Sean also fielded questions on IRC.

And other people helping out with the launch of Lucee this evening, including the meatspace attendees who also gave the thing a vibe and kept Micha honest.

Good work everyone.

--
Adam

Lucee

G'day:
OK, here it is. Today it was announced that Railo has been forked, and there is a new CFML engine in the community: Lucee.



I am currently at the product release event, and Pixl8 Interactive's offices in Clapham, London (", England", for my USAn readers).

What's most interesting about this is that the lead dev on Railo - Micha - has moved onto the Lucee project. And I understand Igal is also moving from Railo to Lucee too.

Lucee is available now. I've had the briefest of plays with it, and it seems as solid as one would expect of a product Micha has been working on.

The strapline from their site says this:

Lucee is a light-weight dynamic scripting language for the JVM that enables the rapid development of simple to highly sophisticated web applications. Lucee is made for the web environment, it simplifies common tasks for this environment.

I've been able to ask the Lucee Team a few questions, trying to second guess what the community would be wondering.

0

G'day:
I'm embargoed until about 6:31pm GMT.

Will have proper info then.

--
Adam

Second set of eyes on some code, pls?

G'day:
I'm having some trouble working out if I'm doing something wrong, or whether there's a bug in the library I'm using here. I'm erring towards it being me being daft and missing something. Could you perhaps cast yer eyes over this lot and see if I'm being a muppet? Cheers.

Adam Cameron plagarises James Harvey

G'day:
I'm replicating this - unmodified - from its original source (http://webdevsourcerer.com/):

Sunday, 25 January 2015

Public service announcement: James Harvey / @webdevsourcerer is a systematic plagiarist

G'day:
OK, so not "all's well that ends well" as I thought the other day ("Follow-up to "Imitation might be the sincerest form of flattery, but..."").

Some investigations have taken place (see comments on that article above), and another couple of instances of James Harvey plagiarising other people's work have surfaced:

Firstly this:
Updating Coldfusion 10 to Java 8

Is an almost verbatim copy of this:
Upgrading the JRE used by ColdFusion

But that is just a Stack Overflow answer, and only a coupla paragraphs.

This one however is dreadful: he's ripped off a bunch of Nathan's work, and claimed it has his own:

James's knock-off:
CF-Pagination :: Pagination Made Easy

Nathan's original:
Pagination.cfc

If you look at the code, it's basically identical. Except for stuff like James replacing Nathan's name with his own, and saying things like "This is a ColdFusion Component the Ol'Sourcerer wrote some time back".

Oh (thanks to Big Mad Kev for finding this), he's also ripped of Ray:

Generating Speech with ColdFusion and Java

With his own knock-off:
CF-TTS :: Text-to-Speech with ColdFusion and Java

I am concerned about linking to his blog too much, as it will just give him traffic. However he did take down the article I mentioned the other day, so hopefully these will follow suit too.


James? You're scum, sunshine. And I think people ought to be aware of this.

And to my readers? If you were to Retweet the message below, it would perhaps not be the worst thing you did today, for the integrity of the CFML community. Your decision.


--
Adam

...4...

to happen in the CFML community

Saturday, 24 January 2015

5...

The most significant thing

Follow-up to "Imitation might be the sincerest form of flattery, but..."

G'day:
A few days back I got a bit stroppy with a plagiarist: "Imitation might be the sincerest form of flattery, but...".

I note that James has deleted the page(s) in question.

Instead of simply citing the work properly (ie: put the creative commons attribution on it).

How weird is that? It seems to me it was not an exercise in helping the community, it was an exercise in bigging himself up. Via knocking-off someone else's work.

Dubious.

Still: all's well that ends well, I guess.

--
Adam

"Regular expressions in CFML" link summary

G'day:
This is not a very interesting article. I just need a list of links to other articles for my book.
I have just reminded myself I still need to write that last section! Oops.

So, yeah, nothing new here. Sorry.

--
Adam

Sunday, 18 January 2015

REST Web APIs: The Book... competition winner

G'day:
A few weeks ago I ran a "competition" to win a copy of Adam Tuttle's "REST Web APIs: The Book" book.

I have to admit that it'd slipped my mind until I was reminded about it a coupla days back.

In a very partial (sic) way I have selected a winner at not-at-all-random...

Saturday, 17 January 2015

Imitation might be the sincerest form of flattery, but...

... out and out plagiarism sux.

G'day:
As you hopefully know, a coupla months back I wrote a document "Documentation for CFScript". This document is on GitHub for everyone to reference and collaborate with: CFScript documentation.

Please note that anything written on this blog is copyrighted, and that repository on GitHub is specifically not licensed (ie: there is no license.md). This means I do not actively give licence for people to do with it what they like. With no licence, the usual copyright laws apply automatically.

Update

Actually I just noticed I licensed that specific work under Creative Commons (it's inline in the doc). So despite my notice for James to stop using copyrighted material, provided he posts the creative commons notice on his blog, that'll be fine. Well it'll get him off the hook. I still don't think what he has done is "fine".


TBH, my position is that if people ask to use stuff I write, I will almost certainly say "yeah, go for it... just attribute me appropriately". But... you know... ask first.

Friday, 16 January 2015

"Learn CFML in 24 Hours" status

G'day:
You might remember this: "Learn CFML in 24 hours: chapter 0" back in Dec. I've not had much more to say about it, but I am working on it. I have that sketchy draft of chapter 0, and also pretty much done chapter 1 ("Variables, commands/statements/expressions, operators").

I'd like to get some other people's eyes on it, and I've had some volunteers to help sanity check / bullshit detect on it. But I don't want it to be a public thing, and I've been mulling over how to implement that. Yesterday I created a BitBucket account... BitBucket allows private repos for free. I am not prepared to pay GitHub £5/month to enable me to have a single private repo for a coupla dozen files. I've also invited a coupla bods to have access to it, to see how that works.

I'm pretty busy at the moment so progress has been a bit slower than I would have liked. But I am enjoying the writing I've thusfar done, so my interest is not waning at all. I have a month off work in Feb, so I anticipate breaking the back of the thing then. As long as the Kiwi sunshine and World Cup Cricket don't occupy all my time during that period. The good thing about cricket is that it takes all day, and doesn't require 100% of one's attention, so I should be able to multi-task a bit.

Anyway, that's that. A bunch of people have indicated interest in the project so I thought I'd write a quick update.

And now to convert the work I've done so far into markdown. Grumble.

--
Adam

Wednesday, 14 January 2015

PHP: async requests using GuzzleHttp and request pools

G'day:
This'll round out my investigations into GuzzleHttp for the time being. Yesterday I looked at "PHP: trying to get async HTTP requests working via GuzzleHttp", and got that working with only a coupla hitches (all from me not RTFMing). That approached things by making individual async calls. What I'm doing today is to create an array of requests, and use GuzzleHttp's request pools to make them all simultaneously. It's taken about four hours to get the code together in a way I think makes sense. I had a coupla delays due to not paying attention to my code, then being bemused as to why it didn't work. But on the whole it was pretty smooth and predictable. And it just works.

Tuesday, 13 January 2015

PHP: trying to get async HTTP requests working via GuzzleHttp

G'day:
Last week I started looking at a few frameworks on PHP: "PHP: messing around with Silex, Pimple & GuzzleHttp". In that article I set up a basic Silex-driven site, using Pimple for managing dependency injection, and GuzzleHttp for handling HTTP requests to a stub REST API I had knocked together in CFML.

That was - for all intents and purposes - using synchronous calls in GuzzleHttp. It was making them asynchronously, but then immediately blocking until they completed. One of my requirements is to use asynchronous calls, so the HTTP requests don't block my app. Well: as little as possible, anyhow. If I have three five-second HTTP requests to make, I'd rather fire and forget them for as long as possible until I need the data, at which point I'll wait for them to complete. TBH, for my actual purposes REST is the wrong answer, especially if we need to actually worry about its latency, but we seem to be stuck with that. We'd be in the position to install the app on our PHP servers themselves and integrate with them more tightly (it's our own app we're communicating with via REST), but we're not able to do this for reasons I cannot fathom. C'est la vie.

Anyway, I've build on my app before - well the general structure of it - expanding it out to be a "blog"...or at least a remote REST API which serves up an article, some reference links and some comments for a given article number.

Sunday, 11 January 2015

CFML: design brain-fart in Application-specific DSN definitions on ColdFusion

G'day:
This article is just some analysis (and opinion, unsurprisingly) on the situation described in this bug ticket: "THIS.datasources changes ignored until CF restart".

In ColdFusion 11 (and Railo 4) one can specify data source definitions in Application.cfc, for example:

// Application.cfc
component {
    this.name = "myApp";

    this.datasources = {
        myDsn    = {
            database    = "dbName",
            host        = "localhost",
            port        = "3306",
            driver        = "MySQL5",
            username    = "dbUser",
            password    = "dbPassword"
        }
    };
    this.datasource    = "myDsn";
}

(That's for ColdFusion 11. Railo's syntax differs slightly: there's an example of it further down).

Saturday, 10 January 2015

Getting Clojure installed and running and "G'day World"-ing

G'day:
This is just another one of those article about my experience with working out how to get a language operational in my dev environment. All it really serves to do is to demonstrate there's not much to it, so it should not be a "barrier to entry" if you decide to have a look at Clojure. I am way too much a newbie with it to be offering anything actually insightful here though.

Obviously that said if one's just wanting to give it a bash, there's online REPLs about the place (eg: Try Clojure) so no need to install it, but their scope will be limited, and for some reason I prefer to have stuff running locally.

I've worked through the Try Clojure tutorial before - ages ago - but other than that know nothing about it other than it seems to be overly in love with parentheses. Here goes...

Friday, 9 January 2015

Another quick code puzzle (any language)

G'day:
Yeah, I'm a bit cheeky suggesting another puzzle when I've not yet found time to review all the answers for the last one (Something for the weekend? A wee code puzzle (in CFML, PHP, anything really...)), from back in November! Oh well. I'm sure it's the fun of the exercise rather than me reporting back on it that's the key bit anyone.

TBH I had forgotten about the other one, but will review another of the answers this weekend. I'll make a point of it.

Anyway... what's this one all about..?

Tuesday, 6 January 2015

PHP: help me understand calling a callback in PHP

G'day:
This flummoxed me yesterday, and I wonder if anyone can cast any light on the scene for me.

I've got this sort of situation:

class TestCallback {

    private $callback;

    function __construct($callback){
        $this->callback = $callback;
    }

    // [...]

}

Monday, 5 January 2015

PHP: messing around with Silex, Pimple & GuzzleHttp

G'day:

Updated 2024-08-30

Reworded to avoid my misuse of the word "singleton" which I was using to mean "an object that is created once and reused", which is not a singleton. A singleton is about implementation, not usage. This was pointed out to me by John Whish of the CFML community, during a conversation we were having about misuse of that term.

As you know, I've recently shifted most of my attention in my day job from CFML to PHP. This has had a coupla false starts: firstly I was needed back on the CFML team for a while to oversee some maintenance work, and then I was off work with eye trouble for a week or so. And then bloody Xmas & New Year came around, interfering with everything as well. All in all: I am well behind in my PHP learning / baptism-by-fire. I've good a month to catch up before I'm off work again for a month whilst I follow New Zealand's trials and tribulations in the Cricket World Cup. Conveniently being held in New Zealand (oh, and Aussie too, I s'pose) this year. This is only "convenient" as I was scheduled to be back in NZ around that time anyhow, checking in on the family and drinking beer with mates.

[disclosure... I am victim of a flight delay so have been killing time coding and drinking Guinness. I am halfway through my fourth pint, so my verbal diarrhoea will be worse than usual, as that previous paragraph evidenced. On review of the rest during proofreading, it doesn't get any better].

Anyway, I sat down and looked at our new app's PHP codebase the other day and went "Blimey! This is complicated!". Now I'm a newbie with PHP so I don't expect to follow the minutiae of each line of code, but I figured I should understand the general flow of what's going on. But no. For what should be a reasonably simple website (a front for online accommodation booking), we seemed to have an awful lot of Routings and Factories and Providers and Builders and Controllers and Repositories and combinations of the above (eg: ProviderFactoryBuilders... or it might be RepositoryBuilderFactories or something). It looked to me like a Java application in its structure, not one written in a dynamic web language. Still: I could be missing something, so decided to look into the app's architecture.

Basically we're using a framework called Silex. This is based on Symfony, but is very cut down. I guess if it was the CFML world, Silex might be taking FW/1's approach, and Symfony might be more like how ColdBox views the world. My position on frameworks is that FW/1 is about right; and ColdBox is too all-inclusive. But mileage varies, and let's not get into that. I'd heard of Symfony - but never used it - but had not heard of Silex. However reading the docs it sounds quite good (in that the docs are fairly short, I understand them).

I've been using ColdSpring for years, and like the idea of DI. Coming from CFML, it makes slightly less sense to me in the context of PHP as everything is request-centric: there's no sense of application (and hardly any sense of session in what I'd consider to be a professional implementation). So unlike how ColdSpring will cache bean configs for the life of the application, one has to tread more cautiously with PHP because the whole lot starts from scratch every request. That said, it's still desirable to define the wiring of one's classes & dependencies once, and then when one comes to need a new service (or ProviderFactoryRepositoryBuilder ;-), it's just a matter of saying "one of those please", rather than having to hand code initialising the thing with all its dependencies (and their dependencies, etc). Fortunately Silex ships with a DI framework - Pimple - baked in. Nice. But I need to know how to use it. I also needed to know whether we really need a lot of our factory classes, as they didn't really seem to be doing much... and I knew that if I was using CFML and ColdSpring I'd not need them at all. I was hoping this'd be the same for our new app.

Another new concept to me - I've been stuck using Fusebox (the XML version, not the CFC version) for years - is having the framework handle the request routing too. We've been using .htaccess for this previously. TBH: I actually think this is the correct approach, too: routing is the job of the HTTP tier, not the application tier. However frameworks seem to want to do this stuff themselves these days. They seem to have migrated from "MVC" to be more like "RMVC" (or, more sensibly: "RCMV"), with the R standing for routing. Our routing code seemed to be very very repetitive (copy and paste boilerplate ceremony with a few differences per route), and also seemed to be a mishmash of config and code, which struck me as being less than ideal. As well as just making for a lot of code that we oughtn't need. I was buggered if I knew how to fix it, but I figured I'd try to understand how we got to where we did, and whether there might be a better approach.

Finally, this new app hits a REST API to get its data. I'm hesitant about using REST for internal server-to-server comms... it's just slow and not the right tool for the job if one can just run the code to do the work, instead of making an HTTP request... to run the code to do the job. But we have no choice here, so REST is what we use. I know the theory of REST adequately (and a lot better having read Adam Tuttle's book: "Book review: REST Web APIs: The Book (win a copy here)"), and can wire together REST stuff in CFML, but had no idea how it works in PHP. It's not just a matter of calling <cfhttp>, which is about where my experience of consuming REST web services extends to. On our project we use something called GuzzleHttp. Which also sounds pretty bloody cool, actually.

Oh... that wasn't the last bit. We're also using Twig as our templating engine, and whilst I had not specific qualms about our usage of it, I simply didn't know how it worked, so wanted to have a brief look at that too. Well: one qualm. I dunno why mark-up-aware languages like PHP and CFML actually need a templating engine... given that's what the languages themselves are for. PHP's less good at it than CFML is (no custom tags, for example), but IMO all the templating engines I've seen make sense in the context of the languages they do the templating for, but most of the considerations justifying a specific additional tier for views just doesn't apply with either PHP or CFML. But "it's the way it's done", so be it.

I'm in Ireland this weekend, and I had Saturday and Sunday afternoons free to either stare at walls (of my B&B or Shannon Airport, respectively), or write some code. So my weekend mission was to install Silex, Pimple, GuzzleHttp, Twig, etc, and write a proof of concept site doing some routing, some DI, and some REST requests and displaying them with some Twiggage. I had a false start yesterday as the wireless at the pub (which is where I do my work on Saturdays) was off when I arrived, so I had to do something which didn't require any RTFM. So I continued to work on "Learn CFML in 24 hours". I knocked out another coupla thousand words, which was good. It was only today @ Shannon I've been able to work on the PHP stuff. "fortunately" my flight has been delayed so I have had a good few hours sitting here with a Guinness and messing around with this PHP stuff.

Pleasingly... it's only taken me a coupla hours to knock out the basic skeleton of what I wanted to test, I'm moderately happy with it, and it covers all the bases. And I think I know how everything works now, too. Hopefully. Here it is.

Installation

This is just a pleasure. It's all done by Composer, and it all just works. This probably wouldn't be mention-worthy for most people, but coming from the CFML world (where CommandBox is only just now beginning to offer any sort of package management), it's a great new experience. I've already got Composer installed, so all I need for my app is the composer.json file:

{
    "require": {
        "silex/silex": "~1.1",
        "symfony/config": "~2.6",
        "symfony/yaml": "~2.6",
         "guzzlehttp/guzzle": "~5.0",
        "twig/twig": ">=1.8,<2.0-dev"
    },
    "autoload": {
        "psr-4": {
            "dac\\silexdemo\\": "src/",
            "app\\": "app/"
        }
    }
}

I then do composer update from the command line, and everything installs. Oh the PSR-4 stuff is just my namespacing for my own code, which Composer looks after the autoloading of these days too. Nice.

My plan is to create a site which has a single route: /user/n/ where n is an ID, and the site will dash off and call the REST service, get the requested user, and then - via Twig - output the details (the details being : ID, firstName, lastName). That's it. I wanted to use the minimum of code (whilst still architecting things adequately), and especially a minimum of procedural code. I wanna be using OO, not just PHP script files.

File organisation

My app is organised as follows:

phpsilex.local/ - app root
 composer.json
 composer.lock
 vendor/ - Silex, Twig, Pimple, Symfony, GuzzleHttp stuff, as installed by Composer
 app/ - app bootstrap stuff
  app.php
  Dependencies.php
  Routes.php
 config/ - non PHP files
  routes.yml
 src/ - the code for the app itself
  beans/
   User.php
  controllers/
   User.php
  services/
   User.php
  views/
   user.html.twig
 public/ - site root (only this stuff is web browseable)
  index.php

Website code

As one has come to expect these days, the publicly accessible code is minimal:

<?php
// index.php
require __DIR__ . '/../app/app.php';

It just bootstraps the app.

Application config and bootstrap

<?php
// app.php

use Silex\Application;

require_once __DIR__.'/../vendor/autoload.php';


$app = new Application();
$app["debug"] = true;

$app->register(new Silex\Provider\ServiceControllerServiceProvider());
$app->register(new Silex\Provider\TwigServiceProvider(), [
    "twig.path" => __DIR__.'/../src/views'
]);

app\Dependencies::configure($app);
app\Routes::configure($app);

$app->run();

app.php does the boilerplate stuff of declaring the app to be a Silex one, and configuring it and running it. My two intrusions here are how I am doing the DI, and how I'm doing the routing.

The general Silex guidance seems to err towards procedural code, which sux a bit. I want to keep things as encapsulated as possible, and also making them as testable as possible. From start to finish. This means classes and methods. So I've put my dependency injection config into a class, as I have with the routing.

Dependencies (and their injection)


<?php
// Dependencies.php

namespace app;

use \dac\silexdemo\controllers;
use \dac\silexdemo\services;
use \dac\silexdemo\beans;
use GuzzleHttp\Client;

class Dependencies {

    static function configure($app){

        $app["controllers.hello"] = $app->share(function() {
            return new controllers\Hello();
        });        
        $app["controllers.user"] = $app->share(function($app) {
            return new controllers\User($app["twig"], $app["services.user"]);
        });        


        $app["services.user"] = $app->share(function($app) {
            return new services\User($app["factories.user"], $app["services.guzzle.client"]);
        });        


        $app["factories.user"] = $app->protect(function($id, $firstName, $lastName) {
            return new beans\User($id, $firstName, $lastName);
        });


        $app["services.guzzle.client"] = function() {
            return new Client();
        };
    }

}

Look how easy that lot is. Pimple is baked into Silex, so all I need to do is to stick a bunch of function expressions into the $app itself. Then when I come to use them, I just reference them in exactly the same way. This is still using configuration over convention (so like how ColdSpring would do it, more than how DI/1 or even more so WireBox would want to do it), and this suits me as well. But the configuration is clean because it uses actual code.This comment flies in the face of other comments I've made regarding separating config from code, but I think it's the appropriate handling here as the config actually is code: it's defining how code should be run.

Note that because everything here is a function expression, nothing actually runs to start with. I was concerned that if one had a huge dependency-injected application that there's be a lot of upfront overhead whilst the dependencies are executed. This would be disastrous with PHP needing to do this every request, but the DI config is only ever actually executed when it's needed. Nice.

There are three main conceits I'm leveraging here.

Firstly, the default syntax is to create a new object each time a service reference is used, eg:

$app["services.guzzle.client"] = function() {
    return new Client();
};

Each time I reference $app["services.guzzle.client"] in my code, I'll get a new Client object. This is fine in this situation, but for a lot of my services I can reuse the same object, so I use this syntax instead:

$app["controllers.user"] = $app->share(function($app) {
    return new controllers\User($app["twig"], $app["services.user"]);
});

The share() call there returns the same object every time. We have a lot of boilerplate code in our app currently which ensures this, and as far as I can tell it's simply not necessary.

We also have a lot of hand-written factory classes for handling creation of transient beans. This is baked into Pimple too:

$app["factories.user"] = $app->protect(function($id, $firstName, $lastName) {
    return new beans\User($id, $firstName, $lastName);
});

That basically defines a factory for creating transient User beans. I didn't need any specific factory code at all: all I needed to do is to wrap my function expression with the protect() call. Nice one.

There are no-doubt situation wherein we might actually need a bespoke factory method or two, but I can't recall seeing any in our existing code. And certainly not for my purposes. Still: the way to handle this is to only write the code that's necessary. We don't need a factory for every transient if Pimple can handle it for us with configuration. We should only write the code that we need. Code that fulfills only a ceremonial or "just in case" role is bad code.

Aside: I can't help but think if we'd been using TDD whilst writing this lot, we'd have a lot less code to contend with, because TDD encourages the writing of only necessary code. Unfortunately a lot of this code was written before our current team existed, so we had no say in that particular decision.

That's my dependencies wired.

Routing


<?php
// Routes.php

namespace app;

use Silex\Application;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Routing\Loader\YamlFileLoader;
use Symfony\Component\Routing\RouteCollection;

class Routes {

    static function configure($app){
        $app["routes"] = $app->extend("routes", function (RouteCollection $routes, Application $app) {
            $loader     = new YamlFileLoader(new FileLocator(__DIR__ . "/../config"));
            $collection = $loader->load("routes.yml");

            $routes->addCollection($collection);
            return $routes;
        });
    }

}

This code I copied from some blog or other (I'll try to dig it up, but don't have it in front of me just now... found it: "Scaling Silex applications (part II). Using RouteCollection"). The documented approach to routing in Silex is to write each of them with PHP code, within the app.php file. That's bloody awful. Not only do I want them out of the app.php file, I also want them out of PHP code completely. Route definitions are not code. Plain and simple. So this code above lets me define my routes using YAML (which I am unimpressed with as a format, but hey) instead:

# routes.yml
_hello:
    path:        /hello/{name}
    defaults:    { _controller: "controllers.hello:doGet"}

_user:
    path:        /user/{id}
    defaults:    { _controller: "controllers.user:getUser"}
    requirements:
        id:  \d+

I'm not doing much with the routing here, but you get the general idea. One thing I am doing here is that my controllers are DI-ed services (Silex's guidance is to do those inline too!), which the Symfony RouteCollection approach supports just fine. Note that controllers.user is a reference to the controller's definition in Dependencies.php, and getUser is the relevant method in same. I'll get to that.

Application code

Indeed I'll get to that now. Here's the controller;

<?php
// User.php

namespace dac\silexdemo\controllers;

class User {

    protected $twig;
    protected $userService;

    function __construct($twig, $userService){
        $this->twig = $twig;
        $this->userService = $userService;
    }

    function getUser($id){
        $user = $this->userService->getUser($id);

        return $this->twig->render('user.html.twig', array(
            'user' => $user,
        ));
    }

}

This shows the correlation between the DI config and the actual classes. From Dependencies.php:

$app["controllers.user"] = $app->share(function($app) {
    return new controllers\User($app["twig"], $app["services.user"]);
});

The controller needs to know about the User service so it can go grab some data, and it needs to know about Twig, which is configured for all the views. This is from app.php:

$app->register(new Silex\Provider\TwigServiceProvider(), [
    "twig.path" => __DIR__.'/../src/views'
]);

(I say "all": there's only the one view in this app).

getUser() calls the model (services.user) and renders the view (user.html.twig), passing the User fetched from the model to it.

Here's the model:

<?php
// User.php

namespace dac\silexdemo\services;

class User {

    protected $userFactory;
    protected $guzzleClient;

    function __construct($userFactory, $guzzleClient){
        $this->userFactory = $userFactory;
        $this->guzzleClient = $guzzleClient;
    }

    function getUser($id){
        $startTime = self::getElapsed("Start");

        $response = $this->guzzleClient->get('http://cf11.local:8511/rest/api/person/' . $id,["future"=>true]);
        self::getElapsed("After async guzzle call", $startTime);

        $response->wait();
        self::getElapsed("After wait() call", $startTime);

        $userAsArray = $response->json();

        $userFactory = $this->userFactory;
        $user = $userFactory($userAsArray["ID"], $userAsArray["FIRSTNAME"], $userAsArray["LASTNAME"]);

        return $user;
    }

    private static function getElapsed($message, $start=-1){
        if ($start == -1){
            $start = time();
        }
        error_log(sprintf("%s: %d", $message, time() - $start));
        return $start;
    }

}

There's a lot more code there than is necessary, as I am also recording some metrics when I run that code (getElapsed() and the calls to same).

I hit my REST web service using an async call to Guzzle, as this is also a requirement which we seem to have hand-coded instead of using what's provided for us already. I just wanted to make sure it all works. Here the Guzzle call returns a FutureResponse object rather than a result, per se. This lets the rest of the code continue whilst Guzzle makes the (non-performant) REST call. I'm being slightly contrived here, but it's not until the wait() call is made that the calling code will actually block until Guzzle is done. This would not be how I'd do this usually, but it demonstrates the concept. I've tweaked my web service to take five seconds to run, and if we look in the logs, we can see that the blocking only occurs when wait() is called:

Start: 0
After async guzzle call: 0
After wait() call: 5

There's a bunch of other stuff one can do with the future / promise pattern here, but that's a discussion for another day. This just proves Guzzle does a good job of making async HTTP calls, without any coding on our part.

The web service returns JSON, which Guzzle can expect, and I populate a new bean with this. This is where we use that config-free bean factory. Remember this from Dependencies.php?

$app["factories.user"] = $app->protect(function($id, $firstName, $lastName) {
    return new beans\User($id, $firstName, $lastName);
});

Getting and populating a new User bean is as simple as:

$userFactory = $this->userFactory;
$user = $userFactory($userAsArray["ID"], $userAsArray["FIRSTNAME"], $userAsArray["LASTNAME"]);

Calling $userFactory() is creating a new User object. Oh... the upper case key names are ColdFusion's fault, btw. It does that (uppercases stuff). Stoopid.

Here's the User bean:

<?php
// User.php

namespace dac\silexdemo\beans;

class User {

    protected $id;
    protected $firstName;
    protected $lastName;

    function __construct($id, $firstName, $lastName){
        $this->id = $id;
        $this->firstName = $firstName;
        $this->lastName = $lastName;
    }

    function getId(){
        return $this->id;
    }

    function getFirstName(){
        return $this->firstName;
    }

    function getLastName(){
        return $this->lastName;
    }

}

Note there's nothing in there to help the factory process: it's just a standard class.

That gets passed to the Twig file, which is then rendered:

{# user.html.twig #}

ID: {{ user.id }}<br>
First Name: {{ user.firstName }}<br>
Last Name: {{ user.lastName }}<br>

Note how I don't need to call the getter methods explicitly: Twig works that out for me. Because I'm accessing user.id, it knows to get that with getId(). Cool!

And all this renders the - very spectacular - result of:

ID: 1
First Name: Zachary
Last Name: Cameron Lynch


Job done.

That's about it, really. I've put Silex through its motions, testing its routing and its integration with Pimple. I've had a look at Guzzle, and rendered stuff with Twig.

From start to finish - including research and doc-reading time - this took about eight hours I guess. 4h the other day to do the installs and get the routing sorted out; another 4h today for the DI, controllers, model (including Guzzle) and view stuff. And maybe 2h to write up and proofread. Pretty quick. This is not a brag (at all), it demonstrates that everything is documented well, googling for stuff is easy with PHP because the community is so big, and that the apps actually do what they set out to do, and just get on with it, rather than throwing unnecessary shite at me (like some of my experiences with popular CFML solutions to similar situations).

And my flight's finally in the air, so I'm gonna shut this thing down and do something more entertaining. Sleep, probably.

Righto.

--
Adam

Saturday, 3 January 2015

Brad tries <cfclient>

G'day:
This is just a heads-up. Brad's having a go at <cfclient>: "My First Foray Into CFClient". He's blogged about his initial challenges, and it makes for interesting reading.

In an epicaricatic sort of way.

Thursday, 1 January 2015