Deploying PHP applications with Phing

Posted by on Apr 9, 2013 in PHP Programming | 4 Comments

How many steps are required to deploy your software? Some people say it shouldn’t be more than one. I’m little bit more relaxed about it so I would say two steps are still fine. If it takes more than two then most likely you need a build script.

Why do you need a build script? Because your time is precious. You don’t want to waste it on repetitive boring tasks. You don’t want to waste other people’s time either. If you forget to tell your collegues that they have to do something after code update (like release a database patch) they might get confused. It’s also not safe. More things to do during a deployment means higher chance something will go wrong. Human is the weakest link in any system.

Every deployment process is unique to an application but there are few common areas:

  • Fetching latest code
  • Configuration
  • File system operations
  • Database update

There might be more areas but those are the most popular. Each of them can require multiple actions. All of that and much more can be handled though a build tool.

Phing is a PHP project build system or build tool based on ​Apache Ant. You can do anything with it that you could do with a traditional build system like GNU make. It use simple XML build files and extensible PHP “task” classes.

There are many ways to install Phing. My recommendation is to go for the composer. After all we want to limit number of deployment steps.

Create or edit composer configuration file.

Add phing to the “require” section.

Install Phing.

After Phing is installed you can access it directly from vendors directory.

If you don’t like the long path you can always create a symlink in “/usr/local/sbin” or any other preferred location.

The build script we are going to create will do three things: generate configuration, create logs file and release database changes. Each of those targets will have own build file. You can obviously keep everything in one script but it’s XML. Even short scripts can easily get unreadable.

First lets create the main build.xml file. It will bound all XML files together.

The script defines two targets: main and database-init. The default target defined in the “project” tag is “main”. If you run Phing without any parameters this is the target which is going to be executed.

The default target doesn’t do anything on it’s own. It calls three other targets: logs, configuration and database from external files. We will create those files in a second.

Build.xml does one very important thing. It loads properties.

A property file has similar syntax to an ini file. Defined values will be used to build the application. Every environment (for example: production, staging, workdev, homedev, etc) will most likely have own property file. Duplicating everything for a new environment is not the best approach. Imagine you create a new setting which will be the same for all environments. You will have to paste it to every property file. To avoid this scenario my proposition is to have a default file which is always loaded on startup. Settings unique to an environment should overwrite the default one.

Now if you deploy to production set “build.env” property accordingly.

This is simple but powerful strategy.

Lets create now missing XML files.

The easiest target is “logs“. It creates logs directory with application.log file. It also changes ownership of that file to the Apache user “www-data“. Chown requires root permissions. If you need this line you will have to run phing as root.

Next target is inside build-configuration.xml. It fills configuration template with settings from *.properties files and save to config directory.

The last build file releases database changes. Dbdeploy task reads deltas from “database/delta“. Each delta should begin with a number, for example: 1-create-new-table.sql, 2-update-something.sql and so on. Those numbers are compared with local changelog stored in the target database (we will create this table in a second). Dbdeploy creates one patch file which consists of all missing deltas. The file will be saved as “database/deploy-db-${DSTAMP}${TSTAMP}.sql“.

Once the file is created we use good old fashion mysql client to insert it into a database. Phing doesn’t have build-in task to call MySql but you can run any command with “exec” task.

In order to make the above code work you need the “changelog” table. You can create it manually but since we are in the build script business leave that with Phing.

That should create the table in your database. In case of any problems you can troubleshoot it with “-verbose” parameter.

You should get an error because the table already exists.

We almost done. The last step is to create example deltas.

You have probably noticed that both SQL files have @UNDO section. It’s not mandatory but some versions of Phing might not work without it. Code from @UNDO will go to “database/undo-db-${DSTAMP}${TSTAMP}.sql” file. It should allow to roll back database release in case of an error.

There is much more you can do with Phing. It has many tasks and extensions. Go though the manual to find out about all its features. XML is not the best language for programming but it does the job. So long you remember to keep it tight you shouldn’t have any problems.

4 Comments

  1. Petah
    12/04/2013

    Nice article. Just 1 comment on you database delta naming. You should use a timestamp instead of an incremental number, so when working in teams, your new deltas won’t conflict.

    Reply
    • Lukasz Kujawa
      12/04/2013

      Very good point! We keep having conflicts from time to time but I didn’t give any thought to it. That makes perfect sense, thank you for that Petah.

      Reply
  2. Damiano
    20/12/2013

    Thank you! I was looking forward to find a post like this!
    Is there anyway to get the SQL undo file automatically built by phing. If I remember correctly Weploy can do it

    Reply
  3. Dmitry Pashkevich
    25/03/2014

    You can tell Composer where to put binaries when fetching dependencies:

    “config”: {
    “vendor-dir”: “src/vendor”,
    “bin-dir”: “bin”
    }

    That way you will run phing via bin/phing from the project directory.

    Reply

Leave a Reply