FuelPHP Updates

In working with FuelPHP recently a few thing have come up. The devs on #fuelphp on Freenode have been awesome at coming up with or accepting fixes. Here are the problems with immediate workarounds and long term solutions.

Fuel won’t let you refer to a View_Model with a templating engine extension

While View::forge('hello/world.twig') works fine, ViewModel::forge('hello/world.twig') fails to find the ViewModel because of the “.twig” part.

Workaround is to instead explicitly set the view on your ViewModel with public $_view = 'world.twig';

Long term fix is coming in Fuel 1.2.1 maintenance release https://github.com/fuel/core/commit/c06b0c67c027ee17bafa59cb089bd6c5baee80ab

Fuel insists that controllers are in the global namespace and prefixed with “Controller_”

No workaround.

Long term fix coming in Fuel 1.3 that will allow you to namespace and prefix controllers as you desire (as you already can with views) via a config setting: https://github.com/fuel/core/commit/882e564de66dc3f4edf83181b8fa36a8a8b083cd

Fuel’s View_Smarty does not expose the “plugins_dir” option.

Workaround is to set the option anyway, direct on the Parser object in your controller(s). The trouble is that accessing this object (and working out if it is Smarty-related) can be complicated. The naive version is something like:

$this->view->parser()->addPluginsDir(APPPATH.DS.'pluginsdirectoryname');

But if you want to continue to support non-Smarty rendering engines and are using ViewModels and Controller_Template it ends up looking more like (don’t judge me!):

        if (is_object($this->canvas)) {
            $rc = new \ReflectionClass($this->canvas);
            if ($rc->hasMethod('parser')) {
                $parser = $this->canvas->parser();
            } else if ($rc->hasProperty('_view')) {
                $parser = $this->canvas->_view->parser();
            } else {
                throw new YokoException('Unable to initialize Smarty i18n plugin.');
            }
            $smartyParam = 'plugins_dir';
            $ourPluginsDir = APPPATH . '/smarty';
            $pluginsDir = array_merge($parser->$smartyParam, [$ourPluginsDir]);
            $parser->$smartyParam = $pluginsDir;
        }

The long term fix is coming in the 1.3 release of Fuel’s parser package https://github.com/fuel/parser/pull/48. The devs were very good about accepting this patch which bodes well for future contributions to the project.

Spain’s Finalists

Spain 1 - 0 Germany 2008
Spain 1 - 0aet Netherlands 2010
Spain 4 - 0 Italy 2012
Casillas (F F F)
Sergio Ramos (F F F)
Xavi (F F F)
Iniesta (F F* t87)
Alonso (f63 t87 F)
Farbregas (t63 f87 t75)
Torres (t78* f105 f75*)
Capdevila (F F _)
Puyol (F F _)
Busquets (_ F F)
Silva (t56 S t59*)
Fabregas (_ f87 t75)
Pedrito (_ t60 f59)
Arbeloa (S S F)
Marchena (F S _)
Senna (F _ _)
Pique (_ _ F)
Alba (_ _ F*)
Jesus Navas (_ f60 S)
Santi Cazorla (f66 _ S)
Mata (_ S f87*)
Villa (S t105 _)
Guiza (f78 _ _)
Reina (S S S)
Albiol (S S S)
Llorente (_ S S)
Javi Martinez (_ S S)
Juanfran (_ _ S)
Negredo (_ _ S)
Palop (S _ _)
Fernando Navarro (S _ _)
Sergio Garcia (S _ _)
Juanito (S _ _)
De la Red (S _ _)
Valdes (_ S _)

F = full match played, t = subbed off, f = subbed on, S = unused substitute, _ = not present, * = goal.

Using Mustache with FuelPHP

  • Add ‘parser’ to the ‘packages’ array in config.php
  • Name your views ‘foo.mustache’ not ‘foo.php’
  • Call your views with View::forge(‘bar/foo.mustache’) not View::forge(‘bar/foo’)
  • Still call your ViewModels with ViewModel::forge(‘bar/foo’) but add a public $_view = ‘bar/foo.mustache’ variable to your ViewModel

Twitter Bootstrap

I’m a little late to the party on this one I think but I have to give a shout out to Twitter Bootstrap.

Multiverse Screenshot

A project at work used it and it looked nice so I had a play for a side project I am working on. It was really easy to get the side project looking good and feeling usable with my so-so design skills thanks to the framework. Great stuff.

strip_tags is just awful

strip_tags is possibly the worst of all the builtin PHP functions. It doesn’t even remotely do what it says. It is absolutely useless on user input because it strips everything from the first “<” it sees to the next “>” so if your input is

"<p>I <3 Monkeys</p>

its output is

I 

!!! And what else are you stripping tags from apart from user input? Avoid.

badmatch CRASH REPORT couch_file:init/1

I got this Couch DB crash running a view:

Error: EXIT

{{badmatch,[]},
 [{couch_query_servers,new_process,3,
                       [{file,"couch_query_servers.erl"},{line,472}]},
  {couch_query_servers,lang_proc,3,
                       [{file,"couch_query_servers.erl"},{line,462}]},
  {couch_query_servers,handle_call,3,
                       [{file,"couch_query_servers.erl"},{line,334}]},
  {gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,578}]},
  {proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,227}]}]}

This is caused by “language”: “JavaScript” in the view. It must be “javascript” (case sensitive) or omitted for javascript queries. Not sure where I copied that from.

Loading Dependencies in PhantomJS without using a Webserver

We run our javascript QUnit unit tests “headless” using the amazing PhantomJS. Originally our dependency loading code was overridden as a no-op within the tests. But that began to cause problems and require tests to run in a certain order and other Bad Things.

Within the WebPage environment in PhantomJS it is possible to form <script> tags and load code from a webserver, but not so easy to keep things running entirely from the commandline. Especially when you also want to run the same tests in the browser in QA (not everything that passes in WebKit passes in IE8, as if you didn’t know that).

To make this work I overrode the dependency loading code so that it just stores strings of dependencies in a variable xq.required. Then:


...

// Load the tests.
for (var i = 0; i < sources.length; i++) {
    _.page.injectJs(sources[i]);
}

// Now that we've loaded some tests we will have registered some
// dependencies (with require calls in the page).  Load the
// dependencies, recursively if they themselves have dependencies.
_.loadDependencies(_.page);

...

// Loop over the known dependencies and load them.  Also load any of their
// dependencies and so on, turtles all the way down.
_.loadDependencies = function(page) {
    var loaded = [];
    while (true) {
        // Ask the page what dependencies it has
        toLoad = page.evaluate(function () {
            return window.xq.required; // executes inside page, not phantom
        });

        // Diff toLoad with loaded to get stuff that hasn't been loaded yet
        toLoad = toLoad.filter(function(i) { return (loaded.indexOf(i) < 0); });
        if (toLoad.length === 0) {
            break;
        }

        // Load the stuff that hasn't been loaded yet
        for (var i = 0; i < toLoad.length; i++) {
            _.loadByNamespace(page, toLoad[i]);
        }
        // Add what we just loaded to the list of stuff we've already loaded
        loaded = loaded.concat(toLoad);
    }
}

Where loadByNamespace converts its second argument to a file path and loads it into the first argument with page.injectJs.

Now all the dependencies are loaded we can run the tests and report the results.

Google Street View Image API

One of the many cool APIs available through Google Maps is the Street View Image API. This gives you full access to the images taken by the Google vans that photograph streets.

The API is dead simple in the basic form, just a URL:

http://maps.googleapis.com/maps/api/streetview?size=WIDTHxHEIGHT&location=LOCATION&sensor=false

(The required “sensor” param tells Google if you are getting the location from a GPS device or similar – probably a privacy thing.)

The images are not necessarily spectacular brochure shots:

http://maps.googleapis.com/maps/api/streetview?size=400x400&location=Parliament+Square,+London&sensor=false

Unspectacular Street View of Parliament Square, London

but it is a useful API for any app that is displaying places that a user might not have been to before. It can both give them an idea of it’s the kind of place they are looking for and help them find it. Here’s an example, a local keycutters:

http://maps.googleapis.com/maps/api/streetview?size=400x400&location=Timpson,+Victoria+Station,+London&sensor=false

Street View of Timpson key cutters at Victoria Station, London