Tag: 3d

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
consider.

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

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 introducezend-permissions-rbac,
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:

$ composer require zendframework/zend-permissions-rbac

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

Vocabulary

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!

Basics

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.

Unknown roles

One thing to note: if the role used with isGranted() does not exist, this
method raises an exception, specifically aZendPermissionsRbacExceptionInvalidArgumentException, 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, usinghasRole():

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

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"
permission.

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
follows:

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
permissions?

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:

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')

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:

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 implementZendPermissionsRbacAssertionInterface, which defines the single methodpublic 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:

Summary

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
tuned!

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 usingExpressive andzend-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.

Authentication

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:

Adapters implementing the authenticate() method perform the logic necessary to
authenticate a request, and return the results via aZendAuthenticationResult 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:

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 theAuthenticationService:

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 toZendServiceManagerAbstractFactoryReflectionBasedAbstractFactory.

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

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.

This middleware checks for a valid identity using the hasIdentity() method ofAuthenticationService. 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:

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:

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.

Conclusion

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.

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
configuration.

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?

Installation

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 configurationproviders. 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
stored.

ZendConfigAggregatorZendConfigProvider, which acts similarly to thePhpFileProvider, but which can aggregate any formatzend-config supports, including
INI, XML, JSON, and YAML.

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

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

Generators

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:

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
following:

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
usage.

zend-config and PHP configuration

The above example uses only the ZendConfigProvider, and not thePhpFileProvider. 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 thePhpFileProvider 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.

Caching

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 filecache/config.php:

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 aPhpFileProvider 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 shouldalways 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 asconfig/local.php with the following contents:

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 yourconfig.php file and adding the appropriate entries.

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
script!

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
framework!

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!

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:

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 thefile_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
directly.

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

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:

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:

*
!.gitignore

Finally, in order to activate the caching system we need to add theCacheMiddleware 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:

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.

Conclusion

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.

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
extension, which is typically built-in to PHP. Doing so,
however, requires more than a passing familiarity withXPath, 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:

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).

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.

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.

Installation

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
same.

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 viaFactory::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():

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

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() andfromFiles(), a boolean flag. When true, it will return aZendConfigConfig instance, which implements Countable, Iterator, andArrayAccess, 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:

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, butZendConfigConfig 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:

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 theFactory 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:

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
well.

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

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.

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.

Finally, we need to add the directives extension=pdo_sqlsrv.so andextension=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 createsqlsrv.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:

Laravel Homestead is an interesting
project by the Laravel community that provides a Vagrant
box for PHP developers. It includes a full set of services for PHP developers,
such as the Nginx web server, PHP 7.1, MySQL, Postgres, Redis, Memcached, Node, and
more.

One the most interesting features of this project is the ability to enable it per
project. This means you can run a vagrant box for your specific PHP project.

In this post, we’ll examine using it for Zend Framework MVC, Expressive, and
Apigility projects. In each case, installation and usage is exactly the same.

Install the Vagrant box

We used VirtualBox and the following command to install the laravel/homestead
box:

$ vagrant box add laravel/homestead

The box is 981 MB, so it will take some minutes to download.

Homestead, by default, uses the host name homestead.app, and requires that you
update your system hosts file to point that domain to the virtual machine IP
address. To faciliate that, Homestead provides integration with thevagrant-hostsupdater
Vagrant plugin. We recommend installing that before your initial run of the
virtual machine:

$ vagrant plugin install vagrant-hostsupdater

Use Homestead in ZF projects

Once you have installed the laravel/homestead vagrant box, you can use it globally
or per project.

If we install Homestead per-project, we will have a full development server
configured directly in the local folder, without sharing services with other
projects. This is a big plus!

To use Homestead per-project, we need to install the laravel/homestead
package within our Zend Framework, Apigility, or Expressive project. This can be
done using Composer with the following command:

$ composer require --dev laravel/homestead

After installation, execute the homestead command to build the Vagrantfile:

$ vendor/bin/homestead make

This command creates both the VagrantFile and a Homestead.yaml configuration
file.

Configuring Homestead

By default, the vagrant box is set up at address 192.168.10.10 with the hostnamehomestead.app. You can change the IP address in Homestead.yaml if you want,
as well as the hostname (via the sites[].map key).

The Homestead.yaml configuration file contains all details about the
vagrant box configuration. The following is an example:

This configuration file is very simple and intuitive; for instance, the folders
to be used are reported in the folders section; the map value is the local
folder of the project, the to value is the folder on the virtual machine.

If you want to add or change more features in the virtual machine you can used
the Homestead.yaml configuration file. For instance, if you prefer to add
MariaDB instead of MySQL, you need to add the mariadb option:

SSH keys managed by GPG

One of our team uses the gpg-agent as an ssh-agent, which caused some
configuration problems initially, as the ~/.ssh/id_rsa and its .pub
sibling were not present.

When using gpg-agent for serving SSH keys, you can export the key usingssh-add -L. This may list several keys, but you should be able to find the
correct one. Copy it to the file ~/.ssh/gpg_key.pub, and then copy that file
to ~/.ssh/gpg_key.pub.pub. Update the Homestead.yaml file to reflect
these new files:

authorize: ~/.ssh/gpg_key.pub.pub
keys:
- ~/.ssh/gpg_key.pub

The gpg-agent will take care of sending the appropriate key from there.

Running Homestead

To run the vagrant box, execute the following within your project root:

$ vagrant up

If you open a browser to http://homestead.app you should now see your
application running.

Manually managing your hosts file

If you chose not to use vagrant-hostsupdater, you will need to update your
system hosts file.

On Linux and Mac, update the /etc/hosts file to add the following line:

192.168.10.10 homestead.app

On Windows, the host file is located in C:WindowsSystem32driversetchosts.

More information

We’ve tested this setup with each of the Zend Framework zend-mvc skeleton
application, Apigility, and Expressive, and found the setup "just worked"! We
feel it provides excellent flexibility in setting up development environments,
giving developers a wide range of tools and technologies to work with as they
develop applications.

If your collection does not fit one of these adapters, you can create a custom
adapter. To do so, you will need to implementZendPaginatorAdapterAdapterInterface, which defines two methods:

count() : int

getItems(int $offset, int $itemCountPerPage) : array

Each adapter need to return the total number of items in the collection,
implementing the count() method, and a portion (a page) of items starting
from $offset position with a size of $itemCountPerPage per page.

With these two methods we can use zend-paginator with any type of collection.

For instance, imagine we need to paginate a collection of blog posts and we
have a Posts class that manage all the posts. We can implement an adapter
like this:

In this example, we created a zend-paginator adapter using a custom Posts
class. This class stores the collection of posts using a protected array
($posts). This adapter is then passed to an instance of Paginator.

When creating a Paginator, we need to configure its behavior.
The first setting is the scrolling style. In the example above, we used theSliding
style, a Yahoo!-like scrolling style that positions the current page number as
close as possible to the center of the page range.

Note: the Sliding scrolling style is the default style used by zend-paginator. We need
to set it explicitly using Paginator::setDefaultScrollingStyle() only if we
do not use zend-servicemanager
as a plugin manager. Otherwise, the scrolling style is loaded by default from
the plugin manager.

The other two configuration values are the current page number and the number
of items per page. In the example above, we started from page 1, and we count 8
items per page.

We can then iterate on the $paginator object to retrieve the post of the
current page in the collection.

At the end, we can retrieve the information regarding the previous page, the
next page, the total items in the collection, and more. To get these values
we need to call the getPages() method. We will obtain an object like this:

Using this information, we can easily build an HTML footer to navigate across
the collection.

Note: using zend-view, we can consume the paginationControl
helper, which emits an HTML pagination bar.

In the next section, I’ll demonstrate using the Plates
template engine.

An example using Plates

Plates implements templates using native PHP; it is fast and easy to use,
without any additional meta language; it is just PHP.

In our example, we will create a Plates template to paginate a collection of
data using zend-paginator. We will use bootstrap as
the UI framework.

For purposes of this example, blog posts will be accessible via the following URL:

/blog[/page/{page:d+}]

where [/page/{page:d+}] represents the optional page number (using the regexpd+ to validate only digits). If we open the /blog URL we will get the
first page of the collection. To return the second page we need to connect to/blog/page/2, third page to /blog/page/3, and so on.

For instance, we can manage the page parameter using a PSR-7 middleware class
consuming the previous Posts adapter, that works as follow:

Summary

The zend-paginator component of Zend Framework is a powerful and easy to use
package that provides pagination of data. It can be used as standalone component
in many PHP projects using different frameworks and template engines. In this
article, I demonstrated how to use it in general purpose applications.
Moreover, I showed an example using Plates and Bootstrap, in a PSR-7 middleware
scenario.