Patrick Schwisow

Category: PHP

Code Standards: Why and What?

This is the first part in a multi-part series on code standards. I’ll start by laying out why we need them and what things actually comprise a code standard. Examples are in PHP, but most concepts are language-agnostic. Future installments will go into detail about different aspects of code standards and how to define them and use them to improve your team’s code. Read the rest of this entry »

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 »

Response to “On Visibility in OOP”

I just read Matthew Weier O’Phinney’s On Visibility in OOP.  Aside from being an excellent and easy to follow explanation of public / protected / private / final in PHP, MWOP’s experiences dealing with private and final in framework and library classes somewhat echo my own.

In the example he presented, he was unable to extend and improve Doctrine\Common\Annotations\DocParser due to the fact that it has private members and is marked as final. This proved a significant roadblock to interoperability between Zend Framework 2 and Doctrine 2.

In my case, I also encountered a roadblock in Doctrine 2, but for me it was a block to testability. Doctrine\ORM\EntityManager is marked as final, and while I understand the reasoning behind the statement “this should not be extended”, I would propose that in the case of creating tests, this reasoning no longer applies. To allow the flexibility necessary to create effective tests, it is frequently necessary to extend a class in order to create a mock or proxy. If the class in question is final or makes most of its inner workings private, we must resort to overly complex and less realistic methods to create tests for code that touches it.  This in turn decreases the chances that our tests are actually covering the cases we intend them to cover and increases the chances of false positives and false negatives from the test suite. In short, we’re forced to choose between “hacking” someone else’s code to change visibility or creating very brittle tests.

Interview on Voices of the ElePHPant

When I was at php|tek 12 a few weeks ago, I had the pleasure of recording an episode of the Voices of the ElePHPant Podcast with The Cal Evans. The episode published today, so I guess I’m now #internetfamous. In all honesty, I was pretty nervous about it, so I’ll be listening along with everyone else to hear what I said. I know I talked about PHP User Groups, Community, and Conferences.

One thing I intended to do was thank all the wonderful sponsors who helped Lake / Kenosha PHP get off the ground with the generous donations of “swag” for the new members.  I’ll take the time to thank them now:

Thank you all for your generous support of of the community, and thank you also to the other companies who generously provided the next round of giveaways for LKPUG and many other user groups at php|tek this year.

Post php|tek Resolution

This year’s php|tek was my second, and it was a great experience. (More about that in later posts.) After tek11, my steps into the community were joining twitter (a small step) and founding a local PHP User Group (a much bigger step). This year’s plan:

And here is the blog in question! I have now fulfilled the first half of the resolution; now to write something other than “random crap”. Well, maybe next time…