Logging PHP applications

Every PHP application generates errors, warnings, and notices and throws
exceptions. If we do not log this information, we lose a way to identify and
solve problems at runtime. Moreover, we may need to log specific actions such as
a user login and logout attempts. All such information should be filtered and
stored in an efficient way.

PHP offers the function error_log() to send an error
message to the defined system logger, and the function
set_error_handler() to specify a handler for
intercepting warnings, errors, and notices generated by PHP.

These functions can be used to customize error management, but it’s up to the
developer to write the logic to filter and store the data.

Zend Framework offers a logging component, zend-log;
the library can be used as a general purpose logging system. It supports
multiple log backends, formatting messages sent to the log, and filtering
messages from being logged.

Last but not least, zend-log is compliant with PSR-3,
the logger interface standard.


You can install zend-log using the
following composer command:

composer require zendframework/zend-log


zend-log can be used to create log entries in different formats using multiple
backends. You can also filter the log data from being saved, and process the
log event prior to filtering or writing, allowing the ability to substitute,
add, remove, or modify the data you log.

Basic usage of zend-log requires both a writer and a logger instance.
A writer stores the log entry into a backend, and the logger
consumes the writer to perform logging operations.

As an example:

use ZendLogLogger;
use ZendLogWriterStream;

$logger = new Logger;
$writer = new Stream('php://output');

$logger->log(Logger::INFO, 'Informational message');

The above produces the following output:

2017-09-11T15:07:46+02:00 INFO (6): Informational message

The output is a string containing a timestamp, a priority (INFO (6)) and the
message (Informational message). The output format can be changed using the
setFormatter() method of the writer object ($writer).
The default log format, produced by the Simple
formatter is as follows:

%timestamp% %priorityName% (%priority%): %message% %extra%

where %extra% is an optional value containing additional information.

For instance, if you wanted to change the format to include only log %message%, you could do the following:

$formatter = new ZendLogFormatterSimple('log %message%' . PHP_EOL);

Log PHP events

zend-log can also be used to log PHP errors and exceptions. You can log PHP
errors using the static method Logger::registerErrorHandler($logger) and
intercept exceptions using the static method Logger::registerExceptionHandler($logger).

use ZendLogLogger;
use ZendLogWriter;

$logger = new Logger;
$writer = new WriterStream(__DIR__ . '/test.log');

// Log PHP errors

// Log exceptions

Filtering data

As mentioned, we can filter the data to be logged; filtering removes messages
that match the filter criteria, preventing them from being logged.

We can use the addFilter() method of the Writer

to add a specific filter.

For instance, we can filter by priority, accepting only log entries with a
priority less than or equal to a specific value:

$filter = new ZendLogFilterPriority(Logger::CRIT);

In the above example, the logger will only store log entries with a priority
less than or equal to Logger::CRIT (critical). The priorities are defined by
the ZendLogLogger class:

const EMERG   = 0;  // Emergency: system is unusable
const ALERT   = 1;  // Alert: action must be taken immediately
const CRIT    = 2;  // Critical: critical conditions
const ERR     = 3;  // Error: error conditions
const WARN    = 4;  // Warning: warning conditions
const NOTICE  = 5;  // Notice: normal but significant condition
const INFO    = 6;  // Informational: informational messages
const DEBUG   = 7;  // Debug: debug messages

As such, only emergency, alerts, or critical entries would be logged.

We can also filter log data based on regular expressions, timestamps, and more.
One powerful filter uses a zend-validator
ValidatorInterface instance to filter the log; only valid entries would be
logged in such cases.

Processing data

If you need to provide additional information to logs in an automated fashion,
you can use a ZendLogProcesser class. A processor is executed before the
log data are passed to the writer. The input of a processor is a log event,
an array containing all of the information to log; the output is also a log
, but can contain modified or additional values. A processor modifies
the log event to prior to sending it to the writer.

You can read about processor adapters offered by zend-log in the

Multiple backends

One of the cool feature of zend-log is the possibility to write logs using
multiple backends. For instance, you can write a log to both a file and a
database using the following code:

use ZendDbAdapterAdapter as DbAdapter;
use ZendLogFormatter;
use ZendLogWriter;
use ZendLogLogger;

// Create our adapter
$db = new DbAdapter([
    'driver'   => 'Pdo',
    'dsn'      => 'mysql:dbname=testlog;host=localhost',
    'username' => 'root',
    'password' => 'password'

// Map event data to database columns
$mapping = [
    'timestamp' => 'date',
    'priority'  => 'type',
    'message'   => 'event',

// Create our database log writer
$writerDb = new WriterDb($db, 'log', $mapping); // log table
$formatter = new FormatterBase();
$formatter->setDateTimeFormat('Y-m-d H:i:s'); // MySQL DATETIME format

// Create our file log writer
$writerFile = new WriterStream(__DIR__ . '/test.log');

// Create our logger and register both writers
$logger = new Logger();
$logger->addWriter($writerDb, 1);
$logger->addWriter($writerFile, 100);

// Log an information message
$logger->info('Informational message');

The database writer requires the credentials to access the table where you will
store log information. You can customize the field names for the database table
using a $mapping array, containing an associative array mapping log fields to
database columns.

The database writer is composed in $writerDb and the file writer in
$writerFile. The writers are added to the logger using the addWriter()
method with a priority number; higher integer values indicate higher priority
(triggered earliest). We chose priority 1 for the database writer, and priority
100 for the file writer; this means the file writer will log first, followed by
logging to the database.

Note: we used a special date formatter for the database writer. This is
required to translate the log timestamp into the DATETIME format of MySQL.

PSR-3 support

If you need to be compatible with PSR-3,
you can use ZendLogPsrLoggerAdapter. This logger can be used anywhere
a PsrLogLoggerInterface is expected.

As an example:

use PsrLogLogLevel;
use ZendLogLogger;
use ZendLogPsrLoggerAdapter;

$zendLogLogger = new Logger;
$psrLogger = new PsrLoggerAdapter($zendLogLogger);

$psrLogger->log(LogLevel::INFO, 'We have a PSR-compatible logger');

To select a PSR-3 backend for writing, we can use the ZendLogWriterPsr
class. In order to use it, you need to pass a PsrLogLoggerInterface instance
to the $psrLogger constructor argument:

$writer = new ZendLogWriterPsr($psrLogger);

zend-log also supports PSR-3 message

via the ZendLogProcessorPsrPlaceholder class. To use it, you need to add a
PsrPlaceholder instance to a logger, using the addProcess() method.
Placeholder names correspond to keys in the "extra" array passed when logging a

use ZendLogLogger;
use ZendLogProcessorPsrPlaceholder;

$logger = new Logger;
$logger->addProcessor(new PsrPlaceholder);

$logger->info('User with email {email} registered', ['email' => 'user@example.org']);

An informational log entry will be stored with the message User with email user@example.org registered.

Logging an MVC application

If you are using a zend-mvc based
application, you can use zend-log as module. zend-log provides a
class, which registers ZendLog as a module in your application.

In particular, the zend-log module provides the following services (under
the namespace ZendLog):

Logger::class         => LoggerServiceFactory::class,
'LogFilterManager'    => FilterPluginManagerFactory::class,
'LogFormatterManager' => FormatterPluginManagerFactory::class,
'LogProcessorManager' => ProcessorPluginManagerFactory::class,
'LogWriterManager'    => WriterPluginManagerFactory::class,

The Logger::class service can be configured using the log config key;
the documentation provides configuration examples.

In order to use the Logger service in your MVC stack, grab it from the service
container. For instance, you can pass the Logger service in a controller using
a factory:

use ZendLogLogger;
use ZendServiceManagerFactoryFactoryInterface;

class IndexControllerFactory implements FactoryInterface
    public function __invoke(
        ContainerInterface $container,
        array $options = null
    ) {
        return new IndexController(

via the following service configuration for the IndexController:

'controllers' => [
    'factories' => [
        IndexController::class => IndexControllerFactory::class,

Logging a middleware application

You can also integrate zend-log in your middleware applications.
If you are using
add the component’s ConfigProvider
to your config/config.php file.

Note: if you are using zend-component-installer,
you will be prompted to install zend-log’s config provider when you install
the component via Composer.

Note: This configuration registers the same services
provided in the zend-mvc example, above.

To use zend-log in middleware, grab it from the dependency injection
container and pass it as a dependency to your middleware:

namespace AppAction;

use PsrContainerContainerInterface;
use ZendLogLogger;

class HomeActionFactory
    public function __invoke(ContainerInterface $container) : HomeAction
        return new HomeAction(

As an example of logging in middleware:

namespace AppAction;

use InteropHttpServerMiddlewareDelegateInterface;
use InteropHttpServerMiddlewareMiddlewareInterface as ServerMiddlewareInterface;
use PsrHttpMessageServerRequestInterface;
use ZendLogLogger;

class HomeAction implements ServerMiddlewareInterface
    private $logger;

    public function __construct(Logger $logger)
        $this->logger = logger;

    public function process(
        ServerRequestInterface $request,
        DelegateInterface $delegate
    ) {
        $this->logger->info(__CLASS__ . ' has been executed');

        // ...

Listening for errors in Expressive

Expressive and Stratigility
provide a default error handler middleware implementation,
ZendStratigilityMiddlewareErrorHandler which listens for PHP errors and
exceptions/throwables. By default, it spits out a simple error page when an
error occurs, but it also provides the ability to attach listeners, which can
then act on the provided error.

Listeners receive the error, the request, and the response that the error
handler will be returning. We can use that information to log information!

First, we create an error handler listener that composes a logger, and logs the

use Exception;
use PsrHttpMessageResponseInterface;
use PsrHttpMessageServerRequestInterface;
use Throwable;
use ZendLogLogger;

class LoggingErrorListener
     * Log message string with placeholders
    const LOG_STRING = '{status} [{method}] {uri}: {error}';

    private $logger;

    public function __construct(Logger $logger)
        $this->logger = $logger;

    public function __invoke(
        ServerRequestInterface $request,
        ResponseInterface $response
    ) {
        $this->logger->error(self::LOG_STRING, [
            'status' => $response->getStatusCode(),
            'method' => $request->getMethod(),
            'uri'    => (string) $request->getUri(),
            'error'  => $error->getMessage(),

The ErrorHandler implementation casts PHP errors to ErrorException
instances, which means that $error is always some form of throwable.

We can then write a delegator factory that will register this as a listener on
the ErrorHandler:

use LoggingErrorListener;
use PsrContainerContainerInterface;
use ZendLogLogger;
use ZendLogProcessorPsrPlaceholder;
use ZendStratigilityMiddlewareErrorHandler;

class LoggingErrorListenerFactory
    public function __invoke(
        ContainerInterface $container,
        callable $callback
    ) : ErrorHandler {
        $logger = $container->get(Logger::class);
        $logger->addProcessor(new PsrPlaceholder());

        $listener = new LoggingErrorListener($logger);
        $errorHandler = $callback();
        return $errorHandler;

And then register the delegator in your configuration:

// In a ConfigProvider, or a config/autoload/*.global.php file:
use LoggingErrorListenerFactory;
use ZendStratigilityMiddlewareErrorHandler;

return [
    'dependencies' => [
        'delegators' => [
            ErrorHandler::class => [

At this point, your error handler will now also log errors to your configured


The zend-log component offers a wide set of features,
including support for multiple writers, filtering of log data,
compatibility with PSR-3, and

Hopefully you can use the examples above for consuming zend-log in your
standalone, zend-mvc, Expressive, or general middleware applications!

Learn more in the zend-log documentation.

Source: Zend feed

Specialized Response Implementations in Diactoros

When writing PSR-7 middleware, at some
point you’ll need to return a response.

Maybe you’ll be returning an empty response, indicating something along the
lines of successful deletion of a resource. Maybe you need to return some HTML,
or JSON, or just plain text. Maybe you need to indicate a redirect.

But here’s the problem: a generic response typically has a very generic
constructor. Take, for example, ZendDiactorosResponse:

public function __construct(
    $body = 'php://memory',
    $status = 200,
    array $headers = []

$body in this signature allows either a PsrHttpMessageStreamInterface
instance, a PHP resource, or a string identifying a PHP stream. This means that
it’s not terribly easy to create even a simple HTML response!

To be fair, there are good reasons for a generic constructor: it allows
setting the initial state in such a way that you’ll have a fully populated
instance immediately. However, the means for doing so, in order to be
generic, leads to convoluted code for most consumers.

Fortunately, Diactoros provides a number of convenience implementations to help
simplify the most common use cases.


The standard response from an API for a successful deletion is generally a 204 No Content. Sites emitting webhook payloads often expect a 202 Accepted with
no content. Many APIs that allow creation of resources will return a 201 Created; these may or may not have content, depending on implementation, with
some being empty, but returning a Location header with the URI of the newly
created resource.

Clearly, in such cases, if you don’t need content, why would you be bothered to
create a stream? To answer this, we have
ZendDiactorosResponseEmptyResponse, with the following constructor:

public function __construct($status = 204, array $headers = [])

So, a DELETE endpoint might return this on success:

return new EmptyResponse();

A webhook endpoint might do this:

return new EmptyResponse(StatusCodeInterface::STATUS_ACCEPTED);

An API that just created a resource might do the following:

return new EmptyResponse(
    ['Location' => $resourceUri]


Redirects are common within web applications. We may want to redirect a user to
a login page if they are not currently logged in; we may have changed where some
of our content is located, and redirect users requesting the old URIs; etc.

ZendDiactorosResponseRedirectResponse provides a simple way to create and
return a response indicating an HTTP redirect. The signature is:

public function __construct($uri, $status = 302, array $headers = [])

where $uri may be either a string URI, or a PsrHttpMessageUriInterface
instance. This value will then be used to seed a Location HTTP header.

return new RedirectResponse('/login');

You’ll note that the $status defaults to 302. If you want to set a permanent
redirect, pass 301 for that argument:

return new RedirectResponse('/archives', 301);

// or, using fig/http-message-util:
return new RedirectResponse('/archives', StatusCodeInterface::STATUS_PERMANENT_REDIRECT);

Sometimes you may want to set an header as well; do that by passing the third
argument, an array of headers to provide:

return new RedirectResponse(
    ['X-ORIGINAL_URI' =>  $uri->getPath()]


Sometimes you just want to return some text, whether it’s plain text, XML, YAML,
etc. When doing that, taking the extra step to create a stream feels like

$stream = new Stream('php://temp', 'wb+');

To simplify this, we offer ZendDiactorosResponseTextResponse, with the
following signature:

public function __construct($text, $status = 200, array $headers = [])

By default, it will use a Content-Type of text/plain, which means you’ll
often need to supply a Content-Type header with this response.

Let’s return some plain text:

return new TextResponse('Hello, world!');

Now, let’s try returning a Problem Details XML response:

return new TextResponse(
    ['Content-Type' => 'application/problem+xml']

If you have some textual content, this is the response for you.


The most common response from web applications is HTML. If you’re returning
HTML, even the TextResponse may seem a bit much, as you’re forced to provide
the Content-Type header. To answer that, we provide
ZendDiactorosResponseHtmlResponse, which is exactly the same as
TextResponse, but with a default Content-Type header specifying
text/html; charset=utf-8 instead.

As an example:

return new HtmlResponse($renderer->render($template, $view));


For web APIs, JSON is generally the lingua franca. Within PHP, this generally
means passing an array or object to json_encode(), and supplying a
Content-Type header of application/json or application/{type}+json, where
{type} is a more specific mediatype.

Like text and HTML, you likely don’t want to do this manually every time:

$json = json_encode(
$stream = new Stream('php://temp', 'wb+');
$response = new Response(
    ['Content-Type' => 'application/json']

To simplify this, we provide ZendDiactorosResponseJsonResponse, with the
following constructor signature:

public function __construct(
    $status = 200,
    array $headers = [],
    $encodingOptions = self::DEFAULT_JSON_FLAGS
) {

where $encodingOptions defaults to the flags specified in the previous

This means our most common use case now becomes this:

return new JsonResponse($data);

What if we want to return a JSON-formatted Problem Details response?

return new JsonResponse(
    ['Content-Type' => 'application/problem+json']

One common workflow we’ve seen with JSON responses is that developers often want
to manipulate them on the way out through middleware. As an example, they may
want to add additional _links elements to HAL responses, or add counts for

Starting in version 1.5.0, we provide a few extra methods on this particular
response type:

public function getPayload() : mixed;
public function getEncodingOptions() : int;
public function withPayload(mixed $data) : JsonResponse;
public function withEncodingOptions(int $options) : JsonResponse;

Essentially, what happens is we now store not only the encoded $data
internally, but the raw data; this allows you to pull it, manipulate it, and
then create a new instance with the updated data. Additionally, we allow
specifying a different set of encoding options later; this can be useful, for
instance, for adding the JSON_PRETTY_PRINT flag when in development. When the
options are changed, the new instance will also re-encode the existing data.

First, let’s look at altering the payload on the way out. zend-expressive-hal
injects _total_items, _page, and _page_count properties, and you may want
to remove the underscore prefix for each of these:

function (ServerRequestInterface $request, DelegateInterface $delegate) : ResponseInterface
    $response = $delegate->process($request);
    if (! $response instanceof JsonResponse) {
        return $response;

    $payload = $response->getPayload();
    if (! isset($payload['_total_items'])) {
        return $response;

    $payload['total_items'] = $payload['_total_items'];

    if (isset($payload['_page'])) {
        $payload['page'] = $payload['_page'];
        $payload['page_count'] = $payload['_page_count'];
        unset($payload['_page'], $payload['_page_count']);

    return $response->withPayload($payload);

Now, let’s write middleware that sets the JSON_PRETTY_PRINT option when in
development mode:

function (
    ServerRequestInterface $request,
    DelegateInterface $delegate
) : ResponseInterface use ($isDevelopmentMode) {
    $response = $delegate->process($request);

    if (! $isDevelopmentMode || ! $response instanceof JsonResponse) {
        return $response;

    $options = $response->getEncodingOptions();
    return $response->withEncodingOptions($options | JSON_PRETTY_PRINT);

These features can be really powerful when shaping your API!


The goal of PSR-7 is to provide the ability to standardize on interfaces for
your HTTP interactions. However, at some point you need to choose an actual
implementation, and your choice will often be shaped by the features offered,
particularly if they provide convenience in your development process. Our goal
with these various custom response implementations is to provide convenience
to developers, allowing them to focus on what they need to return, not how to
return it.

You can check out more in the Diactoros documentation.

Source: Zend feed

Community Corner: Discourse Forums!

For many years, we’ve had requests for dedicated Zend Framework forums. We’ve
resisted doing so, and instead deferred to using mailing lists and Stack
Overflow tags. However, these are imperfect: searching for questions and answers
is often difficult if not impossible.


In the past few years, Discourse has proved itself
as a community-centric forum solution. Further, they have started offering
free hosting for open source communities, making it a compelling option to

We recently reached out to them, and they have agreed to host the Zend Framework
forums for us.

Zend Framework Community

So, please join us on the Zend Framework Discourse Forums!

You may sign up with any Google, GitHub, or Twitter account, or register
directly with the site.

Some pages of interest:

Additionally, we have setup a section for
contributors to discuss
architecture, milestones, etc.

Welcome all, and we look forward to your conversations!

Source: Zend feed

Manage permissions with zend-permissions-rbac

In our previous post, we
covered authentication of a user via Expressive middleware. In that post, we
indicated that we would later discuss authorization, which is the activity of
checking if an authenticated user has permissions to perform a specific
action, from within the context of a middleware application.

Before we do that, however, we thought we’d introduce
our lightweight role-based access control (RBAC) implementation.

Installing zend-permissions-rbac

Just as you would any of our components, install zend-permissions-rbac via

$ composer require zendframework/zend-permissions-rbac

The component has no requirements at this time other than a PHP version of at
least 5.5.


In RBAC systems, we have three primary items to track:

  • the RBAC system composes zero or more roles.
  • a role is granted zero or more permissions.
  • we assert whether or not a role is granted a given permission.

zend-permissions-rbac supports role inheritance, even allowing a role to inherit
permissions from multiple other roles. This allows you to create some fairly
complex and fine-grained permissions schemes!


As a basic example, we’ll create an RBAC for a content-based website. Let’s
start with a "guest" role, that only allows "read" permissions.

use ZendPermissionsRbacRbac;
use ZendPermissionsRbacRole;

// Create some roles
$guest= new Role('guest');

$rbac = new Rbac();

We can then assert if a given role is granted specific permissions:

$rbac->isGranted('guest', 'read'); // true
$rbac->isGranted('guest', 'write'); // false

Unknown roles

One thing to note: if the role used with isGranted() does not exist, this
method raises an exception, specifically a
ZendPermissionsRbacExceptionInvalidArgumentException, indicating the
role could not be found.

In many situations, this may not be what you want; you may want to handle
non-existent roles gracefully. You could do this in a couple ways. First, you
can test to see if the role exists before you check the permissions, using

if (! $rbac->hasRole($foo)) {
    // failed, due to missing role
if (! $rbac->isGranted($foo, $permission)) {
    // failed, due to missing permissions

Alternately, wrap the isGranted() call in a try/catch block:

try {
    if (! $rbac->isGranted($foo, $permission)) {
        // failed, due to missing permissions
} catch (RbacInvalidArgumentException $e) {
    if (! strstr($e->getMessage(), 'could be found')) {
        // failed, due to missing role

    // some other error occured
    throw $e;

Personally, I don’t like to use exceptions for application flow, so I
recommend the first solution. That said, in most cases, you will be working
with a role instance that you’ve just added to the RBAC.

Role inheritance

Let’s say we want to build on the previous example, and create an "editor" role
that also incorporates the permissions of the "guest" role, and adds a "write"

You might be inclined to think of the "editor" as inheriting from the "guest"
role — in other words, that it is a descendent or child of it.
However, in RBAC, inheritance works in the opposite direction: a parent
inherits all permissions of its children. As such, we’ll create the role as

$editor = new Role('editor');


$rbac->isGranted('editor', 'write'); // true
$rbac->isGranted('editor', 'read');  // true
$rbac->isGranted('guest',  'write'); // false

Another role might be a "reviewer" who can "moderate" content:

$reviewer = new Role('reviewer');


$rbac->isGranted('reviewer', 'moderate'); // true
$rbac->isGranted('reviewer', 'write');    // false; editor only!
$rbac->isGranted('reviewer', 'read');     // true
$rbac->isGranted('guest',    'moderate'); // false

Let’s create another, an "admin" who can do all of the above, but also has
permissions for "settings":

$admin= new Role('admin');


$rbac->isGranted('admin',    'settings'); // true
$rbac->isGranted('admin',    'write');    // true
$rbac->isGranted('admin',    'moderate'); // true
$rbac->isGranted('admin',    'read');     // true
$rbac->isGranted('editor',   'settings'); // false
$rbac->isGranted('reviewer', 'settings'); // false
$rbac->isGranted('guest',    'write');    // false

As you can see, permissions lookups are recursive and collective; the RBAC
examines all children and each of their descendants as far down as it needs to
determine if a given permission is granted!

Creating your RBAC

When should you create your RBAC, exactly? And should it contain all roles and

In most cases, you will be validating a single user’s permissions. What’s
interesting about zend-permissions-rbac is that if you know that user’s role,
the permissions they have been assigned, and any child roles (and their
permissions) to which the role belongs, you have everything you need. This means
that you can do most lookups on-the-fly.

As such, you will typically do the following:

  • Create a finite set of well-known roles and their permissions as a global RBAC.
  • Add roles (and optionally permissions) for the current user.
  • Validate the current user against the RBAC.

As an example, let’s say I have a user Mario who has the role "editor", and also
adds the permission "update". If our RBAC is already populated per the above
examples, I might do the following:

$mario= new Role('mario');


$rbac->isGranted($mario,   'settings'); // false; admin only!
$rbac->isGranted($mario,   'update');   // true; mario only!
$rbac->isGranted('editor', 'update');   // false; mario only!
$rbac->isGranted($mario,   'write');    // true; all editors
$rbac->isGranted($mario,   'read');     // true; all guests

Assigning roles to users

When you have some sort of authentication system in place, it will return some
sort of identity or user instance generally. You will then need to map this
to RBAC roles. But how?

Hopefully, you can store role information wherever you persist your user
information. Since roles are essentially stored internally as strings by
zend-permissions-rbac, this means that you can store the user role as a discrete
datum with your user identity.

Once you have, you have a few options:

  • Use the role directly from your identity when checking permissions: e.g.,
    $rbac->isGranted($identity->getRole(), 'write')
  • Create a ZendPermissionsRbacRole instance (or other concrete class) with
    the role fetched from the identity, and use that for permissions checks:
    $rbac->isGranted(new Role($identity->getRole()), 'write')
  • Update your identity instance to implement
    ZendPermissionsRbacRoleInterface, and pass it directly to permissions
    checks: $rbac->isGranted($identity, 'write')

This latter approach provides a nice solution, as it then also allows you to
store specific permissions and/or child roles as part of the user data.

The RoleInterface looks like the following:

namespace ZendPermissionsRbac;

use RecursiveIterator;

interface RoleInterface extends RecursiveIterator
     * Get the name of the role.
     * @return string
    public function getName();

     * Add permission to the role.
     * @param $name
     * @return RoleInterface
    public function addPermission($name);

     * Checks if a permission exists for this role or any child roles.
     * @param  string $name
     * @return bool
    public function hasPermission($name);

     * Add a child.
     * @param  RoleInterface|string $child
     * @return Role
    public function addChild($child);

     * @param  RoleInterface $parent
     * @return RoleInterface
    public function setParent($parent);

     * @return null|RoleInterface
    public function getParent();

The ZendPermissionsRbacAbstractRole contains basic implementations of most
methods of the interface, including logic for querying child permissions, so we
suggest inheriting from that if you can.

As an example, you could store the permissions as a comma-separated string and
the parent role as a string internally when creating your identity instance:

use ZendPermissionsRbacAbstractRole;
use ZendPermissionsRbacRoleInterface;
use ZendPermissionsRbacRole;

class Identity extends AbstractRole
     * @param string $username
     * @param string $role
     * @param array $permissions
     * @param array $childRoles
    public function __construct(
        string $username,
        array $permissions = [],
        array $childRoles = []
    ) {
        // $name is defined in AbstractRole
        $this->name = $username;

        foreach ($this->permissions as $permission) {

        $childRoles = array_merge(['guest'], $childRoles);
        foreach ($this->childRoles as $childRole) {

Assuming your authentication system uses a database table, and a lookup returns
an array-like row with the user information on a successful lookup, you might
then seed your identity instance as follows:

$identity = new Identity(
    explode(',', $row['permissions']),
    explode(',', $row['roles'])

This approach allows you to assign pre-determined roles to individual users,
while also allowing you to add fine-grained, individual permissions!

Custom assertions

Sometimes a static assertion is not enough.

As an example, we may want to implement a rule that the creator of a content
item in our website always has rights to edit the item. How would we implement
that with the above system?

zend-permissions-rbac allows you to do so via dynamic assertions. Such
assertions are classes that implement
ZendPermissionsRbacAssertionInterface, which defines the single method
public function assert(Rbac $rbac).

For the sake of this example, let’s assume:

  • The content item is represented as an object.
  • The object has a method getCreatorUsername() that will return the same
    username as we might have in our custom identity from the previous example.

Because we have PHP 7 at our disposal, we’ll create the assertion as an
anonymous class:

use ZendPermissionsRbacAssertionInterface;
use ZendPermissionsRbacRbac;
use ZendPermissionsRbacRoleInterface;

$assertion = new class ($identity, $content) implements AssertionInterface {
    private $content;
    private $identity;

    public function __construct(RoleInterface $identity, $content)
        $this->identity = $identity;
        $this->content = $content;

    public function assert(Rbac $rbac)
        return $this->identity->getName() === $this->content->getCreatorUsername();

$rbac->isGranted($mario, 'edit', $assertion); // returns true if $mario created $content

This opens even more possibilities than inheritance!


zend-permissions-rbac is quite simple to operate, but that simplicity hides a
great amount of flexibility and power; you can create incredibly fine-grained
permissions schemes for your applications using this component!

Next week, Enrico will cover using the component within a middleware stack; stay

Save the date!

Want to learn more about Expressive and Zend Framework? What better location
than ZendCon 2017! ZendCon will be hosted 23-26 October 2017 in Las Vegas,
Nevada, USA. Visit the ZendCon website for more

Source: Zend feed

Middleware authentication

Many web applications require restricting specific areas to authenticated
users, and may further restrict specific actions to authorized user roles.
Implementing authentication and authorization in a PHP application is often
non-trivial as doing so requires altering the application workflow. For
instance, if you have an MVC design, you may need to change the dispatch logic
to add an authentication layer as an initial event in the execution flow, and
perhaps apply restrictions within your controllers.

Using a middleware approach is simpler and more natural, as middleware easily
accommodates workflow changes. In this article, we will demonstrate how to
provide authentication in a PSR-7 middleware application using
Expressive and
zend-authentication. We
will build a simple authentication system using a login page with username and
password credentials.

Since the content of this post is quite long, we’ll detail authorization
in a separate blog post.

Getting started

This article assumes you have already created an Expressive application. For the
purposes of our application, we’ll create a new module, Auth, in which we’ll
put our classes, middleware, and general configuration.

First, if you have not already, install the tooling support:

$ composer require --dev zendframework/zend-expressive-tooling

Next, we’ll create the Auth module:

$ ./vendor/bin/expressive module:create Auth

With that out of the way, we can get started.


The zend-authentication component offers an adapter-based authentication
solution, with both a number of concrete adapters as well as mechanisms for
creating and consuming custom adapters.

The component exposes ZendAuthenticationAdapterAdapterInterface, which
defines a single authenticate() method:

namespace ZendAuthenticationAdapter;

interface AdapterInterface
     * Performs an authentication attempt
     * @return ZendAuthenticationResult
     * @throws ExceptionExceptionInterface if authentication cannot be performed
    public function authenticate();

Adapters implementing the authenticate() method perform the logic necessary to
authenticate a request, and return the results via a
ZendAuthenticationResult object. This Result object contains the
authentication result code and, in the case of success, the user’s identity.
The authentication result codes are defined using the following constants:

namespace ZendAuthentication;

class Result
    const SUCCESS = 1;
    const FAILURE = 0;

If we want to implement a login page with username and password
authentication, we can create a custom adapter such as the following:

// In src/Auth/src/MyAuthAdapter.php:

namespace Auth;

use ZendAuthenticationAdapterAdapterInterface;
use ZendAuthenticationResult;

class MyAuthAdapter implements AdapterInterface
    private $password;
    private $username;

    public function __construct(/* any dependencies */)
        // Likely assign dependencies to properties

    public function setPassword(string $password) : void
        $this->password = $password;

    public function setUsername(string $username) : void
        $this->username = $username;

     * Performs an authentication attempt
     * @return Result
    public function authenticate()
        // Retrieve the user's information (e.g. from a database)
        // and store the result in $row (e.g. associative array).
        // If you do something like this, always store the passwords using the
        // PHP password_hash() function!

        if (password_verify($this->password, $row['password'])) {
            return new Result(Result::SUCCESS, $row);

        return new Result(Result::FAILURE_CREDENTIAL_INVALID, $this->username);

We will want a factory for this service as well, so that we can seed the
username and password to it later:

// In src/Auth/src/MyAuthAdapterFactory.php:

namespace Auth;

use InteropContainerContainerInterface;
use ZendAuthenticationAuthenticationService;

class MyAuthAdapterFactory
    public function __invoke(ContainerInterface $container)
        // Retrieve any dependencies from the container when creating the instance
        return new MyAuthAdapter(/* any dependencies */);

This factory class creates and returns an instance of MyAuthAdapter.
We may need to pass some dependencies to its constructor, such as a database
connection; these would be fetched from the container.

Authentication Service

We can now create a ZendAuthenticationAuthenticationService
that composes our adapter, and then consume the AuthenticationService in
middleware to check for a valid user. Let’s now create a factory for the

// in src/Auth/src/AuthenticationServiceFactory.php:

namespace Auth;

use InteropContainerContainerInterface;
use ZendAuthenticationAuthenticationService;

class AuthenticationServiceFactory
    public function __invoke(ContainerInterface $container)
        return new AuthenticationService(

This factory class retrieves an instance of the MyAuthAdapter service and use
it to return an AuthenticationService instance. The AuthenticationService
class accepts two parameters:

  • A storage service instance, for persisting the user identity. If none is
    provided, the built-in PHP session mechanisms will be used.
  • The actual adapter to use for authentication.

Now that we have created both the custom adapter, as well as factories for the
adapter and the AuthenticationService, we need to configure our application
dependencies to use them:

// In src/Auth/src/ConfigProvider.php:

// Add the following import statement at the top of the classfile:
use ZendAuthenticationAuthenticationService;

// And update the following method:
public function getDependencies()
    return [
        'factories' => [
            AuthenticationService::class => AuthenticationServiceFactory::class,
            MyAuthAdapter::class => MyAuthAdapterFactory::class,

Authenticate using a login page

With an authentication mechanism in place, we now need to create middleware to
render the login form. This middleware will do the following:

  • for GET requests, it will render the login form.
  • for POST requests, it will check for credentials and then attempt to
    validate them.

    • for valid authentication requests, we will redirect to a welcome page
    • for invalid requests, we will provide an error message and redisplay the

Let’s create the middleware now:

// In src/Auth/src/Action/LoginAction.php:

namespace AuthAction;

use AuthMyAuthAdapter;
use InteropHttpServerMiddlewareDelegateInterface;
use InteropHttpServerMiddlewareMiddlewareInterface as ServerMiddlewareInterface;
use PsrHttpMessageServerRequestInterface;
use ZendAuthenticationAuthenticationService;
use ZendDiactorosResponseHtmlResponse;
use ZendDiactorosResponseRedirectResponse;
use ZendExpressiveTemplateTemplateRendererInterface;

class LoginAction implements ServerMiddlewareInterface
    private $auth;
    private $authAdapter;
    private $template;

    public function __construct(
        TemplateRendererInterface $template,
        AuthenticationService $auth,
        MyAuthAdapter $authAdapter
    ) {
        $this->template    = $template;
        $this->auth        = $auth;
        $this->authAdapter = $authAdapter;

    public function process(ServerRequestInterface $request, DelegateInterface $delegate)
        if ($request->getMethod() === 'POST') {
            return $this->authenticate($request);

        return new HtmlResponse($this->template->render('auth::login'));

    public function authenticate(ServerRequestInterface $request)
        $params = $request->getParsedBody();

        if (empty($params['username'])) {
            return new HtmlResponse($this->template->render('auth::login', [
                'error' => 'The username cannot be empty',

        if (empty($params['password'])) {
            return new HtmlResponse($this->template->render('auth::login', [
                'username' => $params['username'],
                'error'    => 'The password cannot be empty',


        $result = $this->auth->authenticate();
        if (!$result->isValid()) {
            return new HtmlResponse($this->template->render('auth::login', [
                'username' => $params['username'],
                'error'    => 'The credentials provided are not valid',

        return new RedirectResponse('/admin');

This middleware manages two actions: rendering the login form, and
authenticating the user’s credentials when submitted via a POST request.

You will also need to ensure that you have:

  • Created a login template.
  • Added configuration to map the auth template namespace to one or more
    filesystem paths.

We leave those tasks as an exercise to the reader.

We now need to create a factory to provide the dependencies for this

// In src/Auth/src/Action/LoginActionFactory.php:

namespace AuthAction;

use AuthMyAuthAdapter;
use InteropContainerContainerInterface;
use ZendAuthenticationAuthenticationService;
use ZendExpressiveTemplateTemplateRendererInterface;

class LoginActionFactory
    public function __invoke(ContainerInterface $container)
        return new LoginAction(

Map the middleware to this factory in your dependencies configuration witin the

// In src/Auth/src/ConfigProvider.php,

// Update the following method to read as follows:
public function getDependencies()
    return [
        'factories' => [
            ActionLoginAction::class => ActionLoginActionFactory::class,
            AuthenticationService::class => AuthenticationServiceFactory::class,
            MyAuthAdapter::class => MyAuthAdapterFactory::class,

Use zend-servicemanager’s ReflectionBasedAbstractFactory

If you are using zend-servicemanager in your application, you could skip the
step of creating the factory, and instead map the middleware to

Finally, we can create appropriate routes. We’ll map /login to the
LoginAction now, and allow it to react to either the GET or POST methods:

// in config/routes.php:
$app->route('/login', AuthActionLoginAction::class, ['GET', 'POST'], 'login');

Alternately, the above could be written as two separate statements:

// in config/routes.php:
$app->get('/login', AuthActionLoginAction::class, 'login');
$app->post('/login', AuthActionLoginAction::class);

Authentication middleware

Now that we have the authentication service and its adapter and the login
middleware in place, we can create middleware that checks for authenticated
users, having it redirect to the /login page if the user is not authenticated.

// In src/Auth/src/Action/AuthAction.php:

namespace AuthAction;

use InteropHttpServerMiddlewareDelegateInterface;
use InteropHttpServerMiddlewareMiddlewareInterface as ServerMiddlewareInterface;
use PsrHttpMessageServerRequestInterface;
use ZendAuthenticationAuthenticationService;
use ZendDiactorosResponseRedirectResponse;

class AuthAction implements ServerMiddlewareInterface
    private $auth;

    public function __construct(AuthenticationService $auth)
        $this->auth = $auth;

    public function process(ServerRequestInterface $request, DelegateInterface $delegate)
        if (! $this->auth->hasIdentity()) {
            return new RedirectResponse('/login');

        $identity = $this->auth->getIdentity();
        return $delegate->process($request->withAttribute(self::class, $identity));

This middleware checks for a valid identity using the hasIdentity() method of
AuthenticationService. If no identity is present, we redirect the redirect
configuration value.

If the user is authenticated, we continue the execution of the next middleware,
storing the identity in a request attribute. This facilitates consumption of the
identity information in subsequent middleware layers. For instance, imagine you
need to retrieve the user’s information:

namespace AppAction;

use InteropHttpServerMiddlewareDelegateInterface;
use InteropHttpServerMiddlewareMiddlewareInterface as ServerMiddlewareInterface;
use PsrHttpMessageServerRequestInterface;

class FooAction
    public function process(ServerRequestInterface $request, DelegateInterface $delegate)
        $user = $request->getAttribute(AuthAction::class);
        // $user will contains the user's identity

The AuthAction middleware needs some dependencies, so we will need to create
and register a factory for it as well.

First, the factory:

// In src/Auth/src/Action/AuthActionFactory.php:

namespace AuthAction;

use InteropContainerContainerInterface;
use ZendAuthenticationAuthenticationService;
use Exception;

class AuthActionFactory
    public function __invoke(ContainerInterface $container)
        return new AuthAction($container->get(AuthenticationService::class));

And then mapping it:

// In src/Auth/src/ConfigProvider.php:

// Update the following method to read as follows:
public function getDependencies()
    return [
        'factories' => [
            ActionAuthAction::class => ActionAuthActionFactory::class,
            ActionLoginAction::class => ActionLoginActionFactory::class,
            AuthenticationService::class => AuthenticationServiceFactory::class,
            MyAuthAdapter::class => MyAuthAdapterFactory::class,

Like the LoginActionFactory above, you could skip the factory creation and
instead use the ReflectionBasedAbstractFactory if using zend-servicemanager.

Require authentication for specific routes

Now that we built the authentication middleware, we can use it to protect
specific routes that require authentication. For instance, for each route that
needs authentication, we can modify the routing to create a pipeline that
incorporates our AuthAction middleware early:

$app->get('/admin', [
], 'admin');

$app->get('/admin/config', [
], 'admin.config');

The order of execution for the middleware is the order of the array elements.
Since the AuthAction middleware is provided as the first element, if a user is
not authenticated when requesting either the admin dashboard or config page,
they will be immediately redirected to the login page instead.


There are many ways to accommodate authentication within middleware
applications; this is just one. Our goal was to demonstrate the ease with which
you may compose authentication into existing workflows by creating middleware
that intercepts the request early within a pipeline.

You could certainly make a number of improvements to the workflow:

  • The path to the login page could be configurable.
  • You could capture the original request path in order to allow redirecting to
    it following successful login.
  • You could introduce rate limiting of login requests.

These are each interesting exercises for you to try!

As noted in the introduction, this article demonstrates only authentication.
Stay tuned for a future article that will demonstrate authorization middleware
using zend-permissions-rbac.

Save the date!

Want to learn more about Expressive and Zend Framework? What better location
than ZendCon 2017! ZendCon will be hosted 23-26 October 2017 in Las Vegas,
Nevada, USA. Visit the ZendCon website for more

Source: Zend feed

Manage your application with zend-config-aggregator

With the rise of PHP middleware, many developers are creating custom
application architectures, and running into an issue many frameworks already
solve: how to allow runtime configuration of the application.

configuration is often necessary, even in custom applications:

  • Some configuration, such as API keys, may vary between environments.
  • You may want to substitute services between development and production.
  • Some code may be developed by other teams, and pulled into your application
    separately (perhaps via Composer), and require
  • You may be writing code in your application that you will later want to share
    with another team, and recognize it should provide service wiring information
    or allow for dynamic configuration itself.

Faced with this reality, you then have a new problem: how can you configure your
application, as well as aggregate configuration from other sources?

As part of the Expressive initiative, we now offer a standalone solution for
you: zend-config-aggregator


First, you will need to install zend-config-aggregator:

$ composer require zendframework/zend-config-aggregator

One feature of zend-config-aggregator is the ability to consume multiple
configuration formats via zend-config.
If you wish to use that feature, you will also need to install that package:

$ composer require zendframework/zend-config

Finally, if you are using the above, and want to parse YAML files, you will need
to install the YAML PECL extension.

Configuration providers

zend-config-aggregator allows you to aggregate configuration from configuration
providers. A configuration provider is any PHP callable that will return an
associative array of configuration.

By default, the component provides the following providers out of the box:

  • ZendConfigAggregatorArrayProvider, which accepts an array of configuration
    and simply returns it. This is primarily useful for providing global defaults
    for your application.
  • ZendConfigAggregatorPhpFileProvider, which accepts a glob pattern
    describing PHP files that each return an associative array. When invoked, it
    will loop through each file, and merge the results with what it has previously
  • ZendConfigAggregatorZendConfigProvider, which acts similarly to the
    PhpFileProvider, but which can aggregate any format
    zend-config supports, including
    INI, XML, JSON, and YAML.

More interestingly, however, is the fact that you can write providers as simple
invokable objects:

namespace Acme;

class ConfigProvider
    public function __invoke()
        return [
            // associative array of configuration

This feature allows you to write configuration for specific application
features, and then seed your application with it. In other words, this feature
can be used as the foundation for a modular
which is exactly what we did with Expressive!


You may also use invokable classes or PHP callables that define generators as
configuration providers! As an example, the PhpFileProvider could
potentially be rewritten as follows:

use ZendStdlibGlob;

function () {
    foreach (Glob::glob('config/*.php', Glob::GLOB_BRACE) as $file) {
        yield include $file;

Aggregating configuration

Now that you have configuration providers, you can aggregate them.

For the purposes of this example, we’ll assume the following:

  • We will have a single configuration file, config.php, at the root of our
    application which will aggregate all other configuration.
  • We have a number of configuration files under config/, including YAML, JSON,
    and PHP files.
  • We have a third-party "module" that exposes the class
  • We have developed our own "module" for re-distribution that exposes the class

Typically, you will want aggregate configuration such that third-party
configuration is loaded first, with application-specific configuration merged
last, in order to override settings.

Let’s aggregate and return our configuration.

// in config.php:
use ZendConfigAggregatorConfigAggregator;
use ZendConfigAggregatorZendConfigProvider;

$aggregator = new ConfigAggregator([
    new ZendConfigProvider('config/*.{json,yaml,php}'),

return $aggregator->getMergedConfig();

This file aggregates the third-party configuration provider, the one we expose
in our own application, and then aggregates a variety of different configuration
files in order to, in the end, return an associative array representing the
merged configuration!

Valid config profider entries

You’ll note that the ConfigAggregator expects an array of providers as the
first argument to the constructor. This array may consist of any of the

  • Any PHP callable (functions, invokable objects, closures, etc.) returning an
  • A class name of a class that defines __invoke(), and which requires no
    constructor arguments.

This latter is useful, as it helps reduce operational overhead once you
introduce caching, which we discuss below. The above example demonstrates this

zend-config and PHP configuration

The above example uses only the ZendConfigProvider, and not the
PhpFileProvider. This is due to the fact that zend-config can also consume
PHP configuration.

If you are only using PHP-based configuration files, you can use the
PhpFileProvider instead, as it does not require additionally installing the
zendframework/zend-config package.

Globbing and precedence

Globbing works as it does on most *nix systems. As such, you need to pay
particular attention to when you use patterns that define alternatives, such
as the {json,yaml,php} pattern above. In such cases, all JSON files will be
aggregated, followed by YAML files, and finally PHP files. If you need them
to aggregate in a different order, you will need to change the pattern.


You likely do not want to aggregate configuration on each and every application
request, particularly if doing so would result in many filesystem hits.
Fortunately, zend-config-aggregator also has built-in caching features.

To enable these features, you will need to do two things:

  • First, you need to provide a second argument to the ConfigAggregator
    constructor, specifying the path to the cache file to create and/or use.
  • Second, you need to enable caching in your configuration, by specifying a
    boolean true value for the key ConfigAggregator::ENABLE_CACHE.

One common strategy is to enable caching by default, and then disable it via
environment-specific configuration.

We’ll update the above example now to enable caching to the file

use ZendConfigAggregatorArrayProvider;
use ZendConfigAggregatorConfigAggregator;
use ZendConfigAggregatorPhpFileProvider;
use ZendConfigAggregatorZendConfigProvider;

$aggregator = new ConfigAggregator(
        new ArrayProvider([ConfigAggregator::ENABLE_CACHE => true]),
        new ZendConfigProvider('config/{,*.}global.{json,yaml,php}'),
        new PhpFileProvider('config/{,*.}local.php'),

return $aggregator->getMergedConfig();

The above adds an initial setting that enables the cache, and tells it to cache
it to cache/config.php.

Notice also that this example changes the ZendConfigProvider, and adds a
PhpFileProvider entry. Let’s examine these.

The ZendConfigProvider glob pattern now looks for files named global with
one of the accepted extensions, or those named *.global with one of the
accepted extensions. This allows us to segregate configuration that should
always be present from environment-specific configuration.

We then add a PhpFileProvider that aggregates local.php and/or *.local.php
files specifically. An interesting side-note about the shipped providers is that
if no matching files are found, the provider will return an empty array; this
means that we can have this additional provider that is looking for separate
configurations for the "local" environment! Because this provider is aggregated
last, the settings it exposes will override any others.

As such, if we want to disable caching, we can create a file such as
config/local.php with the following contents:

use ZendConfigAggregatorConfigAggregator;

return [ConfigAggregator::ENABLE_CACHE => false];

and the application will no longer cache aggregated configuration!

Clear the cache!

The setting outlined above is used to determine whether the configuration
cache file should be created if it does not already exist.
zend-config-aggregator, when provided the location of a configuration cache
file, will load directly from it if the file is present.

As such, if you make the above configuration change, you will first need to
remove any cached configuration:

$ rm cache/config.php

This can even be made into a Composer script:

"scripts": {
    "clear-config-cache": "rm cache/config.php"

Allowing you to do this:

$ composer clear-config-cache

Which allows you to change the location of the cache file without needing to
re-learn the location every time you need to clear the cache.

Auto-enabling third-party providers

Being able to aggregate providers from third-parties is pretty stellar; it means
that you can be assured that configuration the third-party code expects is
generally present — with the exception of values that must be provided by the
consumer, that is!

However, there’s one minor problem: you need to remember to register these
configuration providers with your application, by manually editing your
config.php file and adding the appropriate entries.

Zend Framework solves this via the zf-component-installer Composer
. If your
package is installable via Composer, you can add an entry to your package
definition as follows:

"extra": {
    "zf": {
        "config-provider": [

If the end-user:

  • Has required zendframework/zend-component-installer in their application (as
    either a production or development dependency), AND
  • has the config aggregation script in config/config.php

then the plugin will prompt you, asking if you would like to add each of the
config-provider entries found in the installed package into the configuration

As such, for our example to work, we would need to move our configuration script
to config/config.php, and likely move our other configuration files into a


This approach is essentially that taken by Expressive.

When those changes are made, any package you add to your application that
exposes configuration providers will prompt you to add them to your
configuration aggregation, and, if you confirm, will add them to the top of the

Final notes

First, we would like to thank Mateusz Tymek, whose
prototype ‘expressive-config-manager’ project became zend-config-aggregator.
This is a stellar example of a community project getting adopted into the

Second, this approach has some affinity to a proposal from the folks who brought
us PSR-11, which defines the ContainerInterface used within Expressive for
allowing usage of different dependency injection containers. That same group is
now working on a service provider
proposal that would standardize how standalone libraries expose services to
containers; we recommend looking at that project as well.

We hope that this post helps spawn ideas for configuring your next project!

Save the date!

Want to learn more about Expressive and Zend Framework? What better location
than ZendCon 2017! ZendCon will be hosted 23-26 October 2017 in Las Vegas,
Nevada, USA. Visit the ZendCon website for more

Source: Zend feed

Caching middleware with Expressive

Performance is one of the key feature for web application. Using a middleware
architecture makes it very simple to implement a caching system in PHP.

The general idea is to store the response output of a URL in a file (or in
memory, using memcached) and use it for subsequent
requests. In this way we can bypass the execution of the previous middlewares
starting from the second request.

Of course, this technique can only be applied for static contents, that does not
require update for each HTTP request.

Implement a caching middleware

Imagine we want to create a simple cache system with Expressive.
We can use an implementation like that:

namespace AppAction;

use InteropHttpServerMiddlewareDelegateInterface;
use InteropHttpServerMiddlewareMiddlewareInterface as ServerMiddlewareInterface;
use PsrHttpMessageServerRequestInterface;
use ZendDiactorosResponseHtmlResponse;

class CacheMiddleware implements ServerMiddlewareInterface
    protected $config;

    public function __construct(array $config)
        $this->config = $config;

    public function process(ServerRequestInterface $request, DelegateInterface $delegate)
        $url  = str_replace('/', '_', $request->getUri()->getPath());
        $file = $this->config['path'] . $url . '.html';
        if ($this->config['enabled'] && file_exists($file) &&
            (time() - filemtime($file)) < $this->config['lifetime']) {
            return new HtmlResponse(file_get_contents($file));

        $response = $delegate->process($request);
        if ($response instanceof HtmlResponse && $this->config['enabled']) {
            file_put_contents($file, $response->getBody());
        return $response;

In this example, we used the PSR-15
proposal to implement the Middleware interface using the process() function.
This is the suggested way to implement middleware in Expressive 2.0.

The idea of this middleware is quite simple. If the caching system is enabled
and if the requested URL matches an existing cache file, we return the cache
content as HtmlResponse,
ending the execution flow.

If the requested URL path does not exist in cache, we process the delegate
middleware (basically we continue with the normal workflow) and we store the
response in the cache, if enabled.

Configure the cache system

To manage the cache, we used a configuration key cache to specify the path of
the cache files, the lifetime in seconds and the enabled value to turn on
and off the caching system.

Since we used a file to store the cache content, we can use the file
modification time to manage the lifetime of the cache. We used the filemtime
function of PHP to retrieve the modification file time.

Note: if you want to use memcached instead of file you need to replace the
file_get_contents and file_put_contents functions with Memcached::get
and Memcached::set. Moreover, you do not need to check for lifetime
because when you set a content in memcached you can specify the expiration

In order to pass the $config dependency, we can use a simple factory class.
This is an example:

namespace AppAction;

use InteropContainerContainerInterface;
use Exception;

class CacheFactory
    public function __invoke(ContainerInterface $container)
        $config = $container->get('config');
        if (isset($config['cache']) && isset($config['cache']['enabled'])) {
            if ($config['cache']['enabled']) {
                if (!isset($config['cache']['path'])) {
                    throw new Exception('The cache path is not configured');
                if (!isset($config['cache']['lifetime'])) {
                    throw new Exception('The cache lifetime is not configured');
        return new CacheMiddleware($config['cache']);

Following the folder structure of Expressive, we can store this configuration in
a simple PHP file in the config/autoload directory. For instance, we can store
it in config/autoload/cache.local.php file, as follows:

return [
    'cache' => [
      'enabled'  => true,
      'path'     => __DIR__ . '/../../data/cache/',
      'lifetime' => 3600 // in seconds

We used the folder /data/cache for storing the caching file. The content of
this folder should be omitted in the version control. For instance, using git
you can omit the content putting a .gitignore file inside the cache folder
with the following content:


Finally, in order to activate the caching system we need to add the
CacheMiddleware class as service. In our example, we used zend-servicemanager
as service container. To add the cache system we can use a configuration file
(e.g. /config/autoload/cache.global.php) with the following content:

return [
    'dependencies' => [
        'factories' => [
            AppActionCacheMiddleware::class => AppActionCacheFactory::class

How enable the cache for specific routes

We mentioned early that this caching mechanism works fine for static content.
That means we need a way to enable the cache only for specific routes.

We can simply add the CacheMiddleware class as first middleware to be executed
for all the routes representing static contents.

For instance, imagine to have a /about route that show an about page of your
web site. We can add the CacheMiddleware as follow:

use AppAction;

$app->get('/about', [
], 'about');

The middleware actions to be excuted for the /about URL are CacheMiddleware
and AboutAction in this order. The $app object is the instance of ZendExpressiveApplication,
the main class that manages the execution of an Expressive application.


In this brief article we showed how to build a caching system for a PHP
application based on PSR-7 and PSR-15. A middleware architecture facilitates the
design of a cache layer because it uses the same workflow, an HTTP request as
input and an HTTP response as output. In this way, we can manage the HTTP
request, get the HTTP response for any middlewares and store the result for
caching, all in one place.

In this article we used the zendframework/zend-expressive-skeleton application
as example. For more information about Expressive, visit the documentation.

Save the date!

Want to learn more about Expressive and Zend Framework? What better location
than ZendCon 2017! ZendCon will be hosted 23-26 October 2017 in Las Vegas,
Nevada, USA. Visit the ZendCon website for more

Source: Zend feed

Scrape Screens with zend-dom

Even in this day-and-age of readily available APIs and RSS/Atom feeds, many
sites offer none of them. How do you get at the data in those cases? Through the
ancient internet art of screen scraping.

The problem then becomes: how do you get at the data you need in a pile of HTML
soup? You could use regular expressions or any of the various string functions
in PHP. All of these are easily subject to error, though, and often require some
convoluted code to get at the data of interest.

Alternately, you could treat the HTML as XML, and use the DOM
, which is typically built-in to PHP. Doing so,
however, requires more than a passing familiarity with
XPath, which is something of a black art.

If you use JavaScript libraries or write CSS fairly often, you may be familiar
with CSS selectors, which allow you to target either specific nodes or groups of
nodes within an HTML document. These are generally rather intuitive:

jQuery('section.slide h2').each(function (node) {

What if you could do that with PHP?

Introducing zend-dom

zend-dom provides CSS selector
capabilities for PHP, via the ZendDomQuery class, including:

  • element types (h2, span, etc.)
  • class attributes (.error, .next, etc.)
  • element identifiers (#nav, #main, etc.)
  • arbitrary element attributes (div[onclick="foo"]), including word matches
    (div[role~="navigation"]) and substring matches (div[role*="complement"])
  • descendents (div .foo span)

While it does not implement the full spectrum of CSS selectors, it does provide
enough to generally allow you to get at the information you need within a page.

Example: retrieving a navigation list

As an example, let’s fetch the navigation list from the ZendDomQuery
documentation page itself:

use ZendDomQuery;

$html = file_get_contents('https://docs.zendframework.com/zend-dom/query/');
$query = new Query($html);
$results = $query->execute('ul.bs-sidenav li a');

printf("Received %d results:n", count($results));
foreach ($results as $result) {
    printf("- [%s](%s)n", $result->getAttribute('href'), $result->textContent);

The above queries for ul.bs-sidenav li a — in other words, all links
within list items of the sidenav unordered list.

When you execute() a query, you are returned a ZendDomNodeList instance,
which decorates a DOMNodeList in order to
provide features such as Countable, and access to the original query and
document. In the example above, we count() the results, and then loop over them.

Each item in the list is a DOMNode, giving you
access to any attributes, the text content, and any child elements. In our
case, we access the href attribute (the link target), and report the text
content (the link text).

The results are:

Received 3 results:
- [#querying-html-and-xml-documents](Querying HTML and XML Documents)
- [#theory-of-operation](Theory of Operation)
- [#methods-available](Methods Available)

Other uses

Another use case is for testing. When you have classes that return HTML, or if
you want to execute requests and test the generated output, you often don’t want
to test exact contents, but rather look for specific data or fragments within
the document.

We provide these capabilities for zend-mvc
applications via the zend-test component,
which provides a number of CSS selector assertions
for use in querying the content returned in your MVC responses. Having these
capabilities allows testing for dynamic content as well as static content,
providing a number of vectors for ensuring application quality.

Start scraping!

While this post was rather brief, we hope you can appreciate the powerful
capabilities of this component! We have used this functionality in a variety of
ways, from testing applications to creating feeds based on content differences
in web pages, to finding and retrieving image URIs from pages.

Get more information from the zend-dom documentation.

Source: Zend feed

zend-config For All Your Configuration Needs

Different applications and frameworks have different opinions about how
configuration should be created. Some prefer XML, others YAML, some like JSON,
others like INI, and some even stick to the JavaProperties format; in Zend
Framework, we tend to prefer PHP arrays, as each of the other formats
essentially get compiled to PHP arrays eventually anyways.

At heart, though, we like to support developer needs, whatever they may be, and,
as such, our zend-config component
provides ways of working with a variety of configuration formats.


zend-config is installable via Composer:

$ composer require zendframework/zend-config

The component has two dependencies:

  • zend-stdlib, which provides some
    capabilities around configuration merging.
  • psr/container, to allow reader and
    writer plugin support for the configuration factory.

Latest version

This article covers the most recently released version of zend-config, 3.1.0,
which contains a number of features such as PSR-11 support that were not
previously available. If you are using Zend Framework, you should be able to
safely provide the constraint ^2.6 || ^3.1, as the primary APIs remain the

Retrieving configuration

Once you’ve installed zend-config, you can start using it to retrieve and access
configuration files. The simplest way is to use ZendConfigFactory, which
provides tools for loading configuration from a variety of formats, as well as
capabilities for merging.

If you’re just pulling in a single file, use Factory::fromFile():

use ZendConfigFactory;

$config = Factory::fromFile($path);

Far more interesting is to use multiple files, which you can do via
Factory::fromFiles(). When you do, they are merged into a single
configuration, in the order in which they are provided to the factory. This is
particularly interesting using glob():

use ZendConfigFactory;

$config = Factory::fromFiles(glob('config/autoload/*.*'));

What’s particularly interesting about this is that it supports a variety of

  • PHP files returning arrays (.php extension)
  • INI files (.ini extension)
  • JSON files (.json extension)
  • XML files (using PHP’s XMLReader; .xml extension)
  • YAML files (using ext/yaml, installable via PECL; .yaml extension)
  • JavaProperties files (.javaproperties extension)

This means that you can choose the configuration format you prefer, or
mix-and-match multiple formats, if you need to combine configuration from
multiple libraries!

Configuration objects

By default, ZendConfigFactory will return PHP arrays for the merged
configuration. Some dependency injection containers do not support arrays as
services, however; moreover, you may want to pass some sort of structured object
instead of a plain array when injecting dependencies.

As such, you can pass a second, optional argument to each of fromFile() and
fromFiles(), a boolean flag. When true, it will return a
ZendConfigConfig instance, which implements Countable, Iterator, and
ArrayAccess, allowing it to look and act like an array.

What is the benefit?

First, it provides property overloading to each configuration key:

$debug = $config->debug ?: false;

Second, it offers a convenience method, get(), which allows you to specify a
default value to return if the value is not found:

$debug = $config->get('debug', false); // Return false if not found

This is largely obviated by the ?: ternary shortcut in modern PHP versions,
but very useful when mocking in your tests.

Third, nested sets are also returned as Config instances, which gives you the
ability to use the above get() method on a nested item:

if (isset($config->expressive)) {
    $config = $config->get('expressive'); // same API!

Fourth, you can mark the Config instance as immutable! By default, it acts
just like array configuration, which is, of course, mutable. However, this can
be problematic when you use configuration as a service, because, unlike an
array, a Config instance is passed by reference, and changes to values would
then propagate to any other services that depend on the configuration.

Ideally, you wouldn’t be changing any values in the instance, but
ZendConfigConfig can enforce that for you:

$config->setReadOnly(); // Now immutable!

Further, calling this will mark nested Config instances as read-only as well,
ensuring data integrity for the entire configuration tree.

Read-only by default!

One thing to note: by default, Config instances are read-only! The
constructor accepts an optional, second argument, a flag indicating whether or
not the instance allows modifications, and the value is false by default.
Whenever you use the Factory to create a Config instance, it never enables
that flag, meaning that if you return a Config instance, it will be read-only.

If you want a mutable instance from a Factory, use the following construct:

use ZendConfigConfig;
use ZendConfigFactory;

$config = new Config(Factory::fromFiles($files), true);

Including other configuration

Most of the configuration reader plugins also support "includes": directives
within a configuration file that will include configuration from another file.
(JavaProperties is the only configuration format we support that does not have
this functionality included.)

For instance:

  • INI files can use the key @include to include another file relative to the
    current one; values are merged at the same level:

    webhost = 'www.example.com'
    @include = 'database.ini'
  • For XML files, you can use XInclude:

    <?xml version="1.0" encoding="utf-8">
    <config xmlns:xi="http://www.w3.org/2001/XInclude">
      <xi:include href="database.xml"/>
  • JSON files can use an @include key:

      "webhost": "www.example.com",
      "@include": "database.json"
  • YAML also uses the @include notation:

    webhost: www.example.com
    @include: database.yaml

Choose your own YAML

Out-of-the-box we support the YAML PECL extension
for our YAML support. However, we have made it possible to use alternate
parsers, such as Spyc or the Symfony YAML component, by passing a callback to the
reader’s constructor:

use SymfonyComponentYamlYaml as SymfonyYaml;
use ZendConfigReaderYaml as YamlConfig;

$reader = new YamlConfig([SymfonfyYaml::class, 'parse']);
$config = $reader->fromFile('config.yaml');

Of course, if you’re going to do that, you could just use the original library,
right? But what if you want to mix YAML and other configuration with the
Factory class?

There aer two ways to register new plugins. One is to create an instance and
register it with the factory:

use SymfonyComponentYamlYaml as SymfonyYaml;
use ZendConfigFactory;
use ZendConfigReaderYaml as YamlConfig;

Factory::registerReader('yaml', new YamlConfig([SymfonyYaml::class, 'parse']));

Alternately, you can provide an alternate reader plugin manager. You can do that
by extending ZendConfigStandaloneReaderPluginManager, which is a barebones
PSR-11 container for use as a plugin manager:

namespace Acme;

use SymfonyComponentYamlYaml as SymfonyYaml;
use ZendConfigReaderYaml as YamlConfig;
use ZendConfigStandaloneReaderPluginManager;

class ReaderPluginManager extends StandaloneReaderPluginManager
     * @inheritDoc
    public function has($plugin)
        if (YamlConfig::class === $plugin
            || 'yaml' === strtolower($plugin)
        ) {
            return true;

        return parent::has($plugin);

     * @inheritDoc
    public function get($plugin)
        if (YamlConfig::class !== $plugin
            && 'yaml' !== strtolower($plugin)
        ) {
            return parent::get($plugin);

        return new YamlConfig([SymfonyYaml::class, 'parse']);

Then register this with the Factory:

use AcmeReaderPluginManager;
use ZendConfigFactory;

Factory::setReaderPluginManager(new ReaderPluginManager());

Processing configuration

zend-config also allows you to process a ZendConfigConfig instance and/or
an individual value. Processors perform operations such as:

  • substituting constant values within strings
  • filtering configuration data
  • replacing tokens within configuration
  • translating configuration values

Why would you want to do any of these operations?

Consider this: deserialization of formats other than PHP cannot take into
account PHP constant values or class names!

While this may work in PHP:

return [
    AcmeComponent::CONFIG_KEY => [
        'host' => AcmeComponent::CONFIG_HOST,
        'dependencies' => [
            'factories' => [
                AcmeMiddlewareAuthorization::class => AcmeMiddlewareAuthorizationFactory::class,

The following JSON configuration would not:

    "AcmeComponent::CONFIG_KEY": {
        "host": "AcmeComponent::CONFIG_HOST"
        "dependencies": {
            "factories": {
                "AcmeMiddlewareAuthorization::class": "AcmeMiddlewareAuthorizationFactory::class"

Enter the Constant processor!

This processor looks for strings that match constant names, and replaces them
with their values. Processors generally only work on the configuration values,
but the Constant processor allows you to opt-in to processing the keys as

Since processing modifies the Config instance, you will need to manually
create an instance, and then process it. Let’s look at that:

use AcmeComponent;
use ZendConfigConfig;
use ZendConfigFactory;
use ZendConfigProcessor;

$config = new Config(Factory::fromFile('config.json'), true);
$processor = new ProcessorConstant();

// ['AcmeMiddlewareAuthorization' => 'AcmeMiddlewareAuthorizationFactory']

This is a really powerful feature, as it allows you to add more verifications
and validations to your configuration files, regardless of the format you use.

In version 3.1.0 forward

The ability to work with class constants and process keys was added only
recently in the 3.1.0 version of zend-config.

Config all the things!

This post covers the parsing features of zend-config, but does not even touch on
another major capability: the ability to write configuration! We’ll leave that
to another post.

In terms of configuration parsing, zend-config is simple, yet powerful. The
ability to process a number of common configuration formats, utilize
configuration includes, and process keys and values means you can highly
customize your configuration process to suit your needs or integrate different
configuration sources.

Get more information from the zend-config

Source: Zend feed

PHP and SQL Server for Linux

This week we tested the public preview of Microsoft SQL Server for Linux
using PHP 7 with our component zendframework/zend-db.

Microsoft announced the availability of a public
preview of SQL Server for Linux on the 16th of November, 2016. This new
version of SQL Server has some interesting features such as:

  • transparent data encryption;
  • always encrypted;
  • row level security;
  • in-memory tables;
  • columnstore indexing;
  • native JSON support;
  • support for in-database analytics with R-integration.

Moreover, the performance of the new DBMS seems to be very impressive. Microsoft
published a case study with 1.2 million requests per second with In-Memory OLTP
on a single commodity server

We tested the last preview of SQL Server (CTP1.2 – using a Vagrant
box with Ubuntu 16.04 and 4 GB RAM.

Install SQL Server on Linux

We followed the instructions list on the Microsoft website
to install SQL Server for PHP on Ubuntu 16.04.

To ensure optimal performance of SQL Server, the Ubuntu box should have at least
4 GB of memory.

The first step is to add the GPG key for the Microsoft repositories.

$ sudo su
$ curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
$ curl https://packages.microsoft.com/config/ubuntu/16.04/mssql-server.list > /etc/apt/sources.list.d/mssql-server.list
$ exit

Then we can update the repository list and install the mssql-server package,
using the following commands:

$ sudo apt-get update
$ sudo apt-get install mssql-server

Now we can run the setup for sqlserver. We will be required to accept the EULA and
choose a password for the System Administrator (SA).

sudo /opt/mssql/bin/sqlservr-setup

After the installation, we will have SQL Server running on Linux!

Install the command line utility for SQL Server

Now that we have the DBMS running, we need a tool to access it. Microsoft
provides a command line tool named sqlcmd. This program is very similar to the
MySQL client tool, quite familiar to PHP developers.

To install sqlcmd, we need to run the following commands:

$ sudo su
$ curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list > /etc/apt/sources.list.d/mssql-tools.list
$ exit
$ sudo apt-get update
$ sudo apt-get install msodbcsql mssql-tools unixodbc-dev
$ echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bash_profile
$ echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc
$ source ~/.bashrc


Though the Microsoft documentation does not indicate it, we also installed
msodbcsql; without it, we ran into dependency issues.

If the installation was successful, we can start using the command line tool.
For instance, we can ask for the SQL Server vesion using the following

$ sqlcmd -S localhost -U sa -P yourpassword -Q "SELECT @@VERSION"

where yourpassword should be replaced with the SA password that you choose
during the SQL Server setup.

This command will return output like the following:

Microsoft SQL Server vNext (CTP1.2) - (X64)
	Jan 10 2017 19:15:28
	Copyright (C) 2016 Microsoft Corporation. All rights reserved.
	on Linux (Ubuntu 16.04.1 LTS)

Install the SQL Server extension for PHP

Next, we need to install the PHP extension for SQL Server. This can be done
using PECL.


If you do not have PECL installed on Ubuntu, you can install it with the
following command:

$ sudo apt-get install php-dev

To install the sqlsrv and pdo_sqlsrv extensions for PHP, we need to execute
the following commands:

$ sudo apt-get install unixodbc-dev gcc g++ build-essential
$ sudo pecl install sqlsrv pdo_sqlsrv

Finally, we need to add the directives extension=pdo_sqlsrv.so and
extension=sqlsrv.so to our PHP configuration (generally php.ini). In our
case, running Ubuntu (or any other Debian-flavored distribution) we have the
PHP configuration files stored in /etc/php/7.0/mods-available. We can create
sqlsrv.ini and pdo_sqlsrv.ini containing the respective configurations. As
a last step, we need to link these configurations to our specific PHP environments.
For this, you can have two choices:

  • For Ubuntu, you can use the phpenmod command.
  • Alternately, you can symlink to the appropriate directory.

For our purposes, we are using PHP 7.0, from the CLI SAPI, so we can do either of the following:

# Using phpenmod:
$ sudo phpenmod -v 7.0 -s cli sqlsrv pdo_sqlsrv
# Manually symlinking:
$ sudo ln -s /etc/php/7.0/mods-available/sqlsrv.ini /etc/php/7.0/cli/conf.d/20-sqlsrv.ini
$ sudo ln -s /etc/php/7.0/mods-available/pdo_sqlsrv.ini /etc/php/7.0/cli/conf.d/20-pdo_sqlsrv.ini

Integration tests with zend-db

We used the above information to add support for SQL Server to the
zendframework/zend-db vagrant
configuration, used by developers to test against the various database platforms
we support.

We updated the Vagrantfile,
enabling integration tests for MySQL, PostgreSQL and SQL Server on Linux, to
read as follows:

$install_software = <<SCRIPT
export DEBIAN_FRONTEND=noninteractive
apt-get -yq update

apt-get -yq install postgresql

# Allow external connections to PostgreSQL as postgres
sed -i "s/#listen_addresses = 'localhost'/listen_addresses = '*'/" /etc/postgresql/9.5/main/postgresql.conf
sed -i "s/peer/trust/" /etc/postgresql/9.5/main/pg_hba.conf
echo 'host all all trust' >> /etc/postgresql/9.5/main/pg_hba.conf
service postgresql restart

debconf-set-selections <<< "mysql-server mysql-server/root_password password Password123"
debconf-set-selections <<< "mysql-server mysql-server/root_password_again password Password123"
apt-get -yq install mysql-server

# Allow external connections to MySQL as root (with password Password123)
sed -i 's/' /etc/mysql/mysql.conf.d/mysqld.cnf
mysql -u root -pPassword123 -e 'USE mysql; UPDATE `user` SET `Host`="%" WHERE `User`="root" AND `Host`="localhost"; DELETE FROM `user` WHERE `Host` != "%" AND `User`="root"; FLUSH PRIVILEGES;'
service mysql restart

# More info here: https://www.microsoft.com/en-us/sql-server/developer-get-started/php-ubuntu

curl -s https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
curl -s https://packages.microsoft.com/config/ubuntu/16.04/mssql-server.list > /etc/apt/sources.list.d/mssql-server.list
apt-get -yq update
apt-get -yq install mssql-server
printf "YESnPassword123nPassword123nyny" | /opt/mssql/bin/sqlservr-setup

curl -s https://packages.microsoft.com/config/ubuntu/16.04/prod.list > /etc/apt/sources.list.d/mssql-tools.list
apt-get -yq update
ACCEPT_EULA=Y apt-get -yq install msodbcsql mssql-tools unixodbc-dev
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> /home/vagrant/.bash_profile
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> /home/vagrant/.bashrc
source /home/vagrant/.bashrc

$setup_vagrant_user_environment = <<SCRIPT
if ! grep "cd /vagrant" /home/vagrant/.profile > /dev/null; then
  echo "cd /vagrant" >> /home/vagrant/.profile

Vagrant.configure(2) do |config|
  config.vm.box = 'bento/ubuntu-16.04'
  config.vm.provider "virtualbox" do |v|
    v.memory = 4096
    v.cpus = 2

  config.vm.network "private_network", ip: ""

  config.vm.provision 'shell', inline: $install_software
  config.vm.provision 'shell', privileged: false, inline: '/vagrant/.ci/mysql_fixtures.sh'
  config.vm.provision 'shell', privileged: false, inline: '/vagrant/.ci/pgsql_fixtures.sh'
  config.vm.provision 'shell', privileged: false, inline: '/vagrant/.ci/sqlsrv_fixtures.sh'
  config.vm.provision 'shell', inline: $setup_vagrant_user_environment

This vagrant configuration installs Ubuntu 16.04 with 4 GB of RAM and the
following databases (user and password are reported in parenthesis):

  • MySQL 5.7.17 (root/Password123)
  • PostgreSQL 9.5 (postgres/postgres)
  • SQL Server (sa/Password123)

We can use this virtual machine to run PHPUnit for the
zend-db integration tests. If you want to test this vagrant box, you can clone
the zend-db repository and run the following command:

$ vagrant up

This creates a VM running with IP

More information

In this post, we’ve demonstrated how to install the public preview release of
SQL Server on Ubuntu. Microsoft also provides support for other Linux
distributions, such as Red Hat and Suse.

You can also install this new SQL Server preview on Windows, macOS, Azure or
using a preconfigured Docker container.

Get more information on SQL Server for Linux from the official website.
Find specific information on how to use SQL Server with PHP

Source: Zend feed

1 2 3 4