Magic _Get Getter for Static Properties in PHP

Magic __get getter for static properties in PHP

No, it is not possible.

Quoting the manual page of __get :

Member overloading only works in
object context. These magic methods
will not be triggered in static
context. Therefore these methods can
not be declared static.


In PHP 5.3, __callStatic has been added ; but there is no __getStatic nor __setStatic yet ; even if the idea of having/coding them often comes back on the php internals@ mailling-list.

There is even a Request for Comments: Static classes for PHP

But, still, not implemented (yet ? )

PHP how to use magic method with static class?

In short, no.

__get() and __set() are instance methods. They are essentially the functions that make up the stdClass(), which is an instance.

If you must set static content in this manner you can give the class a stdClass parameter and a singleton structure that would allow you to magically set and get data.

For example:

class UserData {
protected static $_instance;
protected $_data = array();

public static function get_instance() {
static $initialized = FALSE;

if ( ! $initialized) {
self::$_instance = new UserData;
$initialized = TRUE;
}

return self::$_instance;
}

public function __get($var) {
$self = self::get_instance();

return isset($self->_data[$var]) ? $self->_data[$var] : NULL;
}

public function __set($var, $val) {
$self = self::get_instance();

$self->_data[$var] = $val;
}
}

Then you could go:

 $UserData =& UserData::get_instance();
$UserData->var = 'val';

echo $UserData->var; // prints 'val'

I don't recommend using Singletons in PHP, however, because they are pointless. You can read some reasons why in the post Best practice on PHP singleton classes.

Either use a static class or an instance class.

Magic getters and setters are shortcuts. You can implement the same behavior with normal setters and getters. The example below provides the same functionality, but the intent is a lot more clear:

class UserData {
protected $id, $name, $login;

public static function set_name($name) {
self::$name = $name;
}

public static function set_login($login) {
self::$login = $login;
}

public static function get_id() {
return self::$id;
}

public static function get_name() {
return self::$name;
}

public static function get_login() {
return self::login;
}
}

Notice how in the above code $id is not writable. It is only readable. $name and $login are readable and writable. It is easier and less buggy to control reading and writing using normal setters and getters. Magic methods are just that, magic, and usually magic is not concrete and is less understandable in code.

The final point I want to make is, why would UserData be static? Unless you only have 1 user in the entirety of your code it doesn't make sense to have it static. Perhaps I am not getting the whole picture, but something with an id and name should be instantiated so that you can have multiple instances. Otherwise, why have the id because the class itself is unique.

Best practice: PHP Magic Methods __set and __get

I have been exactly in your case in the past. And I went for magic methods.

This was a mistake, the last part of your question says it all :

  • this is slower (than getters/setters)
  • there is no auto-completion (and this is a major problem actually), and type management by the IDE for refactoring and code-browsing (under Zend Studio/PhpStorm this can be handled with the @property phpdoc annotation but that requires to maintain them: quite a pain)
  • the documentation (phpdoc) doesn't match how your code is supposed to be used, and looking at your class doesn't bring much answers as well. This is confusing.
  • added after edit: having getters for properties is more consistent with "real" methods where getXXX() is not only returning a private property but doing real logic. You have the same naming. For example you have $user->getName() (returns private property) and $user->getToken($key) (computed). The day your getter gets more than a getter and needs to do some logic, everything is still consistent.

Finally, and this is the biggest problem IMO : this is magic. And magic is very very bad, because you have to know how the magic works to use it properly. That's a problem I've met in a team: everybody has to understand the magic, not just you.

Getters and setters are a pain to write (I hate them) but they are worth it.

OOP PHP, getters and setters using magic methods

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.

Handling PHP properties like those in C# (getter & setter)

This is what PHP magic methods are for. They allow you to dynamically set properties through a mediator method. Here, let me show you an example:

abstract class GetterSetterExample
{
public function __get($name)
{
$method = sprintf('get%s', ucfirst($name));

if (!method_exists($this, $method)) {
throw new Exception();
}

return $this->$method();
}

public function __set($name, $v)
{
$method = sprintf('set%s', ucfirst($name));

if (!method_exists($this, $method)) {
throw new Exception();
}

$this->$method($v);
}
}

class Cat extends GetterSetterExample
{
protected $furColor;

protected function getFurColor()
{
return $this->furColor;
}

protected function setFurColor($v)
{
$this->furColor = $v;
}
}

$cat = new Cat();
$cat->furColor = 'black';

Yeah... it terrifies me as well.

This is why most people are content with using methods like the ones in Cat, but public. Apparently there is discussion on adding "real" getters and setters, but there appears to be a lot of conflict between those who hate them for concerns of readability, semantics and principle...

Using __get() (magic) to emulate readonly properites and lazy-loading

Here is the results of your code as reported by Zend Debugger with PHP 5.3.6 on my Win7 machine:

Benchmark results

As you can see, the calls to your __get methods are a good deal (3-4 times) slower than the regular calls. We are still dealing with less than 1s for 50k calls in total, so it is negligible when used on a small scale. However, if your intention is to build your entire code around magic methods, you will want to profile the final application to see if it's still negligible.

So much for the rather uninteresting performance aspect. Now let's take a look at what you consider "not that important". I'm going to stress that because it actually is much more important than the performance aspect.

Regarding Uneeded Added Complexity you write

it doesn't really increase my app complexity

Of course it does. You can easily spot it by looking at the nesting depth of your code. Good code stays to the left. Your if/switch/case/if is four levels deep. This means there is more possible execution pathes and that will lead to a higher Cyclomatic Complexity, which means harder to maintain and understand.

Here is numbers for your class A (w\out the regular Getter. Output is shortened from PHPLoc):

Lines of Code (LOC):                                 19
Cyclomatic Complexity / Lines of Code: 0.16
Average Method Length (NCLOC): 18
Cyclomatic Complexity / Number of Methods: 4.00

A value of 4.00 means this is already at the edge to moderate complexity. This number increases by 2 for every additional case you put into your switch. In addition, it will turn your code into a procedural mess because all the logic is inside the switch/case instead of dividing it into discrete units, e.g. single Getters.

A Getter, even a lazy loading one, does not need to be moderately complex. Consider the same class with a plain old PHP Getter:

class Foo
{
protected $bar;
public function getBar()
{
// Lazy Initialization
if ($this->bar === null) {
$this->bar = new Bar;
}
return $this->bar;
}
}

Running PHPLoc on this will give you a much better Cyclomatic Complexity

Lines of Code (LOC):                                 11
Cyclomatic Complexity / Lines of Code: 0.09
Cyclomatic Complexity / Number of Methods: 2.00

And this will stay at 2 for every additional plain old Getter you add.

Also, take into account that when you want to use subtypes of your variant, you will have to overload __get and copy and paste the entire switch/case block to make changes, while with a plain old Getter you simply overload the Getters you need to change.

Yes, it's more typing work to add all the Getters, but it is also much simpler and will eventually lead to more maintainable code and also has the benefit of providing you with an explicit API, which leads us to your other statement

I specifically want my API to be "isolated"; The documentation should tell others how to use it :P

I don't know what you mean by "isolated" but if your API cannot express what it does, it is poor code. If I have to read your documentation because your API does not tell me how I can interface with it by looking at it, you are doing it wrong. You are obfuscating the code. Declaring properties in an array instead of declaring them at the class level (where they belong) forces you to write documentation for it, which is additional and superfluous work. Good code is easy to read and self documenting. Consider buying Robert Martin's book "Clean Code".

With that said, when you say

the only reason is the api beauty;

then I say: then don't use __get because it will have the opposite effect. It will make the API ugly. Magic is complicated and non-obvious and that's exactly what leads to those WTF moments:

Code Quality: WTF per minute

To come to an end now:

i don't see any real disadvantages, I guess it's worth it

You hopefully see them now. It's not worth it.

For additional approaches to Lazy Loading, see the various Lazy Loading patterns from Martin Fowler's PoEAA:

There are four main varieties of lazy load. Lazy Initialization uses a special marker value (usually null) to indicate a field isn't loaded. Every access to the field checks the field for the marker value and if unloaded, loads it. Virtual Proxy is an object with the same interface as the real object. The first time one of its methods are called it loads the real the object and then delegates. Value Holder is an object with a getValue method. Clients call getValue to get the real object, the first call triggers the load. A ghost is the real object without any data. The first time you call a method the ghost loads the full data into its fields.

These approaches vary somewhat subtly and have various trade-offs. You can also use combination approaches. The book contains the full discussion and examples.

Magic __get in php

The problem is that your echoing the name in your __get() method, this will output the value straight away, but return the value of the variable to display later.

If you change the routine to...

public function __get($name){
//echo "$name,";
return "$name,".$this -> b[$name];
}

Your output becomes - A,b,B



Related Topics



Leave a reply



Submit