0

RecursiveIteratorIterator クラスを使用して、すべての子オブジェクトを反復処理して、特定のキーが特定の列のリストと一致するかどうかを確認しようとしています。キー/列が一致したら、テキストを変更します。ただし、各要素にオブジェクトがある多次元配列にアクセスできるようにしたいと考えています。

配列とオブジェクトをトラバースし、下位の子レベルを反復処理してキー名を確認する方法を探しています。キー名が一致する場合、コールバック関数またはある種の正規表現/置換関数を実行したい場合があります。

データは次のようになります。



[0] => Array
        (
            [01__BLAH_A] => 1
            [01__BLAH_B] => 0
            [01__BLAH_C] => 1
            [01__BLAH_D] => 1
            [01__BLAH_E] => 1
            [01__BLAH_F] => 1
            [01__BLAH_G] => 0
            [01__BLAH_H] => 3
            [01__BLAH_I] => 0
            [01__BLAH_J] => 1
            [01__BLAH_K] => 1
            [01__BLAH_L] => 1
            [01__BLAH_M] => 3
            [SOME_OBJECT] => some_object Object
            (
                    [variable_1:some_type:private] => 
                    [variable_2:some_type:private] => 
                    [my_data:protected] => Array
                    (
                            [BLAH_1_A] => nAME
                            [BLAH_1_B] => blahblah
                            [BLAH_1_C] => other_dude
                            [BLAH_1_D] => 1
                            [BLAH_1_E] => 55
                            [BLAH_1_F] => 1
                            [BLAH_1_G] => null
                            [BLAH_1_H] => 1234567989
                    )

            )
        [SOME_OTHER_OBJECT] => some_other_object Object
            (
                    [variable_1:some_type:private] => 
                    [variable_2:some_type:private] => 
                    [my_data:protected] => Array
                    (
                            [BLAH_2_A] => nAME of another
                            [BLAH_2_B] => fofofofo
                            [BLAH_2_C] => right_dude
                            [BLAH_2_D] => 1
                            [BLAH_2_E] => 33
                            [BLAH_2_F] => 2
                            [BLAH_2_G] => 0
                            [BLAH_2_H] => 987654321
                    )
            )
        )
[1] => Array
        (
            [02__BLAH_A] => 1
            [02__BLAH_B] => 0
            [02__BLAH_C] => 1
            [02__BLAH_D] => 1
            [02__BLAH_E] => 1
            [02__BLAH_F] => 1
            [02__BLAH_G] => 0
            [02__BLAH_H] => 3
            [02__BLAH_I] => 0
            [02__BLAH_J] => 1
            [02__BLAH_K] => 1
            [02__BLAH_L] => 1
            [02__BLAH_M] => 3
            [SOME_OTHER_OBJECT] => some_other_object Object
            (
                    [variable_1:some_type:private] => 
                    [variable_2:some_type:private] => 
                    [my_data:protected] => Array
                    (
                            [BLAH_2_A] => nAME of another
                            [BLAH_2_B] => fofofofo
                            [BLAH_2_C] => right_dude
                            [BLAH_2_D] => 1
                            [BLAH_2_E] => 33
                            [BLAH_2_F] => 2
                            [BLAH_2_G] => 0
                            [BLAH_2_H] => 987654321
                    )
            )
        )

データには、オブジェクトの基本的な配列と、いくつかの要素とネストされたオブジェクトがあることに注意してください。各配列要素に異なるオブジェクト型が存在する場合があります。これらすべての要素にとらわれない反復子が必要です。

RecursiveIteratorIterator で正しい道を進んでいると思っていましたが、それらのオブジェクトに到達したときに障害物にぶつかりました。

class Modifiable_Iterator
extends RecursiveIteratorIterator
{
    private $char_set;
    private $columns_to_check = array();

    static function make($mixed_array_data)
    {
        return new self($mixed_array_data); 
    }

    function __construct($data)
    {
        parent::__construct(
            new RecursiveArrayIterator($data), 
            RecursiveIteratorIterator::SELF_FIRST, 
            null);

        $this->columns_to_check = array('BLAH_2_A', 'BLAH_1_A');        
    }

    final public function current($parent_key_name = null)
    {
        // Retrieves the current value
        $current    = parent::current();

        if (in_array($this->key(), $this->columns_to_check))
        {
            // If the column name matches the list of columns in the private
            // variable, then edit the column value

            return _some_function_that_edits_this_value($current);
        }

        return $current;
    }

    final public function exec() 
    {
        $this->_loop_check($this);

        return $this;
    }

    final private function _loop_check($iterator)
    {
        while ($iterator->valid())
        {
            if ($iterator->hasChildren())
            {
                $this->_loop_check($iterator->getChildren());
            }

            $this->offsetSet($iterator->key(), $this->current());

            $iterator->next();
        }
    }    

    final private function _some_function_that_edits_this_value($value)
    {
        // Do something to the value and return it.

        return $value;
    }
}

混合データ オブジェクトを取得して、このコードを次のように実行できるようにしたいと考えています。

$new_text = Modifiable_Iterator::make($mixed_bag_of_data)->exec();
4

2 に答える 2

0

ArrayIteratorを試す:

class YourClass extends ArrayIterator{

    public function __construct($data)
    {

        parent::__construct($data);

        // your further logic

    }

}

データで満たされたインスタンスを提供しましょう:

$family = new YourClass(array(

        'name' => 'John',
        'surname' => 'Doe',
        'myAwesomeFriend' => new YourClass(array(

            'name' => 'Jane',
            'surname' => 'Doe'          

        ))

));

次に、このデータをクロールする方法を見つけます。

// we search the $element for keys in $keyList, then call the $callback eventually
function map($callback, $element, $keyList){

    foreach($element as $key => &$value)
    {

        //if we need to iterate on the field, we do again
        if($value instanceof ArrayIterator)
            map($callback, $value, $keyList);

        // else if key is in given keylist, we apply callback
        else if( in_array( $key, $keyList) )
            $value = call_user_func($callback, $value);

    }

}

でこの関数を使用して$family、指定されたキーを検索し、name値を小文字に変換します。

map(function($element){

    return strtolower($element);

}, $family, array('name'));

var_dump($family)何が出力されるか見てみましょう。

object(YourClass)#1 (1) {
  ["storage":"ArrayIterator":private]=>
  array(3) {
    ["name"]=>
    string(4) "john"
    ["surname"]=>
    string(3) "Doe"
    ["myAwesomeFriend"]=>
    object(YourClass)#2 (1) {
      ["storage":"ArrayIterator":private]=>
      array(2) {
        ["name"]=>
        string(4) "jane"
        ["surname"]=>
        string(3) "Doe"
      }
    }
  }
}

ケースを降ろし、ミッションを完了しました。

警告: オブジェクト/サブオブジェクトを拡張する必要があります。ArrayIteratorそのように感じない場合は、そのインターフェイス [ IteratorArrayAccessなど] の多くを手動で実装する必要があります。

于 2012-08-23T22:33:48.473 に答える
0

これまでのところ、これは私が思いついたものであり、すべてのオブジェクトで機能しますが、個々の配列要素では機能しません。リフレクションを使用してオブジェクトを変更すると、データが保持されます。

これを実行するための現在のコード スニペットは、元のコンセプトに似ています。

Replacer::make($mixed_data_array)->exec();

そして、私が書いたコードは以下のとおりです。ほとんどの場合、ネストされたオブジェクトはすべて期待どおりに変更され、$mixed_datay_array 変数に返されます。Iterators の offsetSet() メソッドを使用して変更している他の配列要素はすべて保存されていません。「魔法の &」を使用する必要があるかどうかわかりませんが、これはこのシナリオには当てはまらないようです... それとも、別のリフレクションを行う必要がありますか? ヘルプ!

(以下のコード)

class Replacer
extends RecursiveIteratorIterator 
{
    private $char_set;
    private $char_set_exclude = array();
    private $columns_to_check = array();
    private $columns_with_titles = array();
    private $data;
    private $title_prefix;

    static function make($mixed_array_data)
    {
        return new self(
            is_null($mixed_array_data) ? array() : $mixed_array_data);
    }

    function __construct($data)
    {
        parent::__construct(new RecursiveArrayIterator($data),
                            RecursiveIteratorIterator::SELF_FIRST,
                            null);

        $this->char_set = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
                          . 'abcdefghijklmnopqrstuvwxyz';

        $this->char_set_exclude =
            array('@',
                  ' ',
                  '.');

        $this->columns_to_check =
            array('email_address',
                  'member_name');

        $this->data = $data;
    }

    final public function replace_text($key, $value)
    {
        if (is_array($value))
            {
            return $value;
        }

        $new_text = null;

        if (in_array(strtolower($key), $this->columns_to_check))
        {
            $cypher = str_shuffle($this->char_set);

            // Each character in the string is replaced with shuffled text.
            for ($i = 0; $i < strlen($value); $i += 1)
            {
                if (in_array($value[ $i ], $this->char_set_exclude))
                {
                    $new_text .= $value[ $i ];
                }
                else
                {
                    $new_text .=
                        $cypher[strpos($this->char_set, $value[$i])];
                }
            }
        }
        else
        {
            $new_text = $value;
        }

        return $new_text;
    }

    final public function exec()
    {
        $this->rewind();

        if (!$this->valid())
        {
            return $this->process_child_object($this->data);
        }

        return $this->begin_replace($this)->get_data();
    }

    final public function get_data()
    {
        return $this->data;
    }

    final public function set_columns_to_check($columns_array)
    {
        if (!is_array($columns_array) && !empty($columns_array)
        {
            $columns_array = array($columns_array);
        }

        $this->columns_to_check = $columns_array;

        return $this;
    }

    final private function begin_replace($data)
    {
        while ($data->valid())
        {
            // If there are any child elements, repeat procedure for subobjects.
            if ($data->hasChildren())
            {
                $this->begin_replace($data->getChildren());
            }

            if (is_object($data->current()))
            {
            // If the current element is an object, process it 
                // differently: using Refelection

                $this->process_child_object($data->current());
            }
            else
            {
                // Used built-in offsetSet method to update the data keys if
                // the array element isn't in a private/protected object and
                // the key name is also in the columns_to_check array.

                $data->offsetSet(
                    $data->key(),
                    $this->replace_text($data->key(), $data->current()));
            }
            $data->next();
        }

        return $data;
    }

    final private function process_child_object($object)
    {
        if (!is_object($object))
        {
            return $object;
        }

        // Used Reflection Object and Property to make the data object
        // accessible for reading and updating.
        $ref_object = new ReflectionObject($object);

        foreach ($ref_object->getProperties(
                    ReflectionProperty::IS_PROTECTED) as
                        $protected_property)
        {
            $data_array = array();

            $ref_prop = $ref_object->getProperty(
                            $protected_property->name);
            $ref_prop->setAccessible('true');

            foreach ($ref_prop->getValue($object) as
                         $key => $value)
            {
                $data_array[$key] = $this->replace_text($key, $value);
            }
            $ref_prop->setValue($object, $data_array);
        }

        return $object;
    }
}
于 2012-08-29T13:59:00.233 に答える