0

I need to share some associative data between different parts of my application with two requirements:

  • immutability (so read-only)
  • safety against mispelling or unavailable index inside the data
  • mixed type

I have no need for behaviour, internal state, validation or other DDD related features, just simple read-only data.

For the past I intensively used arrays for such situations but I feel they are more prone to errors due to possible mispelling (trying to access a non existent key would just trigger a notice in standard php configuration) and easy overwriting.

So I recently came to build up a simple class that stores an associative array of data but exposes one and only one way to access it, through php built-in magic methods, throwing exceptions for undefined index inside the data and preventing clients from writing it. Code follows:

class MyData
{

    protected $data;

    public function __construct(array $data) {
        $this->data = $data;
    }

    public function __get($propertyName) {

        if (!array_key_exists($propertyName, $this->data)) {
            throw new Exception("Missing key ".$propertyName);
        }

        return $this->data[$propertyName];
    }

    public function __set($propertyName, $value) {

        throw new Exception("Cannot write ".$propertyName);

    }

}

I think this approach would prevent from external writing and trying to access unavailable index, throwing related exceptions and notifying clients in a stronger way than a notice would do. Indeed this would throw an exception:

echo $data->propertyThatDoesNotExist;

and also this:

$data->propertyThatExists = $newValue;

I've already read about arrays vs objects for more or less specific purpose, such as:

I am curious about what some more experienced developers (even outside of php) think about this solution, thank you very much.

Sir_Faenor
  • 101
  • 1

1 Answers1

1

Your approach does not address spelling issues during development. Nor do you get any type hinting for your IDE. There is however a doc annotation that comes in handy:

/**
 * @property-read string $name
 * @property-read string $email
 * @property-read string $username
 */
class User
{
    public $name;
    public $email;
    public $username;

With this approach your IDE knows exactly which properties your class has and will flag spelling errors. It will also warn against any attempt to assign a value to a property outside of the class. Not quite as good as run time read-only protection but it works well for me. And of course it tells your IDE what the expected types are.

The doc property also works for properties accessed via __get($name). It basically restricts $name values and again provides type hints.

Just something to consider.

Cerad
  • 598
  • 4
  • 9
  • I did not know about that doc annotation, it's interesting even it's not runtime protection. Thanks! – Sir_Faenor May 20 '19 at 21:29
  • Since Cerad is defining public fields for the class instead of magic get/set functions, you get runtime protection as well. `$user = new User(); $user->foo = 4;` will throw a runtime error when attempting to assign the `foo` property. – Greg Burghardt Oct 17 '19 at 16:55
  • @GregBurghardt I wish it worked that way but php itself has no readonly capability. Your IDE will complain. phpstorm gives you a nice red flag but the code will actually run just fine. – Cerad Oct 17 '19 at 17:41