Язык PHP упрощает веб-программирование, поэтому он так популярен. Но также из-за своей простоты PHP постепенно превратился в относительно сложный язык, с бесконечными фреймворками, различными языковыми функциями и различиями версий, которые часто заставляют нас перегружаться, и нам приходится тратить много времени на отладку. В этой статье перечислены десять наиболее подверженных ошибкам мест, которые заслуживают нашего внимания.
Ошибка №1: Вforeach
Оставление ссылки на массив после цикла
Неясно в PHPforeach
Как работает обход? Если вы хотите повторить на массиве, управляя каждым элементом в массиве, вforeach
Удобно использовать ссылки в циклах, например.
$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
$value = $value * 2;
}
// $arr 现在是 array(2, 4, 6, 8)
Проблема в том, что это может привести к некоторым непредвиденным побочным эффектам, если вы не будете осторожны. В приведенном выше примере после выполнения кода$value
остается в области видимости и содержит ссылку на последний элемент массива. позже с$value
Связанная операция непреднамеренно изменяет значение последнего элемента в массиве.
ты должен помнитьforeach
Он не создает область на уровне блоков. Таким образом, в приведенном выше примере$value
является глобальной ссылочной переменной. существуетforeach
При обходе каждая итерация образует пару$arr
Ссылка на следующий элемент. Когда обход окончен,$value
будет цитировать$arr
последний элемент и оставаться в области видимости
Такое поведение может привести к некоторым трудно обнаруживаемым и запутанным ошибкам, вот пример
$array = [1, 2, 3];
echo implode(',', $array), "\n";
foreach ($array as &$value) {} // 通过引用遍历
echo implode(',', $array), "\n";
foreach ($array as $value) {} // 通过赋值遍历
echo implode(',', $array), "\n";
Приведенный выше код будет выводиться
1,2,3
1,2,3
1,2,2
Вы правильно прочитали, последнее значение в последней строке равно 2 , а не 3 , почему?
после завершения первогоforeach
После пересечения,$array
не изменилось, но, как описано выше,$value
оставил пару$array
Опасная ссылка на последний элемент (потому чтоforeach
Получено по ссылке$value
)
Это получается при переходе ко второмуforeach
, произошла эта "странная вещь". когда$value
Получено по заданию,foreach
скопируйте каждый по порядку$array
элементы для$value
когда второйforeach
Детали такие
- Шаг 1: Скопируйте
$array[0]
(он же 1 ) в$value
($value
Фактически$array
Ссылка на последний элемент, т.е.$array[2]
),так$array[2]
Теперь равно 1. так$array
теперь содержит [1, 2, 1] - Шаг 2: Скопируйте
$array[1]
(ака 2) до$value
($array[2]
цитаты), поэтому$array[2]
Теперь равняется 2. так$array
теперь содержит [1, 2, 2] - Шаг 3: Скопируйте
$array[2]
(теперь равно 2 ) в$value
($array[2]
цитаты), поэтому$array[2]
Сейчас равна 2. так$array
теперь содержит [1, 2, 2]
чтобыforeach
чтобы избежать этой проблемы, используя удобную ссылку вforeach
После казниunset()
Отбросьте переменную, содержащую ссылку. Например
$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
$value = $value * 2;
}
unset($value); // $value 不再引用 $arr[3]
Распространенная ошибка № 2: непониманиеisset()
поведение
несмотря на названиеisset,ноisset()
не будет возвращаться только тогда, когда переменная не существуетfalse
, где значение переменнойnull
также возвращается, когдаfalse
.
Это поведение более сложно, чем начальные проблемы, а также является распространенным источником ошибок.
Взгляните на код ниже:
$data = fetchRecordFromStorage($storage, $identifier);
if (!isset($data['keyShouldBeSet']) {
// do something here if 'keyShouldBeSet' is not set
}
Разработчик, должно быть, хотел подтвердитьkeyShouldBeSet
Существует ли в$data
середина. Однако, как указано выше, если$data['keyShouldBeSet']
существует и имеет значениеnull
когда, isset($data['keyShouldBeSet'])
также вернетсяfalse
. Таким образом, приведенная выше логика не является строгой.
Давайте посмотрим на другой пример:
if ($_POST['active']) {
$postData = extractSomething($_POST);
}
// ...
if (!isset($postData)) {
echo 'post not active';
}
Приведенный выше код, как правило, считается, если$_POST['active']
возвращение true
,Так postData
должно существовать, поэтомуisset($postData)
также вернетсяtrue
. Напротив, isset($postData)
возвращение false
Единственный возможный$_POST['active']
Также вернутьсяfalse
.
Однако это не так!
Как я сказал, если$postData
существует и настроен наnull
,isset($postData)
также вернетсяfalse
. То есть, даже если$_POST['active']
возвращение true
,isset($postData)
также может вернутьсяfalse
.
再一次说明上面的逻辑不严谨。
Кстати, если намерение вышеуказанного кода действительно подтверждается снова$_POST['active']
возвращаться лиtrue
, зависит отisset()
Это плохое решение для любого сценария. Лучшей практикой является двойная проверка$_POST['active']
,который:
if ($_POST['active']) {
$postData = extractSomething($_POST);
}
// ...
if ($_POST['active']) {
echo 'post not active';
}
В этом случае, хотя важно проверить, существует ли переменная на самом деле (т. е.: определить, является ли переменная неустановленной илиnull
); но использоватьarray_key_exists()
Эта функция является более надежным решением.
Например, мы могли бы переписать первый пример выше следующим образом:
$data = fetchRecordFromStorage($storage, $identifier);
if (! array_key_exists('keyShouldBeSet', $data)) {
// do this if 'keyShouldBeSet' isn't set
}
Кроме того, совмещаяarray_key_exists()
и get_defined_vars()
, мы можем более надежно определить, существует ли переменная в текущей области видимости:
if (array_key_exists('varShouldBeSet', get_defined_vars())) {
// variable $varShouldBeSet exists in current scope
}
Распространенная ошибка №3: О возврате по ссылке путаница, возвращаемая значением
Рассмотрим следующий фрагмент кода:
class Config
{
private $values = [];
public function getValues() {
return $this->values;
}
}
$config = new Config();
$config->getValues()['test'] = 'test';
echo $config->getValues()['test'];
Если вы запустите приведенный выше код, вы получите следующий вывод:
PHP Notice: Undefined index: test in /path/to/my/script.php on line 21
Что пошло не так?
Проблема с приведенным выше кодом заключается в том, что он не проясняет разницу между возвратом массива по ссылке и возвратом массива по значению. Если вы явно не укажете PHP вернуть массив по ссылке (например, используя&
), иначе PHP по умолчанию вернет массив «по значению». Это означает, что будет возвращена копия массива, поэтому вызываемая функция не является тем же экземпляром массива, что и массив, к которому обращается вызывающий объект.
так лицоgetValues()
Звонок вернется$values
Копия массива, а не ссылка на него. Имея это в виду, давайте вернемся к двум ключевым строкам в приведенном выше примере:
// getValues() 返回了一个 $values 数组的拷贝
// 所以`test`元素被添加到了这个拷贝中,而不是 $values 数组本身。
$config->getValues()['test'] = 'test';
// getValues() 又返回了另一份 $values 数组的拷贝
// 且这份拷贝中并不包含一个`test`元素(这就是为什么我们会得到 「未定义索引」 消息)。
echo $config->getValues()['test'];
Возможная модификация - сохранить первый проходgetValues()
возвращение$values
Копия массива, и последующие операции выполняются над этой копией, например:
$vals = $config->getValues();
$vals['test'] = 'test';
echo $vals['test'];
Этот код будет работать нормально (например, он выведетtest
без создания каких-либо сообщений «индекс не определен»), но этот подход может не соответствовать вашим потребностям. В частности, приведенный выше код не изменяет исходный$values
множество. Если вы хотите изменить исходный массив (например, добавитьtest
элемент), вам нужно изменитьgetValues()
функция, которая возвращает$values
Ссылка на сам массив. добавив перед именем функции&
чтобы указать, что эта функция вернет ссылку, например:
class Config
{
private $values = [];
// 返回一个 $values 数组的引用
public function &getValues() {
return $this->values;
}
}
$config = new Config();
$config->getValues()['test'] = 'test';
echo $config->getValues()['test'];
Это выведет ожидаемыйtest
.
Но теперь, чтобы сделать вещи немного более запутанными, учитывайте следующий фрагмент кода:
class Config
{
private $values;
// 使用数组对象而不是数组
public function __construct() {
$this->values = new ArrayObject();
}
public function getValues() {
return $this->values;
}
}
$config = new Config();
$config->getValues()['test'] = 'test';
echo $config->getValues()['test'];
Если вы думаете, что этот код приведет к тому же数组
Пример той же ошибки «неопределенный индекс», это неправильно. На самом деле, этот код будет работать нормально. Причина в том, что, в отличие от массивов,PHP всегда будет передавать объекты по ссылке. (ArrayObject
— это объект SPL, который точно имитирует использование массивов, но работает как объекты. )
Как показывают приведенные выше примеры, часто не сразу становится очевидным, следует ли вам иметь дело со ссылками или копиями. Поэтому разберитесь с этим поведением по умолчанию (например, переменные и массивы передаются по значению; объекты передаются по ссылке) и внимательно изучите документацию API для функции, которую вы будете вызывать, чтобы узнать, возвращает ли она значение, копию массива. , ссылка на массив или необходима ссылка на объект.
Тем не менее, нам нужно понимать, что мы должны стараться избегать возврата массива илиArrayObject
, так как это позволит вызывающей стороне изменить личные данные объекта экземпляра. Это нарушает инкапсуляцию объекта. Поэтому лучше всего использовать традиционные «геттеры» и «сеттеры», например:
class Config
{
private $values = [];
public function setValue($key, $value) {
$this->values[$key] = $value;
}
public function getValue($key) {
return $this->values[$key];
}
}
$config = new Config();
$config->setValue('testKey', 'testValue');
echo $config->getValue('testKey'); // 输出『testValue』
Этот метод позволяет вызывающему$values
Установите или получите любое значение в массиве, пока сам массив общедоступен.
Распространенная ошибка № 4: выполнение запроса в цикле
Если это так, нетрудно увидеть, что ваш PHP не работает должным образом.
$models = [];
foreach ($inputValues as $inputValue) {
$models[] = $valueRepository->findByValue($inputValue);
}
Здесь может и не быть настоящей ошибки, но если вы будете следовать логике кода, вы можете найти этот, казалось бы, безобидный вызов$valueRepository->findByValue()
В итоге выполняется такой запрос:
$result = $connection->query("SELECT `x`,`y` FROM `values` WHERE `value`=" . $inputValue);
Результат Каждый цикл будет генерировать запрос к базе данных. Итак, если вы предоставите массив, содержащий 1000 значений для этого цикла, он сгенерирует 1000 отдельных запросов на ресурс! Если такие скрипты вызываются в несколько потоков, у него будут потенциальные опасности, которые приведут к сбою системы.
Поэтому очень важно, чтобы при выполнении запроса ваш код собирал как можно больше значений, а затем получал все результаты одним запросом.
Общим местом, где мы часто видим неэффективность запросов (например, в циклах), является использование массива значений (например, большого количества идентификаторов) для выполнения запроса к таблице. Чтобы получить все данные для каждого идентификатора, код будет перебирать массив, выполняя один SQL-запрос для каждого идентификатора, который часто выглядит следующим образом:
$data = [];
foreach ($ids as $id) {
$result = $connection->query("SELECT `x`, `y` FROM `values` WHERE `id` = " . $id);
$data[] = $result->fetch_row();
}
нотолько одинОператор SQL-запроса может выполнять ту же работу более эффективно, например:
$data = [];
if (count($ids)) {
$result = $connection->query("SELECT `x`, `y` FROM `values` WHERE `id` IN (" . implode(',', $ids));
while ($row = $result->fetch_row()) {
$data[] = $row;
}
}
Поэтому, когда ваш код прямо или косвенно делает запрос запроса, обязательно распознайте этот запрос. Получите желаемые результаты с помощью одного запроса, насколько это возможно. Тем не менее, осторожность по-прежнему необходима, или еще одна простая ошибка, о которой мы расскажем ниже...
FAQ # 5: Обманы и неэффективность использования памяти
Одновременная выборка нескольких записей определенно более эффективна, чем выборка по одной, но когда мы используем PHPmysql
При расширении это также может стать причинойlibmysqlclient
Возникает состояние «недостаточно памяти».
Мы демонстрируем в тестовой среде: ограниченная память (512 МБ ОЗУ), MySQL иphp-cli
.
Мы загрузим таблицу данных следующим образом:
// 连接 mysql
$connection = new mysqli('localhost', 'username', 'password', 'database');
// 创建 400 个字段
$query = 'CREATE TABLE `test`(`id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT';
for ($col = 0; $col < 400; $col++) {
$query .= ", `col$col` CHAR(10) NOT NULL";
}
$query .= ');';
$connection->query($query);
// 写入 2 百万行数据
for ($row = 0; $row < 2000000; $row++) {
$query = "INSERT INTO `test` VALUES ($row";
for ($col = 0; $col < 400; $col++) {
$query .= ', ' . mt_rand(1000000000, 9999999999);
}
$query .= ')';
$connection->query($query);
}
Хорошо, теперь давайте посмотрим на использование памяти:
// 连接 mysql
$connection = new mysqli('localhost', 'username', 'password', 'database');
echo "Before: " . memory_get_peak_usage() . "\n";
$res = $connection->query('SELECT `x`,`y` FROM `test` LIMIT 1');
echo "Limit 1: " . memory_get_peak_usage() . "\n";
$res = $connection->query('SELECT `x`,`y` FROM `test` LIMIT 10000');
echo "Limit 10000: " . memory_get_peak_usage() . "\n";
Результат:
Before: 224704
Limit 1: 224704
Limit 10000: 224704
Прохладный. Похоже, что память для этого запроса управляется изнутри безопасно с точки зрения использования памяти.
Чтобы сделать это еще более ясным, мы удвоили лимит до 100 000. Ух~ Если мы сделаем это, мы получим следующие результаты:
PHP Warning: mysqli::query(): (HY000/2013):
Lost connection to MySQL server during query in /root/test.php on line 11
Что именно произошло?
Это включает в себя PHPmysql
模块的工作方式的问题了。 это простоlibmysqlclient
Агент особенно ответственен за сухой и живой. После того, как каждый из данных найден, он немедленно помещает данные в память.Так как эта память еще не управляется PHP, когда мы увеличиваем лимит в запросе,memory_get_peak_usage()
Не показывает увеличения использования ресурсов. Мы были обмануты самоуспокоенностью «управление памятью в порядке», поэтому в приведенной выше демонстрации есть такая проблема.
Честно говоря, наше управление памятью действительно ошибочно, и мы также сталкиваемся с проблемами, подобными той, что показана выше.
При использованииmysqlnd
Модули, вы можете, по крайней мере, избежать описанного выше трюка (хотя это само по себе не улучшит использование вашей памяти). mysqlnd
компилируется в собственное расширение PHP и делаетвстречаИспользуйте диспетчер памяти PHP.
Поэтому, если использоватьmysqlnd
вместоmysql
, мы получим более реалистичную информацию об использовании памяти:
Before: 232048
Limit 1: 324952
Limit 10000: 32572912
Кстати, это хуже, чем раньше. Согласно документации PHP,mysql
использовать mysqlnd
В два раза больше памяти для хранения данных, поэтому изначальноmysql
Этот сценарий на самом деле использует больше памяти (примерно в два раза), чем показано здесь.
Чтобы избежать этой проблемы, подумайте о том, чтобы ограничить количество запросов, которые вы должны зацикливать, меньшим числом, например:
$totalNumberToFetch = 10000;
$portionSize = 100;
for ($i = 0; $i <= ceil($totalNumberToFetch / $portionSize); $i++) {
$limitFrom = $portionSize * $i;
$res = $connection->query(
"SELECT `x`,`y` FROM `test` LIMIT $limitFrom, $portionSize");
}
Когда мы объединяем эту распространенную ошибку с приведенной вышеРаспространенная ошибка № 4При совместном рассмотрении мы понимаем, что наш идеальный код должен достичь баланса между ними. Следует ли сделать запрос гранулированным и повторяющимся или сделать один запрос огромным. То же самое верно и в жизни, важен баланс; ни одна из крайностей не является плохой, и это может привести к тому, что PHP не будет работать должным образом.
Распространенная ошибка № 6: игнорирование проблем Unicode/UTF-8
В каком-то смысле это была проблема самого PHP, а не то, с чем вы могли столкнуться при отладке PHP, но она так и не была решена должным образом. Ядром PHP 6 является поддержка Unicode. Но это было приостановлено из-за приостановки PHP 6 в 2010 году.
Это не означает, что разработчики могут избежатьПравильная обработка UTF-8И не делайте предположения, что все строки должны быть "древними ASCII". Код, который неправильно обрабатывает строки, отличные от ASCII, будет выглядеть грубо.Ошибки Гейзенберга (heisenbugs)стал печально известным. когда имя содержит
Когда кто-то из "Шредингера" регистрируется в вашей системе, даже простойstrlen($_POST['name'])
Также есть проблемы со звонками.
Вот список некоторых, чтобы избежать этой проблемы:
- Если вы еще не знаете UTF-8, вы должны хотя бы знать основы. здесьЕсть хорошее введение.
- Обязательно используйте
mb_*
Функции для замены старых функций обработки строк (убедитесь, что в вашей сборке PHP включено расширение «multibyte»). - Убедитесь, что ваша база данных и таблицы настроены на кодировку Unicode (многие сборки MySQL по-прежнему используют Unicode по умолчанию).
latin1
). - Помните
json_encode()
будет преобразовывать токены, отличные от ASCII (например: «Шрёдингер» будет преобразован в «Шру00ф6дингер»), ноserialize()
Не будуконвертировать. - Убедитесь, что файлы PHP также имеют кодировку UTF-8, чтобы избежать конфликтов при объединении жестко закодированных строк или настройке строковых констант.
Francisco ClariaОпубликовано в этом блогеUTF-8 Primer for PHP and MySQLявляется ценным ресурсом.
Распространенная ошибка № 7: мышление$_POST
Всегда включайте данные, которые вы POST
Независимо от своего имени,$_POST
Массив не всегда содержит данные, которые вы отправляете, он может быть пустым. Чтобы понять это, давайте рассмотрим следующий пример. Предположим, мы используемjQuery.ajax()
Смоделируйте запрос на обслуживание следующим образом:
// js
$.ajax({
url: 'http://my.site/some/path',
method: 'post',
data: JSON.stringify({a: 'a', b: 'b'}),
contentType: 'application/json'
});
(Кстати, обратите внимание наcontentType: 'application/json'
. Мы отправляем данные в формате JSON, который очень популярен в интерфейсах. это вAngularJS $http
serviceгде тип данных по умолчанию для отправки. )
На стороне сервера нашего примера мы просто печатаем$_POST
множество:
// php
var_dump($_POST);
Как ни странно, результат такой:
array(0) { }
Зачем?我们的 JSON 串{a: 'a', b: 'b'}
Что именно произошло?
причина в томКогда тип содержимого application/x-www-form-urlencoded
или multipart/form-data
PHP будет автоматически анализировать только полезную нагрузку POST. Для этого есть исторические причины - эти два типа контента находятся в PHP.$_POST
Два важных типа, которые уже используются до реализации. Таким образом, независимо от использования любых других типов контента (даже тех, которые сейчас популярны, например,application/json
),
PHP также не загружается автоматически в полезную нагрузку POST.
теперь, когда$_POST
является суперглобальной переменной, если мы переопределимоднажды(как можно раньше в нашем скрипте), измененное значение (включая полезную нагрузку POST) будет доступно для ссылки в нашем коде. Это важно, потому что$_POST
Он обычно используется фреймворками PHP и почти всеми пользовательскими сценариями для получения и передачи данных запроса.
Так, например, при работе с типом контентаapplication/json
Когда полезная нагрузка POST, нам нужно вручную проанализировать содержимое запроса (декодировать данные JSON) и перезаписать$_POST
переменные следующим образом:
// php
$_POST = json_decode(file_get_contents('php://input'), true);
тогда, когда мы печатаем$_POST
Когда массив используется, мы видим, что он правильно содержит действительное содержимое POST; следующим образом:
array(2) { ["a"]=> string(1) "a" ["b"]=> string(1) "b" }
Распространенная ошибка № 8: Думать, что PHP поддерживает односимвольные типы данных
Прочтите приведенный ниже код и подумайте, что он выведет:
for ($c = 'a'; $c <= 'z'; $c++) {
echo $c . "\n";
}
если ваш ответa
прибытьz
Тогда вы можете быть удивлены на ответ был ошибкой.
Правильно, выводитa
прибытьz
, тем не менее, он продолжает выводитьaa
прибытьyz
. Давайте посмотрим, почему это так.
Не в PHPchar
тип данных; может использоваться только сstring
тип. Помните, что в PHP добавлениеstring
Типz
что вы получаетеaa
:
php> $c = 'z'; echo ++$c . "\n";
aa
Менее смущательно,aa
Лексикографический порядокменьше, чем z
из:
php> var_export((boolean)('aa' < 'z')) . "\n";
true
Вот почему приведенный выше простой код выведетa
прибыть z
, ПотомПродолжатьЭкспортaa
прибыть yz
. он остановился наza
, это первое отношение, с которым он сталкиваетсяz
Большойиз:
php> var_export((boolean)('za' < 'z')) . "\n";
false
На самом деле, в PHPиметь подходящиеспособ вывода в циклеa
прибытьz
Значение:
for ($i = ord('a'); $i <= ord('z'); $i++) {
echo chr($i) . "\n";
}
или это:
$letters = range('a', 'z');
for ($i = 0; $i < count($letters); $i++) {
echo $letters[$i] . "\n";
}
Распространенная ошибка № 9: игнорирование стандартов кода
Хотя игнорирование стандартов кодирования не приводит напрямую к необходимости отладки PHP-кода, это, вероятно, самая важная тема для обсуждения.
В проекте игнорирование спецификаций кода может вызвать массу проблем. Самые оптимистичные прогнозы, до и после коды не совпадают (до этого каждый из разработчиков "делает свое дело"). Но худший результат, PHP-код не может работать или трудно (иногда невозможно) пройти, что является кодом для отладки, повышения производительности, проектов обслуживания трудно. А это означает снижение продуктивности вашей команды, увеличение количества дополнительных (или, по крайней мере, ненужных) энергозатрат.
К счастью для разработчиков PHP, существует Рекомендация по стандартам кодирования PHP (PSR), которая состоит из следующих пяти стандартов:
- PSR-0: стандарт автозагрузки
- PSR-1: Базовый стандарт кодирования
- PSR-2: Руководство по стилю кодирования
- PSR-3: интерфейс журнала
- PSR-4: Автозагрузка расширенной версии
PSR изначально создавался крупнейшими организаторами на рынке. Zend, Drupal, Symfony, Joomla и др.разноеВнесли свой вклад в эти стандарты и придерживались их. Даже, пытавшийся много лет назад стать стандартной ГРУШЕЙ, теперь добавлен в PSR.
В некотором смысле почти не имеет значения, каковы ваши стандарты кодирования, если вы следуете стандарту и придерживаетесь его, но в целом рекомендуется следовать PSR, если только в вашем проекте нет чего-то еще, что усложняет его. причина сопротивления. Все больше и больше команд и проектов соблюдают PSR. На данный момент большинство PHP-разработчиков достигли консенсуса, поэтому использование стандарта кода PSR полезно, поскольку новые разработчики, присоединяющиеся к команде, чувствуют себя более знакомыми и удобными с вашим стандартом кода.
Распространенная ошибка № 10: Злоупотреблениеempty()
Некоторым разработчикам PHP нравится использовать почти всеempty()
Проведите проверку логических значений. Однако в некоторых случаях это может вызвать путаницу.
Во-первых, давайте вернемся к массиву иArrayObject
Экземпляр (аналогичный массиву). Учитывая их сходство, легко предположить, что они ведут себя одинаково. Однако это оказалось опасным предположением. Например, в PHP 5.0:
// PHP 5.0 或后续版本:
$array = [];
var_dump(empty($array)); // 输出 bool(true)
$array = new ArrayObject();
var_dump(empty($array)); // 输出 bool(false)
// 为什么这两种方法不产生相同的输出呢?
Что еще хуже, результаты могут отличаться до PHP 5.0:
// PHP 5.0 之前:
$array = [];
var_dump(empty($array)); // 输出 bool(false)
$array = new ArrayObject();
var_dump(empty($array)); // 输出 bool(false)
К сожалению, такой подход довольно распространен. Например, в Zend Framework 2Zend\Db\TableGateway
изTableGateway::select()
вызов в результатеcurrent()
способ возврата данных, как предлагает документация. Разработчики могут легко стать жертвой таких ошибок данных.
Чтобы избежать этих проблем, лучше использовать лучший подходcount()
Чтобы проверить пустую структуру массива:
// 注意这会在 PHP 的所有版本中发挥作用 (5.0 前后都是):
$array = [];
var_dump(count($array)); // 输出 int(0)
$array = new ArrayObject();
var_dump(count($array)); // 输出 int(0)
Кстати, поскольку PHP будет0
преобразовать вfalse
, count()
можно использовать вif()
Внутри условия для проверки наличия пустого массива. Также стоит отметить, что в PHPcount()
В массиве постоянная сложность (O(1)
операция), что делает более ясным, что это правильный выбор.
другое использованиеempty()
Опасны примеры, когда это и магические методы_get()
использовать вместе. Давайте определим два класса и сделаем так, чтобы они оба имелиtest
Атрибуты.
Сначала определим содержащиесяtest
Общественная собственностьRegular
своего рода.
class Regular
{
public $test = 'value';
}
Затем мы определяемMagic
класс, используя магический метод здесь__get()
работать, чтобы получить доступ к егоtest
Атрибуты:
class Magic
{
private $values = ['test' => 'value'];
public function __get($key)
{
if (isset($this->values[$key])) {
return $this->values[$key];
}
}
}
Хорошо, теперь давайте попробуем получить доступ кtest
свойства, чтобы увидеть, что происходит:
$regular = new Regular();
var_dump($regular->test); // 输出 string(4) "value"
$magic = new Magic();
var_dump($magic->test); // 输出 string(4) "value"
Все идет нормально.
Но сейчас мы звоним каждому из них.empty()
, Давай посмотрим что происходит:
var_dump(empty($regular->test)); // 输出 bool(false)
var_dump(empty($magic->test)); // 输出 bool(true)
кашель. поэтому, если мы полагаемся наempty()
, мы можем ошибиться$magic
свойстваtest
пусто, когда на самом деле установлено значение'value'
.
К сожалению, если класс использует магический метод__get()
чтобы получить значение свойства, то нет надежного способа проверить, является ли значение свойства пустым.
Вне области действия класса вы можете только проверить, является лиnull
значение, это не означает, что соответствующий ключ не установлен, поскольку фактически он может быть установлен какnull
.
Вместо этого, если мы попытаемся процитироватьRegular
Атрибуты не существуют в экземплярах класса, мы получим уведомление, подобное следующему:
Notice: Undefined property: Regular::$nonExistantTest in /path/to/test.php on line 10
Call Stack:
0.0012 234704 1. {main}() /path/to/test.php:0
Итак, главное здесьempty()
Этот метод следует использовать с осторожностью, так как он может привести к запутанным и даже потенциально вводящим в заблуждение результатам, если не соблюдать осторожность.
Суммировать
Простота использования PHP вызывает у разработчиков ложное чувство комфорта, а некоторые нюансы и особенности самого языка могут стоить вам много времени на отладку. Это может привести к неправильной работе PHP-программ и вызвать проблемы, подобные описанным здесь.
PHP значительно изменился за свою 20-летнюю историю. Стоит потратить время на то, чтобы ознакомиться с тонкостями самого языка, так как это помогает сделать программное обеспечение, которое вы пишете, более расширяемым, надежным и удобным в сопровождении.
Для получения более современных знаний PHP перейдите кСообщество знаний Laravel / PHP