Modern PHP

Not many useful PHP books have been published recently (or I haven’t yet discovered them), so I really want to recommend “Modern PHP” by Josh Lockart, another quality product by O’Reilly.

Having only 270 pages, it’s easy and quick to read, and includes a lot of best practices, tips and state-of-the-art tools for writing cool PHP applications. Some topics are covered a bit briefly like e.g. Testing, but the book points you towards the right direction and gives you useful references to read further, if you are interested.

The experienced PHP developer should actually be familiar with most of the topics, so you would probably not learn too much new stuff. Still it gives the good feeling of confirming that you are somewhat up-to-date 🙂

I’m not payed for this at all, however I think that this book is really valuable for the PHP community and therefor I’d like to promote it.

Debug your Codeception tests with Xdebug and PhpStorm

I use Xdebug not only to hunt bugs, but also for every day development work. Of course I also want to use it when I write my Tests.

I one of my current projects we use Codeception for both Unit- and Acceptance tests. Debugging Codeception Unit tests is as easy as for phpUnit, you just have to make sure that the XDEBUG_SESSION is set as described in my post about CLI debugging with Xdebug.

What to test

The Acceptance tests I want to debug are basically just testing a REST API, so it’s usually firing HTTP GET/POST requests against various endpoints:

class ApiCest
{
    public function testDoStuff(ApiTester $I)
    {
        $I->sendPOST('/my/api/dostuff');

        // testing starts here
        $I->seeResponseCodeIs(200);
    }
}

In order to debug the actual “dostuff” endpoint, I have to take care of two things: the debug session Cookie and the PhpStorm debug configuration.

Set the XDEBUG_SESSION cookie

The usual way for me to start a PhpStorm session for non-CLI scripts is to enable the “Start Listening for PHP Debug Connections” in PhpStorm and initiate a request with the XDEBUG_SESSION cookie set.

I’m assuming you have PhpStorm and Xdebug set up neat and nicely, as there are enough tutorials about it out there.

This works nicely when I start a debug session using the browser, but now we don’t use a browser and send the requests via HTTP directly (Codeception uses Guzzle for that), so we have to set the Cookie somehow. There are (at least) three ways to do that with Codeception:

  1. Set the cookie manually in the Cest (Codeception acceptance test class)
    $I->setCookie('XDEBUG_SESSION', 'PHPSTORM');
    
  2. Configure Guzzle to send the Cookie via the codeception.yml

    This is described Codeception manual on the PhpBrowser module

  3. Use the codeception/remote-debug extension

    Codeception-Remotedebug on Github

    This is my preferred way, it’s actually pretty easy to set up, and this way I just send the Cookie all the time.

    extensions:
      enabled:
        - Codeception\Extension\RemoteDebug
        config:
          Codeception\Extension\RemoteDebug:
            sessionName: PHPSTORM
    
    Since the Cookie value (PHPSTORM) might be different for other developers, these settings should not be under Version control. Codeception allows several ways to achieve this, in our project we use a environment-specific setup, which is not under version control and allows use to extend or overwrite settings on the project-wide, version controlled codeception.yml. Learn more about it in the Codeception manual on Environments.

    Increase PhpStorms Max. connections

    This might cause some headache if you forget about it. When I tried to all of the above ways to set the Cookie, the debug session was always getting stuck, i.e. PhpStorm was not stopping at the breakpoints and rather did – nothing!

    phpstorm_debug_max_connections

    By default, PhpStorm is configured to handle 1 debug session at a time. However in my case, I need to handle 2 debug sessions in parallel now – one debug session for the actual Acceptance test, and one for the API.

    And that’s it. I hope this will be useful for other Codeception users as well!

Filesystem abstraction in PHP, and why it sometimes is useful!

Phase 1: Ignorance and Bias

Recently I came across Flysystem, which is a Filesystem abstraction layer for PHP. Now if you haven’t worked with a Filesystem abstraction layer before (like me), you may ask yourself:

Why on earth should I add even more overhead for something basic as the file system?

Let me explain why it will be extremely useful. Say we have to build a small app in PHP which should download files from an FTP server, process them in some way, and store the result on the servers filesystem. No doubt, this can be done with PHPs build-in functions without a problem, you might say. Sure, but don’t forget the Unit Tes… dang! See?

Phase 2: Skepticism and Curiosity

The solution for our problem once more is: Abstraction. Abstract the process from any file system. Just use the same set of operations on some sort of adapter, no matter where or how the files are stored, and let the Abstraction Layer do the rest. You guessed it already, this is where Flysystem comes into play! Let’s start with a simple file download:

<?php

namespace My\Cool\Project;

use League\Flysystem\FilesystemInterface;

class Downloader
{
    /**
     * @var FilesystemInterface
     */
    protected $remoteFileSystem;

    /**
     * @var FilesystemInterface
     */
    protected $localFileSystem;

    /**
     * @param FilesystemInterface $remoteFileSystem
     * @param FilesystemInterface $localFileSystem
     */
    public function __construct(
        FilesystemInterface $remoteFileSystem,
        FilesystemInterface $localFileSystem
    ) {
        $this->remoteFileSystem = $remoteFileSystem;
        $this->localFileSystem = $localFileSystem;
    }

    /**
     * Download files from the remote file system
     * @param string $fileName The filename
     * @return bool
     */
    public function download($fileName)
    {
        $fileDownloaded = false;
        
        // This is the actual download part!
        if (!$this->localFileSystem->has($fileName)) {
            $fileDownloaded = $this->localFileSystem->writeStream(
                $fileName,
                $this->remoteFileSystem->readStream($fileName)
            );
        }
        // Done!

        return $fileDownloaded;
    }
}

Is that it?? Aww yeah, that’s it. When it’s with Flysystem, we only need a few lines of code, because it’s so intense!

To make the code it more clear, I called the filesystems $localFileSystem and $remoteFileSystem, but of course both filesystems could be remote ones, e.g. FTP and Amazon S3. This means that by using Flysystem, you completely decouple your code from the filesystem, so you’re prepared when the Business suddenly decides to “implement some totally easy changes”.

Of course our example is not 100%ly done, since we still have to create the Filesystem adapters we pass upon instantiation. But that’s ok, we need to do the configuration somewhere anyway. Below function is part of another class, which makes use of our Downloader class:

public function downloadSomeFile()
{
    $ftpAdapter = new \League\Flysystem\Adapter\Ftp([
        'host' => 'ftp.server.com',
        'username' => '123',
        'password' => 'abc'
    ]);
    $remoteFileSystem = new \League\Flysystem\Filesystem($ftpAdapter);

    $localAdapter = new \League\Flysystem\Adapter\Local('/tmp');
    $localFileSystem = new \League\Flysystem\Filesystem($localAdapter);

    $downloader = new \My\Cool\Project\Downloader(
        $remoteFileSystem, 
        $localFileSystem
    );
    
    $downloader->download('somefile.txt');
}

This is what I also like about using Flysystem: within the Downloader class we don’t have to deal with any path settings or such. It is completely done within the code which instantiates the Downloader, i.e. in the domain where it belongs.

it's so decoupled

Phase 3: Awesomeness and Why-didn’t-I-use-it-before-ness

I assume most of you didn’t even read until here, since you’ve been totally blown away and immediately started refactoring your code, giggling slightly insane. But wait, we’re not even fully through yet. Remember the question about Unit Tests? With Flysystem, it’s a breeze:

public function testCanDownloadFileFromRemoteFileSystem()
{
    // This mocks the remote server (could e.g. be an FTP) where we want to 
    // download the file from
    $remoteAdapter = new \League\Flysystem\Vfs\VfsAdapter(new Vfs);
    $remoteFileSystem = new \League\Flysystem\Filesystem($remoteAdapter);

    // Put a file on the virtual remote filesystem
    $remoteFileSystem->put('somefile.txt', 'just some content, does not matter');

    // Virtualize our local file system also, so no need to clean up or anything, 
    // since by using vfsStream, we don't even have to take care where to store 
    // the files during our tests.
    $localAdapter = new \League\Flysystem\Vfs\VfsAdapter(new Vfs);
    $localFileSystem = new \League\Flysystem\Filesystem($localAdapter);

    $downloader = new \My\Cool\Project\Downloader(
        $remoteFileSystem, 
        $localFileSystem
    );

    $this->assertTrue($downloader->download('somefile.txt'));
    $this->assertTrue($localFileSystem->has('somefile.txt'));
}

Flysystem even provides an adapter for vfsStream, a stream wrapper for PHP which helps greatly when you have to test filesystem-related functionality.

In simple terms, it simulates a file system in memory, which many (but not all) PHP functions can use.

Check it out here if you don’t know it yet: vfsStream on Github

So here we are: we just wrote a test for the FTP download in 5 minutes – without having to change a single line in the Downloader, without relying on a real FTP server. We could exchange the remote filesystem with Dropbox or even your Grandma’s closet within two minutes. Awesome. Now get a coffee and head over to your co-worker, who’s still struggling with the Downloader class and PHPs lovely native FTP support.

Traits and Extbase

With Extbase, accessing the extensions settings outside Controllers (e.g. in a Repository) requires some manual work which is always the same. I don’t know why Extbase doesn’t support us with helper functions, but anyway, let’s put this needed code where we can reuse it easily.

Being limited to PHP 5.3 until last month, my usual approach was to inherit the Repository from an Abstract class which contained a set of often used functions. This still works fine, but if you want these functions to be used in let’s say a Typo3 hook class, or a Scheduler task (where the Extbase support is even worse! Geez!), you probably end up writing a couple of Abstract classes which are more or less the same. Of course you can inherit the Abstract class from another abstract class … which again doesn’t work with Scheduler tasks, because they need to inherit from a base class anyways… nah…

Since we now use PHP 5.4 on our production servers (and yes, I am aware that 2014 is almost over), I felt urged to find out whether Traits can be used with Extbase. Surprisingly (working with Extbase and Typo3 is easier with a good amount of sarcasm) it was as easy as it should be! I tested it on Extbase 6.2, and the Autoloader loads the Trait without any hassle.

Here’s some example. First, the trait itself:

<?php

namespace Vendor\Myextension\Library;

trait Settings
{
    /**
     * The extensions name
     * @var string
     */
    protected $extensionName;

    /**
     * Typo3 Settings array
     * @var array
     */
    protected $settings;

    /**
     * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
     */
    protected $configurationManager;

    /**
     * @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
     * @inject
     */
    public function injectConfigurationManager(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager) {
        $this->configurationManager = $configurationManager;
    }

    /**
     * Getter for $extensionName
     * @return string
     */
    public function getExtensionName() {
        if ($this->extensionName === NULL) {
            $className = get_class($this);
            if (strpos($className, '\\') !== FALSE) {
                $classNameParts = explode('\\', $className, 4);
                // Skip vendor and product name for core classes
                if (strpos($className, 'TYPO3\\CMS\\') === 0) {
                    $this->extensionName = $classNameParts[2];
                } else {
                    $this->extensionName = $classNameParts[1];
                }
            } else {
                list(, $this->extensionName) = explode('_', $className);
            }
        }

        return $this->extensionName;
    }

    /**
     * Setter for $extensionName
     * @param string $extensionName
     * @return Settings
     */
    public function setExtensionName($extensionName) {
        $this->extensionName = $extensionName;
        return $this;
    }

    /**
     * Setter for $settings
     * @param array $settings
     * @return $this
     */
    public function setSettings($settings) {
        $this->settings = $settings;

        return $this;
    }

    /**
     * Getter for $settings
     * The settings are loaded from the configurationManager on demand only.
     * If this is called outside Extbase (i.e. from a system hook, or a scheduler task) you must use
     * \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT
     * and fetch the extensions settings via the array structure, e.g.
     * $this->settings['plugin.']['yourextension.']['persistence.']['storagePid']
     * @param string $settingsType
     * @return array
     */
    public function getSettings($settingsType = \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS) {
        if ($this->settings === NULL) {
            $this->settings = $this->configurationManager->getConfiguration(
                $settingsType, $this->getExtensionName()
            );
        }

        return $this->settings;
    }
}

No rocket science, but stuff you need all over again. I decided to use “Library” as namespace, but this doesn’t really matter.

So far, so easy. To include a trait, you only need to add the “use”-statement inside your class, e.g. in a repository:

<?php

namespace Vendor\Myextension\Domain\Repository;

class SomeSeriousStuff
{
    // Note the "use" statement within the class. This inserts our Trait class.
    use \Vendor\Myextension\Library\Settings;

    public function mySeriousFunction() {
        $settings = $this->getSettings();
        // ... do some serious things here with the settings
    }
}

This works really smooth, I think I will get used to Traits very soon.

Fun fact: shortly after finishing my Extension where I used Traits for the first time, I was asked for a PHP 5.3 compatible version. Fuuuuuu……

Getting the subclass name in the parent class

A mistake that I keep making – I have a class that extends another class, and I need to know the class name of that subclass. However the function where I need the name is in the parent class. In this case the magic constant __CLASS__ won’t work, as it will return the name of the class where it is found. get_class() won’t work, too. Not without an important difference:

abstract class Foo
{
   public function getClassName()
   {
      return __CLASS__;
     // or return get_class();
   }

   public function getSubClassName()
   {
      return get_class($this); // the reference $this is important here
   }
}

class Bar extends Foo {};

$bar = new Bar();

echo '1. ' . $bar->getClassName();
echo '<br>';
echo '2. ' . $bar->getSubClassName();

This will result in

1. Foo
2. Bar

The first function call returns the (in my case) incorrect name of the parent class, while the 2nd function works as I need it.

Updating XAMPP without a headache

After some years the time has come to update my local WAMP stack to a more recent PHP version. I also made good experiences with WAMP (the other cool stack for Windows), but I’ll stick with XAMPP and install 1.8.2. This will also make things easier.

  1. Rename the old xampp installation directory C:\xampp (or where you installed it) to C:\_xampp
  2. Install new XAMPP under C:\xampp
  3. Copy all (needed) files/projects from C:\_xampp\htdocs to C:\xampp\htdocs
  4. Copy all (needed) databases from C:\_xampp\mysql\data to C:\xampp\mysql\data. EDIT: Don’t do that if your databases contain InnoDB tables. Use mysqldump instead.
  5. Check differences between both php.ini files, located in C:\xampp\php
  6. If you made changes on your MySQL configuration, don’t forget to check C:\xampp\mysql\bin\my.ini as well
  7. check your PHP Extensions in php/ext: you will need new versions of any additional PHP extensions (e.g. libpdf). Keep in mind that PHP 5.4 has been compiled using VC9 instead VC6 as in PHP 5.3 when chasing for downloads!
  8. Take over changes in C:\xampp\apache\conf\httpd.conf – most likely you’ll have some VirtualHost configurations
  9. Run the security check in XAMPP (http://localhost/security/index.php) to set the MySQL password to the same password you used in the old XAMPP installation (if you’re using a password)
  10. Be happy! You now shouldn’t need to change any path configurations, passwords etc. to run your web applications on your new PHP 5.4 stack!

The actual migration from PHP 5.3 to 5.4 shouldn’t be a problem, as there aren’t that many compatibility issues. I didn’t experience any PHP issues so far.

I was rather concerned about just copying the MySQL files – but it worked like a charm and is definitely quicker than doing mysqldumps and loads on many big databases – UNLESS you don’t have InnoDB tables in your databases. In this case better use mysqldump. In general it’s a good idea to keep an eye on the mysql_error.log in the data directory.

Good thing about this approach: you still have your old XAMPP installation at hand which you can reuse by simply renaming the two directories. Always be backed up!

Create your own PHP extension (on Windows)

Have you ever thought about writing your own PHP extension? There are surely a few pro’s and con’s about creating PHP extensions to solve problems, rather than write PHP libraries. But this is not the question here. I was curious. So come on Internet, let’s see what you got for me…

http://www.dreamincode.net/forums/topic/244215-introduction-to-creating-a-php-extension-for-windows/

http://blog.slickedit.com/2007/09/creating-a-php-5-extension-with-visual-c-2005/comment-page-2/

Looks a bit outdated, but that’s not the problem here for me. As a matter of fact, I’m currently still using PHP 5.3.5 on my dev for a list of reasons. And I’m using Windows and I don’t complain (I know my colleagues think I’m weird). Luckily both tutorials cover the topic on how to create the extension on Windows for PHP 5.3.5. So much win! And the best: it really works!

Basically both tutorials provide you with everything you need. I had to get over two pitfalls though:

  1. php5ts.lib is not in the source code directory I just extracted, but in my WAMP stack already – in my case I located it in C:\xampp\php\dev
  2. error C2371: ‘socklen_t’ : redefinition; different basic types – I really have no idea why this error came up during compilation. However I was able to “solve” *cough* the problem by commenting out lines 149 to 151 in the php.h:
    #ifndef HAVE_SOCKLEN_T
    typedef unsigned int socklen_t;
    #endif
    

    Surely not the ideal solution, but as I just wanted to try it out I don’t mind at the moment.