Перевірка існування елемента масиву
Про те, чому мало перевірки if( isset($test[«key»]) ) і чому така умова може бути обчислена в true, хоча $test[«key»] не існує .
Зіткнувся з такою перевіркою у Zend Framework. Відразу після виходу версії 1.5 я вирішив спробувати використати компонент Zend_Form та протестувати його на прикладі з мануалу.
Тестовий скрипт виводив помилку під час навішування валідації на форму (Fatal error: Cannot unset string offsets). Чи не встигли оновити мануал, чи помилку в коді фреймворку, але скрипт не працював. Під час дослідження коду я помітив перевірку на наявність ключа в асоціативному масиві, яка зроблена неправильно та може давати осічки.
Перевірку було зроблено так:
if( isset($test["key"]) ) unset($test["key"]);
З першого погляду все працює: якщо ключ є, видалити його. Але це правильно, тільки якщо $test дійсно масив!
Функція передбачає, що вона отримає масив, але через неточність у мануалі в цю функцію було передано рядок:
$ test = "test string"; if( isset($test[«key»]) ) unset($test[«key»]);
І в ході виконання цього коду виводиться помилка "Fatal error: Cannot unset string offsets", це відбувається через те, що умовний вираз обчислюється як true, нібито в масиві $ test встановлено ключ key, але видалити його не виходить, т.к . це зовсім не масив, а рядок.
В результаті перевірки відбувається перетворення типів з рядкового в цифровий. Навіщо робиться це перетворення? Я думаю, що інтерпретатор «думає» так:
$test - це рядок, до якого намагаються звернутися як до масиву. Т.к. до символів рядка можна звернутися як до елементів масиву ($test[0], $test[1] і т.п.), то ключем є цифра, а в коді написана рядок, отже требаперетворити один тип на інший.
Що говорить документація про перетворення рядка на число?
Перетворення рядків у числа Якщо рядок розпізнається як числове значення, результуюче значення та тип визначається так, як показано далі. Рядок буде розпізнаний як float, якщо він містить будь-який із символів '.', 'e', або 'E'. Інакше її буде визначено як ціле. Значення визначається по початковій частині рядка.Якщо рядок починається з вірного числового значення, буде використано це значення. Інакше значенням буде 0 (нуль).
В результаті код «спрощується» до такого виду:
$ test = "test string"; if( isset($test[0]) ) unset($test[0]);
І перевірка починає працювати не так, як передбачалося! Перевіряється наявність першого знака у рядку (обчислюється як true, тому що перша літера є) і далі слід спроба знищити (розустановити) першу літеру рядка, що призводить до помилки.
Перед перевіркою на наявність ключа в масиві необхідно перевіряти, чи це справді масив.