Следует ли мне использовать assert в моем PHP-коде?

87

Сотрудник несколько раз добавлял команду assert в наши библиотеки в тех местах, где я бы использовал оператор if и выдал исключение. (До этого я даже не слышал об assert.) Вот пример того, как он это использовал:

assert('isset($this->records); /* Records must be set before this is called. */');

Я бы сделал:

if (!isset($this->records)) {
    throw new Exception('Records must be set before this is called');
}

Из прочтения документации PHP по assert кажется, что рекомендуется убедиться, что assert активен, и добавить обработчик перед использованием assert. Я не могу найти место, где он это сделал.

Итак, мой вопрос: я использую assert как хорошую идею, учитывая вышеизложенное, и должен ли я использовать его чаще вместо if и исключений?

Еще одно замечание: мы планируем использовать эти библиотеки в различных проектах и ​​на серверах, включая проекты, в которых мы, возможно, даже не участвуем (библиотеки с открытым исходным кодом). Есть ли разница в использовании assert?

Дэррил Хайн
источник
Неужели это 'isset(строчка с кодом assert)? Не просто isset(без кавычек ')?
Питер Мортенсен

Ответы:

79

Эмпирическое правило, которое применимо к большинству языков (все, что я смутно знаю), заключается в том, что assertиспользуется, чтобы утверждать, что условие всегда истинно, тогда как an ifподходит, если возможно, что оно иногда терпит неудачу.

В этом случае я бы сказал, что assertэто уместно (исходя из моего слабого понимания ситуации), потому что он всегдаrecords должен быть установлен до вызова данного метода. Таким образом, неспособность установить запись будет скорее ошибкой в ​​программе, чем условием выполнения. Здесь это помогает гарантировать (при соответствующем тестировании), что нет возможного пути выполнения программы, который мог бы вызвать вызов кода, который защищен с помощью , без установки.assertassertrecords

Преимущество использования assertв отличие от использования ifзаключается в том, что assertего можно отключить в производственном коде, что снижает накладные расходы. Ситуации, с которыми лучше всего справиться, ifмогут возникнуть во время выполнения в производственной системе, и поэтому ничего не теряется из-за невозможности их выключить.

ааронастерлинг
источник
4
В дополнение к этому вы можете не захотеть отключать утверждения в своем производственном коде, потому что они помогают убедиться, что условия «этого никогда не должно происходить» остаются неизменными. Возможно, будет лучше позволить вашему приложению прекратить выполнение утверждения, чем позволить вашим пользователям продолжать выполнение пути, которого не должно существовать.
derekerdmann
2
@derekerdmann: Верно. Для некоторых утверждений может быть достаточно их зарегистрировать (в производстве) или распечатать предупреждение (в среде разработки). Но поскольку часто утверждения также защищают связанный с безопасностью код, вы также можете включить assert_options(ASSERT_BAIL). Это быстрее, чем вручную, если / все равно обходные пути.
Марио
4
@derekerdmann Я бы не согласился с этим (в контексте использования assert () в php). Это большой недостаток уязвимости, поскольку assert () обрабатывает все строковые аргументы как PHP-код, поэтому (теоретически) возможно внедрить и запустить произвольный код. IMHO, assertions на продакшене надо отключить
Виталий Лебедев
2
@VitaliyLebedev, если вы не хотите быть восприимчивыми к инъекциям, не передавайте строки для утверждения.
Дэймон Снайдер
9
Поздно к вечеринке, но PHP.net заявляет: «Утверждения следует использовать только как средство отладки».
Коэн.
25

Думайте об утверждениях как о «властных комментариях». Вместо комментария вроде:

// Note to developers: the parameter "a" should always be a number!!!

использование:

assert('is_numeric(a) /* The parameter "a" should always be a number. */');

Значения точно такие же и предназначены для одной и той же аудитории, но первый комментарий легко забыть или проигнорировать (независимо от того, сколько восклицательных знаков), в то время как «мощный комментарий» доступен не только для чтения и понимания людьми, он также постоянно тестируется на компьютере во время разработки и не будет проигнорирован, если вы настроите хорошую обработку assert в коде и в рабочих привычках.

С этой точки зрения утверждения - это совершенно другая концепция, чем if (error) ... и исключения, и они могут сосуществовать.

Да, вы должны комментировать свой код, и да, вы должны использовать «мощные комментарии» (утверждения), когда это возможно.

Дэйв Уолли
источник
что, если при разработке при тестировании вы всегда передаете в assert хорошее состояние, но в производстве, если assert отключен - какой-то пользователь передает другое условие, о котором вы не думали при тестировании? Или иначе вам нужно держать assert всегда включенным, но разве это не то же самое, что выписывать собственный чек?
Darius.V
Тогда ваша программа выйдет из строя. Исправьте это правильно с помощью операторов if и функций обработки ошибок вашего языка и среды разработки. утверждения могут выявить проблемы, существуют более эффективные способы их решения.
DaveWalley
Обратите внимание, что с PHP 7.2 передача строки в assert для оценки устарела. Печально, потому что это выглядело весьма удобно.
Дженни Теуниссен,
16

Это полностью зависит от вашей стратегии развития. Большинство разработчиков не знают assert()и используют последующее модульное тестирование. Но проактивные и встроенные схемы тестирования иногда могут быть полезными.

assert полезен, потому что его можно включать и отключать. Это не снижает производительность, если такой обработчик утверждения не определен. У вашего коллеги его нет, и вам следует разработать код, который временно включает его в среде разработки (если включены E_NOTICE / E_WARNING, то должен быть обработчик утверждения). Я использую его иногда, когда мой код не может принять смешанные типы переменных - обычно я не использую строгую типизацию в слабо типизированном PHP, но есть случайные варианты использования:

 function xyz($a, $b) {
     assert(is_string($a));
     assert(is_array($b));

Что, например, компенсировало бы отсутствие спецификаторов типа string $a, array $b. PHP5.4 их поддерживает, но не проверяет.

Марио
источник
Что означает "php 5.4 будет, но не проверять"?
Kzqai
1
PHP 5.4 имеет, поддерживает и проверяет утверждения.
DaveWalley
7

Assert не заменяет обычное управление потоком, такое как ifили исключения, потому что оно предназначено только для отладки во время разработки.

Марк Снидович
источник
6

Важное замечание относительно assert в PHP более ранних версий, чем 7. В отличие от других языков с конструкцией assert, PHP не выводит утверждения assert полностью - он обрабатывает их как функцию (выполните debug_backtrace () в функции, вызываемой утверждением). Отключение утверждений, кажется, просто заставляет функцию ничего не делать в движке. Обратите внимание, что PHP 7 можно заставить эмулировать это поведение, установив для zend.assertions значение 0 вместо более обычных значений 1 (включено) или -1 (выключено).

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

<?php
  function foo($a) { 
    echo $a . "\n"; 
    return TRUE;
  }
  assert_options(ASSERT_ACTIVE, FALSE);

  assert( foo('You will see me.'));
  assert('foo(\'You will not see me.\')');

  assert_options(ASSERT_ACTIVE, TRUE);

  assert( foo('Now you will see'));
  assert('foo(\'both of us.\')');

Учитывая намерение assert, это ошибка, причем давно существующая, поскольку она присутствует в языке с тех пор, как assert был введен еще в PHP 4.

Строки, передаваемые в assert, оцениваются со всеми вытекающими из этого последствиями для производительности и опасностями, но это единственный способ заставить операторы assert работать так, как они должны работать в PHP (это поведение устарело в PHP 7.2).

РЕДАКТИРОВАТЬ: Изменено выше, чтобы отметить изменения в PHP 7 и 7.2

Майкл Моррис
источник
1
В PHP 7 есть / будет zend.assertionsнастройка ini на полное отключение assert().
Kontrollfreak
Это отличная новость, но, судя по имеющейся там документации, похоже, что патч для PHPUnit в порядке, добавляя обработчик обратного вызова assert, который генерирует исключение AssertionException, когда утверждения не работают в PHP 5.x. Таким образом, модульные тесты могут использовать аннотацию @expectedException AssertionException независимо от того, работают они на PHP 5.x или 7.
Майкл Моррис,
3

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

Кайл
источник
7
Но в коде все равно будут утверждения. Они просто не будут активны в производственной среде.
aaronasterling
1
Я бы оставил их в производстве и вместо этого настроил бы свой обработчик ошибок.
Дэниел В.
3

Нет, ваш коллега не должен использовать его в качестве обработчика ошибок общего назначения. Согласно инструкции:

Утверждения следует использовать только как функцию отладки. Вы можете использовать их для проверки работоспособности, которая проверяет условия, которые всегда должны быть ИСТИННЫМИ и которые указывают на некоторые ошибки программирования, если нет, или для проверки наличия определенных функций, таких как функции расширения или определенные системные ограничения и возможности.

Утверждения не должны использоваться для обычных операций времени выполнения, таких как проверка входных параметров. Как показывает практика, ваш код всегда должен работать правильно, если проверка утверждений не активирована.

Если вы знакомы с автоматизированными наборами тестов, глагол assert обычно используется для проверки вывода некоторых методов или функций. Например:

function add($a, $b) {
    return $a + $b;
}

assert(add(2,2) == 5, 'Two and two is four, dummy!');
assert(is_numeric(add(2,2)), 'Output of this function to only return numeric values.');

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

Дин Ор
источник
3

Ваш коллега действительно пытается применить дизайн по контракту (DbC) на языке Eiffel на основе книги: Object Oriented Software Construction, 2nd Edition.

Утверждение в том виде, в каком он его использовал, было бы {P} -частью логики Хора или тройки Хора: {P} C {Q}, где {P} - это предусловие assert (ion) s, а {Q} - постусловие assert (ion) s.

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

Более того, если функция assert содержит ошибки, я предлагаю вам не использовать ее в производственном коде. Тем не менее, я рекомендую вам использовать его при разработке и тестировании кода, где это необходимо.

Наконец, если вы изучите проектирование по контракту, вы обнаружите, что есть последствия использования логических утверждений в свете объектно-ориентированного классического наследования, то есть вы никогда не должны ослаблять предусловие или постусловие. Это может быть опасно для ваших полиморфных объектов-потомков, взаимодействующих друг с другом. Пока вы не поймете, что это значит - я оставлю это в покое!

Более того - я настоятельно рекомендую разработчикам PHP провести всестороннее изучение дизайна по контракту и попытаться как можно скорее включить его в PHP! Тогда все мы можем извлечь выгоду из наличия компилятора / интерпретатора с поддержкой DbC, который справился бы с проблемами, указанными в ответах (выше):

  1. Правильно реализованный компилятор с поддержкой проектирования по контракту (надеюсь) не будет содержать ошибок (в отличие от текущего утверждения PHP).
  2. Правильно реализованный компилятор с поддержкой проектирования по контракту справится с нюансами управления полиморфной логикой утверждений за вас, вместо того, чтобы ломать голову над этим вопросом!

ПРИМЕЧАНИЕ. Даже использование if-statement в качестве замены assert (предусловия) будет иметь ужасные последствия, если используется для усиления предусловия или ослабления постусловия. Чтобы понять, что это значит, вам нужно будет изучить дизайн по контракту, чтобы знать это! :-)

Удачи в учебе и учебе.

Ларри
источник