Scope battles

25 11 2006

PHP allows you to define functions inside other functions. Example:

class C {
  function m() {
    function do_magic($x) { return $x + 42; }
    $var_magic = 42;

Actually it’s a function inside a method, but it doesn’t matter here. Class C has been added to the global scope, so we can refer to its name and create instances. And do_magic of course is not visible outside of the class definition, right?

echo do_magic(5); // => "Call to undefined function: do_magic()"

Right. OK, let’s make an actual object and call a method.

$c = new C;

All fine. Is $var_magic visible outside of a method?

echo $var_magic; // => null

Of course not. What about a do_magic function?

echo do_magic(5); // => 47

That’s pretty bad, isn’t it? Variables have its local scope inside functions, but functions have only one scope: global. Except when they are methods. But hey, it’s a feature, not a bug, so don’t feel too bad about it.

It hit me today, when I defined a function inside a method and (silly me!) wanted to use a method second time:

$c->m(); // => "Cannot redeclare do_magic()"

I’ve spent few moments to understand what redefining function foo() from file bar.php on line 1 originally defined in the same place meant.

Fatal error: Cannot redeclare do_magic() (previously declared in /var/www/foo.php:42) in /var/www/foo.php on line 42

But I finally got that one. Solutions? Define all your functions outside of other definitions. Or use function_exists.

No rant today, sorry. Go study the Zen of PHP instead.

PHP useless indexes

17 11 2006

There’s something terribly wrong in the way PHP parses its input. Look at this simple piece of code:


In any decent language you’d expect this expression to return 1. But not in PHP:

Parse error: syntax error, unexpected ‘[‘

Only solution seems to be to use some temporary variable or function to get a value from newly created array in the same expression.

function get($a, $n) { return $a[$n]; }
echo get(array(1, 2, 3), 0); // => 1

It hurts most when you work with functions that return arrays of values. You can’t write:
localtime()[2] to get current hour, you can’t write:
stat('some_path')[7] to get size of file or:
split(':', 'root:x:0:0:root:/root:/bin/bash')[6] to get shell used by root.

And to get things worse, string indexes behave the same way…

Recursive getattr/setattr

10 11 2006

By accident I found an old piece of code.

def rec_getattr(obj, attr):
    """Get object's attribute. May use dot notation.

    >>> class C(object): pass
    >>> a = C()
    >>> a.b = C()
    >>> a.b.c = 4
    >>> rec_getattr(a, 'b.c')
    if '.' not in attr:
        return getattr(obj, attr)
        L = attr.split('.')
        return rec_getattr(getattr(obj, L[0]), '.'.join(L[1:]))

def rec_setattr(obj, attr, value):
    """Set object's attribute. May use dot notation.

    >>> class C(object): pass
    >>> a = C()
    >>> a.b = C()
    >>> a.b.c = 4
    >>> rec_setattr(a, 'b.c', 2)
    >>> a.b.c
    if '.' not in attr:
        setattr(obj, attr, value)
        L = attr.split('.')
        rec_setattr(getattr(obj, L[0]), '.'.join(L[1:]), value)

I can’t recall any use case though, so it’s the thing you’d have to figure out yourself. ;-)

Frozen madness starts again

6 11 2006

PenguinNew version of Frozen Bubble has been released a week ago and I finally had some time to test it. I must admit Frozen guys did a great job. It took them a bit, but it was worth it. Interface is better and richer, so no need to use a command line anymore (which is great for a common user). Penguin graphics have been changed to more 3d-ish (well, sign of our times – I liked original 2d graphic, but the new one is also nice). The hit of this release is of course full multiplayer support. There are a lot of servers running already and the only thing that is missing is players. So if you have Linux installed give FB 2.0 a try. I need players for multiplayer games. ;-)

BTW, after a vacation I’m finally back. Expect some updates on Cheesecake soon.