Archive for the 'PHP' Category

PHPUnit: how to run specific tests

There might be a need to run a specific test or a subset of tests. Either it is just debugging a broken test or a new method or organizing tests into a suite.

A simple way to do that would be via ––filter switch:
phpunit ––filter testSomething

Alternatively a test can be tagged as belonging to a group using the @group annotation like this:

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @group testGroup
     */
    public function testSomething()
    {
    }
}

Tests can be selected for execution based on groups using ––group switch:
phpunit ––group testGroup

An extra benefit of the @group annotation is that ––exclude-group switch also allows to ignore groups of tests.

PHP: array_filter() with arguments

Recently I have been using a lot of array related functions. Especially array_count_values() function came in handy. It really saved me a lot of sweat while cracking some puzzles.

Anyway the other day I got a chance to play with array_filter() function. At the first look this function works just fine and required callback function is extremely easy to write. Until one realizes it would be great to have an extra parameter the same way array_walk() does.

// some array
$a = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
 
// custom multiplier
$p = 5;
 
function walk(&$value, $key, $m)
{
	$value = $value * $m;
}
 
// multiply each element of the array by $p
array_walk($a, 'walk', $p);
 
print_r($a);

So with array_walk() it is possible simply to add optional third parameter ($p in the example above) and it will just work. However array_filter() does not allow that third parameter – there are just two of them…

What I did not take into account that callback function “can not only be simple functions, but also object methods, including static class methods”. As result this solves the issue in the way shown below.

// some array
$a = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
 
// threshold to filter array elements
$p = 5;
 
// method of this class can be uses as a callback function
class Filter {
	private $threshold;
 
	function __construct($threshold)
	{
		$this->threshold = $threshold;
	}
 
	function isLower($i)
	{
		return $i < $this->threshold;
	}
}
 
$r = array_filter($a, array(new Filter($p), 'isLower'));
print_r($r);

Although the code above gets the job done, it feels like it is too much effort for an “extra parameter”. And this is when anonymous function and closure come helpful to provide one-line solution which I always appreciate:

$a = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
$p = 5;
 
$r = array_filter($a, function($i) use ($p) { return $i < $p; });
print_r($r);

Voilà – such an elegant way to solve the problem! 🙂

PHP: IP Address Validation

It’s common to use a regular expression to validate IP addresses.
However I’ve run into couple reliable ways to validate IP address without help of regexps. This should make code cleaner and easier to read or debug in case of any issues.

The first way is to use PHP function filter_var() which returns the filtered data, or false if the filter fails.

$ip = filter_var($ip, FILTER_VALIDATE_IP);
if ($ip !== false)
{
  // IP address is valid
}

Note that filter_var() has been supported since PHP 5.2. So it will not work for ‘older’ production environments.
So here comes another way to validate an IP address which should work even for PHP 4.

if (long2ip(ip2long($ip))) == $ip)
{
  // IP address is valid
}

Also fliter_var() unlike ip2long()/long2ip() supports IPv6. For instance, there are four additional flags:

  • FILTER_FLAG_IPV4 – allow only IPv4;
  • FILTER_FLAG_IPV6 – allow only IPv6;
  • FILTER_FLAG_NO_PRIV_RANGE – dot not allow IP from private range;
  • FILTER_FLAG_NO_RES_RANGE – do not allow IP from reserved range.

For example, the code to allow only IPv6 not from reserved range should look like this:

 
$ip = "2a01:e35:aaa4:6860:a5e7:5ba9:965e:cc93";
$ip = filter_var($ip, FILTER_VALIDATE_IP, array(
                  FILTER_FLAG_IPV6, FILTER_FLAG_NO_RES_RANGE));
if ($ip !== false)
{
  // IP address is valid
}

PHP: Array as the arguments part of the sprintf function

Using sprintf() function is a great way to integrate variables into string templates. It produces nice and readable code. But the drawback is that sprintf() does not support array as the argument part. As result the code may look less readable as number of parameters increases.

In that case vsprintf() is your friend. It works exact the same way as sprintf() except it accepts array instead of separate arguments.

$link = sprintf('<a href="%s">%s</a>', $url, $title);
$params = array($url, $title);
$link = vsprintf('<a href="%s">%s</a>', $params);

PHP: Daylight Savings Time Support

The ‘old’ way to support time zones in PHP did not have built-in full time zones support. Each time date(‘I’) flag had to be checked to verify daylight savings time period.

For example, to convert local time to GMT could be done the following way.

// time zone difference in hours
$tz = -5;
 
// local date and time
$local = '2011-03-24 11:00:00';
 
// adjust time zone difference in hours
// using daylight savings time flag and convert to seconds
$diff = ($tz + date('I', strtotime($local))) * 3600;
 
// convert to GMT by subtracting $diff number of seconds
$gmt = date('Y-m-d H:i:s', strtotime($local) - $diff);
 
// the output is 2011-03-24 15:00:00
print $gmt;

However utilizing DateTime class included in PHP 5 makes time zone related operations less stressful.

$local = '2011-03-24 11:00:00';
 
// create and instance of DateTime
// using local date/time and its time zone
$date = new DateTime($local, new DateTimeZone('America/New_York'));
 
// change time zone
$date->setTimezone(new DateTimeZone('GMT'));
 
// the date is already converted to GMT
// so the output is 2011-03-24 15:00:00
print $date->format('Y-m-d H:i:s');

So using time zone name instead of actual difference in hours or seconds allows to take care of daylight savings time automatically.

However unlike MySQL CONVERT_TZ() PHP uses different list of time zone names. For example, for Eastern standard time PHP offers ‘America/New_York’ time zone name while MySQL prefers ‘US/Eastern’.

Zend Server JobQueue issue

Zend Server may be a really helpful product and only ZendJobQueue by itself is a great concept for multitasking, queuing or even regular cron replacement. But unfortunately it lacks a good documentation

For instance I have not managed to find anything official about ZendJobQueue as nearly good as Zend Framework documentation. There are some good blog posts and presentations though which were great to start with. But they do not include in-depth details or troubleshooting guide.

In my case I ran into the following error message: ‘Bad response from Job Queue server to a createHttpJob request. QLocalSocket: Remote closed.’ once I tried to go beyond simple test code.

Although Zend Server was up and running and test ZendJobQueue code was working, it looked like everything had crashed really bad…

A quick search showed that someone had already run into a similar Job Queue problem about a year ago. No solution has been posted though. But the fact that my test code still was working helped to address the issue.

So here is my original call which caused the error message showed above.

$params = array('paramValue');
 
$q = new ZendJobQueue();
$q->createHttpJob('/task.php', $params);

And this is the call which works perfectly

$params = array('paramName' => 'paramValue');
 
$q = new ZendJobQueue();
$q->createHttpJob('/task.php', $params);

So apparently the $params array must have keys!
Of course one can tell that simply by looking at the error message but it was not that obvious for the first hour.

P.S.: Zend Server 5.1 Development License.

PHP: Mac address validating and formatting

There are three common tasks related to storing mac address in the database as char(12) (it is feasible to use bigint instead but in our case we opted for a text type).

1. Validating mac address entered by user.
In our case user is allowed to enter mac address the way s/he wants:
000CF15698AD
00:0C:F1:56:98:AD
00-0C-F1-56-98-AD

So the perfect solution would be to apply regular expression like this one:
/([a-fA-F0-9]{2}[:|\-]?){6}/

For example, in plain PHP:

public static function IsValid($mac)
{
  return (preg_match('/([a-fA-F0-9]{2}[:|\-]?){6}/', $mac) == 1);
}

or using Zend Framework:

public static function IsValid($mac)
{
  $validator = new Zend_Validate_Regex('/([a-fA-F0-9]{2}[:|\-]?){6}/');
  return $validator->isValid($mac);
}

2. Remove separating symbols ‘:’ or ‘-‘ from the mac address to fit only 12 character storage.
This is really easy to implement using str_replace().

public static function RemoveSeparator($mac, $separator = array(':', '-'))
{
  return str_replace($separator, '', $mac);
}

3. Adding separating symbol ‘:’ back when the mac address is displayed to a user.
This could look a bit ugly if the straight forward solution is applied, e.g. something like below:

public static function AddSeparator($mac, $separator = ':')
{
  $result = '';
  while (strlen($mac) > 0)
  {
    $sub = substr($mac, 0, 2);
    $result .= $sub . $separator;
    $mac = substr($mac, 2, strlen($mac));
  }
 
  // remove trailing colon
  $result = substr($result, 0, strlen($result) - 1);
}

So I kept looking for more elegant solution. Ideally I was hoping for one-line solution. And the magic comes with function str_split():

public static function AddSeparator($mac, $separator = ':')
{
  return join($separator, str_split($mac, 2));
}

PHP: split() alternative

Unfortunately (or fortunately?) split() function became deprecated as of PHP 5.3.0.

However besides annoying warning Deprecated: Function split() is deprecated in… it also means that soon this function is going to be completely unavailable. So it’s better not to wait when the warning turns into the fatal error.

Recommended alternative preg_split() has the same parameters, i.e. the only change required is just adding prefix preg_ to each call of split().

Of course if regular expression is not required then it is better apply explode() to split a string by string.

PHP: How to get microseconds

Sometimes it could be necessary to get time with microseconds part. Especially it is good for something like logging when multiple entries are recorded per second and preferably to know how they are spread out.

At first glance everything looked very easy:

print date('Y-m-d H:i:s.u') ;

where u is microseconds.

However it turns out PHP manual has a tiny note which states

Since this [date()] function only accepts integer timestamps the u format character is only useful when using the date_format() function with user based timestamps created with date_create().

Which means that u kind of works but in case of calling within date() function the result is always .000000.

So the straight forward solution could be something like this:

print date('Y-m-d H:i:s') . substr((string)microtime(), 1, 8);

But the drawback of the solution above is two calls to the time related functions which potentially may cause some accuracy issues.

Thus even better solution is doing just one call of time function:

list($usec, $sec) = explode(' ', microtime());
print date('Y-m-d H:i:s', $sec) . $usec;

PHP: Don’t repeat parameters using sprintf()

While missing a C# method String.Format() I’ve found a similar function in PHP, i.e. sprintf(). But since this function is slightly different from String.Format() it is not obvious how to avoid duplicating the same parameters.

For instance how to re-write an example below without passing $url and $title twice.

$link = sprintf('<a href="%s" title="%s">%s</a> (%s)</li>',
              $url, $title, $title, $url);

The answer is less obvious compare to C# but still pretty handy:

$link = sprintf('<a href="%1$s" title="%2$s">%2$s</a> (%1$s)',
              $url, $title);

In both cases echo $link give the same output like:

<a href="http://www.google.com/" title="Google">Google</a> (http://www.google.com/)

but the second way makes the code more elegant 🙂 .

Next Page »