Programster's Blog

Tutorials focusing on Linux, programming, and open-source

PHP Packages - Requiring Self For Testing

I like to be able to test code before I push it out onto the Internet. Some might say this is a good idea. Unfortunately, this has always been a hassle when developing PHP packages. I shall now explain why and the solution.

The Problem

Package Structure

When developing a package, I would always create it with the following structure:

|- README
|- composer.json
|- LICENSE.txt
|
|- src/
|    |- Class1.php
|    |- Class2.php
|
|- testing/
     |- composer.json
     |- main.php // entry point to kick off all tests
     |- tests/
        |- TestClass1.php
        |- TestClass2.php

All the package's code is within src/ with testing of that src/ code within a separate /testing folder. All the metadata, such as thel license and composer.json file, which describes the package, is on the top layer outside of src and testing.

Autoloading Package Source Files

For loading all of the classes within src/ for testing, I was forced to do one of three options:

  1. Manually include all the src/ files in the main.php script so that they were loaded (no autoloading).

    • This is tedious, especially as classes are added/removed or renamed in src/.

  2. Include the package as a requirement inside the composer.json file within testing, then keep pushing the package every time there was a change, and then perform a composer update.

    • This was a very slow cycle.

  3. Include the package as a requirement inside the composer.json file, then manually add a symlink of ../../../ for the package within the testing area's vendor directory.

    • This worked well but was manual and couldn't rely on others to do it.

Composer Paths

I looked online for what others are doing. It seems that a lot of people are testing their packages from outside the package with an external application. I don't like this approach as it means your testing folder can easily get out of sync, and less likely to get maintained.

I read about using relative paths [1][2] which looked promising, but it didn't work for me as I would always get error messages like so:

[RuntimeException]                                                                                                   
Package programster/my-package cannot install to 
"/home/stuart/Seafile/programster/programming/packages/package-my-package/testing/vendor/programster/my-package" 
inside its source at "/home/stuart/Seafile/programster/programming/packages/package-my-package" 

The Solution - Script Hooks

The solution came from weotch in a Gigthub issue thread. Composer supports script hooks, which I had completely forgotten about. This means we can just automate the third option I was doing before (replacing the package with a symlink).

{
    "require": {
        "programster/my-package": "@dev"
    },
    "scripts": {
        "post-install-cmd": [
            "rm -rf vendor/programster/my-package && ln -s ../../../ vendor/programster/my-package"
        ],
        "post-update-cmd": [
            "rm -rf vendor/programster/my-package && ln -s ../../../ vendor/programster/my-package"
        ]
    }
}

Remember to replace programster with whatever your vendor name is, and my-package with the name of your package.

Now you can work away on your src/ classes without having to push code and they will automatically load when you kick off your tests in your testing area.

References

Last updated: 28th February 2022
First published: 16th August 2018