5

What strategies are available to creating accessors and mutators for a PHP class' private members? Is this suggestion good: http://cormacscode.wordpress.com/2009/01/22/read-only-object-variables-in-php-using-magic-methods/

<?php
class classWithReadOnlyVar
{
    private $readOnlyVar;

    public function __get($varName)
    {
        return $this->$varName;
    }

    public function __set($varName,$varValue)
    {
    }
}

What if some members needed a private, public, or protected property method?

3
  • 1
    I don't understand the question "What if some members needed a private, public, or protected property method?" Commented Jul 7, 2011 at 2:48
  • Say you have a private member $foo (members should all be private, right?) and you wanted an accessor to the member $foo, but only want that class to be able to access it. So you would need a private function get_foo(), which is a private property. Commented Jul 7, 2011 at 3:17
  • 1
    I dont understand the sense of having a private accessor function, if you want this property to use inside the class, you can use $this->property. Getters and setters are meant to be used when value of a variable is set from out side the class to get its value. If you really need a private function, why use magic function? . you can name it something else. Commented Jul 7, 2011 at 4:29

3 Answers 3

6

First of all, __get, __set, etc. are defined public and you cannot have them otherwise. Magic methods should be used wisely as it takes about three times as long to make a call to a magic method than simply calling a class method.

class A {
   public function __get($name) { ... }

   public function __getValue() { ... }     // <== is faster

}

Usually (normally, preferably), you will have your class members private or protected (never public) and have accessors and mutators to encapsulate them. Those accessors and mutator may be of any visibility, depending on what the user can do with the members. You may also have immutable classes by declaring only accessors for your members, which are initialized in the constructor only.

So, your sample class should read

class classWithReadOnlyVar {
   private $readOnlyVar;

   public function getReadonlyVar() {
     return $this->readOnlyVar;
   }

}

and should not use the magic methods.

There may be many reasons to avoid using magic methods at all :

  1. they break code completion
  2. they are slower at run-time
  3. they make refactoring and maintenance a bit (lot) harder/complicated
  4. you can't have a protected magic method
  5. etc.

Class members

Their visibility should be private or protected, it all depends if you want them accessible by inheritence. They should never be public as this breaks the OO paradigm.

Example of a protected member:

class A {
    protected $_name = 'A';

    public function getName() { return $this->_name; }
}

class B {
    protected $_name = 'B';   // getName() will not return 'B'
}

(without $_name being protected, this would not be possible, and no need to redefine the accessor)

Accessors

They should be protected or public. Having a private accessor makes no sense; a class should access it's member directly. If the member needs processing, the class will know regardless when to call the accessor or not.

Mutators

They should be protected or public. As for the accessors, it makes no sense to have a private mutator... unless a very rare case when processing needs to be done internally. If a class member has no accessor, it should not have a mutator. It also makes no sense to have a mean to set a value without being able to get the value back somehow.

Sign up to request clarification or add additional context in comments.

1 Comment

shouldn't ALL class members be private?
4

Having a __set method that does nothing is nonsense. It gives the false impression that properties exist and are accessible when they really are not. Just leave out the empty method and PHP will properly throw errors when you try to modify non-modifiable properties.

Also, any accessor methods can only be public, anything else doesn't make sense. protected and private accessor methods don't make sense, since the only entity able to access them could also access the properties directly.


Summary of the discussion in the comments below:

  • you want to hide properties from code outside the class, since that code is not reliable and can potentially mess up the state of your class
  • code inside the class can reasonably be expected to behave properly with regards to its own properties, so there's no need to protect a class's properties from the class itself
  • a class can always access all its properties directly anyway, providing getters and setters doesn't ensure protection at all (like it does for access from outside the class)

17 Comments

private & protected accessors make sense for referring to members in the current object.
@deceze, private accessors don't make sense, indeed, but protected accessors may. I have seen it Zend Framework in a few places, where there was a few subclasses and accessors needed to implement different protected treatement for the base class...
@deceze - scoping accessors and mutators as private, protected, and public are basic OOP concepts, right? I'm not sure if I understand what you are saying.
But what's the difference between $this->getFoo() and $this->foo if both are private? Why does a class need accessors to its own properties? There may be a case for protected accessors and child classes, but I have hardly encountered that in practice.
@Stack I'd agree that private setters can make sense to ensure the consistency of your data. private getters don't make any sense at all though if you're only returning the value as-is. It just bloats your code and doesn't protect against anything.
|
2

It is not a good idea to use magic methods as __get() and __set(). The first reason being that the code doesn’t communicate its intend clearly. Secondly, a client will have to know about which fields are available to be able to use the magic methods. This violates the Law of Demeter that says a module shouldn’t know about the innards of the objects it manipulates.

Accessors, mutators and predicates should be named for their value and prefixed with get, set, and is.

For example:

MyClass
{
    /** @var int */
    private $myField;

    /** @return int */
    protected function getMyField()
    {
        $this->myField;
    }
    /** @param int $myField */
    protected function setMyField($myField)
    {
        $this->myField = $myField;
    }
}

You should by all means try to avoid creating public mutators, as it is tempting for external functions to use them the way a procedural program would use a data structure and thereby manipulate the internal state of the object, known as Feature Envy.

The methods of a class should only be interested in the fields and methods of the class they belong to, and not the fields and methods of other classes. When a method uses accessors and mutators of another object to manipulate the data within that object, then it envies the scope of that object. So we want to eliminate Feature Envy because it exposes the internals of one class to another. Sometimes, however, Feature Envy is a necessary evil, e.g., in the case where one object’s method requires the another object’s data to act upon, and moving that object’s method into the object holding the data would violate principles of object oriented design.

Why then should we have accessors and mutators if other classes shouldn’t use them? Well, making accessors and mutators protected allows for derived classes to access the parent’s private fields, yet still shielding the objects internal state from external peeking and poking.

But couldn’t we then just set the field to protected and thereby eliminating excess methods of the class? We surely could, but then you don’t have the possibility to shield the field from being set by a subclass.

In PHP, we could even argue that it makes sense for a class to use private mutators, so we have the opportunity to type check the value before it is set on the field.

For example:

MyClass
{
    /** @var int */
    private $myField;

    /** @return int */
    protected function getMyField()
    {
        $this->myField;
    }
    /** @param int $myField */
    private function setMyField($myField)
    {
        if (!(is_int($myField) || null === $myField)) {
            throw new \InvalidArgumentException(sprintf(
                '%s expects $myField to be integer; received %s.'
                __METHOD__,
                gettype($myField)
            ));
        }
        $this->myField = $myField;
    }
}

Here an exception is thrown if $myField is not an integer or null. However, it could also be argued that such code is a sign of mistrust to own coding. Regardless, having a private mutator can easily be changed to protected to allow for subclass manipulation of the field, and the accessor method could be set to private or removed to prevent subclasses reading the field.

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.