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.

3
  • What are you specifically curious about? Can you ask a question in which the prima facie answer isn't "I like it" or "I don't like it"? Commented May 20, 2019 at 14:56
  • @RobertHarvey Is this a convenient approach that fulfills my requirements?
    – Sir_Faenor
    Commented May 20, 2019 at 21:31
  • Only you can answer that. Commented May 20, 2019 at 21:32

1 Answer 1

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.

3
  • I did not know about that doc annotation, it's interesting even it's not runtime protection. Thanks!
    – Sir_Faenor
    Commented May 20, 2019 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. Commented Oct 17, 2019 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
    Commented Oct 17, 2019 at 17:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.