Tuesday, March 26, 2013

Logging revisited

If you're logging to a file - but of course you're logging to a database aren't you!? - but, if you are logging to a file, it's PHP so of course, there's a function for that:
file_put_contents($file, $data, FILE_APPEND|LOCK_EX)
From the manpage:
This function is identical to calling fopen(3), fwrite(3) and fclose(3) successively to write data to a file.

And of course, add the LOCK_EX only if you care about not corrupting your logfiles!

Enjoy! :)

Tuesday, March 19, 2013

Install PHP man pages locally

Ah. Did you know you can install the PHP man pages onto your own little Unix machine? Only trouble is you have to become an PEAR expert first! Or you can just do this:
$ pear install doc.php.net/pman
Now it might complain that your PEAR needs updating, in which case you can usually just follow the suggestions to update and then try again. Once done, you can then access all the PHP documentation right from your command-line. Eg:
$ pman preg_match
There is an apropos(1) option, but it doesn't work and apparently never will because all the PHP man pages have "junk descriptions" (according to makewhatis) So to find functions, googling or searching php.net is going to be faster. Alternatively I've created this little script which can help: (save it as "pman" and put it somewhere in your PATH ahead of /usr/bin)
#! /bin/bash
# a pman replacement
# show php man page as per normal
# but if no exact man page, list all man pages with matching text

MAN=`which man`
$MAN -M /usr/lib/php/pear/docs/pman/ $*
if [ $? -ne 0 ]; then
 echo ""
 echo "Listing commands matching: $*"

 cmds=$( ls /usr/lib/php/pear/docs/pman/man3 | grep "$*" | sed 's/.[0-9].gz//' )
 select cmd in $cmds
 do
  $MAN -M /usr/lib/php/pear/docs/pman/ $cmd
  break
 done
fi
So now, if you run:
$ pman some_man_page
it will show the man page as usual. But if you run say:
$ pman tags
you get:
No manual entry for tags

Listing commands matching: tags
1) get_meta_tags
2) strip_tags
#?
Now you can just type the number (in this case 1 or 2) to see the man page.
BTW, if you check, you'll see that all pman does is this:
#!/bin/sh
MAN=`which man`
$MAN -M /usr/lib/php/pear/docs/pman/ $*
Which means all you have to do is add something like:
export MANPATH=/usr/share/man:/usr/lib/php/pear/docs/pman
to your Shell startup file and you'll be able to access the PHP man pages with man just like any other man page.

Tuesday, March 12, 2013

PHP grief

Speaking of grief in PHP, I've been starting to code seriously in PHP for all of a few months now, and I'm learning a lot as I go, but man, the pain, the grief PHP inflicts upon the unwary

Having read The PHP Singularity - and then STILL deciding to go that path, I thought I was well prepared for what lay ahead. How wrong I was! PHP has an uncanny knack of kicking you in the bejezel when you least expect it.

For example. I learnt that all arrays in PHP are associative. Ok, I can deal with that. array[0] is really just array["0"] or something like that. It's really quite flexible. So I was a little disturbed when I went looking for how to determine if a given array is associative or not. I mean, it can't be that hard right!? (I've got to stop saying that!) It wasn't just that there was no "correct" way to determine the answer. It wasn't just that every thread I stumbled across that discussed it was suggesting bizarre and crazy ways to determine the answer. I mean, having much Perl experience I'm familiar with the "many ways to skin a cat" paradigm. But all the answers I found just seemed so... ugly!

Look, I'm not the worlds greatest programmer. Far from it. But still, I like my solutions to be both simple and stylish, if I can help it. That PHP has a built-in function for sooo many things, but not this... Well, what else to expect from a committee-designed weakly-typed language I guess. If I don't like it, I can always bugger off I suppose. *sigh*

In the end I settled on this:

function isAssoc($array) {
        return array_values($array) === $array ? FALSE : TRUE;
}

Also, what's with these damn error messages - or lack thereof! It's bad enough that there's no way to enforce type checking and variable declaration (No, really, I want to declare ALL my variables before I use them. Honest!) similar to Perl's "use strict". But how hard does PHP make you work just to see ANY error messages!? And even then on certain versions (not that old, I swear) php -l wouldn't even tell me what line number my error was on, simply saying:

syntax error in file

Syntax error somewhere in your file. Good luck with that. Gee, thanks PHP.

Another thing that really bugs me is the way PHP will sometimes tell me the variable I'm trying to use has not been initialised before use, but at othertimes wont. What is with that!? Ok. This might only apply to (slightly) older versions of PHP, but unfortunately our PHP server at work cannot be upgraded for reason too complicated to explain here :(

On the other hand, PHP gets upset if I try to apply the ++ operator to an array, even if I've initialised the array as empty.

BREAKING NEWS: I've discovered the array_fill() function. So now I can initialise my arrays with:

$a = array_fill($start, $size, $initial_value);
and dodge all those nasty "undefined offset" errors which so many PHP programmers don't seem to mind scattered all through their logs.

Array Insert Order

Ok. Associate arrays. Great. Index arrays. Still work just fine. But arrays that remember the order of insertion? Please, does that has to be built-in to the language?? For the love of programming, why? So:

$a[5] = 'five';
$a[1] = 'one';
$a[8] = 'eight';
$a[2] = 'two;

echo implode(',', $a);
will print:
five, one, eight, two

IS THAT REALLY NECESSARY??

Ok. Before I learnt to use array_fill() - which inserts my initial values in order anyway - I found that I could get my elements back into index-order with a ksort(). Eg. With the example above, if, before the "echo" statement, I add:

ksort($a);
I will then get my elements printed in the expected order: one, two, five, eight.

But of course I wont need to as I'll be using array_fill() so as to avoid any "undefined offset" errors.

Monday, March 11, 2013

parse_ini_file grief

I know that different versions of php can vary greatly, but this is ridiculous and exposes just how miserable it can be when trying to debug Php code.

I had parse_ini_file working perfectly well developing on my desktop mac. Not too difficult a task. Then I tried to deploy to the server and got this error:
Warning: file_get_contents('http://localhost/server-status'): failed to open stream: No such file or directory in /.../apachemon.php on line 183
Now strike me grievously but how am I supposed to spot the mistake here!? (hint: I am 100% certain the resource at "http://localhost/server-status" does exist) Did you spot it? At the time I grumbled and prodded and poked and extracted code segments attempting to test them in isolation.

And eventually, I figured it out.

Now I could be wrong on this, but I don't think I'm an idiot! I've debugged my fair share of code. But even when I structure my code well, implement a decent Logger, and test as best I know how, Php still has a myriad of ways to kick me in the butt.

Did you spot the bug yet?

On my desktop I'm running PHP 5.3.15 and on the server it's PHP 5.2.6. Is that such a big difference?

Now I think I'm actually starting to enjoy coding in php. At least, when it works! But for now, I think I know what my Php debugging catch-cry is:

"Good luck with that!"

Oh, and the bug, just in case you didn't spot it. Or should I say, the "bug":
I used single quotes but apparently it wanted double quotes

json_encode vs serialize

json_encode and serialize are both PHP functions for generating a storable representation of a value. So. What do they do, and why would you choose one over the other?

What do they do?

Well, in simple terms, they flatten the data object you pass to them into a single thread or stream of data, but in such a way so that the structure of the data can be completely recovered.

Why would you do this?

To quote the php manual:
This is useful for storing or passing PHP values around without losing their type and structure.
A serialized or json'ed object becomes a single simple reference that can be bandied about willy nilly. Stored. Retrieved. Thrown down a digital pipe somewhere without regard for it's welfare. But mostly, to store it in a DB or text file

Which would I choose?

I would choose json_encode for data that I knew to be strings only (no binary data)
I would choose serialize for creating BLOBs to go into a database.
I would choose json_encode when I wanted a human readable result.
I would choose serialize for more stand-alone type objects such as a php session or php user construct.
But I'm still learning, so I might change my mind about that later :)

Example Code

<?php

// example serialize and json_encode

$var['name'] = 'John';
$var['thresh'] = 88.2;

$json = json_encode($var);
$serialized = serialize($var);

print "JSON: $json\n";
print "Serialized: $serialized\n"; // safe because we know there's no binary data

$fromJson = json_decode($json);
$fromSerialize = unserialize($serialized);

print "From JSON:\n";
print_r($fromJson);
print "From serialize:\n";
print_r($fromSerialize);

?>

Example Output




JSON: {"name":"John","thresh":88.2}
Serialized: a:2:{s:4:"name";s:4:"John";s:6:"thresh";d:88.200000000000003;}
From JSON:
stdClass Object
(
    [name] => John
    [thresh] => 88.2
)
From serialize:
Array
(
    [name] => John
    [thresh] => 88.2
)

Logging

A long time ago, this log call was presented to me as a sample logging function line.
NSLog([NSString stringWithFormat:@"\t(%08x.%04d)%s %@",self,__LINE__,__FUNCTION__,format], ##__VA_ARGS__)
I'm just sayin'

PHP Templates

StringTemplate for PHP. Ah, what a relief! Someone has ported it: github.com/ewiger/stringtemplate. Now, I just have to solve all those dependancies and I'm set!.

Why use StringTemplate?

Because it provides the best combination of flexibility and yet strict adherence to the Template rules. Don't believe me? Here, please read this: StringTemplate MVC Paper