|
 |
Вызовы методов, как и обращения к свойствам объекта, могут
быть перегружены с использованием методов __call, __get и __set.
Эти методы будут срабатывать только в том случае, если объект
или наследуемый объект не содержат свойства или метода,
к которому осуществляется доступ.
void __set ( string имя, mixed значение ) void __get ( mixed имя )
С помощью этих методов обращения к свойствам класса могут быть перегружены
с целью выполнения произвольного кода, описанного в классе.
В аргументе имя передаётся имя свойства, к которому
производится обращение. Аргумент значение метода __set()
должен содержать значение, которое будет присвоено свойству класса
с именем имя.
Пример 19-19. Пример перегрузки с использование __get и __set
<?php
class Setter {
public $n;
private $x = array("a" => 1, "b" => 2, "c" => 3);
function __get($nm) {
print "ЧИтаем [$nm]\n";
if (isset($this->x[$nm])) {
$r = $this->x[$nm];
print "Получили: $r\n";
return $r;
} else {
print "Ничего!\n";
}
}
function __set($nm, $val) {
print "Пишем $val в [$nm]\n";
if (isset($this->x[$nm])) {
$this->x[$nm] = $val;
print "OK!\n";
} else {
print "Всё плохо!\n";
}
}
}
$foo = new Setter();
$foo->n = 1;
$foo->a = 100;
$foo->a++;
$foo->z++;
var_dump($foo);
?>
|
Результатом выполнения будет:
Пишем 100 в [a]
OK!
Читаем [a]
Получили: 100
Пишем 101 в [a]
OK!
Читаем [z]
Ничего!
Пишем 1 в [z]
Всё плохо!
object(Setter)#1 (2) {
["n"]=>
int(1)
["x:private"]=>
array(3) {
["a"]=>
int(101)
["b"]=>
int(2)
["c"]=>
int(3)
}
}
|
|
mixed __call ( string имя, array аргументы )
С использованием этого метода, методы класса могут быть перегружены
с целью выполнения произвольного кода, описанного в классе.
В аргументе имя передаётся имя вызванного
метода. Аргументы, которые были переданы методу при обращении,
будут возвращены чере аргументы.
Значение, возвращённое методом __call(), будет передано вызывающему оператору.
Пример 19-20. Пример перегрузки с использованием __call
<?php
class Caller {
private $x = array(1, 2, 3);
function __call($m, $a) {
print "Вызван метод $m :\n";
var_dump($a);
return $this->x;
}
}
$foo = new Caller();
$a = $foo->test(1, "2", 3.4, true);
var_dump($a);
?>
|
Результатом выполнения будет:
Вызван метод test:
array(4) {
[0]=>
int(1)
[1]=>
string(1) "2"
[2]=>
float(3.4)
[3]=>
bool(true)
}
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
|
|
Перегрузка
mnaul at nonsences dot angelo dot edu
11-Jul-2006 12:58
This is just my contribution. It based off of many diffrent suggestions I've see thought the manual postings.
It should fit into any class and create default get and set methods for all you member variables. Hopfuly its usefull.
<?php
public function __call($name,$params)
{
if( preg_match('/(set|get)(_)?/',$name) )
{
if(substr($name,0,3)=="set")
{
$name = preg_replace('/set(_)?/','',$name);
if(property_exists(__class__,$name))
{
$this->{$name}=array_pop($params);
return true;
}
else
{
return false;
}
return true;
}
elseif(substr($name,0,3)=="get")
{
$name = preg_replace('/get(_)?/','',$name);
if(property_exists(__class__,$name) )
{
return $this->{$name};
}
else
{
return false;
}
}
else
{
return false;
}
}
else
{
die("method $name dose not exist\n");
}
return false;
}
me at brenthagany dot com
12-Apr-2006 10:52
Regarding the post by TJ earlier, about the problems extending DOMElement. Yes, it is true that you can't set extDOMElement::ownerDocument directly; however, you could append the extDOMElement to a DOMDocument in __construct(), which indirectly sets ownerDocument. It should work something like so:
class extDOMElement extends DOMElement {
public function __construct(DOMDocument $doc) {
$doc->appendChild($this); //extDOMElement::ownerDocument is now equal to the object that $doc points to
}
}
Now, I admit I've never actually needed to do this, but I see no reason why it shouldn't work.
Sleepless
23-Feb-2006 11:22
Yet another way of providing support for read-only properties. Any property that has
"pri_" as a prefix will NOT be returned, period, any other property will be returned
and if it was declared to be "protected" or "private" it will be read-only. (scope dependent of course)
function __get($var){
if (property_exists($this,$var) && (strpos($var,"pri_") !== 0) )
return $this->{$var};
else
//Do something
}
Eric Lafkoff
21-Feb-2006 10:56
If you're wondering how to create read-only properties for your class, the __get() and __set() functions are what you're looking for. You just have to create the framework and code to implement this functionality.
Here's a quick example I've written. This code doesn't take advantage of the "type" attribute in the properties array, but is there for ideas.
<?php
class Test {
private $p_arrPublicProperties = array(
"id" => array("value" => 4,
"type" => "int",
"readonly" => true),
"datetime" => array("value" => "Tue 02/21/2006 20:49:23",
"type" => "string",
"readonly" => true),
"data" => array("value" => "foo",
"type" => "string",
"readonly" => false)
);
private function __get($strProperty) {
if (isset($this->p_arrPublicProperties[$strProperty])) {
return $this->p_arrPublicProperties[$strProperty]["value"];
} else {
throw new Exception("Property not defined");
return false;
}
}
private function __set($strProperty, $varValue) {
if (isset($this->p_arrPublicProperties[$strProperty])) {
if ($this->p_arrPublicProperties[$strProperty]["readonly"]) {
throw new Exception("Property is read-only");
return false;
} else {
$this->p_arrPublicProperties[$strProperty]["value"] = $varValue;
return true;
}
} else {
throw new Exception("Property not defined");
return false;
}
}
private function __isset($strProperty) {
return isset($this->p_arrPublicProperties[$strProperty]);
}
private function __unset($strProperty) {
unset($this->p_arrPublicProperties[$strProperty]);
}
}
$objTest = new Test();
print $objTest->data . "\n";
$objTest->data = "bar"; print $objTest->data;
$objTest->id = 5; ?>
derek-php at seysol dot com
10-Feb-2006 12:08
Please note that PHP5 currently doesn't support __call return-by-reference (see PHP Bug #30959).
Example Code:
<?php
class test {
public function &__call($method, $params) {
return $GLOBALS['var2'];
}
public function &actual() {
return $GLOBALS['var1'];
}
}
$obj = new test;
$GLOBALS['var1'] = 0;
$GLOBALS['var2'] = 0;
$ref1 =& $obj->actual();
$GLOBALS['var1']++;
echo "Actual function returns: $ref1 which should be equal to " . $GLOBALS['var1'] . "<br/>\n";
$ref2 =& $obj->overloaded();
$GLOBALS['var2']++;
echo "Overloaded function returns: $ref2 which should be equal to " . $GLOBALS['var2'] . "<br/>\n";
?>
PHP at jyopp dotKomm
22-Dec-2005 11:01
Here's a useful class for logging function calls. It stores a sequence of calls and arguments which can then be applied to objects later. This can be used to script common sequences of operations, or to make "pluggable" operation sequences in header files that can be replayed on objects later.
If it is instantiated with an object to shadow, it behaves as a mediator and executes the calls on this object as they come in, passing back the values from the execution.
This is a very general implementation; it should be changed if error codes or exceptions need to be handled during the Replay process.
<?php
class MethodCallLog {
private $callLog = array();
private $object;
public function __construct($object = null) {
$this->object = $object;
}
public function __call($m, $a) {
$this->callLog[] = array($m, $a);
if ($this->object) return call_user_func_array(array(&$this->object,$m),$a);
return true;
}
public function Replay(&$object) {
foreach ($this->callLog as $c) {
call_user_func_array(array(&$object,$c[0]), $c[1]);
}
}
public function GetEntries() {
$rVal = array();
foreach ($this->callLog as $c) {
$rVal[] = "$c[0](".implode(', ', $c[1]).");";
}
return $rVal;
}
public function Clear() {
$this->callLog = array();
}
}
$log = new MethodCallLog();
$log->Method1();
$log->Method2("Value");
$log->Method1($a, $b, $c);
foreach ($array as $o) $log->Replay($o);
?>
trash80 at gmail dot com
03-Dec-2005 08:59
Problem: $a->b->c(); when b is not instantiated.
Answer: __get()
<?php
class a
{
function __get($v)
{
$this->$v = new $v;
return $this->$v;
}
}
class b
{
function say($word){
echo $word;
}
}
$a = new a();
$a->b->say('hello world');
?>
d11wtq
11-Nov-2005 01:07
Regarding the code posted by this user:
mileskeaton at gmail dot com
23-Dec-2004 03:23
There's no need to loop over your entire object. I do this lots and all you need to do is convert the method name used into the name of a property, then check
if (isset($this->{$property_name}))
{
//Get ready to do a set
}
I usually check if the developer called a get or set and also do some snity checking on the type of property they're requesting.
TJ <php at tjworld dot net>
03-Nov-2005 10:11
Using the getter/setter methods to provide read-only access to object properties breaks the conventional understanding of inheritence.
A super class using __set() to make a property read-only cannot be properly inherited because the visible (read-only) property - with conventional public or protected visibility - cannot be set in the sub-class.
The sub-class cannot overload the super class's __set() method either, and therefore the inheritence is severely compromised.
I discovered this issue while extending DOMDocument and particularly DOMElement.
When extDOMDocument->createElement() creates a new extDOMElement, extDOMElement->__construct() can't set the extDOMElement->ownerDocument property because it's read-only.
DOMElements are totally read-only if they do not have an ownerDocument, and there's no way to set it in this scenario, which makes inheritence pretty pointless.
seufert at gmail dot com
01-Nov-2005 04:25
This allows you to seeminly dynamically overload objects using plugins.
<PRE>
<?php
class standardModule{}
class standardPlugModule extends standardModule
{
static $plugptrs;
public $var;
static function plugAdd($name, $mode, $ptr)
{
standardPlugModule::$plugptrs[$name] = $ptr;
}
function __call($fname, $fargs)
{ print "You called __call($fname)\n";
$func = standardPlugModule::$plugptrs[$fname];
$r = call_user_func_array($func, array_merge(array($this),$fargs));
print "Done: __call($fname)\n";
return $r;
}
function dumpplugptrs() {var_dump(standardPlugModule::$plugptrs); }
}
class a extends standardPlugModule
{ function text() { return "Text"; } }
class p
{ static function plugin1($mthis, $r)
{ print "You called p::plugin1\n";
print_r($mthis);
print_r($r);
}
} a::plugAdd('callme', 0, array('p','plugin1'));
class p2
{ static function plugin2($mthis, $r)
{ print "You called p2::plugin2\n";
$mthis->callme($r);
}
} a::plugAdd('callme2', 0, array('p2','plugin2'));
$t = new a();
$testr = array(1,4,9,16);
print $t->text()."\n";
$t->callme2($testr);
?>
</pre>
Will result in:
----------
Text
You called __call(callme2)
You called p2::plugin2
You called __call(callme)
You called p::plugin1
a Object
(
[var] =>
)
Array
(
[0] => 1
[1] => 4
[2] => 9
[3] => 16
)
Done: __call(callme)
Done: __call(callme2)
----------
This also clears up a fact that you can nest __call() functions, you could use this to get around the limits to __get() not being able to be called recursively.
divedeep at yandex dot ru
24-Oct-2005 11:14
Small examle showing how to work with magic method __call and parameters, that u want to pass as a reference.
DOES NOT WORK:
<?php
class a {
public function __call($m,&$p) {
$p[0]=2;
}
}
$a= new a();
$b=1;
$a->foo($b);
echo $b;
?>
WORKS:
<?php
class a {
public function __call($m,$p) {
$p[0]=2;
}
}
$a= new a();
$b=1;
$a->foo(&$b);
echo $b;
?>
26-Aug-2005 07:32
To those who wish for "real" overloading: there's not really any advantage to using __call() for this -- it's easy enough with func_get_args(). For example:
<?php
class Test
{
public function Blah()
{
$args = func_get_args();
switch (count($args))
{
case 1: break;
case 2: break;
}
}
}
?>
iv
26-Aug-2005 03:01
To "NOTE: getter cannot call getter"
When i changed
'using_getter' => 10 * $this->a
to
'using_getter' => 10 * $this->_a
the result became 60, how it should be
yuggoth23 at yahoo dot no dot spam dot com
08-Aug-2005 06:00
I just wanted to share with the rest of you since I had not seen it posted yet that __call does not work as a static method. Consider a utility class that has all static methods on it where one or more of those methods are overloaded. The snippet below will not work until you remove the "static" keyword from the __call function declaration:
class MyClass
{
public static _call($method, $args)
{
switch($method)
{
case "Load":
if(count($args) == 1)
$this->funcLoad1($args[0]);
elseif(count($args) == 2)
$this->funcLoad2($args[0],$args[1]);
}
}
private static funcLoad1($userID)
{
//do something
}
private static funcLoad2($userID, $isActive)
{
//do something slightly different
}
}
This means any class that wishes to "overload" methods in PHP must be instantiatable in some regard. I like to use a pattern whereby one of the participants is a utility class exposing only static functions which operates on data exposed from an "info" or state-bag class. This is still possible in PHP, I suppose. It just means I'll need to incur overhead to instantiate my utility classes as well.
You CAN, by the way, call one static method from another using $this as I've done in the switch according to a "note" section on this page.
http://us3.php.net/language.oop
Again that may pre-suppose some level of instantiation for the class.
NOTE: getter cannot call getter
04-Aug-2005 12:37
By Design (http://bugs.php.net/bug.php?id=33998) you cannot call a getter from a getter or any function triggered by a getter:
class test
{
protected $_a = 6;
function __get($key) {
if($key == 'stuff') {
return $this->stuff();
} else if($key == 'a') {
return $this->_a;
}
}
function stuff()
{
return array('random' => 'key', 'using_getter' => 10 * $this->a);
}
}
$test = new test();
print 'this should be 60: '.$test->stuff['using_getter'].'<br/>'; // prints "this should be 60: 0"
// [[ Undefined property: test::$a ]] on /var/www/html/test.php logged.
print 'this should be 6: '.$test->a.'<br/>'; // prints "this should be 6: 6"
06-May-2005 03:50
Please note that PHP5's overloading behaviour is not compatible at all with PHP4's overloading behaviour.
Marius
02-May-2005 02:15
for anyone who's thinking about traversing some variable tree
by using __get() and __set(). i tried to do this and found one
problem: you can handle couple of __get() in a row by returning
an object which can handle consequential __get(), but you can't
handle __get() and __set() that way.
i.e. if you want to:
<?php
print($obj->val1->val2->val3); ?> - this will work,
but if you want to:
<?php
$obj->val1->val2 = $val; ?> - this will fail with message:
"Fatal error: Cannot access undefined property for object with
overloaded property access"
however if you don't mix __get() and __set() in one expression,
it will work:
<?php
$obj->val1 = $val; $val2 = $obj->val1->val2; $val2->val3 = $val; ?>
as you can see you can split __get() and __set() parts of
expression into two expressions to make it work.
by the way, this seems like a bug to me, will have to report it.
ryo at shadowlair dot info
22-Mar-2005 10:22
Keep in mind that when your class has a __call() function, it will be used when PHP calls some other magic functions. This can lead to unexpected errors:
<?php
class TestClass {
public $someVar;
public function __call($name, $args) {
trigger_error(sprintf('Call to undefined function: %s::%s().', get_class($this), $name), E_USER_ERROR);
}
}
$obj = new TestClass();
$obj->someVar = 'some value';
echo $obj; $serializedObj = serialize($obj); $unserializedObj = unserialize($someSerializedTestClassObject); ?>
thisisroot at gmail dot com
18-Feb-2005 07:27
You can't mix offsetSet() of the ArrayAccess interface (~helly/php/ext/spl/interfaceArrayAccess.html) and __get() in the same line.
Below, "FileManagerPrefs" is an object of class UserData which implements ArrayAccess. There's a protected array of UserData objects in the User class, which are returned from __get().
<?php
Application::getInstance()->user->FileManagerPrefs[ 'base'] = 'uploads/jack';
?>
Creates this error:
Fatal error: Cannot access undefined property for object with overloaded property access in __FILE__ on line __LINE__
However, __get() and offsetGet() play deceptively well together.
<?php
echo Application::getInstance()->user->FileManager['base'];
?>
I guess it's a dereferencing issue with __get(). In my case, it makes more sense to have a middle step (user->data['FileManager']['base']), but I wanted to tip off the community before I move on.
mileskeaton at gmail dot com
23-Dec-2004 12:23
<?php
class Person
{
private $name;
private $age;
private $weight;
function __construct($name, $age, $weight)
{
$this->name = $name;
$this->age = $age;
$this->weight = $weight;
}
function __call($val, $x)
{
if(substr($val, 0, 4) == 'get_')
{
$varname = substr($val, 4);
}
elseif(substr($val, 0, 3) == 'get')
{
$varname = substr($val, 3);
}
else
{
die("method $val does not exist\n");
}
foreach($this as $class_var=>$class_var_value)
{
if(strtolower($class_var) == strtolower($varname))
{
return $class_var_value;
}
}
return false;
}
function getWeight()
{
return intval($this->weight * .8);
}
}
$a = new Person('Miles', 35, 200);
print $a->get_name() . "\n";
print $a->getName() . "\n";
print $a->get_Name() . "\n";
print $a->getname() . "\n";
print $a->get_age() . "\n";
print $a->getAge() . "\n";
print $a->getage() . "\n";
print $a->get_Age() . "\n";
print $a->getWeight() . "\n";
print $a->getNothing();
print $a->hotdog();
?>
richard dot quadling at bandvulc dot co dot uk
26-Nov-2004 05:54
<?php
abstract class BubbleMethod
{
public $objOwner;
function __call($sMethod, $aParams)
{
if (isset($this->objOwner))
{
return call_user_func_array(array($this->objOwner, $sMethod), $aParams);
}
else
{
echo 'Owner for ' . get_class($this) . ' not assigned.';
}
}
}
class A_WebPageContainer
{
private $sName;
public function __construct($sName)
{
$this->sName = $sName;
}
public function GetWebPageContainerName()
{
return $this->sName;
}
}
class A_WebFrame extends BubbleMethod
{
private $sName;
public function __construct($sName)
{
$this->sName = $sName;
}
public function GetWebFrameName()
{
return $this->sName;
}
}
class A_WebDocument extends BubbleMethod
{
private $sName;
public function __construct($sName)
{
$this->sName = $sName;
}
public function GetWebDocumentName()
{
return $this->sName;
}
}
class A_WebForm extends BubbleMethod
{
private $sName;
public function __construct($sName)
{
$this->sName = $sName;
}
public function GetWebFormName()
{
return $this->sName;
}
}
class A_WebFormElement extends BubbleMethod
{
private $sName;
public function __construct($sName)
{
$this->sName = $sName;
}
public function GetWebFormElementName()
{
return $this->sName;
}
}
$objWPC = new A_WebPageContainer('The outer web page container.');
$objWF1 = new A_WebFrame('Frame 1');
$objWF1->objOwner = $objWPC;
$objWF2 = new A_WebFrame('Frame 2');
$objWF2->objOwner = $objWPC;
$objWD1 = new A_WebDocument('Doc 1');
$objWD1->objOwner = $objWF1;
$objWD2 = new A_WebDocument('Doc 2');
$objWD2->objOwner = $objWF2;
$objWFrm1 = new A_WebForm('Form 1');
$objWFrm1->objOwner = $objWD1;
$objWFrm2 = new A_WebForm('Form 2');
$objWFrm2->objOwner = $objWD2;
$objWE1 = new A_WebFormElement('Element 1');
$objWE1->objOwner = $objWFrm1;
$objWE2 = new A_WebFormElement('Element 2');
$objWE2->objOwner = $objWFrm1;
$objWE3 = new A_WebFormElement('Element 3');
$objWE3->objOwner = $objWFrm2;
$objWE4 = new A_WebFormElement('Element 4');
$objWE4->objOwner = $objWFrm2;
echo "The name of the form that '" . $objWE1->GetWebFormElementName() . "' is in is '" . $objWE1->GetWebFormName() . "'<br />";
echo "The name of the document that '" . $objWE2->GetWebFormElementName() . "' is in is '" . $objWE2->GetWebDocumentName(). "'<br />";
echo "The name of the frame that '" . $objWE3->GetWebFormElementName() . "' is in is '" . $objWE3->GetWebFrameName(). "'<br />";
echo "The name of the page container that '" . $objWE4->GetWebFormElementName() . "' is in is '" .$objWE4->GetWebPageContainerName(). "'<br />";
?>
Results in.
The name of the form that 'Element 1' is in is 'Form 1'
The name of the document that 'Element 2' is in is 'Doc 1'
The name of the frame that 'Element 3' is in is 'Frame 2'
The name of the page container that 'Element 4' is in is 'The outer web page container.'
By using the abstract BubbleMethod class as the starting point for further classes that are contained inside others (i.e. elements on a form are contained in forms, which are contained in documents which are contained in frames which are contained in a super wonder global container), you can find properties of owner without knowing their direct name.
Some work needs to be done on what to do if no method exists though.
bigtree at DONTSPAM dot 29a dot nl
17-Nov-2004 01:47
Keep in mind that the __call method will not be called if the method you are calling actually exists, even if it exists in a parent class. Check the following example:
<?php
class BaseClass
{
function realMethod()
{
echo "realMethod has been called\n";
}
}
class ExtendClass extends BaseClass
{
function __call($m, $a)
{
echo "virtual method " . $m . " has been called\n";
}
}
$tmpObject = new ExtendClass();
$tmpObject->virtualMethod();
$tmpObject->realMethod();
?>
Will output:
virtual method virtualMethod has been called
realMethod has been called
You might expect that all method calls on ExtendClass will be handled by __call because it has no other methods, but this is not the case, as the example above demonstrates.
xorith at gmail dot com
06-Oct-2004 07:40
A few things I've found about __get()...
First off, if you use $obj->getOne->getAnother, both intended to be resolved by __get, the __get() function only sees the first one at first. You can't access the second one. You can, however, return the pointer to an object that can handle the second one. In short, you can have the same class handle both by returning a new object with the data changed however you see fit.
Secondly, when using arrays like: $obj->getArray["one"], only the array name is passed on to __get. However, when you return the array, PHP treats it just as it should. THat is, you'd have to make an array with the index of "one" in __get in order to see any results. You can also have other indexes in there as well.
Also, for those of you like me, I've already tried to use func_get_args to see if you can get more than just that one.
If you're like me and were hoping you could pass some sort of argument onto __get in order to help gather the correct data, you're out of look. I do recommend using __call though. You could easily rig __call up to react to certain things, like: $account->properties( "type" );, which is my example. I'm using DOM for data storage (for now), and I'm trying to make an interface that'll let me easily switch to something else - MySQL, flat file, anything. This would work great though!
Hope I've been helpful and I hope I didn't restate something already stated.
Dot_Whut?
30-Sep-2004 06:52
Sharp readers (i.e. C++, Java programmers, etc.) may have noticed that the topic of "Overloading" mentioned in the documentation above really isn't about overloading at all... Or, at least not in the Object Oriented Programming (OOP) sense of the word.
For those who don't already know, "overloading" a method (a.k.a. function) is the ability for functions of the same name to be defined within a Class as long as each of these methods have a different set of parameters (each method would have a different "signature", if you will). At run-time, the script engine could then select and execute the appropriate function depending on which type or number of arguments were passed to it. Unfortunately, PHP 5.0.2 doesn't support this sort of overloading. The "Overloading" mentioned above could be better described as "Dynamic Methods and Properties"...
By using "Dynamic Methods and Properties", we can make our class instances "appear" to have methods and properties that were not originally present in our Class definitions. Although this is a great new feature (which I plan to use often), through the use of "__get", "__set", and "__call", we can also emulate "real" overloading as shown in a few of the code examples below.
Dot_Whut?
29-Sep-2004 06:14
You can create automatic getter and setter methods for your private and protected variables by creating an AutoGetterSetter Abstract Class and having your Class inherit from it:
// Automatic Getter and Setter Class
//
// Note: when implementing your getter and setter methods, name your
// methods the same as your desired object properties, but prefix the
// function names with 'get_' and 'set_' respectively.
abstract class AutoGetAndSet {
public function __get ($name) {
try {
// Check for getter method (throws an exception if none exists)
if (!method_exists($this, "get_$name"))
throw new Exception("$name has no Getter method.");
eval("\$temp = \$this->get_$name();");
return $temp;
}
catch (Exception $e) {
throw $e;
}
}
public function __set ($name, $value) {
try {
// Check for setter method (throws an exception if none exists)
if (!method_exists($this, "set_$name"))
throw new Exception("$name has no Setter method (is read-only).");
eval("\$this->set_$name(\$value);");
}
catch (Exception $e) {
throw $e;
}
}
}
class MyClass extends AutoGetAndSet
{
// private and protected members
private $mbr_privateVar = "privateVar";
protected $mbr_protectedVar = "protectedVar";
// 'privateVar' property, setter only (read-only property)
function get_privateVar () {
return $this->mbr_privateVar;
}
// 'protectedVar' property, both getter AND setter
function get_protectedVar () {
return $this->mbr_protectedVar;
}
function set_protectedVar ($value) {
$this->mbr_protectedVar = $value;
}
}
$myClass = new MyClass();
// Show original values, automatically uses 'get_' methods
echo '$myClass->protectedVar == ' . $myClass->protectedVar . '<br />';
echo '$myClass->privateVar == ' . $myClass->privateVar . '<br />';
// Modify 'protectedVar', automatically calls set_protectedVar()
$myClass->protectedVar = "Modded";
// Show modified value, automatically calls get_protectedVar()
echo '$myClass->protectedVar == ' . $myClass->protectedVar . '<br />';
// Attempt to modify 'privateVar', get_privateVar() doesn't exist
$myClass->privateVar = "Modded"; // throws an Exception
// Code never gets here
echo '$myClass->privateVar == ' . $myClass->privateVar . '<br />';
anthony at ectrolinux dot com
25-Sep-2004 06:40
For anyone who is interested in overloading a class method based on the number of arguments, here is a simplified example of how it can be accomplished:
<?php
function Error($message) { trigger_error($message, E_USER_ERROR); exit(1); }
class Framework
{
public function __call($func_name, $argv)
{
$argc = sizeof($argv);
switch ($func_name) {
case 'is_available':
$func_name = ($argc == 2) ? 'is_available_single' : 'is_available_multi';
break;
default: Error('Call to undefined function Framework::'. $func_name .'().');
}
return call_user_func_array(array(&$this, $func_name), $argv);
}
protected function is_available_multi()
{
if (($argc = func_num_args()) < 2) {
Error('A minimum of two arguments are required for Framework::is_available().');
}
$available_addons = array();
return $available_addons;
}
protected function is_available_single($mod_name, $mod_addon)
{
return true;
}
}
$fw = new Framework;
$test_one = $fw->is_available('A_Module_Name', 'An_Addon');
var_dump($test_one);
$test_two = $fw->is_available('A_Module_Name', 'Addon_0', 'Addon_1', 'Addon_2');
var_dump($test_two);
?>
---
By adding additional case statements to Framework::__call(), methods can easily be overloaded as needed. It's also possible to add any other overloading criteria you require inside the switch statement, allowing for more intricate overloading functionality.
DevilDude at darkmaker dot com
22-Sep-2004 07:57
Php 5 has a simple recursion system that stops you from using overloading within an overloading function, this means you cannot get an overloaded variable within the __get method, or within any functions/methods called by the _get method, you can however call __get manualy within itself to do the same thing.
31-Aug-2004 11:39
For those using isset():
Currently (php5.0.1) isset will not check the __get method to see if you can retrieve a value from that function. So if you want to check if an overloaded value is set you'd need to use something like the __isset method below:
<?php
class foo
{
public $aryvars = array();
function __construct() {}
function __get($key)
{
return array_key_exists($key, $this->aryvars) ?
$this->aryvars[$key] : null;
}
function __isset($key) {
if (!$isset = isset($this->$key)) {
$isset = array_key_exists($key, $this->aryvars);
}
return $isset;
}
function __set($key, $value)
{
$this->aryvars[$key] = $value;
}
}
echo '<pre>';
$foo = new foo();
$foo->a = 'test';
echo '$foo->a == ' . $foo->a . "\n";
echo 'isset($foo->a) == ' . (int) isset($foo->a) . "\n";
echo 'isset($foo->aryvars[\'a\']) == ' . isset($foo->aryvars['a']) . "\n";
echo '$foo->__isset(\'a\') == ' . $foo->__isset('a') . "\n";
var_dump($foo);
?>
| |