ТаблицейБлоками.Знак собаки @ и подавление ошибок в PHP |
Последняя модификация: 16.09.2016 г
Страница загружена с адреса: http://webdesign.site3k.ru/consulting/array_walk_error.html
Как-то столкнулся с любопытной проблемой: при обработке массивов функцией вызванной командой array_walk, вместо того, чтобы проверять наличие массива, решил подавить сообщение ошибки, если массива нет. Как говорится, на нет и суда нет. Массив не был обязательной частью данных, он мог быть создан в результате действия предыдущего кода, а мог и не быть, в зависимости от различных условий. При наличии, массив обрабатывался, при отсутствии выдавалось сообщение об ошибке. Вот я и решил не проверять существование массива, а поставить в array_walk собаку, чтобы упростить код.
Я был сильно удивлен, когда обнаружил, что массив перестал обрабатываться.
Предлагаю посмотреть этот тестовый код:
<?php error_reporting(E_ALL); function drink(&$a,$key){ $a='Пить '.$a; echo '<p>'.$a.'</p>'; } $a1=array('чай','кофе'); $a2=array('молоко','кефир'); $a3=array('пиво','виски'); array_walk($a1,'drink'); @array_walk($a2,'drink'); array_walk(@$a3,'drink'); echo '<pre>'; print_r($a1); print_r($a2); print_r($a3); echo '</pre>'; array_walk($a4,'drink'); @array_walk($a4,'drink'); array_walk(@$a4,'drink'); ?>
Вначале я пишу error_reporting(E_ALL) , чтобы независимо от настроек сервера, видеть ошибки PHP. Далее я создаю функцию, присоединяющую слово к элементу массива. При чем, поскольку массив передается по ссылке, функция должна изменять этот массив, а не переданную ей копию. Но, на всякий случай (лучше перебдеть, чем недобдеть), делаю из функции проверочный вывод на экран.
Далее я создаю 3 тестовых массива с набором слов и для каждого из них вызываю функцию обработки командой array_walk. Но в первом случае я использую array_walk в чистом виде, а в двух следующих использую подавление сообщения об ошибке, если указанного массива не существует. При этом в одном варианте я ставлю собаку перед вызовом функции, а в другом перед именем массива. Вначале мне показалось, что это дает одинаковый результат. Для проверки я вывел содержимое массивов после обработки внутри блока <pre>.
А потом, тем же образом, каким вызывал обработку существующих массивов, вызвал обработку несуществующих, чтобы посмотреть, действительно ли сообщение об ошибке подавляется.
И я получил такой результат:
Пить чай Пить кофе Пить молоко Пить кефир Пить пиво Пить виски Array ( [0] => Пить чай [1] => Пить кофе ) Array ( [0] => Пить молоко [1] => Пить кефир ) Array ( [0] => пиво [1] => виски ) Warning: array_walk() [function.array-walk]: The argument should be an array in test.php on line 23 Warning: array_walk() [function.array-walk]: The argument should be an array in test.php on line 25
Внутри функции drink все шло по плану. PHP готов был пить и чай, и молоко, и даже пиво. По крайней мере, вывод на экран внутри функции соответствовал ожидаемому. Но на деле оказалось, что он филонит и тайком выливает виски под стол, потому как при проверке массива через print_r, слово "пить" не присоединилось, ни к пиву, ни к виски.
Почему так происходит, мне совершенно непонятно. Ведь внутри функции все выглядело великолепно независимо от положения собаки при вызове.
Далее последовала проверка подавления ошибки. Вызов обработки несуществующего массива без собаки вполне ожидаемо выдал сообщение об ошибке "аргумент должен быть массивом". Вызов обработки, предваренный собакой, ожидаемо умолчал об ошибке. А вот что будет при вызове с собакой перед массивом, заранее не было известно. То ли PHP будет нем, как рыба, потому что о массиве ему говорить запрещено, то ли начнет жаловаться, что аргумент должен быть массивом. Оказалось, жалуется. Жалуется, если массива нет. Симулирует обработку, если массив есть. То есть, помещение собаки перед массивом не только не отключает сообщение об ошибке, но и само вызывает ошибку при обработке данных.
Только помещение собаки перед вызовом функции дает желаемый результат: если массив есть, он обрабатывается, если его нет, PHP молча проходит мимо.
На последок хочу добавить, что вместо array_walk в большенстве случаев лучше использовать array_walk_recursive, который обработает не только простые массивы, но и многомерные со всеми их уровнями вложенности. Лучше использовать array_walk_recursive сразу, чем потом, в случае изменения массива, проверять, обрабатывается ли он рекурсивно.