Better Getters and Setters in PHP

If you’re reading our PHP tech posts, Beaconfire RED is hiring and we want to talk to you! Come join our tech team in 2017 and play with Drupal 8! 

Common practice in PHP development seems to follow a common flow: create class properties with names like $class->title and getters and setters named $class->getTitle() and $class->setTitle(). Using additional methods like this, even when they’re standardized in a framework, are not ideal because it requires developers to know both the property name as well as the standards in your project.

 

Our goal here will be to allow developers to apply and retrieve data in the following format while still leveraging the power of getters and setters.

$object->created = 0123456789;

$created = $object->created;

The Base Class

When building classes, I have them all inherit from this base class:

class BFR_Object{
  public function __construct($attributes = Array()){
    // Apply provided attribute values
    foreach($attributes as $field=>$value){
      $this->$field = $value;
    }
  }

  function __set($name,$value){
    if(method_exists($this, $name)){
      $this->$name($value);
    }
    else{
      // Getter/Setter not defined so set as property of object
      $this->$name = $value;
    }
  }

  function __get($name){
    if(method_exists($this, $name)){
      return $this->$name();
    }
    elseif(property_exists($this,$name)){
      // Getter/Setter not defined so return property if it exists
      return $this->$name;
    }
    return null;
  }
}

The Child Class

class Example_Object extends BFR_Object{
  private $_created;
  private $_modified;

  public function __construct($attributes = array()){
    parent::__construct($attributes);

    // Place any other constructor logic here
  }

  protected function created($value = null){
    // If value was provided, set the value
    if($value){
      if(is_numeric($value)){
        $dtStr = date("c", $value);
        $this->_created = new DateTime($dtStr);
      }
      else if($value instanceof DateTime){
        $this->_created = $value;
      }
    }
    // If no value was provided return the existing value
    else{
      return $this->_created;
    }
  }

  protected function modified($value = null){
    // If value was provided, set the value
    if($value){
      if(is_numeric($value)){
        $dtStr = date("c", $value);
        $this->_modified = new DateTime($dtStr);
      }
      else if($value instanceof DateTime){
        $this->_modified = $value;
      }
    }
    // If no value was provided return the existing value
    else{
      return $this->_modified;
    }
  }
}

There are a few important pieces here to cover:

Properties:

The two properties I’ve included in the child class are $_created and $_modified. The underscore naming scheme is not important, and you can adopt whatever you prefer. The only requirement is that the property name not match exactly what you want to be accessing from the object. In this example, they should not be named $modified and $created.

Another important aspect is the “private” visibility for each property. Either a “private” or “protected” visibility will work, but you do not want anyone accessing this property directly.

Constructor:

While not inside the scope of this article, I have included this basic constructor as a reference. It will simply assign any values it is provided in an options array as properties of the object.

The “Magic” (AKA Getter and Setter):

__get and __set are “magic methods” in PHP. These will be fired anytime someone attempts to set or retrieve the value of a property on this object. We’re leveraging these magic methods to check for a method matching the property name used. If the method exists, we call it (providing the value for __set). If a method doesn’t exist, we check to see if a property of that name exists. If not, we don’t return anything.

The second check for a property allows this object and those that extend it to support on-the-fly property declaration. If you remove it you can require that all objects declare their properties in the class definition.

The Methods

The final part of this class is the created and modified methods. It is important that these be named as what the developer will want to access. In addition, these must be protected or private as the __get and __set methods are only called when no property or method is publicly available.

We accept a $value argument in each method, but provide a default value of null so that no errors are thrown when the method is used as a getter. Within these methods, we should add any logic that should fire when the property is updated or retrieved as you would with getCreated in other frameworks. In this specific example there is logic to convert timestamp data into PHP DateTime objects.

Moving Forward

After applying this logic to a base class that you then extend from, you can create private properties and methods as necessary on all objects that then leverage the __set and __get logic. For those properties you don’t want getters or setters you can just create public properties using the actual name developers should reference.


Are you smart, motivated, and eager to solve problems in a collaborative environment? 

If so, we want you! Join our team!

See Our Current Career Opportunities