I am learning PHP at the moment and noticed a curious fact from the tutorial:
Note: $this is a special variable that can't be assigned.
Doesen't object that cannot be assigned have to be implemented as constant, not a variable?
Why it is made so?
The scope of a constant is global. In contrast, $this changes throughout the application, because it depends on the context (i.e. the class).
Consider this short example:
class A {
function printThis() { echo $this; }
}
class B {
function printThis() { echo $this; }
}
Clearly, $this in class B is different from $this in class A, thus by definition it cannot be a constant*.
*) Edit: However, in PHP there exist magic constants that change depending on the context:
<?php
$line1 = __LINE__;
$line2 = __LINE__;
assert($line1 == $line2); // fails
So I think user deceze summarized it pretty well in the comments: "Meh, that's PHP."
I am using Reflection against the following class:
class Constant {
const CONSTANT = 3;
public $test1 = 'CONSTANT';
public $test2 = CONSTANT;
}
When using ReflectionClass::getDefaultProperties(); I get the following notice:
PHP Notice: Use of undefined constant CONSTANT - assumed 'CONSTANT'
on this line of code:
$defaultValues = $reflectionClass->getDefaultProperties();
First, I wonder why I get the notice here (I mean, I can't anticipate/avoid the notice even though the code is 100% correct)?
And second, when using var_export($defaultValues[3]), it outputs 'CONSTANT' which is normal because it has been casted to string.
So, how can I output CONSTANT instead of 'CONSTANT' for $test2 and still output a quote-delimited string for $test1?
Edit: I get CONSTANT for both cases ($test1 and $test2) but because of that I can't differentiate between them. I want to be able to know: that is a string, or that is the name of a constant.
why I get the notice here?
because you mean self::CONSTANT but tried to use global CONSTANT, e.g. your code assumes
const CONSTANT = 3; // global constant
class Constant {
const CONSTANT = 3; // class constant
public $test1 = 'CONSTANT';
public $test2 = CONSTANT; // refers to global constant
}
but you wanted to do this:
class Constant {
const CONSTANT = 3;
public $test1 = 'CONSTANT';
public $test2 = self::CONSTANT; // self indicated class scope
}
With the latter, this
$reflectionClass = new ReflectionClass('Constant');
var_dump( $reflectionClass->getDefaultProperties() );
will give
array(2) {
["test1"]=>
string(8) "CONSTANT"
["test2"]=>
int(3)
}
Is there a way to get ["test2"] => self::CONSTANT via Reflection? No. The Reflection API will evaluate the constant. If you want self::CONSTANT you'd have to try some of the 3rd party static reflection APIs.
And obviously, if you want 'CONSTANT', write "'CONSTANT'".
Regarding EDIT:
I get CONSTANT for both cases ($test1 and $test2) but because of that I can't differentiate between them. I want to be able to know: that is a string, or that is the name of a constant.
$foo = CONSTANT means assign the constant value to the foo property. It does not mean assign the constant itself. By assigning the value to a property, it no longer is a constant value. It's mutable. The "name of a constant" is represented as a string. You can use ReflectionClass::hasConstant to check whether that string happens to also be the name of a defined constant in the class or use defined for global constants.
Since you use CONSTANT for the value of $test2 and do not define it before it will throw the "undefined constant" error. Do you want to use the class constant CONSTANT as value for the public $test2? Then use public $test2 = self::CONSTANT. Otherwise define CONSTANT as constant before the class.
Please note that PHP casts all unknown constants as strings with the value of the name of the unknown constant.
I understand that by using "=" objects are assigned by reference and for other variable and for datatypes like strings and integers you need to use "=&" to assign by reference. When you assign an object by reference explicitly using "=&", it seems to not affect assignment. However when you assign the object to a global it does.
Consider the following:
<?php
$global_obj = null;
class my_class {
var $value;
public function __construct() {
global $global_obj;
$global_obj =& $this;
$GLOBALS['some_var'] = $this;
}
}
$a = new my_class();
$a->my_value = 5;
$global_obj->my_value = 10;
echo 'A: ' . $a->my_value; //5
echo '<br />';
echo 'Global Object: ' . $global_obj->my_value; //10
echo '<br />';
echo 'Globals Array Value: ' . $some_var->my_value; //5
?>
If you remove the ampersand in the code above $this is assigned to $global_obj by reference. My question is why does having the ampersand in there seemingly stop this from happening?
Thanks
What is happening here is that $global_obj is a reference to the variable $this. $this is a pseudo-variable that exists inside methods to be a reference to the current object. But who knows what happens to $this when it goes outside of the method scope. It is probably a bad idea to have a reference to $this.
In fact, if you investigate it a bit further, after your constructor returns, if you examine $global_obj, its value is null. The PHP engine probably sets $this to null after the method exits (but this is not documented anywhere), and your $global_obj, since it is a reference to the variable $this, follows it. When you set an attribute on null, it instantiates a new object of class stdClass automatically, so it seems to succeed. But of course this is a completely different object than the one in $a.
I have this:
one string variable which holds the class name ($classname)
one string variable with holds the property name ($propertyname)
I want to get that property from that class, the problem is, the property is static and I don't know how to do that.
If the property weren't static, it would have been:
$classname->$propertyname;
if the property were a method, I could have used call_user_function
call_user_func(array($classname, $propertyname));
But in my case, am I just lost. I am however hoping that it is possible. With the thousands of functions that PHP has, he'd better have something for this as well. Maybe I'm missing something?
Thanks!
Edit:
for those with eval() solutions: thanks, but it is out of the question
for those with get _class _vars() solutions: thanks, but it seems it returns "the default properties of the given class" (php.net), and yes, I would like that value to be changable (even though it does help me in some of the cases)
If you are using PHP 5.3.0 or greater, you can use the following:
$classname::$$propertyname;
Unfortunately, if you are using a version lower than 5.3.0, you are stuck using eval() (get_class_vars() will not work if the value is dynamic).
$value = eval($classname.'::$'.$propertyname.';');
EDIT: I've just said get_class_vars() wouldn't work if the value is dynamic, but apparently, variable static members are part of "the default properties of a class". You could use the following wrapper:
function get_user_prop($className, $property) {
if(!class_exists($className)) return null;
if(!property_exists($className, $property)) return null;
$vars = get_class_vars($className);
return $vars[$property];
}
class Foo { static $bar = 'Fizz'; }
echo get_user_prop('Foo', 'bar'); // echoes Fizz
Foo::$bar = 'Buzz';
echo get_user_prop('Foo', 'bar'); // echoes Buzz
Unfortunately, if you want to set the value of the variable, you will still need to use eval(), but with some validation in place, it's not so evil.
function set_user_prop($className, $property,$value) {
if(!class_exists($className)) return false;
if(!property_exists($className, $property)) return false;
/* Since I cannot trust the value of $value
* I am putting it in single quotes (I don't
* want its value to be evaled. Now it will
* just be parsed as a variable reference).
*/
eval($className.'::$'.$property.'=$value;');
return true;
}
class Foo { static $bar = 'Fizz'; }
echo get_user_prop('Foo', 'bar'); // echoes Fizz
set_user_prop('Foo', 'bar', 'Buzz');
echo get_user_prop('Foo', 'bar'); // echoes Buzz
set_user_prop() with this validation should be secure. If people start putting random things as $className and $property, it will exit out of the function as it won't be an existing class or property. As of $value, it is never actually parsed as code so whatever they put in there won't affect the script.
I think this is the simplest:
$foo = new ReflectionProperty('myClassName', 'myPropertyName');
print $foo->getValue();
To return a variable value that is set by a Static Variable you need to call:
$static_value = constant($classname.'::'.$propertyname);
Check out the documentation :: PHP Constant Documentation
You should be able to do something like:
eval("echo $classname::$propertyname;");
I just did a quick test and got this to work for me. Not sure if there's a better way or not, but I wasn't able to find one.
'eval' looks so close to 'evil', and I hate using it and/or seeing it in code. With a few ideas from other answers, here's a way to avoid it even if your php isn't 5.3 or higher.
Changed to reflect testing based on a comment.
class A {
static $foo = 'bar';
}
A::$foo = 'baz';
$a = new A;
$class = get_class($a);
$vars = get_class_vars($class);
echo $vars['foo'];
Outputs 'baz'.
One thing I noticed is that you can't set variables which are protected in static classes as the eval() command runs in a scope outside the class. The only thing to get around this would be to implement a static method inside the/every class which runs the eval(). This method could be protected as the call_user_func() [to call the setter method] also runs from inside the class.
Potentially relevant: discussion on late static binding in PHP - When would you need to use late static binding?.
get_class_vars is not same as get_object_vars.
I think get_clas_vars should return the original property values.
Even if for you said eval is out of the question, prior PHP 5.3 the easiest solution is still by using eval:
eval("\$propertyval = $classname::\$propertyname;");
echo $propertyval;
Getting and setting both static and non static properties without using Reflection
Using Reflection works but it is costly
Here is what I use for this purpose,
It works for PHP 5 >= 5.1.0 because I'm using property_exist
function getObjectProperty($object, $property)
{
if (property_exists(get_class($object), $property)) {
return array_key_exists($property, get_object_vars($object))
? $object->{$property}
: $object::$$property;
}
}
function setObjectProperty($object, $property, $value)
{
if (property_exists(get_class($object), $property)) {
array_key_exists($property, get_object_vars($object))
? $object->{$property} = $value
: $object::$$property = $value;
}
}
You can use ReflectionClass:
class foo
{
private static $bar = "something";
}
$class = "foo";
$reflector = new ReflectionClass($class);
$static_vars = $reflector->getStaticProperties();
var_dump($static_vars["bar"]);
i have something like
define("__ROOT_PATH__", "http://{$_SERVER['HTTP_HOST']}/admin");
within a class. how do i call it from within functions is it with a cologn? i tried looking it up on google but nothing.
thanks
The function define() is intended for global constants, so you just use the string __ROOT_PATH__ (I would recommend using another naming scheme, though. Constants starting with two underscores are reserved by PHP for their magic constants)
define('__ROOT_PATH__', 'Constant String');
echo __ROOT_PATH__;
If you want to declare a class constant, use the const keyword:
class Test {
const ROOT_PATH = 'Constant string';
}
echo Test::ROOT_PATH;
There is one problem though: The class constants are evaluated while your script is being parsed, so you cannot use other variables within these constants (so your example will not work). Using define() works, as it is treated like any other function and the constant value can be defined dynamically.
EDIT:
As PCheese pointed out, you can access class constants using the keyword self, instead of the class name from within the class:
class Test {
const ROOT_PATH = 'Constant string';
public function foo() {
echo self::ROOT_PATH;
}
}
# You must use the class' name outside its scope:
echo Test::ROOT_PATH;
Using define will define the constant globally, so just refer to it directly in your code:
echo __ROOT_PATH__;
If you want to scope a constant to a class, you need to declare it differently. However, this syntax will not let you declare it dynamically as you did above, using $_SERVER.
<?php
class MyClass {
const MY_CONST = "foo";
public function showConstant() {
echo self::MY_CONST;
}
}
// Example:
echo MyClass::MY_CONST;
$c = new MyClass();
$c->showConstant();
Simply use the name of the constant.
i.e.
echo "Root path is " . __ROOT_PATH__;