PHP TestFest 2017

Prepare for PHP Test Fest 2009

It’s Wednesday 7/6/2017 and Ben Ramsey emails the UG-ADMIN mailinglist with the subject “PHP TestFest 2017”. I instantaneously think about the 2009 edition which I attended (at Combell, organized by PHPBelgium).

I had a great time during that edition (I even managed to get some test code into PHP source) and was wondering if we could join this years edition with a couple of local user groups.

I co-organize PHP-WVL and currently work for Combell (based in Ghent), so after some chatting with the folks of Ghent PHP, we agreed to join forces and organize this years edition back at Combell.

As the date we’d picked came closer, I had to figure out how to test the code and get them into PHP Source these days, so I could “mentor” the visitors. I still remembered how to write phpt files, but had to figure out the rest of the flow.

I started by listening to PHP Roundtable episode 65 in the car, to get up to speed. Next up: the PHP TestFest websiteΒ and the excellent video series of Sammy.

For specific questions, the mailing list is perfect, which helped me with some questions about code coverage.

I figured out there is a fork of the PHP source where all pull requests with tests should be created:Β https://github.com/phpcommunity/phptestfest-php-src/pulls.

Figuring out what to test.

To find code that needs testing, you have a couple of options:

When you look for uncovered code on the gcov site, there are some things you need to take into account:

  • It is possible that the gcov server does not have all PHP modules loaded (making it appear lots of code fragments are uncovered), see — SKIPIF —;
  • Some tests use “exec” or “popen”, making it “untrackable” for the coverage tool (the cli_server test appear to be uncovered for this reason);
  • Not all testfest tests have been merged.

To solve the last problem, I created a fork of the phptestfest fork, where I merged all the approved pull requests in a branch. This way, I can render code coverage for a specific path, including all the phptestfest tests from other users.

Our contribution.

After 2 weeks, we managed to submit 13 pull requests, where most were approved!

#phptestfest

Resources

Custom URL’s in Zend Framework

I am searching for a url solution in Zend Framework for quite a while now, and I hope the solution I came up with helps other people trying to do the same thing.

More important, I hope more experienced (Zend Framework) developers can point out problems or suggest better solutions for this problem.

The CMS of my company makes it possible for users to create a tree structure of products/sections/chapters/etc… with an unlimited depth. This makes the url they ‘generate’ very variable.

I’ve searched the internet for solutions and for quite a while I used the solution of Jani. Because this completely overrides the Router object, I continued my search via the Zend MVC mailing list and had an extensive look at the Zend_Controller_Router manual page.

During the search Ivo Jansch tweeted about ZF URL’s and pointed me towards chaining, but the solution for my specific problem lies in adding Routes to the route stack.

I wanted to override the default route so that a language is required in the url. To accomplisch this I added an new function in the Bootstrap.php file:

protected function _initLanguageRoute()
{
    $this->bootstrap('frontController');
    $fc = $this->frontController;
    $router = $fc->getRouter();
    $route = new Zend_Controller_Router_Route(
        ':language/:controller/:action/*',
        array(
            'module' => 'default',
            'language' => 'nl',
            'controller' => 'index',
            'action' => 'index'
        ),
        array(
            'language' => '[a-z]{2}'
        ),
        $translator
    );
    $router->addRoute('default', $route);
}

To add a variable route (calculated by the CMS), I extended the Zend_Controller_Router_Route_Abstract class so the route could also be used by the Url View helper.

The database I use below, has calculated colums already, but a custom mapping can be calculated in the class as well. As long as the request object is pointed to some controller and action.

class Coudenysj_Controller_Router_Route_Mapping extends Zend_Controller_Router_Route_Abstract
{

    private $_db;


    public function __construct($db)
    {
        $this->_db = $db;
    }

    /**
     * A method to publish the way the route operates.
     *
     * @see Zend/Controller/Router/Rewrite.php:392
     *
     * @return int
     */
    public function getVersion()
    {
        return 1;
    }

    /**
     * The required functions (required by Interface).
     *
     * @param Zend_Config $config The config object with defaults.
     *
     * @return void
     */
    public static function getInstance(Zend_Config $config)
    {
        return;
    }

    /**
     * Matches a user submitted path with a previously defined route.
     * Assigns and returns an array of defaults on a successful match.
     *
     * @param string $path Path used to match against this routing map
     *
     * @return array|false An array of assigned values or a false on a mismatch
     */
    public function match($path)
    {
        $path = trim($path, '/');

        return $this->_db->fetchRow(
            'SELECT lang, controller, action, id
            FROM mapping
            WHERE url = ?;',
            array($path)
        );
    }

    /**
     * Assembles a URL path defined by this route
     *
     * @param array   $data    An array of variable and value pairs used as parameters
     * @param boolean $reset   Not used (required by interface)
     * @param boolean $encode  Not used (required by interface)
     * @param boolean $partial Not used (required by interface)
     *
     * @return string|false Route path with user submitted parameters
     */
    public function assemble($data = array(), $reset = false, $encode = false, $partial = false)
    {
        if (!isset($data['language'])) {
            if (Zend_Registry::isRegistered('Zend_Locale')) {
                $locale = Zend_Registry::get('Zend_Locale');
                $data['language'] = $locale->getLanguage();
            } else {
                return false;
            }
        }
        if (   !isset($data['controller'])
            || !isset($data['action'])
            || !isset($data['id'])
        ) {
            return false;
        }

        $result = $this->_db->fetchRow(
            'SELECT url
            FROM mapping
            WHERE lang = ? AND controller = ? AND action = ? AND id = ?;',
            array(
                $data['language'],
                $data['controller'],
                $data['action'],
                $data['id'],
            )
        );
        return $result['url'];
    }
}

Now I can add a new ‘cms’ route to the Router object in the Bootstrap.php file:

protected function _initMappingRoute()
{
    $db = new Zend_Db::factory(...);
    $this->bootstrap('frontController');
    $fc = $this->frontController;
    $router = $fc->getRouter();
    $router->addRoute(
        'cms', new Coudenysj_Controller_Router_Route_Mapping($db)
    );
}

Remember that the route stack is processed backwards, so the ‘cms’ route is the first one to try and route the url.

This route can now also be used in the view:

echo $this->url(
    array(
        'id' => $id,
        'controller' => $controller,
        'action' => $action
    ),
    'cms' // the name of our route
);

This solution works for me (for now) and suggestions to improve it are very welcome!

phpBenelux

phpbenelux_logoAs you can see in my Blogroll panel, on my homepage, profile and LinkedIn, I’m a proud member of phpBenelux.

Tuesday there was another meeting, this time in the offices of Netlog, with talks from Ivo Jansch about “PHP and the Cloud” and Felix De Vliegher about “High gear PHP with Gearman”.

Because it took me quite a while to figure out what user group was/is the real one and what’s the plan for the future, I wanted to share my “knownledge” with you:

  • phpBenelux is the new name after phpBelgium and phpgg merged.
    I’m still waiting for Luxembourg, but they’ll come along :).
  • Meetings are held monthly, one month in the Netherlands, the other month in Belgium.
  • A new website (phpBenelux.eu) is on the way, and I’m offering my help, together with my colleague Jochen.

I will post updates on the list above, because there are still some questions that I haven’t figured out yet:

  • How the website is working.
  • What should have happened, after I payed € 15.
  • Why the “statutes” are still those of phpgg.

That being said: phpBenelux is a great user group, where I’ll offer my help to expand the knowledge of PHP into the benelux.

See you on the next meeting!