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

    }

}

使用此函数 over $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- 如果您不喜欢那样,您必须手动实现它的许多接口 [ Iterator, ArrayAccess, 等等]。

于 2012-08-23T22:33:48.473 回答
0

到目前为止,这是我想出的,它适用于所有对象,但不适用于单个数组元素。当我使用反射修改对象时,数据被保留。

我当前执行此操作的代码片段仍然类似于我原来的概念:

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

我写的代码如下。在大多数情况下,我的所有嵌套对象都按预期更改并返回到 $mixed_datay_array 变量。我使用迭代器的 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 回答