I’m experimenting with implementing magic methods as a way to get properties from my active record classes - I’m using __get and __set. The only problem I have is very poor error messages that don’t really say where the error really is. This is a simplified example:
class TestClass {
public function __get($name) {
$method = 'get'.$name;
return $this->$method();
}
public function getMe() {
return 'me!';
}
}
And somewhere in another file I use it like this:
$test = new TestClass;
$me = $test->Me;
And when I make a mistake:
$me = $test->Mee;
I get this error message:
Fatal error: Call to undefined method TestClass::getMee() in C:\www\ est\TestClass.php on line 6
This points to the magic method and it’s not very helpful. How can I get information about the calling code? I tried register_shutdown_function() to catch the fatal errors and using debug_backtrace() but debug_backtrace() doesn’t really give me any backtrace, it only mentions the shutdown function itself. Any ideas?
class TestClass
{
public function __get($name)
{
$method = 'get'.$name;
if (method_exists($this, $method) === false)
{
throw new Exception(sprintf('Access to invalid record property "%s"', $name));
}
return $this->$method();
}
public function getMe()
{
return 'me!';
}
}
This method doesn’t really solve anything because I get the same information as in the case of fatal error, only worded differently. I want to know which line of what script invoked the magic method, in this case this one:
$me = $test->Mee;
So I need some kind of backtrace of how execution got to the __get.
Apart from that I’d like to keep my magic method as short as possible.
Thanks, Anthony, I wasn’t aware of Exception::getTrace and it does exactly what I need! I wanted to avoid throwing exceptions inside magic methods but I can handle it.
It’s only too bad that debug_backtrace is useless inside a shutdown function. The best thing would be if we could catch fatal errors but that’s just wishful thinking…
BTW, I have found some SPL exceptions - what’s their purpose? They all share the same interface - are they meant to provide some predefined descriptive exceptions so that we don’t have to define another class? If that’s the case I will use BadMethodCallException instead of creating my own (like InvalidArgumentException in Anthony’s code).