Phing for power users - frOSCon8

Post on 08-May-2015

601 views 2 download

Transcript of Phing for power users - frOSCon8

Phing for power usersStephan 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 orbuild 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 ­vPhing 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 installLoading composer repositories with package informationInstalling dependencies  ­ Installing phing/phing (2.5.0)    Downloading: 100%         

Writing lock fileGenerating 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 ­vPhing 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 predefinedtasks 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 -lBuildfile: /tmp/myproject/build.xmlDefault 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 -initUnknown argument: -initphing [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 -lBuildfile: /tmp/myproject/build.xmlDefault target:--------------------------------------------------------- hello

Main targets:--------------------------------------------------------- -init Property initialization

Subtargets:--------------------------------------------------------- hello

Enforce Internal Targets

Phing for power users

$> ./vendor/bin/phing -lBuildfile: /tmp/myproject/build.xmlDefault 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 -lBuildfile: /tmp/myproject/build.xmlDefault 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 morethan 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>

<?phprequire_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

<?phprequire_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">

<propertyfile="./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">

<propertyfile="./build.properties" />

 <echo msg="${Hello}" /></target>

</project>

Properties File

Phing for power users

Hello=Hello, world!build.properties:

Properties File

Phing for power users

$> phingBuildfile: /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"><!-- 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

<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

$> phingBuildfile: /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

$> phingBuildfile: /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/phpunitphpunit.junit.log=build/logs/junit.xmlphpunit.coverage.clover=build/logs/clover.xmlphpunit.coverage.html=build/coverage

phpcs.path=vendor/bin/phpcsphpcs.log=build/logs/checkstyle.xml

sencha.senchaCmd=/user/local/lib/sencha/senchasencha.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

<?phprequire_once 'phing/Task.php';

class ConfigMapperTask extends Task {/** * @see \Task::main() */public function main() {

// will import $APP_CONF in local contextrequire_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">

<!--The following target namespaces exist:db:* - Database specific targetsapp:* - Application specific tasksci:* - 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

<?xml version="1.0"?><project name="myproject" default="app:run">

<!--The following target namespaces exist:lib1:* - Targets imported from lib1lib2:* - Targets imported from lib2app:* - 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

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">

<!--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

<?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

$> ./vendor/bin/phingBuildfile: /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 -lBuildfile: /tmp/myproject/build.xmlDefault 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"> <!-- 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>

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">

<!-- Returns canonicalized absolute pathname -->

<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">

<!-- 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

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/629370http://www.sxc.hu/photo/615731