Is PHP exists a function that detect the change of variable?
That is something like this:
//called when $a is changed.
function variableChanged($value) {
echo "value changed to " . $value;
}
$a = 1;
//attach the variable to the method.
$a.attachTo("variableChanged");
$a = 2;
$a = 3;
//expected output:
//value changed to 2
//value changed to 3
I know that it is easy to achieve if I use the "setter" method. But since I am working on some existing codes, I am not able to modify them. Can somebody tell me how to achieve my purpose? Thanks.
know that it is easy to achieve if I use the "setter" method. But since I am working on some existing codes, I am not able to modify them.
I assume that you can change some code, but not the object / class you are working with. If you cannot change any code at all this question would be useless.
What you can do is make your own class, extending the class you are working with, and adding your setter there. For all purposes you can not-override the parent setting, except for a magic setter on whatever you need to track. Track changes and then call the parent functions, so no changes in any other internal workings will be in effect.
This could only be achieved by wrapping your variable within a class, and implementing a onchange yourself.
ie.
class MyVarContainer {
var $internalVar = array();
function __get($name) {
return !empty($this->internalVar[$name]) $this->internalVar[$name] ? FALSE;
}
function __set($name, $value) {
$oldval = $this->$name;
$this->internalVar[$name] = $value;
if($oldval !== FALSE) {
onUpdated($name, $oldval, $value);
} else {
onCreated($name, $value);
}
}
function onCreated($name, $value) {
}
function onUpdated($name, $oldvalue, $newvalue) {
}
}
You could revised your code as simple like this just to produce that expected output you want.
function variableChanged($value) {
return "value changed to " . $value;
}
$a = 1;
echo $a = variableChanged(2);
echo '<br/>';
echo $a = variablechanged(3);
=================
//output
value changed to 2
value changed to 3
or using a class like this....
class VariableHandler{
private $Variable;
function setVariable($initialValue = NULL){
$this->Variable = $initialValue;
return $initialValue;
}
function changeValue($newValue = NULL){
$this->Variable = $newValue;
return "value has change to ". $newValue;
}
}
$var = new VariableHandler;
echo $a = $var->setVariable(1);
echo '<br/>';
echo $var->changeValue(2);
echo '<br/>';
echo $var->changeValue(3);
=================
//output
value changed to 2
value changed to 3
Besides using a debugger:
The SplObserver interface is used alongside SplSubject to implement
the Observer Design Pattern.
http://www.php.net/manual/en/class.splobserver.php
Or the magic methods __get() and __set(): Encapsulating the variable into a class, you could implement a event handler yourself and register the change of a variable. Also you could attach callbacks like here:
<?php
header("content-type: text/plain");
class WatchVar {
private $data = array();
private $org = array();
private $callbacks = array();
public function __set($name, $value) {
if (!array_key_exists($name, $this->data)) {
$this->org[$name] = $value;
} else {
//variable gets changed again!
$this->triggerChangedEvent($name, $value);
}
$this->data[$name] = $value;
}
public function &__get($name) {
if (array_key_exists($name, $this->data)) {
if ($this->data[$name] != $this->org[$name]) {
//variable has changed, return original
//return $this->org[$name];
//or return new state:
return $this->data[$name];
} else {
//variable has not changed
return $this->data[$name];
}
}
}
public function addCallback($name, $lambdaFunc) {
$this->callbacks[$name] = $lambdaFunc;
}
protected function triggerChangedEvent($name, $value) {
//$this->data[$name] has been changed!
//callback call like:
call_user_func($this->callbacks[$name], $value);
}
}
$test = new WatchVar;
$test->addCallback('xxx', function($newValue) { echo "xxx has changed to {$newValue}\n"; });
$test->xxx = "aaa";
echo $test->xxx . "\n";
//output: aaa
$test->xxx = "bbb";
//output: xxx has changed to bbb
echo $test->xxx . "\n";
//output bbb
function messyFunction(&$var) {
$var = "test";
}
messyFunction($test->xxx);
//output:
Related
I want to declare a variable inside a class with an unknown name
class Example {
function newVar($name, $value) {
$this->$name = $value;
}
}
And I want to use it that way
$c = new Example();
$c->newVar('MyVariableName', "This is my Value");
echo($c->MyVariableName);
The Important thing is, that I do not know the name of the variable. So I cannot put a public $MyVariable inside the class.
Is that in anyway possible? and if yes, can i do this with different scopes (private, protected, public) ?
U should use magic methods __get and __set (example without checking):
class Example {
private $data = [];
function newVar($name, $value) {
$this->data[$name] = $value;
}
public function __get($property) {
return $this->data[$property];
}
public function __set($property, $value) {
$this->data[$property] = $value;
}
}
$c = new Example();
$c->newVar('MyVariableName', "This is my Value");
echo($c->MyVariableName);
// This is my Value
$c->MyVariableName = "New value";
echo($c->MyVariableName);
// New value
See http://php.net/manual/en/language.oop5.magic.php
If i am understanding this correctly you can tweak a little bit by using key value array
class Example {
private $temp;
function __construct(){
$this->temp = array();
}
function newVar($name, $value) {
$this->temp[$name] = $value;
}
function getVar($name){
return $this->temp[$name];
}
}
$c = new Example();
$c->newVar('MyVariableName', "This is my Value");
echo($c->getVar('MyVariableName'));
Instead of using private you can use protected as well.
Your looking for magic calling. In PHP you can use the __call() function to do stuff like that. Have a look here: http://www.garfieldtech.com/blog/magical-php-call
Off the top of my head, something like
function __call($vari, $args){
if(isset($this->$vari){
$return = $this->$vari;
}else{
$return = "Nothing set with that name";
}
}
This will also work for private, protected and public. Can also use it to call methods as required in a class
Working on a project of translating website and I had chose this solution
.
I'm trying to accomplish something like :
$VAR1 = $translate->__('Word_To_Translate');
This, not works for me since, the result is directly shown in stdout of the webpage. Even so when trying to call $VAR1 no result is returned.
This is not easily possible with the class you've mentioned.
If you wish to edit the class so it'll return the value instead of echoing it, you can edit class.translation.php, replace the two occurances of echo $str; with return $str;, and replace echo $this->lang[$this->language][$str]; with return $this->lang[$this->language][$str] (simply changing echo to return on both instances).
//$VAR1 delegating
$VAR1 = $translate->__('Word_To_Translate');
//class.translation.php
`class Translator {
private $language = 'en';
private $lang = array();
public function __construct($language){
$this->language = $language;
}
private function findString($str) {
if (array_key_exists($str, $this->lang[$this->language])) {
return $this->lang[$this->language][$str];
return;
}
return $str;
}
private function splitStrings($str) {
return explode('=',trim($str));
}
public function __($str) {
if (!array_key_exists($this->language, $this->lang)) {
if (file_exists($this->language.'.txt')) {
$strings = array_map(array($this,'splitStrings'),file($this->language.'.txt'));
foreach ($strings as $k => $v) {
$this->lang[$this->language][$v[0]] = $v[1];
}
return $this->findString($str);
}
else {
return $str;
}
}
else {
return $this->findString($str);
}
}
}`
Switched the echo for a return
Thank you very much uri2x && Rizier123.
For the moment looks that it is working solution.
Best wishes !
Someone asked for an event handler that registeres variable changes in this question: PHP how to detect the change of variable?
I tried to develop a quick class with PHP's magic functions __get and __set. This works until I pass the member into a normal function by reference, it does not trigger the event anymore.
Is this a bug, or something that is not possible, or do I just miss something?
<?php
header("content-type: text/plain");
class WatchVar {
private $data = array();
private $org = array();
private $callbacks = array();
public function __set($name, $value) {
if (!array_key_exists($name, $this->data)) {
$this->org[$name] = $value;
} else {
//variable gets changed again!
$this->triggerChangedEvent($name, $value);
}
$this->data[$name] = $value;
}
public function &__get($name) {
if (array_key_exists($name, $this->data)) {
if ($this->data[$name] != $this->org[$name]) {
//variable has changed, return original
//return $this->org[$name];
//or return new state:
return $this->data[$name];
} else {
//variable has not changed
return $this->data[$name];
}
}
}
public function addCallback($name, $lambdaFunc) {
$this->callbacks[$name] = $lambdaFunc;
}
protected function triggerChangedEvent($name, $value) {
//$this->data[$name] has been changed!
//callback call like:
call_user_func($this->callbacks[$name], $value);
}
}
$test = new WatchVar;
$test->addCallback('xxx', function($newValue) { echo "xxx has changed to {$newValue}\n"; });
$test->xxx = "aaa";
echo $test->xxx . "\n";
//output: aaa
$test->xxx = "bbb";
//output: xxx has changed to bbb
echo $test->xxx . "\n";
//output bbb
function messyFunction(&$var) {
$var = "test";
}
messyFunction($test->xxx);
//output: nothing, why?
Altering this code it works:
function messyFunction(&$var) {
$var->xxx = "test";
}
messyFunction($test);
//output: xxx has changed to test
//output: nothing, why?
Even passed by reference, the function only recieves a clone of the member variable instead of the instance + magic functions.
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
Is there such a function like in_array, but can be used on objects?
Nope, but you can cast the object to an array and pass it into in_array().
$obj = new stdClass;
$obj->one = 1;
var_dump(in_array(1, (array) $obj)); // bool(true)
That violates all kinds of OOP principles though. See my comment on your question and Aron's answer.
First of all, arrays and objects are quite different.
A PHP object can not be iterated through like an array, by default. A way to implement object iteration is to implement the Iterator interface.
Concerning your specific question, you probably want to take a look at the ArrayAccess interface:
class obj implements ArrayAccess {
private $container = array();
public function __construct() {
$this->container = array(
"one" => 1,
"two" => 2,
"three" => 3,
);
}
public function offsetSet($offset, $value) {
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
public function offsetExists($offset) {
return isset($this->container[$offset]);
}
public function offsetUnset($offset) {
unset($this->container[$offset]);
}
public function offsetGet($offset) {
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
}
Now you can access your object like an array in the following manner:
$object = new obj();
var_dump(isset($obj['two'])); // exists!
var_dump(isset($obj['foo'])); // does not exist
Before you go crazy on this though, please consider why you are actually trying to do this and take a look at the examples at php.net.
Option 2: when you are simply trying to see if a property exists, you can use property_exists() for this:
class foo {
public $bar = 'baz';
}
$object = new foo();
var_dump(property_exists($object, 'bar')); // true
You could cast the object to an array:
$obj = new stdClass();
$obj->var = 'foobar';
in_array( 'foobar', (array)$obj ); // true
function in_object($needle, $haystack) {
return in_array($needle, get_object_vars($haystack));
}
It's unbelievable how all the people miss the point of the usefulness of an in_object PHP method! Here is what I came up with, it is very useful, and you will see why!
Here is a simple function I wrote which will check if a value can be found within an object.
<?php
// in_object method
// to check if a value in an object exists.
function in_object($value,$object) {
if (is_object($object)) {
foreach($object as $key => $item) {
if ($value==$item) return $key;
}
}
return false;
}
?>
This is very useful if an object has been created dynamically (especially from external code, which you don't control, as in an application-plugin, CMS, etc), and you don't know the object's properties.
The above function will return the property, so you will be able to use it in your code later on.
Here is a very good basic example of how useful this function is!
<?php
class My_Class {
function __construct($key, $value) {
$this->$key = $value;
// As you can see, this is a dynamic class, its properties and values can be unknown...
}
}
function in_object($value,$object) {
if (is_object($object)) {
foreach($object as $key => $item) {
if ($value==$item) return $key;
}
}
return false;
}
function manipulate_property($value,$object) {
if ($property = in_object($value,$object)) {
// value found. I can now use this property.
// I can simply echo'it (makes no sense, as I could instead simply echo "value")
echo "<br />I found the property holding this value: ".$object->$property;
// or (here comes the good part)
// change the property
$object->$property = "This is a changed value!";
echo "<br />I changed the value to: ".$object->$property;
// or return it for use in my program flow
return $property;
} else {
echo "Value NOT FOUND!<br />";
return false;
}
}
// imagine if some function creates the class conditionally...
if ( 1 == 1) {
$class = new My_Class("property","Unchanged Value");
} else {
$class = new My_Class("property","Some Other Value");
}
// now let's check if the value we want exists, and if yes, let's have some fun with it...
$property = manipulate_property("Unchanged Value",$class);
if ($property) {
$my_variable = $class->$property;
echo "<br />This is my variable now:".$my_variable;
} else $my_variable = $some_other_variable;
?>
Just run it to see for yourself!
I don't recommend it, because it's very bad practice but you can use get_object_vars.
Gets the accessible non-static properties of the given object according to scope.
There are other limitations you should refer to the documentation to see if it is suitable for you.
if(in_array('find me', get_object_vars($obj)))
This is the most efficient and correct solution. With some modifications it could be applied to check any data type present in any object.
if(gettype($object->var1->var2) == "string"){
echo "Present";
}