DEV Community

Benjamin Delespierre
Benjamin Delespierre

Posted on • Updated on

All flavors of PHP 8 getters

Getters are the most basic methods a class can have. Their only job is to return a value, usualy from its properties. There's isn't much to say about it.

Or is there? 🀨

Today I'm going to present you 10 ways you can implement a getter in PHP 8.0.

  • Public access
  • Classic getter
  • Classic getter, but without a verb
  • Getter/setter
  • Magic getter
  • Offset getter
  • Magic call
  • Reference getter
  • Encapsulation violation 🀫
  • Returning a closure

Let's get started!

Public access

class User
{
    public string $name = "Robert PAULSON";
}
Enter fullscreen mode Exit fullscreen mode

βœ… Pros

  • No need to write a getter method

❌ Cons

  • Cannot be part of an interface
  • Exposes object's internal state

Classic getter

class User
{
    public function getName(): string
    {
        return "Robert PAULSON";
    }
}
Enter fullscreen mode Exit fullscreen mode

βœ… Pros

  • Can be part of an interface
  • Straightforward and easy to understand

❌ Cons

  • Β―\_(ツ)_/Β―

Classic getter, but without a verb

class User
{
    public function name(): string
    {
        return "Robert PAULSON";
    }
}
Enter fullscreen mode Exit fullscreen mode

βœ… Pros

  • Can still be part of an interface
  • Shorter than getName

❌ Cons

  • Methods without a verb can be confusing

Getter/setter

class User
{
    private string $name = "Robert PAULSON";

    public function name(?string $name = null): string
    {
        if ($name) {
            $this->name = $name;
        }

        return $this->name;
    }
}
Enter fullscreen mode Exit fullscreen mode

βœ… Pros

  • Same as above
  • Less code to write if you also need a setter

❌ Cons

Magic getter

/**
 * @property string $name
 */
class User
{
    private string $name = "Robert PAULSON";

    public function __get($key)
    {
        if ($key == 'name') {
            return $this->name;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

βœ… Pros

  • You can expose protected and private members on your own terms

❌ Cons

  • You can't always typehint the returned value
  • Some companies have banned this (seriously)
  • You're going to need an annotation @property or PHPStorm won't "see" it

Offset getter

class User extends ArrayAccess
{
    public offsetExists($offset): bool
    {
        return $offset == 'name';
    }

    public offsetGet($offset)
    {
        if ($offset == 'name') {
            return 'Robert PAULSON';
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

βœ… Pros

  • Same as magic getter
  • Super cool $object['name'] notation

❌ Cons

  • Same as magic getter

Magic call

/**
 * @method string getName()
 */
class User
{
    public function __call($method, $args)
    {
        if ($method == 'getName') {
            return 'Robert PAULSON';
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

βœ… Pros

  • Same as using magic getters
  • Save "space" by grouping several getters in one method
  • You can create aliases for your existing getters without adding new methods

❌ Cons

  • You're going to need an annotation @method or PHPStorm won't "see" it
  • If you need this to lower the number of methods in your class, it might be the sign of a design issue

Reference getter

class User
{
    private string $name = "Robert PAULSON";

    public function &getName(): string
    {
        return $this->name;
    }
}

// usage
$user = new User;
var_dump($user); // string(14) "Robert PAULSON"

$name = &$user->getName();
$name = "Tyler DURDEN";
var_dump($user); // string(12) "Tyler DURDEN"
Enter fullscreen mode Exit fullscreen mode

βœ… Pros

  • Used for performance optimizations (eliminates the function call overhead)

❌ Cons

  • I hope you know what you're doing

Encapsulation violation

class User
{
    private string $name = "Robert PAULSON";
}

// can't access $name because it's private
// and there's no getter to obtain its value
$user = new User();

// let's use the reflection API
$property = (new ReflectionClass(User::class))->getProperty('name');
$property->setAccessible(true);
$name = $property->getValue($user);

echo $name; // Robert PAULSON
Enter fullscreen mode Exit fullscreen mode

βœ… Pros

  • Very useful for debugging classes you don't own without rewriting them

❌ Cons

  • Very slow
  • Quite ugly

Returning a closure

class User
{
    private $name = "Robert PAULSON";

    public function obtainNameGetter(): callable
    {
        return fn() => $this->name;
    }

    public function setName(string $name): void
    {
        $this->name = $name;
    }
}

// usage
$user = new User();
$getter = $user->obtainNameGetter();

echo $getter(); // "Robert PAULSON"

// now let's change the username
$user->setName("Tyler DURDEN");

echo $getter(); // "Tyler DURDEN"
Enter fullscreen mode Exit fullscreen mode

βœ… Pros

  • Can be used to solve circular dependencies
  • Poor-man lazy-loading implementation
  • You can compose the returned closure

❌ Cons

  • The returned closure is a black-box
  • You cannot serialize the returned value easily

Did I miss something? Tell me what you think of this list in the comments and don't forget to like/follow, it keeps me motivated to write more articles for you πŸ€—

Top comments (10)

Collapse
 
doekenorg profile image
Doeke Norg

I know the list is about 8.0, but in PHP 8.1 you can add readonly to the public parameter to prevent this con: Anyone can change the value. Nice list though!

Collapse
 
bdelespierre profile image
Benjamin Delespierre

Thanks, I'll add it as soon as PHP 8.1 comes out. There won't be much need for 99% of getters after that release πŸ˜…

Collapse
 
andreidascalu profile image
Andrei Dascalu

You have a weird definition of what's a pro and a con:

  • 2 birds with one stone => violate SRP, which you mention so technically you're calling the pro also a con (fair, but confusing)
  • can execute arbitrary code => violate the meaning of getter (eg: return the underlying value, the expectation is not to have arbitrary side-effects), technically is a call to violate SRP

"Getter/setter" - also same 'con' as public access: anyone can change the value.

Collapse
 
bdelespierre profile image
Benjamin Delespierre

Thanks for your message. Your concerns actually make sense. I'll change the article.

Collapse
 
kastaneda profile image
ΠšΠ°Ρ€Π»ΠΎΡ ΠšΠ°ΡΡ‚Π°Π½Π΅Π΄Π° ✳️

One more way: use Closure::bind().

Collapse
 
doekenorg profile image
Doeke Norg • Edited
class SomeClass
{
    private string $title = 'Some Class Title';
}

$some = new SomeClass();

$closure = function() {
    return $this->title;
};

$get_title = $closure->bindTo($some, $some);

echo $get_title(); // Some Class Title
Enter fullscreen mode Exit fullscreen mode

I think this is the idea. Very nasty, but indeed effective :-)

::bind() is the static variant of ->bindTo()

For those that want an example from the wild, checkout this NullAdapter from Symfony. It uses Closure::bind() to wrap items from the cache into CacheItem DTO's.

Collapse
 
bdelespierre profile image
Benjamin Delespierre

Interesting way of breaking encapsulation πŸ‘

Collapse
 
bdelespierre profile image
Benjamin Delespierre

Good call πŸ˜‰ I'm adding it!

Collapse
 
sujitagarwal profile image
Sujit Agarwal

Interesting.

Collapse
 
vuong profile image
Vuong

Thanks for posting. Where are exactly the points that only can do within >= 8.x versions?