SlideShare a Scribd company logo
Phing for power users
Stephan Hochdörfer, bitExpert AG
Phing for power users
About me
 Stephan Hochdörfer
 Head of IT at bitExpert AG, Germany
 enjoying PHP since 1999
 S.Hochdoerfer@bitExpert.de
 @shochdoerfer
Phing for power users
I used to be an Ant fanboy...
What is Phing?
Phing for power users
What is Phing?
Phing for power users
It is a PHP project build system or
build tool based on Apache Ant.
What is Phing?
Phing for power users
Domain Specific Language
for an project build system.
Phing for power users
A build system for PHP code? Srsly?
Glue for all 3rd party tools
Phing for power users
How to install Phing?
Phing for power users
$> pear channel­discover pear.phing.info
$> pear install phing/phing
How to install Phing? The PEAR way...
Phing for power users
Installing Phing
$> phing ­v
Phing 2.5.0
$> pear channel­discover pear.phing.info
$> pear install phing/phing
How to install Phing? The PEAR way...
Phing for power users
Installing Phing
Running Phing:
How to install Phing?
Phing for power users
Installing Phing globally? WTF?!?
How to install Phing?
Phing for power users
Phing is „just another“
dependency for your project.
How to install Phing? The Composer way...
Phing for power users
{
"require": {
"phing/phing": "2.5.0"
}
}
How to install Phing? The Composer way...
Phing for power users
composer.json:
{
"require": {
"phing/phing": "2.5.0"
}
}
$> php composer.phar install
Loading composer repositories with package information
Installing dependencies
  ­ Installing phing/phing (2.5.0)
    Downloading: 100%         
Writing lock file
Generating autoload files
How to install Phing? The Composer way...
Phing for power users
composer.json:
Running Composer:
How to install Phing? The Composer way...
Phing for power users
/tmp/myproject
   |­vendor
   |­­­bin
   |­­­­­@phing
   |­­­composer
   |­­­phing
   |­­­­­phing
   |­­­­­­­bin
   |­­­­­­­­­phing
   |­­­­­­­build
   |­­­­­­­classes
   |­­­­­­­docs
   |­­­­­­­etc
   |­­­­­­­test
How to install Phing? The Composer way...
Phing for power users
/tmp/myproject
   |­vendor
   |­­­bin
   |­­­­­@phing
   |­­­composer
   |­­­phing
   |­­­­­phing
   |­­­­­­­bin
   |­­­­­­­­­phing
   |­­­­­­­build
   |­­­­­­­classes
   |­­­­­­­docs
   |­­­­­­­etc
   |­­­­­­­test
$> ./vendor/bin/phing ­v
Phing 2.5.0
Running Phing:
Phing Basics
Phing for power users
Phing Basics
Phing for power users
Project, Target, Task, Properties
Phing Basics: Project
Phing for power users
Root node of a build file
containing one or more targets.
Phing Basics: Target
Phing for power users
A group of tasks that
run as an entity.
Phing Basics: Task
Phing for power users
Custom piece of code to
perform a specific function.
Phing Basics: Properties
Phing for power users
Properties (variables) help to
customize execution.
Phing Basics: Built-In Properties
Phing for power users
e.g. host.os, line.separator,
phing.version, php.version, …
<?xml version="1.0"?>
<project name="myproject" default="init">
<target name="init">
<!­­ insert logic here ­­>
</target>
</project>
Phing Basics: Sample Build File
Phing for power users
<?xml version="1.0"?>
<project name="myproject" default="hello">
<target name="hello" 
description="Says Hello, world!">
 <echo msg="Hello, world!" />
</target>
</project>
Build File – Hello World example
Phing for power users
Why Phing?
Phing for power users
Runs everywhere where PHP runs.
Why Phing?
Phing for power users
No additional depencendies needed
(e.g. Java, …).
Why Phing?
Phing for power users
More than 120 predefined
tasks to choose from.
Why Phing?
Phing for power users
Easy to extend by writing
custom tasks in PHP.
Enforce Internal Targets
Phing for power users
Enforce Internal Targets
Phing for power users
<?xml version="1.0"?>
<project name="myproject" default="hello">
<target name="init"
description="Property initialization">
<property name="Hello" value="Hello, world!" />
</target>
<target name="hello"
depends="init">
<echo msg="${Hello}" />
</target>
</project>
Enforce Internal Targets
Phing for power users
$> ./vendor/bin/phing -f build.xml hello
/tmp/myproject/build.xml
myproject > init:
myproject > hello:
[echo] Hello, world!
BUILD FINISHED
Total time: 0.0474 seconds
Enforce Internal Targets
Phing for power users
$> ./vendor/bin/phing -f build.xml init
/tmp/myproject/build.xml
myproject > init:
BUILD FINISHED
Total time: 0.0476 seconds
Enforce Internal Targets
Phing for power users
$> ./vendor/bin/phing -l
Buildfile: /tmp/myproject/build.xml
Default target:
---------------------------------------------------------
hello
Main targets:
---------------------------------------------------------
init Property initialization
Subtargets:
---------------------------------------------------------
hello
Enforce Internal Targets
Phing for power users
Internal targets are just helpers
like private methods.
<?xml version="1.0"?>
<project name="myproject" default="hello">
<target name="-init"
description="Property initialization">
<property name="Hello" value="Hello, world!" />
</target>
<target name="hello"
depends="-init">
<echo msg="${Hello}" />
</target>
</project>
Enforce Internal Targets – The solution
Phing for power users
Enforce Internal Targets – The solution
Phing for power users
$> ./vendor/bin/phing -f build.xml -init
Unknown argument: -init
phing [options] [target [target2 [target3] ...]]
Options:
-h -help print this message
-l -list list available targets
-v -version print the version information
-q -quiet be extra quiet
-verbose be extra verbose
-debug print debugging information
Report bugs to <dev@phing.tigris.org>
Enforce Internal Targets – The solution
Phing for power users
Are the targets really hidden?
Enforce Internal Targets
Phing for power users
$> ./vendor/bin/phing -l
Buildfile: /tmp/myproject/build.xml
Default target:
---------------------------------------------------------
hello
Main targets:
---------------------------------------------------------
-init Property initialization
Subtargets:
---------------------------------------------------------
hello
Enforce Internal Targets
Phing for power users
$> ./vendor/bin/phing -l
Buildfile: /tmp/myproject/build.xml
Default target:
---------------------------------------------------------
hello
Main targets:
---------------------------------------------------------
-init Property initialization
Subtargets:
---------------------------------------------------------
hello
Enforce Internal Targets (Improved Version)
Phing for power users
<?xml version="1.0"?>
<project name="myproject" default="hello">
<target name="-init"
hidden="true"
description="Property initialization">
<property name="Hello" value="Hello, world!" />
</target>
<target name="hello"
depends="-init">
<echo msg="${Hello}" />
</target>
</project>
$> ./vendor/bin/phing -l
Buildfile: /tmp/myproject/build.xml
Default target:
---------------------------------------------------------
hello
Subtargets:
---------------------------------------------------------
hello
Enforce Internal Targets (Improved Version)
Phing for power users
Custom Tasks
Phing for power users
Custom Tasks
Phing for power users
Phing can do way more
than simple exec calls!
Custom Task (Adhoc definition)
Phing for power users
<?xml version="1.0"?>
<project name="myproject" default="hello">
<target name="init">
<adhoc-task name="mytask"><![CDATA[
class MyTask extends Task {
/**
* (non-PHPdoc)
* @see Task::main()
*/
public function main() {
// Custom code here...
}
}
]]></adhoc-task>
</target>
<target name="hello"
depends="init">
<mytask />
</target>
</project>
<?php
require_once 'phing/Task.php';
class MyTask extends Task {
/**
* (non-PHPdoc)
* @see Task::main()
*/
public function main() {
// Custom code here...
}
}
Custom Task (External file)
Phing for power users
<?xml version="1.0"?>
<project name="myproject" default="hello">
<target name="init">
<taskdef
name="mytask"
classpath="${project.basedir}"
classname="MyApp.Common.Phing.MyTask" />
</target>
<target name="hello"
depends="init">
<mytask />
</target>
</project>
Custom Task (External file)
Phing for power users
<?php
require_once 'phing/Task.php';
class MyTask extends Task {
protected $file;
/**
* @param string $file
*/
public function setFile($file) {
$this->file = $file;
}
/**
* @see Task::main()
*/
public function main() {
// Custom code here...
}
}
Custom Task with Parameters
Phing for power users
<?xml version="1.0"?>
<project name="myproject" default="hello">
<target name="init">
<taskdef
name="mytask"
classpath="${project.basedir}"
classname="MyApp.Common.Phing.MyTask" />
</target>
<target name="hello"
depends="init">
<mytask file="myfile.txt" />
</target>
</project>
Custom Task with Parameters
Phing for power users
Properties File
Phing for power users
Properties File
Phing for power users
Use properties to
cutomize build behaviour.
<?xml version="1.0"?>
<project name="myproject" default="hello">
<target name="hello" 
description="Says whatever you want to say">
<property
file="./build.properties" />
 <echo msg="${Hello}" />
</target>
</project>
Properties File
Phing for power users
<?xml version="1.0"?>
<project name="myproject" default="hello">
<target name="hello" 
description="Says whatever you want to say">
<property
file="./build.properties" />
 <echo msg="${Hello}" />
</target>
</project>
Properties File
Phing for power users
Hello=Hello, world!
build.properties:
Properties File
Phing for power users
$> phing
Buildfile: /tmp/myproject/build.xml
myproject > hello:
[property] Loading /tmp/myproject/build.properties
[echo] Hello, world!
BUILD FINISHED
Total time: 0.0601 seconds
Properties File - Improved version
Phing for power users
Properties File - Improved version
Phing for power users
Requirement: Externalize properties
but offer customization capabilities!
<?xml version="1.0"?>
<project name="myproject" default="hello">
<target name="hello" depends="init">
<echo msg="${Hello}" />
</target>
<target name="init" depends="prop, local-prop">

</target>
<target name="prop">
<echo
message="Loading default build.properties"/>
<property
file="build.properties" />
</target>
Properties File - Improved version
Phing for power users
<target name="local-prop"
if="local-prop.exists"
depends="local-prop-check">
<echo message="Loading custom properties!"/>
<property
file="local.properties"
override="true"/>
</target>
<target name="local-prop-check">
<available
file="local.properties"
property="local-prop.exists" />
</target>
</project>
Properties File - Improved version
Phing for power users
$> phing
Buildfile: /tmp/myproject/build.xml
myproject > prop:
[echo] Loading default build.properties
[property] Loading /tmp/myproject/build.properties
myproject > local-prop-check:
myproject > local-prop:
myproject > init:
myproject > hello:
[echo] Hello, world!
BUILD FINISHED
Total time: 0.1383 seconds
Properties File - Improved version
Phing for power users
$> phing
Buildfile: /tmp/myproject/build.xml
myproject > prop:
[echo] Loading default build.properties
[property] Loading /tmp/myproject/build.properties
myproject > local-prop-check:
myproject > local-prop:
[echo] Loading custom properties!
[property] Loading /tmp/myproject/local.properties
myproject > init:
myproject > hello:
[echo] Hello my world!
BUILD FINISHED
Total time: 0.0493 seconds
Properties File - Improved version
Phing for power users
build.properties example
Phing for power users
phpunit.path=vendor/bin/phpunit
phpunit.junit.log=build/logs/junit.xml
phpunit.coverage.clover=build/logs/clover.xml
phpunit.coverage.html=build/coverage
phpcs.path=vendor/bin/phpcs
phpcs.log=build/logs/checkstyle.xml
sencha.senchaCmd=/user/local/lib/sencha/sencha
sencha.jsb3File=app.jsb3
build.properties example
Phing for power users
Use distinct naming conventions
for your properties.
Accessing application configuration
Phing for power users
Accessing application configuration
Phing for power users
Duplicating configuration code
is a bad habit.
Accessing application configuration
Phing for power users
<?php
require_once 'phing/Task.php';
class ConfigMapperTask extends Task {
/**
* @see Task::main()
*/
public function main() {
// will import $APP_CONF in local context
require_once('src/bootstrap.php');
$project = $this->project;
$project->setProperty(
'db.host', $APP_CONF['db_host']);
$project->setProperty(
'db.database', $APP_CONF['db_database']);
$project->setProperty(
'db.user', $APP_CONF['db_user']);
$project->setProperty(
'db.password', $APP_CONF['db_passwd']);
}
}
Accessing application configuration
Phing for power users
<?xml version="1.0"?>
<project name="myproject" default="hello">
<taskdef name="readAppConfig"
classpath="${phing.dir}/src/"
classname="MyApp.Common.Phing.AppConfigTask" />
<target name="init" depends="prop, local-prop">
<readAppConfig />
</target>
<target name="prop">
<echo message="Load default build.properties"/>
<property file="build.properties" />
</target>
<target name="local-prop"
if="local-prop.exists"
depends="local-prop-check">

</project>
Imports for Targets can help structuring
Phing for power users
<?xml version="1.0"?>
<project name="myproject" default="app:run">

<import file="build/build.db.xml" />
<import file="build/build.app.xml" />
<import file="build/build.ci.xml" />
</project>
Imports for Targets can help structuring
Phing for power users
<?xml version="1.0"?>
<project name="myproject" default="app:run">

<import file="vendor/vendor1/lib1/build/build.xml" />
<import file="vendor/vendor2/lib2/build/build.xml" />
<import file="build/build.app.xml" />
</project>
Import Targets: Composer packages
Phing for power users
Import Targets: Path handling
Phing for power users
Be aware that imports behave
like include in PHP!
<?xml version="1.0"?>
<project name="myproject" default="lib1:run">

<import file="vendor/lib1/build/build.xml" />
</project>
Import Targets: Path handling
Phing for power users
build.xml
<?xml version="1.0"?>
<project name="lib1" default="lib1:run">
<target name="lib1:run">
<echo msg="Local dir: ${phing.dir.lib1}" />
<echo msg="Global dir: ${phing.dir}" />
</target>
</project>
<?xml version="1.0"?>
<project name="myproject" default="lib1:run">

<import file="vendor/lib1/build/build.xml" />
</project>
Import Targets: Path handling
Phing for power users
build.xml
vendor/lib1/build/build.xml
$> ./vendor/bin/phing
Buildfile: /tmp/myproject/build.xml
myproject > lib1:run:
[echo] Local dir: /tmp/myproject/vendor/lib1/build
[echo] Global dir: /tmp/myproject
BUILD FINISHED
Total time: 0.0411 seconds
Import Targets: Path handling
Phing for power users
Import Targets: Path handling
Phing for power users
Be aware to always(!) use the
projects name in lowercase format!
Import Targets: Path handling
Phing for power users
It`s ${phing.dir.myproject}
not ${phing.dir.MyProject}!
Distinct Target Naming
Phing for power users
<?xml version="1.0"?>
<project name="myproject" default="ci:run-tests">
<target name="app:clean-cache">
</target>
<target name="app:create-cache">
</target>
<target name="db:migrate">
</target>
<target name="js:minifiy">
</target>
<target name="ci:lint">
</target>
<target name="ci:run-tests">
</target>
</project>
Distinct Target Naming
Phing for power users
<?xml version="1.0"?>
<project name="myproject" default="app:create-cache">
<target name="app:clean-cache"
description="Removes all cache files.">
</target>
<target name="app:create-cache"
description="Builds the cache files from the
xml configuration.">
</target>
</project>
Adding meaningful descriptions
Phing for power users
$> phing -l
Buildfile: /tmp/myproject/build.xml
Default target:
-----------------------------------------------------
app:create-cache Builds the cache files from the xml
configuration.
Main targets:
------------------------------------------------------
app:clean-cache Removes all cache files.
app:create-cache Builds the cache files from the xml
configuration.
Adding meaningful descriptions
Phing for power users
Prompt user for input
Phing for power users
<?xml version="1.0"?>
<project name="myproject" default="run">
<target name="run">

<input
propertyname="tag"
defaultValue="mytag">Tag to create?</input>
<liquibase-tag
tag="${tag}"
jar="/opt/liquibase/liquibase.jar"
classpathref="/opt/liquibase/lib/mysql.jar"
changelogFile="${project.basedir}/diff.xml"
username="liquibase"
password="liquibase"
url="jdbc:mysql://localhost/myproject"/>
</target>
</project>
Calling PHP functions from Phing
Phing for power users
Calling PHP functions from Phing
Phing for power users
<?xml version="1.0"?>
<project name="myproject" default="run">
<target name="run">

<php function="realpath"
returnProperty="app.dir">
<param value="${app.dir}"/>
</php>
</target>
</project>
Restrict user access
Phing for power users
<?xml version="1.0"?>
<project name="myproject" default="run">
<target name="run">

<if>
<not>
<equals arg1="${env.USER}"
arg2="root" />
</not>
<then>
<fail message="Wrong user!" />
</then>
</if>
</target>
</project>
Restrict user access
Phing for power users
Path handling
Phing for power users
Path handling
Phing for power users
<?xml version="1.0"?>
<project name="myproject" default="ci:phpunit">

<target name="ci:phpunit"
depends="-init, -ci:prepare">
<resolvepath propertyName="phpunit.path.abs"
dir="${phing.dir}"
file="${phpunit.path}"/>
<exec executable="${phpunit.path.abs}" />
</target>
</project>
Phing + Jenkins
Phing for power users
Phing + Jenkins
Phing for power users
Install the Jenkins Phing plugin
Phing for power users
Phing for power users
Phing for power users
Phing + Composer + Jenkins
Phing for power users
Phing + Composer + Jenkins
Phing for power users
Install the Jenkins EnvInject plugin
Phing for power users
Phing for power users
Phing for power users
Phing for power users
Follow conventions
Phing for power users
Follow conventions
Phing for power users
Phing expects your build file to be called
build.xml and the build’s properties file
build.properties
Follow conventions
Phing for power users
Pick meaningful, human-readable
names for targets and properties.
Follow conventions
Phing for power users
Make build files self-contained.
Thank you!
http://joind.in/9023
Phing for power users
Image Credits
http://www.sxc.hu/photo/629370
http://www.sxc.hu/photo/615731

More Related Content

Phing for power users - frOSCon8