I've got the following class:
class Foo
{
$bar1 = 'a';
$bar2 = 'b'
public function Update($updateInfo)
{
$this->$updateInfo['property'] = $updateInfo[$updateInfo['property']];
}
}
In my code, I've created a Foo object:
$objFoo = new Foo();
Now, I want to be able to update either of the properties, without the update function knowing which. The array would look like this:
$updateInfo['bar1'] = 'newVal';
$updateInfo['property'] = 'bar1';
I remember hearing or reading that something like this is possible in PHP, but I'm currently getting the error:
Object of class could not be converted to string
Am I mistaken in thinking this can be done? Or is there a better way of doing this?
You must be using PHP 7+. This is because of a backwards incompatible change in the handling of indirect variables, properties, and methods. In PHP 5, your code works as is because it's being interpreted as
$this->{$updateInfo['property']} = $updateInfo[$updateInfo['property']];
which is your intended behavior. However, in PHP 7+ it's interpreted as
($this->$updateInfo)['property'] = $updateInfo[$updateInfo['property']];
so it gives you the error you're getting.
Make the behavior you want explicit and it will work fine in both versions:
class Foo
{
private $bar1 = 'a';
private $bar2 = 'b';
public function Update($updateInfo)
{
$this->{$updateInfo['property']} = $updateInfo[$updateInfo['property']];
}
}
$objFoo = new Foo();
$updateInfo['bar1'] = 'newVal';
$updateInfo['property'] = 'bar1';
$objFoo->Update($updateInfo);
var_dump($objFoo);
Demo
Put braces around the value being used as the property:
$this->{$updateInfo['property']} = $updateInfo[$updateInfo['property']];
Related
I need to call an function that is part of an object. The following call works as one would expect:
$someobject = getobject();
$result = $someobject->somefunction->value();
However, I need the "somefunction" component to be a variable.
I have tried to do it like this:
$var = 'somefunction';
$result = '$someobject->' . $var '->value'();
This does not work, but I hope it conveys what I am looking for. I've also tried a lot of variations based upon call_user_func() – without finding a syntax that works.
I am running: PHP 7.2.24-0ubuntu0.18.04.3. Vanilla version for Ubuntu 18.04 LTS.
To access a property or method dynamically based on its name, you simply use one more $ sign than you would normally.
For example, if we have this object:
class Foo {
public $someproperty = 'Hello!';
public function somefunction() {
return 'Hello World';
}
}
$someobject = new Foo;
Then we can access the property normally:
echo $someobject->someproperty;
Or dynamically by name:
$var = 'someproperty';
echo $someobject->$var;
Similarly, we can access the method normally:
echo $someobject->somefunction();
Or dynamically by name:
$var = 'somefunction';
$result = $someobject->$var();
Note that your example is a bit confusing, because you talk about "accessing a function where one part is a variable", but all you're actually trying to do is access a property dynamically, and you then happen to be calling a method on the object stored in that property. So the part you've called somefunction is actually the name of a property.
Here's an example that looks a bit like yours, but with the names changed:
class A {
public $foo;
}
class B {
public function value() {
return 'Hello World';
}
}
$a = new A;
$a->foo = new B;
$propertyname = 'foo';
echo $a->$propertyname->value();
class Blueprint
{
public function method()
{
return 'placeholder';
}
}
$object = new Blueprint;
$method = 'method';
// this will output `placeholder`
echo $object->{$method}();
Hooray!
The following (two alternative valid syntaxes are produced for the second line) works as expected without producing any errors:
$foobar = 'somefunction';
$result = $someobject->$foobar->value();
$result = $someobject->{$foobar}->value();
The following:
$foobar = 'somefunction';
$result = $someobject->${foobar}->value();
also works (i.e. it produces the expected result), but it also produces this warning:
Warning: Use of undefined constant foobar - assumed 'foobar' (this will throw an Error in a future version of PHP) …
Many thanks to all that commented. In particular Duc Nguyen and Swetank Poddar. I think it was the comment by Duc Nguyen in combination with the following comment by Swetank Poddar that finally cracked it.
I know accessing object's properties dynamically using string e.g.
$obj->{$string};
But what about objects themselves?
Like I have string
$obj = '$model->property';
How to use this?
For example in if statement, to have something like
if($model->property) but by using this string?
Tried if({$obj}), if(${$obj})... nothing works.
I don't know if it even possible, but maybe?
I've set up a small test case...
class A {
public $b = 5;
}
$test = new A();
$var = "test";
echo ${$var}->b;
I think this last line is what your after.
Update:
If you want the object and the property, then the nearest I could get is to use...
class A {
public $b = 5;
}
$test = new A();
$var = "test->b";
list($var, $property) = explode("->", $var);
echo ${$var}->$property;
I've just recently began messing around with anonymous functions, and I decided to try and put it to actual use in a larger project. After hitting a road block, I tried setting up a small practice script, but the same issue persists. I don't quite understand what's going on, but here's some code,
$a = function($in) {
echo $in;
};
$a('b4ng');
The above works just fine, as does the following,
class Foo {
public $cmd;
public function __construct() {
$this->cmd = new stdClass();
$this->cmd->a = 'b4ng';
}
}
$bar = new Foo();
echo $bar->cmd->a;
That being made clear, the following does not work,
class Foo {
public $cmd;
public function __construct() {
$this->cmd = new stdClass();
$this->cmd->message = function($in) {
echo $in;
};
}
}
$bar = new Foo();
$bar->cmd->message('b4ng');
When attempting to use the snippet above, I'm hit with the following error,
Fatal error: Call to undefined method stdClass::message()
I understand what the error is telling me, I just don't understand why; 'message' obviously isn't a native function/method of stdClass, but it was declared in 'cmd'.
There is another thing you can't do:
$o = new SomeClass();
$m = $o->someMethod;
$m();
The issue here is that PHP has a special syntax for a member function call, which is what matches $o->foo(). In your case though, foo is a closure (i.e. a data member, not a method) so that causes the error. In order to fix this, you first need to retrieve foo from your object and then invoke it:
// use brackets to force evaluation order
($o->foo)(args..);
// use dedicated function
call_user_func($o->foo, args..);
// use two steps
$foo = $o->foo;
$foo(args..);
I'd try the first variant first, but I'm not sure if PHP allows it. The last variant is the most cludgy, but that one surely works.
In PHP, you can't define class methods outside the class itself. So, you can't create an instance of stdClass and then dynamically create methods for it.
I hit a strange problem today and even as a PHP engineer i'm stumped at this:
It seems you can access a class constant from an object instance such as:
class a {
const abc = 1;
}
$a = new a();
var_dump($a->abc);
This will output null instead of the expected 1. I was able to do the following:
class a {
const abc = 1;
}
$a = new a();
var_dump(a::abc);
But in the context of a sub object that doesn't really know who the parent is exactly, i find it extremely annoying to do:
class a {
const abc = 1;
}
$a = new a();
$c = get_class($a);
var_dump($c::abc);
Is it me or this is completly stupid, if not, please enlighten me why its working that way.
EDIT
Another way of doing it but it's not really better:
class a {
const abc = 1;
}
class b {
public function getA(){
return new a();
}
}
$b = new b();
$c = $b->getA();
var_dump($c::abc);
This last example works more like what i am doing and experiencing...
Just use the instance variable with the scope resolution operator:
$a = new a();
var_dump($a::abc);
This prints 1.
I found a relatively nice and clean way to make my problem easier to live with. Here is the solution i've applied. It is not necessarely the best but for my uses it does exactly what i need.
By creating a magic __get method, i intercept the request for the constant name from and instance point of view and i use a quick reflection to see if that constant exists and return it's value.
This allows me to actually use all in one line a pattern that looks like this:
class a {
const abc = 1;
public function __get($key){
$r = new ReflectionObject($this);
if($r->hasConstant($key)){ return $r->getConstant($key); }
}
}
class b {
public function getA(){
return new a();
}
}
$b = new b();
var_dump($b->getA()->abc);
var_dump($b->getA()->def);
Althought i'd have liked to do:
var_dump($b->getA()::abc);
var_dump($b->getA()::def);
I guess this could be possible later in 5.4+ considering we finaly have array dereferencing, we could probably ask them to add static dereferencing soon.
The PHP documentation indicates that class constants are accessed via SRO (::) rather than ->.
<?php
class MyClass
{
const constant = 'constant value';
function showConstant() {
echo self::constant . "\n";
}
}
echo MyClass::constant . "\n";
ike I mentioned, in php constants are tied to the class definition, they are static by definition and cannot be accessed using the -> operator.
If you really want to use it with your coding paradigm, you can try the reflection class in php5.
class MyClass
{
const A = "I am A";
}
$o = new ReflectionClass( "MyClass" );
echo $o->getconstant("A"); //will print "I am A"
Also, I think the example in your EDIT might not work..I did not run it, but I am not sure if the SRO(::) can be invoked on anything that is not a class reference..
I know this is an old thread, but for people who want to know the best way to do this have a look at the PHP function constant().
With constant() you can simply do this:
$a = new a();
$value = constant(get_class($a)."::abc");
// $value === 1
this has been available since PHP 4, and still works perfectly in PHP 5.5
When trying to use const defined in a class inside a different namespace, the Scope Resolution Operator (::) can be used without problems as stated by the docs prefixing the namespace before the class in which the const was declared with this format:
(<namespace>"\")*<className>::<const>
With the next namespace, class and const definitions:
models/OperationModel.php
<?php
namespace models;
class OperationModel {
const OPERATION_INITIALIZING = 1;
}
You can use the const from another namespace\class like this:
controllers/MobileController.php
<?php
namespace controllers;
use models\OpertionModel;
class MobileController {
private function thingy() {
$operation_status = models\OperationModel::OPERATION_INITIALIZING;
}
}
Take the following code as an example:
class xpto
{
public function __get($key)
{
return $key;
}
}
function xpto()
{
static $instance = null;
if (is_null($instance) === true)
{
$instance = new xpto();
}
return $instance;
}
echo xpto()->haha; // returns "haha"
Now, I'm trying to archive the same result but without have to write the xpto class. My guess is I should have to write something like this:
function xpto()
{
static $instance = null;
if (is_null($instance) === true)
{
$instance = new stdClass();
}
return $instance;
}
echo xpto()->haha; // doesn't work - obviously
Now, is it possible to add __get() magic functionality to the stdClass object? I guess not, but I'm not sure.
No, it is not possible. You cannot add anything to stdClass. Also, unlike Java, where every object is a direct or indirect subclass of Object, this is not the case in PHP.
class A {};
$a = new A();
var_dump($a instanceof stdClass); // will return false
What are you really trying to achieve? Your question sounds a bit like "I want to close the door of my car, but without having a car" :-).
The OP looks like they are trying to achieve a singleton pattern using a function in the global scope which is probably not the correct way to go, but anyway, regarding Cassy's answer, "You cannot add anything to stdClass" - this is not true.
You can add properties to the stdClass simply by assigning a value to them:
$obj = new stdClass();
$obj->myProp = 'Hello Property'; // Adds the public property 'myProp'
echo $obj->myProp;
However, I think you need PHP 5.3+ in order to add methods (anonymous functions / closures), in which case you might be able to do something like the following. However, I've not tried this. But if this does work, can you do the same with the magic __get() method?
UPDATE: As noted in the comments, you cannot dynamically add methods in this way. Assigning an anonymous function (PHP 5.3+) does just that and simply assigns a function (strictly a closure object) to a public property.
$obj = new stdClass();
$obj->myMethod = function($name) {echo 'Hello '.$name;};
// Fatal error: Call to undefined method stdClass::myMethod()
//$obj->myMethod('World');
$m = $obj->myMethod;
$m('World'); // Output: Hello World
call_user_func($obj->myMethod,'Foo'); // Output: Hello Foo