The title is a bit wishful thinking, but you'll see why. The guides below could be used for any PHP project out there, but it will represent different things for different quality code.
This tutorial doesn't handle UI testing.
You probably have a lot of code sitting on your hard drive and hopefully you already use some sort of source control system. The trouble is, that whenever you have to fix a bug, you can only be sure, that the specified bug is gone, but you're unaware of what might have broken.
This tutorial assumes you know what OOP is and you use it more-or-less for your projects. In an ideal world you develop a web application, users use it and everybody is 'appy. But we (at least I) don't live in an ideal world, money doesn't come with spam. (wish it would). Did you ever get into a situation of working way more for the same amount of money, because bugfixing took twice as much time as creating the software? I did. The 2day coding resulted 2 weeks of bugfixing. Why? - you may ask. Every single bug I've fixed, introduced a new one somewhere else. That was the point that led me to the world of
eXtreme programming.
Why PHP?
I am a webdeveloper, and this is my preference. The tools and techniques I am going to introduce are more-or-less available for all greater programming languages. These are all inherited from the Java utilities.
JUnit,
Checkstyle,
PMD,
JDepend,
Ant, etc...
Determining code quality
Code (software) quality consists of many parameters. The first and unquestionable aspect is:
It needs to be working! But is it that simple, or there's some mystical fog around this? What would you say about the following example?
[cc lines="-1" lang="php" escaped="true"]
=8&&$t
[/cc]
Do you know what this does? Could you explain to someone what the responsibility of this class and method is? Will you be able to do this half year after you've created the method? Don't think so. Imagine thousands of lines of this kind of code.
But what happens when you put a little effort in coding and you try to make things
READABLE?
[cc lines="-1" lang="php" escaped="true"]
numOfPeople = $numOfPeople;
$this->sizeOfRoom = $sizeOfRoom;
}//end __construct()
/**
* calculates the sound level in a time of day
*
* @param int $timeOfDay The int of time of day
*
* @return int The calculated average sound level
*/
public function calculateSoundLevel($timeOfDay)
{
$multiplier = 1;
if ($timeOfDay >= 8 && $timeOfDay sizeOfRoom / $this->numOfPeople);
$soundLevel = $peopleDensity * $multiplier;
return $soundLevel;
}//end calculateSoundLevel()
}
?>
[/cc]
This may seem like a huge waste of time and lines, but think about it as an insurance, that you will always know what that particular piece of code does. You may also notice, that there are some formatting techniques that are not ordinary. These are because of my
CODING STANDARD, which ensures that every piece of code is formatted the same way all across the project. It's not a detailed example, but you may notice the elimination of the
global syntax and requiring the data in the constructor of the object. We call that kind of process
dependency inversion, which is one of the main pillars of
TESTABILITY. According to the
wiki, the following things should be taken into account when speaking - or determining - code quality:
My Unified Theory of Bugs
by Miško Hevery
-
Logical: Logical bug is the most common and classical “bug.” This is your “if”s, “loop”s, and other logic in your code. It is by far the most common kind of bug in an application. (Think: it does the wrong thing)
-
Wiring: Wiring bug is when two different objects are miswired. For example wiring the first-name to the last-name field. It could also mean that the output of one object is not what the input of the next object expects. (Think: Data gets clobbered in process to where it is needed.)
-
Rendering: Rendering bug is when the output (typical some UI or a report) does not look right. The key here is that it takes a human to determine what “right” is. (Think: it “looks” wrong)
@misko.hevery.com
Measuring code quality
As I mentioned above, there are many tools to measure all aspects of code quality.
Readability
The concept behind readable code is to strictly follow a
coding standard of your choice. You can do this with
PHP_CodeSniffer . There are multiplied pre-defined standards that come with the installation, I prefer the PHPCS.
PHP_CodeSniffer
Installation
[cc lang="bash" line_numbers="off"]
sudo pear install PHP_CodeSniffer
[/cc]
Usage
[cc lang="bash" line_numbers="off" escaped="true"]
phpcs <path/to/directory/or/file>
[/cc]
As you can see, the usage of phpcs is quite simple. You define what you want to verify and that's it. Of course there's a ton of other
options, but you won't use most of them.
What does it do?
The phpcs software scans your code token by token and validates it through a set of rules. If the validation fails, it gives a detailed report on what went wrong.
[cc lang="bash" line_numbers="off"]
FILE: /home/meza/dev/someproject/mailer.php
--------------------------------------------------------------------------------
FOUND 9 ERROR(S) AND 1 WARNING(S) AFFECTING 10 LINE(S)
--------------------------------------------------------------------------------
2 | ERROR | Missing file doc comment
22 | WARNING | Line exceeds 100 characters; contains 108 characters
36 | ERROR | Expected 1 space after the longest variable name
44 | ERROR | Expected "if (...) {\n"; found "if (...)\n {\n"
72 | ERROR | undocumented class found
80 | ERROR | Expected 1 space after the longest variable name
84 | ERROR | Expected "if (...) {\n"; found "if (...)\n {\n"
101 | ERROR | undocumented class found
106 | ERROR | Expected 1 space after the longest variable name
116 | ERROR | undocumented class found
--------------------------------------------------------------------------------
[/cc]
As you can see, the phpcs shows exactly where and how you didn't follow the coding standard. These rules are not only formatting rules, they also measure
cyclomatic complexity, which highly refers to the readability and maintainability of your code.
A coder with great discipline and taste does not commit any code that raises even a warning. If you act this way, then you'll find yourself fully documenting all of your code and writing them in the same manner everywhere.
PHPmd
This is the project site of PHPMD. It is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD. PHPMD can be seen as an user friendly frontend application for the raw metrics stream measured by PHP Depend.
What PHPMD does is: It takes a given PHP source code base and look for several potential problems within that source. These problems can be things like:
- Possible bugs
- Suboptimal code
- Overcomplicated expressions
- Unused parameters, methods, properties
PHPMD is currently in an early development stage and so it only provides a limited set of pre defined rules that detect bad smells within the analysed source code.
phpmd.org
This tool will be your second best friend in dealing with legacy code and a silent hunter if you have done a good job.
Installation
[cc lang="bash" line_numbers="off"]
sudo pear channel-discover pear.phpmd.org
sudo pear channel-discover pear.pdepend.org
#the version may differ, please review
sudo pear install --alldeps phpmd/PHP_PMD-0.2.4
[/cc]
Usage
[cc lang="bash" line_numbers="off" escaped="true"]
phpmd <path/to/directory/or/file> <xml | text | html> <codesize,naming,unusedcode> --reportfile /path/to/report/file
[/cc]
For local running and examining you would run the following command in your project root:
[cc lang="bash" line_numbers="off" escaped="true"]
phpmd . text codesize,naming,unusedcode
[/cc]
What does it do?
phpmd analyses your code and warns whenever it finds a variable name too short, too long, a class or method with a big mass, big cyclomatic complexity and so on. You'll get a detailed list of points where some rethinking or refactoring should be considered.
[cc lang="bash" line_numbers="off"]
legacy.php:2 This class has a bunch of public methods and attributes
legacy.php:2 This class has too many methods, consider refactoring it.
legacy.php:6 The method preExecuteHook() has a Cyclomatic Complexity of 22.
legacy.php:6 The method preExecuteHook() has an NPath complexity of 6440.
legacy.php:6 Avoid really long methods.
legacy.php:36 Avoid variables with short names like $br
legacy.php:72 Avoid variables with short names like $aw
legacy.php:163 Avoid variables with short names like $fm
legacy.php:174 Avoid unused local variables such as '$_POST'.
[/cc]
If you think that a warning/error should not be considered as one, then you can explicit tell phpmd to skip these.
[cc lines="-1" lang="php" escaped="true"]
/**
* This will suppress all the PMD warnings in
* this class.
*
* @SuppressWarnings(PHPMD)
*/
class Bar
{
function foo()
{
$baz = 23;
}
}
[/cc]
[cc lines="-1" lang="php" escaped="true"]
/**
* Example class for supressing method errors
*/
class Bar
{
/**
* This will suppress UnusedLocalVariable
* warnings in this method
*
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
*/
public function foo()
{
$baz = 42;
}
}
[/cc]
What are these messages and what do they mean?
Current Rulesets
List of rulesets and rules contained in each ruleset.
-
Code Size Rules: The Code Size Ruleset contains a collection of rules that find code size related problems.
-
Naming Rules: The Naming Ruleset contains a collection of rules about names - too long, too short, and so forth.
-
Unused Code Rules: The Unused Code Ruleset contains a collection of rules that find unused code.
Code Size Rules
-
CyclomaticComplexity: Complexity is determined by the number of decision points in a method plus one for the method entry. The decision points are 'if', 'while', 'for', and 'case labels'. Generally, 1-4 is low complexity, 5-7 indicates moderate complexity, 8-10 is high complexity, and 11+ is very high complexity.
-
NPathComplexity: The NPath complexity of a method is the number of acyclic execution paths through that method. A threshold of 200 is generally considered the point where measures should be taken to reduce complexity.
-
ExcessiveMethodLength: Violations of this rule usually indicate that the method is doing too much. Try to reduce the method size by creating helper methods and removing any copy/pasted code.
-
ExcessiveClassLength: Long Class files are indications that the class may be trying to do too much. Try to break it down, and reduce the size to something manageable.
-
ExcessiveParameterList: Long parameter lists can indicate that a new object should be created to wrap the numerous parameters. Basically, try to group the parameters together.
-
ExcessivePublicCount: A large number of public methods and attributes declared in a class can indicate the class may need to be broken up as increased effort will be required to thoroughly test it.
-
TooManyFields: Classes that have too many fields could be redesigned to have fewer fields, possibly through some nested object grouping of some of the information. For example, a class with city/state/zip fields could instead have one Address field.
-
TooManyMethods: A class with too many methods is probably a good suspect for refactoring, in order to reduce its complexity and find a way to have more fine grained objects.
Naming Rules
-
ShortVariable: Detects when a field, local or parameter has a very short name.
-
LongVariable: Detects when a field, formal or local variable is declared with a long name.
-
ShortMethodName: Detects when very short method names are used.
-
ConstructorWithNameAsEnclosingClass: A constructor method should not have the same name as the enclosing class, consider to use the PHP 5 __construct method.
-
ConstantNamingConventions: Class/Interface constant nanmes should always be defined in uppercase.
-
BooleanGetMethodName: Looks for methods named 'getX()' with 'boolean' as the return type. The convention is to name these methods 'isX()' or 'hasX()'.
Unused Code Rules
-
UnusedPrivateField: Detects when a private field is declared and/or assigned a value, but not used.
-
UnusedLocalVariable: Detects when a local variable is declared and/or assigned, but not used.
-
UnusedPrivateMethod: Unused Private Method detects when a private method is declared but is unused.
-
UnusedFormalParameter: Avoid passing parameters to methods or constructors and then not using those parameters.
Maintainability
Ever tried changing functionality that bubbled through your code? Did you fix the same typo in hundreds (or maybe thousands) of files? I did. This time it was not my fault though, I was just asked to alter the site. (heh, some knowing what I refer to are possibly facepalming now)
Ok, so you are a smart fellow, the right conclusions are made:
DON'T COPY-PASTE!!! NOT EVER! Trust me, maintaining a copy-pasted code is the seventh hell. But what could you do to eliminate code duplication?
phpcpd
phpcpd stands for PHP Copy Paste Detector. It's a tool created and maintained by the author of PHPUnit:
Sebastian Bergmann.
Installation
[cc lang="bash" line_numbers="off"]
sudo pear channel-discover pear.phpunit.de
sudo pear channel-discover components.ez.no
sudo pear install --alldeps phpunit/phpcpd
[/cc]
Usage
[cc lang="bash" line_numbers="off" escaped="true"]
phpcpd <path/to/directory/or/file>
[/cc]
The default behaviour is that it detects code as duplicate if at least 70 tokens in a row are the same. This is good for a legacy project, but could still leave in some ugly stuff. If you're confident with your code, try keeping that level at 10-20 tokens.
[cc lang="bash" line_numbers="off" escaped="true"]
phpcpd --min-tokens 10 <path/to/directory/or/file>
[/cc]
If you don't like the idea of using tokens, then try lines
[cc lang="bash" line_numbers="off" escaped="true"]
phpcpd --min-lines 3 <path/to/directory/or/file>
[/cc]
What does it do?
The
phpcpd scans your code and searches for patterns that match. It can warn you on the screen, or generate a pmd style xml output for further usage.
[cc lang="bash" line_numbers="off" escaped="true"]
phpcpd 1.3.1 by Sebastian Bergmann.
Found 1 exact clones with 6 duplicated lines in 1 files:
- Http/CurlTest.php:308-314
Http/CurlTest.php:333-339
0.37% duplicated lines out of 1602 total lines of code.
[/cc]
This means, that I've copied some code from the lines 308-314 to 333-339. Let's look in the file.
[cc first_line="308" lines="-1" lang="php" escaped="true"]
$this->object->setReturnTransfer(true);
$this->object->setUrl(
'file://'.dirname(__FILE__).'/_files/curlExecuteTest.txt'
);
$actual = $this->object->execute();
$this->assertSame($expected, $actual);
[/cc]
[cc first_line="333" lines="-1" lang="php" escaped="true"]
$this->object->setReturnTransfer(true);
$this->object->setUrl(
'file://'.dirname(__FILE__).'/_files/curlExecuteTest.txt'
);
$actual = $this->object->execute();
$this->assertSame($expected, $actual);
[/cc]
So it's right! I know it's a small error, but it's still there and I'm strict!
Solution:
[cc first_line="309" lines="-1" lang="php" escaped="true"]
/**
* Prepares and calls an execute
*
* @param Curl $object The object to use
* @param string $url The url to call
* @param vool $isPost Flag
*
* @return string response
*/
private function runExecute(Curl $object, $url, $isPost=false)
{
$object->setPost($isPost);
$object->setReferrer(true);
$object->setUrl($url);
return $object->execute();
}//end runExecute()
[/cc]
And the duplicated code could be modified the following way:
[cc first_line="69" lines="-1" lang="php" escaped="true"]
$this->testFile = 'file://'.dirname(__FILE__).'/_files/curlExecuteTest.txt';
[/cc]
[cc first_line="332" lines="-1" lang="php" escaped="true"]
$actual = $this->runExecute($this->object, $this->testFile, false);
$this->assertSame($expected, $actual);
[/cc]
[cc first_line="351" lines="-1" lang="php" escaped="true"]
$actual = $this->runExecute($this->object, $this->testFile, true);
$this->assertSame($expected, $actual);
[/cc]
Testability
Guide: Writing Testable Code
To keep our code at Google in the best possible shape we provided our software engineers with these constant reminders. Now, we are happy to share them with the world.
Many thanks to these folks for inspiration and hours of hard work getting this guide done:
Also thanks to Blaine R Southam who has turned it into a
pdf book.
Flaw #1: Constructor does Real Work
Warning Signs
- new keyword in a constructor or at field declaration
- Static method calls in a constructor or at field declaration
- Anything more than field assignment in constructors
- Object not fully initialized after the constructor finishes (watch out for initialize methods)
- Control flow (conditional or looping logic) in a constructor
- Code does complex object graph construction inside a constructor rather than using a factory or builder
- Adding or using an initialization block
Flaw #2: Digging into Collaborators
Warning Signs
- Objects are passed in but never used directly (only used to get access to other objects)
- Law of Demeter violation: method call chain walks an object graph with more than one dot (.)
- Suspicious names: context, environment, principal, container, or manager
Flaw #3: Brittle Global State & Singletons
Warning Signs
- Adding or using singletons
- Adding or using static fields or static methods
- Adding or using static initialization blocks
- Adding or using registries
- Adding or using service locators
Flaw #4: Class Does Too Much
Warning Signs
- Summing up what the class does includes the word “and”
- Class would be challenging for new team members to read and quickly “get it”
- Class has fields that are only used in some methods
- Class has static methods that only operate on parameters
by Miško Hevery @
misko.hevery.com
You'll be amazed by the effect the previous tools will cause to the quality of your code. If you don't know anything about
design patterns, or extreme programming, or refactoring, you'll still end up in some sort of testable code. But the main parameter is:
how easy is to write test cases to your code?
The golden rule is, that
if a method/object/application is hard to test then
the implementation is bad, you need to restructure, refactor your code.
What is Refactoring?
Refactoring is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior. Its heart is a series of small behavior preserving transformations. Each transformation (called a 'refactoring') does little, but a sequence of transformations can produce a significant restructuring. Since each refactoring is small, it's less likely to go wrong. The system is also kept fully working after each small refactoring, reducing the chances that a system can get seriously broken during the restructuring. @refactoring.com
PHPUnit
The best tool for unit-testing php code is
PHPUnit. It's fully written in php by
Sebastian Bergmann. You can find a lot of useful stuff from him on
slideshare and his
webpage. My personal favorite is
QA in php projects, which covers almost the same topic as I do now. This slide has a great example for using PHPUnit while creating a bowling scoring system in php. (Bowling Kata)
Bowling Kata
We are going to create an object, BowlingGame, which, given a valid sequence of rolls for one line of American Ten-Pin Bowling, produces the total score for the game. This story is picked because it’s about the right size for a couple of hours of Test-Driven Development demonstration. Here are some things that the program will not do:
- We will not check for valid rolls.
- We will not check for correct number of rolls and frames.
- We will not provide scores for intermediate frames.
Depending on the application, this might or might not be a valid way to define a complete story, but we do it here for purposes of keeping the demonstration light. I think you’ll see that improvements like those above would go in readily if they were needed.
I’ll briefly summarize the scoring for this form of bowling:
- Each game, or “line” of bowling, includes ten turns, or “frames” for the bowler.
- In each frame, the bowler gets up to two tries to knock down all the pins.
- If in two tries, he fails to knock them all down, his score for that frame is the total number of pins knocked down in his two tries.
- If in two tries he knocks them all down, this is called a “spare” and his score for the frame is ten plus the number of pins knocked down on his next throw (in his next turn).
- If on his first try in the frame he knocks down all the pins, this is called a “strike”. His turn is over, and his score for the frame is ten plus the simple total of the pins knocked down in his next two rolls.
- If he gets a spare or strike in the last (tenth) frame, the bowler gets to throw one or two more bonus balls, respectively. These bonus throws are taken as part of the same turn. If the bonus throws knock down all the pins, the process does not repeat: the bonus throws are only used to calculate the score of the final frame.
- The game score is the total of all frame scores.
What makes this game interesting to score is the lookahead in the scoring for strike and spare. At the time we throw a strike or spare, we cannot calculate the frame score: we have to wait one or two frames to find out what the bonus is.
That should be all you need to know about the game. If, after reading this article, you remain confused, please let me know via email so that I can improve the writeup. @xprogramming.com
So let's install PHPUnit
Installation
[cc lang="bash" line_numbers="off"]
sudo pear channel-discover pear.phpunit.de
sudo pear channel-discover pear.symfony-project.com
sudo pear install --alldeps phpunit/phpunit
[/cc]
Usage
PHPUnit has no effect if you don't write tests. Tests for code has several depths that should be covered during coding. If you're dealing with existing legacy code, then you'll probably try going from
top to bottom. If you're creating a new software, then you'll proceed from
bottom to up.
The list follows:
-
System testing - Testing conducted on a complete, integrated system to evaluate the system's compliance with its specified requirements. System testing falls within the scope of black box testing, and as such, should require no knowledge of the inner design of the code or logic.
-
Functional testing - Validating an application or Web site conforms to its specifications and correctly performs all its required functions. This entails a series of tests which perform a feature by feature validation of behaviour, using a wide range of normal and erroneous input data. This can involve testing of the product's user interface, APIs, database management, security, installation, networking, etc. Testing can be performed on an automated or manual basis using black box or white box methodologies.
-
Integration testing - Testing in which modules are combined and tested as a group. Modules are typically code modules, individual applications, client and server applications on a network, etc. Integration Testing follows unit testing and precedes system testing.
-
Unit testing - Functional and reliability testing in an Engineering environment. Producing tests for the behaviour of components of a product to ensure their correct behaviour prior to system integration.
-
Micro testing - Functional testing in Engineering environment. Producing tests for methods of an object to ensure that it does what it's meant to do.
I would like to offer the agile and XP communities a new word for an old idea: Microtest instead of Unit Test.
My rationale for this suggestion is straightforward. Unit test already has a meaning out there in the development world, and that meaning doesn’t match the agile/XP meaning. There are several aspects of an ordinary microtest that can be entirely absent from a normal unit test. Traveling around as I do, I get to work with an enormous number of teams, and I can tell you from extensive experience, teaching people about XP style testing is notably easier when we don’t have to repurpose a word that’s already in play.
A microtest has the following properties:
- It is short, typically under a dozen lines of code.
- It is always automated.
- It does not test the object inside the running app, but instead in a purpose-built testing application.
- It invokes only a tiny portion of the code, most usually a single branch of a single function.
- It is written gray-box, i.e. it reads as if it were black-box, but sometimes takes advantage of white-box knowledge. (Typically a critical factor in avoiding combinatoric issues.)
- It is coded to the same standard as shipping code, i.e. the team’s best current understanding of coding excellence.
- It is vault-committed source, with a lifetime co-terminous with the functionality it tests.
- In combination with all other microtests of an app, it serves as a ‘gateway-to-commit’. That is, a developer is encouraged to commit anytime all microtests run green, and discouraged (strongly, even nastily) to commit otherwise.
- It takes complete control of the object-under-test and is therefore self-contained, i.e. running with no dependencies on anything other than the testing code and its dependency graph.
- It runs in an extremely short time, milliseconds per test.
- It provides precise feedback on any errors that it encounters.
- It usually (not always) runs entirely inside a single computer.
- It usually (not always) runs entirely inside a single process, i.e. with few extra-process runtime dependencies.
- It is part of a collection all or any subset of which is invokable with a single programmer gesture.
- It is written before the code-change it is meant to test.
- It avoids most or all usage of ‘awkward’ collaborators via a variety of slip-and-fake techniques.
- It rarely involves construction of more than a few classes of object, often one or two, usually under five.
Over the years I’ve had some pretty comedic conversations with people. One fellow was quite irate and insisted that the list above was just the definition of a good unit test. Maybe microtest just means good unit test.
By @geepawhill on anarchycreek.com
This tutorial is not intended to teach you to write good tests, but there's a massive amount of links on the bottom you can learn from. The focus is on the tools!
So how to use phpunit?
[cc lang="bash" line_numbers="off" escaped="true"]
phpunit <path/to/directory/or/file/to/test>
[/cc]
Of course this is pretty uncomfortable if the number of tests are growing, but there's a config xml you can create to define all your needs.
Before we process, we need to declare, that the examples below assumes you have a project root with an src and a tests folder!!!
- projectbase
- src/- <your application source>
- tests/ - <unit tests to your source>
- phpunit.xml
[cc lines="-1" lang="xml" escaped="true"]
tests
[/cc]
If you name this file
phpunit.xml, then it is automatically recognized if you run the command from the same directory. If not, you can use the following:
[cc lang="bash" line_numbers="off" escaped="true"]
phpunit --configuration <path/to/configuration/xml>
[/cc]
When running on command line, I prefer to see the textdox styled report, you can try it out by issuing:
[cc lang="bash" line_numbers="off" escaped="true"]
phpunit --testdox --configuration <path/to/configuration/xml>
[/cc]
Putting it all together
You're probably wondering how much time will it take to run all these tools one-by-one after every modification of the code. It is a huge overhead. You're right. You can probably write a nice little console script to do all the dirty stuff for you, but I'll show something more publicly acceptable.
ANT
Apache Ant is a Java-based build tool. In theory, it is kind of like make, without make's wrinkles.
Installation
Ant is a Java tool, so in order to run it, you need at least a runtime environment. (get it from
java.com)
Ant has been used successfully on many platforms, including Linux, commercial flavours of Unix such as Solaris and HP-UX, Windows NT-platforms, OS/2 Warp, Novell Netware 6, OpenVMS and MacOS X. The platforms used most for development are, in no particular order, Linux, MacOS X, Windows XP and Unix; these are therefore that platforms that tend to work best. As of Ant1.7, Windows 9x is no longer supported.
To build and use Ant, you must have a JAXP-compliant XML parser installed and available on your classpath, such as Xerces.
The binary distribution of Ant includes the latest version of the Apache Xerces2 XML parser. Please see http://java.sun.com/xml/ for more information about JAXP. If you wish to use a different JAXP-compliant parser, you should remove xercesImpl.jar and xml-apis.jar from Ant's lib directory.
You can then either put the JARs of your preferred parser into Ant's lib directory or put the jars on the system classpath. Some parts of Ant will fail if you use an old parser, especially one that is not namespace-aware. In particular, avoid the Crimson parser.
Tip: "ant -diagnostics" will list the XML parser used and its location.
For the current version of Ant, you will also need a JDK installed on your system, version 1.4 or later required, 1.5 or later strongly recommended. The later the version of Java , the more Ant tasks you get.
Note: If a JDK is not present, only the JRE runtime, then many tasks will not work.
Note: Ant 1.8.* works with jdk1.4 and higher, Ant 1.7.* works with jdk1.3 and higher, Ant 1.6.* works with jdk 1.2 and higher, Ant 1.2 to Ant 1.5.* work with jdk 1.1 and higher.
For most Unix systems both Java and Ant could be accessed via the main software repository. For OSX, (Snow Leopard at least) both are pre-installed.
For others, you can grab a version from
http://ant.apache.org/bindownload.cgi
Usage
Ant works from an xml that defines the steps that it should make in order to
make the software. For your php project you'll need the following
build.xml in the root of the project:
- projectbase
- src/- <your application source>
- tests/ - <unit tests to your source>
- phpunit.xml
- build.xml
So the build xml:
[cc lines="-1" lang="xml" escaped="true"]
[/cc]
If the build.xml is in it's place, all you have to do is to issue the command:
[cc lang="bash" line_numbers="off" escaped="true"]
ant
[/cc]
If everything went OK, you'll see an incremental output of all your wired tools, with all nececerry information. The term
failonerror ensures that if one of the tools exited with a warning or error, the process stops after running the tool, and the build fails. If this behaviour is not acceptable to your taste, then switch these attribute to false.
For running only phpunit
[cc lang="bash" line_numbers="off" escaped="true"]
ant phpunit
[/cc]
For phpcs
[cc lang="bash" line_numbers="off" escaped="true"]
ant phpcs
[/cc]
If you want to get informed about the possible targets, run
[cc lang="bash" line_numbers="off" escaped="true"]
ant -projecthelp
[/cc]
Integrating ant
Most modern IDEs are capable of handling ant build processes. Eclipse for example can run a specified task on every save action. The common form is right clicking on build.xml and run target -> <selected target> and an output window/pane comes up and you'll see the results.
Literature
Hopefully this post was able to kick-start you on the path of XP, and gave you a good insight on what you should be practising over the next couple of years. (Yes, it's a huge process to master this way of thinking). But there's help! You can always ask me, or any evangelist out there! And you can always use your google-fu for words like:
"extreme programming", "tdd", "test driven development", "unit testing", "design pattern", "solid development principles", "refactoring", "legacy code", "Misko Hevery", "Martin Fawler", "Kent Beck", "Michael Feathers", "xUnit patterns"
Or you could start reading these links:
Faces:
- Robert C Martin "Uncle Bob"
Books
happy coding