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.
Related
Here's a little mock-up to describe my predicament:
<?php
$var = "Before";
function getVar(){
global $var;
return $var;
}
$array = Array(
"variable" => "Var = " . getVar()
);
$var = "After";
echo $array['variable'];
?>
That code would echo 'Before', I'm aiming for it to echo 'after'. I realize that this is how PHP is supposed to work however it's crucial for the array to execute getVar() only when it's called.
How would I go about doing this?
You can not do this since array declaration will initialize it - so you're mixing function calling at array's 'usage' and at it's definition. There's no 'usage': array is already defined to that moment.
However, an answer could be using ArrayAccess, like this:
class XArray implements ArrayAccess
{
private $storage = [];
public function __construct()
{
$this->storage = func_get_args();
}
public function offsetSet($offset, $value)
{
if(is_null($offset))
{
$this->storage[] = $value;
}
else
{
$this->storage[$offset] = $value;
}
}
public function offsetExists($offset)
{
return isset($this->storage[$offset]);
}
public function offsetUnset($offset)
{
unset($this->storage[$offset]);
}
public function offsetGet($offset)
{
if(!isset($this->storage[$offset]))
{
return null;
}
return is_callable($this->storage[$offset])?
call_user_func($this->storage[$offset]):
$this->storage[$offset];
}
}
function getVar()
{
global $var;
return $var;
}
$var = 'Before Init';
$array = new XArray('foo', 'getVar', 'bar');
$var = 'After Init';
var_dump($array[1]);//'After Init'
-i.e. try to call data, which is inside element, when actual get happened. You may want to have different constructor (for associative arrays) - but the general idea was shown.
Editing my answer after the question was edited.
No, what you are trying to achieve isn't possible because when you call the function it returns and it's done at that point. But you could achieve something similar with object oriented coding. I'll create something for you, please wait.
<?php
class Foo {
public function __toString() {
global $var;
return "Var = {$var}";
}
}
$var = "Before";
$array = array( "variable" => new Foo() );
$var = "After";
echo $array['variable'];
?>
PS: Sorry for the late answer, but there was a blackout in Salzburg. :(
It occurred to me that you could also use anonymous functions and invoke/execute those
Proof of concept:
$var = "Before";
function getVar(){
global $var;
return $var;
}
$array = Array(
"variable" => create_function(null, "return 'Var = ' . getVar();")
);
$var = "After";
echo $array['variable']();
returns
Var = After
I want to have a function and then use it multiple times with different parameters.
For example:
<?php
class Test {
var $test;
public function func($val) {
$this->test = $val;
}
public function buildFunc() {
if(!empty($this->test)) {
$ret = $this->test;
}
return $ret;
}
}
?>
Then on calling page:
$test = new Test;
$test->func("test1");
$test->func("test2");
echo $test->buildFunc();
Then it prints test2 on the screen. And I want it to print out both of them.
Either create 2 instances of your object;
$test1 = new Test;
$test1->func("test1");
$test2 = new Test;
$test2->func("test2");
echo $test1->buildFunc();
echo $test2->buildFunc();
Or make test an array;
class Test {
var $test = array();
public function func($val) {
$this->test[] = $val;
}
public function buildFunc() {
return print_r($this->test, true);
}
}
May be you mean that you want to store all values? Then use an array:
public function func($val) {
$this->test[] = $val;
}
public function buildFunc() {
return $this->test
}
And then work with the result as with an array.
Well.. your code does exactly what are you telling it to do. Consider situation when you have no OOP:
$str = 'test 1';
$str = 'test 2';
echo $str; //prints test 2
So you need to echo them separately as if it wont be an OOP situation.
$test = new Test;
$test->func("test1");
echo $test->buildFunc();
$test->func("test2");
echo $test->buildFunc();
When calling the method create 2 instances of the test object.
$test = new Test;
$test->func("test1");
echo $test->buildFunc();
$test2 = new Test;
$test2->func("test2");
echo $test2->buildFunc();
if you dont want to create 2 instances you have to make a array instead.
How about create a constructor and initialize the value of test and concat the second value.
<?php
class Test {
var $test;
public function __construct($init){
$this->test = $init;
}
public function func($val) {
$this->test .= $val;
return $this;
}
public function buildFunc() {
if(!empty($this->test)) {
$ret = $this->test;
}
return $ret;
}
}
$test = new Test("test1");
$test->func("test2");
echo $test->buildFunc();
?>
When you say both do you mean something like
test1test2
or do you want
test1
test2
For the first option you can just append the string:
<?php
class Test {
var $test;
public function func($val) {
$this->test = $test . $val; <-- add val to the end
}
public function buildFunc() {
if(!empty($this->test)) {
$ret = $this->test;
}
return $ret;
}
}
?>
For the second:
<?php
class Test {
var $test = array();
public function func($val) {
$this->test[] = $val; <-- add val to
}
public function buildFunc() {
if(!empty($this->test)) {
foreach($test as $item){
echo $item . "<br/>";
}
}
}
}
?>
Push the variables to an array
<?php
class Test {
var $test;
public function __construct(){
$this->test=array();//Declare $test as an array
}
public function func($val) {
$this->test[]=$val;//Push to array
}
public function buildFunc() {
if(!empty($this->test)) {
$ret = implode(",",$this->test);
}
return $ret;
}
}
?>
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:
I am trying to build a function that will call another function.
For example, if I have an array full of function names to call, is it possible to call a function for every array value without writing it in a script?
Example:
function email($val=NULL) {
if($val)
$this->_email = $val;
else
return $this->_email;
}
function fname($val=NULL) {
if($val)
$this->_fname = $val;
else
return $this->_fname;
}
For email, fname, etc.
But I want to have it like:
function contr_val($key,$val) {
function $key($val=NULL) {
if($val)
$this->_$key = $val;
else
return $this->_$key;
}
function $key($val="hallo");
}
And call it with:
contr_val("email", "test")
You're really trying to create member variables dynamically and retrieve their values. This is what __get() and __set() are for.
Here's how you could use it:
class TestClass {
var $data = array();
public function __set($n, $v) { $this->data[$n] = $v; }
public function __get($n) {
return (isset($this->data[$n]) ? $this->data[$n] : null);
}
public function contr_val($k, $v = NULL) {
if ($v)
$this->$k = $v;
else
return $this->$k;
}
};
$sherp = new TestClass;
$sherp->contr_val("Herp", "Derp");
echo "Herp is: " . $sherp->contr_val("Herp") . "\n";
echo "Narp is: " . $sherp->contr_val("Narp") . "\n";
Something like this:
/*
Input: $val - any value
$varname - the variable name, for instance: _email
*/
function checkValue($val=NULL, $varname) {
if($val)
$this->$var = $val;
else
return $this->$var;
}
checkValue("hello", "_email");
checkValue("hello2", "_name");
If you are doing this for a class, consider using PHP's magic methods __get() and
__set().
In an array full of function names, this calls every function that exists.
ghoti#pc:~$ cat functest.php
#!/usr/local/bin/php
<?php
function one() { print "one\n"; }
function two() { print "two\n"; }
function three() { print "three\n"; }
$a=array( "one", "two", "three", "four" );
foreach ($a as $item) {
if (function_exists($item)) {
$item();
} else {
print "No such function: $item\n";
}
}
ghoti#pc:~$ ./functest.php
one
two
three
No such function: four
ghoti#pc:~$
You need to check if the function exists or not:
function contr_val($key,$val) {
if (!function_exists($key)) {
function $key($val=NULL) {
if ($val)
$this->_$key = $val;
}
}
else {
return $this->_$key;
}
}
How can i pass a class as a parameter in my function
So far i've tried
$sc = new SampleClass();
SampleFunction($sc);
function SampleFunction(&$refClass)
{
echo $refClass->getValue();
}
this is a simplified example of what im doing.. i actually have to do complex procedures inside this sample function. I'm not getting any response from the sample function. What am i doing wrong? thank you
UPDATE
char.php
class Charss {
var $name=0;
var $hp=500;
var $spd=10;
var $rtime=10;
var $dmg=10;
function __construct( $name, $hp, $spd, $rtime , $dmg) {
$this->name = $name;
$this->hp = $hp;
$this->spd = $spd;
$this->rtime = $rtime;
$this->dmg = $dmg;
}
function get_name() {
return $this->name;
}
function set_name($new_name) {
$this->name = $new_name;
}
function get_hp() {
return $this->hp;
}
function set_hp($new_hp) {
$this->hp = $new_hp;
}
function get_spd() {
return $this->spd;
}
function set_spd($new_spd) {
$this->spd = $new_spd;
}
function get_rtime() {
return $this->rtime;
}
function set_rtime($new_rtime) {
$this->rtime = $new_rtime;
}
function get_dmg() {
return $this->get_dmg;
}
function set_dmg($new_dmg) {
$this->dmg = $new_dmg;
}
}
myclass.php
require("char.php");
class Person {
function try_process()
{
$chr1 = new Charss("Player1",500,3,0,50);
$chr2 = new Charss("Player2",500,6,0,70);
while ($chr1->get_hp() > 0 && $chr2->get_hp() > 0)
{
$sth = min($chr1->get_rtime(), $chr2->get_rtime());
if ($chr1->get_rtime() == 0 && $chr2->get_rtime() > 0)
{
exit;
Fight($chr1,$chr2);
$chr1->set_rtime($chr1->get_spd());
}
elseif ($chr2->get_rtime() == 0 && $chr1->get_rtime() > 0)
{
Fight($chr2,$chr1);
$chr2->set_rtime($chr2->get_spd());
}
else
{
Fight($chr1,$chr2); #having trouble with this
$chr1->set_rtime($chr1->get_spd());
}
$chr1->set_rtime($chr1->get_rtime() - $sth);
$chr2->set_rtime($chr2->get_rtime() - $sth);
}
}
function Fight($atk,$def)
{
$def->set_hp($def->get_hp() - $atk->get_dmg());
echo $atk->get_name() . " attacked " . $def->get_name() . " for " . $atk->get_dmg() . " damage";
}
}
so im calling the function try_process on button click
What you're actually doing there is passing an object, not a class.
$sc = new SampleClass();
creates an instance of SampleClass, aka an object.
I assume there's some error being thrown elsewhere as what you have is correct.
I tested the following code and got the expected output:
class SampleClass
{
public function getValue()
{
return 4;
}
}
$sc = new SampleClass();
SampleFunction($sc);
function SampleFunction(&$refClass)
{
echo $refClass->getValue();
}
Output: 4
If you provide more details of your actual code we might be able to determine the problem.
I can't see anything wrong with your code
using &$refClass is however is not recommended and I guess willbe removed from future iteration of PHP version
but here is an example
class objects are passed as reference I suppose so no need of '&'
http://ideone.com/GbmUy
Why is the function argument a reference? Probably shouldn't be.
Other than that, there's nothing wrong with you posted, so the error is likely within SampleClass.
Others have answered pretty well, but this is a silly little example to show you how to modify the class (either by calling a property setter, or setting public properties directly)
class foo {
private $member1;
public $member2;
public function __construct($member1,$member2) {
$this->member1=$member1;
$this->member2=$member2;
}
public function SetMember1($value) {
$this->member1 = $value;
}
public function GetMember1() {
return $this->member1;
}
}
function SetMembers(foo $obj, $member1, $member2) {
// Call a setter
$obj->SetMember1($member1);
// Set a member variable directly
$obj->member2 = $member2;
}
$obj = new foo('default member 1', 'default member 2');
echo "member1 (before): {$obj->GetMember1()}\n";
echo "member2 (before): {$obj->member2}\n";
// Change values
SetMembers($obj, 'new member1', 'new member2');
echo "member1 (after): {$obj->GetMember1()}\n";
echo "member2 (after): {$obj->member2}\n";
This will output:
member1 (before): default member 1
member2 (before): default member 2
member1 (after): new member1
member2 (after): new member2