Современное программирование давно и успешно манипулирует нетипированными переменными. Тип переменной можно не указывать заранее и менять его в процессе выполнения программы.
Такая концепция стала основной в общей парадигме программирования. В самом начале эпохи программирования языки с такой же основательной уверенностью требовали от программиста предварительного описания переменных и строго следили за тем, чтобы им ничего недозволенного не присваивалось. О том, что переменная меняет свой тип, ни программы, ни языки программирования раньше понятия не имели.
О пустом и не существующем
О безопасности и контроле
Практика показывает, что нетипированные языки дают значительно больше свободы программисту, но предполагают, что его отношение к работе над алгоритмом более ответственно.
PHP предлагает современный синтаксис, поддерживающий устоявшуюся семантику, имеет мало ошибок, но требует к себе внимательного отношения. Например, вызов любой функции предполагает какое-то количество параметров.
Вызывая функцию, вовсе не обязательно передавать все параметры, можно передать лишь существенную их часть. Функция «обязана» проверить наличие и существование всех параметров. Те из них, которые отсутствуют или имеют неверные значения, необходимо привести к нормальному виду, присвоить им нужные значения.
В этом контексте функция PHP empty() имеет существенное значение. Выражение:
В этом контексте, имея функцию:
будет иметь желаемое, то есть результат функции. А при вызове:
PHP empty() для объектов и массивов
Синтаксис языка имеет достаточное количество конструкций и функций для работы с объектами и массивами, однако с точки зрения проверки их на существование и на наличие значения особых отличий от переменных нет.
По логике объектно-ориентированного программирования, объект имеет собственное содержание и собственный набор методов. Сказать пустой он или не пустой может только сам объект, но не сторонняя функция, пусть даже входящая в синтаксис языка.
Объект и его функция empty()
На этом простом, но законном, основании следует рассматривать всякий объект в контексте его понимания «пустоты». Например, реализация объекта «Штатное расписание» состоит из записей «Сотрудник». Но если нет ни одного сотрудника, то в «Штатном расписании» всегда есть варианты должностей вероятных сотрудников.
На каком уровне применить здесь функцию PHP empty object? На уровне «Штатное расписание» все существует, даже если нет ни одного сотрудника. На уровне «Сотрудник» объект уже есть, даже если он заполнен не полностью. А не полностью заполненный объект можно отнести к пустому объекту. Пользы от него штатному расписанию никакой.
В зависимости от выбранного стиля программирования, функции PHP empty() и isset() имеют очень важное значение для построения безопасного и надежного алгоритма, однако в отношении объектов все же лучше иметь свой вариант empty(), определяемый его содержанием.
Одна из самых часто используемых конструкций языка, но при этом много раз видел неправильно использование этой конструкции при всей ее простоте для понимания.
Для чего нужна конструкция empty в языке PHP? Empty() используется для проверки элементов на существования значения в переменной, при этом на проверка на значение отличное от null, 0, » и тд. При этом выполняется проверка и на существование самой переменной, элемента, объекта. И самая частая ошибка при ее использовании это использование ее вместе с конструкцией isset:
В итоге получается, что Вы выполняете лишнюю проверку, так как проверка на empty включает себе проверку и на isset.
Еще один важный момент и наверное самый важный, заключается он в том, что использовать данную конструкцию необходимо в случае проверки вложенных элементов (массив или объект), когда возможна генерация ошибки в случае его отсутствия (к примеру E_NOTICE).
Постараюсь объяснить на примере:
Эти примеры эквивалентны и использовать первый вариант нужно только в том случае если уровень ваших ошибок настроен таким образом, что без объявления переменной так же генерируется ошибка.
Конструкция empty по своему подавляет ошибки (E_NOTICE), при этом генерация ошибок не выполняется, что положительно сказывается на производительности скрипта.
Почему второй вариант не правильный? Потому что в случае не существования элемента массива name, будет сгенерирована ошибка E_NOTICE, ВНИМАНИЕ даже если вывод ошибок отключен. Поэтому правильно и использовать первый вариант.
На последок хочу обратить Ваше внимание на то, что в версии PHP 5.4 немного изменено привычное поведение конструкции, советую почитать официальную документацию.
До PHP 5.5 empty() проверяет только переменные, и попытка проверить что-то еще вызовет ошибку синтаксиса. Другими словами, следующий код не будет работать: empty(trim($name)). Используйте вместо него trim($name) == false.
Возвращаемые значения
Список изменений
Версия
Описание
5.5.0
empty() теперь поддерживает выражения, а не только переменные.
Примеры
Пример #2 empty() и строковые индексы
Результат выполнения данного примера в PHP 5.3:
Результат выполнения данного примера в PHP 5.4:
Примечания
Замечание: Поскольку это языковая конструкция, а не функция, она не может вызываться при помощи переменных функций.
При использовании функции empty() на недоступных (необъявленных) свойствах объекта будет вызван встроенный метод объекта __isset(), если он определен.
Смотрите также
Коментарии
Note the exceptions when it comes to decimal numbers:
I know that the statement in the «Return Values» section of the manual already says this in reverse:
«Returns FALSE if var has a non-empty and non-zero value.»
but I was like «Why is this thing returning TRUE for unset variables. «. oh i see now. Its supposed to return TRUE for unset variables.
Since I didn’t like how empty() considers 0 and «0» to be empty (which can easily lead to bugs in your code), and since it doesn’t deal with whitespace, i created the following function:
If you want strings that only contain whitespace (such as tabs or spaces) to be treated as empty then do: check_not_empty($var, 1)
So, whenever you want to check if a form field both exists and contains a value just do: if (check_not_empty($_POST[‘foo’], 1))
David from CodeXplorer: >> The ONLY reason to use empty() is for code readability. It is the same as an IF/ELSE check. >> So, don’t bother using EMPTY in the real world.
This is NOT true. empty() will not generate warnings if you’re testing against an undefined variable as a simple boolean check will. On production systems, warnings are usually shut off, but they are often active on development systems.
In reply to «admin at ninthcircuit dot info»,
To add on to what anon said, what’s happening in john_jian’s example seems unusual because we don’t see the implicit typecasting going on behind the scenes. What’s really happening is:
Note: I don’t remember if PHP even *has* typecasting, much less if this is the correct syntax. I’m just using something for the sake of examples.
Note on the selfmade empty function below:
function_exists() returns false on language constructs and empty is a language construct.
in cases when «0» is not intended to be empty, here is a simple function to safely test for an empty string (or mixed variable):
Please note that results of empty() when called on non-existing / non-public variables of a class are a bit confusing if using magic method __get (as previously mentioned by nahpeps at gmx dot de). Consider this example:
empty() should not necessarily return the negation of the __isset() magic function result, if you set a data member to 0, isset() should return true and empty should also return true. A simpler implementation of the __isset magic function would be:
public function __isset($key) < return isset($this-><$key>); >
I don’t understand why this isn’t included in stdClass and inherited by default.
Ниже поговорим про неожиданные, нестандартные, необычные, не очевидные, странные или особенные ситуации/случаи в PHP.
explode() в 2 раза быстрее unserialize()
Если для сохраняемых чисел не важен тип (число), лучше хранить числа через запятую, чем их же сериализовать.
Точное сравнение: 0 == ‘строка’
PHP язык без строгой типизации и потому иногда могут возникать неожиданные результаты при сравнении (проверке) разных значений.
Так например можно пропустить переменную запроса:
Все следующие значения одинаковы, при сравнении через == (не строгий оператор сравнения):
in_array() нас обманывает
Вы мастер массивов в PHP. Вы уже знаете все о создании, редактировании и удалении массивов. Тем не менее, следующий пример может вас удивить.
AND и &&
Полезная ссылка по этой теме: Приоритет оператора
Шунтирующие операторы (короткая запись)
count() не всегда дает ожидаемый результат
isset() и null
Странное поведение в PHP при передаче значения foreach по ссылке
empty() и объекты
Однако если разобраться, то нет тут ничего удивительного и все логично!
А теперь, зададим значение свойства bar в __isset() и empty() его получит:
Увеличитель числа ++
Имеет большое значение в каком положении использовать ++ (инкремент, увеличитель).
Увеличение строки ++
С числами все довольно просто, но что будет если инкрементить строки?
Этот момент хорошо описан в официальной документации по операциям инкремента/декремента, однако многие не читали этот материал, потому что не ожидали встретить там ничего особенного.
Неточности с плавающей точкой
Посчитайте эту арифметику и скажите результат:
Сколько получилось, 8? А у компьютера 7!
Что получается в итоге и где ошибка?
Однако, если посчитать так, то увидим 0.8, а не 0.79999999999. Хотя этот результат является лишь округлением:
А вот пример сериализации дробного значения:
Какой вывод можно сделать из этого примера? Будьте очень осторожны, когда дело касается дробных чисел (чисел с плавающей точкой) и никогда им не доверяйте.
Все объекты в PHP передаются по ссылке
Это свойство объектов вам возможно известно, но даже в этом случае можно поймать баг, поэтому при работе с объектами будьте внимательны.
Для примера, давайте рассмотрим такой код, когда мы используем объект для удобства, который создается из массива:
Мы создали казалось бы общие данные, который потом планируем использовать в разных переменных, меняя данные объекта где нужно, а где не нужно оставляя исходный вариант. В этом случае изменение данных будет менять их и в исходном варианте.
Так, например, если использовать массив, то все будет работать как мы ожидаем. Тот же код но с массивом:
Поэтому чтобы первый вариант работал правильно, в переменную нужно передавать копию объекта, создаваемую с помощью clone :
Сложение массивов
При сложении массивов элементы добавляемого массива не заменяют исходные, как это часто ожидается.
Изменение типа данных в ключах массива
При создании индекса массива PHP автоматически преобразовывает тип данных. Это надо учитывать, при работе с ассоциативными массивами. Так например, если в индекс передать число в виде строки (‘555’), то в индексе оно станет числом, или если в индекс передать true, то оно станет числом 1, а вот null превратиться в пустую строку. Пример кода см. ниже.
Дополнительно с ключом key будут сделаны следующие преобразования:
Closure::call — вызов анонимной функции с указанием контекста
Это не столько неожиданность, сколько интересная особенность, о которой мало кто знает.
PHP замыкания (анонимные функции) можно вызывать передавая в них контекст (объект). В результате замыкание можно использовать как метод переданного объекта.
Для этого в объекте замыкания есть метод:
Пример того как это использовать
Что мы видим? При вызове одного и того же замыкания мы получаем разный результат, который зависит от контекста вызова (от того какой объект передается и используется в замыкании).
Рекурсивная анонимная (лямбда) функция
Пример ниже показывает как можно создать рекурсивное замыкание. Другими словами как можно создать анонимную функцию и использовать её рекурсивно саму в себе.
Все это может показаться бредом, но как ни странно такие функции мне иногда нужны из-за удобства. Поэтому хорошо бы знать, что такое можно делать в PHP.
Передача по ссылке переменных это дело понятное, а вот ссылки для вызова функции/метода — не совсем. Нужна такая штука для передачи по ссылке переменных с которыми работает функция/метод. Рассмотрим на примере:
Аналогичный пример с объектом:
Рассмотрим еще один пример, где наглядно видно отличие вызова по ссылке и без:
Ссылку из функции можно передавать в другую функцию, которая ожидает ссылку:
Передача функций по ссылке никак не связана с производительностью (ею занимается PHP). Подробно читаем в документации.
isset() в 2 раза быстрее in_array()
Скорости очень быстрые, но если обрабатываются большие массивы, то есть смысл заюзать array_flip() и искать значение через isset() :
isset — Определяет, была ли установлена переменная значением, отличным от null
Описание
Определяет, была ли установлена переменная значением отличным от null
Если были переданы несколько параметров, то isset() вернёт true только в том случае, если все параметры определены. Проверка происходит слева направо и заканчивается, как только будет встречена неопределённая переменная.
Список параметров
Возвращаемые значения
Примеры
Пример #1 Пример использования isset()
// В следующем примере мы используем var_dump для вывода // значения, возвращаемого isset().
$a = «test» ; $b = «anothertest» ;
Функция также работает с элементами массивов:
Пример #2 isset() и строковые индексы
Результат выполнения данного примера:
Примечания
Замечание: Поскольку это языковая конструкция, а не функция, она не может вызываться при помощи переменных функций.
При использовании isset() на недоступных свойствах объекта, будет вызываться перегруженный метод __isset(), если он существует.
Смотрите также
User Contributed Notes 30 notes
I, too, was dismayed to find that isset($foo) returns false if ($foo == null). Here’s an (awkward) way around it.
Of course, that is very non-intuitive, long, hard-to-understand, and kludgy. Better to design your code so you don’t depend on the difference between an unset variable and a variable with the value null. But «better» only because PHP has made this weird development choice.
In my thinking this was a mistake in the development of PHP. The name («isset») should describe the function and not have the desciption be «is set AND is not null». If it was done properly a programmer could very easily do (isset($var) || is_null($var)) if they wanted to check for this!
The new (as of PHP7) ‘null coalesce operator’ allows shorthand isset. You can use it like so:
You can safely use isset to check properties and subproperties of objects directly. So instead of writing
How to test for a variable actually existing, including being set to null. This will prevent errors when passing to functions.
«empty() is the opposite of (boolean) var, except that no warning is generated when the variable is not set.»
!empty() mimics the chk() function posted before.
in PHP5, if you have
I tried the example posted previously by Slawek:
$foo = ‘a little string’; echo isset($foo)?’yes ‘:’no ‘, isset($foo[‘aaaa’])?’yes ‘:’no ‘;
He got yes yes, but he didn’t say what version of PHP he was using.
I tried this on PHP 5.0.5 and got: yes no
But on PHP 4.3.5 I got: yes yes
Any foreach or similar will be different before and after the call.
To organize some of the frequently used functions..
Return Values : Returns TRUE if var exists and has value other than NULL, FALSE otherwise.
isset expects the variable sign first, so you can’t add parentheses or anything.
With this simple function you can check if an array has some keys:
If you regard isset() as indicating whether the given variable has a value or not, and recall that NULL is intended to indicate that a value is _absent_ (as said, somewhat awkwardly, on its manual page), then its behaviour is not at all inconsistent or confusing.
Here is an example with multiple parameters supplied
Note that isset() is not recursive as of the 5.4.8 I have available here to test with: if you use it on a multidimensional array or an object it will not check isset() on each dimension as it goes.
Imagine you have a class with a normal __isset and a __get that fatals for non-existant properties. isset($object->nosuch) will behave normally but isset($object->nosuch->foo) will crash. Rather harsh IMO but still possible.
// pretend that the methods have implementations that actually try to do work // in this example I only care about the worst case conditions
// if property does not exist < echo «Property does not exist!» ; exit; // > >
$obj = new FatalOnGet ();
Uncomment the echos in the methods and you’ll see exactly what happened:
On a similar note, if __get always returns but instead issues warnings or notices then those will surface.
The following is an example of how to test if a variable is set, whether or not it is NULL. It makes use of the fact that an unset variable will throw an E_NOTICE error, but one initialized as NULL will not.
The problem is, the set_error_handler and restore_error_handler calls can not be inside the function, which means you need 2 extra lines of code every time you are testing. And if you have any E_NOTICE errors caused by other code between the set_error_handler and restore_error_handler they will not be dealt with properly. One solution:
?>
Outputs: True False Notice: Undefined variable: j in filename.php on line 26
This will make the handler only handle var_exists, but it adds a lot of overhead. Everytime an E_NOTICE error happens, the file it originated from will be loaded into an array.