Distributed work with Gearman

Permanent Link: Distributed work with Gearman 16. November 2011 Comment No Comment

Gearman is an easy-to-use message queue system with binaries for many programming languages. I gave a 1hr session about Gearman at this years International PHP Conference in Mainz, you can watch the slides here:

If you have any questions regarding Gearman feel free to ask in the comments. I hope the slides clear up most of it.

Developer's shame day: Dear god

Permanent Link: Developer's shame day: Dear god 3. November 2010 Comment No Comment

Since today is Developer's shame day, I dug deep in my old code and found some really terrifying stuff. Both code examples are several years old.

The first example needs no further explanation:

case "submit_add":

$some=0;

$tempo_add = file("users.gspb");
while (list($key, $value) = each($tempo_add)) {
$tempo_add[$key] = eregi_replace("\n", "", $value);
$tempo_add2 = explode("<~>", $tempo_add[$key]);
$user[$key] = $tempo_add2[1];
}

while(list($key2, $value2) = each($user)) {
if ($feld[0] == $user[$key2]) { echo "Fehler
Der Username $user[$key2] ist bereits belegt. Bitte wählen sie einen anderen Usernamen.
[Zurück]"; $some=1; break; }
}

if ($some!=1) {
if (empty($feld[0])) { echo "Fehler
Das Feld Username ist leer.
[Zurück]"; }
elseif (empty($feld[1])) { echo "Fehler
Das Feld Passwort ist leer.
[Zurück]"; }
elseif (empty($feld[2])) { echo "Fehler
Das Feld Passwort (Wdh.) ist leer.
[Zurück]"; }
elseif (empty($feld[3])) { echo "Fehler
Das Feld Nickname (im GSPB) ist leer.
[Zurück]"; }
elseif (empty($feld[8])) { echo "Fehler
Das Feld E-Mail Adresse ist leer.
[Zurück]"; }
elseif (empty($feld[4])) { echo "Fehler
Das Feld Realname ist leer.
[Zurück]"; }
elseif (empty($feld[5])) { echo "Fehler
Das Feld Alter ist leer.
[Zurück]"; }
elseif (empty($feld[7])) { echo "Fehler
Das Feld Woher stammt dein Nickname? ist leer.
[Zurück]"; }

elseif (preg_match("/<~>/", $feld[0])) { echo "Fehler
Die Zeichenkombination <~> ist unzulässig.
[Zurück]"; }
elseif (preg_match("/<~>/", $feld[1])) { echo "Fehler
Die Zeichenkombination <~> ist unzulässig.
[Zurück]"; }
elseif (preg_match("/<~>/", $feld[2])) { echo "Fehler
Die Zeichenkombination <~> ist unzulässig.
[Zurück]"; }
elseif (preg_match("/<~>/", $feld[3])) { echo "Fehler
Die Zeichenkombination <~> ist unzulässig.
[Zurück]"; }
elseif (preg_match("/<~>/", $feld[4])) { echo "Fehler
Die Zeichenkombination <~> ist unzulässig.
[Zurück]"; }
elseif (preg_match("/<~>/", $feld[5])) { echo "Fehler
Die Zeichenkombination <~> ist unzulässig.
[Zurück]"; }
elseif (preg_match("/<~>/", $feld[6])) { echo "Fehler
Die Zeichenkombination <~> ist unzulässig.
[Zurück]"; }
elseif (preg_match("/<~>/", $feld[7])) { echo "Fehler
Die Zeichenkombination <~> ist unzulässig.
[Zurück]"; }
elseif (preg_match("/<~>/", $feld[8])) { echo "Fehler
Die Zeichenkombination <~> ist unzulässig.
[Zurück]"; }

elseif ($feld[1] != $feld[2]) { echo "Die Passworteingaben stimmen nicht überein!
[Zurück]"; }

else {

$feld[7] = eregi_replace("\n", "
", $feld[7]);

$feld[0] = stripslashes($feld[0]);
$feld[1] = stripslashes($feld[1]);
$feld[2] = stripslashes($feld[2]);
$feld[3] = stripslashes($feld[3]);
$feld[4] = stripslashes($feld[4]);
$feld[5] = stripslashes($feld[5]);
$feld[6] = stripslashes($feld[6]);
$feld[7] = stripslashes($feld[7]);
$feld[8] = stripslashes($feld[8]);

unset($feld[2]);


$temp['a']=implode("<~>", $feld);

$zeilen = file("users.gspb");
while (list($key, $value) = each($zeilen)) {
$temp[$key] = eregi_replace("\n", "", $value);
}


$output=implode("\n", $temp);

$file = fopen("users.gspb", "w");
fputs ($file, $output);
fclose($file);

echo "Sie wurden erfolgreich hinzugefügt. Klicken sie hier, um auf die Startseite zurückzukehren";
}
}

break;

This second example actually made me laugh real hard. It's the only line in a file named index.php:

<?php require('index2.php');>  

Introducing: "Developer's shame day"

Permanent Link: Introducing: 26. Oktober 2010 Comment No Comment

Cem Demin (aka "der php hacker") had a great idea: The "Developers shame day". A day on which every developer posts some old code of his that is so awful it really hurts.

The first developers shame day will be on Nov. 3rd, 2010. Let's all participate!

var_dumpo

Permanent Link: var_dumpo 20. September 2010 Comment No Comment

This common typo will never bug you again!

function var_dumpo()
{
$arguments = func_get_args();
call_user_func_array('var_dump', $arguments);
}

;-)

Ext.JS: Global shortcuts (e.g. close current tab)

Permanent Link: Ext.JS: Global shortcuts (e.g. close current tab) 17. März 2010 Comment No Comment

Ext.JS has an easy way of configuring keyboard shortcuts using Ext.KeyMap. If you wanna use global shortcuts you simply have to bind it to document. I wanna show you in an example how you can bind Alt+X to closing the current tab of a TabPanel layout accessible by the JavaScript variable tabPanelLayout.

new Ext.KeyMap(
document,
[
{
// Alt + X: Close current tab
key: 'x',
alt: true,
// Prevent any browser actions triggered by the shortcut that may occur
stopEvent: true,
fn: function() {
var activePanel = tabPanelLayout.getActiveTab();
Layout.closeTab(activePanel);
}
}
]
);

That's about it! If you only have this one shortcut, you can leave out the JS array.

Cookies in JavaScript

Permanent Link: Cookies in JavaScript 29. Juli 2009 Comment No Comment

Using in Cookies is rather complicated. All the cookies are in the string document.cookie. Here's a little static class I wrote a while ago to handle saving, reading and removing Cookies in JavaScript

/**
* Static Class for Cookie functions
*/
function Cookie()
{
}

/**
* Save new Cookie
*
* @param string name
* @param string value
* @param number days
*/
Cookie.save = function(name, value, days)
{
if (typeof days != 'undefined') {
var date = new Date();
date.setTime(date.getTime() + (days*24*60*60*1000));
var expires = "; expires=" + date.toGMTString();
} else {
var expires = "";
}
document.cookie = name + "=" + value + expires + "; path=/";
}

/**
* Read Cookie value
*
* @param string name
*/
Cookie.get = function(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') {
c = c.substring(1,c.length);
}
if (c.indexOf(nameEQ) == 0) {
return c.substring(nameEQ.length,c.length);
}
}
return null;
}

/**
* Delete Cookie
*
* @param string name
*/
Cookie.remove = function(name) {
Cookie.save(name,"",-1);
}

Canonicals: What they are and how to use them

Permanent Link: Canonicals: What they are and how to use them 10. Juni 2009 Comment No Comment

Today I wanna give a short insight on canonicals. A canonical is a (fairly new) SEO item in the head section of a HTML document, which contains the parent URL. Let's say you have a page which contains a sortable list whose URL is www.chip.de/sortable-list. Besides AJAX-sorting you also have sorting with URLs like www.chip.de/sortable-list/order/price/dir/desc, which reloads the page showing the sortable list ordered by the price descending. Usually you either 2 would have pages in the Google index with the almost the same content or 1 page since you put noindex,nofollow in one of them. This is where canonical comes into play:

First off you leave the noindex, nofollow in the page with the sorted list. Second you put a canonical into the HTML head section telling search engines where it can find the original list!

<link rel="canonical" href="/sortable-list">

That way the sorted list URL will not only not be in the Google index but the original URL gets more "attention".

The latest Firefox versions already show if a canonical is defined:

Canonicals in Firefox

P.S. It does no harm to put the canonical tag in the original page itself.

CLI: Catching Ctrl+C, kill commands and fatal errors

Permanent Link: CLI: Catching Ctrl+C, kill commands and fatal errors 31. Mai 2009 Comment No Comment

Sometimes, when you write a Command Line (CLI) script, you want to catch Ctrl+C. Simple Example: Your script does some stuff on the database and you don't want to leave it in the state it had in the moment the script was killed, Ctrl+C'd or quit by a fatal error.

First off, we have to tell PHP to use ticks, in order for the catches to work (for more information on ticks check the pcntl_signal() documentation):

declare(ticks = 1);

Then, we add shutdown functions to the classes that have to rollback the state:

/**
* Cleanup if process has been killed unexpectedly
*
*/
private function shutdown()
{
$this->getTargetDb()->rollBack();
print('Script quit unexpectedly. Doing rollback' . PHP_EOL);
exit();
}

/**
* Method that is executed, when a Fatal Error occurs
*
*/
public function fatalErrorShutdown()
{
$lastError = error_get_last();
if (!is_null($lastError) && $lastError['type'] === E_ERROR) {
$this->shutdown();
}
}

/**
* Method, that is executed, if script has been killed by
* SIGINT: Ctrl+C
* SIGTERM: kill
*
* @param int $signal
*
*/
public function sigintShutdown($signal)
{
if ($signal === SIGINT || $signal === SIGTERM) {
$this->shutdown();
}
}

You shouldn't forget to include an exit() at some point in the shutdown methods or your script won't quit at all. In the example the shutdown() method (which is called by both shutdown methods, that will be registered afterwards) quits the script as soon as it's done.

As a last step, you only have to register your shutdown methods and you're done:

$sync = new Process_Sync();
// Catch Fatal Error (Rollback)
register_shutdown_function(array($sync, 'fatalErrorShutdown'));
// Catch Ctrl+C, kill and SIGTERM (Rollback)
pcntl_signal(SIGTERM, array($sync, 'sigintShutdown'));
pcntl_signal(SIGINT, array($sync, 'sigintShutdown'));

Redirecting to an Error page when a Fatal Error occurs

Permanent Link: Redirecting to an Error page when a Fatal Error occurs 7. April 2009 Comment No Comment

Everyone surely has already encountered it: A fatal error on a page that is online including the white page you get to see because of it. There's a nice and easy way of showing an error page instead of a white page if a Fatal Error occurs by using output buffering. According to its documentation, ob_start() supports a callback as first parameter. The cool thing is that this callback is also called in case of a Fatal Error!

Here's how it works:

class Redirector
{
public static function redirectOnError($buffer)
{
$lastError = error_get_last();
if(!is_null($lastError) && $lastError['type'] === E_ERROR) {
header('HTTP/1.1 302 Moved Temporarily');
header('Status: 302 Moved Temporarily');
header('Location: error.php');
exit();
}
return $buffer;
}
}

ob_start(array('Redirector', 'redirectOnError'));
print('Hello');

foobar();

ob_end_flush();

The callback is called in the moment the content is flushed, that's why the flush should always be the last line in your output script if you wanna catch all Fatal errors. Since the callback is always executed, I added a check whether a fatal error occured, since we don't want a redirect when no error occurs. Also keep in mind that the callback has to return the buffered output (that is already passed to the callback as a parameter) which will be printed after its execution. (Note that error_get_last() is PHP >= 5.2.0)

Note: Instead of an 302 redirect, which I used in the example, you could also just output an error message on the same page:

public static function redirectOnError($buffer)
{
$lastError = error_get_last();
if(!is_null($lastError) && $lastError['type'] === E_ERROR) {
header('HTTP/1.1 503 Service Temporarily Unavailable');
header('Status: 503 Service Temporarily Unavailable');
$buffer = 'Sorry, an error occured';
}
return $buffer;
}

Thanks to Jakob and Lars for some input and refinements on this blogpost.

Unit Tests: How to test for Exceptions

Permanent Link: Unit Tests: How to test for Exceptions 3. April 2009 Comment No Comment

When unit testing, you'd also want to test whether your application throws Exceptions as expected (the following examples are based on SimpleTest). Assumption for the examples is, that we have a method that expects an integer as parameter.

First way you probably come up with is this:

try {
$class->method('abc');
} catch(Exception $e) {
$this->assertIsA('Exception', $e);
}

Generally this looks ok, but it's not. If the method doesn't throw an exception, the test won't fail since the catch block is never executed. That's why we simply drag the test out of the catch block:

try {
$class->method('abc');
} catch(Exception $e) {
}
$this->assertIsA('Exception', $e);
unset($e);

Now the test fails when the exception isn't thrown because first of all $e won't be set and will surely not be an exception. It is important to add an unset($e), especially if you're testing for more exceptions directly afterwards.

Let's now assume that the method throws an InvalidArgumentException if the given parameter is not an integer.

try {
$class->method('abc');
} catch(Exception $e) {
}
$this->assertIsA('InvalidArgumentException', $e);
unset($e);

Now the test is in a state where it fails when no exception is thrown or when the thrown exception is not an InvalidArgumentException.

In case you're not lazy on typing, you might add one more line, which also allows you to put the assert back into the catch block:

try {
$class->method('abc');
$this->fail('Excepted exception was not thrown');
} catch(Exception $e) {
$this->assertIsA('InvalidArgumentException', $e);
unset($e);
}
		
		

Don't use NOW() in MySQL SELECT Queries

Permanent Link: Don't use NOW() in MySQL SELECT Queries 18. März 2009 Comment No Comment

You maybe want to show all the products that have been added to your database in the last 7 days on your high-traffic website. You are using MySQL and your query probably looks like this:

SELECT *
FROM product
WHERE created >= NOW() - INTERVAL 7 DAY

Although being correct, this query is a bad query, because it is using NOW(). NOW() returns the current date and time to the split second. In other words: This query will never land in the cache query!

The better way would be to read the current day in PHP and pass it on to the query:

$date = date('Y-m-d');
$sql = '
SELECT *
FROM product
WHERE created >= "%s" - INTERVAL 7 DAY
';
$sql = sprintf($sql, $date);

Now we have a SQL query that perfectly lands in the cache query.

hitting the fork button on github

Permanent Link: hitting the fork button on github 27. Februar 2009 Comment No Comment

http://github.com/sebs/laconica/tree/master

The github (sf.net clone) has one big difference in the ui: a button called fork.

You simply copy any repo. into your own and fork it. The result is public so it follows the open source idea quite well.

Zend Framework: Generating URLs from defined routes

Permanent Link: Zend Framework: Generating URLs from defined routes 23. Februar 2009 Comment No Comment

The Zend Controller Router allows you to easily create your own routes. That is very useful, but it seems rather unknown, that you can also use these routes to generate URLs according to their definition.

Let's assume you have a route like this:

$controller = Zend_Controller_Front::getInstance();
$router = $controller->getRouter();

$productRoute = new Zend_Controller_Router_Route(
':pid/:name',
array(
'module' => 'default',
'controller' => 'product',
'action' => 'show'
),
array(
'pid' => '\d+'
)
);
$router->addRoute('productpage', $productRoute);

In this route we parse a URL that is supposed to show a product page. The URL will look like http://www.phpdevblog.net/4815162342/Microsoft+Windows+XP+Professional for example. The Router would explode it into pid=4815162342 and name="Microsoft Windows XP Professional"

To generate a URL with the help of the Router you simply need to call the assemble method, which you can either call from the router or the definied route, but keep in mind that you will get different results!

From the router the assemble would look like this:

$link = $router->assemble(
array(
'pid' => '4815162342',
'name' => 'Microsoft Windows XP Professional'
),
'productpage'
);

The result of this method is /4815162342/Microsoft+Windows+XP+Professional

The only difference, if you call the assemble method from the defined route, is that you don't need the route name in the second parameter:

$link = $productRoute->assemble(
array(
'pid' => '4815162342',
'name' => 'Microsoft Windows XP Professional'
)
);

Here the result is 4815162342/Microsoft Windows XP Professional

Useful Firefox addons for web developers

Permanent Link: Useful Firefox addons for web developers 9. Februar 2009 Comment No Comment

Here's a short list of useful Firefox addons for web developers:

Firebug

Probably the most useful addon around. You cannot only change the whole page on the fly (HTML and CSS) but you also have the very useful Javascript console which can even be used by your application for debugging. Furthermore you can see all AJAX activities. Download at https://addons.mozilla.org/de/firefox/addon/1843

FirePHP

FirePHP allows you to send debug messages to Firebug through a PHP Script. Download at https://addons.mozilla.org/de/firefox/addon/6149

Greasemonkey

Greasemonkey allows you to create JavaScript scripts for specific or all websites (Use of wildcard * possible!). This can be very useful to either test how new scripts would integrate into your website or to change the look and behaviour of any other webseite. Download at https://addons.mozilla.org/de/firefox/addon/748

Stylish

Same as Greasemonkey only for CSS. This way you can easily test new styles on a website before integrating them. Download at https://addons.mozilla.org/de/firefox/addon/2108

Web Developer

The classic one. Allows you to easily change behaviour of your browser (caching, JavaScript, etc.) and has a whole lot of other useful tools like showing document size, the styles, submitting the page to the W3C Validator, Browserframe resize (to test your application for specific resolutions), viewing response headers, outlining specific elements and so on. Download at https://addons.mozilla.org/de/firefox/addon/60

Search Status

Although it also shows the page rank (not very reliable), I only use this addon to display all the nofollow links on a page, which is really extremely useful. Download at https://addons.mozilla.org/de/firefox/addon/321

DNS Cache

My own Firefox extension that allows you to disable the DNS caching of Firefox, which comes in quite handy when you have to check your webservers quickly. For a more detailed description see here. Download at https://addons.mozilla.org/de/firefox/addon/5914

Detecting mobile devices: Android devices

Permanent Link: Detecting mobile devices: Android devices 3. Februar 2009 Comment No Comment

I have an addition for my recent blog post "Detecting mobile devices" a while ago. In order to also detect Android phones (like the T-Mobile G1), you simply need to add the string "android" to the mobileClients array (both PHP and JavaScript). I also edited the original blogpost by adding the "android" string.

A Raid to the local Bookstore

Permanent Link: A Raid to the local Bookstore 31. Januar 2009 Comment No Comment

When it comes to buying books i am so 1992. I do buy some at onlineshops, but most time i need to be there  i the local bookstore. Today i found some books i had on my watchlist for a time:

The Productive Programmer

Anyone who develops software for a living needs a proven way to produce it better, faster, and cheaper. The Productive Programmer offers critical timesaving and productivity tools that you can adopt right away, no matter what platform you use. Master developer Neal Ford not only offers advice on the mechanics of productivity-how to work smarter, spurn interruptions, get the most out your computer, and avoid repetition-he also details valuable practices that will help you elude common traps, improve your code, and become more valuable to your team.

from: Amazon

Beeing productive as a programmer is always an issue. And since we have newbies to the programming bizz in our company it is maybe a good way to get around the beast Code Complete.

Implementation Patterns

The Kent Beck Signature Book series was nice from the beginning on. Starting with the read of Patterns of Enterprise Application Architecture I bought (but did not already read) all of that series. Getting a new one into my fingers is nice. I guess the thing goes the same direction as the above stated, just  a bit more focus on the "how to ake great api" part.

Great code doesn’t just function: it clearly and consistently communicates your intentions, allowing other programmers to understand your code, rely on it, and modify it with confidence. But great code doesn’t just happen. It is the outcome of hundreds of small but critical decisions programmers make every single day. Now, legendary software innovator Kent Beck–known worldwide for creating Extreme Programming and pioneering software patterns and test-driven development–focuses on these critical decisions, unearthing powerful “implementation patterns” for writing programs that are simpler, clearer, better organized, and more cost effective.

from: Amazon

Art of Capacity Planning

Success on the web is measured by usage and growth. Web-based companies live or die by the ability to scale their infrastructure to accommodate increasing demand. This book is a hands-on and practical guide to planning for such growth, with many techniques and considerations to help you plan, deploy, and manage web application infrastructure. The Art of Capacity Planning is written by the manager of data operations for the world-famous photo-sharing site Flickr.com, now owned by Yahoo! John Allspaw combines personal anecdotes from many phases of Flickr's growth with insights from his colleagues in many other industries to give you solid guidelines for measuring your growth, predicting trends, and making cost-effective preparations. Topics include: Evaluating tools for measurement and deployment Capacity analysis and prediction for storage, database, and application servers Designing architectures to easily add and measure capacity Handling sudden spikes Predicting exponential and explosive growth How cloud services such as EC2 can fit into a capacity strategy ….

from: Amazon

Far away from guidelines like "How o write a performant php script" etc. Just a more strategic guide of how to plan survival of the next big wave of users on your website. Pretty small book, but a good companion if you are in the "lots of users" bizz. Helps open the eyes for the things that happen outside of your code, and might get you a hint of sleeping well even with a growing  wewbapp.

Die Kunst des Webtrackings

Since our app and daily work is about webtracking, this handy little 300 page book just hits the shelfes in the right moment, just waiting to be picked up.

see: Amazon.de

No interesting books about php, just a good one about zend framework would be nice, but as far as i know, there is not yet anything.

Maybe someone who is stopping by here has a hint what can be next of my neverending list of books, best related to the stuff posted here. ;)

Using array_unique() with multidimensional arrays

Permanent Link: Using array_unique() with multidimensional arrays 31. Januar 2009 Comment No Comment

There's one problem with array_unique(): It doesn't work with multidimensional arrays. Here's an example:

$array = array(
array(
'id' => 123,
'name' => 'Some Product',
'ean' => '1234567890123'
),
array(
'id' => 123,
'name' => 'Some Product',
'ean' => '4852950174938'
),
array(
'id' => 123,
'name' => 'Some Product',
'ean' => '1234567890123'
),
);
$uniqueArray = array_unique($array);
var_dump($uniqueArray);

Two elements are exactly the same, but one element has a different EAN, yet the var_dump() returns the following:

array(1) {
[0]=>
array(3) {
["id"]=>
int(123)
["name"]=>
string(12) "Some Product"
["ean"]=>
string(13) "1234567890123"
}
}

Obviously this is unexpected behaviour. array_unique() threw out the second element, which is clearly not the same as Element 1 and 3. The easiest way I came across is using md5 hashes for comparison of the elements. All you need is to iterate over the first dimension, serialize it and create a MD5 hash of it for comparison:

/**
* Create Unique Arrays using an md5 hash
*
* @param array $array
* @return array
*/
function arrayUnique($array, $preserveKeys = false)
{
// Unique Array for return
$arrayRewrite = array();
// Array with the md5 hashes
$arrayHashes = array();
foreach($array as $key => $item) {
// Serialize the current element and create a md5 hash
$hash = md5(serialize($item));
// If the md5 didn't come up yet, add the element to
// to arrayRewrite, otherwise drop it
if (!isset($arrayHashes[$hash])) {
// Save the current element hash
$arrayHashes[$hash] = $hash;
// Add element to the unique Array
if ($preserveKeys) {
$arrayRewrite[$key] = $item;
} else {
$arrayRewrite[] = $item;
}
}
}
return $arrayRewrite;
}

$uniqueArray = arrayUnique($array);
var_dump($uniqueArray);

Now the result is the one array_unique() should have already given:

array(2) {
[0]=>
array(3) {
["id"]=>
int(123)
["name"]=>
string(12) "Some Product"
["ean"]=>
string(13) "1234567890123"
}
[1]=>
array(3) {
["id"]=>
int(123)
["name"]=>
string(12) "Some Product"
["ean"]=>
string(13) "4852950174938"
}
}

This works with as many dimensions as you like.

Extending interfaces

Permanent Link: Extending interfaces 21. Januar 2009 Comment No Comment

What many people don't know is that you cannot only extend classes in PHP but also interfaces:

interface First_Interface
{
public function show();
}

interface Second_Interface extends First_Interface
{
public function update();
}

class Product implements Second_Interface
{
public function show() {}
public function update() {}
}

You have to remember though that extending is not possible when you have the same function in both interfaces:

interface Second_Interface extends First_Interface
{
    public function show();
    public function update();
}

This will lead to a PHP Fatal Error:
PHP Fatal error:  Can't inherit abstract function First_Interface::show() (previously declared abstract in Second_Interface) in /tmp/interfaces.php on line 8

Fatal error: Can't inherit abstract function First_Interface::show() (previously declared abstract in Second_Interface) in /tmp/interfaces.php on line 8

Note: This possibility is also described in the PHP documentation for interfaces (Example #3)

JavaScript loops profiled

Permanent Link: JavaScript loops profiled 20. Januar 2009 Comment No Comment

Today I was curious and wanted to know which way of looping in JavaScript fastest. So far, I always use for var i in array, since someone once told me, it is the fastest way. For testing I created an Array with 10000 elements:

ids = [];
for (var i = 1; i <= 10000; i++) {
ids.push(i);
}

I used the JavaScript Profiler of Firebug for profiling. The testsystem was: Intel Dual Core T2500 @ 2.00 GHz, 2 GB RAM, Ubuntu 8.04, Firefox 3.0.5 (only installed addon is Firebug). I profiled each loop variation 5 times and took the average time for comparison. The loop variations did nothing but loop and were the following:

Loop 1:

for(var i = 0; i < ids.length; i++) {}

Loop 2:

for (var i in ids) {}

Loop 3:

function process(element, index, array) {}
ids.forEach(process);

When I started the test, I didn't think there would be such huge differences in the performance of those three:

JavaScript loops profiled

Here are the profiling results in detail, in case you're interested (all times in ms):

Loop 1:
1,428
0,842
0,987
0,833
0,831

Loop 2:
6,084
4,471
6,040
6,256
6,509

Loop 3 (number in brackets is the profiled runtime of the function process() - see declaration above)
33,055 (16,439)
33,262 (16,489)
33,792 (17,044)
34,682 (17,312)
35,875 (17,637)

Using count() in for()-loops

Permanent Link: Using count() in for()-loops 16. Januar 2009 Comment No Comment

Since I just stumbled across it again, here's a common "mistake" often done, which can be avoided very easily. Situation is an array, that is looped over using for(). It might look something like this:

$elements = array(
'Element 1',
'Element 2',
'Element 3',
'Element 4',
'Element 5',
);

for ($i = 1; $i <= count($elements); $i++) {
print('Processing Element #' .$i . PHP_EOL);
}

This is bad coding style because the count() is in the loop header. In this case everytime the loop moves on to the next element, the function count() is called (here 5 times), although the size of the array never changes. The right (and more performant) way to do this, would be:

$elementsCount = count($elements);
for ($i = 1; $i <= $elementsCount; $i++) {
print('Processing Element #' .$i . PHP_EOL);
}

Now count() is only called once and the code does exactly the same.

Detecting mobile devices

Permanent Link: Detecting mobile devices 13. Januar 2009 Comment No Comment

Nowadays it is getting more important to having his website also readable for mobile clients. While there is the possibility of using WURFL for detection, WURFL seems to be a bit overhead when you only want to display simple contents (mostly text). I agree, that you should use WURFL, or similar, if you're planning on providing media contents, such as videos. Still, you have to keep at the back of your mind, that it is very unlikely that you will have users using a 3 year old Siemens Mobile Phone browsing your website. The target consumers are definately iPhone users or any other user with a newer mobile phone.

When browsing to your website, people want to type the known adress (e.g. www.phpdevblog.net) instead of a new adress (e.g. www.phpdevblog.net/mobile). So obviously the best way would be to detect users using mobile devices and redirecting them to the mobile adress or using another Front Controller or… (the decision is up to you). An easy way is to check the users Useragent in order to detect mobile devices. I will show you two examples, one covering server-side detection (using php - obviously) and the other covering client-side detection using JavaScript. Both ways should cover most mobile devices, if you have any additions please let me know! Thank you

Edit on Februar 3rd, 2009: I added the needed string for Android phones to the arrays

Server-Side detection:

class Client
{
/**
* Available Mobile Clients
*
* @var array
*/
private $_mobileClients = array(
"midp",
"240x320",
"blackberry",
"netfront",
"nokia",
"panasonic",
"portalmmm",
"sharp",
"sie-",
"sonyericsson",
"symbian",
"windows ce",
"benq",
"mda",
"mot-",
"opera mini",
"philips",
"pocket pc",
"sagem",
"samsung",
"sda",
"sgh-",
"vodafone",
"xda",
"iphone",
"android"
);

/**
* Check if client is a mobile client
*
* @param string $userAgent
* @return boolean
*/
public function isMobileClient($userAgent)
{
$userAgent = strtolower($userAgent);
foreach($this->_mobileClients as $mobileClient) {
if (strstr($userAgent, $mobileClient)) {
return true;
}
}
return false;
}

}

$client = new Client();
$client->isMobileClient($_SERVER['HTTP_USER_AGENT']);

Client-Side detection:

function Client() {
}

Client.prototype.mobileClients = [
"midp",
"240x320",
"blackberry",
"netfront",
"nokia",
"panasonic",
"portalmmm",
"sharp",
"sie-",
"sonyericsson",
"symbian",
"windows ce",
"benq",
"mda",
"mot-",
"opera mini",
"philips",
"pocket pc",
"sagem",
"samsung",
"sda",
"sgh-",
"vodafone",
"xda",
"iphone",
"android"
];

Client.prototype.isMobileClient = function(userAgent)
{
userAgent=userAgent.toLowerCase();
for (var i in this.mobileClients) {
if (userAgent.indexOf(this.mobileClients[i]) != -1) {
return true;
}
}
return false;
}

var client = new Client();
client.isMobileClient(navigator.userAgent);

Strange behaviour of date()

Permanent Link: Strange behaviour of date() 2. Januar 2009 Comment No Comment

Recently I used time and date functions to calculate an ETA and remaining running time for a longer running script. When showing the remaining time using the date() function I encountered some strange behaviour:

According to the documentation date() expects the second parameter to be a UNIX Timestamp, which - as we all know - starts at 01.01.1970 00:00:00. Meaning when you print out date('H:i', 0) you'd expect to get 00:00. For some reason this is not the case:

php > print date('H:i', 0);
01:00

Does anyone know why this is the case? In my script I did a workaround by simply substracting one hour, which isn't the best style obviously.

(Note: I used the php interactive shell you can run by executing php -a to print out the date)

Reset Stylesheet

Permanent Link: Reset Stylesheet 10. Dezember 2008 Comment No Comment

In case you don't know it yet: Yahoo developed a reset stylesheet that resets all the browser specific defaults (like body padding), which is really useful for developing web pages. You can get it at http://developer.yahoo.com/yui/reset/

Using Checkboxes / Radio Buttons and Labels

Permanent Link: Using Checkboxes / Radio Buttons and Labels 24. November 2008 Comment No Comment

Common situation: You have a formular on a webpage that has some radio buttons or checkboxes in it. Most likely there is some text besides the radio button / checkbox and most likely you will not be able to click the text in order to trigger the radio button / checkbox. This is rather annoying, especially since the solution to this is fairly easy:














DNS caching in Firefox

Permanent Link: DNS caching in Firefox 18. November 2008 Comment No Comment

Sometimes you need to change servers quickly, for example if you're trying to find out which of your 18 webservers is having a problem. When modifying the hosts file, you would expect the browser to point to the new direction (at latest after restarting nscd).

When using Firefox you will have the problem that it will still not work. The reason is simple: Firefox caches DNS entries for 60 seconds by default. In case of 18 webservers that would mean that you have to restart Firefox 18 times or you will have to wait 18 minutes altogether.

For that reason I wrote a simple Firefox Plugin called DNS Cache, that allows you to deactivate the internal DNS caching of Firefox. You can download the plugin at https://addons.mozilla.org/de/firefox/addon/5914.

Catching Warnings and Notices

Permanent Link: Catching Warnings and Notices 15. November 2008 Comment No Comment

Simple example: You want to open a file in PHP and there might be a slight possibility, that the file doesn't exist. Your code probably looks like this:

$fileHandle = fopen('text.txt', 'r');
if (!$fileHandle) {
print("Couldn't open file");
}

According to the php documentation, fopen will return FALSE if it can't open the file, but (and that's the annoying part), it will also generate a warning. Furthermore the php documentation suggests using @ before the function call to suppress the warning. Using @ before a function call is really, really bad coding and it makes your code slow (since @ changes the error_reporting to E_NONE before the function call and changes it back to what you had before after the function call) . But why return false, when it will generate a warning anyhow? Throwing an Exception would be the better way. Here's a way to do it.

First, we create a class containing a public static method that handles the error:

class ErrorHandler
{
    /**
    * catches php errors / warnings / notices and throws
    * an exception instead
    *
    * @param int $errNo
    * @param string $errStr
    */
    public static function handle($errNo, $errStr=NULL)
    {
        switch ($errNo) {
            case E_WARNING:
                throw new RuntimeException($errStr,$errNo);
            break;
            default:
                throw new Exception($errStr,$errNo);
            break;
        }
    }
}

The method handle has the error number and the error message, which are both generated by php. Error number would be something like E_NOTICE, E_WARNING or E_STRICT. The error string in this case would be "failed to open stream: No such file or directory' in /var/www/phpinfo.php:15".

Now we add a public static function method to the same class, that overwrites the php error handler with the method we just wrote:

/**
* Overwrites the PHP error handler and uses our own
*
*/
public static function set()
{
set_error_handler(array(__CLASS__ , 'handle'));
}

Now there's only one thing left to do: We have to call the set method at the beginning of our script:

ErrorHandler::set();

If we now execute the script again, we will not get a warning, instead we'll get an uncaught exception: Fatal error: Uncaught exception 'RuntimeException' with message 'fopen(text.txt) [function.fopen]: failed to open stream: No such file or directory' in /var/www/phpinfo.php:15 Stack trace: #0 [internal function]: ErrorHandler::handle(2, 'fopen(text.txt)…', '/var/www/phpinfo…', 35, Array) #1 /var/www/phpinfo.php(35): fopen('text.txt', 'r') #2 {main} thrown in /var/www/phpinfo.php on line 15

The cool thing is, that now we can simply catch the exception and will not flood the phperror log with Warnings, where they are probably not needed. Our script would now look like this:

ErrorHandler::set();
try {
    $fileHandle = fopen('text.txt', 'r');
} catch(Exception $e) {
    print("Couldn't open file");
}

Note, that you can catch anything but a fatal error.

Netbeans

Permanent Link: Netbeans 14. November 2008 Comment No Comment

If you're unhappy with your IDE (for whatever reason), you might want to try out Netbeans. The PHP support is rather new and not too stable yet, but apart from the download package being really small (the zip file containing both windows and linux binaries is only 30mb large - 90mb unpacked), it provides some really nice features/advantages:

  • No more "Building workspace", indexing of projects takes seconds
  • It's really fast
  • It not only has code completion for PHP but also for CSS and JavaScript
  • It shows browser compatibilities of JavaScript functions / methods / properties
  • Very easy, good and fast renaming functionality (in the source code). Just type Ctrl+R, when you're on a variable or method and all the occurences in the file are renamed at the same time
  • It's open source

Apart from that, it provides standard features like CVS / SVN support, defining projects and so on.

I have used Zend Studio for Eclipse so far (which is already quite good in my opinion), but I was really impressed how good Netbeans is when Petr Pisl demonstrated it to me at the International PHP Conference 2008. Since then, I'm only using Netbeans. According to Petr one of the next versions will also have Symfony support.

When using it, you have to remind yourself, that the php support is still far from being finished and that it still has some bugs. You can download Netbeans at http://php.netbeans.org - the latest version is 6.5 RC2.