Programster's Blog

Tutorials focusing on Linux, programming, and open-source

PHP Gotchas and Tips

PHP

Here are some PHP observations that I have been caught out with and am posting so I don't get caught again. This post will grow over time as I find more bite-size nuggets.

Gotcha: Array Merge

Guess what the output to this code will be:

<?php

$lookup_table = array();
$lookup_table['hello'] = array();
$lookup_table['hello'][] = 1;
$lookup_table['hello'][] = 2;

$my_array1 = array();

$my_array1 = array_merge($my_array1, $lookup_table['hello']);
$my_array1 = array_merge($my_array1, $lookup_table['world']);
$my_array1 = array_merge($my_array1, $lookup_table['hello']);

print_r($my_array1);

The answer is nothing. Not only does the script not throw an error, but the merged array loses all elements that it was holding at the point of passing a null value as a parameter, as well as all future elements that would be merged after that call. If you manually run the script, you may see some notices and warnings, but you probably won't see these if the script is called by another script, or if your php ini settings are set to not shown warnings/notices (production settings).

Gotcha isset on array

You may use something like below to check if your index is in an array:

isset($row[$myFieldName])

However, this will not return the correct value if the index does exist, but the value is set to null (common with results from a database). Unfortunately you have to do:

$keys = array_keys($row);
return in_array($myFieldName, $keys);

# alternatively (may be faster but need to test)
$keys = array_keys($row);
$indexedKeys = array_flip($keys); // this array wont have any null values
return in_array($myFieldName, $indexedKeys);

Gotcha - readline()

I write interactive PHP scripts all the time for CLI tools. This often requires getting a variable from the user using readline. An example piece of code would be:

$search_string = readline('Enter the string of text you wish to replace: ' . PHP_EOL);

The PHP_EOL at the end is so that the user inputs the answer on a new line but this doesn't work! Instead you need to do this:

print 'Enter the string of text you wish to replace: ' . PHP_EOL;
$search_string = readline();

Tested on PHP 7.0.4-7ubuntu2.1 (cli) ( NTS )

Gotcha: NULL Values in CSV

The fputcsv() function will treat null values like an empty string, so if you are importing/exporting data, things can get lost in translation. To work around this, it may be easiest to export/import to JSON files instead.

Tip: empty($array) or count($array)==0

At one point, I was using empty() to "quickly" check if an array hadn't been given any values. This is because I didn't know at the time that the count() function is actually O(1) and not O(n) like I had assumed (i.e. it doesn't actually loop through the elements and count them). Thus you can use them interchangeably.

Types

is_int() will not evaluate true for a value of "20", however is_numeric would. Remember that all $_POST values, as well as values retrieved from MySql are actually in string form unless you, or your libraries, have done this for you.

Floating Point Values

Anything that requires precision and involves decimal places can lead to pain. This is because PHP does not have a double type like Java. Instead it only has integer and float types internally.

Issues can arise without doing anything complicated. For example here is an issue that arises from simple basic addition.

$myVal=0;

for($i = 0; $i<1000000; $i++)
{
    $myVal += 0.1;
}

echo $myVal; //100000.00000133
echo floor((0.1+0.7)*10); // outputs 7 not 8

Here is a great explanation of what is going on:

Helpful Workarounds

Some tips from the PHP manual:

  • Never trust floating number results to the last digit.
  • Do not compare floating point numbers directly for equality.

Using BCMATH

Using bcmath may resolve your issues. For example, here is the previous example using bcmath:

echo floor(bcadd(0.1, 0.7,1)*10); // outputs 8 "correctly"

Bcmath uses truncation rather than rounding. E.g. bcdiv(100,11,1); will give you 9.0, not 9.1 which is closer in value to the actual answer. The third value for precision is optional so you may sometimes forget it, but without it you will get a 0 if you haven't set the scale with bscale($precision).

Regular Expressions

Use Single Quotes

As a rule of thumb, use single quotes (') on the patterns for regular expresssions. This will save you headaches when you use the $ symbol for specifying the end of the pattern as shown below.

$pattern = "/^[0-9]*$/"

The $ symbol within double quotes tries to evaluate a variable.

Start and End Characters

Don't forget that you need to specify a character for the start and end of your regular expression pattern when passing to preg_match etc.

Although you can use any character to indicate the start and end of the regular expression, as a rule of thumb, use (/). This is because most on-line examples use this and lots of characters such as | have special meanings in Regexps that you may need to use (in this case OR).

$pattern = "/^[0-9]*$/"