Mirmo Dynamics

Si tu kiffes pas reunoi, t'écoutes pas et puis c'est tout.

To content | To menu | To search

Keyword - self

Entries feed - Comments feed

Monday 14 July 2008

About the self keyword in static methods

While setting up a test server for some software I wrote at the office, I eventually noticed the following notice:

Notice: Use of undefined constant self - assumed 'self'

That surprised me, because 1) I though self were some kind of "superglobal" constant or a special token of the parser, always automatically available in a static method and 2) the code works. So what's up in there ? Let's make a simple test:

<?php

class foo {
	static public function bar() {
		var_dump(is_callable(array('self', 'foobar')));
		var_dump(is_callable(array(self, 'foobar')));

		var_dump(class_exists('self'));
		var_dump(class_exists(self));

		self::foobar();
	}

	static public function foobar() {
	}
}

foo::bar();

Executing the above code will yeld the following result:

bool(true)

Notice: Use of undefined constant self - assumed 'self' in /home/geoffreyb/test.php on line 6
bool(true)
bool(false)

Notice: Use of undefined constant self - assumed 'self' in /home/geoffreyb/test.php on line 9
bool(false)

What do we learn here ? Not much actually. It seems like self as a constant is only available when used with the scope resolution operator, aka double-colon or paamayim nekudotayim. When you want to use it in, for example, a callback definition, use a string representation of self:

<?php

is_callable(array('self', 'bar'));
call_user_func(array('self', 'bar'));

Which, while making absolutely no sense at all, works. Another way to get around this is to use the get_class() function that, without any argument, will return the name of the class you're currently in (foo in my example).

After a bit more investiging, I found out that there is nothing special about the self token, which is actually a string token. You can check this very easily with the following code:

<?php

class foo {
	static public function bar() {
		self::foobar();
	}
}

var_dump(token_get_all(file_get_contents(__FILE__)));

Somewhere inside the output, you'll find the following piece of text:

  array(2) {
    [0]=>
    int(307)
    [1]=>
    string(4) "self"
  }

And the token id 307 is resolved by token_name to string.

Monday 5 November 2007

Extending Zend_Controller_Router_Route: the singleton problem.

Today I ran into an issue while extending Zend_Controller_Router_Route. I wanted to add a little path pre/post processing in the match() and assemble() methods, so I just extended the Route class to add my tiny bits of code into the methods. Except it did not work at all. After a few debuging, it turned out that the Router uses Zend_Controller_Router_Route::getInstance() to retrieve a route object, which uses a new self(); statement to instantiate the route object. Problem is that self always refers to the current class definition we're in, if the method is called from a child class, without being overloaded, self will refer to the wrong class.

Example:

class Foo {
	public static function getInstance() {
		return new self;
	}
}
 
class Bar extends Foo {}
 
var_dump(Bar::getClass());

echoes something like:

object(Foo)#1 (0) {
}

Which is fscking wrong IMHO. A quick workaround is to overload the getInstance method, which is what I call pretty annoying as it does not follow the DRY principle.