PHP Magic Features
时间:2009-04-16 来源:cobrawgl
Magic methods, which are class methods with specific names, are used to perform various specialized tasks. They are grouped into two: overloading methods and non-overloading methods. Overloading magic methods are used when your code attempts to access a method or a property which does not exist. Non-overloading methods perform other tasks.
Magic functions, which are similar to magic methods, but are just plain functions outside any class. Currently there is only one magic function in PHP.
Magic constants, which are similar to constants in notation, but act more like "dynamic" constants - their value depends on where you use them.
We'll also look at some practical examples of using some of these, and lastly we'll check out what new features PHP 5.3 is going to add.
Magic methods
For starters, let's take a look at the magic methods PHP provides. We will first go over the non-overloading methods.
__construct and __destruct
class SomeClass {
public function __construct() {
}
public function __destruct() {
}
}
The most common magic method in PHP is __construct. In fact, you might not even have thought of it as a magic method at all, as it's so common. __construct is the class constructor method, which gets called when you instantiate a new object using the new keyword, and any parameters used will get passed to __construct.
$obj = new SomeClass();
__destruct is __construct's "pair". It is a class destructor, which is rarely used in PHP, but still it is good to know about its existence. It gets called when your object falls out of scope or is garbage collected.
function someFunc() {
$obj = new SomeClass();
//when the function ends, $obj falls out of scope and SomeClass __destruct is called
}
someFunc();
If you make the constructor private or protected, it means that the class cannot be instantiated, except inside a method of the same class. You can use this to your advantage, for example to create a singleton.
__clone
class SomeClass {
public $someValue;
public function __clone() {
$clone = new SomeClass();
$clone->someValue = $this->someValue;
return $clone;
}
}
The __clone method is called when you use PHP's clone keyword, and is used to create a clone of the object. The purpose is that by implementing __clone, you can define a way to copy objects.
$obj1 = new SomeClass();
$obj1->someValue = 1;
$obj2 = clone $obj1;
echo $obj2->someValue;
//echos 1
Important: __clone is not the same as =. If you use = to assign an object to another variable, the other variable will still refer to the same object as the first one! If you use the clone keyword, the purpose is to return a new object with similar state as the original. Consider the following:
$obj1 = new SomeClass();
$obj1->someValue = 1;
$obj2 = $obj1;
$obj3 = clone $obj1;
$obj1->someValue = 2;
What are the values of the someValue property in $obj2 and $obj3 now? As we have used the assign operator to create $obj2, it refers to the same object as $obj1, thus $obj2->someValue is 2. When creating $obj3, we have used the clone keyword, so the __clone method was called. As __clone creates a new instance, $obj3->someValue is still the same as it was when we cloned $obj1: 1.
If you want to disable cloning, you can make __clone private or protected.
__toString
class SomeClass {
public function __toString() {
return 'someclass';
}
}
The __toString method is called when PHP needs to convert class instances into strings, for example when echoing:
$obj = new SomeClass();
echo $obj;
//will output 'someclass'
This can be a useful example to help you identify objects or when creating lists. If we have a user object, we could define a __toString method which outputs the user's first and last names, and when we want to create a list of users, we could simply echo the objects themselves.
__sleep and __wakeup
class SomeClass {
private $_someVar;
public function __sleep() {
return array('_someVar');
}
public function __wakeup() {
}
}
These two methods are used with PHP's serializer: __sleep is called with serialize(), __wakeup is called with unserialize(). Note that you will need to return an array of the class variables you want to save from __sleep. That's why the example class returns an array with _someVar in it: Without it, the variable will not get serialized.
$obj = new SomeClass();
$serialized = serialize($obj);
//__sleep was called
unserialize($serialized);
//__wakeup was called
You typically won't need to implement __sleep and __wakeup, as the default implementation will serialize classes correctly. However, in some special cases it can be useful. For example, if your class stores a reference to a PDO object, you will need to implement __sleep, as PDO objects cannot be serialized.
As with most other methods, you can make __sleep private or protected to stop serialization. Alternatively, you can throw an exception, which may be a better idea as you can provide a more meaningful error message.
An alternative to __sleep and __wakeup is the Serializable interface. However, as its behavior is different from these two methods, the interface is outside the scope of this article. You can find info on it in the PHP manual.
__set_state
class SomeClass {
public $someVar;
public static function __set_state($state) {
$obj = new SomeClass();
$obj->someVar = $state['someVar'];
return $obj;
}
}
This method is called in code created by var_export. It gets an array as its parameter, which contains a key and value for each of the class variables, and it must return an instance of the class.
$obj = new SomeClass();
$obj->someVar = 'my value';
var_export($obj);
This code will output something along the lines of:
SomeClass::__set_state(array('someVar'=>'my value'));
Note that var_export will also export private and protected variables of the class, so they too will be in the array.
Overloading methods
Now that we've gone through all the non-overloading methods, we can move to the overloading ones.
If you define an overloading magic method, they all have some behavior that's important to know before using them. They only apply to methods and variables that are inaccessible:
- methods or variables that do not exist at all
- variables which are outside the scope
Basically, this means that if you have a public member foo, overloading methods will not get called when you attempt to access it. If you attempt to access member bar, which does not exist, they will.
Also, if you declare a private/protected variable $hiddenVar, and attempt to access it outside the class' own methods, the overloading methods will get called. This applies to classes which inherit the original— any attempt to access the parent's private variables will result in overloading method calls.
__call
class SomeClass {
public function __call($method, $parameters) {
}
}
This magic method is called when the code attempts to call a method which does not exist. It takes two parameters: the method name that was being called, and any parameters that were passed in the call.
$obj = new SomeClass();
$obj->missingMethod('Hello');
//__call is called, with 'missingMethod' as the first parameter and
//array('Hello') as the second parameter
This can be used to implement all kinds of useful things. For example, you can use __call to create automatic getter methods for variables in your class:
class GetterClass {
private $_data = array(
'foo' => 'bar',
'bar' => 'foo'
);
public function __call($method, $params) {
if(substr($method, 0, 3) == 'get') {
//Change the latter part of the method name to lowercase
//so that it matches the keys in the data array
return $this->_data[strtolower(substr($method, 3))];
}
}
}
$obj = new GetterClass();
echo $obj->getBar();
//output: foo
You could also use this to emulate mixins. By storing mixin classes inside a variable in the class, you could use __call to check each of them for a method that's not in the main class.
If you implement __call, any call that attempts to use a method which does not exist will go into it. You should always throw an exception in the end of __call if the method was not handled. This will help prevent bugs that can occur, if you mistype a method name and it goes into call which could silently ignore it.
If you, for some reason, want to create a method with a PHP reserved word as its name, you can fake one using __call. For example, normally you can't have a method called "function" or "class", but with __call it's possible.
__get and __set
class SomeClass {
public function __get($name) {
}
public function __set($name, $value) {
}
}
The __get and __set pair is called when attempting to read or write inaccessible variables, for example:
$obj = new SomeClass();
$obj->badVar = 'hello';
//__set is called with 'badVar' as first parameter and 'hello' as second parameter
echo $obj->otherBadVar;
//__get is called with 'otherBadVar' as the first parameter
__get works similar to __call, in that you can return a value from it. If you don't return a value from __get, the value is assumed to be null.
These two can be used to, for example, creating read-only variables, or C#-style properties which run some code when being read or written.
class PropertyClass {
private $_foo;
public function setFoo($value) {
//some code here
$this->_foo = $value;
}
public function getFoo() {
return $foo;
}
public function __set($name, $value) {
$setter = 'set' . ucfirst($name);
$this->$setter($value);
}
public function __get($name) {
$getter = 'get' . ucfirst($name);
return $this->$getter();
}
}
$obj = new PropertyClass();
//Since foo does not exist, __set is called, and
//set then calls setFoo which can run additional code
$obj->foo = 'something';
Like __call, it's important that you throw an exception if you don't handle a variable in __get or __set. Again, this will help prevent bugs that are caused by misspelled variable names.
__unset and __isset
class SomeClass {
public function __isset($name) {
}
public function __unset($name) {
}
}
If you want to use __get and __set, it's often useful to also implement __unset and __isset. They are called with unset and isset respectively:
$obj = new SomeClass();
isset($obj->someVar);
//calls __isset with 'someVar'
unset($obj->otherVar);
//calls __unset with 'otherVar'
Words of warning about magic methods
While it's possible to do fun things with magic methods, like making setting variables actually call methods, and even completely nonsensical things like making unset ($obj->foo) echo the value of $foo, keep in mind that while they are useful, it's easy to go overboard and make something that's more difficult to maintain because it's confusing.
Magic functions
This topic should probably be just called "magic function", since at the moment there is only one.
__autoload
You can define a function called __autoload to implement a default autoloader. Normally you would need to call spl_autoload_register to register any autoloader functions or methods, but not with __autoload.
function __autoload($class) {
require_once $class . '.php';
}
$obj = new SomeClass();
//assuming SomeClass isn't defined anywhere in the code,
//the __autoload function is now called with 'SomeClass' as its parameter
If you have more complex autoloading logic, you may want to just implement it as a class, similar to Zend_Loader in the Zend Framework, and then register the autoloader method manually with spl_autoload_register or a special method of the class.
Magic constants
Magic constants are special predefined constants in PHP. Unlike other predefined constants, magic constants have a different value depending on where you use them. All of them use a similar naming style: __NAME__ - that is, two underscores, name in upper case, and then two more underscores.
Here are all the magic constants:
<LINK href="file:///C:\Users\test88\AppData\Local\Temp\msohtmlclip1\01\clip_filelist.xml" rel=File-List><LINK href="file:///C:\Users\test88\AppData\Local\Temp\msohtmlclip1\01\clip_themedata.thmx" rel=themeData><LINK href="file:///C:\Users\test88\AppData\Local\Temp\msohtmlclip1\01\clip_colorschememapping.xml" rel=colorSchemeMapping> <STYLE> </STYLE>
Constant |
Description |
Example value |
__FILE__ |
The currently executing file |
/var/www/index.php |
__LINE__ |
The current line |
10 |
__FUNCTION__ |
The current function |
myFunction |
__CLASS__ |
The current class |
MyClass |
__METHOD__ |
The current method |
MyClass::myMethod |
__COMPILER_HALT_OFFSET__ |
The byte offset in the file, where __halt_compiler() was called |
10 |
You can use __FUNCTION__ inside class methods. In this case, it will return just the method's name, unlike __METHOD__, which will always return the name with class name prefixed.
These constants are mainly useful for using as details in error message, and to assist in debugging. However, there are a few other uses as well.
Here's an example of throwing an exception with magic constants in the message: throw new Exception('Error in file ' . __FILE__. 'on line ' . __LINE__);
It's worth noting that exception backtraces already come with the file and line.
__FILE__ can be used to get the current script's directory:
dirname(__FILE__);
With __CLASS__, it's possible to determine a parent class' name:
class Parent {
public function someMethod() {
echo __CLASS__;
}
}
class Child extends Parent {
}
$obj = new Child();
$obj->someMethod();
//Will output: Parent
Finally, the more special __COMPILER_HALT_OFFSET__ can only be used when a file contains a call to __halt_compiler(). This is a special PHP function, which will end the compiling of the file at the point where the function is called. Any data after this will be ignored. You could use this, for example, to store some additional data. You can then read this data by reading the current file, and looking at __COMPILER_HALT_OFFSET__. The usage of this approach is outside the scope of this article, so check the PHP manual for more details.
PHP 5.3 magic features
Lastly, let us check out what new magic methods and constants PHP 5.3 brings to the table!
New magic methods in PHP 5.3
PHP 5.3 adds two new magic methods: __invoke and __callStatic.
class NewMethodsClass {
public static function __callStatic($method, $parameters) {
}
public function __invoke($parameters) {
}
}
__callStatic is the same as __call, except it's used in static context:
NewMethodsClass::someStaticMethod('Hi');
//calls __callStatic with 'someStaticMethod' and array('Hi')
As with __call, remember to throw an exception if you don't handle some method in __callStatic, so that you prevent any bugs that are caused by mistyped method names.
__invoke is a more interesting: It gets called when you use a class instance like a function
$obj = new NewMethodsClass();
$obj('Hi');
//calls __invoke with 'Hi'
This could be useful for implementing the command pattern, or to create something similar as the Runnable or Callable interfaces in Java.
New magic constants in PHP 5.3
PHP 5.3 adds two new magic constants: __DIR__ and __NAMESPACE__
__DIR__ is the same as calling dirname(__FILE__)
__NAMESPACE__ contains the current namespace, which is a new feature of PHP 5.3.
New magic functions in PHP 5.3
PHP 5.3 does not add any new magic functions.
Summary
PHP contains magic methods, magic constants and a magic function, which are special features of the PHP language and can be used for various purposes. They are some of the features in PHP that makes it stand out from the crowd, as not many others provide similar capabilities, and as such, any serious PHP programmer should know them.