Category Archives: PHP

isset() or !empty() versus in_array()

I saw an interesting comment on stackoverflow about using a function to check if a variable exists such as isset() or empty() instead of using a search function such as in_array(). I hadn’t really given much thought to that before but it seems that it would be much quicker than looping an array to check for a value. I guess the caveat is that you would either need to know the index number or know the key name (if an associative array) to be able to make use of it.

For example:

$lunch = array('fruit' => 'Apple', 'snack' => 'Pudding', 'mainCourse' => 'Sandwich');

if (!empty($lunch['fruit'] and $lunch['fruit'] == 'Apple') {
// do something
}

The alternative I might have used would be

if (array_key_exists('fruit', $lunch) and $lunch['fruit'] == 'Apple') {
// do something
}

Ilia Alshanetsk wrote a blog post in which he compares the speed of using isset() versus array_key_exists with PHP 5.4. While isset() is quicker, it isn’t something that makes much sense to go back and fix (it falls into the micro-optimization category).

I decided to give his test a try and also check out how !empty() performs. I expected it would be somewhere between the two since empty() does an isset() check. I saw slightly different results checking on PHP 5.4.4, but those could be chalked up to not using the same server configuration (and in any case they are negligible).

isset() – 0.021 seconds
array_key_exists() – 0.068 seconds
!empty() – 0.025 seconds

Here is the code I used to generate my results:

$arr = array();
$fp = fopen("/usr/share/dict/words", "r");
while ($i < 5000 && ($w = fgets($fp))) {
    $arr[trim($w)] = ++$i;
}

$s = microtime(1);
for ($i = 0; $i < 100000; $i++) {
    isset($arr['abracadabra']);
}
$e = microtime(1);

echo "Isset: ".($e - $s)."\n";
echo '<br />';

$s = microtime(1);
for ($i = 0; $i < 100000; $i++) {
    array_key_exists('abracadabra', $arr);
}
$e = microtime(1);

echo "array_key_exists: ".($e - $s)."\n";
echo '<br />';

$s = microtime(1);
for ($i = 0; $i < 100000; $i++) {
    !empty($arr['abracadabra']);
}
$e = microtime(1);
echo "!empty(): ".($e - $s)."\n";

Signs you should refactor

I’ve been trying to look at my code a bit more critically, which always feels like one of the hardest things to do.

  1. Methods are long. If the method is getting lengthy look it over and see if it tries to handle too many things. I can’t tell you a definitive number of lines before it is a warning sign because it varies depending on what you are doing. When you see or write a long method, just give it a read through and think about whether any of it would make sense in it’s own method.
  2. Too many parameters. When you have too many parameters, look at them and decide if some of the parameters are related. If there are any that are related, is there an existing object that ties them together? Should there be one? Consolidating the parameters or even a portion of the functionality into a new class may a good way to clean it up.
  3. Lots of temporary variables. When you use a lot of them, look through the class and see if you are duplicating any code. Do you find yourself using the same temporary variables often? Perhaps moving them to a method of their own, albeit small, will help keep the code DRY.
  4. Too many properties. If there are a lot of properties it may mean you have too much going on in this class. Look at the properties and see if grouping them into similar ones helps reveal a hidden class you can pull out.
  5. Not using accessor methods. This one I see the most benefit from when the class is utilized in other classes. By using accessor methods you don’t tie a value to the property. You have the ability to hook in or pass additional formatting or optional parameters in.

Foreach loops and using references

I saw a few posts on stackoverflow about issues with passing values into a foreach loop by reference. For example:

$array = array('one', 'two', 'three');
foreach ($array as &$a) {
// do stuff.
}

As is the case with all foreach loops the last $a in the loop continues to exist after it finishes looping. In this case since we used references a variable exists that references the last element in the array. So you run the risk of inadvertently changing a value like is the case in this example on stackoverflow.

The PHP manual suggests using unset() after the foreach loop to terminate the link.

$array = array('one', 'two', 'three');
foreach ($array as &$a) {
// do stuff.
}
unset($a);

References are powerful for a lot of scenarios, and this will make me more mindful to do cleanup on my references when using them.

DateTime createFromFormat versus date

I came across createFromFormat today and thought it would be a quick way of getting and formatting a date. So I wanted to compare speed with my normal method of converting a date into a new format.

$rawDate = '12/12/2012';
$start = microtime();
$date = DateTime::createFromFormat('j/n/Y', $rawDate);
echo $date->format('j-n-Y');
$end = microtime();

// ~0.000094 seconds
print_r($end-$start);

$start = microtime();
$date = date('j-n-Y', strtotime($rawDate));
$end = microtime();

// ~0.000067 seconds
print_r($end-$start);

So using the DateTime method is ~0.000027 seconds slower, which isn’t a whole heck of a lot. In my opinion it is well worth the additional time to be left with a DateTime object. That way you can utilize convenient methods like diff without needing to code your own custom implementation.

The testing was all done with PHP 5.4.7

DateTime->date does not work unless using print_r() or var_dump()

Are you banging your head against the desk wondering why the DateTime object isn’t pulling the date property into your variable? Are you testing to make sure the variable exists using print_r() or possibly var_dump()? Well there is your problem.

I had the same issue, it was showing up when using print_r(), but it wasn’t assigning the value of the date property to the variable unless I used print_r() right before it. On first glance I didn’t see anything wrong, but then I saw this notice popping up in my error logs:

Notice: Undefined property: DateTime::$date in /home/livedev/cvs/ezpublish/classes/dataaccessobject.php on line 347

According to Derick from a PHP bug report, “date being available is actually a side-effect of support for var_dump() here”.

This is an excellent answer about the way to pull the date without using the date property that does not technically exist. You can use the format method to pull it in how you need it. In my case i was able to grab the date by changing my code from:

$date = strtotime($dateTimeObject->date);

to

// pulls the timestamp for the date.
$date = $dateTimeObject->format('U');

When is integer 0 equal to a string?

I ran into this pickle the other day. I had added a conditional like this:

$array = array('Levi Jackson');
foreach ($array as $key=>$val) {
	if ($key == 'SomeArrayKey') {
		// do something
	} else {
		// do something else
	}
}

It was incredible befuddling that it was actually evaluating as true even thought there was no key set to ‘SomeArrayKey’. Just to confirm the $key did not somehow contain ‘SomeArrayKey’ I var_dumped it. It showed that it was int(0). So in other words PHP was telling me that int(0) == ‘SomeArrayKey’ which is crazy. I tried using a strict comparison of three equal signs (===) and it worked right.

So what went wrong? Type juggling. One of the things that makes PHP flexible is that you can compare strings to integers and it “just works”. Well, most of the time it just works. This time it was converting ‘SomeArrayKey’ into an integer, which in this case was 0. So in fact when I thought I was comparing an Apple and and Orange, PHP changed my Orange into an Apple and it evaluated to true when I didn’t think it would!

PHP zvals and symbol table(s)

I’ve had a broad understanding of how PHP maintains and stores variables, but recently I did some more reading about it so that I could understand a solution on stackoverflow better.

zvals Container

When you assign a variable it creates a “row” in what is known as a zval container. At this stage I find it easiest to think of them like table rows, or an array item. They contain:

  • Type of the variable
  • Value of the variable
  • a boolean value for is_ref. This lets PHP know if the variable is a reference
  • refcount, which contains how many variables point to this value. This doesn’t always mean it is a variable assigned by reference. If for instance you do
    // refcount is 1 after you set this
    $a = 'Levi Jackson';
    // refcount is 2 after this point
    $b = $a;
    
    // refcount for $a is 1 and $b is 1
    $b = 'Jackson Levi';
    

It is important to note that the zvals table does not include variable names. Those are stored in what is called a symbol table.

Symbol Table

There is a symbol table for each level of scope (global, functions, methods, etc…). as well for certain types value  types. Scalar values like a string are pretty straight forward, at the most basic level a symbol table maps a variable name to a zval slot. Compound Types such as arrays and objects create separate symbol tables for their properties. So for an object like the following:

Class Person{
public $name;
public $email;

public function __construct($name = '', $email = ''){
$this->name = $name;
$this->email = $email;
}
}

$levi = new Person('Levi', 'test@test.com');

This would add an entry for $levi in the global symbol table and then it would create two more symbol tables for both properties name and email.

Garbage Collection

Once a variable in the zval container has 0 for a refcount it is deleted from memory. An interesting example posed on the php.net site though shows where you can lull yourself into the false idea that all of your values have been removed from memory. For instance, given the following

$tmpArray = array( 'name' => 'Levi');

You have a symbol table for $tmpArray as well as for $tmpArray[‘name’]. If you were to then unset($tmpArray) that would remove the value for $tmpArray, but the key ‘name’ would still exist because it still has a refcount of 1 since it has a value of ‘Levi’. So to remove that you would need to unset($tmparray[‘name’]) and then unset($tmpArray) to remove both from memory.

 

Pretty interesting stuff, and I hope to do some more reading on the internals of PHP soon. One of the more interesting things i’ve come across recently related to the above is that the foreach loop has the potential to copy the array it is looping (and rightly so) if the refcount for the array variable is more than 1.

http://php.net/manual/en/features.gc.refcounting-basics.php

$_SESSION doesn’t allow numeric keys in PHP < 5.4

I answered this question on stackoverflow the other day and it led me to question why $_SESSION[1] = ‘Something'; doesn’t work in PHP < 5.4. Give it a try, it should give you a notice along the lines of

Notice: Unknown: Skipping numeric key 1 in Unknown on line 0

I found this bug report when I was researching which says that the reason it throws the notice is because “false/true/null/etc are not valid key names”.  To me that isn’t a great answer though because normal arrays can have numeric keys. Luckily  Wiseguy replied to my answer and pointed out that the PHP docs used to say “The keys in the $_SESSION associative array are subject to the same limitations as regular variable names in PHP, i.e. they cannot start with a number and must start with a letter or underscore. For more details see the section on variables in this manual. I guess this was a side effect of register_globals. Curiously, the docs no longer state this. Perhaps this limitation was removed when the register_globals option was removed in PHP 5.4.0″

I wasn’t sure how register_globals fit into this though. I did some digging and was close to posting on stackoverflow to get some feedback about it! So I started working from a different angle, forget about it being a $_SERVER specific issue, maybe that didn’t have anything to do with it. That landed me on the super globals page.

Super Globals

Not to regurgitate info, but super globals are global variables in scope in any part of a script without the need to prepend them with a ‘global’ keyword. With register_globals on PHP would create variables for all of the child items in the super global. For instance:

$_SERVER['Levi'] = 'Levi Jackson';

This would create a variable $Levi that has the value of ‘Levi Jackson’. So if you had set a key of $_SERVER[1] = ‘Levi Jackson’ it would try and create a variable $1 set to ‘Levi Jackson’. Which if you are familiar with variable basics is a big “no no”.

PHP 5.4

Not that PHP 5.4 removed support for register_globals you can now use numeric keys with your super globals like $_SESSION. Enjoy.

Exceptions need the namespace

Came across this small doozy tonight. When catching an exception set in another file that has a namespace, you need to use the namespace in that file to reference the exception type. Pretty straightforward and it makes complete sense… it just wasn’t the first thing I thought of when I got an error.

For instance, given a file that has the following in it:

namespace Levi;
throw new MyException('something went wrong');

And another file that includes that file, the following would fail:

try{
// do something
} catch (MyException $e) {
// catch something
}

You need to put the namespace in front of the exception:

try{
// do something
} catch (Levi\MyException $e) {
// catch something
}

Configuring phabricator with MAMP

I installed phabricator with MAMP earlier this month and figured i’d post a couple notes to help anyone else out doing it. These are the configuration instructions I am working from, so give those a read through as well.

Setting up the alias

Since i’m working locally I wanted to setup an alias of http://phabricator.local to point my phabricator directory within MAMP. From Terminal you can edit the hosts file:

sudo vim /private/etc/hosts/

and add this line in:

127.0.0.1       phabricator.local

Then I went over to my virtual host file for MAMP located at /Applications/MAMP/conf/apache/extra/httpd-vhosts.conf and I added the following:

<VirtualHost *:8888>
    ServerName phabricator.local
    DocumentRoot /Applications/MAMP/htdocs/phabricator/phabricator/webroot/
    <Directory /Applications/MAMP/htdocs/phabricator/phabricator/webroot/>
        Options Indexes FollowSymLinks MultiViews
        AllowOverride All
        Order allow,deny
        allow from all
    </Directory>
    RewriteEngine on
    RewriteRule ^/rsrc/(.*)     -                       [L,QSA]
    RewriteRule ^/favicon.ico   -                       [L,QSA]
    RewriteRule ^(.*)$          /index.php?__path__=$1  [B,L,QSA]
</VirtualHost>

Setting up MySQL

Once you have that all setup it tells you to visit the domain in the browser, so go to http://phabricator.local. On this page it will tell you it either detected MySQL perfect, or that you need to fix some config issues. I had to update my password as well as the host post for MySQL. So from the directory you installed phabricator in (not the webroot) execute these from Terminal.

./bin/config set mysql.pass PASSWORD
./bin/config set mysql.host 127.0.0.1

(if you use localhost here you may run into issues running the upgrade/connecting to the database. This question on Stackoverflow outlines the issues pretty well http://stackoverflow.com/questions/4219970/warning-mysql-connect-2002-no-such-file-or-directory-trying-to-connect-vi )

./bin/config set mysql.port 8889

After successfully viewing the next step in the browser, I had to run the database update/install script.

./bin/storage upgrade

Admin account

After that you need to setup your first admin account. I had more trouble here because it was using OSX’s default PHP install instead of MAMP’s. So unless you want to update your system to use MAMP’s PHP version, you should do the following (this path may not be 100% accurate for everyone)

/Applications/MAMP/bin/php/php5.4.4/bin/php ./bin/accountadmin

You will know if you need to do this if you attempt to run the ./bin/accountadmin, enter in the username and press enter, and get back a really long error message (the most important being this line):

[2013-07-05 20:15:52] EXCEPTION: (AphrontQueryConnectionException) Attempt to connect to root@localhost failed with error #2002: No such file or directory. at [/Users/ComputerName/Dropbox/htdocs/phabricator/libphutil/src/aphront/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php:54]

After that you will be able to start customizing things from the admin. I’ll make another post about how to install Differential and Arcanist to begin utilizing those once I get them up and running.

Side Notes

While writing up this post it became evident that phabricator/libphutil did not support the use of ports when using mysqli. I forked both repos, made the modifications, and eventually submitted pull requests to get that functionality added in. Thanks to Evan Priestley for the help!
phabricator
libphutil