Комментарии 30
$array = new CallbackArrayObject([
'foo' => function() {
return 'foo ' . uniqid();
},
'bar' => function() {
return 'bar ' . time();
},
]);
$foo = $array['foo']; // "foo 526afed12969d"
$bar = $array['bar']; // "bar 1382743789"
Очень страшно!) Никак не ожидаешь что простой вывод данных из массива может потянуть за собой кучу действий, о которых я могу только гадать (запросы в БД, запись/удаление файлов и т.д.).
public function offsetGet($index)
{
if (!isset($this->initialized[$index])) {
$this->initialized[$index] = $this->getCallbackResult(parent::offsetGet($index));
}
return $this->initialized[$index];
}
Здесь если метод getCallbackResult вернет null, то повторно будет выполнен код, isset()
Лучше использовать array_key_exists(), так точно отработает один раз.
0
Если такое встретить в коде то совсем непонятно что тут происходит
Мне куда понятней было бы увидеть:
$array = new DefaultingArrayObject($array);
$foo = $array->setDefault('default for foo')['foo'];
Мне куда понятней было бы увидеть:
$array = new DefaultingArrayObject($array);
$foo = $array->get('foo', 'default for foo');
+8
Согласен, и я указал что это не самая лучшая форма записи. То, что так можно делать, совсем не значит что так делать нужно. Насчет вашего варианта — это уже не работа с массивом, а работа с объектом и это уже немного другая история — такое можно организовать и не на базе ArrayObject, а просто классом, инкапсулирующим массив.
+2
Каждый под себя что-то делает
Я, например, использую что-то вроде:
Я, например, использую что-то вроде:
<?php
function valueFromArray(array $data, $map, $default = '')
{
if(!is_array($map))
{
$map = array($map);
}
foreach($map as $m)
{
if(is_array($data))
{
if(array_key_exists($data[$m]))
{
$data = $data[$m];
continue;
}
else
{
$data = $default;
break;
}
}
break;
}
return $data;
}
echo valueFromArray($array, 'name');
echo valueFromArray($array, 'email', 'empty');
echo valueFromArray($array, array('settings', 'auth'), 'default');
?>
-1
Отличная статья! Ничего кроме первых двух примеров не знал. Очень полезно, спасибо.
0
думаю лучше будет так
или так
protected function getCallbackResult(\Closure $callback)
{
return $callback();
}
или так
protected function getCallbackResult(callable $callback)
{
return call_user_func($callback);
}
+2
Чем лучше?
0
Если использовать тайп хинт Closure, то лаконичнее запись $callback().
Если использовать тайп хинт callable, то можно передавать не только анонимные функции, но и такие штуки:
Если использовать тайп хинт callable, то можно передавать не только анонимные функции, но и такие штуки:
call_user_func('my_callback_function');
call_user_func(array('MyClass', 'myCallbackMethod'));
call_user_func(array($obj, 'myCallbackMethod'));
call_user_func('MyClass::myCallbackMethod');
+2
Хинт callable можно использовать только с 5.4+, а поскольку 5.3 будет официально поддерживаться еще до 2014-07, то, возможно, рановато отказываться от его поддержки в приложениях. Плюс к этому неплохо было бы проверить на то, а валидный ли колбек передали. С учетом сказанного, я бы переписал это вот так:
protected function getCallbackResult($callback)
{
if (is_callable($callback)) {
return call_user_func($callback);
}
throw new \Exception('Invalid callable provided');
}
Разумеется, что можно изменить Exception — на свой тип, или же просто возвращать null.
Кстати, этот метод имеет один, возможно, неприятный побочный эффект. А именно: внутри класса is_callable() будет true даже для private и protected методов. Соответственно существует возможность передать туда array($classInstance, 'privateOrProtectedMethod') и оно будет успешно выполнено. Так что нужно иметь ввиду данную особенность.
protected function getCallbackResult($callback)
{
if (is_callable($callback)) {
return call_user_func($callback);
}
throw new \Exception('Invalid callable provided');
}
Разумеется, что можно изменить Exception — на свой тип, или же просто возвращать null.
Кстати, этот метод имеет один, возможно, неприятный побочный эффект. А именно: внутри класса is_callable() будет true даже для private и protected методов. Соответственно существует возможность передать туда array($classInstance, 'privateOrProtectedMethod') и оно будет успешно выполнено. Так что нужно иметь ввиду данную особенность.
0
Очень спорная реализация, но идея наследования от базовых классов для заточки их под себя оправдывает написание статьи.
+1
К моему стыду крайнему удивлению, все мои эксперименты попытки внедрить SPL/ArrayObject в реальный проект заканчивались примерно таким вот образом:
0
<?php
$a = new ArrayObject();
echo (int)is_array($a);
0
+2
Да, есть такое, но оно и не удивительно — массив это отдельный тип данных.
Насчет внедрения — я считаю что переход от массивов к ArrayObject дело, в общем случае, не то чтобы не полезное, но даже вредное. Использовать объекты имеет смысл только в отдельных конкретных случаях, когда есть понимание, что с ними будет проще, чем без них.
Насчет внедрения — я считаю что переход от массивов к ArrayObject дело, в общем случае, не то чтобы не полезное, но даже вредное. Использовать объекты имеет смысл только в отдельных конкретных случаях, когда есть понимание, что с ними будет проще, чем без них.
+2
Тут еще подводный камень в том, что стандартные array_XXXXX функции не будут работать с этим объектом. Нужно кастать к array, но и на выходе получим array, а не arrayObject. Так что если использовать arrayObject, то нужно использовать его везде.
+1
Скопирую свой комментарий чуть выше:
Насчет внедрения — я считаю что переход от массивов к ArrayObject дело, в общем случае, не то чтобы не полезное, но даже вредное. Использовать объекты имеет смысл только в отдельных конкретных случаях, когда есть понимание, что с ними будет проще, чем без них.
+1
Тут чем дальше под воду, тем толще камни.
В результате — с SPL/ArrayObject приходится четко мониторить, чтобы порожденные объекты не выходили за пределы «своего» кода и не пытались использоваться каким-либо другим образом.
is_array("scalar") // false;
is_array((array)"scalar") // true; счастливой отладки
В результате — с SPL/ArrayObject приходится четко мониторить, чтобы порожденные объекты не выходили за пределы «своего» кода и не пытались использоваться каким-либо другим образом.
+1
а что собственно в вашем примере «камни»?
(array)«scalar» в данном случае преобразуется в array(0 => 'scalar')
(array)«scalar» в данном случае преобразуется в array(0 => 'scalar')
0
Предположим, что переменная не объявлена парой строчек выше, а приходит извне (и мы ожидаем массив), и нам ее нужно передать в функцию array_foo().
До приведения:
* если $bar — массив, все ок
* если $bar is ArrayObject — получаем варнинг
* иначе — получаем варнинг
После приведения:
* если $bar — массив, все ок
* если $bar is ArrayObject — все ок
* иначе — счастливой отладки
В итоге код начинает пестрить костылями типа ($foo instanceof ArrayObject) || is_array($foo)
function foo1($bar){
return array_merge(array(1), $bar);
}
До приведения:
* если $bar — массив, все ок
* если $bar is ArrayObject — получаем варнинг
* иначе — получаем варнинг
После приведения:
* если $bar — массив, все ок
* если $bar is ArrayObject — все ок
* иначе — счастливой отладки
В итоге код начинает пестрить костылями типа ($foo instanceof ArrayObject) || is_array($foo)
+1
<?php
public function __call($func, $argv)
{
if (!is_callable($func) || substr($func, 0, 6) !== 'array_')
{
throw new BadMethodCallException(__CLASS__.'->'.$func);
}
return call_user_func_array($func, array_merge(array($this->getArrayCopy()), $argv));
}
?>
это по поводу нерабочих array_* функций. Не панацея, но всё же)
0
Написать свои методы не проблема. Я про то, что array_values($obj) по прежнему не будет работать.
Нужно понимать, что в данный момент мы работаем с объектом, а не с массивом. Я это хотел сказать.
Нужно понимать, что в данный момент мы работаем с объектом, а не с массивом. Я это хотел сказать.
0
Если применить шаблон проектирования «Костылинг», то можно воспользоваться override_function и переопределить все нужные функции.
0
Что нужно чтобы ArrayObject использовать в реальных проектах?
1. Нужно чтобы функции работы с массивами работали с ArrayObject также, как и с массивами
2. Обычный массив должен иметь поведение идентичное простейшей реализации ArrayObject (http://www.php.net/manual/ru/class.arrayobject.php ). Вплоть до
3. Было бы классно, если бы это не повлияло на производительность.
4. Единственный способ отличить обычнй массив от кастомизированного:
Но меня терзают смутные сомнения по поводу совместимости таких нововведений с существующими расширениями, т.ч. это будет не скоро, если вообще будет.
1. Нужно чтобы функции работы с массивами работали с ArrayObject также, как и с массивами
2. Обычный массив должен иметь поведение идентичное простейшей реализации ArrayObject (http://www.php.net/manual/ru/class.arrayobject.php ). Вплоть до
([] instanceof ArrayObject) === true;
3. Было бы классно, если бы это не повлияло на производительность.
4. Единственный способ отличить обычнй массив от кастомизированного:
$array instanceof MyArrayObject
Но меня терзают смутные сомнения по поводу совместимости таких нововведений с существующими расширениями, т.ч. это будет не скоро, если вообще будет.
0
Этот код (не прм в таком виде, в расширенном) используется в реальном проекте. Но я, пожалуй, процитирую еще раз свою мысль:
Насчет внедрения — я считаю что переход от массивов к ArrayObject дело, в общем случае, не то чтобы не полезное, но даже вредное. Использовать объекты имеет смысл только в отдельных конкретных случаях, когда есть понимание, что с ними будет проще, чем без них.
+1
НЛО прилетело и опубликовало эту надпись здесь
Рекомендую не писать кучу иссетов, когда они в условии собираются через &&. Проще так:
if (isset($array['foo'], $array['bar'], $array['baz'])) {
}
+1
Подумалось… а операторы сравнения можно как-то сократить?
Вроде такого: if ($val=='one' || $val=='two' || $val=='three') {… }?
Вроде такого: if ($val=='one' || $val=='two' || $val=='three') {… }?
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Расширение возможностей массива в PHP