This post is password protected. You must visit the website and enter the password to continue reading.
Category Archives: Planet Ibuildings
-
[Shashikant Jagtap]Protected: The Agile Tester January 29, 2012
-
[Shashikant Jagtap]Web Acceptance Testing Framework for PHP January 13, 2012
Web Acceptance Testing Framework for PHP
A Web acceptance testing framework for PHP is now available on GitHub. It can be used for web acceptance testing of the PHP projects. This framework is build around Behat, Mink, Sauce Labs and ANT. You can configure it with a continuous integration server like Jenkins.

Behat is a BDD framework for PHP. Behat comes up with Mink which is used for browser emulation (functional testing) where browser interaction takes place. Behat and Mink combination is used for web acceptance testing.
Key features of web acceptance testing framework
- Selenium and Sahi servers are plugged into build file, user doesn’t need to download or launch Selenium or Sahi server while running features with ANT or Jenkins.
- Test reports are generated in HTML and xml format in “/report” directory.
- Easy switching over to another driver. You can run tests using any available drivers, just by specifying name of them.
- Framework is integrated with cloud testing service called “Sauce Labs”. You can run your features in cloud with selenium driver.
- Testers can get started easily without any hassles.
- With little changes in config file, it is ready for your project.
Behat for Testers
Testers might have tried Cucumber & Selenium or Cucumber & capybara combination for Ruby projects. BDD for testers could be very interesting topic to discuss. Testers can now use BDD framework for PHP project as well. Thanks to Behat !
Behat has provided lot of driver options for testers. Its not limited to selenium. It has following mink drivers.
Tester can pick one of these and start implementing features. Goutte can be used for tests which doesn’t need browser interaction. Tester can always switch to another driver if feature is failing for one of the driver. There are some pro’s and con’s of each driver
Sahi Selenium Webdriver
Implicit Wait Yes No Yes Hidden Link Access Yes Yes No Sauce Lab Support No Yes No Speed Medium Medium Fast Https sites testing Hectic, need to add certificate exception Accepts certificated automatically Accepts certificated automatically It is very important to choose right driver for your testing. It depends on your application and requirement. This framework makes it easy to switch over to another driver.
What you need to use framework?
Install Behat using Behat documentation which explained all these methods in simple way. Similarly install mink using Mink documentation.
- Create Your Project
Initiate Behat:
$ behat --init
- Clone Web Acceptance testing Framework from GitHub
- Clone test framework from Github into your local machine.
- Make sure you have initialized Behat in your root directory, currently its “Behat”.
- Now you are ready to use the framework for adding features and running them with different drivers like Selenium, Sahi or webdriver.
- How to use framework?
- Change “base_url” “browser” parameter in all config files like ‘behat.yml’, ‘sahi.yml’, ‘selenium.yml’, and ‘webdriver.yml’ as per your project requirement. eg change in sahi.yml file as shown below
default: context: parameters: javascript_session: sahi base_url: http://{your url} browser: {your browser} show_cmd: open %sRun features locally
To run feature locally, you need to start Selenium or Sahi server before executing them.
Download Selenium or Sahi and launch Selenium or Sahi server as shown below
Selenium Server
You need to download selenium server jar file and execute following command:$ cd /path/to/selenium-server $java -jar selenium-server-standalone-2.15.0.jar
Sahi Server
Download sahi zip file from SourceForgeNow launch Sahi Server using command below:
$ cd /path/to/sahi $ cd userdata/bin $bash start_sahi.sh
Now you can run behat command to run feature locally
$ cd /path/to/behat $behat --name wikiSearch (your feature)
You can see feature running on your local host.
- How to run features using ANT
Requirement
You need to install Apache Ant on your localhost. You can download and install Ant from Apache Ant website.No need install or download selenium or Sahi. It is configured inside this framework. Ant will automatically launch selenium or Sahi server still you need to update version of selenium server frequently.
Now you got three drivers options to run your features
- Sahi Driver
- Selenium Driver
- Selenium 2 Driver (webdriver)
- Run feature with selenium driver
$ cd /path/to/my/project $ant runSelenium
- Run feature with Sahi driver.
$ cd /path/to/my/project $ant runSahi
- Run feature with webdriver
$ cd /path/to/my/project $ant runWebdriver
- Run feature on Sauce Labs
$ cd /path/to/my/project $ant sauceTests
If you are using selenium driver, then you have to implement wait in your step definitions every time there is new page is loaded like this:
$this->getMink()->getSession()->wait("3000");Sahi driver waits automatically for element to be appear on page or page to load. You don’t have to implement wait if you would like to use Sahi driver.
Once you run ‘ant’ command from terminal you will see your feature running in browser, you configured.You will see reports generated in “/report” directory. There will be HTML reports as well.
Configure Jenkins to run features
- Download & Launch Jenkins by executing below command:
$ java -jar jenkins.war
Visit url : http://localhost:8080 to access Jenkins in browser. You can specify different port to launch Jenkins.
- Create new Build a free-style software project and name it “Behat”
- Configure job by specifying your repository.(Git, SVN, CSV)
- You need to Invoke Ant as shown below
If you wish to run tests using Sahi driver then specify target as “runSahi”
You can see reports generated once you run your feature.
Conclusion
Using web acceptance test framework for PHP, you can write features and run them easily on ANT, Jenkins and Sauce labs with choice of available drivers.
Happy BDD for testers !! Any questions or comment mention me @Shashikant86 on Twitter.
-
[Shashikant Jagtap]Behat with Sauce Labs and Jenkins January 13, 2012
Abstract:
The key benefits of Behavior-Driven-Development (BDD) practices are communication enhancement and customer satisfaction. You can read more about that by Dan North and Gojko Adzic. Perhaps the biggest practical challenge in the way of reaping those benefits is the burden of provisioning, installation and maintenance of requisite complex and fussy infrastructure. The recent availability of latest CI servers like Jenkins & cloud based testing services like Sauce Labs carries the potential to remove that barrier. This post discusses and shows how to integrate Behat an emerging BDD framework for PHP with continuous integration server like Jenkins and cloud based testing services like Sauce Labs.
What is Behat?
Behat is a BDD framework for PHP. There are some tools available for BDD like Cucumber for Ruby, SpecFlow for .NET and Lettuce for Python. Behat is the first BDD tool for PHP applications. Developers can also use the PHPSpec framework to implement classes within the Behat projects. Behat is written in PHP by Konstantin Kudryashov. With Behat, you can write human readable stories which turns as tests to run against your application. Behat can be used for API testing, functional testing and data-driven testing. Developers will do API testing and we will carry on with functional testing (web acceptance testing) with Behat.
Functional Testing with Behat and Mink
Behat is used for acceptance testing (any tests) by executing a Gherkin scenario. Developers can implement integrated classes. Testers start thinking of more workflow level and technical level steps (actions) which turns as scenarios for features. Once tester started to think of Web Acceptance Testing (functional testing) with browser interaction then another tool called “Mink” comes into the picture.
Mink is used for browser emulation (functional testing) where browser interaction takes place. As of now, there are following Selenium drivers available for browser emulation.
- Selenium 1 Driver provides bridge for Selenium RC (Selenium 1).
- Selenium 2 driver provides bridge for Selenium 2. (Facebook webdriver) for PHP.
Behat In Action:
You must have pear installed in order to proceed with Behat installation. Now we will run these commands from your terminal window:
$ sudo pear channel-discover pear.behat.org $ sudo pear channel-discover pear.symfony.com $ sudo pear install behat/gherkin-beta $ sudo pear install behat/behat-beta
Test your installation by running this command:
$ behat --version Behat version 2.2.0
Now let’s install Mink, run following commands from terminal window:
$ pear channel-discover pear.symfony.com $ pear channel-discover pear.behat.org $ pear install behat/mink
Mink is ready to use. We have to include “mink/autoload.php” in your classes as shown below:
require_once 'mink/autoload.php';
Start Your Project
Navigate to project root directory and initialize Behat by running these commands:
$ cd /path/to/my/project $ ls application $ behat --init +d features - place your *.feature files here +d features/bootstrap - place bootstrap scripts and static files here +f features/bootstrap/FeatureContext.php - place your feature related code here $ ls application features $cd features $ ls bootstrap $cd bootstrap $ls FeatureContext.php
This will create “features” directory and “bootstrap/FeatureContext.php” for you. Now we will directly jump to the project created with a feature file.
You can use NetBeans with installed Cucumber plugin for Gherkin syntax highlighting. Project structure will look like this:
Directory Structure
Behat has already created “features” directory and “features/bootstrap” directory with “FeatureContext.php” in it.
bootstrap.php
We can this create file in define some constants and some third-party libraries which needs to be included in class files. This file should look like this:
<?php date_default_timezone_set('Europe/London'); require_once 'mink/autoload.php'; require_once 'PHPUnit/Autoload.php'; require_once 'PHPUnit/Framework/Assert/Functions.php'; require_once 'PHPUnit/Extensions/SeleniumTestCase.php'; require_once 'PHPUnit/Extensions/SeleniumTestCase/SauceOnDemandTestCase.php';behat.yml
This file is a default config file that Behat uses to execute features. Example of behat.yml is shown below:
default: context: parameters: javascript_session: selenium base_url: http://en.wikipedia.org/wiki/Main_Page browser: firefox show_cmd: open %s
You can change the drivers by changing the “javascript_session” parameter. It can be “selenium” or “webdriver”.
sauce.yml
This file is used for running features on Sauce Labs. The code for this file is explained in “behat and Sauce Labs” section below.
build.xml
This file is used for running features with ANT. We can use this ANT file to plug into Jenkins. Simple ANT file should look like this:
<project name="behat" default= "behat" basedir=""> <delete dir="${basedir}/report" /> <mkdir dir="${basedir}/report"/> <target name="behat"> <exec dir="${basedir}" executable="behat" failonerror="true"> <arg line="-f junit --out ${basedir}/report"/> </exec> </target> <target name="create-test-report" description="Generate reports for executed JUnit tests."> <junitreport todir="./report"> <fileset dir="${basedir}/report"> <include name="TEST-*.xml"/> </fileset> <report format="frames" todir="./report/html"/> </junitreport> </target> </project>report
This directory is used to store *.xml reports generated by Behat’s “junit” formatter. We can use this reports to plug into Jenkins.
Start your Engine
Remember, you have to download latest version of selenium server. Now navigate to directory where you saved selenium server .jar file. You have to launch it using command shown below:
java -jar selenium-server-standalone-2.15.0.jar
Behat & Sauce Labs
Sauce Labs is a cloud testing service which allows selenium tests to run in the cloud. Sauce Labs allocates machines and browsers for your tests, capture screenshot for every step and record video of all jobs(tests). You don’t need to setup separate machines to run tests. Sauce labs helps us to write tests without complex infrastructure.
In order to integrate Behat with Sauce Labs, you need to have an account with Sauce Labs. You need “Username” and “API Key” to plug them into a config file.
Behat executes features with “behat.yml” file by default, but we can run features with any other configuration file. We can create another configuration file like “sauce.yml” to run features on Sauce Labs. Example “sauce.yml” should look like this:
default: context: parameters: default_session: goutte javascript_session: selenium base_url: http://en.wikipedia.org/wiki/Main_Page browser: firefox selenium: host: ondemand.saucelabs.com port: 80 browser: > { "username": "your username", "access-key": "your API key", "browser": "firefox", "browser-version": "7", "os": "Windows 2003", "name": "Testing Selenium with Behat" }We will use “sauce.yml” as a config file to run features on Sauce Labs. If you wish to run all features from “features” directory on Sauce labs, you can use this command:
behat -c sauce.yml
When to implement step definitions?
- If you can speak fluent Gherkin, then you don’t need to write code. Behat/Mink will understand Gherkin and run your features without suggesting step definitions.
- If features written by someone else, you can take full advantage of Mink API’s in order to implement step definitions suggested by Behat/Mink.
- It’s very important to write good Gherkin to write minimum code.
- It’s very easy to access Mink API’s by writing simple code.
Now we will see how will you “click” particular element on page. You need to use Xpath as a locator for that element. Mink will suggest some step definitions, you need to complete it like this:
/** * @Given /^I click Something$/ */ public function iClickSomething() { $this->getMink()->getSession()->getDriver()->click("//Xpath"); }Example : Feature wikiSearch
We will write simple feature to add product into shopping cart. Feature will look like this:
Feature: wikiSearch In order to search information on wiki As a Wiki user I want to get sensible results from site @javascript Scenario Outline: Search Keywords on Google Given I am on "/" And I fill in searchBox with "<input>" When I press search button Then I should see "<output>" Examples: | input | output | | London | lʌndən/ | | NewYork | nɪu ˈjɔək | | Sydney | sɪdni/ | | Mumbai | मुंबई | | Bejing | 北京 | | Tokyo | 東京 | | Lahore | لاہور | | Paris | paʁi |
Feature Explained
A feature file mentioned above is a good example of data-driven testing. This feature will execute our scenario for 8 different data set mentioned in the example section. This feature will have following steps
- User will navigate to Wikipedia main page.
- User enter “London” in search box.
- User will check if that page has city name in their local language as described in output.
- This test will run for 8 different cities as shown in “examples” section of feature.
This feature is also good example of testing internationalization as it consist of test data (examples) in different languages.
Now we will run this feature using command:
behat --name wikiSearch
Remember, we are running it locally for now using default config file “behat.yml” with Selenium driver. After executing above command, we will get some step definitions suggested by Behat/Mink
Feature: wikiSearch In order to search information on wiki As a Wiki user I want to get sensible results from site @javascript Scenario Outline: Search Keywords on Wiki # features/wikiSearch.feature:8 Given I am on "/" # FeatureContext::visit() And I fill in searchBox with "<input>" When I press search button Then I should see "<output>" # FeatureContext::assertPageContainsText() Examples: | input | output | | London | lʌndən/ | Undefined step "I fill in searchBox with "London"" Undefined step "I press search button" | NewYork | nɪu ˈjɔək | Undefined step "I fill in searchBox with "NewYork"" Undefined step "I press search button" | Sydney | sɪdni/ | Undefined step "I fill in searchBox with "Sydney"" Undefined step "I press search button" | Mumbai | मुंबई | Undefined step "I fill in searchBox with "Mumbai"" Undefined step "I press search button" | Bejing | 北京 | Undefined step "I fill in searchBox with "Bejing"" Undefined step "I press search button" | Tokyo | 東京 | Undefined step "I fill in searchBox with "Tokyo"" Undefined step "I press search button" | Lahore | لاہور | Undefined step "I fill in searchBox with "Lahore"" Undefined step "I press search button" | Paris | paʁi | Undefined step "I fill in searchBox with "Paris"" Undefined step "I press search button" 8 scenarios (8 undefined) 32 steps (8 passed, 8 skipped, 16 undefined) 0m15.771s You can implement step definitions for undefined steps with these snippets: /** * @Given /^I fill in searchBox with "([^"]*)"$/ */ public function iFillInSearchboxWith($argument1) { throw new PendingException(); } /** * @When /^I press search button$/ */ public function iPressSearchButton() { throw new PendingException(); }You can see above, Behat/Mink has suggested some step definitions for undefined steps. We can implement these step definitions using Mink in “bootstrap/FeatureContext.php” file. We can implement first step definition “iFillInSearchboxWith($argument1)” like this:
/** * @Given /^I fill in searchBox with "([^"]*)"$/ */ /* public function iFillInSearchboxWith($input) { $this->fillField("searchInput",$input); }Now we will run “behat”command again, you can see eight step is passed. Terminal window output should be like this:
/Feature: wikiSearch In order to search information on wiki As a Wiki user I want to get sensible results from site @javascript Scenario Outline: Search Keywords on wiki # features/wikiSearch.feature:7 Given I am on "/" # FeatureContext::visit() And I fill in searchBox with "<input>" # FeatureContext::iFillInSearchboxWith() When I press search button Then I should see "<output>" # FeatureContext::assertPageContainsText() Examples: | input | output | | London | lʌndən/ | Undefined step "I press search button" | NewYork | nɪu ˈjɔək | Undefined step "I press search button" | Sydney | sɪdni/ | Undefined step "I press search button" | Mumbai | मुंबई | Undefined step "I press search button" | Bejing | 北京 | Undefined step "I press search button" | Tokyo | 東京 | Undefined step "I press search button" | Lahore | لاہور | Undefined step "I press search button" | Paris | paʁi | Undefined step "I press search button" 8 scenarios (8 undefined) 32 steps (16 passed, 8 skipped, 8 undefined) 0m25.568s You can implement step definitions for undefined steps with these snippets: /** * @When /^I press search button$/ */ public function iPressSearchButton() { throw new PendingException(); }We have to repeat this process until all steps get “passed”. We can implement these steps by adding some code in “bootstrap/FeatureContext.php” file. The code will look like this:
<?php use Behat\Behat\Context\ClosuredContextInterface, Behat\Behat\Context\TranslatedContextInterface, Behat\Behat\Context\BehatContext, Behat\Behat\Exception\PendingException; use Behat\Gherkin\Node\PyStringNode, Behat\Gherkin\Node\TableNode; use Behat\Mink\Behat\Context\MinkContext; use Behat\Mink\Session; use Behat\Mink\Driver\DriverInterface; require_once 'bootstrap.php'; /** * Features context. */ class FeatureContext extends MinkContext { /** * @Given /^I fill in searchBox with "([^"]*)"$/ */ public function iFillInSearchboxWith($input) { $this->fillField("searchInput",$input); } /** * @When /^I press search button$/ */ public function iPressSearchButton() { $this->getMink()->getSession()->getDriver()->click("//*[@id='searchButton']"); $this->getMink()->getSession()->wait("3000"); }After implementation, we have to run “behat” command again. You will see this:
Feature: wikiSearch In order to search information on wiki As a Wiki user I want to get sensible results from site @javascript Scenario Outline: Search Keywords on wiki # features/wikiSearch.feature:8 Given I am on "/" # FeatureContext::visit() And I fill in searchBox with "<input>" # FeatureContext::iFillInSearchboxWith() When I press search button # FeatureContext::iPressSearchButton() Then I should see "<output>" # FeatureContext::assertPageContainsText() Examples: | input | output | | London | lʌndən/ | | NewYork | nɪu ˈjɔək | | Sydney | sɪdni/ | | Mumbai | मुंबई | | Bejing | 北京 | | Tokyo | 東京 | | Lahore | لاہور | | Paris | paʁi | 8 scenarios (8 passed) 32 steps (32 passed) 0m42.794s
We managed to get all our scenario/steps “passed”. It’s time to login into your Sauce Labs account to see this scenario running on Sauce Labs.
Now we have to use “sauce.yml” config file. We will run below mentioned command from terminal and we can see output on Sauce Labs as shown below:
behat -c sauce.yml
Screenshots for feature running on Sauce Labs:
You can see detail steps, screenshots and video of this job on Sauce Labs
See terminal output as shown below:Building Features with Jenkins
Continuous Integration (CI) is one of the best practice in agile projects to detect bugs early. Each integration is verified by an automated build to detect integration errors as quickly as possible. Now we will see how we can integrate Jenkins to our behat project.
Don’t forget to start your engine before building project in Jenkins. You have start Selenium/WebDriver before starting Jenkins.
There is “/report” directory to save JUnit reports in xml format generated by Behat. You can generate reports using this command:
behat -f junit --out ~/pathto/report
To continue, you need Jenkins installed on your machine. Now start Jenkins by executing *.war file from terminal on port 8080. You also need to visit “http://localhost:8080″ to see Jenkins GUI.
$ java -jar jenkins.war
- Visit http://localhost:8080/ and you should see Jenkins Dashboard.
- Create “New Job” for behat project.
- Specify your SCM to create Jenkin’s workspace on your system.
- You can specify build file as per your project structure. In here I have configured in “Acne” project by specifying build file path and test reports like this.
- Save your configuration. Go back to project and click”Build Now”, you will see:
- You can see features running in browser.
- Sit back and enjoy your features running on Jenkins till it finishes job.
- Few minutes later, you will see your build is “Green”:
- Watch your test results:
Behat, Sauce Labs and Jenkins
You can run all your features on Sauce Labs just by updating ANT file like:
<project name="behat" default= "behat" basedir=""> <delete dir="${basedir}/report" /> <mkdir dir="${basedir}/report"/> <target name="behat"> <exec dir="${basedir}" executable="behat" failonerror="true"> <arg line="-c sauce.yml -f junit --out ${basedir}/report"/> </exec> </target> <target name="create-test-report" description="Generate reports for executed JUnit tests."> <junitreport todir="./report"> <fileset dir="${basedir}/report"> <include name="TEST-*.xml"/> </fileset> <report format="frames" todir="./report/html"/> </junitreport> </target> </project>Alternatively, you can configure Sauce On demand tunnel into your Jenkins.
Conclusion:
We can write web acceptance tests with Behat and Mink combination. We can plug them into Sauce Labs with config file (“sauce.yml”) and run them on a CI server. Now you can implement BDD practices for PHP applications by taking benefit from Bahat and Sauce Labs.
Demo
You can watch video demonstration of this blog on Vimeo and YouTube. More about me
-
[Shashikant Jagtap]Agile Testing with Behat, Sauce and Jenkins January 13, 2012
Abstract:
The key benefits of Behavior-Driven-Development (BDD) practices are communication enhancement and customer satisfaction. You can read more about that by Dan North and Gojko Adzic. Perhaps the biggest practical challenge in the way of reaping those benefits is the burden of provisioning, installation and maintenance of requisite complex and fussy infrastructure. The recent availability of latest CI servers like Jenkins & cloud based testing services like Sauce Labs carries the potential to remove that barrier. This post discusses and shows how to integrate Behat an emerging BDD framework for PHP with continuous integration server like Jenkins and cloud based testing services like Sauce Labs.
What is Behat?
Behat is a BDD framework for PHP. There are some tools available for BDD like Cucumber for Ruby, SpecFlow for .NET and Lettuce for Python. Behat is the first BDD tool for PHP applications. Developers can also use PHPSpec framework to implement classes underneath Behat for PHP & Zend Framework applications. Behat is written in PHP by Konstantin Kudryashov. With Behat, you can write human readable stories which turns as tests to run against your application. Behat can be used for API testing, functional testing and data-driven testing. Developers will do API testing and we will carry on with functional testing (web acceptance testing) with Behat.
Functional Testing with Behat and Mink
Behat is used for acceptance testing (any tests) by executing a Gherkin scenario. Developers can implement integrated classes. Testers start thinking of more workflow level and technical level steps (actions) which turns as scenarios for features. Once tester started to think of Web Acceptance Testing (functional testing) with browser interaction then another tool called “Mink” comes into the picture.
Mink is used for browser emulation (functional testing) where browser interaction takes place. As of now, there are following Selenium drivers available for browser emulation.
- Selenium 1 Driver provides bridge for Selenium RC (Selenium 1).
- Selenium 2 driver provides bridge for Selenium 2. (Facebook webdriver) for PHP.
Behat In Action:
You must have pear installed in order to proceed with Behat installation. Now we will run these commands from your terminal window:
$ sudo pear channel-discover pear.behat.org $ sudo pear channel-discover pear.symfony.com $ sudo pear install behat/gherkin-beta $ sudo pear install behat/behat-beta
Test your installation by running this command:
$ behat --version Behat version 2.2.0
Now let’s install Mink, run following commands from terminal window:
$ pear channel-discover pear.symfony.com $ pear channel-discover pear.behat.org $ pear install behat/mink
Mink is ready to use. We have to include “mink/autoload.php” in your classes as shown below:
require_once 'mink/autoload.php';
Start Your Project
Navigate to project root directory and initialize Behat by running these commands:
$ cd /path/to/my/project $ ls application $ behat --init +d features - place your *.feature files here +d features/bootstrap - place bootstrap scripts and static files here +f features/bootstrap/FeatureContext.php - place your feature related code here $ ls application features $cd features $ ls bootstrap $cd bootstrap $ls FeatureContext.php
This will create “features” directory and “bootstrap/FeatureContext.php” for you. Now we will directly jump to the project created with some feature files.
You can use NetBeans with installed Cucumber plugin for Gherkin syntax highlighting. Project structure will look like this:
Directory Structure Explained
Behat has already created “features” directory and “features/bootstrap” directory with “FeatureContext.php” in it.
bootstrap.php
We can this create file in define some constants and some third-party libraries which needs to be included in class files. This file should look like this:
<?php date_default_timezone_set('Europe/London'); require_once 'mink/autoload.php'; require_once 'PHPUnit/Autoload.php'; require_once 'PHPUnit/Framework/Assert/Functions.php'; require_once 'PHPUnit/Extensions/SeleniumTestCase.php'; require_once 'PHPUnit/Extensions/SeleniumTestCase/SauceOnDemandTestCase.php';We have some Gherkin features written over there.
behat.yml
This file is a default config file that Behat uses for executing features. Example behat.yml should is shown below:
default: context: parameters: javascript_session: selenium base_url: http://uat.acnestudios.com browser: firefox show_cmd: open %s
You can change drivers by changing “javascript_session” variable. It can be “selenium” or “webdriver”.
sauce.yml
This file is used for running features on Sauce Labs. The code for this file is explained in “behat and Sauce Labs” section below.
build.xml
This file is used for running features with ANT. We can use this ANT file to plug into Jenkins. Simple ANT file should look like this:
<project name="behat" default= "behat" basedir=""> <delete dir="${basedir}/report" /> <mkdir dir="${basedir}/report"/> <target name="behat"> <exec dir="${basedir}" executable="behat" failonerror="true"> <arg line="-f junit --out ${basedir}/report"/> </exec> </target> <target name="create-test-report" description="Generate reports for executed JUnit tests."> <junitreport todir="./report"> <fileset dir="${basedir}/report"> <include name="TEST-*.xml"/> </fileset> <report format="frames" todir="./report/html"/> </junitreport> </target> </project>report
This directory is used to store *.xml reports generated by Behat’s “junit” formatter. We can use this reports to plug into Jenkins.
Start your Engine
Remember, you have to download latest version of selenium server. Now navigate to directory where you saved selenium server .jar file. You have to launch it using command shown below.
java -jar selenium-server-standalone-2.15.0.jarBehat & Sauce Labs
Sauce Labs is a cloud testing service which allows selenium tests to run in the cloud. Sauce Labs allocates machines and browsers for your tests, capture screenshot for every step and record video of all jobs(tests). You don’t need to setup separate machines to run tests. Sauce labs helps us to write tests without complex infrastructure.
In order to integrate Behat with Sauce Labs, you need to have an account with Sauce Labs. You need “Username” and “API Key” to plug them into a config file.
Behat executes features with “behat.yml” file by default, but we can run features with any other configuration file. We can create another configuration file like “sauce.yml” to run features on Sauce Labs. Example “sauce.yml” should look like this:
default: context: parameters: default_session: goutte javascript_session: selenium base_url: http://shop.acnestudios.com browser: firefox selenium: host: ondemand.saucelabs.com port: 80 browser: > { "username": "your username", "access-key": "your API key", "browser": "firefox", "browser-version": "7", "os": "Windows 2003", "name": "Testing Selenium with Behat" }We will use “sauce.yml” as a config file to run features on Sauce Labs. If you wish to run all features from “features” directory on Sauce labs, you can use this command:
behat -c sauce.yml
When to implement step definitions?
- If you can speak fluent Gherkin, then you don’t need to write code. Behat/Mink will understand Gherkin and run your features without suggesting step definitions.
- If features written by someone else, you can take full advantage of Mink API’s in order to implement step definitions suggested by Behat/Mink.
- It’s very important to write good Gherkin to write minimum code.
- It’s very easy to access Mink API’s by writing simple code.
Now we will see how will you “click” particular element on page. You need to use Xpath as a locator for that element. Mink will suggest some step definitions, you need to complete it like this:
/** * @Given /^I click first product$/ */ public function iClickFirstProduct() { $this->getMink()->getSession()->getDriver()->click("//Xpath"); }Example : Feature addToCart (Need implementation)
We will write simple feature to add product into shopping cart. Feature will look like this:
Feature: addToCart In order to shop some items on Acne site As a customer I want to add items in my bag @javascript Scenario: check all category pages Given I am on Category page When I choose product And I configure it Then I should see product added to shopping cart
Now we will run this feature using command:
behat --name addToCart
Remember, we are running it locally for now using default config file “behat.yml” with Selenium driver. After executing above command, we will get some step definitions suggested by Behat/Mink
You can see above, Behat/Mink has suggested some step definitions for undefined steps. We can implement these step definitions using Mink in “bootstrap/FeatureContext.php” file. We can implement first step definition ( iAmOnCategoryPage) like this:
/** * @Given /^I am on Category page$/ */ public function iAmOnCategoryPage() { $this->visit('/shop/women/aw11/skirts.html'); }Now we will run “behat”command again, you can see one step is passed. Terminal window output should be like this:
We have to repeat this process until all steps get “passed”. We can implement these steps by adding some code in “bootstrap/FeatureContext.php” file. The code will look like this:
<?php use Behat\Behat\Context\ClosuredContextInterface, Behat\Behat\Context\TranslatedContextInterface, Behat\Behat\Context\BehatContext, Behat\Behat\Exception\PendingException; use Behat\Gherkin\Node\PyStringNode, Behat\Gherkin\Node\TableNode; require_once 'bootstrap.php'; use Behat\Mink\Behat\Context\MinkContext; use Behat\Mink\Session; use Behat\Mink\Driver\DriverInterface; /** * Features context. */ class FeatureContext extends MinkContext { /** * @Given /^I am on Category page$/ */ public function iAmOnCategoryPage() { $this->visit('/shop/women/aw11/skirts.html'); } /** * @When /^I choose product$/ */ public function iChooseProduct() { $this->getMink()->getSession()->getDriver()->click("//a/div/img"); $this->getMink()->getSession()->wait("10000"); } /** * @Given /^I configure it$/ */ public function iConfigureIt() { $this->getMink()->getSession()->getDriver()->click("//*[@id='bodycontainer']/article/div/section[1]/div/form/div[1]/div/a"); $this->getMink()->getSession()->getDriver()->click("//*[@id='bodycontainer']/article/div/section[1]/div/form/div[1]/ul/li[2]/a"); $this->getMink()->getSession()->getDriver()->click("//*[@id='bodycontainer']/article/div/section[1]/div/form/div[2]/div/a"); $this->getMink()->getSession()->getDriver()->click("//*[@id='bodycontainer']/article/div/section[1]/div/form/div[2]/ul/li[2]/a"); $this->getMink()->getSession()->getDriver()->click("//*[@id='bodycontainer']/article/div/section[1]/div/form/button"); $this->getMink()->getSession()->wait("10000"); } /** * @Then /^I should see product added to shopping cart$/ */ public function iShouldSeeProductAddedToShoppingCart() { $this->assertElementOnPage('#cartHeader'); $this->getMink()->getSession()->getDriver()->click(".//*[@id='cartHeader']"); $this->assertElementOnPage('#topCartContent'); }After implementation, we have to run “behat” command again. You will see this:
We managed to get all our scenario/steps “passed”. It’s time to login into your Sauce Labs account to see this scenario running on Sauce Labs.
Now we have to use “sauce.yml” config file. We will run below mentioned command from terminal and we can see output on Sauce Labs as shown below:
behat -c sauce.yml --name addToCart
Screenshots for feature running on Sauce Labs:
You can see detail steps, screenshots and video of this job on Sauce Labs here.
See terminal output as shown below:Building Features with Jenkins
Continuous Integration (CI) is one of the best practice in agile projects to detect bugs early. Each integration is verified by an automated build to detect integration errors as quickly as possible. Now we will see how we can integrate Jenkins to our behat project.
Don’t forget to start your engine before building project in Jenkins. You have start Sahi/Selenium/WebDriver before starting Jenkins.
There is “/report” directory to save JUnit reports in xml format generated by Behat. You can generate reports using this command:
behat -f junit --out ~/pathto/report
To continue, you need Jenkins installed on your machine. Now start Jenkins by executing *.war file from terminal on port 8080. You also need to visit “http://localhost:8080″ to see Jenkins GUI.
$ java -jar jenkins.war
- Visit http://localhost:8080/ and you should see Jenkins
- Create “New Job” for behat project.
- Specify your SCM to create Jenkin’s workspace on your system.
- You can specify build file as per your project structure. In here I have configured in “Acne” project by specifying build file path and test reports like this.
- Save your configuration. Go back to project and click”Build Now”, you will see:
- You can see features running in browser.
- Sit back and enjoy your features running on Jenkins till it finishes job.
- Few minutes later, you will see your build is “Green”:
- Watch your test results:
Behat, Sauce Labs and Jenkins
You can run all your features on Sauce Labs just by updating ANT file like:
<project name="behat" default= "behat" basedir=""> <delete dir="${basedir}/report" /> <mkdir dir="${basedir}/report"/> <target name="behat"> <exec dir="${basedir}" executable="behat" failonerror="true"> <arg line="-c sauce.yml -f junit --out ${basedir}/report"/> </exec> </target> <target name="create-test-report" description="Generate reports for executed JUnit tests."> <junitreport todir="./report"> <fileset dir="${basedir}/report"> <include name="TEST-*.xml"/> </fileset> <report format="frames" todir="./report/html"/> </junitreport> </target> </project>Alternatively, you can configure Sauce On demand tunnel in Jenkins.
You can watch demo of this blog on vimeo.
Conclusion:
We can write web acceptance tests with Behat and Mink combination. We can plug them into Sauce Labs with config file (“sauce.yml”) and run them on a CI server. Now you can implement BDD practices for PHP applications by taking benefit from Bahat and Sauce Labs.
-
[Devis Lucato]Select: Inversion of Control December 13, 2011
Inversion of control is the answer to more maintainable, testable, modular code, a common pattern in OOP adopted by frameworks and enterprise projects. The main idea is to separate configuration (class names and initialisation parameters) from implementation (class instantiations and static calls), avoiding hard coded class names and parameters, so that they can be replaced by third parties and during tests.
There are at least three common ways of injecting dependencies (see here for a detailed description).
Constructor Injection: injection through constructor parameters
For every object, all the dependencies are passed as constructor arguments. Constructor injection is fairly straitforward and works quite well on small projects, but as a project and the number of dependecies grow, so do constructors' signatures length. Complex constructors are eventually refactored to receive an array of dependecies or moving them up in the hierarchy, in some base class, which evolves into a registry of dependencies (among the other responsibilities).
Setter Injection: public setters for every dependency
Every dependency is set using a public method inside a class. For instance three classes depending on a Mailer would have each a `setMailer($object)` method. Fairly simple to implement but leads to duplication and maintainability issues, every time an object is create all the setters must be called.
Service locator: holder/registry of components/services
All the dependencies are provided by a builder, which serves as a registry of dependencies and/or service definitions. The service locator knows how to instantiate each dependency. Such service exposes methods like `getMailer()`, `getLogger()` etc. A service locator centralises the configuration detailing classes and parameters involved on objects instantiations.
Select is a static Service Locator implementation with PHP method overloading. It allows to replace classes and can be used to hold components/services, identified by unique names and automatically exposed with getter methods.
Select is designed to be subclassed with a custom class name, as opposed to the common injection through constructors. To replace Select you subclass the main class. For instance: during tests you can either use a different set of definitions (suggested) or use a mocked Service Locator class implementing the same interface iSelect.
-
[Mark van der Velden]A world without cookies June 29, 2011
Imagine a world wide web without cookies. And this might not be the strangest thing, since the use of cookies is severely limited since Wednesday the 22nd of June 2011 in the Netherlands. And more countries will follow: http://www.bbc.co.uk/news/technology-12668552.
The Law
The (Dutch) law, that requires a user to agree before storing data, doesn't only apply on HTTP cookies. But in fact any kind of data that is stored on the users computer. Such as; HTML5 storage, flash cookies. But also desktop applications, etc.. The law also states that cookies "required" for certain functionality, are allowed without confirmation. Personally I don't see how anything will change, with this exception in place. And I wonder how many experts were involved into making this law. But that is a subject for another article perhaps...
What are cookies
Cookies are little packages of information stored in the browser of a website visitor, they can contain "small" amounts of data such as an identifiable token or a user preference.
What purpose do cookies serve
Cookies are very generic and can be used for many things, good and bad. The most popular probably being tracking your activity and advertisement. But they are also used to keep a state between requests and to store a preference. Such as "remember me" at a login form, or perhaps "no I do not want to participate in your survey".
Cookie problems
IfWhen the law becomes international, you are potentially violating laws by simply calling something like: setcookie() without the users consent. There are hacks around the typical HTTP cookies, by using flash cookies for example (Oh and btw, here a guide on how to clear those: http://www.macromedia.com/...ngs_manager07.html). And possibly HTML5 storage could play a role here (or any of it's derivatives).Another thing that has been happening, is visitor awareness and thus browser features. More and more people block cookies to stop advertisement tracking, but unfortunately this also prevents a user to use the features he or she wants to use (such as login sessions, etc.). There is an answer for this and quite a few browser vendor's plan on implementing the "Do Not Track" (http://donottrack.us/) feature, or have already done so. But I'm not too happy with it. The downside of "Do Not Track" is that it's voluntary for website owners and advertisement companies to respect this feature. Other tools include projects such as "Ad Blocker", that only block cookies (and more) for advertisement purposes. It works pretty good, but that is hardly user-friendly.
But, back to "no more cookies"... How do you solve the problem of keeping a state between requests over a stateless protocol?
Some ideas
Well in short, I have some ideas but definitely no real answers. I don't think there is a real answer just yet.
Let's take the example of a login session. Where you want to offer a secure section to your visitors, where they can (e.g.) read their e-mail, privately. A few things come to mind:
- Identifying yourself in every relevant request, using headers for example or an argument &userId=42 in the request. This however has some big problems on its own.
- Digest authentication on every relevant request.
- Take a look at other technologies, such as IPv6 and move authentication to a lower level.
- Perhaps use a commit and rollback system. Where you can do a variety of actions and only your password will be asked in the end, either applying or discarding your actions. Requiring you only to enter your login once. Not the most ideal solution for e-mail though, I have to admit. With this system you would re-post your data to next screens in a similar fashion as e.g. a wizard.
- Or perhaps we need to move away from complete server-side applications and move to the client, Javascript applications and use the HTTP spec a little better as it was intended and only have web-services.
Many, if not all, of the things I mentioned above would require secure connections (SSL/TLS) to avoid other security problems. Which might not be a bad move anyway.Personally I think that there is a future, in an improved implementation of digest authentication over SSL. One that uses HMAC and stronger algorithms, SSL would then supply the missing server validation feature. It should also be more strict and not fall back to insecure legacy features.Conclusion
All in all I firmly believe that the browser should play a big role in this new cookie recipe and should (partially) solve these problems. Also there should be a more clear separation between "generic storage" and authentication versus a simulated persistency. In more perfect world I would vote for a solution that works on other (underlying) layers and make it application agnostic.I suppose the point I'm trying to make with this article is the following: Take away a feature the entire world uses (since 1996), and wait for the brilliant and creative minds, perhaps such as yourself, to come up with a more innovative feature. It's time for something better!Another interesting read:
I made some updates to this article, based on some comments. -
[Devis Lucato]PHP BDD with Cucumber/Cuke4Php April 6, 2011
This is a quick reference on how to start using Cucumber with PHP, thanks to Cuke4Php.
Cucumber is a Behavioural Driven Development framework written in Ruby. Using a wire it allows to write BDD steps in PHP to test PHP applications. Cuke4php is in fact a wire protocol implementation of Cucumber written in PHP. If you are interested on BDD you might want to have a look at Behat too, which is written entirely in PHP.
As an exercise this guide should guide through the installation of Ruby, Cucumber, Cuke4php, and PHPUnit to implement the classic Calculator's Addition feature example. Please consider this just a demo. The system used is a Debian 6 Squeeze. Zend Framework is used only to simulate a real world project scenario.
Here we go! [N.B. working as root]
Install Ruby
# apt-get install ruby ruby1.8-dev rdoc1.8 irb libxml2-dev libxslt1-dev libc6-dev-i386 libopenssl-ruby ... Note, selecting 'ruby1.8' instead of 'rdoc1.8' Note, selecting 'ruby' instead of 'irb' Note, selecting 'libruby' instead of 'libopenssl-ruby' ...
Manual installation of Ruby gems
# cd /tmp # wget http://rubyforge.org/frs/download.php/60718/rubygems-1.3.5.tgz # tar xvf rubygems-1.3.5.tgz # cd rubygems-1.3.5 # ruby setup.rb RubyGems 1.3.5 installed
Add gems
# gem sources -a http://gems.github.com http://gems.github.com added to sources
# gem install cucumber mechanize rspec webrat Building native extensions. This could take a while... ERROR: Error installing cucumber: cucumber requires gherkin (>= 2.3.5, runtime) ....
# gem install gherkin cucumber Building native extensions. This could take a while... ....... Successfully installed gherkin-2.3.5 Successfully installed cucumber-0.10.2
Install a custom copy of Zend Framework
# wget http://framework.zend.com/releases/ZendFramework-1.11.4/ZendFramework-1.11.4.tar.gz # tar xzf ZendFramework-1.11.4.tar.gz # mkdir ~/bddtest # mv ZendFramework-1.11.4 ~/bddtest/ZF # PATH=$PATH:~/bddtest/ZF/bin/ # chmod u+x ~/bddtest/ZF/bin/zf.sh # alias zf='~/bddtest/ZF/bin/zf.sh'
Create a new ZF project
# cd ~/bddtest # zf create project zfapp Creating project at ~/bddtest/zfapp .....
Create Features tree:
# cd ~/bddtest/zfapp # mkdir -p features/support # mkdir -p features/step_definitions
Ruby configuration
# cd ~/bddtest # nano features/support/env.rb
File content:
# RSpec require 'rspec/expectations' # Webrat require 'webrat' require 'test/unit/assertions' World(Test::Unit::Assertions) Webrat.configure do |config| config.mode = :mechanize end World do session = Webrat::Session.new session.extend(Webrat::Methods) session.extend(Webrat::Matchers) session end
Create the first requirement, the Calculator Addition feature
nano features/addition.feature
File content, the Calculator Addition feature to implement:
Feature: Addition In order to avoid silly mistakes As a math idiot I want to be told the sum of two number Scenario: Add two numbers Given I have entered 50 into the calculator And I have entered 70 into the calculator When I press add Then the result should be 120 on the screen
And test the feature with cucumber:
# cucumber features/addition.feature
The second command verifies the feature which is not implemented yet and returns some errors:
Feature: Addition In order to avoid silly mistakes As a math idiot I want to be told the sum of two number Scenario: Add two numbers # features/addition.feature:6 Given I have entered 50 into the calculator # features/addition.feature:7 And I have entered 70 into the calculator # features/addition.feature:8 When I press add # features/addition.feature:9 Then the result should be 120 on the screen # features/addition.feature:10 1 scenario (1 undefined) 4 steps (4 undefined) 0m0.003s
Install Cuke4php
# gem install cuke4php ******************************************************************************** Please install PHPUnit >= 3.0 if you've not already done it! Add PEAR channels: pear channel-discover pear.phpunit.de pear channel-discover components.ez.no pear channel-discover pear.symfony-project.com Install PHPUnit: pear install phpunit/PHPUnit ******************************************************************************** Successfully installed cuke4php-0.9.5 1 gem installed
Cuke4php requires PHPUnit, let's install it, in this case the installation failed because PEAR was not up to date:
# pear channel-discover pear.phpunit.de Adding Channel "pear.phpunit.de" succeeded Discovery of channel "pear.phpunit.de" succeeded # pear channel-discover components.ez.no Adding Channel "components.ez.no" succeeded Discovery of channel "components.ez.no" succeeded # pear channel-discover pear.symfony-project.com Adding Channel "pear.symfony-project.com" succeeded Discovery of channel "pear.symfony-project.com" succeeded # pear install phpunit/PHPUnit ... phpunit/PHPUnit requires PEAR Installer (version >= 1.9.2), installed version is 1.9.1 ...
Upgrade PEAR and install PHPUnit
# pear upgrade # pear install phpunit/PHPUnit
Create the wire protocol to allow Cucumber to test the application PHP code
# nano features/Cuke4Php.wire
File content:
host: localhost port: <%= ENV['CUKE4PHP_PORT'] %>
Enable PHPUnit autoloader, to avoid issues later in the tests
# nano features/support/Env.php
File content:
<?php require "PHPUnit/Autoload.php";
Let's test the features with Cuek4php to see if Cuke4php is working fine.
# cuke4php features/addition.feature Cuke4Php listening on port 16816 Feature: Addition In order to avoid silly mistakes As a math idiot I want to be told the sum of two number Scenario: Add two numbers # features/addition.feature:6 Given I have entered 50 into the calculator # features/addition.feature:7 And I have entered 70 into the calculator # features/addition.feature:8 When I press add # features/addition.feature:9 Then the result should be 120 on the screen # features/addition.feature:10 1 scenario (1 undefined) 4 steps (4 undefined) 0m0.086s You can implement step definitions for undefined steps with these snippets: Given /^I have entered (\d+) into the calculator$/ do |arg1| pending # express the regexp above with the code you wish you had end /** * Given /^I have entered 50 into the calculator$/ **/ public function stepIHaveEntered50IntoTheCalculator() { self::markPending(); } Given /^I have entered (\d+) into the calculator$/ do |arg1| pending # express the regexp above with the code you wish you had end /** * Given /^I have entered 70 into the calculator$/ **/ public function stepIHaveEntered70IntoTheCalculator() { self::markPending(); } When /^I press add$/ do pending # express the regexp above with the code you wish you had end /** * When /^I press add$/ **/ public function stepIPressAdd() { self::markPending(); } Then /^the result should be (\d+) on the screen$/ do |arg1| pending # express the regexp above with the code you wish you had end /** * Then /^the result should be 120 on the screen$/ **/ public function stepTheResultShouldBe120OnTheScreen() { self::markPending(); }The only missing bits now are the steps required to implement the feature. They are defined in a php file:
# nano features/step_definitions/calculator_steps.php
Temporary file content:
<?php class CalculatorSteps extends CucumberSteps { /** * Given /^I have entered 50 into the calculator$/ **/ public function stepIHaveEntered50IntoTheCalculator() { self::markPending(); } /** * Given /^I have entered 70 into the calculator$/ **/ public function stepIHaveEntered70IntoTheCalculator() { self::markPending(); } /** * When /^I press add$/ **/ public function stepIPressAdd() { self::markPending(); } /** * Then /^the result should be 120 on the screen$/ **/ public function stepTheResultShouldBe120OnTheScreen() { self::markPending(); } }Testing cuke4php now
# cuke4php features/addition.feature Cuke4Php listening on port 16816 Feature: Addition In order to avoid silly mistakes As a math idiot I want to be told the sum of two number Scenario: Add two numbers # features/addition.feature:6 Given I have entered 50 into the calculator # /root/bddtest/zfapp/features/step_definitions/calculator_steps.php:8 Not Implemented (Cucumber::Pending) features/addition.feature:7:in `Given I have entered 50 into the calculator' And I have entered 70 into the calculator # /root/bddtest/zfapp/features/step_definitions/calculator_steps.php:15 When I press add # /root/bddtest/zfapp/features/step_definitions/calculator_steps.php:22 Then the result should be 120 on the screen # /root/bddtest/zfapp/features/step_definitions/calculator_steps.php:29 1 scenario (1 pending) 4 steps (3 skipped, 1 pending) 0m0.129s
Let's define the real steps which describe the feature and to verify that the feature is implemented:
# nano features/step_definitions/calculator_steps.php
Here's the final file content. Note how PHPDoc is used to map a method to a step description and to extract the required parameters:
<?php class CalculatorSteps extends CucumberSteps { public function beforeAll() { require 'application/models/Calculator.php'; $this->aGlobals['calculator'] = new Application_Model_Calculator(); } /** * Given /^I have entered 50 into the calculator$/ **/ public function stepIHaveEntered50IntoTheCalculator() { $this->aGlobals['calculator']->enter(50); } /** * Given /^I have entered 70 into the calculator$/ **/ public function stepIHaveEntered70IntoTheCalculator() { $this->aGlobals['calculator']->enter(70); } /** * When /^I press add$/ **/ public function stepIPressAdd() { $this->aGlobals['calculator']->doSum(); } /** * Then /^the result should be 120 on the screen$/ **/ public function stepTheResultShouldBe120OnTheScreen() { $this->assertEquals(120, $this->aGlobals['calculator']->getResult()); } }And create the model which encapsulate the Calculator logic:
# zf create model Calculator Creating a model at ~/bddtest/zfapp/application/models/Calculator.php Updating project profile '~/bddtest/zfapp/.zfproject.xml' # nano application/models/Calculator.php
File content:
<?php class Application_Model_Calculator { protected $result; protected $values; public function __construct() { $this->result = 0; $this->values = array(); } public function enter($value) { $this->values[] = $value; } public function doSum() { for ($this->result = reset($this->values); $value = next($this->values);) $this->result += $value; } public function getResult() { return $this->result; } }...and testing the feature now...
# cuke4php features/addition.feature Cuke4Php listening on port 16816 Feature: Addition In order to avoid silly mistakes As a math idiot I want to be told the sum of two number Scenario: Add two numbers # features/addition.feature:6 Given I have entered 50 into the calculator # /root/bddtest/zfapp/features/step_definitions/calculator_steps.php:13 And I have entered 70 into the calculator # /root/bddtest/zfapp/features/step_definitions/calculator_steps.php:20 When I press add # /root/bddtest/zfapp/features/step_definitions/calculator_steps.php:27 Then the result should be 120 on the screen # /root/bddtest/zfapp/features/step_definitions/calculator_steps.php:34 1 scenario (1 passed) 4 steps (4 passed) 0m0.173s
Done! Mission accomplished! Now in order to implement another feature, ie Multiply, it's just a matter of creating a feature-file, the steps required to verify the feature, and implement the business logic in some model.
-
[Felix de Vliegher]Goodbye Ibuildings, hello Egeniq April 5, 2011
A couple of years ago, when I joined Ibuildings, it was because I had a passion for PHP and the PHP community, and Ibuildings was THE company embodying this spirit. I’ve got the opportunity to work at some of the most amazing projects, and met some of the smartest people in PHP-land. However since about a year, I’ve noticed my interests shifting a lot to mobile and mobile development. Of course I’m still as passionate about PHP as I was a few years ago, but I feel that mobile development is quite close to web development in a couple of ways, especially nowadays when people often expect to have internet available at all times. After focussing more and more on mobile development and seeing a huge future in it, I decided it was time for me to move on and leave Ibuildings.
To better match my combined interest of PHP and mobile development, I decided to join Egeniq. For those who don’t know Egeniq, they’re an awesome mobile technology company, founded 7 months ago by my former colleagues Peter, Bas and Ivo. Other than your average mobile app shop, they focus on proper software engineering, architecture, performance and scalability. They build on the years of experience they have in the PHP world and bring that expertise to the mobile business. They’re also really into geek stuff, and mobile devices fit nicely into that category, too :-)
Even though deciding on leaving Ibuildings was probably the hardest career decision I ever had to make, I very much look forward playing around with all things mobile and working closely with my former colleagues again. Exciting! :-) I’d like to thank Ibuildings for the opportunities they gave me and all the wonderful people over there, from whom I’ve learned so much. I will be at Ibuildings until the end of April, starting at Egeniq at the start of May.
This is quite a big step, and I thought about taking this step for a long time. But you know you should pursue something, if you have a very strong gut feeling that you just need to do this. The feeling that, if you don’t do this, you will regret it. The feeling that you get when considering all the pros and cons, but deep down inside yourself you know you already made up your mind. It goes without saying that I’m very much looking forward to the new things to come at Egeniq!
-
[Devis Lucato]Drizzle: Big-Data-happy MySQL fork debuts March 17, 2011
Drizzle, a lightweight fork of Oracle's MySQL database for cloud computing, has been released by open sourcers. Version 2011.03.13 has been released as general availability version. It comes nearly three years after the project was announced by Brian Aker, one of MySQL's key architects.
Drizzle aims to be different from MySQL, stripping out "unnecessary" features loved by enterprise and OEMs in the name of greater speed and simplicity and for reduced management overhead.
Drizzle has no stored procedures, triggers, or views and, in a blow to a large chunk of the computing and IT establishment, it doesn't run on Microsoft's Windows. Also, there's no embedded server.
Drizzle has been optimized for "massively concurrent" environments and is designed for "modern" POSIX systems, and there aren't any installation scripts.
The GA includes log-based replication, the HailDB relational database engine instead of the Oracle-owned InnoDB, and "easy migration" from MySQL using the Drizzledump.
-
[Devis Lucato]Constrained Application Protocol (CoAP) March 15, 2011
Constrained Application Protocol (CoAP), is a specialized RESTful (Representational State Transfer) protocol for use with constrained networks and nodes for machine-to-machine applications such as smart energy and building automation.
These constrained nodes often have 8-bit microcontrollers with small amounts of ROM and RAM, while networks such as 6LoWPAN often have high packet error rates and a typical throughput of 10s of kbit/s.
CoAP provides the REST Method/Response interaction model between application end-points, supports built-in resource discovery, and includes key web concepts such as URIs and content-types.
CoAP easily translates to HTTP for integration with the web while meeting specialized requirements such as multicast support, very low overhead and simplicity for constrained environments.
The goal of CoAP is not to blindly compress HTTP, but rather to realize a subset of REST common with HTTP but optimized for M2M applications. Although CoRE could be used for compressing simple HTTP interfaces, it more importantly also offers features for M2M such as built-in discovery, multicast support and asynchronous transactions.
Unlike HTTP, CoAP deals with these REST interchanges asynchronously over a UDP transport with support for both unicast and multicast interactions. This is achieved using transaction messages (CON=Confirmable, NON=Non-Confirmable, ACK=Acknowledgment, RST=Reset) supporting optional reliability (with exponential back-off) and transaction IDs between end-points to carry REST requests and responses. These transactions are transparent to the REST interchanges. The only difference being that responses may arrive asynchronously.
The goal of binding CoAP to UDP is to provide the bare minimum features for the protocol to operate over UDP, without trying to re-create the full feature set of a transport like TCP.
See http://tools.ietf.org/html/draft-ietf-core-coap-01 for the current Internet-Draft and a complete description.
-
[Harrie Verveer]DB migrations: rename instead of drop March 2, 2011
In my talks and article I always mention that when it comes to database migrations it is generally a bad idea to rely on undo-patches for rollbacks. As an example I always use the same story: “Imagine you write a database patch that removes a column from a table, and then you write an undo-patch that adds the column back again. Sure, your database schema is now the same as it was before – but the content is gone!” And then I would move on by saying that you probably shouldn’t rely on undo-patches anyway for rollbacks, because it is better to thoroughly test everything before updating so you are absolutely certain that you won’t have to rollback. Also, I recommended to make backups first, and use that to revert database migrations when needed. Easier said than done!
The fact that I am in the luxurious position where this approach usually is an option, doesn’t mean that this is a solution for everyone. For other projects where, for example, larger databases are involved, rolling back by restoring a backup would be considered highly inefficient (and if the database is in use it would actually result in data loss). Being able to rollback patches can be convenient, but reverting dropped columns can’t be done off course.
When I did my talk at DPC10 in Amsterdam however, someone in the audience (sorry, I don’t remember who it was) suggested a very simple but very effective solution. In fact, it was such a simple solution that I couldn’t stand I hadn’t thought of it before: If you rename your column or table first instead of dropping it you can still just use an undo-patch to revert the change. In a later stage, when you are absolutely certain you don’t need to do a rollback anymore, you can simply drop the renamed column or table by writing another patch. For example in a next release.
Reverting a renamed column
As you can see, sometimes the simplest solution is the best. Beware though, as in some situations this approach might still cause problems: Records added to this table between the moment the patch was applied and the moment the undo file was applied won’t have a value in the age column, to name just one. As always when we are talking database version control, it all depends on the specific situation you are in.
I myself still refuse to write undo-patches as most patches create new tables or columns anyway, instead of removing them – and if patches actually do drop tables or columns they probably haven’t been very significant for quite some time anyway. Maybe the main reason is that I have simply never found myself in the situation where an undo-patch would have helped me. This might be different for you though, and if you are looking for a way to rollback dropped columns or tables, renaming them first might be a pretty good idea.
-
[Ben Longden]PHP on Azure February 13, 2011
When I first started out playing with PHP a number of years ago, I did it on my home PC running windows. I remember manually setting up apache (I never tried with IIS), configuring php to work with it and getting a mysql database up and running. The whole process was time consuming and frustrating – without package management it’s not a particularly pleasant experience.
With Microsofts Azure cloud platform available on a trial [link], and with PHP Benelux running the PHP on Azure contest [link] I decided it would be a good opportunity to have another go at using the windows environment as a hosting platform – though this time using the Web Platform Installer (WebPI) with IIS and SQL Server instead of my traditional LAMP stack.
WebPI promised that this would be a simple process. It certainly looks the part, and has a good range of PHP apps that can be setup out the box. Unfortunately as I was getting all the pre-requisites installed in my Windows 7 VM, the PHP command line tools didn’t appear to have the Windows Azure SDK available as a dependency. Nor was it available when I searched for it within WebPI.
SQL Server did install ok though, so I followed the manual instructions [link] to get IIS set up with PHP, and get the SDK installed.
That was fine – up until the point that I was building my first test deployment to my local development ‘cloud’. It transpires that the package.php script (used to build an azure package out of your php project ready for deployment) assumes that your username does not contain a space. Mine was ‘Ben Longden’.
So I created a new user account and tried again. None of the Azure SDK tools worked under my new user and just claimed that ‘A fatal error occured’, but reinstalling them helped. The next issue was that my new user doesn’t have access to connect to the SQL server installation (this is something that I have not yet resolved!).
So not plain sailing so far! I found it quite frustrating in all (compared to ‘sudo tasksel install lamp-server’), however my initial deployment to the Azure Cloud went fine in the end, once the environment was set up.
Initial concerns are with how the development process will work. Up next, will be setting up IIS as a development environment that I can work with, and automating the deployment to my local cloud.
-
[Harrie Verveer]Benchmarking Xdebug February 6, 2011
A very popular and widely used PHP extension for debugging and profiling PHP applications is Xdebug. Normally this tool would be used on your development environment, or in rare occasions on some remote environment that is similar to the production environment. However, a little while ago one of my co-workers found a production environment running Xdebug, and we wondered how severe this was influencing performance. Since you are adding extra overhead it is very likely things will run slower. But how significant is this difference. Would it be something worth considering?
Disclaimer
Now I know that debugging a production environment is generally considered unnecessary and a really bad idea. I myself can’t really think of a situation where I would actually want to have Xdebug on my production machine – but that’s not the point. I just had a new question that I wanted to answer: “How much does installing Xdebug affect the performance of PHP?”The hardware
To be able to test this I first needed to find a proper testing environment. Unfortunately I don’t have any webservers laying around, and testing directly on my macbook would probably be a bad idea – since there are a million processes running that all influence the performance of the machine, and therefore would influence the results. After some pondering I realised that I had an old (well… more like ancient) laptop laying around that would actually suit this purpose quite well. When it comes to speed it doesn’t come anywhere close to a modern production environment – but since we will be looking at relative changes rather than absolute measurements I figured it would be just fine.The software
I downloaded and installed a fresh new copy of Debian. Once it was installed I decided to take the easy way and install PHP simply by using apt-get install php5. Finally, I installed xdebug using the PECL installer. With just this installed and nothing else, I was convinced my benchmark results would be quite reliable.Software installed:
- Debian 5.0.8
- PHP 5.2.6 with Suhosin-patch
- Xdebug 2.1.0
The benchmark
Why write a benchmark when they already exist? For testing purposes I decided to use the bench.php script that comes with the PHP source. This script does not run out-of-the-box when Xdebug is installed, because one test (Ackermann function) exceeds the default Xdebug limit of 100 nested function calls. Now I could of course increase this number, but I wanted to test how Xdebug performs without any modifications to the configuration – so I decided to simply skip this one test and just run the remaining ones.Finally, after a lot of preparation, I was ready to run the script. \o/
The results
I decided to run the script three times with the xdebug extension enabled, and then another three times with the xdebug extension disabled. The results were pretty clear:No Xdebug
(time in seconds)Xdebug
(time in seconds)Run 1 Run 2 Run 3 Run 1 Run 2 Run 3 simple 0.687 0.648 0.681 1.327 1.361 1.367 simplecall 0.813 0.744 0.786 3.581 3.770 3.875 simpleucall 1.138 1.225 1.169 4.439 4.510 4.877 simpleudcall 1.289 1.469 1.336 4.621 4.609 4.747 mandel 2.016 2.259 2.161 3.646 4.028 4.036 mandel2 2.774 2.753 2.766 3.495 4.008 4.215 ary(50000) 0.171 0.175 0.171 0.210 0.224 0.224 ary2(50000) 0.136 0.136 0.136 0.172 0.180 0.184 ary3(2000) 1.363 1.365 1.416 2.133 2.463 2.450 fibo(30) 3.694 3.517 3.538 10.418 12.364 12.943 hash1(50000) 0.212 0.216 0.214 0.515 0.618 0.638 hash2(500) 0.264 0.266 0.266 0.283 0.357 0.342 heapsort(20000) 0.676 0.667 0.671 1.026 1.198 1.196 matrix(20) 0.556 0.552 0.555 0.810 0.960 0.936 nestedloop(12) 1.221 1.208 1.311 2.197 2.570 2.573 sieve(30) 0.786 0.786 0.789 1.105 1.231 1.226 strcat(200000) 0.090 0.093 0.096 0.148 0.173 0.169 Total: 17.886 18.079 18.062
40.126 44.624 45.998 Average: 18.009 seconds
43.583 seconds So there we go. Without Xdebug it takes about 18 seconds to run the script, and with Xdebug enabled is takes about 44 seconds: 241% of the original time (or: it took 2.41 times as long to run the script).
So what does this mean?
In this situation adding Xdebug slowed down the execution of the benchmark script quite significantly.So what doesn’t this mean?
This doesn’t mean that your application will slow down with the same relative percentages, nor does it mean that you can compensate for this by buying 2.41x more servers. When we look at the breakdown per test it’s clear to see that the differences per test are quite significant as well. For example: the time needed to run the ‘ary’-test increased with only 27%, while the time needed for the ‘simplecall’ test increased with a whopping 379%.Apparently the overhead caused by running Xdebug varies and is related to what kind of operations you are running. Calling functions seems to be affected a lot by Xdebug, while the effect of manipulating strings or arrays is less severe.
Test No xdebug Xdebug Percentage simplecall 0.78 3.74 479% simpleucall 1.18 4.61 391% simpleudcall 1.36 4.66 341% fibo(30) 3.58 11.91 332% hash1(50000) 0.21 0.59 276% simple 0.67 1.35 201% nestedloop(12) 1.25 2.45 196% mandel 2.15 3.9 182% strcat(200000) 0.09 0.16 176% ary3(2000) 1.38 2.35 170% heapsort(20000) 0.67 1.14 170% matrix(20) 0.55 0.9 163% sieve(30) 0.79 1.19 151% mandel2 2.76 3.91 141% ary2(50000) 0.14 0.18 131% ary(50000) 0.17 0.22 127% hash2(500) 0.27 0.33 123% A more realistic setup – Testing WordPress with ApacheBench
The numbers from the previous test are quite clear – but they hardly represent a real world example of an application. To get a bit more insight in how enabling or disabling Xdebug would affect an actual real life application I decided to add Apache, MySQL and WordPress into the mix, and benchmark the default homepage of a standard WordPress installation using the benchmark tool ApacheBench (ab), which comes with Apache. Adding these applications means there will be a lot more factors to influence the results – but I was hoping the results would somehow give an indication on how much an average application would be affected by installing Xdebug.I ran ApacheBench a number of times. Every time it fired a total of 100 requests and every time I changed the number of maximum concurrent requests. I assumed that when Xdebug was enabled the requests would take longer – but because of that it’s also more likely the maximum number of concurrent requests that’s still acceptable is reached sooner. After a couple of test runs I had the following figures:
Max. concurrent requests No Xdebug (req./sec.) Xdebug (req./sec.) Difference (req./sec.) Difference % 1 2,79 2,22 0,57 20,43% 5 2,75 2,16 0,59 21,45% 10 2,73 2,13 0,6 21,98% 15 2,69 2,11 0,58 21,56% 20 2,64 2,1 0,54 20,45% 25 2,61 1,34 1,27 48,66% 30 0,95 - timeout - 0,95 100,00% or in a pretty graph:
So not only does it take about 20% more time to respond to requests in normal situations, the maximum number of requests your server can handle is also influenced significantly.
Conclusion
So as a conclusion we can say what we actually already suspected, Xdebug influences performance quite a lot. How much exactly is hard to tell and depends on what your application does. Xdebug is a great tool, and pretty much irreplaceable when you are looking for a proper tool to debug or profile an application on your development- or testing environment. Running it on your production machines however is something that would only make sense in very specific situations, as it is typically unnecessary and it slows down your application a lot. Also, make sure you turn off Xdebug on environments that you want to use for benchmarking your application – as it will influence your results.Edit: Directly after publishing this article Xdebug mentioned on twitter that the current version in SVN is supposed to be a lot faster. I’ll benchmark this new version soon – and let you know if there is any significant different.
-
[Devis Lucato]Debian 6.0 "Squeeze" with FreeBSD ports February 6, 2011
After 24 months of constant development, the Debian Project is proud to present its new stable version 6.0 (code name "Squeeze"). Debian 6.0 is a free operating system, coming for the first time in two flavours. Alongside Debian GNU/Linux, Debian GNU/kFreeBSD is introduced with this version as a "technology preview".
Debian 6.0 includes the KDE Plasma Desktop and Applications, the GNOME, Xfce, and LXDE desktop environments as well as all kinds of server applications. It also features compatibility with the FHS v2.3 and software developed for version 3.2 of the LSB.
Debian 6.0 "Squeeze" introduces technical previews of two new ports to the kernel of the FreeBSD project using the known Debian/GNU userland: Debian GNU/kFreeBSD for the 32-bit PC (kfreebsd-i386) and the 64-bit PC (kfreebsd-amd64). These ports are the first ones ever to be included in a Debian release which are not based on the Linux kernel. The support of common server software is strong and combines the existing features of Linux-based Debian versions with the unique features known from the BSD world.
Another first is the completely free Linux kernel, which no longer contains problematic firmware files. These were split out into separate packages and moved out of the Debian main archive into the non-free area of our archive, which is not enabled by default. In this way Debian users have the possibility of running a completely free operating system, but may still choose to use non-free firmware files if necessary.
Furthermore, Debian 6.0 introduces a dependency based boot system, making system start-up faster and more robust due to parallel execution of boot scripts and correct dependency tracking between them. Various other changes make Debian more suitable for small form factor notebooks, like the introduction of the KDE Plasma Netbook shell.
Debian 6.0 includes over 10,000 new packages like the browser Chromium, the monitoring solution Icinga, the package management frontend Software Center, the network manager wicd, the Linux container tools lxc and the cluster framework Corosync.
The installation process for Debian GNU/Linux 6.0 has been improved in various ways, including easier selection of language and keyboard settings, and partitioning of logical volumes, RAID and encrypted systems. Support has also been added for the ext4 and Btrfs filesystems and - on the kFreeBSD architecture - the Zettabyte filesystem (ZFS).
As always, Debian GNU/Linux systems may be upgraded painlessly, in place, without any forced downtime, but it is strongly recommended to read the release notes as well as the installation guide for possible issues, and for detailed instructions on installing and upgrading.
-
[Devis Lucato]Changes in the TIOBE index January 31, 2011
The TIOBE Programming Community index is an indicator of the popularity of programming languages. The index is updated every month and is based on the number of skilled engineers world-wide, courses and vendors. Data is extracted from the popular search engines. The index is not about the best programming language or a measure of LOC.
While PHP (-2.24%) has lost one position to C++ (-0.93%), moving from third to fourth position, Java (+0.29%) and C (-0.39%) are pretty stable at the top.
However some relevant changes happened in 2010. Python (+1.81%) has been declared the programming language of 2010, moving from 7th to 5th position, and it is now the "de facto" standard in system scripting. Python was declared language of the year also in 2007.
Some big step forward also for Objective-C (+1.63%), while it's surprising (to me at least) that Javascript (-1.12%) is losing momentum moving from 9th to 11th position.

Looking at the last 5 years trend:
- Java, VB and Perl are slowly losing ground
- C, C++, PHP and Ruby are stable
- Python, C# and Object-C are speeding up
-
[Devis Lucato]Silverstripe's modules moved to Github January 20, 2011
The announce from SilverStripe Core Development that all modules marked as Silverstripe supported, have been moved from svn to GitHub.
Modules were previously hosted on svn.silverstripe.org and are now hosted at https://github.com/silverstripe . This doesn't include the Silverstripe "core" though (phpinstaller, sapphire, cms) which will be migrated over the next days/weeks.
Other modules available at svn.silverstripe.org will progressively be migrated to their owners' github account when possible, or other places such as Google code.
For people using svn:externals to the trunk, the core team suggest to use Piston, which copies a snapshot of the code into your own repository and knows how to update this snapshot. For people using a specific tag instead, the SVN repository is still available (read only).
Downloads at http://silverstripe.org/modules will continue to be available.
Previous SVN repositories are now read only and contributions will be managed via pull requests (see http://help.github.com/pull-requests for a good tutorial).
-
[Harrie Verveer]TechPortal article on database version control January 20, 2011
Last year I spoke at different conferences throughout Europe about database version control. However, a while ago I decided that I did the talk often enough and that it’s time to move on. Therefor I wrote a big wrap-up article that summarizes everything I told (and learned) during these events. I’m proud to announce that this article was published on ibuildings’ techPortal site today!You can find the article here:
http://techportal.ibuildings.com/2011/01/11/database-version-control/ -
[Harrie Verveer]Video: me doing my database version control talk at PHPNW10 December 9, 2010
As mentioned before I’ve done my talk “database version control without pain” a couple of times last year. When I did my talk at PHPNW10 in Manchester the organisation made videos of the different speakers, and last week they published the video of me doing my talk on blip.tv.
I found it quite shocking to see myself doing my talk. I’m sure we’ve all been there: you record your own voice (for example on you answering machine) and when you hear it back it sounds really awkward. It’s that feeling, but then with video… in a foreign language… 1 hour long. Anyway, I learned a lot from seeing this back, and I can definitely use the video to improve my future talks. For example: I now see I really need to be more aware of where I keep my hands when I’m speaking, and that my English still has this annoying dutch accent going on
Anyway, if you missed the talk or if you want to see it again, or if you simply want to see me speak in front of an audience (hi mom!) this is your chance!
The slides hard to read on the video because of the low picture quality. Therefor I’d recommend keeping the associated slides next to it and click along with me.
Many thanks to PHPNW, Magma Digital and everybody else involved in organizing this great conference and making and publishing the videos.
-
[Devis Lucato]Anonymous objects in PHP - Composition, Mocks, Refactoring November 30, 2010
PHP allows to define anonymous object in two different ways:
Casting an array to object:
$object = (object) array('date' => time());Using the internal class:
$object = new stdClass; // == (object) array(); $object->date = time();
Both solutions allow to instantiate an anonymous object with properties. They are used as value objects and have no other purpose than storing values, so no logic can be included and they don't come with methods. They can be used as function parameters instead of arrays, for instance. PHP 5.3.0 introduced anonymous functions and closures, so it is now possible to attach functions to these VOs (*).
$object = new stdClass; $object->date = time(); $object->foo = function() { echo "Hello!"; };The first thing to notice is that these properties are not methods but callable functions:
$object->foo(); // error: Call to undefined method stdClass::foo() call_user_func($object->foo); // ok
And foo() doesn't have access to date; a method that accesses $object->date needs to be a closure:
$object->printDate = function($format) use ($object) { echo date($format, $object->date); }; call_user_func($object->printDate, 'Y'); // => 2010Writing call_user_func() everytime is bit annoying and prevent using the attached closures as methods, but there is a viable solution: introducing an Anonymous class:
class Anonymous { function __call($name, $arguments) { // Note: Closure class is an implementation detail // so it might be removed or renamed in the future if (isset($this->$name) && $this->$name instanceof Closure) { return call_user_func_array($this->$name, $arguments); } } } $object = new Anonymous; $object->date = time(); $object->printDate = function($format) use ($object) { echo date($format, $object->date); }; $object->printDate('Y'); // => 2010There are three possible use cases of anonymous objects: object composition, unit testing and application design/refactoring.
- A similar approach can be used to attach new methods to an existing object adding additional behaviour dynamically (__call needs to support the attached closures). Attached methods however don't have access to private and protected properties/methods.
- Testing often use mock objects to inject fake elements and define a behaviour at runtime; these mocks can be defined as anonymous objects.
- Designing an application often requires to put in place new objects very quickly, without having a dedicated class defined yet. Refactoring often abstract code out of a class, an anonymous object can be used as a temporary container for the logic to move.
(*) Attaching objects to a stdClass introduces some complexity, for example php closures are not serialisable as one would expect from a value object.
- A similar approach can be used to attach new methods to an existing object adding additional behaviour dynamically (__call needs to support the attached closures). Attached methods however don't have access to private and protected properties/methods.
-
[Harrie Verveer]Sharing slides is a gesture November 17, 2010
Whenever I do a talk, it always surprises me how many people ask about my slides. Some people couldn’t make it to the conference and ask for the slides instead, others want to have the slides as a reminder of the talk and some of them might even use the slides to do a slide karaoke back at the office. Great purposes, and usually I’m more than happy to share them with the world. What surprised me even more however, is that some people seem to get upset whenever a speaker decides to not share his slides – or simply forgets about uploading them, or hasn’t come round to doing it just yet. “Whenever a speaker doesn’t upload his slides to slideshare, God kills a kitten”, seems to be the general thought. I disagree for a number of reasons.
My talk is more than just my slides
Photo by Jeroen van Sluijs
First of all, talks are usually more than just the slides. For example: during the most important part of my last talk, my slides showed a picture of a slice of pizza for about 3 minutes. By browsing through someones slides you might get a general idea on what the talk is about, but you’re missing out on the details – and probably the most important message the speaker is trying to get across. It gets even better when the speaker uses more “Presentation Zen”-like techniques. Thijs Feryn‘s talk “PHP through the eyes of a hoster” is a fantastic talk, but if you would download his slides you would mainly just end up with a bunch of beautiful, but meaningless photographs (and some keywords).
That however is the way slides should look! They should confirm and strengthen what the speaker is telling, not tell the story themselves. Therefore sharing your slides then is not only pointless, but whenever somebody is going to see your slides it’s likely that the viewer will get a crooked view of what you were trying to tell in the first place.
Sharing slides is a gesture
I share them however, and most speakers do. My main reason is that people can browse through the slides again afterwards, and remember what I told them. If I did a good job the pizza-slide will then remind the viewer of the used metaphor and what I was trying to tell when this slide was on the screen. Even people that didn’t see my talk might still get a clue about the contents by looking at the slides. They will probably miss the main clue, but at least they will see a couple of mentioned tools that are worth giving a try – and if that makes you happy that’s just great! In such a case I’m glad you at least found some benefit in the work I’ve done.Sharing slides is a gesture though. Something extra the speaker does especially for you. Not sharing slides is not “evil”, it’s normal. There are plenty of reasons why a speaker wouldn’t upload his slides. Maybe he thinks the slides themselves shouldn’t be viewed because the viewer would miss out on so much background information and explanations that it makes the talk look plain and stupid. Maybe there’s a reason like copyright restrictions on used photographs, or maybe the speaker doesn’t want to share his slides because he wants to do the same talk somewhere else next month and he doesn’t like it when people in the audience are already reading his slides before he has even started the talk.
Either way, it’s the speaker’s choice and not something that should be taken for granted. Sharing slides is a little bit extra a speaker did for you, and worth saying “thank you” for. Speakers don’t owe you anything, they usually don’t get paid (worse: they often even have to pay for their own expenses just to be at the conference) and they’ve usually put many hours into preparing their talks. All just for you! So next time, be grateful for the efforts they’ve put into it and give them a beer, instead of bitching about speakers that for whatever reason chose not to share their intellectual property with the entire world.
-
[Harrie Verveer]Burying a talk: a year of database version control November 9, 2010
About a year ago the idea was born to do a talk on database version control. Main reason: I didn’t really have a clue about database version control myself. What’s the right approach? What tools are out there? Am I doing database version control the right way, or is there a better way? While figuring all this out I decided to document all my steps, and finally use the results in a talk so I could share them with the rest of the world. I submitted a talk called “Database version control without pain” to the dutch conference PFCongres, and it got accepted!
Zwolle, Amsterdam, Manchester and Barcelona
Soon after that I got to repeat the same talk at other places in Europe. During these events I met some great people, had a lot of fun, saw cities I had never been before and I learned a lot as well. I tried to improve my talk based on the feedback I got, and sure enough most people seemed to like it!
In total, I did the talk four times now:
- PFCongres, Zwolle (The Netherlands)
- DPC, Amsterdam (The Netherlands)
- PHPNW10, Manchester (UK)
- PHP Barcelona, Barcelona (Spain)
Enough!
I think four is enough. I assume that the majority of the European PHP community has visited at least one of the events above, and had the chance to see the talk (and besides that I’m getting a bit tired of repeating the same story over and over again
). It’s time for something new! I don’t exactly know what it will be, but I have several ideas on new talks so hopefully I can make at least one of those ideas into a talk, and start submitting to the calls for papers at the different conferences really soon!Article on database version control
As a last hoorah I’m planning to write an article on database version control, which will probably be published either here or on techportal. Missed the talk? Looking to refresh your memory? Or downloaded the slides and you’re still wondering what the heck I was talking about during the slide with the picture of a sunken boat? No worries! Just keep an eye on this site and you’ll be able to read all about it soon.
But for now: Farewell database version control talk! You’ve been a great friend!
-
[Mark van der Velden]PHP Quiz part 4 November 9, 2010
It has been a while, but here is part 4 of the PHP Quiz series! A few questions to crack your brain about, or perhaps you know them all? Try them and find out! Also do read the idea behind these quizzes, here: The PHP Quiz series
As always, think of the answer before you execute the code or look it up. Codepad might help you run the examples. You can find round three here.
Visibility is key
Now you see me, now you don't
class testClass {
private $fubar = "rabuf";function test($test) {
var_dump($test->fubar);
}
}class dummy {
function test($test) {
var_dump($test->fubar);
}
}$object1 = new testClass;
$object2 = new testClass;
$dummy = new dummy;$object1->test($object1); // Can $object1 see the private property of object1 ?
$object1->test($object2); // Can $object1 see the private property of object2 ?
$dummy->test($object1); // Can $dummy see the private property of object1 ?
Static, sticky, icky
class test {
public $counter = -1;public function increment() {
static $cnt = 0;
$this->counter = ++$cnt;
return $this;
}
}$object1 = new test;
$object1->increment()->increment();$object2 = new test;
// What will the output be
echo $object2->increment()->counter;Getting the class
class b {
function getClassA() {
echo get_class($this);
}
function getClassB() {
echo get_class();
}
function getClassC() {
echo __CLASS__;
}
}class a extends b {
}$a = new a;
// What will be returned, 'a' or 'b' ?
$a->getClassA();
$a->getClassB();
$a->getClassC();The strptime function
$result = strptime('2010-11-28', '%Y-%m-%d');// What is the output?
echo $result['tm_mday'] .'-'. $result['tm_mon'] .'-'. $result['tm_year'];
The oldtimer
for ($i = 0, $loop = 10, $a = 10; $i < $loop; ++$i, $a = 0) {
++$a;
}// What is the output ?
echo $a; -
[Evert Pot]slowdeath - a simple denial of service attack for most PHP-based servers November 9, 2010
The problem with Apache's approach to dealing with multiple clients, is that there's only ever a limited amount of Client processes available. This is usually is around a few hundred on common webservers.
Because of this, it becomes necessary to handle HTTP requests as quickly as possible. As soon as a request is handled, it can go on serving the next. If a client happens to have a slow connection, this can have a direct effect on the scalability of your frontend server.
A common way to fight this, is to put a caching server in front of your webserver, such as Varnish or Squid. These webservers are better suited to deal with many clients. This will allow your Apache server to send back HTTP responses quickly to the reverse proxy, and let the proxy deal with sending back the response to the client.
However, this doesn't deal with slow requests. Generally, these proxy servers will open connections directly to the backend webserver to avoid having to buffer larger request bodies.
Because PHP installations generally use apache 'prefork mpm', the number of possible connections is considerably low. This is also often the case with Fast-CGI based webservers, such as nginx and lighttpd. So if you were to just able to open up a few hundred connections, and drip in the bytes for the request body it would be very easy to take these servers down.
To test this theory, I wrote a simple python script that does exactly this, you can grab it from github. To use it, try something like this:
-
python slowdeath.py --threads 200 http://localhost/
In my case my webserver was limited to 150 connections. It took about a second for it to stop serving requests.
Big warning: This tool is for research purposes only. Use at your own risk, and only on servers you own.
To take out a server, simply specify a number of threads higher than the MaxClients or whatever setting your webserver happens to use. Note that I only tested this on a few servers, so results may vary. Side effects include diarrhea, rashes, blackouts and death. Do not use while driving.
-
-
[Harrie Verveer]The PHP community: not just about tree hugging geeks October 29, 2010
The interesting people you meet at user group meetings, all the stuff you learn at conferences and the freely available open source projects that are out there. Those are just three of many things that make the PHP community into something awesome. However, every once in a while I hear some plea for the great PHP community that makes me a bit nauseous. “Sharing code, knowledge, elePHPants, it’s all so fun to share and be part of this great club of lovely people! I share my project with you and you share your project with me. And then we hug! Weeeee!!!”
Well f*ck that. Of course, whenever you regularly meet a group of people you will make some new friends, especially when you share a common interest. You might enjoy drinking beers with them or do some coding on their projects, because you think it’s a great project and you want to help them out. However, you will also make friends when you go out to the pub. Hell, you will probably even make friends at the weekly meeting for people with ingrown toenails – which is great, sure! But saying ingrown toenails are a great thing because “you meet such great people” just seems a bit awkward to me.
Don’t get me wrong: I love the sharing, partying and beer drinking as well, and I wouldn’t want to miss it. The most interesting people you meet on conferences are the people you meet on the social events afterwards, but it’s all a consequence, not a cause. Saying you are part of the community because of it, is like saying to your date that you went through the whole process of “having sex and all” because you like smoking the cigarette at the end so much.
So besides the hippy arguments, the PHP community also has some great, more down-to-earth things to offer for companies, developers and everybody else involved. Below I just listed a couple of arguments I could come up with by brainstorming for ten minutes. I’m pretty sure I might have missed one or two, but it should at least give you an idea.
Contributing to open source projects
On first sight, contributing to open source projects might look like charity. You and your noble steed have come to save the day, and with all your goodness you fix a bug in the project. Hooray! But contributing has some other upsides as well. First of all, it gives you a bit more renown and might make your resume look more interesting. How great would it be if you could put “regular contributor to the Zend Framework project” on there? By contributing you would probably learn a lot as well, just by looking into other people’s code and writing documentation or tests for it.Or if you’re using an open source product but you need to fix or extend it for your own purpose, giving back the changes you made means you can now keep updating to newer versions of the product. Your changes will be in it, so you won’t be stuck with the same version for the rest of your life because you added some custom hacks and an update would overwrite those changes.
Open sourcing your own projects
This one is rather simple; open sourcing your own project means that – if you do it right – people will start contributing to it. When your company has developed a somewhat useful tool, open sourcing it will probably mean that other people will start using the tool as well. Eventually, some people might come and fix some bugs in it, improve the tool and build some new features for it. Your tool gets enhanced, extended, better – for free!Conferences and user group meetings
Events like conferences are a great place to learn and to get inspired, but can also be used for recruitment and networking. People might learn from the talks, or from talking to each other. Talks can also be inspirational and work as a trigger for somebody to start investigating a technique or a tool he didn’t think of before.
If you do it right, speaking at such events will give you and the company you’re representing some great renown. Tens, maybe hundreds of potential colleagues, clients and people who might hire you see that you, and the company you’re representing, really know what it’s all about. As a company it’s a great way to show that you take software development seriously, and that your office is a great place to work. The speaker gets a bit more fame, and can add another interesting line on his resume.
I did a little research on this subject by starting a poll about a week ago. The results were somewhat surprising, and they are probably hopelessly unreliable because of the group that answered the poll
Still, it gives you a bit of an idea:67% goes there to talk to interesting people
58% goes there for the talks (which I had expected to be more, since that’s the main thing conferences have to offer – the stuff they’re selling)
53% goes there for the hugging and beer drinking
25% goes there to speak (you might see now what I meant by hopelessly unreliable
)24% goes there to promote themselves
16% goes there to promote their company, brand or product
8% goes there to recruit new developers
19% selected “other”. Answers included “I go there to convert all over to ruby” and one voter goes to PHP conferences to “be away from the missus”
The community online
Whether it’s on IRC or on some forum, there are some great resources out there for getting and sharing information, pretty much for the same reasons as the speaking and attending talks part: the person answering a question get a bit more renown, the person asking the question has his problem solved and can continue. Also, answering questions often forces you to do a little research yourself, how did this work again exactly? Somebody else might give an alternative answer to the question that would work just as well, but you hadn’t thought about that solution yet. You can really learn a lot from answering other people’s questions!The hippy thing
And then of course, there’s the hippy thing. A great network of people that help each other out, give each other advice, do an open source project together and then drink some beer. This post is to show that it’s not the only thing that matters – but of course it is an important reason why it’s a lot of fun to be a part of the PHP community. -
[Evert Pot]Internationalized domain names, are you ready? October 23, 2010
Since may 11 TLD's (top-level domainnames) have been added. In order for this to work successfully, a lot of applications will have to be fixed.
Many email-validation scripts might use an approach like this:
-
$ok = preg_match('/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,6}$/i', $email);
This one is pretty simple, it matches the most common address formats, as long as the tld (.com, nl, .uk, etc) is under 6 characters. For a bit more sophistication you might want to ensure that the tld is a bit more valid:
-
$ok = preg_match('/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|asia|jobs|museum)$/i',$email);
Note: both these regexes were taken from regular-expression.info. The top google hit, and decent examples.
The new TLD's use non-ascii characters, and they might become aliases for existing top-level domains, or new tld's altogether. Here are the currently working examples:
- http://مثال.إختبار - Arabic.
- http://例子.测试 - Chinese (simplified)
- http://例子.測試 - Chinese (traditional)
- http://παράδειγμα.δοκιμή - greek
- http://उदाहरण.परीक्षा Hindi
- http://例え.テスト - Japanese
- http://실례.테스트 - Korean
- http://مثال.آزمایشی - Persian
- http://пример.испытание - Russian
At first sight these look like regular utf-8, characters, but if you look at the sourcecode of this page, you'll notice that it's actually encoded differently.
The korean url http://실례.테스트, is actually encoded as http://xn--9n2bp8q.xn--9t4b11yi5a/. This is called Punycode.
If you want support for these new urls (and thus domainnames in emails), you should have support for punycode. You will likely receive UTF-8 encoded domainnames for email address (example@실례.테스트), but internally you must make sure that you only deal with the punycode representation.
This translating is also what modern browsers do. If you were to paste "http://xn--9n2bp8q.xn--9t4b11yi5a/" directly in the firefox address bar, it will show you the UTF-8 characters instead. Firefox will re-encode to punycode though and use that format for HTTP requests.
The best way really to check for valid email addresses is to use a very liberal regex, but verify with a simple MX record lookup if a mailserver exists for the given domain. This example is an expansion on the first regex.
-
$email = 'example@xn--9n2bp8q.xn--9t4b11yi5a';
-
-
if(preg_match('/^[A-Z0-9._%+-]+@([A-Z0-9.-]+\.[A-Z0-9-]{2,})$/i', $email,$matches)) {
-
$hostname = $matches[1];
-
if (!getmxrr($hostname, $hosts)) {
-
echo "Host has an MX record\n";
-
} else {
-
echo "Host does not exist or does not have an MX record\n";
-
}
-
} else {
-
echo "Email address did not match regular expression\n";
-
}
The preceeding code does not convert UTF-8 to punycode though. There's not yet an easy native way in PHP to do this, but Pear's Net_IDNA2 provides a way. The implementation seems very complex though, and leaves me wondering if there's an easier way to go about it.
-















































