i want to turn a simple string like that "response->dict->words" into a variable name that i can actually work with. I will give an example now. Lets assume the value of $response->dict->words is 67.
Example:
$var = "response->dict->words"
echo $$var; /* PRINT THE VALUE 67 FROM $response->dict->words*/
As you may notice i put an extra dollar sign before the $var because this should work, but it doesn't.
Can anyone help me with this?
class ClassOne {
public function test() {
return 'test';
}
}
class ClassTwo {
public function test2() {
return 'test2';
}
}
$one = new ClassOne();
$two = new ClassTwo();
$objects = array('one', 'two');
$methods = array('test', 'test2');
for ($i = 0; $i < count($objects); $i++) {
echo ${$objects[$i]}->$methods[$i]();
}
You can store classnames or method names as strings and later use them, or even store variable names, like here ${$objects} (variable variables), but you cannot store whole logic.
To evaluate whole logic, you have to use eval(), which is most probably bad idea
$var = "response->dict->words"
eval("?> <?php echo $".$var.";");
You can split your string and make the call as below:
class Response {
public $dict;
public function __construct() {
$this->dict = new stdClass();
$this->dict->words = 'words test';
}
}
$response = new Response();
$var = 'response->dict->words';
$elements = explode('->', $var);
echo ${$elements[0]}->$elements[1]->$elements[2];
Results into words test
Or, if you don't know the level of nesting the object call, you can perform the call in a foreach loop. When the loop exits, the last call will be available after it:
class Response {
public $dict;
public function __construct() {
$this->dict = new stdClass();
$this->dict->words = new stdClass();
$this->dict->words->final = 'test chained string';
}
}
$response = new Response();
$var = 'response->dict->words->final';
$elements = explode('->', $var);
foreach ($elements as $key => $element) {
if ($key == 0) {
$call = ${$element};
continue;
}
$call = $call->$element;
}
echo $call;
Results into: test chained string
There is a better way, why don't you cache the variable like
$var = $response->dict->words;
Related
I have some trouble,When I define a static variable in a method and call it multiple times, the code is as follows:
function test()
{
static $object;
if (is_null($object)) {
$object = new stdClass();
}
return $object;
}
var_dump(test());
echo '<hr>';
var_dump(test());
The output is as follows:
object(stdClass)[1]
object(stdClass)[1]
Yes, they return the same object.
However, when I define a closure structure, it returns not the same object.
function test($global)
{
return function ($param) use ($global) {
//echo $param;
//exit;
static $object;
if (is_null($object)) {
$object = new stdClass();
}
return $object;
};
}
$global = '';
$closure = test($global);
$firstCall = $closure(1);
$closure = test($global);
$secondCall = $closure(2);
var_dump($firstCall);
echo '<hr>';
var_dump($secondCall);
The output is as follows:
object(stdClass)[2]
object(stdClass)[4]
which is why, I did not understand.
By calling test(...) twice in your sample code, you have generated two distinct (but similar) closures. They are not the same closure.
This becomes more obvious some some subtle improvements to your variable names
$closureA = test($global);
$firstCall = $closureA(1);
$closureB = test($global);
$secondCall = $closureB(2);
var_dump($firstCall, $secondCall);
Now consider this code alternative:
$closureA = test($global);
$firstCall = $closureA(1);
$secondCall = $closureA(2);
var_dump($firstCall, $secondCall);
Does that help you understand?
If i use magic __set to set a value to private var how could i set a var as an array ?
Im thinking of something like this, pretend i have a class with __get __set
$myclass->names = 'Name'; // Works
$myclass->names = array('n1'=>'Name1', 'n2' => 'Name2'); // works as well
//this does not work
$myclass->names['n1'] = 'Name1';
$myclass->names['n2'] = 'Name2';
Its the 2 last examples i want to get to work. Have tested various ways but cant figure it out.
You obviously don't output notices, otherwise you'd have gotten the error
Notice: Indirect modification of overloaded property Foo::$bar has no
effect
What you're trying to do is simply not possible. There is exactly one way to make arrays received through __get writable, but that is most likely not what you want.
<?php
class Foo {
protected $bar = array();
public function &__get($name) {
return $this->$name;
}
public function __set($name, $value) {
return $this->$name = $value;
}
}
$foo = new Foo();
$foo->bar = array('a', 'b', 'c');
echo $foo->bar[0]; // output "a"
$foo->bar[0] = 'z'; // fires warning
echo $foo->bar[0]; // output "z"
// all fine, but here's the catch:
$t =& $foo->bar;
$t = array('y');
echo $foo->bar[0]; // output "y"
Now that you've seen how returning values by reference can be a problem, you may be interested in ArrayObject. Something like
<?php
class Foo {
protected $bar = array();
public function __get($name) {
return new ArrayObject(&$this->$name);
}
public function __set($name, $value) {
return $this->$name = $value;
}
}
$foo = new Foo();
$foo->bar = array('a', 'b', 'c');
echo $foo->bar[0]; // output "a"
$foo->bar[0] = 'z'; // fires warning
echo $foo->bar[0]; // output "z"
// all fine, and no catch
$t =& $foo->bar;
$t = array('y');
echo $foo->bar[0]; // still outputs "z"
It won't work. $class->arr['key'] will execute the getter. So basically, what your code will look like is:
array('key' => 'value')['key'] = 'new value';
Which, obviously, does nothing. If you want that to work, you will have to declare the names as a public property.
This expression will invoke the getter:
$myclass->names['n1'] = 'Name1';
^^^^^^^^^^^^^^^
needs to be get
^^^^^^^^^^^^^^^^
assignment later
The only way to make that work is a fugly workaround. By letting the getter return an reference to the know array the following assignment could work.
function & __get($name) {
if (is_array($this->$name)) {
return & $this->$name;
}
else ...
}
So it's really only advisable if it significantly simplifies your API.
Try this code:
class Foo
{
private $bar;
public function __construct()
{
$this->bar = new ArrayObject(array());
}
public function __get($item)
{
if(property_exists($this, $item)) {
return $this->$item;
}
}
public function __set($item, $value)
{
if(property_exists($this, $item)) {
$this->{$item} = $value;
}
}
}
$obj = new Foo();
$obj->bar['color'] = 'green';
foreach($obj->bar as $attribute => $value) {
echo '<p>' . $attribute . ' : ' . $value . '</p>' . PHP_EOL;
}
// output => color : green
I have an array. but I don't know how to access it inside a class.
below is my sample code.
<?php
$value[0]=11;
$value[1]=22;
$value[2]=33;
class test {
var $code1,$code2,$code3;
function __construct() {
$this->$code1 = $value[0];
$this->$code2 = $value[1];
$this->$code3 = $value[2];
echo $code1;
}
}
Read this.
And you can do any one of the following:
Pass the value into the constructor as a parameter (recommended option)
<?php
$value[0]=11;
$value[1]=22;
$value[2]=33;
class test {
var $code1,$code2,$code3;
function __construct($value) {
$this->code1 = $value[0];
$this->code2 = $value[1];
$this->code3 = $value[2];
echo $this->code1;
}
}
$obj = new test($value);
?>
Use the $GLOBALS array (docs)
<?php
$value[0]=11;
$value[1]=22;
$value[2]=33;
class test {
var $code1,$code2,$code3;
function __construct() {
$this->code1 = $GLOBALS['value'][0];
$this->code2 = $GLOBALS['value'][1];
$this->code3 = $GLOBALS['value'][2];
echo $this->code1;
}
}
$obj = new test;
?>
Use the global keyword (docs)
<?php
$value[0]=11;
$value[1]=22;
$value[2]=33;
class test {
var $code1,$code2,$code3;
function __construct() {
global $value;
$this->code1 = $value[0];
$this->code2 = $value[1];
$this->code3 = $value[2];
echo $this->code1;
}
}
$obj = new test;
?>
NOTES
I have corrected a couple of errors above.
You should use $this->code1 instead of $this->$code1. The second version is valid syntactically, but means something else. Consider the following example:
class myClass {
public $myVar = "My Var";
public $anotherVar = "Another Var";
function __construct () {
// creates a local variable to the constructor, called $myVar
// does NOT overwrite the value defined above for the object property
$myVar = "anotherVar";
echo $myVar; // echoes 'anotherVar'
echo $this->myVar; // echoes 'My Var'
echo $this->$myVar; // echoes 'Another Var'
}
}
Also, the above example illustrates the reason why you should use echo $this->code1; and not simply echo $code1;
Something like this:
class test
{
var $code1,$code2,$code3;
function __construct($input)
{
$this->code1 = $input[0];
$this->code2 = $input[1];
$this->code3 = $input[2];
}
}
[...]
$value[0]=11;
$value[1]=22;
$value[2]=33;
$test = new test($value);
Global variables aren't visible inside functions, unless you use the global keyword. e.g.:
// Global variable
$x = 5;
// Won't affect the global
function foo()
{
$x = 3;
}
// Will affect the global
function bar()
{
global $x;
$x = 2;
}
Note
However, in general, it's not a good idea to use global variables like this. It introduces dependencies, and makes your code harder to test and to debug. I suggest you pass the variable in as an argument to your constructor.
class test {
private $code1,$code2,$code3;
function __construct($value) {
$this->code1 = $value[0];
$this->code2 = $value[1];
$this->code3 = $value[2];
echo $this->code1;
}
}
Usage :
$test = new Test($value);
I'm taking array output from a command-line program and parsing it into a PHP object. Consider this example of a very simple way to do this:
$output = explode("\n", shell_exec(myProg));
$obj = new MyObject();
$offset_field1 = 0;
$offset_field2 = 1;
$obj->Field1 = $output[$offset_field1];
$obj->Field2 = $output[$offset_field2];
This is a bit cumbersome, especially when the number of fields increases. Is there a better design pattern or method to accomplish the same feat in a less heavy-handed manner?
Why not put the assignment code in the object?
class MyObject
{
public function __construct(array $data)
{
$this->Field1 = $data['keyname1'];
$this->Field2 = $data['keyname2'];
...
}
}
or use the get magic method.
class MyObject
{
protected $data;
public function __construct(array $data)
{
$this->data = $data;
}
public function __get($key)
{
$map = array('Field1' => 1, 'Feild2' => 2, ...);
if (isset($map[$key])) {
return $this->data[$map[$key]];
}
}
}
I guess this should work:
$output = explode("\n", shell_exec(myProg));
$obj = new MyObject();
foreach ($output as $key => $value)
{
$obj->{'Field' . ($key + 1)} = $value;
}
As it seems you cannot guess the field name from the outpout of your programm, you will have to define it somewhere.
$key_map = array('field_name1', 'field_name2', 'etc');
$obj = new MyObject();
foreach(explode("\n", shell_exec(myProg)) as $k => $v)
{
if(isset($key_map($k))
$obj->$key_map[$k] = $v;
}
How do I get a property in a PHP based on a string? I'll call it magic. So what is magic?
$obj->Name = 'something';
$get = $obj->Name;
would be like...
magic($obj, 'Name', 'something');
$get = magic($obj, 'Name');
Like this
<?php
$prop = 'Name';
echo $obj->$prop;
Or, if you have control over the class, implement the ArrayAccess interface and just do this
echo $obj['Name'];
If you want to access the property without creating an intermediate variable, use the {} notation:
$something = $object->{'something'};
That also allows you to build the property name in a loop for example:
for ($i = 0; $i < 5; $i++) {
$something = $object->{'something' . $i};
// ...
}
What you're asking about is called Variable Variables. All you need to do is store your string in a variable and access it like so:
$Class = 'MyCustomClass';
$Property = 'Name';
$List = array('Name');
$Object = new $Class();
// All of these will echo the same property
echo $Object->$Property; // Evaluates to $Object->Name
echo $Object->{$List[0]}; // Use if your variable is in an array
Something like this? Haven't tested it but should work fine.
function magic($obj, $var, $value = NULL)
{
if($value == NULL)
{
return $obj->$var;
}
else
{
$obj->$var = $value;
}
}
Just store the property name in a variable, and use the variable to access the property. Like this:
$name = 'Name';
$obj->$name = 'something';
$get = $obj->$name;
There might be answers to this question, but you may want to see these migrations to PHP 7
source: php.net
It is simple, $obj->{$obj->Name} the curly brackets will wrap the property much like a variable variable.
This was a top search. But did not resolve my question, which was using $this. In the case of my circumstance using the curly bracket also helped...
example with Code Igniter get instance
in an sourced library class called something with a parent class instance
$this->someClass='something';
$this->someID=34;
the library class needing to source from another class also with the parents instance
echo $this->CI->{$this->someClass}->{$this->someID};
Just as an addition:
This way you can access properties with names that would be otherwise unusable$x = new StdClass;
$prop = 'a b';
$x->$prop = 1;
$x->{'x y'} = 2;
var_dump($x);object(stdClass)#1 (2) {
["a b"]=>
int(1)
["x y"]=>
int(2)
}(not that you should, but in case you have to).
If you want to do even fancier stuff you should look into reflection
In case anyone else wants to find a deep property of unknown depth, I came up with the below without needing to loop through all known properties of all children.
For example, to find $foo->Bar->baz->bam, given an object ($foo) and a string like "Bar->baz->bam".
trait PropertyGetter {
public function getProperty($pathString, $delimiter = '->') {
//split the string into an array
$pathArray = explode($delimiter, $pathString);
//get the first and last of the array
$first = array_shift($pathArray);
$last = array_pop($pathArray);
//if the array is now empty, we can access simply without a loop
if(count($pathArray) == 0){
return $this->{$first}->{$last};
}
//we need to go deeper
//$tmp = $this->Foo
$tmp = $this->{$first};
foreach($pathArray as $deeper) {
//re-assign $tmp to be the next level of the object
// $tmp = $Foo->Bar --- then $tmp = $tmp->baz
$tmp = $tmp->{$deeper};
}
//now we are at the level we need to be and can access the property
return $tmp->{$last};
}
}
And then call with something like:
$foo = new SomeClass(); // this class imports PropertyGetter trait
echo $foo->getProperty("bar->baz->bam");
Here is my attempt. It has some common 'stupidity' checks built in, making sure you don't try to set or get a member which isn't available.
You could move those 'property_exists' checks to __set and __get respectively and call them directly within magic().
<?php
class Foo {
public $Name;
public function magic($member, $value = NULL) {
if ($value != NULL) {
if (!property_exists($this, $member)) {
trigger_error('Undefined property via magic(): ' .
$member, E_USER_ERROR);
return NULL;
}
$this->$member = $value;
} else {
if (!property_exists($this, $member)) {
trigger_error('Undefined property via magic(): ' .
$member, E_USER_ERROR);
return NULL;
}
return $this->$member;
}
}
};
$f = new Foo();
$f->magic("Name", "Something");
echo $f->magic("Name") , "\n";
// error
$f->magic("Fame", "Something");
echo $f->magic("Fame") , "\n";
?>
What this function does is it checks if the property exist on this class of any of his child's, and if so it gets the value otherwise it returns null.
So now the properties are optional and dynamic.
/**
* check if property is defined on this class or any of it's childes and return it
*
* #param $property
*
* #return bool
*/
private function getIfExist($property)
{
$value = null;
$propertiesArray = get_object_vars($this);
if(array_has($propertiesArray, $property)){
$value = $propertiesArray[$property];
}
return $value;
}
Usage:
const CONFIG_FILE_PATH_PROPERTY = 'configFilePath';
$configFilePath = $this->getIfExist(self::CONFIG_FILE_PATH_PROPERTY);
$classname = "myclass";
$obj = new $classname($params);
$variable_name = "my_member_variable";
$val = $obj->$variable_name; //do care about the level(private,public,protected)
$func_name = "myFunction";
$val = $obj->$func_name($parameters);
why edit:
before : using eval (evil)
after : no eval at all. being old in this language.