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.
<?php
class A {
function example() {
echo "I am the original function A::example().<br />\n";
}
}
class B extends A {
function example() {
echo "I am the redefined function B::example().<br />\n";
A::example();
}
}
// there is no object of class A.
// this will print
// I am the original function A::example().<br />
A::example();
// create an object of class B.
$b = new B;
// this will print
// I am the redefined function B::example().<br />
// I am the original function A::example().<br />
$b->example();
?>
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 not 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.
Using PHP 4, I was unable to see an easy method for retrieving static constants in a library, as well as static functions. There was no need to set variables, as the library is final, used for Configuration settings.
<?php
class StaticLibrary
{
//static constants are set here
function finalConstants(&$Vars)
{
$Vars['someVar'] = 'Hello';
$Vars['anotherVar'] = 'World';
$Vars['moreVars'] = 'Keep It Coming';
}
//usage: StaticLibrary::vars('someVar')
function vars($v)
{
static $Vars;
if (!is_array($Vars)) { $Vars = array(); StaticLibrary::finalConstants($Vars); } //setup access to the static contants in our Final library
return($Vars[$v]); //return some var
}
//usage: StaticLibrary::staticFunction('Some string and I want to ')
function staticFunction($str)
{
$str .= 'freak that string!';
return $str;
}
}
?>
I appreciate the ideas below!!
As already pointed out in a previous comment, there is no way to statically access variables, i wrote this small workaround
<?php
class A {
var $MY_VIRTUAL_CONST = 1;
function MY_VIRTUAL_CONST() { $vars = get_class_vars(__CLASS__); return $vars[strToUpper(__FUNCTION__)]; }
}
echo A::MY_VIRTUAL_CONST();
?>
This magically retrieves the correct value without creating an object instance and its easily copy & pasteable, as the methods content is completly generic.
I revised this example to give a better feel for the scope and overloading rules in PHP 4.
<?php
class A {
function example() {
echo "I am the original function A::example().<br />\n";
$this->other();
}
function other() {
echo "I am the original function A::other().<br />\n";
}
}
class B extends A {
function example() {
echo "I am the redefined function B::example().<br />\n";
A::example();
}
function other() {
echo "I am the redefined function B::other().<br />\n";
}
}
// there is no object of class A.
// this will print
// I am the original function A::example().<br />
// PHP Fatal error: Call to a member function on a non-object in test.php on line 5
A::example();
// create an object of class B.
$b = new B;
// this will print
// I am the redefined function B::example().<br />
// I am the original function A::example().<br />
// I am the redefined function B::other().<br />
$b->example();
?>
Robert Chapin
Chapin Information Services
I've noticed that you can't easily obtain a re-scoping of a function-to-object by external means, and it can only really be done by functions ["methods" to be proper] inside the class.
I dub this phenomon "inability to externally modifiy scope resolution of class method", 'cause I don't really know the technical name. (Maybe something else along the lines of "inability to combine scope resolution and member selector operators".)
Enough of this, on to the example:
<?php
class Base {
var $a=5;
function Func() {
echo "Base::Func $this->a<br>";
}
}
class Child extends Base {
function Func() {
$this->a=35;
Base::Func();
//$this->Base::Func(); //Won't work either
echo "Child::Func $this->a<br>";
}
}
$obj=new Child;
$obj->Func(); //That works, outputs as expected
$obj->Base::Func(); //ERROR - See below
?>
As for the error, I get the Hebrew "double colon", as Shachar Shemesh mentioned: "parse error, unexpected T_PAAMAYIM_NEKUDOTAYIM"
I find it odd that I can't do this, and using C++ (where you are able to do this), I feel restricted in this sense.
And I can't think of any workarounds right now, aside from using this scope resolution in the class scope.
NOTE: I'm using PHP 5.0.5 right now (used to be using 4.x, but might've switched it in xampp)
You can simulate the PHP 5 self keyword for static objects in PHP 4 by simply using the class name:
<?php
class A {
function static1() {
return '1';
}
function static2() {
// equivalent to self::static1() in PHP 5
return A::static1() . ' and 2';
}
}
// treat class A as static.
// this will print
// 1 and 2
A::static2();
?>
The A:: reference works just as well internally as externally.
The PHP lexical analyzer gives this token the name "T_PAAMAYIM_NEKUDOTAYIM". It might as well have been written in Hebrew. Wait, it IS written in Hebrew. It means "double colon".
I'm writing this comment mostly so that people who are searching the web for what the #(!(@*&#$ errors such as "unexpected T_PAAMAYIM_NEKUDOTAYIM" or "missing T_PAAMAYIM_NEKUDOTAYIM" mean get to the right page in the PHP manual.
Shachar
after doing some research as the manual was somewhat
vague, i found out that using $this in a class-function is
not fatal as the manual implied, its not even a warning.
it simply comes with a notice saying: $this is undefined.
it appears the class-function is just as a global function.
and as such the static keyword holds its value after scope
changes.
however it seems to also holds its value after the class
gets instanced. take a simple counter function thats a
member of a class. make a few calls to the counter to
let it count to lets say 3. then instance the class and let
it count it again 3 times. you think the score is now 3,
infact its 6.
so one simple way to find out if a function is called from
a instanced class or not is to have the constructor set a
class variable and then check for it using isset. since in
the class-function scope the $this is unavailable isset
will return false. but when the class has been instanced
it will return true.
btw, i tested it with php4.4.2CLI.
hope this will clear somethings up.
my last note is actually not 100% correct... as it is, trying to return a reference to an element of the class variables array won't work, even though the classVars function is declared as (possibly) returning a reference... the correct way to return a reference to one of your class variables would be
<?php
class theClass {
...
function &classVars($name = NULL, ...) {
static $myClassVars;
...
$myRef =& $myClassVars[$name];
return($myRef);
}
}
?>
which works as expected when you're looking for a reference. This should only be necessary when you're trying to return a reference to a piece of the array as opposed to the whole array itself. Simply doing this
<?php
return($myClassVars);
?>
works as expected, ie: you can get a reference to the whole array without needing to explicitly create a reference to it before returning.
Since re-posting a simplified version of the peterjoel.com solution to class variables in php4 was kinda lame, here's the modified solution to give you one function (per class) that allows you to use any number of 'class' variables...
<?php
class theClass {
function &classVars($name = NULL, $value = NULL) { // The classVars function is (possibly) returning a reference...
static $myClassVars; // Define a static var to hold our 'class' vars...
if (!is_array($myClassVars)) { // Initialize the array if it isn't already...
$myClassVars = array();
}
if (!is_null($name) && !is_null($value)) { // If we got two arguments, set the class var that was passed...
$myClassVars[$name] = $value;
}
if (!is_null($name)) { // If we got one argument, return the var it's referring to...
return($myClassVars[$name]);
} else {
return(NULL);
}
}
}
// Setting class variables...
theClass::classVars("varOne", "valueOne");
theClass::classVars("varTwo", "valueTwo");
// Getting class variables...
$myRef = &theClass::classVars("varOne");
echo theClass::classVars("varTwo");
?>
You should be able to store anything in your class vars this way, and depending on how you call the function, you can have either a copy or a reference to the class var returned (see manual on 'returning by reference'...) Again, hope this helps someone...
'emulating' class variables in php4... peter at textstore dot c() dot il put me on the right track... I came up with the following and then before posting this realized that peterjoel.com has already posted almost the exact same thing... anyways, this is a little simpler and only involves one function instead of a getter/setter pair...
class myClass {
function &myVar($arg = NULL) {
static $myClassVar;
if (!is_null($arg)) {
$myClassVar = $arg;
}
return($myClassVar);
}
}
// Setting the 'class' var...
myClass::myVar($myNewClassVar);
// Getting the 'class' var...
$classVarRef = &myClass::myVar();
This should let you set up 'class' vars that will hold anything you can reference... with a little creative array usage, you could modify this to allow any number of class vars without having to write the same above function for each one... hope someone finds it useful...
Using an anonymous class instance via the scope resolution operator does not appear to call the constructor method of the class. Thus, any "stuff" set up within a constructor method (the method called when creating a new instance of a class) will not get set up using anonymous classes.
So for example something like this
<?php
Class A{
var $thing;
function A(
$this->thing = "hello";
)
function get ($var){
return $this->$var;
}
}
echo A::get('thing');
?>
will not print anything as A::thing does not get "set up" outside of the constructor.
You would have to create an instance of class A to get any value for A::thing.
For example:
<?php
$a = new A();
echo $a->get('thing');
?>
(prints "hello")
This is based on my observations and not from any official documentation. I may be wrong about the details.
A note to RichardBronosky's suggestion:
This is a very insecure method, never unserialize user data because they might be able upload a totally different class.
They can only specify properties, not methods but if they can research your code they might be able to do uninteded stuff or create a XSS attack.
So only unserialize user-data when you are in a controlled safe enviroment.
Evert
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...
// The next line is the tricky part.
$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.
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.
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)"; // '$this' to determine whether it is being called statically
}
}
$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.
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!
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
>> 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.
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).
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() NOW DOES CORRECTLY UPDATE $this_class->classFiles :)
call_user_func(array($parent_class,'init'),
&$this_class,$parent_class,$params);
}
} // init()
?>
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().
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.
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 '::'.
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
///////////////////////////////////////////////////////
/*
xobj( );
constructor
SUBCLASSES MUST NOT have any constructors
*/
/////////////////////////////////////////////////////////
function xobj( $params= NULL ){
$current_class=get_class($this);
$this->init($this, $current_class, $params);
// $this->classFiles[0] is the file containing the final subclass
$this->classFile=$this->classFiles[0];
print_r($this->classFiles); exit;
$this->_init($params);
}
///////////////////////////////////////////////////////
/*
void init(&$this_class, $current_class, $params= NULL );
initialization
this class MUST be overridden to set $this->classFiles correctly:
initialization requires __FILE__ to get the path of every class file
*/
/////////////////////////////////////////////////////////
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);");
// call_user_func() DOES NOT CORRECTLY UPDATE $this_class->classFiles:
//call_user_func(array($parent_class,'init'),
$this_class,$parent_class,$params);
}
} // init()
?>
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
)
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.
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();
?>
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.
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 ? ;)
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 ();
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;
//etc..
}
echo Employee::ACTIVE_EMPLOYEE;
//Also tryed:
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;
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.)).
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>";
?>
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>";
?>