5

I've just implemented a custom error with trigger_error(), but the problem is that the line that calls trigger_error() is shown as the offending line in the call stack trace, instead of the calling line.

<?php
trait ClassProtection {
  public function __get($name) {
    $class_name = get_called_class();
    trigger_error("Public property {$class_name}::\${$name} does not exist", E_USER_ERROR);
  }

  public function __set($name, $value) {
    $class_name = get_called_class();
    trigger_error("Public property {$class_name}::\${$name} does not exist and automatic creation has been disabled", E_USER_ERROR);
  }
}

class SomeClass {
  use ClassProtection;

  public $foo = 1;
  protected $bar = 2;
  private $baz = 3;
}

$sc = new SomeClass;

// Try to access existing public property: should work
var_dump($sc->foo);
$sc->foo = 11;
// Try to access existing protected property: should not work - fatal error
var_dump($sc->bar);
$sc->bar = 12;
// Try to access existing private property: should not work - fatal error
var_dump($sc->baz);
$sc->baz = 13;
// Try to create new public property: should not work - fatal error
$sc->qux = 14;
var_dump($sc->qux);

With the line //use ClassProtection; commented, it produces the following:

C:\>php test_class_protection.php
int(1)
PHP Fatal error:  Cannot access protected property SomeClass::$bar in C:\test_class_protection.php on line 28
PHP Stack trace:
PHP   1. {main}() C:\test_class_protection.php:0

Fatal error: Cannot access protected property SomeClass::$bar in C:\test_class_protection.php on line 28

Call Stack:
    0.0000     127344   1. {main}() C:\test_class_protection.php:0

With the line use ClassProtection; uncommented, it produces the following:

C:\>php test_class_protection.php
int(1)
PHP Fatal error:  Public property SomeClass::$bar does not exist in C:\test_class_protection.php on line 5
PHP Stack trace:
PHP   1. {main}() C:\test_class_protection.php:0
PHP   2. SomeClass->__get($name = 'bar') C:\test_class_protection.php:28
PHP   3. trigger_error('Public property SomeClass::$bar does not exist', 256) C:\test_class_protection.php:5

Fatal error: Public property SomeClass::$bar does not exist in C:\test_class_protection.php on line 5

Call Stack:
    0.0000     127648   1. {main}() C:\test_class_protection.php:0
    0.0000     128904   2. SomeClass->__get(string(3)) C:\test_class_protection.php:28
    0.0000     129128   3. trigger_error(string(46), long) C:\test_class_protection.php:5

Note that there are two extra lines in there which are not useful, and are in fact distracting, when debugging. I want the second error to look like the first, except with the message customised.

I seem to remember from when I used to work with Perl that it had some sort of trusted code mechanism that prevented code marked as trusted from appearing in a stack trace. Is there any way in PHP to hide the last one or two calls in the call stack trace without rewriting the whole error handling routine so that it looks like a standard PHP error happened on the correct line?

Ideally, I'd like to get a copy of the stack trace, modify it slightly and return it to PHP's standard error handler.

CJ Dennis
  • 4,226
  • 2
  • 40
  • 69
  • 1
    This answer might give you an idea https://stackoverflow.com/a/1159235/1788201 But is this really a good idea? If you remove those lines, how can one know this error occurred because of some customized behavior (i.e. `ClassProtection` trait)? If I encounter this error message which says some public property does not exist, I'll keep wondering why such error raised? Because PHP allows me to set public class properties like this, so what's wrong? Then I'll have to dig into your code to find out what is really going on. – Nima Aug 05 '17 at 11:39

0 Answers0