Patrick Schwisow

Month: October, 2015

Temporary Composer Changes During Development

Most Composer documentation and tutorials focus on how to use it when your dependencies are (relatively) stable and your “main” project is under development. This is certainly the most common use case, but you’re pretty much left to figure it out yourself if your workflow doesn’t fit these parameters. As a contributor to Phergie, I’ve worked in a very different flow, where the “main” project only contains composer.json and config.php (see Phergie documentation for users). The code for the bot, the underlying IRC client, and all the plugins comes from separate repositories and is installed by Composer. Doing development on these dependencies can be simplified with a few different Composer strategies.

Normal (Production) Configuration

Let’s start with the normal, traditional use of Composer as a baseline.

{
  "require": {
    "phergie/phergie-irc-bot-react": "~1",
    "phergie/phergie-irc-plugin-react-commandhelp": "~1",
    "phergie/phergie-irc-plugin-react-joinpart": "~1",
    "phergie/phergie-irc-plugin-react-pong": "~1",
    "pschwisow/phergie-irc-plugin-react-puppet": "~1"
  },
  "require-dev": {
    "phpunit/phpunit": "4.1.*",
    "phake/phake": "2.0.0-beta2"
  }
}

This is a very basic setup with a minimal set of plugins. Realistically, you’ll have many more plugins, but all the lines will look pretty much the same. Even when we employ different strategies for some dependencies, most of your dependencies will continue to be “normal” entries as shown above.

If we wanted to do work on pschwisow/phergie-irc-plugin-react-puppet, we could check out that repository (as a separate project) and commit changes on a branch. For example, to test a bug fix committed on fix-critical-bug branch, change that line to "pschwisow/phergie-irc-plugin-react-puppet": "dev-fix-critical-bug" and run composer update.

Limitations:

  • All packages must be submitted to Packagist.
  • All changes must be committed and pushed to GitHub before testing.
  • Branches to be tested must be on the same repository as registered in Packagist. (You cannot test changes in a fork of the official repository.)

Non-Packagist Repositories / Overriding Repositories

If the package you want install is not in Packagist (because it’s new) or you want to override the version in Packagist to use your own fork, you can specify the location of the package in composer.json. If we wanted to use a fork of phergie/phergie-irc-bot-react with changes from the standard version, your composer.json might look like:

{
  "require": {
    "phergie/phergie-irc-bot-react": "dev-my-branch as 1.3.0",
    "phergie/phergie-irc-plugin-react-commandhelp": "~1",
    "phergie/phergie-irc-plugin-react-joinpart": "~1",
    "phergie/phergie-irc-plugin-react-pong": "~1",
    "pschwisow/phergie-irc-plugin-react-puppet": "~1"
  },
  "require-dev": {
    "phpunit/phpunit": "4.1.*",
    "phake/phake": "2.0.0-beta2"
  },
  "repositories": [
    {
      "type": "vcs",
      "url":  "git@github.com:PSchwisow/phergie-irc-bot-react.git"
    }
  ]
}

Take note of the use of “as 1.3.0” in the requirements for the bot. This is an alias that tells Composer to treat the installed version of the package as 1.3.0 for the purposes of satisfying other dependencies. If (for example) phergie/phergie-irc-plugin-react-pong requires “~1” of the bot, Composer will be unable to resolve the dependency tree. (You cannot simultaneously install multiple version of the same package.) Aliases are often necessary to avoid these problems when you modify one part of a set of interdependent packages. I chose 1.3.0 specifically because it was the version that was installed prior to these changes. You can check composer.lock to determine the currently installed version of any dependency.

Problems Solved:

  • Packages no longer need to be submitted to Packagist.
  • Branches can reside in any repository you can reach.

Limitations:

  • All changes must be committed and pushed to GitHub before testing.
  • When overriding existing dependencies, you are responsible for ensuring the code loaded is compatible with other dependencies.

A Development Approach

The strategies shown so far require changes to be committed and pushed. In the case of new development or debugging efforts, this may not be desirable. If you want to avoid pushing incomplete code or temporary logging statements, you may want to run the latest local version of a dependency. While you always have the option to change files inside the vendor folder, this can get “messy” and has the disadvantage of not including dev dependencies on the package you are working on.

You can avoid these complications by keeping a separate copy of the package you are working on outside of your “main” project. For new development, this code need not even be under version control. (To clarify, I’d recommend you start using your VCS locally from the beginning of a project, but this is not strictly necessary and you certainly do not have to push to a remote repository at the earliest stages.)

As an example, let’s say we’re creating a new plugin “Foo” that resides in /home/pschwisow/code/foo. Rather than add it as a Composer dependency, we can add an autoload section so that Composer’s autoloader can load the classes from their current location.

{
  "require": {
    "phergie/phergie-irc-bot-react": "~1",
    "phergie/phergie-irc-plugin-react-commandhelp": "~1",
    "phergie/phergie-irc-plugin-react-joinpart": "~1",
    "phergie/phergie-irc-plugin-react-pong": "~1",
    "pschwisow/phergie-irc-plugin-react-puppet": "~1"
  },
  "require-dev": {
    "phpunit/phpunit": "4.1.*",
    "phake/phake": "2.0.0-beta2"
  },
  "autoload": {
    "psr-4": {
      "PSchwisow\\Phergie\\Plugin\\Foo\\": "/home/pschwisow/code/foo/src"
    }
  }
}

The same approach can be used for existing packages if you have a separate copy (outside the vendor directory) by just adding the autoload section as shown above. (Autoload directives in your “main” composer.json override what is declared for the same namespaces in the dependencies’ composer.json files.) Once the need for these temporary measure have passed, you should switch to one of the earlier strategies as a more permanent configuration.

Problems Solved:

  • Changes need not be committed / pushed to GitHub.

Limitations:

  • When overriding existing dependencies, you are responsible for ensuring the code loaded is compatible with other dependencies.

Consolidating Doctrine Migrations

Doctrine Migrations are a great way manage changes in your database structure, but over time migration files can pile up and turn into an unmaintainable mess. Migration usage is well-documented, but developers are left to their own devices when it comes to long-term maintenance. This article outlines a process to clean-up the mess and start fresh with a new base migration with minimal impact on existing database instances.

Note: This article was originally published in the March 2015 issue of php[architect].

Read the rest of this entry »