Phing for power users - frOSCon8
- 2. 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
- 5. What is Phing?
Phing for power users
It is a PHP project build system or
build tool based on Apache Ant.
- 12. How to install Phing?
Phing for power users
Installing Phing globally? WTF?!?
- 13. How to install Phing?
Phing for power users
Phing is „just another“
dependency for your project.
- 17. 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
- 18. 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:
- 25. Phing Basics: Built-In Properties
Phing for power users
e.g. host.os, line.separator,
phing.version, php.version, …
- 33. 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>
- 34. 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
- 35. 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
- 36. 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
- 38. <?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
- 39. 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>
- 41. 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
- 42. 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
- 43. 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>
- 44. $> ./vendor/bin/phing -l
Buildfile: /tmp/myproject/build.xml
Default target:
---------------------------------------------------------
hello
Subtargets:
---------------------------------------------------------
hello
Enforce Internal Targets (Improved Version)
Phing for power users
- 47. 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>
- 49. <?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
- 50. <?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
- 51. <?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
- 56. 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
- 58. Properties File - Improved version
Phing for power users
Requirement: Externalize properties
but offer customization capabilities!
- 59. <?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">
<!-- some more init logic -->
</target>
<target name="prop">
<echo
message="Loading default build.properties"/>
<property
file="build.properties" />
</target>
Properties File - Improved version
Phing for power users
- 61. $> 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
- 62. $> 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
- 63. 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
- 67. 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']);
}
}
- 68. 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>
- 70. <?xml version="1.0"?>
<project name="myproject" default="app:run">
<!--
The following target namespaces exist:
db:* - Database specific targets
app:* - Application specific tasks
ci:* - CI server specific tasks
-->
<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
- 71. <?xml version="1.0"?>
<project name="myproject" default="app:run">
<!--
The following target namespaces exist:
lib1:* - Targets imported from lib1
lib2:* - Targets imported from lib2
app:* - Local application targets
-->
<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
- 72. Import Targets: Path handling
Phing for power users
Be aware that imports behave
like include in PHP!
- 73. <?xml version="1.0"?>
<project name="myproject" default="lib1:run">
<!--
The following target namespaces exist:
lib1:* - Targets imported from lib1
-->
<import file="vendor/lib1/build/build.xml" />
</project>
Import Targets: Path handling
Phing for power users
build.xml
- 74. <?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">
<!--
The following target namespaces exist:
lib1:* - Targets imported from lib1
-->
<import file="vendor/lib1/build/build.xml" />
</project>
Import Targets: Path handling
Phing for power users
build.xml
vendor/lib1/build/build.xml
- 76. Import Targets: Path handling
Phing for power users
Be aware to always(!) use the
projects name in lowercase format!
- 77. Import Targets: Path handling
Phing for power users
It`s ${phing.dir.myproject}
not ${phing.dir.MyProject}!
- 79. <?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
- 80. <?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
- 81. $> 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
- 82. Prompt user for input
Phing for power users
<?xml version="1.0"?>
<project name="myproject" default="run">
<target name="run">
<!-- tag the database -->
<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>
- 84. Calling PHP functions from Phing
Phing for power users
<?xml version="1.0"?>
<project name="myproject" default="run">
<target name="run">
<!--
Returns canonicalized absolute pathname
-->
<php function="realpath"
returnProperty="app.dir">
<param value="${app.dir}"/>
</php>
</target>
</project>
- 86. <?xml version="1.0"?>
<project name="myproject" default="run">
<target name="run">
<!--
Check for root user
-->
<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
- 88. 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>
- 95. Phing + Composer + Jenkins
Phing for power users
Install the Jenkins EnvInject plugin
- 101. Follow conventions
Phing for power users
Phing expects your build file to be called
build.xml and the build’s properties file
build.properties
- 106. Phing for power users
Image Credits
http://www.sxc.hu/photo/629370
http://www.sxc.hu/photo/615731