The Law of Software Thermodynamics

I just found this quote, and I wanted to save it for reference:

There’s this little law of thermodynamics that says energy is neither created nor destroy[ed], it just changes form. This law almost holds true for complexity in software development – except that we can and do create complexity in our systems. And it seems that once we create the complexity, it never seems to get destroyed. It only gets moved around.

Source: Backbone.js: Getting The Model For A Clicked Element by Derick Bailey

PHPUnit and the WordPress file system API

If you unit test your WordPress projects with PHPUnit, sooner or later you’ll want to test some code that interacts with the filesystem. If you are interacting with the filesystem properly, you’ll be using the $wp_filesystem object.

A few months ago I found myself needing to test some code that created a file using the WordPress filesystem API. I searched for a good solution, and found the information about mocking the filesystem on the PHPUnit website. But after tinkering with vfsStream, I decided it would be easier to build a simple filesystem mocker myself than to integrate vfsStream with the WP Filesystem API. So I did.

The result is the WP Filesystem Mock.

[It] Provides a class that can be used as a mock filesystem, and also a shim for the WordPress filesystem API that uses it. This is useful in unit tests that include simple filesystem operations.

Enjoy!

Unserializing Safely

Avoid object injection with this:

	
    if ( 1 !== preg_match( '~O:\d~', $maybe_dangerous ) ) {
        // All is well, this is safe to unserialize.
    }

For security it is best if you use json_* or something else instead. But if you are working with a project built by someone else and you have no choice, this will keep you from unserializing any objects.

Creating Your Own PHPUnit @requires Annotations

PHPUnit offers a feature that lets you skip a test when certain requirements aren’t met. This can be done in two ways:

  1. You can manually check if the requirements are met, and then skip the test with $this->markTestSkipped() if they are not.
  2. In some cases, you can use the @requires annotation, and the test will be skipped automatically when the requirements aren’t met.

Using the @requires annotation is nicer, but PHPUnit only has so many options built in. Sometimes you have custom requirements that can’t really be checked reliably with any of the built-in options. An example is when you need some tests you’ve written for a WordPress plugin to run only when WordPress’s multisite feature is enabled on the test site. In my tests, I find myself needing this a lot. So I’ve been writing this over and over:

if ( ! is_multisite() ) {
     $this->markTestSkipped( 'Multisite must be enabled.' );
}

But just yesterday I realized that this was silly, and that I could easily add my own custom @requires annotation. So I did. Here is the code:

	protected function checkRequirements() {

		parent::checkRequirements();

		$annotations = $this->getAnnotations();

		foreach ( array( 'class', 'method' ) as $depth ) {

			if ( empty( $annotations[ $depth ]['requires'] ) ) {
				continue;
			}

			$requires = array_flip( $annotations[ $depth ]['requires'] );

			if ( isset( $requires['WordPress multisite'] ) && ! is_multisite() ) {
				$this->markTestSkipped( 'Multisite must be enabled.' );
			} elseif ( isset( $requires['WordPress !multisite'] ) && is_multisite() ) {
				$this->markTestSkipped( 'Multisite must not be enabled.' );
			}
		}
	}

You just need to add that method to your base test case class, and you will then be able to use @requires WordPress multisite instead of messing with markTestSkipped() all the time. For tests that should only run when multisite isn’t enabled, you can use @requires WordPress !multisite.

You could easily add more options for any other requirements your tests commonly have.

Fixing a Chronically Corrupt Git Index File

A while back I was working on a project where the website was under version control using git. Whenever I would try to run git status before checking out new changes, I would always get an error:

$ git status
error: bad index file sha1 signature 
fatal: index file corrupt 
fatal: 'git status --porcelain' failed

A quick internet search brought up what I thought was the solution:

$ rm -f .git/index
$ git reset

I say I thought this was the solution. But it wasn’t. I’d try to run git status after this, and the index file would still be corrupt. I struggled with this for a long time, until I finally realized what the problem was. It turned out that there were two folders in the git repo that contained .git directories—in other words, what we had was nested git repositories. This wasn’t intentional, and I had no idea that these .git directories were there. Once they were deleted, everything worked again.

TL;DR

Check to make sure that none of the non-submodule directories under your project have .git directories in them.