Category Archives: PHP

Custom OAuth2 authentication in Apiiglity

I have a client that's writing an Apigility API that needs to talk to a database that's already in place. This also includes the users table that is to be used with Apigility's OAuth2 authentication.

Getting Apigility's OAuth2 integration to talk to a specific table name is quite easy. Simply add this config:

'storage_settings' => array(
    'user_table' => 'user',

To the relevant adapter within zf-mvc-auth => authentication config.

However, if you want to use different column names, that's a bit trickier as they are hardcoded in the OAuth2\Storage\Pdo class. To get Apigility's OAuth2 components to look at the correct columns, you create your own OAuth2 Adapter. I chose to extend ZF\OAuth2\Adapter\PdoAdapter which extends OAuth2\Storage\Pdo and go from there.

ZF\OAuth2\Adapter\PdoAdapter extends the base class to add bcrypt hashing. This is good, so it's a good place to start from. I created a new module, MyAuth to hold my adapter and its factory. The adapter looks like this:

namespace MyAuth;

use ZF\OAuth2\Adapter\PdoAdapter;

 * Custom extension of PdoAdapter to validate against the WEB_User table.
class OAuth2Adapter extends PdoAdapter
    public function __construct($connection, $config = array())
        $config = [
            'user_table' => 'legacy_user'

        return parent::__construct($connection, $config);

    public function getUser($username)
        $sql = sprintf(
            'SELECT * from %s where email_address=:username',
        $stmt = $this->db->prepare($sql);
        $stmt->execute(array('username' => $username));

        if (!$userInfo = $stmt->fetch(\PDO::FETCH_ASSOC)) {
            return false;

        // the default behavior is to use "username" as the user_id
        return array_merge(array(
            'user_id' => $username
        ), $userInfo);

    public function setUser($username, $password, 
        $firstName = null, $lastName = null)
        // do not store in plaintext, use bcrypt

        // if it exists, update it.
        if ($this->getUser($username)) {
            $sql = sprintf(
                'UPDATE %s SET pwd=:password, firstname=:firstName,
                    surname=:lastName WHERE username=:username',
            $stmt = $this->db->prepare($sql);
        } else {
            $sql = sprintf(
                'INSERT INTO %s (email_address, pwd, firstname, surname)
                    VALUES (:username, :password, :firstName, :lastName)',
            $stmt = $this->db->prepare($sql);

        return $stmt->execute(compact('username', 'password', 'firstName',

    protected function checkPassword($user, $password)
        return $this->verifyHash($password, $user['pwd']);

This code for getUser and setUser() is lifted directly from OAuth2\Storage\Pdo and all I've done is changed the column names. In this case I have email_address for my username, and pwd for the password column. Similar, I wrote my own checkPassword based on ZF\OAuth2\Adapter\PdoAdapter, again changing the array key to check to 'pwd'.

Now that we have the actual work done, we need to wire it into Apigility.

Firstly we need a factory so that the DIC can instantiate our adapter:

namespace MyAuth;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Db\Adapter\Driver\Pdo\Pdo as PdoDriver;

class OAuth2AdapterFactory implements FactoryInterface
     * Create service
     * @param ServiceLocatorInterface $serviceLocator
     * @return OAuth2Adapter
    public function createService(ServiceLocatorInterface $serviceLocator)
        $connection = $serviceLocator->get('DB\Master');
        if (!$connection->getDriver() instanceof PdoDriver) {
            throw new \RuntimeException("Need a PDO connection!");

        $pdo = $connection->getDriver()->getConnection()->getResource();
        return new OAuth2Adapter($pdo);

This is fairly standard code. Note that the DB\Master is the name of the database connection that is set up in the Apigility admin. I've been a bit lazy and assume that it's a PDO based adapter. If it isn't, it'll blow up, so if you're not using PDO, then it won't work as is!

To register your new authentication adapter with Apigility, create a config file in config/autoload and call it or something:

return [
    'zf-mvc-auth' => [
        'authentication' => [
            'adapters' => [
                'MyAuth' => [
                    'adapter' => 'ZF\\MvcAuth\\Authentication\\OAuth2Adapter',
                    'storage' => [
                        'storage' => 'MyAuth\OAuth2Adapter',
                        'route' => '/oauth',

The adapter is called MyAuth and is now available to select in the API configuration pages of the admin:

Myauth apigility

To sum up

All in all, it's really easy to write custom OAuth 2 authentication for Apigility as it's a very flexible platform. I've simply changed the column names here, but it would be easy enough to write an adapter against a different storage system altogether, though you would have to override more methods and possibly start from a more appropriate base class.

Debugging PHP SOAP over SSL using Charles

I'm currently integrating against a SOAP server using PHP which wasn't working as I expected, so I wanted to find out what was happening over the wire. I have Charles installed and use it regularly with OS X's system-wide proxy settings. However, PHP's SoapClient doesn't use these, so I had to work out how to do it manually.

Enabling SoapClient to send via a proxy is really easy and is documented by Lorna Mitchell in Using Charles To Debug PHP SOAP:

$options = [
    "cache_wsdl" => WSDL_CACHE_NONE,
    "soap_version" => SOAP_1_1,
    "trace" => 1,
    "proxy_host" => "localhost",
    "proxy_port" => 8888,

$client = new \SoapClient($wsdl, $options);

I did this and saw traffic in Charles. However, my service endpoint is SSL and I saw this error:

PHP Fatal error:  SOAP-ERROR: Parsing WSDL: Couldn't load from '' : failed to load external entity "" in SoapServiceProcessor.php on line 167

Looking in Charles, I saw the note:

You may need to configure your browser or application to trust the Charles Root Certificate. See SSL Proxying in the Help menu.


Again, we turn back to Lorna for how to do sort this out. This time, we need Manipulating HTTP with Charles Proxy, that she wrote for TechPortal. Unhelpfully, that website doesn't use section links, so scroll all way down to the Charles and SSL section to find out the relevant information about how to set up Charles for SSL proxying.

On OS X, you simply do:

  • Help -> SSL Proxying -> Install Charles Root Certificate
  • Proxy -> SSL Proxying Settings:
    • Check Enable SSL Proxying
    • Add the endpoint's domain to the list of locations

Finally, I needed to tell SoapClient to trust Charles' root certificate so that it can decrypt the SSL traffic.

This is done by downloading the Charles root certificate (Help -> SSL Proxying -> Save Charles Root Certificate) and storing it somewhere. I chose to put it in /usr/local/etc/charles-ssl-proxying-certificate.crt.

Finally, configure a new stream_context that knows about this certificate and add it to the SoapClient:

$options = [
    "cache_wsdl" => WSDL_CACHE_NONE,
    "soap_version" => SOAP_1_1,
    "trace" => 1,
    "proxy_host" => "localhost",
    "proxy_port" => 8888,
    "stream_context" => stream_context_create([
        'ssl' => [
            'cafile' => '/usr/local/etc/charles-ssl-proxying-certificate.crt'

$client = new \SoapClient($wsdl, $options);

Now everything works and I can see the actual data that's being sent to the SSL SOAP service and I solved my problem!

Selecting the service port with PHP's SoapClient

I'm currently integrating with a SOAP service which has two different services defined.

The relevant part of the WSDL is:

<wsdl:service name="Config">
    <wsdl:port name="BasicHttpBinding_IConfiguration" binding="tns:BasicHttpBinding_IConfiguration">
        <soap:address location=""/>
    <wsdl:port name="BasicHttpsBinding_IConfiguration" binding="tns:BasicHttpsBinding_IConfiguration">
        <soap:address location=""/>

I discovered that PHP's SoapClient will select the first port it encounters and doesn't provide a way to select another one. This was a nuisance as I wanted to use the SSL one.

Through research, I discovered that I can use __setLocation():


However, I don't control that endpoint, so I would rather select based on the port's name.

As I couldn't find a way to get the data from SoapClient, I decided to parse the WSDL myself and pull the information out. I don't do a lot with XML namespaces, so had to look up how to handle them and then how to extract the right data using XPath.

As I had to look it up, I'm putting it here, so I can find it again more easily!

I converted my new found knowledge into a method to extract the location attribute from the <soap:address> element of the <wsdl:port> with the correct name element:

function getLocationForPort($wsdl, $portName)
    $file = file_get_contents($wsdl);

    $xml = new SimpleXmlElement($file);

    $query = "wsdl:service/wsdl:port[@name='$portName']/soap:address";
    $address = $xml->xpath($query);
    if (!empty($address)) {
        $location = (string)$address[0]['location'];
        return $location;

    return false;

Xpath is ideal for this job!

Usage is simply:

$client = new SoapClient($wsdl);
$sslLocation = getLocationForPort($wsdl, 'BasicHttpsBinding_IConfiguration');
if ($sslLocation) {
// work with $client as normal

Now, all my calls to the SOAP service are via SSL as they should be!

Testing my ZF1 app on PHP7

Zend Framework 1 is still actively maintained and we fully intend to ensure that ZF1 works with no problems on PHP 7 when its released.

Now that PHP 7.0.0 Alpha 1 has been released, it's time to find out if your Zend Framework 1 app works with it. The easiest way to do this is to use a virtual machine. My preference is Vagrant with Rasmus' PHP7dev box.

A simple VM

I wanted to test a client's ZF1 application with PHP 7, so I created this drop-dead simple Vagrantfile to will boot up a virtual machine running PHP7:

# -*- mode: ruby -*-
# vi: set ft=ruby :


# Inline provisioning shell script
@script = <<SCRIPT

# Set up variables

# Switch to PHP7
newphp 7

# rebuild PHP7
makephp 7

# Configure nginx to point at our public/ directory and set APPLICATION_ENV to php7dev
echo '
server {
    listen       80;
    server_name  localhost;
    root         /vagrant/public;
    index        index.php index.html index.htm;
    access_log   /var/log/nginx/default-access.log  main;
    error_log    /var/log/nginx/default-error.log;

    location / {
        try_files $uri $uri/ @rewrite;
    location @rewrite {
        index index.php;
        rewrite ^(.*)$ /index.php;

    location ~ \.php {
        include                  fastcgi_params;
        fastcgi_keep_conn        on;
        fastcgi_index            index.php;
        fastcgi_split_path_info  ^(.+\.php)(/.+)$;
        fastcgi_param            PATH_INFO $fastcgi_path_info;
        fastcgi_param            SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param            APPLICATION_ENV php7dev;
        fastcgi_intercept_errors on;
        fastcgi_pass             unix:/var/run/php-fpm.sock;


' > /etc/nginx/conf.d/default.conf
service nginx restart

cd /vagrant

# Do we need to install and run composer?
if [ -e composer.json ]
  curl -Ss | php
  php composer.phar install --no-progress

# Do we need to create a MySQL database?
if [ -e $MYSQL_DUMP_FILE ]
    mysql -uvagrant -pvagrant -e "DROP DATABASE IF EXISTS $DB_NAME";
    mysql -uvagrant -pvagrant -e "CREATE DATABASE $DB_NAME";
    mysql -u vagrant -pvagrant $DB_NAME < $MYSQL_DUMP_FILE

echo "** Visit http://localhost:8888 in your browser for to view the application **"

# Vagrant configuration
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| = 'rasmus/php7dev' :forwarded_port, guest: 80, host: 8888
  config.vm.hostname = "zf1app.local"
  config.vm.provision 'shell', inline: @script

  config.vm.provider "virtualbox" do |vb|
    vb.customize ["modifyvm", :id, "--memory", "1024"]


The nice thing about creating the provisioning script within the Vagrantfile itself is that we now have a one file solution, but it's probably not the best solution for more complex set ups!

It's slow to start because it re-compiles PHP 7 via the makephp 7 command. Note that the MySQL username and password is vagrant, so I set APPLICATION_ENV to php7dev, so that I can set the correct configuration in application.ini.

What I found

You must read the UPGRADING file as it tells you all the BC breaks. There's a lot of nice tidy-ups and consistency improvements, which fortunately, haven't affected us.

As we've been working on ensuring ZF1 works with PHP7, my client's website worked with just one issue:

"Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP;"

It turned out that we had an old version of the PHP Markdown library. So I upgraded it and this issue was sorted.

In summary

I've explored using Rasmus' box before for unit testing and playing with extensions, but it also turned out that it's the ideal starting point for running my web applications under PHP7 too! I was pleased to discover that so far, I've found nothing broken on my ZF1 applications.

20 years of PHP

Today marks 20 years since PHP was released by Rasmus Lerdorf and Ben has been asking for how we started our PHP journey.

My first use of PHP was to write a website for an online computer gaming guild for EverQuest, back in 1999. A friend recommended it when I asked him how people programmed webpages in something other the C! That first website is still going and I'm not proud of the code. I'm very proud that it's still going strong and running on PHP 5.6 and has had some very minor updates for PHP version changes:

Oh yeah – I also fixed the SQL inject and XSS vulnerabilities!

That's quite a short list to make a PHP 3 application run on PHP 5.6! Of course, it's not object oriented, so I bypassed the upgrade pain there and it doesn't follow the latest best practices.

That PHP website also led to my first job in the web industry as I was headhunted from my job programming Windows applications. My first commercial website was an internal business application for sales tracking in an Internet hosting company. I've mostly staying in internal business applications and B2B apps ever since!

Of course, the way I write in PHP has changed considerably over the years. My first application was HTML pages with PHP where I needed it. I developed a library of procedural functions and moved most of my PHP code into .inc files. My first framework was Fusebox 4, which I first used in 2005. It was a procedural framework, but was a genuine Front Controller and encouraged separation of concerns. When I was ready to replace it with an OOP framework, Zend Framework had been announced and I jumped onto it…

PHP was built from day one for the web and, for me, it's still the best tool for the job!

My Xdebug configuration

With the release of Xdebug 2.3, I have updated my xdebug php.ini settings, so it seems sensible to write them down where I won't lose them!

The new-for-2.3 features which prompted this are xdebug.overload_var_dump, which Derick has written about and xdebug.halt_level which I have previously written about. I find both of these very useful.

This is my current php.ini configuration:

; Xdebug settings

; var_dump() displays everything, including filename and line number
xdebug.overload_var_dump        = 2
xdebug.var_display_max_children = -1
xdebug.var_display_max_data     = -1
xdebug.var_display_max_depth    = -1

; don't supress errors
xdebug.scream = 1

; stop if there's a warning/notice

; remote debugging
xdebug.remote_enable       = 1
xdebug.remote_connect_back = 1
xdebug.remote_port         = 9000

; profiling is triggered via browser extension
xdebug.profiler_enable         = 0
xdebug.profiler_enable_trigger = 1

For every other setting, I've found that the default is fine.

I control html_errors within my app so that it's set to 1 when the app is rendering HTML, and 0 otherwise, such as with CLI applications.

Turn warnings into exceptions

As I keep looking this up in other projects that I've written, I'm putting it here so I can find it more easily.

There are a number of built-in PHP functions that generate a notice or warning that you can't turn off when something goes wrong, such as parse_ini_file and file_get_contents.

One common solution to this is to suppress using the @ operator:

$result = @file_get_contents($url);
if (false === $result) {
    // inspect error_get_last() to find out what went wrong

This doesn't work when using Xdebug with the xdebug.scream set to 1 and its also inelegant and inefficient..

A better way is to use set_error_handler:

set_error_handler(function ($severity, $message, $file, $line) {
    throw new \ErrorException($message, $severity, $severity, $file, $line);

$result = file_get_contents($url);


In this case, we register our own error handler that converts every notice, warning and error into an ErrorException that can then be caught elsewhere. We then call the PHP function of interest and immediately call restore_error_handler to put back the one we had earlier.

Interestingly, in PHP7, we can expect to see exceptions in the engine itself which should allow us to solve this problem like this:

try {
    $result = file_get_contents($url);
} catch (EngineException $e) {
    // do something with $e

Building and testing the upcoming PHP7

The GoPHP7-ext project aims to ensure that all the known PHP extensions out there work with the upcoming PHP 7. This is non-trivial as some significant changes have occurred in the core PHP engine (related to performance) that mean that extensions need to be updated.

In order to help out (and prepare my own PHP code for PHP 7!), I needed the latest version of PHP7 working in a vagrant VM.

Fortunately Rasmus has created a such a VM called php7dev, so let's start there.

Firstly we make a new directory to work in:

$ mkdir php7dev
$ cd php7dev

Within this directory, we can set up the vagrant vm:

$ vagrant box add rasmus/php7dev
$ vagrant init rasmus/php7dev
$ vagrant up

If you are asked to enter the vagrant@'s password, then it's "vagrant".

We can now work within the VM to Update to the latest PHP 7 and work with extensions:

$ vagrant ssh

PHP versions within the VM

Rasmus' box comes with PHP versions 5.3, 5.4, 5.5, 5.6 and 7. For each of these versions, it provides four variants: release, debug, zts-release and zts-debug. A script, called newphp is provided that allows us to change between them like this:

$ newphp {version number} {type}

Where {version number} is one of: 53, 54, 55, 56, or 7 and {type} is one of: debug, zts or debugzts.

The ones I use are:

$ newphp 7
$ newphp 7 debug

The newphp script sets up PHP in both the CLI and nginx and rather usefully, sets up the correct phpize, so that when you build an extension, it will set it up for the current PHP.

Update PHP 7 to the latest version

PHP 7 is actively in development, so we're going to have to update it regularly to pick up the new changes. Rasmus has helpfully provided a script that, makephp, that does this for us:

$ makephp 70

This will grab the latest source code for PHP 7 and then compile and install both the release and debug versions. The makephp script can also compile zts and other PHP versions – run it without arguments to find out how.

Activate your new PHP build:

  • For PHP 7 release: $ newphp 7
  • For PHP 7 debug: $ newphp 7 debug

Check that the "built" date is correct by viewing the output of php -v

In my case, I see:

PHP 7.0.0-dev (cli) (built: Mar 29 2015 11:33:44) 
Copyright (c) 1997-2015 The PHP Group
Zend Engine v3.0.0-dev, Copyright (c) 1998-2015 Zend Technologies
    with Zend OPcache v7.0.4-dev, Copyright (c) 1999-2015, by Zend Technologies

Building an extension

Building an extension is easy enough. Let's walk through the apfd extension that's in PECL:

$ cd ~/src
$ git clone
$ cd apfd
$ make distclean; phpize && ./configure && make
$ make test
$ sudo make install

To install for any other PHP versions that you are using, change the current PHP installation via newphp and then repeating these steps.

To install the extension:

  • $ echo "" | sudo tee /etc/php7/conf.d/mysql.ini > /dev/null

    (Change php7 to the appropriate directory that's in /etc/ for other PHP versions)
  • $ php -m to check that the module is loaded.

Writing tests and upgrading an extension

As the internal C API has changed significantly, code changes are required to make an extension work on PHP7.

The process for tackling this is to "adopt" an extension on the GoPHP7-ext Extensions catalogue and then read the Compiling and testing extensions article on the GoPHP7-ext site, followed by the Testing Extensions page.

If you want to tackle fixing the C code, then the key changes that need to be made can be found on the Upgrading PHP extensions from PHP5 to NG wiki page.

Testing that your PHP code works on PHP7

To test my PHP code, I share it into the VM. This is done in the Vagrantfile using the config.vm.synced_folder directive.

I want to share my Zend Framework 1 source, so I edit Vagrantfile and after the line, I add this line:

config.vm.synced_folder "/www/zendframework/zf1", "/www/zf1"

This maps my local source code which is at /www/zendframework/zf1 into the VM at the /www/zf1 directory.

Run vagrant reload after changing the Vagrantfile in order to effect the change.

I can now vagrant ssh into the VM, cd /www/zf1 and run my unit tests (after installing PHPUnit, of course). If you want to run a website, then you need to set up a vhost as appropriate within the VM.


Rasmus has provided a PHP 7 VM that's very easy to keep up to date, so none of us have any excuse and need to be testing our PHP sites with it, reporting regressions and fixing our code!

Convert PHP Warnings and notices into fatal errors

Xdebug version 2.3 was released last week and includes a feature improvement that I requested back in 2013! Issue 1004 asked for the ability to halt on warnings and notices and I'm delighted that Derick implemented the feature and that it's now in the general release version.

It works really simply too.

Turn on the feature by setting xdebug.halt_level either in your php.ini or via ini_set():

ini_set('xdebug.halt_level', E_WARNING|E_NOTICE|E_USER_WARNING|E_USER_NOTICE);

Now cause a warning:

echo "Before";
imagecreatefromstring(null); // Don't pass null into imagecreatefromstring()!
echo "After";

The result is that "Before" is displayed and then we get the standard Xdebug notice, but "After" is not displayed as the script is halted on the due to the warning.

Xdebug halt-level example

I used to have a error handler in place solely to do this, but now I don't need it!

The most common use-case I have for it is a warning that occurs during a POSTed request that redirects to another page. Other people are dedicated log-checkers, but I'm not and vastly prefer seeing the big orange box telling what's gone wrong.

As I said, I'm really happy to see this feature; it's worth considering turning on your development set up too!

Sending test emails from PHP

I've written before about redirecting email while developing using a trap mail script on my development system. Other people also like MailCatcher.

I've recently switched to using a simple PHP script that creates a elm file on disk that can be opened by Apple's Mail client. This solution is much faster as there is no SMTP processing involved.

Adam Royle came up with this solution in his article Setup a testing mail server using PHP on Mac OS X, so thank you Adam!

In summary:

  • Create ~/smtp_out
  • Create ~/smtp_out/smtp_catcher.php with these contents:
    // create a filename for the emlx file
    list($ms, $time) = explode(' ', microtime());
    $filename = __DIR__ . '/' . date('Y-m-d h.i.s,', $time) . substr($ms,2,3) . '.eml';
    // write the email contents to the file
    $email_contents = fopen('php://stdin', 'r');
    $fstat = fstat($email_contents);
    file_put_contents($filename, $fstat['size'] . "\n");
    file_put_contents($filename, $email_contents, FILE_APPEND);
    // open up the eml file (using Apple Mail)
    exec('open ' . escapeshellarg($filename));
  • Make ~/smtp_out/smtp_catcher.php executable
  • Update php.ini and set:
    sendmail_path = sudo -u rob /Users/rob/smtp_out/smtp_catcher.php

    (Change rob to your username!)

  • Restart Apache
  • Give Apache permission to open Mail, by updating sudoers:
    • sudo visudo
    • Add this to the end:
      %www    ALL=(ALL)   NOPASSWD: /Users/rob/smtp_out/smtp_catcher.php

      (Again, change rob to your username!)

Read Adam's post for the full details on what's going on.

It's a nice solution and I'm liking it!