Posts Tagged ‘PHP’

Object and Array Casting

29. November 2011

No Comments »

A few days ago, I posted about some interesting behavior when casting arrays to objects. I would like to take a look at some other interesting behavior. Lets start be creating a basic class Test with three properties each with a different visibility.

[code]class Test {
private $foo;
protected $bar;
public $bat;

public function __construct() {
$this->foo = "lorem";
$this->bar = "dolor";
$this->bat = "amit";
}
}[/code]

Now create a new instance of the class and cast it to an array.
[code]$test = new Test();
print_r((array)$test);[/code]
The result is as follows:
[code]Array
(
[Testfoo] => lorem
[*bar] => dolor
[bat] => amit
)[/code]
On the surface it appears as if the private property had the class name prepended and the and the protected property had an astrick prepended. Now what happens if we cast this array back to an object?
[code]$test_object = new Test();
$test_array = (array)$test_object;
var_dump((object)$test_array);[/code]

Results in:
[code]object(stdClass)#2 (3) {
["foo":"Test":private]=>
string(5) "lorem"
["bar":protected]=>
string(5) "dolor"
["bat"]=>
string(4) "amit"
}[/code]

How did PHP know the Testfoo key and the *bar key were private and protected? After all, doing the following does not work:
[code]$test_array = array("Testfoo" => "lorem", "*bar" => "dolor", "bat" => "amit");
var_dump((object)$test_array);[/code]

It turns out, PHP stores meta information with the keys in the form of null bytes. Thus
[code]$test_array = array(
"\x00Test\x00foo" => "lorem",
"\x00*\x00bar" => "dolor",
"bat" => "amit"
);
var_dump((object)$test_array);[/code]
Results in the object we expect.
[code]object(stdClass)#2 (3) {
["foo":"Test":private]=>
string(5) "lorem"
["bar":protected]=>
string(5) "dolor"
["bat"]=>
string(4) "amit"
}[/code]

Array vs SplFixedArray

1. February 2011

1 Comment »

PHP offers eight primitive types to the user. One of them being an array. An interesting aspect of PHP arrays is that they are allocated dynamically. Thus, the following code is perfectly acceptable:

[code lang="php"] $arr = array(1, 2, 3, 4);
$arr[50] = 50;
[/code]

The first line set the first four indexes to one through four respectively. This creates an array of size four. The second line sets index fifty to the value fifty augmenting the array size by one. Indexes five through forty nine do not exist, and attempting to access a non existing element will only raise an E_Notice level error message. The dynamic allocation of arrays allows programmers to ignore out of bounds exceptions, segmentation faults, and hours of debugging, but it also leads to a slower write time because the memory locations needed to hold the new data is not already allocated.

The Standard PHP Library offers a SplFixedArray object that pre-allocates the necessary memory thereby solving the issue of slower write times.

[code lang="php"]

$array = new SplFixedArray(5);
print_r($array);[/code]
Which results in (a pre-allocated array)

SplFixedArray Object
(
[0] =>
[1] =>
[2] =>
[3] =>
[4] =>
)

Creation Time

To verify this, I ran a test that writes data to an array and an SplFixedArray and measures the time for different amounts of elements.

[code lang="php"]

for($size = 1000; $size < 100000; $size *= 2) {
$f = $size;
for($t = microtime(true), $a = array(), $i = 0; $i < $size; $i++) {
$a[$i] = NULL;
}
$f .= "," . (microtime(true) - $t) . ",";

for($t = microtime(true), $a = new SplFixedArray($size), $i = 0; $i < $size; $i++) {
$a[$i] = NULL;
}
$f .= (microtime(true) - $t) . "\n";
file_put_contents("./data.csv", $f, FILE_APPEND);
}[/code]

Write Time

It is clear that the SplFixedArray has faster a write time. However, there is a significant overhead when an object is created. To test this, I ran the same test but each time I created a new data structure. From the plot, we can see that creating an array has minimal overhead, whereas creating a new SplFixedArray object and allocating the memory takes a significant more amount of time.

[code lang="php"]

$elements = 20;

for($size = 1000; $size < 100000; $size *= 2) {
$f = $size;
for($t = microtime(true), $i = 0; $i < $size; $i++) {
for($j = 0,$a = array(); $j < $elements; $j++ ) {
$a[$i] = NULL;
}
}
$f .= "," . (microtime(true) - $t) . ",";

for($t = microtime(true), $i = 0; $i < $size; $i++) {
for($j = 0, $a = new SplFixedArray($elements); $j < $elements; $j++) {
$a[$j] = NULL;
}
}
$f .= (microtime(true) - $t) . "\n"; file_put_contents("./data.csv", $f, FILE_APPEND);
}
[/code]

Memory Footprint

Lastly I wanted to see how the memory usage differs between each data structure.

[code lang="php"]

for($size = 1000; $size < 100000; $size *= 2) {

$f = $size . ",";

$t = memory_get_usage();
for($a = array(), $i = 0; $i < $size; $i++) {
$a[$i] = NULL;
}

$f .= (memory_get_usage() - $t) . ",";

$t = memory_get_usage();
for($a = new SplFixedArray($size), $i = 0; $i < $size; $i++) {
$a[$i] = NULL;
}

$f .= ($t - memory_get_usage()) . "\n";

file_put_contents("./data.csv", $f, FILE_APPEND);
}

[/code]

Conclusion

The conclusion is one typical of Computer Science, trade-offs. If you are in an environment where you will need a few arrays with predetermined sizes, the SplFixedArray is the best choice. However, if you will be creating numerous arrays, or the size is unknown, then it is probably better to use the native PHP array.

Crappy Code: date and mktime

3. January 2011

No Comments »

Occasionally (when creating forms) I will need to create a menu that lists the months. I normally use a predefined array and loop through it echoing out the month. From my experience, this is the best method since everything is defined in a single location that has a constant lookup time.

[code lang="PHP"]

[/code]

Recently, I was asked to help debug the following code:
[code lang="PHP"]

[/code]

After I flamed this guy for making 24 unnecessary function calls, I noted that arguments may be left out in order from right to left; any arguments thus omitted will be set to the current value according to the local date and time. Thus when this function ran on the 31st of the month, mktime’s 5th argument was 31 which causes an error in the following instance mktime(0, 0, 0, 2, 31) (because February does not have 31 days). The solution was to manually set the 5th parameter to 1.

[code lang="PHP"]

[/code]

This will generate the correct months, but still does not fix the problem of making 24 unnecessary function calls.