Zend Framework/Homestead Integration

Last year, we wrote about using Laravel Homestead with ZF projects.
Today, we contributed some changes to Homestead to simplify setting it up to
serve Apigility, Expressive, and Zend Framework projects.

As of version 7.6.0, Homestead users can now define sites with any of the
following "type" values:

  • apigility
  • expressive
  • zf

When one of these values is used, Homestead will setup the nginx instance used
by Homestead to properly to work with the project.

Getting started

Much of what we detailed last year is still true:

  • You will need to add the laravel/homestead box to Vagrant: vagrant box add laravel/homestead.

  • You will likely want to use the vagrant-hostsupdater
    plugin to Vagrant to facilitate mapping the VM IP address and server name to
    your system hosts file: vagrant plugin install vagrant-hostsupdater.

  • You will need to temporarily add the laravel/homestead package as a
    development dependency to your application: composer require --dev laravel/homestead.

  • You will need to use the Homestead tooling to create a Vagrantfile and
    Homestead.yaml configuration file: ./vendor/bin/homestead make.

Configuring Homestead

Once you have your Homestead.yaml file created, you can edit it. The two things
we need specifically are:

  • A folder mapping the application root directory to a directory in the vagrant
  • A site definition.

Generally, the folder mapping is already present, and will look something like
the following:

  - map: /home/username/dev/application
    to: /home/vagrant/code

If you want the Homestead.yaml to be portable, however, you can tell it to map
the current directory, and not a fully qualified path:

  - map: .
    to: /home/vagrant/code

Next, we’ll look at the site definition. After you first run homestead make,
you should have the following:

  - map: homestead.test
    to: /home/vagrant/code/public

Let’s change this a bit. First, we’ll give a new site name, then a site type
(I’ll use "expressive" here, but you can change this to "apigility" or "zf"
based on your application), and we’ll enable Z-Ray.

  - map: expressive.test
    to: /home/vagrant/code/public
    type: expressive
    zray: "true"

Yes, the correct value for the zray setting is "true"; see this issue for

From here, we can finally get running:

$ vagrant up

If you are not using the vagrant-hostsupdater plugin, you’ll need to add an
entry to your system hosts file: expressive.test


We’re hoping having this support in place will allow Zend Framework zend-mvc,
Apigility, and Expressive developers to create and share development
environments easily between their teams. If you have additional features you
would like enabled by default (e.g., auto-detection of ZF, Apigility, and
Expressive applications by homestead make, additional default nginx
configuration, etc.), be sure to swing by the
Slack or
forums and ask!

I want to extend a hearty thank you to Joe Ferguson
for helping me provide the integration, and guiding me through the
contribution process for Homestead.

Source: Zend feed

PHP 7.2 Support!

With Expressive 3 complete, we were able
to turn our sights on another important initiative: PHP 7.2 support across all
components and Apigilty modules.

The short story is: as of today, that initiative is complete! If you are using
the Zend Framework MVC framework, Expressive, or Apigility, or any of the ZF
components standalone, you should be able to perform a composer update to get
versions that support PHP 7.2.

The full story is much longer.

How we got there

The PHP project does a pretty stellar job of preserving backwards compatibility.
Some might say they do it to a fault, being averse to any changes that might
cause breakage for users, even if the change fixes bad behavior on the part of
the language.

Interestingly, there have been a ton of initiatives to tighten up the language
and have it behave more predictably. Unfortunately, we, and a number of projects
on which we depend, were bit by some of these efforts that went into PHP 7.1 and

One in particular was problematic.

Let’s say you have a class such as the following:

class SomeContainer
    public function get($name, array $options = null)

Next, we’ll have an extension to that class that overrides that method and
changes the default value:

class AContainerExtension extends SomeContainer
    public function get($name, array $options = [])

These should be fine, right? Wrong.

Starting in 7.1.0, the above emits an E_WARNING due to incompatible
signatures. This is because PHP 7.1 adds nullable types, and considers the
first signature equivalent to a nullable array.

The problem is that PHPUnit, on seeing an E_WARNING, creates an error status
for the test in which it is raised.

There were a number of other minor changes such as deprecated APIs that also
affected our code, often leading to unexpected test errors. Technically, the
code likely could run, but not without emitting deprecation notices and/or
warnings — and our goal is to run cleanly, so that users can see only the
warnings pertinent to their own application code.

On top of this, a number of PHPUnit classes exhibited similar behaviors, which
meant that under PHP 7.2, with the versions of PHP we were using, we could not
verify that our code could work under that version.

The upshot for us is that testing against 7.2 wasn’t as easy as just adding
another PHP version to the test matrix. We also had to find a set of different
PHP versions that we could test against (for instance, PHPUnit 6 and 7 require
PHP 7 versions, but we also still support PHP 5.6 in many of our components and
modules), figure out how to get Travis-CI to install a PHPUnit version
appropriate to the PHP version we were testing in, and ensure that the PHPUnit
features we were using worked across all PHPUnit versions against which we might

Thankfully, we had a secret weapon: Michał Bundyra (@MichalBundyra on twitter).
Michał spent a fair bit of time this past year developing increasingly effective
approaches to this sort of problem, and, once the 7.2 initiative was announced,
jumped in and created patches for almost every single component and module we

Seriously, this was work above and beyond anything I can reasonably expect of
a volunteer. Go thank him, already!

Our approach

The approach Michał developed involves two things. First, a range of known-good
PHPUnit dependencies, and, second, a set of configuration for Travis-CI that
will allow installing the appropriate dependencies.

First, we use the following constraints for PHPUnit in our composer.json files
when both PHP 5.6 and PHP 7 versions are required:

"phpunit/phpunit": "^5.7.21 || ^6.3 || ^7.1",

These allow us to use the 5.7 series for PHP 5.6, and either the 6.3 or 7.1
series when under other PHP versions.

We also commit our composer.lock file. I’ll show why in a moment.

Next, we use configuration similar to the following with Travis-CI:

sudo: false

language: php

    - $HOME/.composer/cache

    - COMPOSER_ARGS="--no-interaction"

    - php: 5.6
        - DEPS=lowest
    - php: 5.6
        - DEPS=locked
        - LEGACY_DEPS="phpunit/phpunit zendframework/zend-code"
    - php: 5.6
        - DEPS=latest
    - php: 7
        - DEPS=lowest
    - php: 7
        - DEPS=locked
        - CS_CHECK=true
        - LEGACY_DEPS="phpunit/phpunit-mock-objects phpspec/prophecy zendframework/zend-code"
    - php: 7
        - DEPS=latest
    - php: 7.1
        - DEPS=lowest
    - php: 7.1
        - DEPS=locked
        - TEST_COVERAGE=true
    - php: 7.1
        - DEPS=latest
    - php: 7.2
        - DEPS=lowest
    - php: 7.2
        - DEPS=locked
    - php: 7.2
        - DEPS=latest

  - if [[ $TEST_COVERAGE != 'true' ]]; then phpenv config-rm xdebug.ini || return 0 ; fi

  - travis_retry composer install $COMPOSER_ARGS --ignore-platform-reqs
  - if [[ $LEGACY_DEPS != '' ]]; then travis_retry composer update $COMPOSER_ARGS --with-dependencies $LEGACY_DEPS ; fi
  - if [[ $DEPS == 'latest' ]]; then travis_retry composer update $COMPOSER_ARGS ; fi
  - if [[ $DEPS == 'lowest' ]]; then travis_retry composer update --prefer-lowest --prefer-stable $COMPOSER_ARGS ; fi
  - stty cols 120 && composer show

  - vendor/bin/phpunit
  - if [[ $CS_CHECK == 'true' ]]; then vendor/bin/phpcs ; fi

Let’s break that down.

We set up a few things up front for all builds: we’re using dockerized php
jobs (sudo: false, language: php), we’re caching composer metadata between
builds (which greatly speeds up the installation process!), and defining our
default composer arguments.

From there, we define our test matrix. Each job in the matrix includes:

  • The PHP version we are testing against.
  • Environment variables for that specific build.

You’ll notice we have three jobs for each PHP version, corresponding to the
following environment variables:

  • DEPS=lowest
  • DEPS=locked
  • DEPS=latest

These variables are indicating how we want to install dependencies:

  • locked indicates we want to use those specified in the composer.lock file.
  • lowest means we want to test against the lowest stable dependencies we allow.
  • latest indicates we want to test against the latest available dependences we

This approach allows us to determine:

  • When we start using features from a library that are not present in the
    earliest version we have indicated we support. If the lowest tests fail, we
    likely either need to change what part of a third-party API we are consuming,
    or bump the minimum supported version of that dependency.

  • When a library has introduced a BC break in a more recent release than we
    tested against previously. In such cases, we can try and find a way to make
    our own usage of that library forwards-compatible with the new version; create
    an issue notifying the developer(s) of that library of the BC break; or change
    our dependencies to not allow the newer version.

Additionally, some jobs have more variables they define:

  • CS_CHECK will tell the job whether or not to run CS checks. (We also often
    define an env variable for running coverage reports.)

  • LEGACY_DEPS allows us to specify dependencies we need to update after
    initial installation. More on that in the coming paragraphs.

We also disable xdebug unless we’re running coverage reports. This speeds up
Composer operations as well as running unit tests. I have the before_install
script detailed above, but do not define any environments with the
TEST_COVERAGE variable set, nor demonstrate how we use it to run reports.

When we hit the install section is when the "magic" happens. The first thing
we do is an install from the lockfile. When we do so, we pass the
--ignore-platform-reqs option, as we cannot guarantee that the dependencies in
the lockfile will work for the current PHP version being used.

We then check to see if LEGACY_DEPS is non-empty. If so, we do a composer update --with-dependencies, passing the value of LEGACY_DEPS as the packages
to update. This allows us to use the lockfile on locked versions, but then get
platform-specific dependencies for libraries where we know that what’s in the
lockfile may not work on all platforms

Next, we check for DEPS=latest, running composer update, and DEPS=lowest,
running composer update --prefer-lowest --prefer-stable.

Finally, we display what dependencies were installed, along with their versions.
We use the construct stty cols 120 to set the display columns, as otherwise
composer will not detect a TTY, and spit out only the dependencies, with no
associated version.

The beauty of this approach is that we are able to use it almost verbatim across
our repositories, with only minor changes to which LEGACY_DEPS we need, and
which versions need them. Having multiple tests per version, spanning a range of
dependencies, has allowed us to identify and solve problems arising from
libraries we consume quickly.

This approach allowed us to run tests under PHP 7.2, fix any issues identified,
and finally release new versions that fully support PHP 7.2.

What’s next?

We have a number of initiatives we’re working on in the coming months:

  • Frank Brückner is working on a site
    refresh to both make the documentation and main sites more consistent in
    look-and-feel, as well as better support mobile browsers.

  • We continue to work on the Apigility on Expressive initiative. While many
    features were released with stable versions for Expressive 3, there’s still work
    to be done, including tooling support.

  • Aleksei Khudiakov has been working on a set of proposals
    for a PSR-7-based zend-mvc v4.

  • We want to work on tutorials and guides to help users make the most of
    Expressive, as well as migrate to Expressive from zend-mvc.

If you want to help out with any of these initiatives:

Source: Zend feed

phpList Upgrade

If you are upgrading from a previous version, then read our manual chapter on upgrading. The general procedure is as follows:

Backup your database
Copy your config.php file in a safe place
Download the latest version of phpList
Overwrite all of the code of the current version with the new one. You can upload the “lists” directory and all it contains
Copy your config.php back
Login to the phpList admin pages and click the “upgrade” link
Just to be sure, use the “Verify database structure” page to check that your database structure is correct

You may also wish to check the latest config.php and re-edit it to match your system.

phpList 3.3.2 released: New statistics, plugins, and fixes

phpList 3.3.2 is a new release with 2 new features, 4 plugins, 20 fixes, and 339 code commits.


  1. Fixed ‘unidentified bounces older than two months’ removal – Thanks to Duncan ,See the Pull Request
  2. Fixed orphaned database records following use of the deleteuser() function in file inc/userlib.php: records included in user_message_forward and linktrack_uml_click tables are now removed cleanly – Thanks to Duncan, See the Pull Request
  3. Fixed missing duplication validation when modifying a subscribers email, now checking if an email address has been entered and does not already exist when changing the email address – Thanks to Duncan, See the Pull Request
  4. Fixed phpList displaying without a theme when the function parse_ini_file() has been disabled. Now when that happens phpList will use the first theme that is found – Thanks to Duncan. See the Pull Request
  5. Fixed display of the selected theme’s directory instead of its name – Thanks to Duncan. See the Pull Request
  6. Fixed repeat message processing trying to copy an index, that caused a repeated campaign to eventually not to be sent because ‘finishsending’ date is in the past – Thanks to Duncan, See the Pull Request
  7. Fixed miscalculation of percentages on Campaign Statistics page
  8. Fixed input fields scale on campaign composer page on small screens
  9. Fixed lists not showing by default on ‘Send a campaign’ page in mobile view
  10. Fixed “Select the lists you want to exclude from this campaign” option only listing the category of lists on Trevelin
  11. Fixed the progress bar remaining visible after the processing of the message queue has finished
  12. Fixed rendering problems of the login page on Safari for Mac
  13. Replaced deprecated each() function in php 7.2 with the equivalent foreach() construct – Thanks to Duncan, See the Pull Request
  14. Fixed menu items of plugins being visible even though the plugin has not yet been enabled – Thanks to Duncan, See the Pull Request
  15. Fixed creation of unnecessary temporary file when exporting subscribers – Thanks to Duncan, See the Pull Request
  16. Fixed embedding of external images when running phpList from command line – Thanks to Duncan. See the Pull Request
  17. Several sets of changes to “Domain Statistics” page to make the results more consistent – Thanks to Duncan, See the Pull Request
  18. Changed default password hashing algorithm (some remaining instances from 3.3.1)
  19. Change the “From Field” on view campaign to print the full sender address, including email address so it is possible to determine on sent campaigns what address it was sent from
  20. Changed the info on “View a Campaign” page to print Campaign subject and Campaign alias, if the campaign has an alias set, for user reference.
  21. Change ‘View a campaign’ page title to use campaign title (not subject).