I am trying to do something with variable variables and I got stuck on an object problem. Imagine this class setup:
class A
{
public $field = 10;
}
class B
{
public $a;
public function __construct()
{
$this->a = new A();
}
}
Now everyone knows that this pice of code works:
$a = new A();
$var = 'field';
echo $a->$var; // this will echo 10
Is there any possibility I could make something like this work?:
$b = new B();
$var = 'a->field';
echo $b->$var; // this fails
Note: any option which does not use eval function?
How about using a closure?
$getAField = function($b) {
return $b->a->field;
};
$b = new B();
echo $getAField($b);
Though, it's only possible in newer versions of PHP.
Or, as a more generic version, something like this:
function getInnerField($b, $path) { // $path is an array representing chain of member names
foreach($path as $v)
$b = $b->$v;
return $b;
}
$b = new B();
echo getInnerField($b, array("a", "field"));
You can write a custom __get method on your class to access the childrens property. This works:
class A
{
public $field = 10;
}
class B
{
public $a;
public function __construct()
{
$this->a = new A();
}
public function __get($property) {
$scope = $this;
foreach (explode('->', $property) as $child) {
if (isset($scope->$child)) {
$scope = $scope->$child;
} else {
throw new Exception('Property ' . $property . ' is not a property of this object');
}
}
return $scope;
}
}
$b = new B();
$var = 'a->field';
echo $b->$var;
Hope that helps
I don't recommend it, but you could use eval:
$b = new B();
$var = 'a->field';
eval( 'echo $b->$'.$var );
This should also work I guess:
$b = new B();
$var1 = 'a';
$var2 = 'field'
echo ($b->$var1)->$var2;
Related
class foo
{
public $bar = 1;
}
#foo.bar = 2; //change class variable and affect all after
$a = new foo();
$a->bar = 2;//avoid DRY
$b = new foo();
$b->bar = 2;//avoid DRY
echo $a->bar;
echo $b->bar;
Is there any way I can change class variable, which would affect all instances created after?
I want to change one default value, but I don't want to keep repeating every time I instantiate a new object.
You'd need to make the variable static.
class foo
{
public static $bar = 1;
}
$a = new foo();
$a::$bar = 2;
$b = new foo();
echo $b::$bar; // 2
You can also do Foo::$bar = 2; to set the variable for all instances.
Having a static property works great if the constructor should be called each time. If you're attempting to chain invocations, however, you might use clone instead.
class Foo
{
public $bar = 1;
public function bar(?int $bar = null): Foo
{
$this->bar = $bar ?? $this->bar;
return $this;
}
public function copy(?int $bar = null): Foo
{
return $this->fork()->bar($bar ?? $this->bar);
}
public function fork(): Foo
{
return clone $this;
}
}
$a = new foo();
$a2 = $a->copy();
$b = $a->copy(2);
$b2 = $a->copy();
$c = $b->copy(3);
$c2 = $b->copy();
https://3v4l.org/Fevli
Note, there is also a __clone() magic method that allows you to configure the actual cloned properties that go along (e.g., reset or increment, etc).
Here's method using static::with() methodology, which might fit best for the DRY approach of preconfiguration.
class Foo
{
public const DEFAULT_BAR = 5;
public $bar = FOO::DEFAULT_BAR;
public function bar(?int $bar = null): Foo
{
$this->bar = $bar ?? $this->bar;
return $this;
}
static public function withBar(?int $bar = null): Foo
{
return (new self())->bar($bar ?? Foo::DEFAULT_BAR);
}
public function fromBar(?int $bar = null): Foo
{
return (new self())->bar($bar ?? $this->bar);
}
public function fork(): Foo
{
return clone $this;
}
}
$a = Foo::withBar();
$b = Foo::withBar(4);
$b2 = $b->fromBar();
$c = Foo::withBar();
https://3v4l.org/qI8SY
I'm having trouble with reading variable defined in one function in another function.
I have:
global $a;
class test{
function aa($somevar){
switch ($myvar){
case 'value':
global $a;
$a = 15;
break;
}
}
function bb(){
global $a;
echo $a;
}
}
$foo = new test();
$ccc = $foo->bb();
var_dump($ccc);
I get dump result NULL.
Thanx
At no point in your code do you assign a value to $a.
The only assignment to $a is in the test->aa method, which uses inconsistent variables and therefore even if called will never assign to $a.
You never run test->aa() to assign a value to a.
$foo = new test();
$foo->aa();
$ccc = $foo->bb();
In this case $ccc will still be null because you are echoing $a in $foo->bb() instead of returning it.
function bb() {
global $a;
return $a;
}
I would also stay away from globals and pass the variable $a on construct of the class. For example:
class test {
public $a;
function __construct($a = null) {
// pass initial var to $a if you want
$this->a = $a;
}
function aa($somevar) {
// reassign $a
$this->a = $somevar;
}
}
$foo = new test();
$foo->aa(5);
// or just $foo = new test(5);
var_dump($foo->a);
The variable $a should be a property inside the class
Here is code try this..
<?php
global $a;
class test{
function aa($somevar){
switch ($somevar){
case 'value':
global $a;
$a = 15;
break;
}
}
function bb(){
global $a;
echo $a;
return $a;
}
}
$foo = new test();
$foo->aa('value');
$ccc = $foo->bb();
var_dump($ccc);
?>
Try this :
class test
{
public $a;
function aa($somevar)
{
switch ($myvar)
{
case 'value':
$this->a = 15;
break;
}
}
function bb()
{
return $this->a;
}
}
$foo = new test();
$ccc = $foo->bb();
var_dump($ccc);
UPDATED:
<?php
class test
{
var $a;
function aa($somevar)
{
switch ($somevar)
{
case 'value':
$this->a = 15;
break;
}
}
function bb()
{
return $this->a;
}
}
$foo = new test();
$foo->aa('value');
$ccc = $foo->bb();
var_dump($ccc);
?>
i have something like this:
class foo
{
//code
}
$var = new foo();
$var->newVariable = 1; // create foo->newVariable
$var->otherVariable = "hello, im a variable"; //create foo->otherVariable
i can get in class foo a list of all variables defined outside by user (newVariable, otherVariable,etc)? Like this:
class foo
{
public function getUserDefined()
{
// code
}
}
$var = new foo();
$var->newVariable = 1; // create foo->newVariable
$var->otherVariable = "hello, im a variable"; //create foo->otherVariable
var_dump($var->getUserDefined()); // returns array ("newVariable","otherVariable");
Thanks!.
Yes, using get_object_vars() and get_class_vars():
class A {
var $hello = 'world';
}
$a = new A();
$a->another = 'variable';
echo var_dump(get_object_vars($a));
echo '<hr />';
// Then, you can strip off default properties using get_class_vars('A');
$b = get_object_vars($a);
$c = get_class_vars('A');
foreach ($b as $key => $value) {
if (!array_key_exists($key,$c)) echo $key . ' => ' . $value . '<br />';
}
What is your goal? Imo it's not very good practice (unless you really know what you are doing). Maybe it's good idea consider create some class property like "$parameters" and then create setter and getter for this and use it in this way:
class foo {
private $variables;
public function addVariable($key, $value) {
$this->variables[$key] = $value;
}
public function getVariable($key) {
return $this->variables[$key];
}
public function hasVariable($key) {
return isset($this->variables[$key]);
}
(...)
}
$var = new foo();
$var->addVariable('newVariable', 1);
$var->addVariable('otherVariable', "hello, im a variable");
And then you can use it whatever you want, for example get defined variable:
$var->getVariable('otherVariable');
To check if some var is already defined:
$var->hasVariable('someVariable')
get_class_vars() http://php.net/manual/en/function.get-class-vars.php
You question is not clear though.
$var->newVariable = 1;
there are two possible contex of above expression
1) you are accessing class public variables.
like
class foo
{
public $foo;
public function method()
{
//code
}
}
$obj_foo = new foo();
$obj_foo->foo = 'class variable';
OR
2) you are defining class variable runtime using _get and _set
class foo
{
public $foo;
public $array = array();
public function method()
{
//code
}
public function __get()
{
//some code
}
public function __set()
{
// some code
}
}
$obj_foo = new foo();
$obj_foo->bar= 'define class variable outside the class';
so in which context your question is talking about?
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
The scenario is this
class a
{
public $val;
}
class b extends a
{
}
class c extends b
{
}
$one = new b();
$one->val = "a value";
$other = new c();
echo $other->val;
// wanted 'a value', got ''
So the result i need here is: "a value", but of course is blank.
What i need is that the 'a' class to always be used as an instance in 'b'. So whenever i use a class that extends the 'b', the parent 'a' class to be inhereted as an instance.
If you read the php manual on the static keyword it gives an example of exactly what you are trying to do. You can read about it here: http://www.php.net/manual/en/language.oop5.static.php
Here is the example code they use.
<?php
class Foo
{
public static $my_static = 'foo';
public function staticValue() {
return self::$my_static;
}
}
class Bar extends Foo
{
public function fooStatic() {
return parent::$my_static;
}
}
print Foo::$my_static . "\n";
$foo = new Foo();
print $foo->staticValue() . "\n";
print $foo->my_static . "\n"; // Undefined "Property" my_static
print $foo::$my_static . "\n";
$classname = 'Foo';
print $classname::$my_static . "\n"; // As of PHP 5.3.0
print Bar::$my_static . "\n";
$bar = new Bar();
print $bar->fooStatic() . "\n";
?>
Since $other = new c(); is actually creating a new instance, it is not possible.
but if you declare val as Static member, you will have the result that you want.
<?
class a
{
public static $val;
}
class b extends a
{
}
class c extends b
{
}
$one = new b();
a::$val = "a value";
echo c::$val;
Here is how to do it without Inheritance:
class A
{
public $foo;
}
class B {
public function __construct(A $a)
{
$this->a = $a;
}
}
class C {
public function __construct(A $a)
{
$this->a = $a;
}
}
$a = new A;
$b = new B($a);
$c = new C($a);
$b->a->val = 'one value';
echo $c->a->val;
If you dont like having to fetch $a first to get to val, you could assign by reference
class A
{
public $foo;
}
class B {
public function __construct(A $a)
{
$this->val = &$a->val;
}
}
class C {
public function __construct(A $a)
{
$this->val = &$a->val;
}
}
$a = new A;
$b = new B($a);
$c = new C($a);
$b->val = 'one value';
echo $c->val;
Though personally I find the first approach more maintainable and clear.