Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Растяжение вероятностей массива в PHP

Anna | 31.05.2014 | нет комментариев

Массив в PHP — один из самых сильных типов данных. Он может трудиться как линейный массив (список), как ассоциативный (словарь) и как смешанный. В качестве ключа массива может применяться либо целое число, либо строка, причем строка, если она представляет собой целое число (скажем, «5»), будет конвертирована в целое. Остальные типы, за исключением массивов и объектов, так же конвертируются в целое либо строку — подробнее дозволено прочитать в документации.

Невзирая на сильные вероятности базового типа array изредка хочется их расширить. Скажем, сходственный кусок кода дозволено встретить, вероятно, в большинстве php-планов:

$foo = isset($array['foo']) ? $array['foo'] : null;
$bar = isset($array['bar']) ? $array['bar'] : null;

Один из методов сделать данный код короче и изящней — применять короткую запись тернарного оператора:

$foo = $array['foo'] ? : null;
$bar = $array['bar'] ? : null;

Но такой код выбросит PHP Notice в случе, когда ключ не определен, а я усердствую писать максимально чистый код — на сервере разработки выставлено error_reporting = E_ALL. И именно в сходственных случаях на поддержка приходит ArrayObject — класс, к объектам которого дозволено обращаться применяя синтаксис массивов и дозволяющий изменять поведение применяя механизм наследования.

Разглядим несколько примеров метаморфозы поведения.

В плане, над которым я теперь тружусь, мы используем следующие базовые преемники ArrayObject:

  • DefaultingArrayObject — возвращает значение по умолчанию, если ключ не определен в массиве;
  • ExceptionArrayObject — бросает исключение, если ключ не определен в массиве;
  • CallbackArrayObject — значения массива являются функциями (замыканиями), которые возвращают некое значение.

DefaultingArrayObject

Данный тип массива ведет себя приблизительно как словарь в Python при вызове dict.get(key, default) — если ключ не определен в массиве — возвращается значение по умолчанию. Это отменно работает в случае, когда значения по умолчанию у всех элементов, к которым мы обращаемся идентичные, и не так изящно, когда мы хотим получать различные значения в случае отсутствия ключа. Полный листинг этого класса выглядит дальнейшим образом:

Листинг класса DefaultingArrayObject

class DefaultingArrayObject extends ArrayObject  
{
    protected $default = null;

    public function offsetGet($index)
    {
        if ($this->offsetExists($index)) {
            return parent::offsetGet($index);
        } else {
            return $this->getDefault();
        }
    }

    /**
     * @param mixed $default
     * @return $this
     */
    public function setDefault($default)
    {
        $this->default = $default;
        return $this;
    }

    /**
     * @return mixed
     */
    public function getDefault()
    {
        return $this->default;
    }
}

Применяя данный класс, дозволено переписать код, тот, что я применял в качестве примера, дальнейшим образом:

$array = new DefaultingArrayObject($array);
$foo = $array['foo'];
$bar = $array['bar'];

В случае различных значений по-умолчанию будет выглядеть не так прекрасно, и вдалеке не факт что эта запись отменнее применения полной тернарной записи — легко покажу как это дозволено сделать (PHP 5.4 ):

$array = new DefaultingArrayObject($array);
$foo = $array->setDefault('default for foo')['foo'];
$bar = $array->setDefault('default for bar')['bar'];

ExceptionArrayObject

Как я теснее подмечал выше, PHP кинет Notice в случае отсутствия ключа в массиве, но изредка хочется контролировать это без применения кучи условных операторов, а логику выполнения контролировать при помощи исключений. Скажем, код вида:

if (isset($array['foo']) && isset($array['bar'] && isset($array['baz'])) {  
    // logic that uses foo, bar and baz array values
} else {
    // logic that does not use foo, bar and baz array values
}

Дозволено переписать дальнейшим образом:

$array = new ExceptionArrayObject($array);
try {
    // logic that uses foo, bar and baz array values
} catch (UndefinedIndexException $e) {
    // logic that does not use foo, bar and baz array values
}

Листинг класса ExceptionArrayObject

class ExceptionArrayObject extends ArrayObject  
{
    public function offsetGet($index)
    {
        if ($this->offsetExists($index)) {
            return parent::offsetGet($index);
        } else {
            throw new UndefinedIndexException($index);
        }
    }
}
class UndefinedIndexException extends Exception  
{
    protected $index;

    public function __construct($index)
    {
        $this->index = $index;
        parent::__construct('Undefined index "' . $index . '"');
    }

    /**
     * @return string
     */
    public function getIndex()
    {
        return $this->index;
    }
}

CallbackArrayObject

И напоследок еще один массив с нестандартным поведением. Вообще, данный тип массива абсолютно дозволено считать фабрикой, потому как элементы массива являются замыканиями (неизвестными функциями), которые вызываются при обращении к соответствующим элементам массива и итог их выполнения возвращается взамен самого элемента. Думаю, что проще будет показать это на примере:

$array = new CallbackArrayObject([
    'foo' => function() {
        return 'foo ' . uniqid();
    },
    'bar' => function() {
        return 'bar ' . time();
    },
]);

$foo = $array['foo']; // "foo 526afed12969d"
$bar = $array['bar']; //  "bar 1382743789" 

Листинг класса CallbackArrayObject

class CallbackArrayObject extends ArrayObject  
{
    protected $initialized = array();

    public function __construct(array $values)
    {
        foreach ($values as $key => $value) {
            if (!($value instanceof Closure)) {
                throw new RuntimeException('Value for CallbackArrayObject must be callback for key ' . $key);
            }
        }
        parent::__construct($values);
    }

    public function offsetGet($index)
    {
        if (!isset($this->initialized[$index])) {
            $this->initialized[$index] = $this->getCallbackResult(parent::offsetGet($index));
        }
        return $this->initialized[$index];
    }

    protected function getCallbackResult(Closure $callback)
    {
        return call_user_func($callback);
    }
}

В данном случае итог выполнения функции кэшируется и вторично функция вызвана не будет. Но сферические итоги в вакууме бывают увлекательны весьма редко, следственно дозволено сделать их настраиваемыми, зависящими от какого-нибудь конфига:

$array = new ConfigurableCallbackArrayObject([
    'foo' => function($config) {
        return 'foo ' . $config['foo'];
    },
    'bar' => function($config) {
        return 'bar ' . $config['bar'];
    },
]);
$array->setConfig(['foo' => 123, 'bar' => 321]);

$foo = $array['foo']; // "foo 123"
$bar = $array['bar']; //  "bar 321" 

Листинг класса ConfigurableCallbackArrayObject

class ConfigurableCallbackArrayObject extends CallbackArrayObject
{
    protected $config;

    protected function getCallbackResult(Closure $callback)
    {
        return call_user_func($callback, $this->getConfig());
    }

    public function setConfig($config)
    {
        $this->config = $config;
    }

    public function getConfig()
    {
        return $this->config;
    }
}

Это все, что я хотел рассказать о примерах применении ArrayObject.

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB