I am trying to decide on what should be the/my Best Practice when it comes to class properties in PHP.

Should I use public properties which means a lot less code, especially when it comes to serializing an object, or iterating over its properties?

The problem is compounded by the ability to assign arbitrarily named values to an object at runtime which I see as leading inevitably to hidden and emergent bugs: it is not in the class definition so I assign an arbitrary property name that someone else chooses and uses elsewhere. Not to mention simple typos of course ;-)

Should I just use the magic methods get and set and store all object data in an array whose scope is private or protected?

Or should I do the classic OOP thing and encapsulate object data and use explicit getters and setters?

I would love some solid advice from the gurus here, from those who have been involved in large projects where the pitfalls of each option are exposed.

    You could use get() and set() to prevent the assignment of arbitrary class variables (e.g. making use of the [man]property_exists/man method to test if it's valid).

    <?php
    class Foo
    {
    	private $bar = 'bar';
    	public function __get($varname)
    	{
    		if(property_exists($this, $varname)) {
    			return $this->$varname;
    		}
    		else {
    			throw new Exception(get_class($this) . "::$varname does not exist");
    		}
    	}
    	public function __set($varname, $value)
    	{
    		if(property_exists($this, $varname)) {
    			throw new Exception(get_class($this) . "::$varname is read-only");
    		}
    		else {
    			throw new Exception(get_class($this) . "::$varname does not exist");
    		}
    	}
    }
    
    $foo = new Foo();
    echo $foo->bar;     // should show 'bar'
    $foo->bar = 'test'; // should throw exception
    

      Thanks for the prompt response. My own take on this is to use explicit getters and setters, but then that is a habit from C#. However, the problem of arbitrary values was haunting me, so I decided to use the magic methods to trap them.

      Here is what I have so far, any comments gratefully received. And thanks to NoDog for the improved Exception messages ;-)

      	/*
      		Allow the use of public property notation, $obj->prop
      		but prevent the getting of arbitrary properties
      	*/
      	public function __get($key) 
      	{
      		$method = 'get' . ucfirst($key); 
                    if (method_exists($this, $method)) 
                    {  
      return $this->$method(); } else { throw new Exception(get_class($this) . "::$key does not exist"); } } /* Allow the use of public property notation, $obj->prop but prevent the setting of arbitrary or read-only properties */ public function __set($key, $val) { $method = 'set' . ucfirst($key); if (method_exists($this, $method)) { $this->$method($val); } elseif (property_exists($this->$key)) { throw new Exception(get_class($this) . "::$key is read only"); } else { throw new Exception(get_class($this) . "::$key does not exist"); } }

      Brad, in my OOP, all properties are encapsulated, hence the need for explicit getters and setters. To limit the scope of a var to class only I simply do not code a getter or setter.

        bradgrafelman;11004756 wrote:

        @: I like the direction you're heading with that example... but how can you enforce visibility constraints?

        For example, I would want your example above to generate an error since $foo->bar is a private property.

        My example will allow private/protected variables to be viewed but not set (from outside of the object). If you don't want them to be viewed, either, then a change in logic comparable to the set() method's should handle that. Or, come to think of it, just don't define a get() method at all. 🙂

          On top of all that, there's no law that says you have to allow arbitrary property names, or that properties accessed via get have to correspond 1:1 to actual object fields. You can throw an exception instead.

          For example, I wrote a complex number class, and used [font=monospace]__get[/font] to provide [font=monospace]Re[/font], [font=monospace]Im[/font], [font=monospace]Mag[/font], and [font=monospace]Arg[/font]. I only stored two of those (as private fields), and the others were calculated as needed. (Exercise for the reader: which two?)

          One rule of thumb I use is that: if it requires a significant amount of work to determine the value of a property, don't use [font=monospace]__get[/font]: if someone asks for [font=monospace]$foo->bar[/font], they probably expect a result pretty much instantly; not the Spanish Inquisition.

            Write a Reply...