How to get better fatal error messages in magic methods?

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?

What about throwing an exception?

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.

Still looking…

Exception::getTrace should do it.


<?php
error_reporting(-1);
ini_set('display_errors', true);

class Breakfast
{
  protected
    $bacon = 'Danish';

  public function getBacon(){
    return $this->bacon;
  }

  public function __get($property){
    if(false === property_exists($this, $property)){
      throw new InvalidArgumentException();
    }
    return $this->$property;
  }
}

$breakfast = new Breakfast;
echo $breakfast->bacon; #Danish

try
{
  $breakfast->sausages; #line 25
}
catch(Exception $exception)
{
  print_r($exception->getTrace());
  /*
    array(1) {
      [0]=>
      array(6) {
        ["file"]=>
        string(35) "C:\\Users\\Anthony\\PHP\\playground.php"
        ["line"]=>
        int(25)
        ["function"]=>
        string(5) "__get"
        ["class"]=>
        string(9) "Breakfast"
        ["type"]=>
        string(2) "->"
        ["args"]=>
        array(1) {
          [0]=>
          string(8) "sausages"
        }
      }
    }
  */
}

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.

Exceptions have getTrace as Anthony pointed out - outside of exceptions you have debug_print_backtrace

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).

Yes, that’s what they are there for.

Thanks.