Using middleware in Zend MVC

Posted on 12th May 2017 | View comments


For over a year, since 2.7, we have had the ability to use a single middleware in a Zend\Mvc-based application (if you're not familiar, that's basically "Zend Framework" as opposed to "Zend Expressive"). A nice advantage here is you can make a forward-port path to migrating across to Zend Expressive. Your config might look something like

<?php
declare(strict_types=1);

return [
    'router' => [
        'routes' => [
            'path' => [
                'type' => 'Literal',
                'options' => [
                    'route' => '/path',
                    'defaults' => [
                        'middleware' => \App\Actions\FooAction::class,
                    ],
                ],
            ],
        ],
    ],
];

The middleware FooAction would be a standard middleware (at that time, the http-interop style middlewares were not supported, but that's changed now as I'll explain):

<?php
declare(strict_types=1);

namespace App\Actions;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Response\JsonResponse;

final class FooAction
{
    public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next = null) {
        return new JsonResponse([
            // ... some data ...
        ]);
    }
}

This change was great, and I did some work for a client who had started using the MiddlewareListener to attach a middleware to a route like this, and saw it in action. Over time I discovered a couple of flaws. Firstly, the matched route information was unavailable, so if you had a route matcher like /foo/:slug, there was no nice way to discover the value of slug from the matched route. So, dutifully I created patch #210 to resolve this issue, which was accepted and released in Zend\Mvc 3.0.4 and up.

Before long, I discovered another glaringly obvious problem: you could only ever have a single middleware added here. If you're familiar with middleware-style applications, this completely defeats the point: you can pipe your middleware to inject behaviour (such as authentication, error handling etc.) but this was not possible.

So, leveraging the existing functionality of Zend\Stratigility pipes, I heavily refactored the MiddlewareListener to instead create a pipe from the middleware definition from config. I made it backwards compatible in the sense that the configuration above would still work, but now you can attach multiple middlewares to a route, like so:

<?php
declare(strict_types=1);

return [
    'router' => [
        'routes' => [
            'path' => [
                'type' => 'Literal',
                'options' => [
                    'route' => '/path',
                    'defaults' => [
                        'middleware' => [
                            \App\Middleware\ExtractJsonPayload::class,
                            \App\Middleware\StoragelessPsr7Session::class,
                            \App\Middleware\VerifyIdentity::class,
                            \App\Actions\FooAction::class,
                        ],
                    ],
                ],
            ],
        ],
    ],
];

This patch recently got merged in Zend\Mvc 3.1.0 so you can now take advantage of this handy new feature. I also added support for middlewares that implement Interop\Http\ServerMiddleware\MiddlewareInterface too, so you can now write something like:

<?php
declare(strict_types=1);

namespace App\Actions;

use Interop\Http\ServerMiddleware\DelegateInterface;
use Interop\Http\ServerMiddleware\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\JsonResponse;

final class FooAction implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, DelegateInterface $delegate)
    {
        return new JsonResponse([
            // ... some data ...
        ]);
    }
}

I provide development, consulting and training for Zend Expressive, Zend Framework, Doctrine and more. If you'd like to discuss your requirements, get in touch with me.

Getting go-ing

Posted on 29th March 2017 | View comments


I had a bit of a play around with Go today. Didn't get much further than a basic hello world but using the net/http component to serve it in the browser. This post really serves as me reminding myself what I did to set everything up and get going.

First up, installing go is easy because I'm on Ubuntu:

$ sudo apt-get install golang

Then, as I'm used to PhpStorm, I went ahead and installed JetBrains's EAP Go IDE, Gogland which was minimal fuss.

Following the documentation and a helpful screencast I managed to figure out something that jarred me to start with. I fired up Gogland, and it kept complaining about GOPATH being missing. It's not immediately clear what this is, but it turns out that Go mandates a specific folder structure, for example if you chose your GOPATH to be in ~/go:

~/go
 - bin
   - project1
   - project2
 - pkg/linux_amd64/github.com/othervendor
   - libfoo.a
 - src/github.com
   - asgrim
     - project1 [...]
     - project2 [...]
   - othervendor
     - libfoo [...]

This was a bit frustrating at first, because I have have all other projects in ~/workspace/<project-name>. So, after throwing my toys out the pram and screaming at a wall, I got over it and understood the folder structure. A good thing to do once you've also done the screaming thing is to set up paths, by adding this into ~/.profile:

export GOPATH=$HOME/go
export PATH=$GOPATH/bin:$PATH

Once set up, I whizzed through the tutorial in the screencast above and got the "hello world" app running, along with the "string" library to reverse the string. I then went a little further and used the net/http library to serve up the string reversing tool as a web page, which was pretty easy to do.

Of course, not that it's much use to anyone, but the repo I set up is in https://github.com/asgrim/go-playground. Just a useful closer, it may help to visualise that folder structure:

~/go
 - bin
   - hello
 - pkg/linux_amd64/github.com/asgrim/go-playground
   - string.a
 - src/github.com
   - asgrim
     - go-playground
       - hello [...]
       - string [...]

Another useful resource I found, though I haven't worked my way through it all is Evert Pot's slides from his talk Go for PHP programmers.

Better Reflection 1.1.0 released

Posted on 25th July 2016 | View comments


After a bit more hacking away, tinkering and violating all good programming practices, I'm pleased to announce the release of Better Reflection 1.1.0, with shiny new features including the highly anticipated monkey patching abilities I've promised at my talk at the Dutch PHP Conference this year. Here's a taster of the main things we've added:

  • Ability to modify function, method and class structure (basic monkey patching)
  • Ability to replace the body of a function or method
  • Updated documentation
  • PHP 7 compatibility
  • Some PHP 7.1 compatibility (more features will come in a future version!)
  • Implemented ::class constant resolution
  • FindReflectionOnLine helper (look up code unit by filename and line number)
  • Various other improvements and bugfixes

The future...

We'd really like to see some feedback on how you folks are using the library, what you find useful, what you think would be a great addition and so on. Right now, the 1.2.0 release is planned to have:

  • PHP 7.1 compatibility (will require PhpParser 3.0 which is currently in beta)
  • Possibly some dynamic autoload handling for easier monkey patching
  • More bug fixes and reflection compatibility
  • Other stuff you request!

Thank you...

Thank you to the contributors for this release!

Go forth and enjoy the new Roave offering of Better Reflection!

Better Reflection - The Lowdown

Posted on 24th June 2016 | View comments


You may have heard me mention Better Reflection a few times already. In fact, I've just done a talk this morning at Dutch PHP Conference 2016 (the slides are on Joind.in now!). If you missed my talk, or weren't able to make the conference... what's the big deal, you might ask. Let me summarise what this is all about, and why this library can help you.

Reflection is part of the core of PHP, and is a built-in set of classes that allow you to introspect your code, and put it under the magnifying glass, so to speak. Surprisingly, there's some limitations as to what you can do; primarily the reflection API is a read-only API, and will give you information only about what is written in code. Additionally, you can only reflect on code that is already loaded - because the way reflection works is to examine the class "blueprint" to get the metadata about methods, properties, parameters, functions, and with PHP 7 also types.

Many of us will have seen, or used, reflection in unit tests. Regardless of your views on testing private properties and methods, there is code out there that does, and these would be tested using reflection. It'd look a little like this:

public function testSomething()
{
    $myObj = new Thing();

    $propReflection = new \ReflectionProperty($myObj, 'foo');
    $propReflection->setAccessible(true);
    $propReflection->setValue($myObj, 'whatever');

    // ... whatever ... 
}

But there's much more to just this simple use case than you think, and it can prove very powerful. This is where Better Reflection comes into play. Better Reflection is a userland library, and therefore doesn't have access (without using PHP's built-in reflection) to everything going on under the hood. Therefore, we've taken a different approach. In Better Reflection, we use the AST generated by Nikita Popov's PHP-Parser library, and use the metadata and more surrounding this to create a mostly-compatible reflection library with some extra bonus features. Let's have a look at a few of these key features...

Reflect without loading code

Because we use the PHP-Parser technique to load the code into AST and analyse like this, we can examine and reflect on code that has not actually been loaded into PHP yet. We can do this a number of different ways, by using these things we called Source Locators. The concept is quite simple; the Source Locators contain the information on how to find the code, and will load the AST. You can use the simple external API, which uses some default Source Locators (which are opinionated and make assumptions about your code), or you can explicitly define the instructions on how to find the code for Better Reflection to load.

This is what it might look like using the defaults:

use BetterReflection\Reflection\ReflectionClass;

$reflection = ReflectionClass::createFromName(\My\ExampleClass::class);

Very simple, right? And if your code doesn't conform to the assumptions we make (i.e. we assume that your autoloader, loads classes from a file - for example if you use Composer to autoload classes, the defaults will work fine):

use BetterReflection\Reflector\ClassReflector;
use BetterReflection\SourceLocator\Type\StringSourceLocator;

$source = <<<EOF
<?php
class MyClassInString {}
EOF;

$reflector = new ClassReflector(new StringSourceLocator($source));

$classInfo = $reflector->reflect(MyClassInString::class);

Yes indeed, this code example shows reflecting code contained in just an arbitrary string!

Monkey patching - modifying code before it runs

One of our planned features in Better Reflection 1.1.0 will be monkey patching, or the ability to modify code before it runs. There are some cases where this will be useful - for example you're using a library which doesn't quite do something, or you'd like to hook into certain places, or modify behaviour arbitrarily.

It's a little long-winded at the moment, and you should use this with caution! Let's say we want to reflect on, and modify the behaviour of this class:

class MyClass
{
    public function foo()
    {
        return 5;
    }
}

By using this code, we can modify the behaviour of the above function to make foo() return 4 instead of 5...

use BetterReflection\Reflection\ReflectionClass;
use PhpParser\PrettyPrinter\Standard as CodePrinter;

// Create the reflections in the normal way
$classInfo = ReflectionClass::createFromName('MyClass');
$methodInfo = $classInfo->getMethod('foo');

// Override the method body with a closure
$methodInfo->setBodyFromClosure(function () {
    return 4;
});

// Bring the class into PHP space
$printer = new CodePrinter();
$classCode = $printer->prettyPrint([
    $classInfo->getAst(),
]);
eval($classCode);

// Instantiate, and execute the resulting class
$c = new MyClass();
var_dump($c->foo()); // will be 4!!!

Essentially, you're replacing the whole body of the function here, but it's also possible to grab the AST from the method, perform your own modifications accordingly, and then call setBodyFromAst() to override the body. You'll also probably notice the rather inelegant method of loading the code; essentially using eval() here, but we still haven't come up with a better, safer, cleaner, and nicer way of doing this (suggestions or ideas are welcome!!).

Better type introspection

As well as using the type information now present in PHP 7, we also read the docblocks and expose methods that allow us to see what these types are for parameters and return types. Naturally, this relies on your docblocks being correct... But, it makes this kind of type introspection possible, even in PHP 5.6 and below code.

More stuff...!

There's plenty of other features already written, as well as planned to be written in future versions of Better Reflection. I'd love to get your feedback on using this library. Does it not do something that you'd like? Would you like to help? Let me know!

PHPSC16 - Scholarship Programme Review

Posted on 15th June 2016 | View comments


This year at PHP South Coast 2016, we decided to run a ticket scholarship programme with fundraising. Someone asked me to blog about how it went, so this is that blog post. I'm presenting my findings here only in note form really, but should give you an idea of how it went:

  • We had a total of 8 contributions to the fund, giving us a total of £543.29 (including VAT). Out of those contributors, five of them preferred to stay anonymous with only three (including me) being happy to have their name published.
  • The "value" of the scholarship award is a £70 (excluding VAT) ticket, so we added wording to the ticketing page to encourage contributions in multiples of £70 (excluding VAT).
  • The largest contribution was £140 (excluding VAT), and the smallest was £1 (excluding VAT). Note that the amount is not important as it goes into a "pot" anyway.
  • Out of the fund, we were able to award five scholarship tickets. We did not have a means to test eligability, we based the system on trust, asking only a handful of questions:
    • Where are you from? (this is important as we needed to advise those applying travelling from afar that we cannot cover travel and accomodation)
    • What is your occupation / are you a student?
    • Where did you hear about us?
  • We had six applicants for tickets. One person never claimed their ticket. Out of the five that did claim, three actually attended the conference.
  • This leaves us with a surplus of £123.29 (including VAT). If we decide to run the scholarship programme next year, this surplus will be used to seed the next programme. If not, we will find a worthy cause to donate the amount to (some ideas are Sustain, Code Club, Bletchley Park).
  • An interesting question was raised about charging VAT, which we investigated with our accountant. We discovered that contributors cannot reclaim VAT on the contributions because the supply is being made to the awardee rather than the contributor. We added this important wording to the Scholarship page on the website.
  • As the conference is not run by a charity (it is run by a not-for-profit social enterprise, PHP Hampshire CIC), unfortunately there are no tax benefits or gift aid available on any of the scheme. The overhead of running a charity is, in our opinion, not worth the additional effort at this stage.

I'm not sure how this measures up to other scholarship programmes, but I feel we operated this with moderate success on a small scale. We're quite a small conference relatively speaking anyway, but I'm mostly happy with the results. I'd like to have been able to award more scholarships, and I'm also a little disappointed that two of the people we awarded tickets to did not attend (as it still costs us for catering etc.). I'm undecided about running the scheme again next year, and instead we may defect to let Sustain or similar external organisation handle this.

The other issue discussed and debated was the means-testing problem; in the end we went down the "trust" route, making an assumption that the applicant "deserved" the ticket. However, this could leave us open to abuse in the future, with tickets being applied for going to those who would otherwise be able to afford or acquire a ticket. It's an interesting problem, because the other method means needing to pry into potentially highly sensitive information about the applicant which they may not feel comfortable revealing to us.