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.

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);
}

;-)

Live on Stage (Slides)

Permanent Link: Live on Stage (Slides) 14. Mai 2010 Comment No Comment

Here are my slides for my talk "Live on Stage" about database staging at phpday2010.

 

Some slides don't seem to have been converted correctly though.

serialize() vs. var_export() vs. json_encode() Memory Usage

Permanent Link: serialize() vs. var_export() vs. json_encode() Memory Usage 18. November 2009 Comment No Comment

Based on serialize() vs. var_export() vs. json_encode() Part 2, let's take a look at the (real) memory usage:

Apart from var_export allo methods seem to use about the same. Let's put the usage in relation to each other to get a better view:

With smaller arrays, the memory usage is exactly the same, from 10.000 elements on the values begin to diverge.

serialize() vs. var_export() vs. json_encode() Part 2

Permanent Link: serialize() vs. var_export() vs. json_encode() Part 2 17. November 2009 Comment No Comment

Based on the comments from my first benchmark in the serialize() vs. var_export() vs. json_encode article 2 days ago, I decided to benchmark once again, this time using different array sizes and I also added the JSON method with recursive UTF8 encoding beforehand. This time, all results are the combined results (exporting and importing), the testing script was exactly the same (except JSON+UTF8 which also had a utf8_encode_recursive function). Let's see what happens when using array sizes from 10 to 1000 elements:

So far, json_encode itself is fastest, closely followed by serialization. JSON with UTF8 Encoding is slowest, but consider that all the values are all below 0,02 seconds.

So far so good, let's look at the results with arrays from 10.000 to 1.000.000 elements:

Apart from the exception Serialization the values mostly develop like with smaller arrays. What's quite odd is that Serialization's runtime seems to be exploding when stepping up from 100.000 to 1.000.000. Why that is so, I cannot tell. Although JSON with UTF8 Encoding is slower than var_export and JSON itself, it's still way faster than serialization.

Conclusions

  1. None of the methods scales linear.
  2. For smaller arrays, JSON is the way to go, as long as your data is already UTF8 encoded. If not, you might want to take serialization.
  3. With larger arrays JSON is still fastest as long as your data is already UTF8 encoded, otherwise var_export is the best choice.

Closing, let's take a look at the graphs from 10 - 1.000.000 item large arrays:

serialize() vs. var_export() vs. json_encode()

Permanent Link: serialize() vs. var_export() vs. json_encode() 16. November 2009 Comment No Comment

There are times when you need to store an array, for example when your array is an index you wanna use again the next time you run your application. In order to store an array you have to transform it into some kind of string represantation first, most people would probably use serialize(). But there are also 2 other ways to achieve that: var_export() and json_encode().

After having them stored the functions to interpret the strings as arrays would be unserialize() if you use serialize(), eval() if you use var_export() and json_decode() if you use json_encode().

So, what about the performance?

In order to test that I wrote a little profiling script that first created some random array with 1.000.000 elements, then exported the array and then imported it again. For the json_encode() test the script looked like that:

$array = array_fill(0, 1000000, rand(1, 9999));

$start = microtime(true);
$export = json_encode($array);
$end = microtime(true);
$duration = $end - $start;
print('JSON Encode: ' . $duration . PHP_EOL);

$start = microtime(true);
$import = json_decode($export);
$end = microtime(true);
$duration = $end - $start;
print('JSON Decode: ' . $duration . PHP_EOL);

Apart from the exporting and importing functions used, the script for serialize() and var_export() looked pretty much the sime, var_export() being the only exception, since I had to add an ending ; to $export in order for it to work with eval().

While it is understandable that importing takes longer for every method than importing, the differences in time are quite astounding:

It's not only interesting to see that unserialize() is damn slow but also that JSON is fastest, which also gets quite clear when looking at combined results:

 

Since it's still in the Ubuntu repositories, I did the performance tests with PHP 5.2.6

Haystack-Needle-Sheet

Permanent Link: Haystack-Needle-Sheet 4. August 2009 Comment No Comment

Surely every PHP developer has come across the PHP Haystack-Needle-Phenomenon: Mostly the parameter order is $haystack, $needle but in some cases it's $needle, $haystack for no apparent reason. Trying to find regularities I found out that it's only the array functions that use needle, haystack instead of haystack, needle - assuming I didn't forget any functions. For that reason I created a Haystack-Needle-Sheet, which shows you which function uses haystack, needle and which one needle, haystack.

If you should come across functions that are missing on this list, please let me know.

You can download the Haystack-Needle-Sheet here

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'));

International PHP Conference Spring Edition Day 2

Permanent Link: International PHP Conference Spring Edition Day 2 27. Mai 2009 Comment No Comment

Some words on sessions I attended on day 2.

OOP … aber richtig (Stefan Priebsch)

Some known basics about OOP. It was ok, but it really was nothing new.

Organized serendipity: Inside report from the core of PHP development (Lukas Smith)

Finally I know why the namespace separator discussion took so long ;-)

Multi-Master MySQL (Arne Blankerts)

While the session itself was rather disappointing, it got me thinking a lot about how it would be possible to set up a multi master architecture. If I ever have any good idea, I'll let you know ;-)

International PHP Conference Spring Edition Day 1

Permanent Link: International PHP Conference Spring Edition Day 1 26. Mai 2009 Comment No Comment

Some words on the sessions I attended today, but first off: Since I only got here yesterday, I could not attend the workshop day.

MySQL Performance Tuning & Optimization Capsule (Sonali Minocha)

Some known stuff, a lot of new tweaks, optimizations and dos/don'ts I didn't know yet. For example PROCEDURE ANALYSE() was totally new to me, you would use it like this: select * from mysql.user procedure analyse(); Basically all this does is to check the results the query produces and it then tells you the optimal type you should use for each column, based on the result data. For more information check the documentation linked above. Furthermore the talk was about optimizing query cache, InnoDB specific optimizations, Index optimiazations and many more.

Sichere Applikationen auf Basis des Zend-Frameworks (Stefan Esser)

I was really looking forward to this session. Although most of the shown, like the proper use of Validators, Filters or how to access $_GET/$_POST/etc. variables correctly, there were things that were new to me, like Zend Frameworks build-in possibility against Cross-Site Request Forgery (CSRF). On one hand, I had hoped to learn a bit more, but on the other hand it maybe tells me that I'm on the right course… ;-)

Why Architecture in Web Development matters (Lars Jankowfsky)

Lars showed some really interesting basics for building a good architecture, spiced up with some examples of how they did it at Swoodoo. I already enjoyed Lars' article on architecture in the PHP magazin a while ago and this talk was a good extension to it. Generally it seems that SOA (Service Oriented Archicture) is a good way for abstraction in architecture, even though it makes integration testing harder, since you have to write more mock-up objects.

The Present and Future of PHP (Andrei Zmievski)

Everytime you think about coding in Ruby, Domo-Kun kills a kitten!

DTrace für AMP-Entwickler und Administratoren (Johannes Schlüter)

Johannes showed really nice ways to trace (and debug) processes (and with focus on php) not using strace, but Suns dtrace. It's possibilites really impressed me and there's not really anything I can say about it right now, apart from the fact that you need PHP 5.3 or MySQL 5.4 (there are backports on Solaris up to 5.1) to be able to use it for those two.

 

By the way: That was fun (in German) ;D

Using func_get_args() as second (or more) parameter

Permanent Link: Using func_get_args() as second (or more) parameter 29. April 2009 Comment No Comment

There's a strange bug in PHP we recently came across: When using func_get_args() is used as second (or third or fourth etc) parameter, PHP crashes with a Fatal Error.

Try this code:

function returnSomething()
{
        return func_get_args();
}

function callSomeFunction($message)
{
        someFunction(func_get_args(), 'Hello World');
        someFunction(1, returnSomething());
        someFunction(1, func_get_args());
}

function someFunction($code, $message)
{
        print($message . PHP_EOL);
}

callSomeFunction('Hello World');

The result should be

Hello World
Array
Array

yet you get

Hello World
Array

Fatal error: func_get_args(): Can't be used as a function parameter
in /home/dominik/test.php on line 12

The code works with any other function (see example code), only func_get_args() causes a crash.

So I thought: Let's commit a bug! I then found out that this very bug has already been committed in 2005: http://bugs.php.net/bug.php?id=27887. The bug was closed (unfixed) with this really unsatisfactory explanation:

[16 Jun 2005 1:34am UTC] tony2001@php.net
Damien, the docs say: "This function cannot be used directly as a function
parameter. Instead, its result may be assigned to a variable, which can
then be passed to the function.", so don't use it as a parameter.

[16 Jun 2005 9:04am UTC] derick@php.net

So there is no bug, and the docs already describe it -> bogus.

Of course: Why should I fix a bug, when I can just put a note in the documentation that says "don't use it as parameter".

Edit: I filed a bug and it was closed with the comment "This is a known issue and have been fixed in 5.3.". Good to know!

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);
}
		
		

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

PHPTestFest 2009

Permanent Link: PHPTestFest 2009 4. Februar 2009 Comment No Comment

Quoting from http://qa.php.net/testfest.php

TestFest 2009

The TestFest is an event that aims at improving the code coverage of the test suite for the PHP language itself. As part of this event, local User Groups (UG) are invited to join the TestFest. These UGs can meet physically or come together virtually. The point however is that people network to learn together. Aside from being an opportunity for all of you to make friends with like minded people in your (virtual) community, it also will hopefully reduce the work load for the PHP.net mentors. All it takes is someone to organize a UG to spearhead the event and to get others involved in writing phpt tests. The submissions will then be reviewed by members of php.net before getting included in the official test suite.

Fine, the next event is on its way. I hope there is a thing at Liip offices Zurich again, but instead of just raiding Pierres (Munich) home again i hope to get some resources from my company.

  • There is need for download space, I guess we can provide that. Our website has lots of bandwith.
  • Maybe Burda can provide an Open Space to use for 2 days. The CXO offices turned out little tricky to use but this time i try to get the new cafe they opened just for reasons like this
  • I hope some people from the team will join the effort
We'll see what happens ;) Last Year was a lot of fun and a lot of learning. I hope i can provide one or two Tests with more sense than last year.

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.

Using array_unique() with multidimensional arrays

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

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)

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)

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.