6

変更リスナーのようなものを変数に追加する方法があるかどうか疑問に思っていました。私が言いたいことの最も単純な例は、これらの線に沿って何かを機能させるでしょう。

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

おそらく、なぜ私はこのアプローチを気にする必要があるのか​​ 、なぜクッキーを設定する関数を作成しないのかと尋ねているでしょう。答えは&__get($var)、多次元配列要素への参照を返す魔法のメソッドを持っていることです。このようなものを使用して、配列要素またはその子の 1 つがいつ編集されたかを検出し、送信したいと考えています。ある場合はクッキー。期待される結果は次のようになります。

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

なぜアップデート機能を使いたくないのかというと、あくまで個人的な好みです。これは、他の人が拡張できるフレームワークで使用される予定であり、Cookie を 1 行のコードで単なる変数として扱うことができるようにすると、より洗練されたものになると思います。

4

3 に答える 3

2

独自のイベント ハンドラー/トリガーを配列などに実装する場合は、クラス ラッパーを使用できます。

お気づきのように、1 つの変数とマジック メソッドで十分です __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直接置き換えることです。また、callable を無名関数に置き換える方法もあります。

于 2013-05-22T03:30:04.243 に答える
0

変数をまったく変更しないのはどうですか。

実行したい「リスニング」タスクを処理できる関数 (またはクラス メソッド) を介してのみ変数にアクセスするのはどうですか?

なぜしなければならないのか: $variable = 5;?

あなたができるとき:setMyVariable(5)?

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

}
于 2013-05-22T03:17:00.560 に答える
-3

phpで変数の値を変更できるのは、変数を変更した場合だけであるため、考え方に誤りがあると思います。したがって、変数を変更するときは、コードを実行してください。

これを行う一般的な方法が必要な場合は、配列またはその値をクラスにカプセル化することをお勧めします。しかし、値が変更されるたびに sendCookie() を実行できない正当な理由はありません。

変更リスナーは、マルチスレッド プログラミング用だと思います。

于 2013-05-22T02:49:45.893 に答える