19

This will throw an error:

class foo
{
   var $bar;

   public function getBar()
   {
      return $this->Bar; // beware of capital 'B': "Fatal:    unknown property".
   }

}

But this won't:

class foo
{
   var $bar;

   public function setBar($val)
   {
      $this->Bar = $val; // beware of capital 'B': silently defines a new prop "Bar"
   }

}

How can I force PHP to throw errors in BOTH cases? I consider the second case more critical than the first (as it took me 2 hours to search for a d....ned typo in a property).

Axel Amthor
  • 10,980
  • 1
  • 25
  • 44
  • 1
    You could check if `Bar` is defined via `isset` – Zach Rattner Jun 21 '13 at 13:17
  • and what if it isn't? I still don't know that `Bar` isn't defined in the class. – Axel Amthor Jun 21 '13 at 13:18
  • Well, choose CamelCase or lowercase (or some other convention) and then stick with it. You can use something like [PHP CodeSniffer (`phpcs`)](http://pear.php.net/package/PHP_CodeSniffer/redirected) to enforce it. Also, erroneous variable names will give you a perfectly understandable error message, which you can use to quickly find out where the error occurred. Using the magic methods `__get` and `__set` may solve the problem, but at what cost? It will slow down the code, and it may give rise to another set of problems. – Sverri M. Olsen Jun 21 '13 at 13:33

2 Answers2

15

You can use magic methods

__set() is run when writing data to inaccessible properties.

__get() is utilized for reading data from inaccessible properties.

class foo
{
   var $bar;

   public function setBar($val)
   {
      $this->Bar = $val; // beware of capital 'B': silently defines a new prop "Bar"
   }

   public function __set($var, $val)
   {
     trigger_error("Property $var doesn't exists and cannot be set.", E_USER_ERROR);
   }

   public function  __get($var)
   {
     trigger_error("Property $var doesn't exists and cannot be get.", E_USER_ERROR);
   }

}

$obj = new foo(); 
$obj->setBar('a');

It will cast error

Fatal error: Property Bar doesn't exists and cannot be set. on line 13

You can set Error Levels according to PHP error levels

Community
  • 1
  • 1
Robert
  • 19,800
  • 5
  • 55
  • 85
  • 1
    Working, but not what I'm looking for, actually: Both answers need to add code to the class in order to programmatically check syntax for errors. You need to add that to all classes (or inherit from somewhere), not the elegant way. Never the less: upvote, I'll consider this – Axel Amthor Jun 21 '13 at 13:26
  • There's no other way ;/ you can always inherit it from abstract class or use traits I don't see anything that is not elegant in it. – Robert Jun 21 '13 at 13:29
  • Got it. I still do not regard this as "elegant", but it's worth a try (and related to PHP and not to your answer!) – Axel Amthor Jun 21 '13 at 13:34
  • @AxelAmthor If you have PHP 5.4+, you could use Traits (like a mix-in) rather than inheritance. – Tyler Collier Oct 07 '14 at 16:21
  • it could be done with traits however personally I don't like them :) – Robert Oct 08 '14 at 09:50
  • @Robert or with an abstract class. However as Axel Amthor already mentioned you have to include it in every class which shouldn't have dynamic properties. – Alexander Behling Oct 15 '21 at 14:17
  • @AlexanderBehling I mentioned abstract class solution in my first comment – Robert Oct 16 '21 at 09:14
11

One solution I can imagine would be (ab)using __set and maybe property_exists:

public function __set($var, $value) {
    if (!property_exists($this, $var)) {
        throw new Exception('Undefined property "'.$var.'" should be set to "'.$value.'"');
    }
    throw new Exception('Trying to set protected / private property "'.$var.'" to "'.$value.'" from invalid context');
}

Demo: http://codepad.org/T5X6QKCI

TimWolla
  • 31,849
  • 8
  • 63
  • 96
  • 1
    You were a bit faster :) beacuse I wrote more complicated example with errors not exceptions – Robert Jun 21 '13 at 13:21
  • @TimWolla You are not abusing the __set function. This "magic" method was defined for this. But you should not use Exception use a derived type of it. The question is which should be used. I'm unsure but I tend to use the OutOfBoundsException. see https://stackoverflow.com/questions/8193798/outofrangeexception-vs-outofboundsexception – Alexander Behling Oct 15 '21 at 15:01