I found some strange PHP behaviour, I'm interested if someone could explain me why some parts of this code works, while others don't.
PHP can create new classes dynamically when class names are stored in variables. And it works fine since I'm using a modern version of PHP (5.5.28). But I found some strange behaviour that I don't really understand.
The problem occurs when the class name is stored in a property of some object. In this case, static functions cannot be called on the dynamic class:
$this->dynClass::SomeFunction() fails and ($this->dynClass)::SomeFunction() fails as well. However $instance = new $this->dynClass works. So if I need to call a static method on $this->dynClass, I have to create a local variable storing the same string: $tmp = $this->dynClass. And then, I can call $tmp::SomeFunction()
I really don't understand this. Could this be a bug? Please someone explain me.
Here's my example code:
<?php
class MyClass {
static function SomeFunction($name){
echo "Hello $name\n";
}
}
MyClass::SomeFunction("World"); //Works fine as it should, prints Hello World
$firstInstance = new MyClass;
$firstInstance::SomeFunction("First"); //prints hello first, no problem
//here comes the interesting part
$dynClass = "MyClass";
$dynClass::SomeFunction("Dynamic"); //Yeah, it works as well
$secondInstance = new $dynClass;
$secondInstance::SomeFunction("Second"); //Hello Second. Fine.
//And here comes the part that I don't understand
class OtherClass {
private $dynClass = "MyClass";
public function test(){
$thirdInstance = new $this->dynClass; //WORKS!
$thirdInstance::SomeFunction('Third'); //Hello Third
//BUT
$this->dynClass::SomeFunction("This"); //PHP Parse error: syntax error, unexpected '::' (T_PAAMAYIM_NEKUDOTAYIM)
//OK, but then this one should work:
($this->dynClass)::SomeFunction("This"); //same error. WHY??
//The only solution is creating a local variable:
$tmp = $this->dynClass;
$tmp::SomeFunction("Local"); //Hello Local
}
}
$otherInstance = new OtherClass;
$otherInstance->test();
?>
Before the Uniform Variable Syntax, php's variable parsing was basically a big lump of corner cases.
In particular, certain operations, like ::, where not supported on (...) expressions.
The two errors you encountered are examples of this loosely defined and inconsistent variable parser.
Related
Examples I have seen for my thing first put this into a variable. It pulls in a lot of data. A object I guess. But i just need one little piece of it.
$data = something();
echo $data->Something;
I just tried this never seen this in any php code and just tryed. Is this something always worked even in ond php versions? Is nobody doing it for a reason, or did I just not saw enough code??
echo something()->Something;
It works fine.
Referencing an object property from the return of a function has been supported since PHP introduced classes (PHP version 5.0.0).
This can be demonstrated with the following simple test case:
class Something {
public $foo;
}
function get() {
$obj = new Something;
$obj->foo = 'bar';
return $obj;
}
echo get()->foo;
This will print bar on every PHP version >= 5.0.0.
I was wondering if there is any possibility in PHP to do following;
<?php
class boo {
static public $myVariable;
public function __construct ($variable) {
self::$myVariable = $variable;
}
}
class foo {
public $firstVar;
public $secondVar;
public $anotherClass;
public function __construct($configArray) {
$this->firstVar = $configArray['firstVal'];
$this->secondVar= $configArray['secondVar'];
$this->anotherClass= new boo($configArray['thirdVal']);
}
}
$classFoo = new foo (array('firstVal'=>'1st Value', 'secondVar'=>'2nd Value', 'thirdVal'=>'Hello World',));
echo $classFoo->anotherClass::$myVariable;
?>
Expected OUTPUT : Hello World
I am getting following error; Parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM
I Googled and it is related to colon (double dots) in $classFoo->anotherClass::$myVariable
I wouldn't like to go all the trouble to change my other classes. Is there anyway around this problem?
Thank you for your help in advance.
P.S. I just didn't want to lose few hours on this to find a way around. I already spent yesterday 2.5 hours to change almost whole Jquery because customer wanted a change and today in the morning I was asked to take the changes back because they didn't want to use it (they changed their mind). I am just trying to avoid big changes right now.
You need to do:
$anotherClass = $classFoo->anotherClass;
echo $anotherClass::$myVariable;
Expanding expressions to class names/objects for static calls/constants is not supported (but expanding variables, as shown above, is).
If you do not care about memory and execution speed, this is correct.
It seems that reference would be better:
$classRef = &$classFoo->anotherClass;
echo $classRef;
Works for me.
My problem is this.
A function return an object, and I want to access its property. It throws and error when I try to do it like this
$this->FunctionThatReturnsAnObject()->Property;
Right now I'm creating a new variable and taking the property from it, like this:
$newVar=$this->FunctionThatReturnsAnObject();
$property=$newVar->Property;
Is this the right way of doing this?
This works perfectly fine in modern PHP versions.
[mcg#mcg-workstation ~]$ php -a
Interactive shell
php > class Foo { public function functionThatReturnsAnObject() { return new Bar(); } }
php > class Bar { public $property = 'Hello, world.'; }
php > $f = new Foo();
php > echo $f->functionThatReturnsAnObject()->property;
Hello, world.
php >
If you're experiencing an error, we'll need to know what the error is. As mentioned in the comments, you might not be able to do this in PHP4, but you shouldn't be using PHP4 in the modern era to begin with. As long as the instance method you're calling returns an object, you can operate on that object directly. (You can't do this from a constructor right now: new Foo()->functionThatReturnsAnObject() will not work because the PHP internals team is full of people that think this would be confusing in some way. I'm not making this up.)
I'm making a plugin system. I have a class extensionmanager that takes the name of a plugin as a constructor parameter. Long story short, this is the code I'm trying to run:
$this->parsedata = function($data) {
$this->extension::parsedata($data);
};
$this-extension is a string with the name of the plugin. I have run static functions in the exact way shown in this example before. Now I'm getting the error unexpected T_PAAMAYIM_NEKUDOTAYIM on that second line (I've heard it roughly translates to "unexpected double colon")
Could anyone help me understand why?
Before the above example I tried to run something like this
$this->parsedata = &$this->extension::parsedata;
Hence the question title. The top example I thought was closer to working so I changed it.
call_user_func may give you a solution. Somewhere in the examples you have this code :
<?php
namespace Foobar;
class Foo {
static public function test() {
print "Hello world!\n";
}
}
call_user_func(__NAMESPACE__ .'\Foo::test'); // As of PHP 5.3.0
call_user_func(array(__NAMESPACE__ .'\Foo', 'test')); // As of PHP 5.3.0
?>
I think you can easily adapt this to call your static function. For example something like :
call_user_func(array($this->extension, 'parseData'), $data);
Do that:
$self = $this;
$this->parsedata = function($data) use ($self) {
{$self->extension}::parsedata($data);
};
Yet, I would suggest to avoid static functions. After all, whoever is going to use your extension manager will need to conform to some interface. Why not take advantage of abstract methods or interfaces to make the user conform to your interface?
Here's what I want to do:
public function all($model) {
$query = 'SELECT ' . implode(', ', $model::$fields) ....;
}
Called like this:
$thing->all(Account);
I get this error:
Parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM in /home/mark/public_html/*/account.php on line 15
When inspecting $model with var_dump it turns out its a string. In the the first example if I change $model to Account on the $query line it works fine.
How can a take a string and turn it back into a class?
Edit: Updated example and title to reflect the problem isn't with self.
Solution: Since I'm not using PHP5.3, I had to resort to using eval() to get what I wanted. Thanks everybody!
Classes are not first-class citizens in PHP, as such they may not be stored in variables, passed as function arguments, or returned from functions.
However, PHP will let you simulate a first-class citizen by using a string containing the name of the class, in certain situations:
$class = "Account";
$instance = new $class(); // You can create instances
call_user_func(array($class, 'frobnicate')); // You can call static functions
That's about all in PHP < 5.3. However, with PHP 5.3, you can also:
$class::frobnicate(); // cleanly call static functions
$fields = $class::$fields; // access static variables
I have also experienced receiving such Fatal Error: Class 'MyClass' not found when you're class has a specific namespace, then it's probably the namespacing. You need to also mention the namespace in your String variable.
$class = "App\MyClass"; // mention the namespace too
$instance = new $class();
See Wikipedia on the scope resolution operator. Especially see the section on PHP and Hebrew.
You cannot use self this way : it can only be used in a static context (i.e. inside a static method) to point to the class -- and not its name.
If you are working with non-static methods (seems you are), you should use $this, instead of self.
Actually, before PHP 5.3, you cannot use a static method/data with a "dynamic" (i.e contained in a variable) class name -- see the examples on the page Static Keyword : they only work with PHP 5.3, for that kind of manipulation.
Which means a portion of code like this one :
class ClassA {
public static $data = 'glop';
}
$className = 'ClassA';
var_dump($className::$data);
Will not work with PHP < 5.3
I find this similar line works in my Laravel app:
$thing->all(new Account);
Try '$this' instead of self.
Self does work this way in PHP. PHP thinks it encounters an unknown constant, which it can't find, and then it assumes it's a string containing 'self'.
Edit: Can you post the class and the code where you instantiate the object?