Алгоритм Php — как достичь этого без eval

#php #arrays #algorithm

#php #массивы #алгоритм

Вопрос:

У меня есть класс, который хранит данные для хранения / доступа к данным с помощью ключей words.separated.by.dots, и он ведет себя следующим образом:

     $object = new MyArray()
    $object->setParam('user.name','marcelo');
    $object->setParam('user.email','some@email.com');

    $object->getParams();

    /*
    array(
        'user' => array(
            'name' => 'marcelo',
            'email' => 'some@email.com'
        )
    );
    */
  

Он работает, но метод unsetParam() был ужасно реализован. Это произошло потому, что я не знал, как добиться этого без функции eval (). Хотя он работает, я обнаружил, что это действительно сложный алгоритм и что вам может показаться забавным пытаться достичь этого без eval().

 class MyArray {
   /**
     * @param string $key
     * @return Mura_Session_Abstract 
     */
    public function unsetParam($key)
    {
        $params = $this->getParams();
        $tmp = $params;
        $keys = explode('.', $key);

        foreach ($keys as $key) {
            if (!isset($tmp[$key])) {
                return $this;
            }
            $tmp = $tmp[$key];
        }

        // bad code!
        $eval = "unset($params['" . implode("']['", $keys) . "']);";
        eval($eval);

        $this->setParams($params);
        return $this;
    }
}
  

Метод тестирования:

 public function testCanUnsetNestedParam()
{
    $params = array(
        '1' => array(
            '1' => array(
                '1' => array(
                    '1' => 'one',
                    '2' => 'two',
                    '3' => 'three',
                ),
                '2' => array(
                    '1' => 'one',
                    '2' => 'two',
                    '3' => 'three',
                ),
            )
        ),
        '2' => 'something'
    );

    $session = $this->newSession();
    $session->setParams($params);

    unset($params['1']['1']['1']);
    $session->unsetParam('1.1.1');

    $this->assertEquals($params, $session->getParams());
    $this->assertEquals($params['1']['1']['2'], $session->getParam('1.1.2'));
}
  

Ответ №1:

Это все?

 <?php
$params = array(
    '1' => array(
        '1' => array(
        '1' => array(
            '1' => 'one',
            '2' => 'two',
            '3' => 'three',
        ),
        '2' => array(
            '1' => 'one',
            '2' => 'two',
            '3' => 'three',
        ),
        )
    ),
    '2' => 'something'
    );

function unsetParam( amp;$array, $paramString ) {
$cur =amp; $array;
$splitted = explode( ".", $paramString );
$len = count( $splitted ) - 1;

    for( $i = 0; $i < $len;   $i ) {

        if( isset( $cur[ $splitted[ $i ] ] ) ) {
        $cur =amp; $cur[ $splitted[ $i ] ];
        }
        else {
        return false;
        }


    }

unset( $cur[ $splitted[$i] ] );


}

unsetParam( $params, "1.1.1");

print_r( $params );

/*
Array
(
    [1] => Array
    (
        [1] => Array
        (
            [2] => Array
            (
                [1] => one
                [2] => two
                [3] => three
            )

        )

    )

    [2] => something
)
*/
  

Комментарии:

1. творческое использование ссылок 🙂

2. Очень приятно! Большое вам спасибо!

Ответ №2:

Вы могли бы упростить свой код, если бы в вашем getParams методе вы разбивали только на многомерный массив:

 class MyArray {
   private $params = array();

   public function setParam($key, $value) {
       $this->params[$key] = $value;
   }
   /**
     * @param string $key
     * @return Mura_Session_Abstract 
     */
    public function unsetParam($key)
    {
        unset($this->params[$key]);
        return $this;
    }

    public function getParams() {
        $retval = array();
        foreach ($this->params as $key => $value) {
            $aux = amp;$retval;
            foreach (explode(".", $key) as $subkey) {
                if (!isset($aux[$subkey])) $aux[$subkey] = array();
                $aux = amp;$aux[$subkey];
            }
            $aux = $value;
        }
        return $retval;
    }
}
  

Комментарии:

1. Это, безусловно, лучший ответ, имхо; ключи уникальны, так почему многомерный массив вообще полезен? Просто сохраните / извлеките с помощью string.with. точки, если только нет какой-то убедительной детали, о которой мы еще не слышали?

2. Многомерный массив лучше соответствует моим потребностям. Мой класс был очень похож на приведенный выше, и в итоге я изменился.

3. @Kato, давайте доведем ваш аргумент до логического завершения: зачем вообще нужен весь этот класс? 😀

4. @Kato это моя точка зрения. Он беспокоился о внутреннем поведении, когда это не имеет значения. Только getParams это имело бы некоторое значение в возвращаемом значении…

5. @Esailija Похоже, что это класс конфигурации. Для меня он хочет сделать что-то вроде шаблона реестра…

Ответ №3:

@gustavotkg и @Esailija оба предложили несколько отличных идей. Вот еще один простой, понятный и краткий подход, который позволяет вообще избежать функции unset() (которая в некоторых случаях может стать причудливой).

Это, конечно, было бы наиболее полезно, когда $ params ограничен значениями, составляющими менее, скажем, 1-10 кб (что начинает становиться немного дороже в отделе процессора / памяти):

 <?php

$params = array(
   '1' => array(
      '1' => array(
         '1' => array(
            '1' => 'one-one',
            '2' => 'one-two',
            '3' => 'one-three',
         ),
         '2' => array(
            '1' => 'two-one',
            '2' => 'two-two',
            '3' => 'two-three',
         ),
      )
   ),
   '2' => 'something'
);

function filterParams($params, $refKey, $base = '') {
   $newvals = array();
   foreach($params as $k=>$v) {
      $joinedKey = $base? $base . '.' . $k : $k;
      if( $joinedKey != $refKey ) {
         $newvals[$k] = is_array($v)? filterParams($v, $refKey, $joinedKey) : $v;
      }
   }
   return $newvals;
}

var_dump(filterParams($params, '1.1.2'));