开发者

Practical applications of PHP magic methods - __get, __set, and __call

I've generally tried to stay away from PHP's magic methods because they seem to obfuscate an object's public interface. That said, th开发者_如何学运维ey seem to be used more and more, at least, in the code I've read, so I have to ask: is there any consensus on when to use them? Are there any common patterns for using these three magic methods?


The main reason is that you do not need to type as much. You could use them for, say, an ORM record and act as implicit setters/getters:

using __call():

$user = new User();
$user->setName("Foo Bar");
$user->setAge(42);
$user->save();

using __set():

$user->name = "Foo Bar";
$user->age = 42;

which maps to a simple array:

array(
    "name" => "Foo Bar",
    "age"  => 42
)

It is much easier to write such an array to the database than doing a lot of manual calls to collect all needed information. __set() and __get() have another advantage over public members: You are able to validate/format your data.


__call()

I've seen it used to implement behaviors, as in add extra functions to a class through a pluginable interface.

Pseudo-code like so:

$method = function($self) {};
$events->register('object.method', $method);
$entity->method(); // $method($this);

It also makes it easier to write mostly similar functions, such as in ORMs. e.g.:

$entity->setName('foo'); // set column name to 'foo'

__get()/__set()

I've mostly seen it used to wrap access to private variables.

ORMs are the best example that comes to mind:

$entity->name = 'foo'; // set column name to 'foo'


It allows you to do things like this:

class myclass {
    private $propertybag;

    public function __get($name) {
        if(isset($this->propertybag[$name]) {return $this->propertybag[$name];}
        throw new Exception("Unknown property " . (string) $name);
    }

 }

Then you can populate $propertybag from a SQL query in a single line, rather than setting a whole bunch of properties one by one.

Also, it allows you to have specific properties which are read-only (ie don't allow them to be modified via __set()). Maybe useful for an ID field, for example.

Also, you can put code into __get() and __set(), so you can do something more complex than just getting or setting a single variable. For example, if you have a storeID field, you may also want to provide a storeName property. You could implement that in __get() via a cross-reference lookup, so you may not need the name actually to be stored in the class. And of course storeName would not want to be implemented in __get().

Lots of possibilities there.

There are of course some down-sides of using magic methods. The biggest one for me is the fact that you lose the auto-complete functionality in your IDE. This may or may not matter to you.


Since magic methods can save you a LOT of coding when it comes to repetitive tasks like defining members, populating them and then retrieving them - instead of doing that boring, long piece of work, you can use mentioned 3 methods to shorten the time to code all that. If needed, I can provide a few examples tho they can be found in various tutorials over the net.

I don't know if it's general consensus, but the usual should apply - use where appropriate. If you find yourself to do repetitive task (define member, populate member, get member, call X functions that differ slightly) - magic methods might help you.


One common pattern is to have a single handle for your clients and proxy the calls to encapsulated objects or singletons based on naming conventions or configurations.

class db
{
    static private $instance = null;

    static public function getInstance()
    {
        if( self::$instance == NULL )
            self::$instance = new db;

        return self::$instance;
    }

    function fetch()
    {
        echo "I'm fetching\n";
    }
}

class dataHandler
{
    function __call($name, $argv)
    {
        if( substr($name, 0, 4) == 'data' )
        {
            $fn = substr($name, 4);
            db::getInstance()->$fn($argv);
        }
    }
}

$dh = new dataHandler;
$dh->datafetch('foo', 'bar');

Same principles can be used to drive different backends of the same functionality without having to change the driver.


Whenever you'd like, as long as the magic properties/methods are documented; undocumented magic should be avoided unless working with a very abstract layer of code, such as when developing an ORM.

acceptable at an abstract layer

/**
 * DB backed model base class.
 */
class Model {
    protected $attributes = [];

    function __get($name) {
        return @$this->attributes[$name];
    }
}

acceptable when documented

/**
 * User model backed by DB tables.
 * @property-read string $first_name
 * @property-read string $last_name
 */
class UserModel extends Model {

}

lazy and unacceptable (and common when using ORMs)

/**
 * This class is magical and awesome and I am a lazy shithead!
 */
class UserModel extends WhoCaresWhenEverythingIsMagical {

}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜