|
|
 |
| Caution |
The following is valid for PHP 4 and later only.
|
Sometimes it is useful to refer to functions and variables
in base classes or to refer to functions in classes that
have not yet any instances. The :: operator is being used
for this.
The above example calls the function example() in
class A, but there is no object of class A, so that
we cannot write $a->example() or similar. Instead we
call example() as a 'class function', that is, as a
function of the class itself, not any object of that
class.
There are class functions, but there are no class variables.
In fact, there is no object at all at the time of the call.
Thus, a class function may not use any object variables (but
it can use local and global variables), and it may no use
$this at all.
In the above example, class B redefines the function example().
The original definition in class A is shadowed
and no longer available, unless you are referring specifically
to the implementation of example() in class A using the
::-operator. Write A::example() to do this (in fact, you
should be writing parent::example(), as shown in the next
section).
In this context, there is a current object and it may have object
variables. Thus, when used from WITHIN an object function, you may use
$this and object variables.
User Contributed Notes
Scope Resolution Operator (::)
RichardBronosky (firstname at lastname dot com)
09-May-2005 04:33
In reply to tim dot ward at stivesdirect dot com:
I had a need to to do a very similar thing. I wrote a class that will be used to create an object which can serialize itself into a cookie and be used to recover itself from the cookie later. To do the recovery you must use the Scope Resolution Operator (::) to call a function as a class function (vs. and object function). The recovery is very simple to initiate because there is only one line of code to call (e.g. $the_object = the_class::the_function(); ) But the function you call must do a ton of stuff, and call a few other functions within the class. This is where the problem lies.
I would like to thank <brooke at jump dot net>, <pollita at php dot net>, and <MagicalTux at FF.ST> (from http://php.net/manual/en/function.get-class.php) for leading me to the solution.
<?php
class foo
{
function get_object_from_cookie(...)
{
...
return unserialize($decrypted_cookie_data);
}
function recover_from_cookie()
{
...prepare to recover...
$o = call_user_func(array(__CLASS__, 'get_object_from_cookie'), $encryption_secret, $cookie_name);
$temp = $o->get_stuff();
$o->other_stuff = $o->do_calculate($temp);
$o->do_complicated_things();
return $o;
}
}
$bar = foo::recover_from_cookie();
...
?>
I hope that saves someone an hour. It sure would have saved me one.
mark at branly dot com
07-Mar-2005 04:53
I didn't see where this issue was addressed so in regards to this post:
\\-------\\//-------//
when using "::" operator inside class functions, you can achieve quite interesting results. let's take this example:
class cCat {
function Miew(){
// cCat does not have a member "kind", but cDog has, and we'll use it
echo "I am ".$this->kind.", and I say MIEW\n";
// here things are even stranger: does cCat class
// support WhoAmI function? guess again...
$this->WhoAmI();
}
}
class cDog {
var $kind = "DOG";
function Bark(){
// let's make this dog act like a cat:)
cCat::Miew();
}
function WhoAmI(){
echo "Yes, I'm really ".$this->kind."!";
}
}
$dog = new cDog();
echo $dog->Bark();
outputs:
I am DOG, and I say MIEW
Yes, I'm really DOG!
The interesting thing here is that cDog is not descendant of cCat nor vice versa, but cCat was able to use cDog member variable and function. When calling cCat::Miew() function, your $this variable is passed to that function, remaining cDog instance!
It looks like PHP doesn't check if some class is an ancestor of the class, calling function via '::'.
//------//\\--------\\
The problem here is not that PHP is not checking ancestry. The problem is that "$this" refers to the calling object $dog even though it is referenced inside the cCat class definition. Since a cCat is never instantiated, $this has not been scoped to it.
To further illustrate the point, if you never instatiate dog (or any object for that matter), $this will never be set and when you statically call the miew function, you will get the following output:
"I am , and I say MIEW"
This flexibility of member handling, PHP does not afford to methods and as such the second line of the miew function will generate a fatal error reporting that the method has been called from a non object.
If you did want to take it one step further in the whole ancestry guessing issue, try instatiating cCat and calling the miew function. You will get the same result as above *EXCEPT* the fatal error will inform you the function has not been defined.
So, there you have it--PHP DOES NOT guess at inheritance.
mark@thedarkside
24-Feb-2005 05:04
Carrying on from the previous note, it is possible to call a member function statically using a variable for the name of the class, i.e.:
<?php
class MyObject {
function myfunc() {
return "Hello! ($this)"; }
}
$type = "MyObject";
$object = new $type();
$message = $object->myfunc();
$message = MyObject->myfunc();
$message = call_user_func(array(&$type, "myfunc"));
O.K., so it's not using the scope resolution operator, but it does what you want... it calls 'myfunc' statically from the class named by $type.
Tom Wiltshire
24-Feb-2005 06:33
It's worth noting that you can't use a variable with this operator.
The following is legal:
class MyObject {
function myfunc() {
return "Hello!";
}
}
$type = "MyObject";
$object = new $type();
$message = $object->myfunc();
(Puts "hello" in $message)
You can also use the scope operator:
$message = MyObject::myfunc();
However, the following is NOT legal:
$message = $type::myfunc();
It's a pity, 'cos right now I've got a situation where it'd be really handy, but that's the way it goes!
stefano at obliquid dot it
07-Jul-2004 07:22
peter has a good point to suggest the use of static function variables instead of static class variables that PHP 4 does not support.
Unfortunately, also the first sentence of the manual is misleading "... refer to functions and variables in base classes or to refer to functions in classes that have not yet any instances. The :: operator is being used for this.".
You cannot use the :: operator on class variables since there are no static class variables. I lost some time trying to refer to class variable with this operator, so I think it may be useful to post a warning.
http://dev.obliquid.com
tango23 at inbox dot lv
06-May-2004 09:20
>> That's how to make class' static variable in PHP4.
Note, that nobody seems to have a way of actually making a global static server-wide object in PHP.
All the static/class var ways above work only within a single PHP page.
If you want to do something like:
class Logger
{
...
function Logger($filepath)
...
}
in some global include file:
$logobj = new Logger("mylogfile");
and then in all of your pages:
$logobj->log("my message");
and there is no way you can make this logobj static (such that all your pages can refer to without making unncecessary copies).
Can't be done with using uninstantiated Logger::log() funcs either, because you are not setting the logfile filepath parameter anywhere persistent.
The only kludge seems to be to use the filepath as a global variable and use Logger:: syntax.
altherac at yahoo dot fr
14-Mar-2004 04:28
Using a class function is a good way to write a Singleton pattern in php 4 using a static variable.
The sample code below shows the pattern in action :
<?
class Singleton
{
var $_info = null;
function Singleton() {
$this->_info = 'Original value';
}
function getInfo() {
return $this->_info;
}
function setInfo($info) {
$this->_info = $info;
}
function &getInstance() {
static $instance = null;
if (is_null($instance)) {
$instance = new Singleton();
}
return $instance;
}
}
$a = & Singleton::getInstance();
$b = & Singleton::getInstance();
$a->setInfo('Hi, my name is A');
echo $b->getInfo();
?>
I have seen some Singleton pattern implementations that were using an external function to instanciate the object, but I don't think it is the best way to implement the pattern.
You may want to force the getInstance() function to be called in a static way with the :: operator by testing if $this is set and raising an error or returning a null value (depending on your design).
Rami Kayyali (rami at bluecubex dot com)
07-Nov-2003 10:20
Regarding the note by "gk at proliberty dot com". No, eval() isn't superior to call_user_func(), eval() has security issues and performance overhead.
To achieve the same results using your example with call_user_func, simply pass $this_class by reference. This is probably because call_user_func is passing a copy of $this_class.
<?php
function init(&$this_class, $current_class, $params= NULL ){
$this_class->classFiles[] = __FILE__;
$parent_class=get_parent_class($current_class);
if( !empty($parent_class)){
call_user_func(array($parent_class,'init'),
&$this_class,$parent_class,$params);
}
} ?>
But please note, call-time pass-by-reference has been deprecared, or at least, PHP 4.3.2 says so.
Conclusion: Minimize using eval as much as possible, many projects had major security issues because they were using eval().
brooke at jump dot net
07-Nov-2003 05:27
Someone mentioned that eval was better than call_user_func because the latter would not result in updates to the objects. I'm not sure this is the right place for such a debate, but if it is, then the right way is:
call_user_func(array(&this, $method), $arg1, $arg2);
You put a reference to (instead of a copy of) this in the array which you pass on.
wikiz at studentas dot lt
15-Jun-2003 07:40
when using "::" operator inside class functions, you can achieve quite interesting results. let's take this example:
class cCat {
function Miew(){
// cCat does not have a member "kind", but cDog has, and we'll use it
echo "I am ".$this->kind.", and I say MIEW\n";
// here things are even stranger: does cCat class
// support WhoAmI function? guess again...
$this->WhoAmI();
}
}
class cDog {
var $kind = "DOG";
function Bark(){
// let's make this dog act like a cat:)
cCat::Miew();
}
function WhoAmI(){
echo "Yes, I'm really ".$this->kind."!";
}
}
$dog = new cDog();
echo $dog->Bark();
outputs:
I am DOG, and I say MIEW
Yes, I'm really DOG!
The interesting thing here is that cDog is not descendant of cCat nor vice versa, but cCat was able to use cDog member variable and function. When calling cCat::Miew() function, your $this variable is passed to that function, remaining cDog instance!
It looks like PHP doesn't check if some class is an ancestor of the class, calling function via '::'.
gk at proliberty dot com
28-May-2003 08:33
Contrary to the comment above, I have found that call_user_func()
is inferior to eval() because call_user_func() does not correctly
update objects passed by reference.
In the example below, each of my subclasses calls init(), passing
a reference to the current object, whose class variables are
initialized by classes in the hierarchy. The class variable
$this->classFiles contains the paths of each file for classes in
the hierarchy. It is not updated correctly using call_user_func()
but eval() works fine.
<?php
function xobj( $params= NULL ){
$current_class=get_class($this);
$this->init($this, $current_class, $params);
$this->classFile=$this->classFiles[0];
print_r($this->classFiles); exit;
$this->_init($params);
}
function init(&$this_class, $current_class, $params= NULL ){
$this_class->classFiles[] = __FILE__;
$parent_class=get_parent_class($current_class);
if( !empty($parent_class)){
eval("$parent_class::init(\$this_class, \$parent_class, \$params);");
$this_class,$parent_class,$params);
}
} ?>
RESULT, using eval():
Array
(
[0] => /usr/local/apache/htdocs/common/php/xobj/xobj_subclass.php
[1] => /usr/local/apache/htdocs/common/php/xobj/xobj.php
)
RESULT, using call_user_func():
Array
(
[0] => /usr/local/apache/htdocs/common/php/xobj/xobj_subclass.php
)
pollita at php dot net
28-Dec-2002 09:47
While the method described by robinv at ecosse dot net to call a method of an arbitrarily named function will work:
eval("$classname::$methodname(\$param1,\$param2);");
There is another way to do so without using eval():
call_user_func(array($classname,$methodname),$param1,$param2);
This will do the same thing without the performance hit of eval and without the added security concerns.
peterjoel.com
17-Nov-2002 10:12
You can implement a getter/setter pair to emulate a class variable. The syntax isn't so weird:
<?
class A{
function getVar($val=NULL){
static $class_variable = 0;
if($val != NULL){
$class_variable = $val;
}
return $class_variable;
}
function setVar($val=0){
return A::getVar($val);
}
}
A::setVar(3);
print A::getVar();
?>
frederik at spam pandora dot be
13-Nov-2002 04:46
My approach to using class constants is by making them functions.
Instead of
$var CONSTANT = 1;
I use
function CONSTANT { return 1; }
Now, when using this:
$i = ConstantClass::CONSTANT();
You can do anything with it: comparing, addition, multiplication. The syntax is a bit weird, but it works perfectly.
pierrick at hydromel_no_spam_ dot net
24-Sep-2002 01:39
It's possible to access class member variables:
class Test {
var $vA = "hello world";
}
$class_var = get_class_vars(Test);
echo $class_var["vA"];
It's sound to be an ellegant way to acces them, no ? ;)
steven at caltech dot co dot uk
28-Aug-2002 08:50
Re: jdominic@prodigy.net 08-Aug-2002 05:49
While it would be nice to have static instance variables you can simply create a method with the same name which returns the value you require. This means you don't have to instantiate an object when you only need its static content. For example:
class Colour {
function default () { return "black"; }
}
var $backgroundColour = Colour::default ();
jdominic at prodigy dot net
08-Aug-2002 02:49
According to the documentation, there is no way to have a class variable, only class functions. There should be class variables, because there's no way to implement CONSTANTS. Something like this won't work:
<?
class Employee {
var $ACTIVE_EMPLOYEE = 1;
}
echo Employee::ACTIVE_EMPLOYEE;
echo Employee::$ACTIVE_EMPLOYEE
?>
returns:
Parse error: parse error, unexpected ';', expecting '(' in /home/x/public_html/Test.php on line 9
It forces you to create an instance of the class:
$emp = new Employee();
echo $emp->ACTIVE_EMPLOYEE;
carl at thep.lu.se
13-Mar-2002 11:20
Note that if you have two classes, 'foo' and 'bar', and a function in foo called with $instance_of_foo->func() calls bar::func2(), $this will be defined in func2, and it will point at an object of class foo. In fact, you can redefine $this to point to an object of any class before calling bar::func2() to have $this to be a reference to that object. This means that you can't have functions that can be called either as static (::) or member(->), since you won't be able to tell the difference. This is a quite unfortunate consequence of the combination of references not containing any type information and it not being possible to call an explicitly chosen base class member function (that is, if x extends y there's no clean way to call y::f() on an object of class x (The unclean way is to assign $this to $instance_of_x, call y::f(), and reset $this to its old value.)).
tim dot ward at stivesdirect dot com
24-Aug-2001 08:34
If you call a function in this way then you need to be aware that internal class references will not work (presumably as there isn't an instance for $this to refer to)
e.g.
>Class fred
>{ function fred()
> { $this->fred2();
> }
> function fred2()
> { echo("hello world");
> }
>}
>fred::fred();
... will cause a
"Fatal error: Call to a member function on a non-object in ..."
This took me ages to work out
peter at textstore dot c() dot il
29-Jul-2001 06:38
And now, encapsulated in constructor. Isn't it nice? Ye, and it WILL work when more objects of class are serialized & unserialized ONLY when serialized in array / hash by one serialize() call - as serialize() should be used. However, when unserialized, it doesn't affect actual instances' "static" variable - thus, there will be 2 or more static variables after unserialization... but you can still change __wakeup() and to access "real"(actual) class' static value's reference, thus you have to use special function for every static value, as in example above... Ye, PHP is good!@
<?
class O {
function O() {
static $statValue=0;
$this->statValue= &$statValue;
}
}
$o1= &new O;
$o2= &new O;
echo "\$o1: ".$o1->statValue."<br>";
echo "\$o2: ".$o2->statValue."<br>";
$o1->statValue= 5;
$o2->statValue= 10;
echo "\$o1: ".$o1->statValue."<br>";
echo "\$o2: ".$o2->statValue."<br>";
?>
peter at textstore dot c() dot il
29-Jul-2001 06:10
That's how to make class' static variable in PHP4. That means reference returned by function value() is the same for all instances of class O, thus they can share common data. This reference is common just for instances on same PHP page, of course.
<? class O {
function &value() {
static $val=0;
return $val;
}
}
$o1= &new O;
$o2= &new O;
echo "\$o1: ".$o1->value()."<b"."r>";
echo "\$o2: ".$o2->value()."<b"."r>";
$ref1= &$o1->value();
$ref1= 5;
$ref2= &$o2->value();
$ref2= 10;
echo "\$o1: ".$o1->value()."<b"."r>";
echo "\$o2: ".$o2->value()."<b"."r>";
?>
| |