Building and Deploying PHP apps with Phing

78
Building and deploying PHP apps with > < Michiel Rook PHP Johannesburg April 2014

description

Slides of the talk that I gave during PHP Johannesburg 2014 https://joind.in/talk/view/10411 Manually creating builds and running deployments can be scary, tedious, error-prone, boring, stressful (check all that apply). What you need is a tool that helps automate the necessary steps to build, test, package and deploy your app. During this talk you will be introduced to the workings of Phing, it's rich set of out-of-the-box tasks and easy extensibility. Step by step, you will learn how to write a comprehensive deployment script. A number of demonstrations will cover testing, packaging, database migration, continuous integration, multi-server deployments and other real-world use cases.

Transcript of Building and Deploying PHP apps with Phing

Page 1: Building and Deploying PHP apps with Phing

Building and deploying PHP apps with

>< ><

Michiel Rook

PHP Johannesburg April 2014

Page 2: Building and Deploying PHP apps with Phing
Page 3: Building and Deploying PHP apps with Phing
Page 4: Building and Deploying PHP apps with Phing

About me

• Freelance PHP & Java contractor / consultant

• PHP since '99

• Phing project lead

• Dutch Web Alliance

• http://www.linkedin.com/in/michieltcs

• @michieltcs

Page 5: Building and Deploying PHP apps with Phing

This talk

• Why a build tool

• What is Phing

• Usage

• Various examples

• Deployments

• Extending Phing

Page 6: Building and Deploying PHP apps with Phing

Why a build tool?

Page 7: Building and Deploying PHP apps with Phing

This is PHP!?

Page 8: Building and Deploying PHP apps with Phing

Repetitive tasks

• Version control

• Database changes

• Testing

• Minifying

• Packaging

• Uploading

• Deploying

• Configuring

Page 9: Building and Deploying PHP apps with Phing

Good programmers are lazy

Page 10: Building and Deploying PHP apps with Phing

Good programmers automate repeatablethings

Page 11: Building and Deploying PHP apps with Phing

Automate!

• Easier handover

• Improves quality

• Reduces errors

• Saves time

• Consolidate scripts, reduce technical debt

Page 12: Building and Deploying PHP apps with Phing
Page 13: Building and Deploying PHP apps with Phing

What is Phing?

Page 14: Building and Deploying PHP apps with Phing

Phing is AWESOME

Page 15: Building and Deploying PHP apps with Phing

What is Phing?

• “PHing Is Not GNU make; it's a PHP project build systemor build tool based on Apache Ant.”

• XML build files

• Mostly cross-platform

• Integrates various popular (PHP) tools

• Lots. Of. Tasks.

Page 16: Building and Deploying PHP apps with Phing
Page 17: Building and Deploying PHP apps with Phing

"Phing is good glue"

Page 18: Building and Deploying PHP apps with Phing

The basics

Page 19: Building and Deploying PHP apps with Phing

Installing Phing

• PEAR installation

$ pear channel-discover pear.phing.info$ pear install [--alldeps] phing/phing

• Optionally, install the documentation package

$ pear install phing/phingdocs• Composer

• Phar package

Page 20: Building and Deploying PHP apps with Phing

Build file

• XML• Contains standard elements

• Task: performs a specific function (copy, git commit, etc.)• Target: collection of tasks, can optionally depend on othertargets

• Project: root node, contains multiple targets

Page 21: Building and Deploying PHP apps with Phing

Example build file

<project name="Example" default="world"><echo>Hi!</echo>

<target name="hello"><echo>Hello</echo>

</target>

<target name="world" depends="hello"><echo>World!</echo>

</target></project>

Page 22: Building and Deploying PHP apps with Phing

Example build file

$ phing [-f build.xml]Buildfile: /home/michiel/phing/build.xml

[echo] Hi!

Example > hello:

[echo] Hello

Example > world:

[echo] World!

BUILD FINISHED

Page 23: Building and Deploying PHP apps with Phing

Properties

• Simple key-value pairs

• ${attribute}

• Replaced by actual value

Page 24: Building and Deploying PHP apps with Phing

Properties

<project name="Example" default="default"><target name="default">

<property file="build.properties" /><property name="foo" value="bar" />

<echo>${foo}</echo></target>

</project>

Page 25: Building and Deploying PHP apps with Phing

Properties

$ phingBuildfile: /home/michiel/phing/build.xml

Example > default:

[echo] bar

BUILD FINISHED

Page 26: Building and Deploying PHP apps with Phing

Fileset

• Denotes a group of files

• Include or exclude files based on patterns

• References: define once, use many

Page 27: Building and Deploying PHP apps with Phing

Fileset

<copy todir="build"><fileset dir="./application">

<include name="**/*.php" /><exclude name="**/*Test.php" />

</fileset></copy>

<fileset dir="./application" includes="**"/>

<fileset dir="./application" includes="**" id="files"/>

<fileset refid="files"/>

Page 28: Building and Deploying PHP apps with Phing

Fileset

• Selectors allow fine-grained matching on certain attributes

• contains, date, file name & size, ...

<fileset dir="${dist}"><and>

<filename name="**"/><date datetime="01/01/2011" when="before"/>

</and></fileset>

Page 29: Building and Deploying PHP apps with Phing

Conditions

• Nested elements that evaluate to booleans

• Used in "condition", "if" and "waitfor" tasks

Page 30: Building and Deploying PHP apps with Phing

Conditions

<if><equals arg1="${foo}" arg2="bar" /><then>

<echo message="The value of property foo is bar" /></then><else>

<echo message="The value of property foo is not bar" /></else>

</if>

Page 31: Building and Deploying PHP apps with Phing

Conditions

<if><available file="composer.json" /><then>

<exec checkreturn="true" command="composer install"passthru="true" logoutput="true" dir="build" />

</then></if>

Page 32: Building and Deploying PHP apps with Phing

Examples

Page 33: Building and Deploying PHP apps with Phing

Examples

• Version control

• Unit testing

• Packaging

• Deployment

• Database migration

• Continuous integration

Page 34: Building and Deploying PHP apps with Phing

Version control

• Git

• SVN

• CVS

Page 35: Building and Deploying PHP apps with Phing

Version control

<svnexportrepositoryurl="svn://localhost/project/trunk/"todir="/home/michiel/dev"/>

<svnlastrevisionrepositoryurl="svn://localhost/project/trunk/"propertyname="lastrev"/>

<echo>Last revision: ${lastrev}</echo>

Page 36: Building and Deploying PHP apps with Phing

Version control

<gitcommitrepository="/home/michiel/dev/phing"message="Update documentation" allFiles="true"/>

<gitpushrepository="/home/michiel/dev/phing"refspec="master" tags="true" />

Page 37: Building and Deploying PHP apps with Phing

PHPUnit

• Built-in support for most configuration options

• Gathers code coverage information

• Various output formats / reports

• PHPUnit 4.x support soon!

Page 38: Building and Deploying PHP apps with Phing

PHPUnit

• Stop the build when a test fails

<phpunit haltonfailure="true" haltonerror="true"bootstrap="my_bootstrap.php" printsummary="true"><batchtest>

<fileset dir="src"><include name="**/*Test.php"/>

</fileset></batchtest>

</phpunit>

Buildfile: /home/michiel/phpunit/build.xml

Demo > test:

[phpunit] Total tests run: 1, Failures: 1, Errors: 0,Incomplete: 0, Skipped: 0, Time elapsed: 0.00591 s

Execution of target "test" failed for the following reason:/home/michiel/phpunit/build.xml:3:44: Test FAILURE (testSayHello inclass HelloWorldTest): Failed asserting that two strings are equal.

Page 39: Building and Deploying PHP apps with Phing

PHPUnit example

• Determine which files to include in the coverage report

<coverage-setup database="reports/coverage.db"><fileset dir="src">

<include name="**/*.php"/><exclude name="**/*Test.php"/>

</fileset></coverage-setup>

• Gather code coverage and other data during the test run<phpunit codecoverage="true">

<formatter type="xml" todir="reports"/><batchtest>

<fileset dir="src"><include name="**/*Test.php"/>

</fileset></batchtest>

</phpunit>

Page 40: Building and Deploying PHP apps with Phing

PHPUnit example

• Generate some reports

<phpunitreport infile="reports/testsuites.xml"format="frames" todir="reports/tests"/>

<coverage-report outfile="reports/coverage.xml"><report todir="reports/coverage" title="Demo"/>

</coverage-report>

Page 41: Building and Deploying PHP apps with Phing

Documentation

• Phing currently integrates with popular documentationtools

• phpDocumentor (2)• ApiGen

• Also supports r(e)ST (reStructuredText)

<phpdoc2 title="Phing API Documentation" output="docs"><fileset dir="../../classes">

<include name="**/*.php"/></fileset>

</phpdoc2>

Page 42: Building and Deploying PHP apps with Phing

phpDocumentor

Page 43: Building and Deploying PHP apps with Phing

Packaging

• Create bundles or packages• tar• zip• phar• PEAR

Page 44: Building and Deploying PHP apps with Phing

Tar / zip

<tar compression="gzip" destFile="package.tgz"basedir="build"/>

<zip destfile="htmlfiles.zip"><fileset dir=".">

<include name="**/*.html"/></fileset>

</zip>

Page 45: Building and Deploying PHP apps with Phing

Phar packages

<pharpackagecompression="gzip"destfile="test.phar"stub="stub.php"basedir="."><fileset dir="hello">

<include name="**/**" /></fileset><metadata>

<element name="version" value="1.0" /><element name="authors">

<element name="John Doe"><element name="e-mail"

value="[email protected]" /></element>

</element></metadata>

</pharpackage>

Page 46: Building and Deploying PHP apps with Phing

SSH

<ssh username="john" password="smith"host="webserver" command="ls" />

<scp username="john" password="smith"host="webserver" todir="/www/htdocs/project/"><fileset dir="test">

<include name="*.html"/></fileset>

</scp>

Page 47: Building and Deploying PHP apps with Phing

Jenkins

Page 48: Building and Deploying PHP apps with Phing

Jenkins

Page 49: Building and Deploying PHP apps with Phing

Jenkins

Page 50: Building and Deploying PHP apps with Phing

Putting it all together

Page 51: Building and Deploying PHP apps with Phing
Page 52: Building and Deploying PHP apps with Phing

Build & deploy script

Objectives:

• Perform syntax check

• Run tests

• Create package

• Deploy via SSH

• To selectable target / environment

• Update database

• Roll back

Page 53: Building and Deploying PHP apps with Phing

Syntax checks & tests

<phplint haltonfailure="true"><fileset dir=".">

<include name="src/**" /></fileset>

</phplint>

<phpunit haltonfailure="true"><batchtest>

<fileset dir="."><include name="src/**/*Test.php" />

</fileset></batchtest>

</phpunit>

Page 54: Building and Deploying PHP apps with Phing

Packaging

<tstamp><format property="build.timestamp" pattern="%Y%m%d%H%M%S"/>

</tstamp>

<property name="build.release" value="${project.name}-${build.timestamp}" /><property name="package.name" value="${build.release}.tar.gz" />

<tar destfile="artifacts/${package.name}" basedir="${build.dir.project}" />

Page 55: Building and Deploying PHP apps with Phing

Multiple targets

• Several deployment targets: testing, staging, production, ...

• One property file per target

• Select based on input

ssh.host=127.0.0.1ssh.username=phingssh.key.private=development-sshssh.key.public=development-ssh.pubdeploy.location=/home/phing/apps

Page 56: Building and Deploying PHP apps with Phing

Multiple targets

<input propertyname="deploy.target"validArgs="testing,staging,production">

Enter target name</input>

<property file="${deploy.target}.properties"/>

Page 57: Building and Deploying PHP apps with Phing

Uploading

<ssh host="${ssh.host}"username="${ssh.username}"privkeyfile="${ssh.key.private}"pubkeyfile="${ssh.key.public}"command="mkdir -p ${deploy.location.project}/${build.release}"failonerror="true" />

<echo>Copying package</echo><scp host="${ssh.host}"

port="${ssh.port}"username="${ssh.username}"privkeyfile="${ssh.key.private}"pubkeyfile="${ssh.key.public}"todir="${deploy.location.project}/${build.release}"file="${package.name.full}" />

Page 58: Building and Deploying PHP apps with Phing

Uploading

<echo>Extracting package</echo><ssh ...

command="cd ${deploy.location.project}/${build.release};tar xzf ${package.name}"

failonerror="true" />

Page 59: Building and Deploying PHP apps with Phing

Symbolic links

• All releases stored in separate directories

• Symlink "current" to latest release

• Allows for easy (code) rollbacks

<echo>Creating symbolic link</echo><ssh ...

command="cd ${deploy.location.project};if [ -h &quot;current&quot; ]; thenrm -f previous; mv current previous; fi;ln -s ${build.release} current" />

Page 60: Building and Deploying PHP apps with Phing

Database migration

• Set of delta SQL files (1-create-post.sql)

• Tracks current version of your db in changelog table

• Generates do and undo SQL files

CREATE TABLE changelog (change_number BIGINT NOT NULL,delta_set VARCHAR(10) NOT NULL,start_dt TIMESTAMP NOT NULL,complete_dt TIMESTAMP NULL,applied_by VARCHAR(100) NOT NULL,description VARCHAR(500) NOT NULL

)

Page 61: Building and Deploying PHP apps with Phing

Database migration

• Delta scripts with do (up) & undo (down) parts

--//

CREATE TABLE `post` (`title` VARCHAR(255),`time_created` DATETIME,`content` MEDIUMTEXT

);

--//@UNDO

DROP TABLE `post`;

--//

Page 62: Building and Deploying PHP apps with Phing

Database migration

<dbdeployurl="sqlite:test.db"dir="deltas"outputfile="deploy.sql"undooutputfile="undo.sql"/>

<pdosqlexecsrc="deploy.sql"url="sqlite:test.db"/>

[dbdeploy] Getting applied changed numbers from DB:mysql:host=localhost;dbname=demo

[dbdeploy] Current db revision: 0[dbdeploy] Checkall:[pdosqlexec] Executing file: /home/michiel/dbdeploy/deploy.sql[pdosqlexec] 3 of 3 SQL statements executed successfully

Page 63: Building and Deploying PHP apps with Phing

Database migration

-- Fragment begins: 1 --INSERT INTO changelog

(change_number, delta_set, start_dt, applied_by, description)VALUES (1, 'Main', NOW(), 'dbdeploy','1-create_initial_schema.sql');

--//

CREATE TABLE `post` (`title` VARCHAR(255),`time_created` DATETIME,`content` MEDIUMTEXT

);

UPDATE changelogSET complete_dt = NOW()WHERE change_number = 1AND delta_set = 'Main';

-- Fragment ends: 1 --

Page 64: Building and Deploying PHP apps with Phing

Database migration

-- Fragment begins: 1 --

DROP TABLE `post`;

--//

DELETE FROM changelogWHERE change_number = 1AND delta_set = 'Main';

-- Fragment ends: 1 --

Page 65: Building and Deploying PHP apps with Phing

Restart services

<ssh ...command="sudo service apache2 reload"failonerror="true" />

Page 66: Building and Deploying PHP apps with Phing

Rolling back

<trycatch><try>

<ssh ...command="cd ${deploy.location.project};

rm -f current;mv -f previous current"

failonerror="true" /><echo>Rollback complete</echo>

</try><catch>

<echo>No previous version deployed!</echo></catch>

</trycatch>

Page 67: Building and Deploying PHP apps with Phing

Extending Phing

Page 68: Building and Deploying PHP apps with Phing

Writing your own task

• Extend from Task

• Contains main() method and optionally init()

• Setter method for each attribute in the build file

Page 69: Building and Deploying PHP apps with Phing

Our new task should

• Accept filesets

• Count number of lines in each file

• Fail the build if a file with zero lines is found

Page 70: Building and Deploying PHP apps with Phing

Our new task

<?php

require_once 'phing/Task.php';

class CountLinesTask extends Task{

public function main(){

$foundEmpty = false;

if ($foundEmpty) {throw new BuildException("One or more files have zero lines");

}}

}

Page 71: Building and Deploying PHP apps with Phing

Injecting file sets

private $_filesets = array();

/*** Creator for _filesets** @return FileSet*/public function createFileset(){

$num = array_push($this->_filesets, new FileSet());return $this->_filesets[$num-1];

}

Page 72: Building and Deploying PHP apps with Phing

Injecting file sets

foreach ($this->_filesets as $fileset) {$files = $fileset->getDirectoryScanner($this->project)

->getIncludedFiles();$dir = $fileset->getDir($this->project)->getAbsolutePath();

foreach ($files as $file) {$path = realpath($dir . DIRECTORY_SEPARATOR . $file);$lines = count(file($path));

$this->log($path . ": " . $lines . " line(s)");

if ($lines == 0) {$foundEmpty = true;

}}

}

Page 73: Building and Deploying PHP apps with Phing

Using the task

<project name="Count Lines" default="count"><target name="count">

<taskdef name="countlines"classpath="/home/michiel/tasks"classname="CountLinesTask" />

<countlines><fileset dir=".">

<include name="**/*.txt" /></fileset>

</countlines></target>

</project>

Page 74: Building and Deploying PHP apps with Phing

Using the task

Buildfile: /home/michiel/examples/count.xml

Count Lines > count:

[countlines] /home/michiel/examples/empty.txt: 0 line(s)[countlines] /home/michiel/examples/lines.txt: 3 line(s)Execution of target "count" failed for the following reason:/home/michiel/examples/count.xml:7:20: One or more files have zero lines

BUILD FAILED/home/michiel/examples/count.xml:7:20: One or more files have zero linesTotal time: 0.0454 seconds

Page 75: Building and Deploying PHP apps with Phing

Alternative: Ad Hoc

<target name="main"><adhoc-task name="foo"><![CDATA[class FooTask extends Task {

private $bar;

public function setBar($bar) {$this->bar = $bar;

}

public function main() {$this->log("In main(): " . $this->bar);

}}]]></adhoc-task><foo bar="TEST"/>

</target>

Page 76: Building and Deploying PHP apps with Phing
Page 77: Building and Deploying PHP apps with Phing

Where to go from here

• Tool versions

• Performance

• Documentation

• PHP 5.3/5.4/5.5

• IDE support

• CI integration

Page 78: Building and Deploying PHP apps with Phing

Questions?

Example code at https://github.com/mrook/phing-examplesPlease leave feedback at https://joind.in/10411

Contact us on:

http://www.phing.info#phing (freenode)@phingofficial

Thank you!