count(), выдающий E_WARNING

До PHP 7.2 использование count() для скалярного значения или несчетного объекта возвращало 1 или 0.

Например: https://3v4l.org/tGRDE

var_dump(count(123)); //int(1)
var_dump(count(new stdclass)); //int(1)
var_dump(count('hello world'));  //int(1)
var_dump(count(null));  //int(0)

В обновлениях до PHP 7.2+ использование count(), как показано выше, приведет к появлению предупреждающего сообщения.

E_WARNING теперь будет выдаваться при попытке подсчета неисчисляемых типов count() (включая псевдоним sizeof()).

Предупреждение: count(): параметр должен быть массивом или объектом, который реализует Countable неисчисляемые типы" rel="noreferrer">[sic]

В результате многие популярные фреймворки повышают уровень E_WARNING и вместо этого выдают исключение.

[ErrorException] count(): параметр должен быть массивом или объектом, который реализует Countable

Поведение при повышении уровня ошибки также было прокомментировано разработчиками PHP.

Среды, которые отображают предупреждения или преобразовывают их в более серьезные ошибки/исключения, будут затронуты, но это должно просто привлечь внимание к ошибке в коде.

Как можно добиться предыдущего поведения count() в PHP 7.2+, который не выдает E_WARNING, без изменения настройки отчета об ошибках и без использования @count()?


person Toskan    schedule 04.04.2018    source источник
comment
strlen()... count предназначен для массивов или объектов, которые реализуют Countable..   -  person Lawrence Cherone    schedule 05.04.2018
comment
@LawrenceCherone count работал на обоих. strlen работает только со строками и числами. Значит, для этого нужно переписывать все кодовые базы? Зачем? везде поставить галочку? написать свою собственную функцию подсчета? печальный   -  person Toskan    schedule 05.04.2018
comment
вы передаете строку/число, неправильная функция для использования в первую очередь..   -  person Lawrence Cherone    schedule 05.04.2018
comment
К сожалению, вы не можете угодить всем, когда добьётесь прогресса, создадите функцию countLegacy() самостоятельно и выполните простой поиск и замену в своей кодовой базе.   -  person Scuzzy    schedule 05.04.2018
comment
В наличии PHP это возвращается, как и ожидалось, но с генерируемым предупреждением. У вас есть что-то дополнительное, из-за чего это выдается как исключение ErrorException. Может быть, вместо этого изменить это? Хотя реальный вопрос: зачем вы вообще это делаете?   -  person Sammitch    schedule 05.04.2018
comment
@Scuzzy @Sammitch, кто сказал, что я им пользуюсь? это повсюду. Все это делают. Почему бы не сделать это? Прогресс? это ломает все виды вещей, абсолютно бесполезно. Почему бы не изменить и функцию echo? python вот-вот оправятся от своих страданий, мы направляемся прямо туда, откуда они пришли. Это шутка   -  person Toskan    schedule 05.04.2018
comment
Попробуйте это ;p 3v4l.org/S28K7   -  person Lawrence Cherone    schedule 05.04.2018
comment
просто погуглите для ›count(): параметр должен быть массивом или объектом, который реализует Countable   -  person Toskan    schedule 05.04.2018
comment
Ссылка говорит An E_WARNING will now be emitted верно? Предупреждение, а не фатальное, что дает разработчикам время отреагировать.   -  person Scuzzy    schedule 05.04.2018
comment
самые популярные фреймворки фатальные по предупреждениям и уведомлениям, что является хорошей практикой   -  person Toskan    schedule 05.04.2018
comment
Everyone does it. Why not do it? Отсутствие охвата передового опыта приведет к нежелательным/неожиданным результатам, что вы и видите сейчас при переходе на 7.2 и использовании count() в непреднамеренная мода, зарегистрированная с 2013 года с array_or_countable. Вместо этого я предлагаю форсировать ожидаемый тип данных, используя преобразование типов count((array) $value), что приведет к нежелательному результату преобразования stdClass в массив и получить 0 вместо 1.   -  person Will B.    schedule 05.04.2018
comment
@fyrye Python 3 намного превосходит Python 2, слово. Сделали ли они себе одолжение, изменив свою print функцию? зачем менять основную функцию count через какие 15 лет? теперь все либы выдают предупреждения и мне нужно откатиться на 7.1. Не могу дождаться, когда они изменят и все остальные основные функции. Лучшей практикой является совместимость, если для этого нет очень-очень веской причины.   -  person Toskan    schedule 05.04.2018
comment
Жалоба на ваше желание перейти на PHP 7.2 и методы разработчиков по внесению изменений в кодовую базу, которые вызвали несовместимость с вашим приложением/библиотеками, не соответствует вашему вопросу, не изменит функционирование PHP 7.2 и не принадлежит к SO. Передовая практика в любом смысле программирования заключается в написании предсказуемого кода и знании типа данных, который вы предоставляете для данной функции, а также тестов, охватывающих его использование. mcrypt, mysql_* поддерживались в PHP 4 и 5, но не в 7 и т. д. Все это было средством оптимизации основного движка, чтобы он работал быстрее или был более безопасным.   -  person Will B.    schedule 05.04.2018
comment
Я бы откатился на 7.1. Разработчики PHP должны были сделать 7.2 8.0; общепринятые рекомендации по номерам версий говорят об увеличении основного (первого) номера, когда изменения нарушают обратную совместимость. Вполне разумно ожидать, что второстепенный патч (увеличение до второго числа) не будет иметь таких разрывов.   -  person citrusy    schedule 05.04.2018
comment
@fyrye Я на самом деле задал вопрос, но пока никто не ответил на него. Все растерялись в эмоциях   -  person Toskan    schedule 05.04.2018
comment
Вы также можете принудительно игнорировать предупреждения @count($value) в кодовой базе. Поскольку count будет работать одинаково независимо от любого типа данных. Однако это не будет учитывать какие-либо библиотеки поставщиков, которые вы используете, и должно действительно использоваться только при подготовке к обновлению функциональности.   -  person Will B.    schedule 05.04.2018
comment
@fyrye Я не могу не смеяться. Вопрос был очень конкретным, функция, которая имеет как выход, так и вход, и действует точно так же, как и старая функция. Здесь нет места для интерпретаций. Да пользовательская функция. Именно это. Так где же ответ?   -  person Toskan    schedule 06.09.2018
comment
Когда было проголосовано за закрытие, желаемым результатом было отсутствие проверки на входе и работа во всех случаях, которые в настоящее время выполняет count, без повышения warning, что он делает в PHP 7.2+. Поскольку нет окончательного способа выполнить это, и можно НЕ учитывайте использование count() в ваших сторонних пакетах, это даст только самоуверенные ответы. Например: 3v4l.org/UFieo — это три разных способа, с помощью которых можно получить альтернативу подсчету. Существуют и другие важные методы. Но в конечном итоге способ, которым вы используете count, не поддерживается и должен быть реорганизован.   -  person Will B.    schedule 06.09.2018
comment
хорошо, спасибо за ссылку, теперь я не понимаю, почему вы не могли включить их в ответ и, возможно, выбрать лучший вариант, основанный на производительности и долгосрочной ремонтопригодности? Я вообще не понимаю, в чем проблема.   -  person Toskan    schedule 06.09.2018
comment
Ваш вопрос не самоуверен, ответы будут. Аспект выбора лучшего варианта будет основан на мнении/желаемом использовании. Потому что каждый из этих примеров относится к конкретному варианту использования. Например, игнорирование предупреждения с использованием @ может быть нежелательным. В дополнение к вашему случаю Framework, который также использует count в своем базовом коде. Ни один из примеров не решит вашу проблему с count в Framework. Это приведет к другим ответам на решение проблемы с фреймворком. Например, переопределение count из базовой библиотеки PHP и многие другие, которые не являются окончательными.   -  person Will B.    schedule 06.09.2018
comment
Я никогда не просил решить проблему с фреймворком. Использование @ плохо, потому что оно не будет поддерживать долгосрочную поддержку. Возможно, однажды они выдадут ошибку вместо предупреждения. По какой-то причине вы просто пытаетесь избежать очень правильного вопроса. Нет лучше и хуже, есть только черное и белое. Я думаю, это глупо. Это действительно так. Боты для голосования в Stackoverflow должны быть остановлены.   -  person Toskan    schedule 06.09.2018
comment
Вы буквально спрашиваете, как лучше всего эмулировать count(), что не является допустимым вопросом для SO. Поскольку это соберет несколько ответов, которые приведут к самоуверенным результатам. По вашему мнению, @ это плохо, несмотря на то, что оно дает желаемый результат из-за ваших очень обоснованных опасений. Долгосрочная ремонтопригодность также субъективна. Все сводится к тому, что вы хотите продолжать использовать count непреднамеренным образом, который был задокументирован с 2013 года. Лучшим подходом, IMO, было бы реорганизовать ваш код, чтобы он был совместим с 7.2+, по причинам, которые вы отметили; что в будущем это может вызвать исключение   -  person Will B.    schedule 06.09.2018
comment
Мне просто интересно, что случилось с stackoverflow. В любом случае, спасибо за ссылку на ответ. Жаль, что это нельзя опубликовать здесь, мне бы очень хотелось получить отзывы об этих решениях или оставить отзыв. Очень жаль.   -  person Toskan    schedule 06.09.2018
comment
@fyrye готово, спасибо   -  person Toskan    schedule 19.09.2018
comment
Вот почему люди используют stackoverflow — для поиска решений проблем, а не просто для критики вопроса, который очень важен для многих разработчиков. Я ценю этот вопрос и все ответы.   -  person Sol    schedule 07.11.2020


Ответы (3)


Как мы уже говорили, есть несколько способов добиться исходной функциональности count() и не создавать E_WARNING.

В PHP 7.3 была добавлена ​​новая функция is_countable, специально предназначенная для устранения E_WARNING проблема и распространенность приложений, использующих is_array($var) || $var instanceof \Countable в своем коде.

В PHP 7.2 было добавлено предупреждение при попытке подсчитать неисчисляемые вещи. После этого все были вынуждены искать и менять свой код, чтобы избежать этого. Обычно стандартным становился следующий фрагмент кода:

if (is_array($foo) || $foo instanceof Countable) { // $foo is countable }

https://wiki.php.net/rfc/is-countable


Замена пользовательских функций

По этой причине кажется, что лучший способ решить проблему — это выполнить ту же функциональность, что PHP делает с is_countable, и создать пользовательскую функцию, чтобы обеспечить соответствие исходной функциональности count.

Пример https://3v4l.org/8M0Wd

function countValid($array_or_countable, $mode = \COUNT_NORMAL)
{
    if (
        (\PHP_VERSION_ID >= 70300 && \is_countable($array_or_countable)) ||
        \is_array($array_or_countable) ||
        $array_or_countable instanceof \Countable
    ) {
        return \count($array_or_countable, $mode);
    }

    return null === $array_or_countable ? 0 : 1;
}

Результат

array: 3
string: 1
number: 1
iterator: 3
countable: 3
zero: 1
string_zero: 1
object: 1
stdClass: 1
null: 0
empty: 1
boolt: 1
boolf: 1

Notice: Undefined variable: undefined in /in/8M0Wd on line 53
undefined: 0

Функция прокладки is_countable()

Используя приведенную выше функцию замены, также можно заменить is_countable на PHP <= 7.2, поэтому она используется только при необходимости с минимальными накладными расходами.

Пример https://3v4l.org/i5KWH

if (!\function_exists('is_countable')) {
    function is_countable($value)
    {
        return \is_array($value) || $value instanceof \Countable;
    }
}

function countValid($array_or_countable, $mode = \COUNT_NORMAL)
{
    if (\is_countable($array_or_countable)) {
        return \count($array_or_countable, $mode);
    }

    return null === $array_or_countable ? 0 : 1;
}

Игнорировать count() предупреждений

Поскольку функциональность count() не изменилась и в прошлом обычно не выдавала предупреждений. Альтернативой использованию пользовательской функции является полное игнорирование предупреждения с помощью @ Error Оператор управления

Предупреждение. Этот подход приводит к тому, что неопределенные переменные рассматриваются как NULL и не отображается сообщение Notice: Undefined variable:.

Пример https://3v4l.org/nmWmE

@count($var);

Результат

array: 3
string: 1
number: 1
iterator: 3
countable: 3
zero: 1
string_zero: 1
object: 1
stdClass: 1
null: 0
empty: 1
boolt: 1
boolf: 1
---
Undefined: 0

Замените count(), используя расширение APD

Что касается замены внутренней функции PHP count(). Существует расширение PECL APD (Advanced PHP Debugger), которое позволяет выполнять override_function< /a>, который работает с основными функциями PHP. Как следует из названия расширения, технически оно предназначено для отладки, но является жизнеспособной альтернативой замене всех экземпляров count пользовательской функцией.

Пример

\rename_function('count', 'old_count');
\override_function('count', '$array_or_countable,$mode', 'return countValid($array_or_countable,$mode);');

if (!\function_exists('is_countable')) {
    function is_countable($value)
    {
        return \is_array($value) || $value instanceof \Countable;
    }
}

function countValid($array_or_countable, $mode = \COUNT_NORMAL)
{
    if (\is_countable($array_or_countable)) {
        return \old_count($array_or_countable, $mode);
    }

    return null === $array_or_countable ? 0 : 1;
}
person Will B.    schedule 21.09.2018

Проблема в том, что вызов count() для скаляра или объекта, который не реализует интерфейс Countable, возвращает 1, что может легко скрыть ошибки.

Учитывая следующее:

function handle_records(iterable $iterable)
{
    if (count($iterable) === 0) {
        return handle_empty();
    }

    foreach ($iterable as $value) {
        handle_value($value);
    }
}

Передача генератора, который ничего не дает, не вызовет ни handle_empty(), ни handle_value().
Кроме того, не будет указано, что ни один из них не был вызван.

По умолчанию это по-прежнему возвращает 1, но дополнительно регистрирует предупреждение. Во всяком случае, это предупреждение привлечет внимание к потенциальным ошибкам в коде.

Дополнительные сведения см. в разделе Подсчет неисчисляемых элементов.

person Obsidian Age    schedule 04.04.2018
comment
ну ты не ошибся. Дело в том, что именно так эта функция и работала. Согласитесь, если вы не понимаете, как работает функция, это может привести к ошибкам. Что они сделали сейчас, так это изменили основную функцию, потому что кому-то не нравилась старая функция. Ломать много библиотек. - person Toskan; 05.04.2018
comment
Я просто устанавливаю переменную в массив перед таким подсчетом. if(!is_array($arrayVariable)){ $arrayVariable = array(); } - person Apit John Ismail; 23.04.2018

Вы можете решить это, используя "??"-оператор. Если левая сторона равна нулю, будет использоваться правая сторона. Так как у нас есть пустой массив, наш результат будет равен нулю.

count(null ?? [])

Другим способом было бы привести его к типу как массив.

count((array) null)
person dipser    schedule 24.02.2020