What is the use of the __sleep and __wakeup magic methods in PHP? I read the PHP documentation but it's still not clear:
class sleepWakeup {
public function __construct() {
// constructor //
}
public function __sleep() {
echo 'Time to sleep.';
}
public function __wakeup() {
echo 'Time to wakeup.';
}
}
$ob = new sleepWakeup();
// call __sleep method
echo $ob->__sleep();
echo "\n";
// call __wakeup method
echo $ob->__wakeup();
This sample code prints:
Time to sleep.
Time to wakeup.
If I were to rename __sleep and __wakeup to foo and bar then it does the same thing. What is the proper use of these two methods?
As already described, __sleep() is called when you serialize() an object and __wakeup() after you unserialize() it.
Serialization is used to persist objects: You will get a representation of an object as a string that can then be stored in $_SESSION, a database, cookies or anywhere else you desire.
Resource values
However, serialize() cannot serialize (i.e. transform into a textual representation) values of the resource type. This is why all of these values will go missing after unserialize()ing it.
Object graph
or members, and the member's members and the ... ad infinitum
Another, perhaps more important point is, that serialize() will traverse the entire object graph of $obj if you serialize it. This is great when you need it, but if you only need parts of the object and certain linked objects are "runtime-specific" and shared across a lot of objects but also by other objects, you may not want that behavior.
PHP handles cyclic graphs correctly! Meaning: If (a member of) $a links to $b, and $b links to $a is handled correctly however many levels deep.
Example - session specific (shared) objects
For instance, a $database object is referenced by $obj->db, but also by other objects. You will want $obj->db to be the same objects - after unserialize()ing - that all the other objects in your next session have, not an isolated instance of the database object.
In this case, you would have __sleep() method such as this:
/**
/* DB instance will be replaced with the one from the current session once unserialized()
*/
public function __sleep() {
unset($this->db);
}
and then restore it like this:
public function __wakeup() {
$this->db = <acquire this session's db object>
}
Another possibility is, that the object is part of some (global) datastructure where it needs to be registered. You could do this manually of course:
$obj = unserialize($serialized_obj);
Thing::register($obj);
However, if it is part of the objects contract that it needs to be in that registry, it's not a good idea to leave this magical call up to the user of your object. The ideal solution is, if the object cares about its responsibilities, i.e. being registered in Thing. That's what __wakeup() allows you to do transparently (i.e. he need no longer worry about that magical dependency) to your client.
Similarly, you could use __sleep() to "un-register" an object if appropriate. (Objects are not destroyed when they're serialized, but it may make sense in your context.)
Closures
Last but not least, closures do not support serialization either. This means that you will have to re-create all attached closures in __wakeup().
They are pretty much like hook functions, which we can use according to our needs. I came up with this simple real time example. Now try executing this code in two scenarios:
class demoSleepWakeup {
public $resourceM;
public $arrayM;
public function __construct() {
$this->resourceM = fopen("demo.txt", "w");
$this->arrayM = array(1, 2, 3, 4); // Enter code here
}
public function __sleep() {
return array('arrayM');
}
public function __wakeup() {
$this->resourceM = fopen("demo.txt", "w");
}
}
$obj = new demoSleepWakeup();
$serializedStr = serialize($obj);
var_dump($obj);
var_dump($serializedStr);
var_dump(unserialize($serializedStr));
Scenario 1:
First by commenting __sleep() and __wakeup() methods, check the output. You will find the resource missing when you unserialize it.
Scenario 2:
Now try running it uncommenting them, you will figure out that the object dumped in first and last var_dump would be same.
These methods are used when calling serialize() and unserialize() on the objects to make sure you have a hook to remove some properties like database connections and set them back when loading. This happens when storing objects in sessions among other things.
Since PHP 7.4 there will be new methods __serialize() and __unserialize() available which should slightly change the usage of __sleep and __wakeup magic methods.
PHP currently provides two mechanisms for custom serialization of
objects: The __sleep()/__wakeup() magic methods, as well as the
Serializable interface. Unfortunately, both approaches have issues
that will be discussed in the following. This RFC proposes to add a
new custom serialization mechanism that avoids these problems.
More in PHP RFC manual https://wiki.php.net/rfc/custom_object_serialization.
// Returns array containing all the necessary state of the object.
public function __serialize(): array;
// Restores the object state from the given data array.
public function __unserialize(array $data): void;
The usage is very similar to the Serializable interface. From a practical perspective the main difference is that instead of calling serialize() inside Serializable::serialize(), you directly return the data that should be serialized as an array.
The following example illustrates how __serialize()/__unserialize() are used, and how they compose under inheritance:
class A {
private $prop_a;
public function __serialize(): array {
return ["prop_a" => $this->prop_a];
}
public function __unserialize(array $data) {
$this->prop_a = $data["prop_a"];
}
}
class B extends A {
private $prop_b;
public function __serialize(): array {
return [
"prop_b" => $this->prop_b,
"parent_data" => parent::__serialize(),
];
}
public function __unserialize(array $data) {
parent::__unserialize($data["parent_data"]);
$this->prop_b = $data["prop_b"];
}
}
This resolves the issues with Serializable by leaving the actual serialization and unserialization to the implementation of the serializer. This means that we don't have to share the serialization state anymore, and thus avoid issues related to backreference ordering. It also allows us to delay __unserialize() calls to the end of unserialization.
try out this
<?php
$ob = new sleepWakeup();
$safe_me = serialize($ob);
$ob = unserialize($safe_me);
?>
Related
In my current application I have a number of objects that are required repeatedly
To save overhead of instantiating the same object over and over again, I keep an array of 'known' objects.
I can check the input data against the array, and - if already set - use the existing object, else go ahead to instantiate the new object and add the pointer reference to the relevant known objects array.
In most use cases, I can check prior to instantiating the class:
if(array_key_exists($identifier,$known_ClassObjects)){
$object = $known_ClassObjects[$identifier];
} else {
$object = new Class($params);
}
However, in some cases I can only identify that the object I am instantiating already exists once already inside it.
In that case I would like to be able to do one of 2 things:
return the OTHER (pre-existing) object instead of this one, e.g.
class Test{
public function __construct($params){
//apply processing to $params, resulting in $identifier
if(array_key_exists($identifier, $known_ClassObjects)){ //$known_ClassObjects is global
return $known_ClassObjects[$identifier];
} else {
//continue __construct() logic
return $this;
}
}
}
However, PHP ALWAYS returns the current object, even with code return $other_object;
'Internally Clone' the current object from the found one [of the same class, obv] so that when it returns, it has the correct relevant properties populated.
NOTE: including any parent/child class properties
-> I want to make this object EXACTLY the same as the found one.
So, if there was a PHP function clone_from(), it would work something like:
if(array_key_exists($identifier,$known_ClassObjects)){
$this->clone_from ($known_ClassObjects[$identifier]);
} else {
//continue with __construct()
}
return $this;
Given that 1. doesn't work and 2. doesn't seem to exist, I have only been able to do this in very 'hacky' ways: iterating through all properties of source object and setting all properties of current object.
However, this has obvious issues esp. with extended parent/child classes etc. which then requires things like reflection classes.
This seems like there SHOULD be a really simple solution, but I have been unable to find one
What you actually could do is using either a Singleton Pattern or a Factory pattern - in both cases, the creation of objects is controlled by some piece of code and you can decide, which object to return. Singleton already is a special form of a Factory pattern.
Consider this code
class Singleton {
protected static $instance;
protected function __construct() {
}
public static function instance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
}
The constructor is protected which will prevent a object construction from "outside" via new. However, there is also a static function instance with which somebody can request an object instance from the factory method.
$obj = Singleton::instance();
So, the (internal) object is created only once, and then delivered afterwards until the script ends.
However, PHP ALWAYS returns the current object, even with code return $other_object;
Constructor is not a regular function. You do not return from it. If you are in __construct() you are already constructing the new object. Before proceed, I strongly recommend you read at least: https://www.php.net/manual/en/language.oop5.decon.php
You most likely need a helper method or factory that would deal with that instead. Putting the logic you tried to place into your constructor is wrong.
Also you missed to clarify what the $identifier really is. But if that's object's property, then you can expose it (i.e. via getter) and have it read for your comparison/whatever by other code.
I have come across a really clever way of doing this. But I only used it once so I can't remember where I found the actual snippet.
I have a class called;
Worksheet {
private $settings
...
funnction GetSettings (){}
...
}
This class has methods, private properties and may extend from an existing class. At runtime I create an object from the class, "process it" and save it in a session so that it can be used by the visitor in a session. At each request I have to get the object from the session feed it back to my processor so that the object properties can be updated (Usually made of multi-dimensional arrays).
I have a simple method in my Processor class that creates the original prototype and mounts the session object to the prototype.
class Processor {
public function Mount ($worksheetObject){
$this->NewWorksheetPrototype (); //Recreate worksheet from prototype.
require ('includes/mount.php'); //This will go through the provided Object and mount it on to the processor.
}
}
Of course I don't want to go through the entire object recreate the properties which may contain lots of multi-dimensional arrays. Is there a reliable way of doing this? I know I can do this
$obj_merged = (object) array_merge((array) $obj1, (array) $obj2);
...but the methods will not be merged.
It is not possible to save an object in a session with the methods attached? I am working in a Symfony2 environment if this helps.
Provided you're on PHP 5.4 or greater, you could try something like this:
trait MergeableTrait{
protected $mergedObject;
public function merge($object2){
$this->mergedObject;
}
public function __get($name){
if(property_exists($this->mergedObject, $name)){
return $this->mergedObject->{'$name'}; //Pretty sure that's how it works.....
}
throw new RuntimeException("{$name} is not an available property");
}
public function __call($name, $arguments){
if(method_exists($this->mergedObject, $name)){
return call_user_func_array([$this->mergedObject, $name], $arguments);
}
throw new RuntimeException("{$name} is not a callable method");
}
}
I haven't tested it, but it should give you a good idea on how to do this. Provided your concrete classes don't have __call or __get methods defined, you can add the merge behavior like so:
class Worksheet{
use MergeableTrait;
...
}
...or wherever you need it.
Then you can do something like this:
$ws = new Worksheet();
$proc = new Processor();
$ws->merge($proc);
This is not tested, nor would I recommend doing things this way as it makes things much harder to debug
Also keep in mind that this won't take into account the visibility of your methods and properties. I would recommend perhaps expanding the checks to include some basic reflection, but overall, this should give you a head start.
I am creating a class which I will use to store and load some settings. Inside the class all settings are stored in an array. The settings can be nested, so the settings array is a multidimensional array. I want to store and load the settings using the magic methods __get and __set, so the settings can act as class members. However, since I'm using nested methods, I can't get the __set method to work when I try to access a nested setting.
The class is like this:
class settings
{
private $_settings = array();
//some functions to fill the array
public function __set($name, $value)
{
echo 'inside the __set method';
//do some stuff
}
}
And the code to use this class:
$foo = new settings();
//do some stuff with the class, so the internal settings array is as followed:
//array(
// somename => somevalue
// bar => array (
// baz = someothervalue
// qux = 42
// )
// )
$foo->somename = something; //this works, __set method is called correctly
$foo->bar['baz'] = somethingelse; //Doesn't work, __set method isn't called at all
How can I get this last line to work?
When accessing an array using this method, it actually goes through __get instead. In order to set a parameter on that array that was returned it needs to be returned as a reference: &__get($name)
Unless, what you mean is that you want each item that is returned as an array to act the same way as the parent object, in which case you should take a look at Zend Framework's Zend_Config object source for a good way to do that. (It returns a new instance of itself with the sub-array as the parameter).
This would work:
$settings = new Settings();
$settings->foo = 'foo';
$settings->bar = array('bar');
But, there is no point in using magic methods or the internal array at all. When you are allowing getting and setting of random members anyway, then you can just as well make them all public.
Edit after comments (not answer to question above)
Like I already said in the comments I think your design is flawed. Let's tackle this step by step and see if we can improve it. Here is what you said about the Settings class requirements:
settings can be saved to a file or a database
settings might need to update other parts of the application
settings need to be validated before they are changed
should use $setting->foo[subsetting] over $setting->data[foo[subsetting]]
settings class needs to give access to the settings data for other classes
first time an instance is made, the settings need to be loaded from a file
Now, that is quite a lot of things to do for a single class. Judging by the requirements you are trying to build a self-persisting Singleton Registry, which on a scale of 1 (bad) to 10 (apocalyptic) is a level 11 idea in my book.
According to the Single Responsibility Principle (the S in SOLID) a class should have one and only reason to change. If you look at your requirements you will notice that there is definitely more than one reason to change it. And if you look at GRASP you will notice that your class takes on more roles than it should.
In detail:
settings can be saved to a file or a database
That is at least two responsibilites: db access and file access. Some people might want to further distinguish between reading from file and saving to file. Let's ignore the DB part for now and just focus on file access and the simplest thing that could possibly work for now.
You already said that your settings array is just a dumb key/value store, which is pretty much what arrays in PHP are. Also, in PHP you can include arrays from a file when they are written like this:
<?php // settings.php
return array(
'foo' => 'bar'
);
So, technically you dont need to do anything but
$settings = include 'settings.php';
echo $settings['foo']; // prints 'bar';
to load and use your Settings array from a file. This is so simple that it's barely worth writing an object for it, especially since you will only load those settings once in your bootstrap and distribute them to the classes that need them from there.
Saving an array as an includable file isnt difficult either thanks to var_export and file_put_contents. We can easily create a Service class for that, for example
class ArrayToFileService
{
public function export($filePath, array $data)
{
file_put_contents($filePath, $this->getIncludableArrayString($data));
}
protected function getIncludableArrayString($data)
{
return sprintf('<?php return %s;', var_export($data, true));
}
}
Note that I deliberatly did not make the methods static despite the class having no members of it's own to operate on. Usign the class statically will add coupling between the class and any consumer of that class and that is undesirable and unneccessary.
All you have to do now to save your settings is
$arrayToFileService = new ArrayToFileService;
$arrayToFileService->export('settings.php', $settings);
In fact, this is completely generic, so you can reuse it for any arrays you want to persist this way.
settings might need to update other parts of the application
I am not sure why you would need this. Given that our settings array can hold arbitrary data you cannot know in advance which parts of the application might need updating. Also, knowing how to update other parts of the application isnt the responsiblity of a data container. What we need is a mechanism that tells the various parts of the application when the array got updated. Of course, we cannot do that with a plain old array because its not an object. Fortunately, PHP allows us to access an object like an array by implementing ArrayAccess:
class HashMap implements ArrayAccess
{
protected $data;
public function __construct(array $initialData = array())
{
$this->data = $initialData;
}
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
public function offsetGet($offset)
{
return $this->data[$offset];
}
public function offsetSet($offset, $value)
{
$this->data[$offset] = $value;
}
public function offsetUnset($offset)
{
unset($this->data[$offset]);
}
public function getArrayCopy()
{
return $this->data;
}
}
The methods starting with offset* are required by the interface. The method getArrayCopy is there so we can use it with our ArrayToFileService. We could also add the IteratorAggregate interface to have the object behave even more like an array but since that isnt a requirement right now, we dont need it. Now to allow for arbitrary updating, we add a Subject/Observer pattern by implementing SplSubject:
class ObservableHashMap implements ArrayAccess, SplSubject
…
protected $observers;
public function __construct(array $initialData = array())
{
$this->data = $initialData;
$this->observers = new SplObjectStorage;
}
public function attach(SplObserver $observer)
{
$this->observers->attach($observer);
}
public function detach(SplObserver $observer)
{
$this->observers->detach($observer);
}
public function notify()
{
foreach ($this->observers as $observers) {
$observers->update($this);
}
}
}
This allows us to register arbitrary objects implementing the SplObserver interface with the ObservableHashMap (renamed from HashMap) class and notify them about changes. It would be somewhat prettier to have the Observable part as a standalone class to be able to reuse it for other classes as well. For this, we could make the Observable part into a Decorator or a Trait. We could also decouple Subject and Observers further by adding an EventDispatcher to mediate between the two, but for now this should suffice.
Now to notify an observer, we have to modify all methods of the class that should trigger a notification, for instance
public function offsetSet($offset, $value)
{
$this->data[$offset] = $value;
$this->notify();
}
Whenever you call offsetSet() or use [] to modify a value in the HashMap, any registered observers will be notified and passed the entire HashMap instance. They can then inspect that instance to see whether something important changed and react as needed, e.g. let's assume SomeComponent
class SomeComponent implements SplObserver
{
public function update(SplSubject $subject)
{
echo 'something changed';
}
}
And then you just do
$data = include 'settings.php';
$settings = new ObservableHashMap($data);
$settings->attach(new SomeComponent);
$settings['foo'] = 'foobarbaz'; // will print 'something changed'
This way, your settings class needs no knowledge about what needs to happen when a value changes. You can keep it all where it belongs: in the observers.
settings need to be validated before they are changed
That one is easy. You dont do it inside the hashmap/settings object at all. Given that the HashMap is just a dumb container holding arbitrary data that is supposed to be used by other classes, you put the validation into those classes that use the data. Problem solved.
should use $setting->foo[subsetting] over $setting->data[foo[subsetting]]
Well, yeah. As you probably have guessed already, the above implementation doesnt use this notation. It uses $settings['foo'] = 'bar' and you cannot use $settings['foo']['bar'] with ArrayAccess (at least to my knowledge). So that is somewhat of a limitation.
settings class needs to give access to the settings data for other classes
This and the next requirement smell like Singleton to me. If so, think again. All you ever need is to instantiate the settings class once in your bootstrap. You are creating all the other classes that are required to fulfill the request there, so you can inject all the settings values right there. There is no need for the Settings class to be globally accessible. Create, inject, discard.
first time an instance is made, the settings need to be loaded from a file
See above.
The part $foo->bar is actually calling __get, this function should (in your case) return an array.
returning the right array in the __get would then be your solution.
As has been stated, this is because it is the array stored in $foo->bar that is being modified rather than the class member. The only way to invoke __set behaviour on an 'array' would be to create a class implementing the ArrayAccess interface and the offsetSet method, however this would defeat the purpose of keeping the settings in the same object.
A reasonably neat and common work around is to use dot delimited paths:
class Settings {
protected $__settings = array();
// Saves a lot of code duplication in get/set methods.
protected function get_or_set($key, $value = null) {
$ref =& $this->__settings;
$parts = explode('.', $key);
// Find the last array section
while(count($parts) > 1) {
$part = array_shift($parts);
if(!isset($ref[$part]))
$ref[$part] = array();
$ref =& $ref[$part];
}
// Perform the appropriate action.
$part = array_shift($parts);
if($value)
$ref[$part] = $value;
return $ref[$part];
}
public function get($key) { return $this->get_or_set($key); }
public function set($key, $value) { return $this->get_or_set($key, $value); }
public function dump() { print_r($this->__settings); }
}
$foo = new Settings();
$foo->set('somename', 'something');
$foo->set('bar.baz', 'somethingelse');
$foo->dump();
/*Array
(
[somename] => something
[bar] => Array
(
[baz] => somethingelse
)
)*/
This also makes it clearer you are not manipulating instance variables, as well as allowing arbitrary keys without fear of conflicts with instance variables. Further processing for specific keys can be achieved by simply adding key comparisons to get/set e.g.
public function set(/* ... */) {
/* ... */
if(strpos($key, 'display.theme') == 0)
/* update the theme */
/* ... */
}
I am working with classes and object class structure, but not at a complex level – just classes and functions, then, in one place, instantiation.
As to __construct and __destruct, please tell me very simply: what is the purpose of constructors and destructors?
I know the school level theoretical explanation, but i am expecting something like in real world, as in which situations we have to use them.
Provide also an example, please.
Regards
A constructor is a function that is executed after the object has been initialized (its memory allocated, instance properties copied etc.). Its purpose is to put the object in a valid state.
Frequently, an object, to be in an usable state, requires some data. The purpose of the constructor is to force this data to be given to the object at instantiation time and disallow any instances without such data.
Consider a simple class that encapsulates a string and has a method that returns the length of this string. One possible implementation would be:
class StringWrapper {
private $str;
public function setInnerString($str) {
$this->str = (string) $str;
}
public function getLength() {
if ($this->str === null)
throw new RuntimeException("Invalid state.");
return strlen($this->str);
}
}
In order to be in a valid state, this function requires setInnerString to be called before getLength. By using a constructor, you can force all the instances to be in a good state when getLength is called:
class StringWrapper {
private $str;
public function __construct($str) {
$this->str = (string) $str;
}
public function getLength() {
return strlen($this->str);
}
}
You could also keep the setInnerString to allow the string to be changed after instantiation.
A destructor is called when an object is about to be freed from memory. Typically, it contains cleanup code (e.g. closing of file descriptors the object is holding). They are rare in PHP because PHP cleans all the resources held by the script when the script execution ends.
Learn by example:
class Person {
public $name;
public $surname;
public function __construct($name,$surname){
$this->name=$name;
$this->surname=$surname;
}
}
Why is this helpful? Because instead of:
$person = new Person();
$person->name='Christian';
$person->surname='Sciberras';
you can use:
$person = new Person('Christian','Sciberras');
Which is less code and looks cleaner!
Note: As the replies below correctly state, constructors/destructors are used for a wide variety of things, including: de/initialization of variables (especially when the the value is variable), memory de/allocation, invariants (could be surpassed) and cleaner code.
I'd also like to note that "cleaner code" is not just "sugar" but enhances readability, maintainability etc.
The constructor is run at the time you instantiate an instance of your class. So if you have a class Person:
class Person {
public $name = 'Bob'; // this is initialization
public $age;
public function __construct($name = '') {
if (!empty($name)) {
$this->name = $name;
}
}
public function introduce() {
echo "I'm {$this->name} and I'm {$this->age} years old\n";
}
public function __destruct() {
echo "Bye for now\n";
}
}
To demonstrate:
$person = new Person;
$person->age = 20;
$person->introduce();
// I'm Bob and I'm 20 years old
// Bye for now
We can override the default value set with initialization via the constructor argument:
$person = new Person('Fred');
$person->age = 20;
$person->introduce();
// if there are no other references to $person and
// unset($person) is called, the script ends
// or exit() is called __destruct() runs
unset($person);
// I'm Fred and I'm 20 years old
// Bye for now
Hopefully that helps demonstrate where the constructor and destructor are called, what are they useful for?
__construct() can default class members with resources or more complex data structures.
__destruct() can free resources like file and database handles.
The constructor is often used for class composition or constructor injection of required dependencies.
The constructor of a class defines what happens when you instantiate an object from this class. The destructor of a class defines what happens when you destroy the object instance.
See the PHP Manual on Constructors and Destructors:
PHP 5 allows developers to declare constructor methods for classes. Classes which have a constructor method call this method on each newly-created object, so it is suitable for any initialization that the object may need before it is used.
and
PHP 5 introduces a destructor concept similar to that of other object-oriented languages, such as C++. The destructor method will be called as soon as all references to a particular object are removed or when the object is explicitly destroyed or in any order in shutdown sequence.
In practise, you use the Constructor to put the object into a minimum valid state. That means you assign arguments passed to the constructor to the object properties. If your object uses some sort of data types that cannot be assigned directly as property, you create them here, e.g.
class Example
{
private $database;
private $storage;
public function __construct($database)
{
$this->database = $database;
$this->storage = new SplObjectStorage;
}
}
Note that in order to keep your objects testable, a constructor should not do any real work:
Work in the constructor such as: creating/initializing collaborators, communicating with other services, and logic to set up its own state removes seams needed for testing, forcing subclasses/mocks to inherit unwanted behavior. Too much work in the constructor prevents instantiation or altering collaborators in the test.
In the above Example, the $database is a collaborator. It has a lifecycle and purpose of it's own and may be a shared instance. You would not create this inside the constructor. On the other hand, the SplObjectStorage is an integral part of Example. It has the very same lifecycle and is not shared with other objects. Thus, it is okay to new it in the ctor.
Likewise, you use the destructor to clean up after your object. In most cases, this is unneeded because it is handled automatically by PHP. This is why you will see much more ctors than dtors in the wild.
I've found it was easiest to grasp when I thought about the new keyword before the constructor: it simply tells my variable a new object of its data type would be give to him, based on which constructor I call and what I pass into it, I can define to state of the object on arrival.
Without the new object, we would be living in the land of null, and crashes!
The Destructor is most obvious from a C++ stand point, where if you dont have a destructor method delete all the memory pointed to, it will stay used after the program exits causing leaks and lag on the clients OS untill next reboot.
I'm sure there's more than enough good information here, but another angle is always helpful from what I've noticed!
constructor is function of class which is executed automatically when object of class is created we need not to call that constructor separately we can say constructor as magic method because in php magic method begin with double underscore characters
PHP object overloading is explained here.
Basically it allows you to define some custom actions when an inaccessible object property or method is accessed.
What are some practical uses for this feature?
Usually, those methods are useful when you are communicating with a 3rd party API or when the method/members structure is unclear.
Let's say you are writing a generic XML-RPC wrapper. Since you don't know the methods available to you before you download the WDL file, it makes sense to use Overloading.
Then, instead of writing the following:
$xmlrpc->call_method('DoSomething', array($arg1, $arg2));
You can use:
$xmlrpc->DoSomething($arg1, $arg2);
which is a more natural syntax.
You can also use member overloading in the same way as method overloading for variable objects.
Just one thing you want to watch for: limit its use only to variable-structure objects or use it only for syntactical shortcuts to getters and setters. It makes sense to keep getters and setters in your class to seperate business logic in multiple methods, but there is nothing wrong in using it as a shortcut:
class ShortcutDemo {
function &__get($name) {
// Usually you want to make sure the method
// exists using method_exists, but for sake
// of simplicity of this demo, I will omit
// that logic.
return call_user_method('get'.$name, $this);
}
function __set($name, &$value) {
return call_user_method('set'.$name, $this, $value);
}
private $_Name;
function &getName() { return $this->_Name; }
function setName(&$value) { $this->_Name = $value; }
}
That way you can continue using your getters and setters to validate and set your data, and still use the syntactic shortcuts as such:
$shortcut->Name = 'Hello';
Another method that Andrew didn't mention (or hasn't mentioned at the time of writing) is for getting rid of getters and setters. Instead of having to declare each setter and getter like this:
$obj->setName("Chacha");
$obj->setRep(10000000000000000000000);
You can instead just do
$obj->Name = "chacha";
$obj->Rep = 100000000000000000;
The second method is more natural.
Magic Methods basically further the thought of Object Oriented programming, and the idea that how you implement a job should not matter to the outside world. Through Magic Methods, it allows you to store your variables however you want, and just let other classes set them in a natural way.
Example: I could store all my user's account preferences in a single array that would make it really easy to iterate through to push it all up to the session.
If I didn't use a Magic Method for this, I would either have to make a bunch of sets or gets, which means writing more code, or allow direct access to the array, which reveals the implementation, so I can't go and change it later.
Instead, using Magic Methods, I just have them set the variable regularly, and I deal with it internally.
You could use it for cases when a class has complex rules for isset and unset. For example, a class containing a variable $a could be an array of objects or other resources, and when unset, they have to do perform some other functionalities.
Though I am not sure why they allow the adding of a new property and retrieving of a non-private property, but you could use it to change the internal state of an object by calling other code depending on the name of the property/member variable being set.
In some cases, this resembles operator overloading in C++
Message forwarding for when you have composed or aggregated objects where polymorphism isn't an option (say, you're using a library class you can't control).
<?php
// Class A is final, so we can't make subclasses.
final class A
{
public function hello( $callback )
{
echo call_user_func( $callback, 'hello world' );
}
}
// so instead, we make a wrapper class that will take an instance
// of A as an aggregate
class B
{
private $a;
public function __construct( A $a )
{
$this->a = $a;
}
// this mimics inheritance on the aggregate object
// method calls are automatically forwarded to instance of A
// if they are valid
public function __call( $method, $args )
{
if ( method_exists( $this->a, $method ) )
{
return call_user_func_array( array( $this->a, $method ), $args );
}
throw new Exception( "Method [$method] not found." );
}
}
class C extends B
{
// This mimics overriding an "inherited" method
public function hello( $callback )
{
echo call_user_func( $callback, 'bonjour le monde' );
}
}
$a = new A;
$b = new B( $a );
$c = new C( $a );
$b->hello( 'strtoupper' );
$c->hello( 'strtoupper' );
This feature is actually what object oriented programming is all about, in the mind of its inventor Alan Kay: Objects sending each other messages, and potentially reacting to any kind of message. Methods fixed at compile time are a limited (but also more efficient) implementation of this concept. That's where Kay's famous-quote "I invented the term object oriented, and I can tell you that C++ wasn't what I had in mind." comes from.
Basically, allowing objects to react to method calls without having a corresponding method fixed at compile time implements this original, broader definition of object orientation. Most modern "dynamic" languages support it in one form or another.
As for what it's good for: take a look at Groovy's Builders for a good example. Basically, it allows very compact low-redundancy syntax by turning method names themselves into data.
One way, which is quite a bit fancier, that I've used it is to create a Linq like Object Relational Management (ORM) system. Where you can then load up a database table structure and manipulated the data (from the database table) as if it were just an object.
i.e.
include('blibrary/bLinq.class.inc');
$linq = new bLinq(new bLinqSql('mysql://dsn'));
$r = $linq->from('Users')
->password
->select();
which translates to the following SQL:
SELECT `password` from Users;
The password in the select statement comes from the overloaded method.
The result can be used like:
(array)$r->password; // which outputs an array multiple results of password;
(string)$r->password; // which outputs a string of the first password hash;
$r->password[2]; // which outputs a string of the third password hash;
The point is that the word "password" could be substituted for any other field in the database on the fly when programming.
I use __get and __set to link objects together e.g.
$user = new User();
echo $user->Profile->views;
This (usually) calls some SQL linking users.id = profile.user_id.
Properties (like that in Python or C#). For example when you use something like this in Nette, you create some class, which shows some property as public:
<?php
class Foo extends Object
{
public $bar;
}
You can access this property natural way – $instance->bar. But when you want to do some validation etc., you just add getter and setter:
<?php
class Foo extends Object
{
private $bar;
public function getBar()
{
return $this->bar;
}
public function setBar($bar)
{
if ($bar === NULL) throw new Exception('…');
$this->bar = $bar;
}
}
And still you use $instance->bar. But when you do $instance->bar = NULL;, it's like calling $instance->setBar(NULL);.