by Adam Brett

Mimicking the recently not approved Property Get/Set Syntax

This article was published on Thursday, January 24, 2013 which was more than 18 months ago , this means the content may be out of date or no longer relevant. You should verify that the technical information in this article is still current before relying upon it for your own purposes.

There was a proposal1 for a new getter and setter syntax that would have enabled you to write code like this:

<?php

// Code sample indicating the terminology
class TimePeriod
{
    private $Seconds; // <-- Traditional Property

    public $Hours { // <-- Guarded Property
        get() { return $this->Seconds / 3600; }   // <-- Accessor, more specifically a getter
        set($x) { $this->Seconds = $x* 3600; }    // <-- Accessor, more specifically a setter
        isset() { return isset($this->Seconds); } // <-- Accessor, more specifically an issetter
        unset() { unset($this->Seconds); }        // <-- Accessor, more specifically an unsetter
    }
}

The benefit here, is that accessing the properties directly on an instantiated object like this:

 <?php
 // Accessing the property is the same as accessing a class member
 $time = new TimePeriod();
 $time->Hours = 12; // Stored as 43200
 echo $time->Hours; // Outputs 12

Would result in the getters and setters being called transparently. Neat! There are plenty of use-cases for this, and it would drastically reduce the amount of code in a lot of libraries and frameworks that are simple getters and setters, but unfortunately a large enough minority of core-contributors disagreed, or weren't happy with the syntax, or the implementation (or something), and the proposal failed to pass, so it won't be making it into the language. Not so neat.

Fortunately, there is a way to replicate this functionality (albeit with less syntactic sugar) using PHP's existing Magic Methods, and when combined with PHP 5.4's traits2, we get something pretty close. Consider the following:

<?php
trait Accessors
{
    public function __get($name)
    {
        $getter = "get{$name}";
        if (method_exists($this, $getter)) {
            return $this->$getter();
        }
    }

    public function __set($name, $value)
    {
        $setter = "set{$name}";
        if (method_exists($this, $setter)) {
            return $this->$setter($value);
        }
    }
}

// Code sample indicating the terminology
class TimePeriod
{
    use Accessors;

    private $Seconds;  // <-- Traditional Property

    protected function getHours()
    {
        return $this->Seconds / 3600;
    }

    protected function setHours($value)
    {
        return $this->Seconds = $value * 3600;
    }
}

Now if we run our test code again:

 <?php
 // Accessing the property is the same as accessing a class member
 $time = new TimePeriod();
 $time->Hours = 12; // Stored as 43200
 echo $time->Hours; // Outputs 12

We get the correct output! For PHP 5.3 we could add the magic methods directly into the class that wants to use them, or create a base class and extend everything we want to have access to the magic getter/setters from that, but that feels messy, traits seem like the cleanest way to do this.

If we add __isset and __unset to the Accessor trait, we can replicate that functionality too:

<?php
trait Accessors
{
    public function __isset($name)
    {
        $issetter = "isset{$name}";
        if (method_exists($this, $issetter)) {
            return $this->$issetter();
        }
    }

    public function __unset($name)
    {
        $unsetter = "set{$name}";
        if (method_exists($this, $unsetter)) {
            return $this->$unsetter();
        }
    }
}

// Code sample indicating the terminology
class TimePeriod
{
    use Accessors;

    private $Seconds;  // <-- Traditional Property

    protected function issetHours()
    {
        return isset($this->Seconds);
    }

    protected function unsetHours($value)
    {
        return unset($this->Seconds);
    }
}

Obviously this is a lot more verbose than the original RFC, but it does provide a way to mimic some of its functionality. If you want to use it, you can see the full trait in this gist.

For exclusive content, including screen-casts, videos, and early beta access to my projects, subscribe to my email list below.


I love discussion, but not blog comments. If you want to comment on what's written above, head over to twitter.