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?
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 :
protected
magic methodClass 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.
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With