I was trying to debug a PHP script when I came across a declaration like:
$cart = new form;
$$cart = $cart->function();
What is $$cart?
What PHP does when you declare $$cart, is try to get the string value of the $cart object, and use that as the name for this variable variable. This means it'd have to call the __toString() magic method of its class.
If there is no __toString() method in the class, this will cause a catchable fatal error:
Catchable fatal error: Object of class MyClass could not be converted to string...
Otherwise, the name of the $$cart variable variable is the string value of the object as returned by that magic method.
An example with the __toString() magic method implemented (different classes/names but similar to your example calling code):
class MyClass {
public function __toString() {
return 'foo';
}
public function some_method() {
return 'bar';
}
}
$obj = new MyClass();
$$obj = $obj->some_method();
echo (string) $obj, "\n"; // foo
echo $$obj; // bar
the double $ is used for a variable variable.
essentially what this entails is the second $ along with the word is a variable the value of which is used for the name of the first $
i.e.-
$first = "second";
$second = 'Goodbye';
echo $$first; // Goodbye
Related
I need to create anonymous object and call it's method
$obj = new stdClass();
$obj->Greeting = function (string $d){return "Hello ".$d;};
$greetings = $obj->Greeting("world!");
But when I try to execute this code I get an error
Call to undefined method stdClass::Greeting()
What's wrong?
You created a stdClass object, not an anonymous one:
$obj = new class () {
public function Greeting(string $d)
{
return "Hello $d";
}
};
echo $greetings = $obj->Greeting("world!");
output:
Hello world!
What's wrong?
Nothing, let's just ask, what's behind or happening here?
The stdClass is used for "empty" objects in PHP or when casting an array to an object ($obj = (object) ['hello' => 'world']).
By default it has no properties (like in $obj = new stdClass;) and also no methods. It is empty in terms of both of these.
Properties can be added dynamically to an stdClass object - but not functions as class methods have to be declared in PHP before instantiating the object.
So the function in your case is a property (PHP has two bags here: one for properties and one for functions) and not a new method dynamically added to it (class MyClass { function method() {...} }).
Let's compare with the original example and provoke the error again:
$obj = new stdClass();
$obj->Greeting = function (string $d) {
return "Hello $d";
};
$greetings = $obj->Greeting("world!");
PHP Fatal error: Uncaught Error: Call to undefined method stdClass::Greeting()
However:
echo $greetings = ($obj->Greeting)("world!");
# #
works, the output:
Hello world!
because PHP is now guided to "call" the ($obj->Greeting) property indirectly, so not looking for the stdClass::Greeting method first.
Normally you don't want that indirection, therefore the suggestion to use the anonymous class instead.
Change
$obj->Greeting("world!");
to
($obj->Greeting)("world!");
or use call_user_func() :
call_user_func($obj->Greeting, 'world!')
I have the following code in my User class:
class User {
public int $id = 0;
public string $code;
public string $name;
}
When assigning $code from the database, I need to find out what type the var is before I can assign it, so I use gettype(), in the __construct function for User:
foreach($data as $key => $val) {
settype($val, gettype($this->$key));
$this->$key = $val;
}
However it returns this error:
Fatal error: Uncaught Error: Typed property User::$code must not be accessed before initialization
I understand what the error message means.
Is there a way to find out the casted type of the variable before its been set?
I'm not sure what exactly you are attempting to do, and without knowing more about the whole thing I won't risk an opinion about the logic of it.
Putting that aside, using gettype() on an uninitialized typed property won't work, because the property has effectively no value at that point.
But, since the property is typed, you could get the property defined type via reflection:
class User {
public int $id = 0;
public string $code;
public string $name;
public function __construct() {
$reflect = new ReflectionClass($this);
$props = $reflect->getProperties();
foreach ($props as $prop) {
echo $prop->getType()->getName(), "\n";
}
}
}
new User();
The above would output:
int
string
string
Doing this on the fly seems like a lot of overhead, so please make sure that there is no better tool at your disposal. Doing this kind of thing on the __construct() method does not look very wise at first sight, but of course I do not know your specific constraints.
If you create a variable as
$foo;
then it is null until it is successfully initialized, so it has no type.
If it is a typed property, then it is simply uninitialized, not null. The rest of my answer contains some experiments conducted based on the information received in the comment-section. So, a variable or property is null before it is initialized, with the exception of typed properties, which are simply uninitialized.
In the comment section it was suggested that the uninitialized variable does not equal to null. So I have written this test code:
<?php
$foo;
echo var_dump($foo === null);
and executed it
It gives a warning and evaluates to true, so the uninitialized variable was indeed null.
Since yivi pointed out that the question refers to a property, I have conducted another experiment:
<?php
class Foo {
public $foo;
public function __construct() {
echo var_dump($this->foo === null);
}
}
new Foo();
and the result is
In PHP does such a magic method automatically run when a variable referencing an object is in an echo statement?
I am sorry, I had quite a difficulty understanding what you're asking. I believe you want the __toString() method:
The __toString() method allows a class to decide how it will react when it is treated like a string. For example, what echo $obj; will print. This method must return a string, as otherwise a fatal E_RECOVERABLE_ERROR level error is emitted.
Here's a quick example:
class A
{
public function __toString()
{
return 'banana';
}
}
$a = new A();
echo $a;
This will print out banana
How can I assign a function to a method in a class in PHP? I tried the following:
class Something{
public function __construct(){
$functionNames = array('foo', 'bar')
$variable = 'blablabla';
foreach($functionNames as $functionName){
if(method_exists($this, $functionName))
continue;
$this->{$functionName}() = function($params){ //should create the methods "foo" and "bar"
echo $variable; //should echo 'blablabla' (I know that the variable was declared outside this function, but how can I access it anyway?)
}; //the error points to here
}
}
}
But this code gives me this error:
Fatal error: Can't use method return value in write context
Does anyone know how I can assign the anonymous function to the class method, while also still being able to access variables outside that function?
You are doing foreach($functionNames as $functionName){ which means that $functionName is a string, not an array. So, don't use $functionName[0].
method_exists takes 2 parameters. One is the object and the other is the method name. It should be:
method_exists($this, $functionName)
As for creating the function, you don't need () on the left side of the =. It should be:
$this->$functionName = function($params) use($variable){
echo $variable;
};
The use($variable) is needed to tell PHP to use that variable inside the function. That's how closures work in PHP, it's different than other languages.
So, your class should look like:
class Something{
public function __construct(){
$functionNames = array('foo', 'bar');
$variable = 'blablabla';
foreach($functionNames as $functionName){
if(method_exists($this, $functionName)){
continue;
}
$this->$functionName = function($params) use($variable){
echo $variable;
};
}
}
}
Problem here is that in this way of making functions, you are not actually creating a class method, but instead creating a class variable that contains a function.
So, you need to call it like so:
$test = new Something;
$foo = $test->foo;
$foo('abc');
You can't just do $test->foo('abc');.
EDIT: Another thing you can do is use PHP's __call "magic method". This will be ran whenever you do ->funcName(), regardless of whether the method exists or not. Using that method, you can just check to see if the method called was 'foo' or 'bar'. See this example:
class Something{
private $variable;
public function __construct(){
$this->variable = 'blablabla';
}
public function __call($name, $params=array()){
if(method_exists($this, $name)){
// This makes sure methods that *do* exist continue to work
return call_user_func(array($this, $name), $params);
}
else{
$functionNames = array('foo', 'bar');
if(in_array($name, $functionNames)){
// You called ->foo() or ->bar(), so do something
// If you'd like you can call another method in the class
echo $this->variable;
}
}
}
}
With this, now you can do the following:
$test = new Something;
$test->foo('abc'); // Will echo "blablabla"
Helo everyone.
I have a class MyClass and a function escape() that can be called as a static class or as an instantiated Object.
MyClass::_escape('..... some string...');
or
$myclass->escape();
What I would like is not to have the underscore on the staic version and for both just have the same function definition. I trie to do.
class MyClass {
public $_string = "";
public function escape($string = null) {
if($string == null)
return new String(mysql_real_escape_string($this->_string));
else
return new String(mysql_real_escape_string($string));
}
}
but this function fails by the PHP parser. Is there a way of doing what I attempted to above??
So to summarise, I would like the static call to look like;
print Myclass::escape('some string');
and the instantiated call to look like;
print $myobject->escape(); //Which escapes the private variable _string
Hope this was clear.
regards
public function _escape($s){
return self::escape($s);
}
What you're trying to achieve won't work without at least some kind of error:
Example using static:
error_reporting(E_ALL ^ E_STRICT);
class MyClass
{
// note the *static* keyword
public static function escape($string = null) {
// $this is not defined, even if called as object-method
var_dump(isset($this));
}
}
$foo = new MyClass();
$foo->escape(); // => bool(false)
MyClass::escape(); // => bool(false)
So, if you remove the static keyword and try again, you'll get:
$foo->escape(); // => bool(true)
but also:
Strict Standards: Non-static method MyClass::escape() should
not be called statically ...
for
MyClass::escape(); // => bool(false)
There are no parse errors in the code you posted. In fact, it works just as you want it to work, as long as you never pass $string to the escape() method in an object context:
$foo = new MyClass();
$foo->_string = 'foo';
$foo->escape(); // This works.
MyClass::escape('bar'); // This works, too.
$foo->escape('baz'); // Don't do this. It'll escape $string instead of $this->_string.
You could resolve this issue by determining whether or not you're in a static context within the escape() method, instead of checking for the existence of $string.