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 🤗

Discussion (9)

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 Author

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 Author

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 Author

Interesting way of breaking encapsulation 👍

Collapse
bdelespierre profile image
Benjamin Delespierre Author

Good call 😉 I'm adding it!

Collapse
sujitagarwal profile image
Sujit Agarwal

Interesting.