I was wondering if there is a way to add something like a change listener to a variable. The simplest example of what I mean would work something along these lines;
// Start with a variable
$variable = "some value";
// Define a listener
function myChangeListener($variable) {
// encode with json_encode and send in cookie
}
// Add my listener to the variable
addListenerToVariable(&$variable, 'myChangeListener');
// Change the variables value, triggering my listener
$variable = "new value";
Now you're probably asking why in the world I would even need to bother with this approach, why not just make a function to set the cookie. The answer is I've got a &__get($var)
magic method that returns a reference to a multi-dimensional array element, and I am hoping to use something like this to detect when/if the array element or one of its children has been edited, then send a cookie if it has. The hoped for result would work something like this;
$cookies->testArray["test"] = "newValue";
// This would send the cookie 'testArray' with the value '{"test":"newValue"}'
I honestly expect this to be completely impossible, and I apologize if I'm correct. But I just learned how to use references correctly yesterday, so I thought I'd ask before I completely write off the idea.
Thanks for any responses I get, be they what I was hoping for or what I expected.
EDIT:
For added clarity, here's an example class for what I'm trying to accomplish;
class MyCookies {
private $cookies = array();
private $cookieTag = "MyTag";
public function __construct() {
foreach($_COOKIE as $cookie => $value) {
if(strlen($cookie)>strlen($this->cookieTag."_")&&substr($cookie,0,strlen($this->cookieTag."_"))==$this->cookieTag."_") {
$cookieVar = substr($cookie,strlen($this->cookieTag."_"));
$this->cookies[$cookieVar]['value'] = json_decode($value);
}
}
}
// This works great for $cookies->testArray = array("testKey" => "testValue");
// but never gets called when you do $cookies->testArray['testKey'] = "testValue";
public function __set($var, $value) {
if(isset($value)) {
$this->cookies[$var]['value'] = $value;
setcookie($this->cookieTag.'_'.$var,json_encode($value),(isset($this->cookies[$var]['expires'])?$this->cookies[$var]['expires']:(time()+2592000)),'/','');
} else {
unset($this->cookies[$var]);
setcookie($this->cookieTag.'_'.$var,'',(time()-(172800)),'/','');
}
return $value;
}
// This gets called when you do $cookies->testArray['testKey'] = "testValue";
public function &__get($var) {
// I want to add a variable change listener here, that gets triggered
// when the references value has been changed.
// addListener(&$this->config[$var]['value'], array(&$this, 'changeListener'));
return $this->config[$var]['value'];
}
/*
public function changeListener(&$reference) {
// scan $this->cookies, find the variable that $reference is the reference to (don't know how to do that ether)
// send cookie
}
*/
public function __isset($var) {
return isset($this->cookies[$var]);
}
public function __unset($var) {
unset($this->cookies[$var]);
setcookie($this->cookieTag.'_'.$var,'',(time()-(172800)),'/','');
}
public function setCookieExpire($var, $value, $expire=null) {
if(!isset($expire)) {
$expire = $value;
$value = null;
}
if($expire<time()) $expire = time() + $expire;
if(isset($value)) $this->cookies[$var]['value'] = $value;
$this->cookies[$var]['expires'] = $expire;
setcookie($this->cookieTag.'_'.$var,json_encode((isset($value)?$value:(isset($this->cookies[$var]['value'])?$this->cookies[$var]['value']:''))),$expire,'/','');
}
}
As for why I don't want to have an update function, it's really just personal preference. This is going to be used in a framework that other people can expand upon, and I think having them be able to treat the cookies as just variables in single lines of code feels slicker.
If you want to implement own event handlers / triggers in array or anything else, then you might use class wrapper.
For one variable __get()
and __set()
magic methods are enough, as you noticed.
For array there is a better handler: ArrayAccess
. It allows to emulate array actions with class methods and common array semantics (like collections in some other languages).
<?php
header('Content-Type: text/plain');
// ArrayAccess for Array events
class TriggerableArray implements ArrayAccess {
protected $array; // container for elements
protected $triggers; // callables to actions
public function __construct(){
$this->array = array();
// predefined actions, which are availible for this class:
$this->triggers = array(
'get' => null, // when get element value
'set' => null, // when set existing element's value
'add' => null, // when add new element
'exists' => null, // when check element with isset()
'unset' => null // when remove element with unset()
);
}
public function __destruct(){
unset($this->array, $this->triggers);
}
public function offsetGet($offset){
$result = isset($this->array[$offset]) ? $this->array[$offset] : null;
// fire "get" trigger
$this->trigger('get', $offset, $result);
return $result;
}
public function offsetSet($offset, $value){
// if no offset provided
if(is_null($offset)){
// fire "add" trigger
$this->trigger('add', $offset, $value);
// add element
$this->array[] = $value;
} else {
// if offset exists, then it is changing, else it is new offset
$trigger = isset($this->array[$offset]) ? 'set' : 'add';
// fire trigger ("set" or "add")
$this->trigger($trigger, $offset, $value);
// add or change element
$this->array[$offset] = $value;
}
}
public function offsetExists($offset){
// fire "exists" trigger
$this->trigger('exists', $offset);
// return value
return isset($this->array[$offset]);
}
public function offsetUnset($offset){
// fire "unset" trigger
$this->trigger('unset', $offset);
// unset element
unset($this->array[$offset]);
}
public function addTrigger($trigger, $handler){
// if action is not availible and not proper handler provided then quit
if(!(array_key_exists($trigger, $this->triggers) && is_callable($handler)))return false;
// assign new trigger
$this->triggers[$trigger] = $handler;
return true;
}
public function removeTrigger($trigger){
// if wrong trigger name provided then quit
if(!array_key_exists($trigger, $this->triggers))return false;
// remove trigger
$this->triggers[$trigger] = null;
return true;
}
// call trigger method:
// first arg - trigger name
// other args - trigger arguments
protected function trigger(){
// if no args supplied then quit
if(!func_num_args())return false;
// here is supplied args
$arguments = func_get_args();
// extract trigger name
$trigger = array_shift($arguments);
// if trigger handler was assigned then fire it or quit
return is_callable($this->triggers[$trigger]) ? call_user_func_array($this->triggers[$trigger], $arguments) : false;
}
}
function check_trigger(){
print_r(func_get_args());
print_r(PHP_EOL);
}
function check_add(){
print_r('"add" trigger'. PHP_EOL);
call_user_func_array('check_trigger', func_get_args());
}
function check_get(){
print_r('"get" trigger'. PHP_EOL);
call_user_func_array('check_trigger', func_get_args());
}
function check_set(){
print_r('"set" trigger'. PHP_EOL);
call_user_func_array('check_trigger', func_get_args());
}
function check_exists(){
print_r('"exists" trigger'. PHP_EOL);
call_user_func_array('check_trigger', func_get_args());
}
function check_unset(){
print_r('"unset" trigger'. PHP_EOL);
call_user_func_array('check_trigger', func_get_args());
}
$victim = new TriggerableArray();
$victim->addTrigger('get', 'check_get');
$victim->addTrigger('set', 'check_set');
$victim->addTrigger('add', 'check_add');
$victim->addTrigger('exists', 'check_exists');
$victim->addTrigger('unset', 'check_unset');
$victim['check'] = 'a';
$victim['check'] = 'b';
$result = $victim['check'];
isset($victim['check']);
unset($victim['check']);
var_dump($result);
?>
Shows:
"add" trigger
Array
(
[0] => check
[1] => a
)
"set" trigger
Array
(
[0] => check
[1] => b
)
"get" trigger
Array
(
[0] => check
[1] => b
)
"exists" trigger
Array
(
[0] => check
)
"unset" trigger
Array
(
[0] => check
)
string(1) "b"
I assume, that you might modify constructor in this code with array reference argument to be able to pass there superglobal array, like $_COOKIE
. Another way is to replace $this->array
with $_COOKIE
directly within class. Also, there is a way to replace callables with anonymous functions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With