2013-05-22 174 views
4

我想知道是否有办法将变量侦听器添加到变量中。我所说的最简单的例子就是沿着这些方向发展;PHP:检测变量值何时发生变化

// 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"; 

现在你可能会问为什么在世界上我甚至会需要使用这种方法,为什么不只是做一个函数来设置cookie的打扰。答案是我有一个&__get($var)神奇的方法,返回一个多维数组元素的引用,我希望使用这样的东西来检测何时/如果数组元素或其子元素之一已被编辑,然后如果有,发送一个cookie。希望得到的结果会像这样工作;

$cookies->testArray["test"] = "newValue"; 
// This would send the cookie 'testArray' with the value '{"test":"newValue"}' 

我诚实地希望这是完全不可能的,如果我是正确的,我很抱歉。但是我刚刚学会了如何正确地使用引用,所以我想在我完全注销这个想法之前我会问。

感谢您收到的任何回复,无论是我期望的还是我期望的。

编辑:

为了增加清晰度,这里是为了什么,我试图完成的示例类;

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,'/',''); 
    } 
} 

至于为什么我不想有更新功能,它实际上只是个人偏好。这将在其他人可以扩展的框架中使用,并且我认为让他们能够将cookies作为单行代码中的变量来处理,从而变得更加轻松。

+0

我不认为这是可能的在PHP中。对于实现SplObserver接口的自定义类可能是一个选项,但您需要在进行更改时通知。 – Orangepill

+0

我不明白你的魔法获得方法与此有什么关系。另外,你的第二个代码示例似乎与你的第一个代码没有太大关系。我认为更好的代码示例会为我澄清事情。我只是抛出一些东西,希望它朝着正确的方向前进:__set()?!这样你可以看到变量何时变化。但是一旦我真的知道你想要什么,我将能够更好地帮助你。 PHP确实对Listener有一些功能。 –

+0

@MichaSchwab我用示例类更新了我的问题。 – MrFigg

回答

2

如果你想实现自己的事件处理程序/触发阵列或其他任何东西,那么你可以使用类包装。

对于一个变量__get()__set()魔术方法就足够了,正如您注意到的那样。 对于数组有一个更好的处理程序:ArrayAccess。它允许使用类方法和常用数组语义来模拟数组操作(如其他一些语言中的集合)。

<?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); 
?> 

表演:

"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" 

我认为,你可以修改的构造在此代码数组引用参数能够通过有超全局数组中,喜欢$_COOKIE。另一种方法是在课堂上直接替换$this->array$_COOKIE。此外,还有一种方法可以用anonymous functions替换可驯化的东西。

+0

即将发布相同的事情:-) +1! –

-3

我觉得你的思维有一些错误,因为他们只能通过变量的方式来改变它的值,如果是改变它。因此,当你改变你的变量时,运行你的代码。

如果您需要这样做的通用方法,我会建议将数组或其值封装到类中。但是,在每次更改值后,不能仅仅运行sendCookie()就没有什么好的理由。

我认为更改监听器适用于多线程编程。

+0

“你的想法错误”? OP可能会编译自己的PHP扩展,这可能会允许这样做。没有错误。 – BlitZ

+1

有人从未听说过观察者模式。 – cHao

+0

的确我还没有听说过。 – user1122069

0

怎么样永远不要改变变量。

只有通过函数(或类方法)才能访问变量,可以处理任何想要执行的“侦听”任务吗?

为什么你必须这样做:$variable = 5;

当你可以做到:setMyVariable(5)

function setMyVariable($new_value) { 
    $variable = $new_value; 
    // do other stuff 

}