Чи повинен я використовувати assert у своєму PHP-коді?


87

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

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

Емпіричне правило, яке застосовується в більшості мов (все, що я неясно знаю), полягає в тому, що a assertвикористовується для ствердження, що умова завжди є істинною, тоді як an ifє доречним, якщо можливо, що це іноді може провалитися.

У цьому випадку я б сказав, що assertце доречно (виходячи з мого слабкого розуміння ситуації), оскільки його recordsслід завжди встановлювати до виклику даного методу. Отже, невдача встановити запис була б помилкою в програмі, а не умовою виконання. Тут assertце допомагає переконатися (за умови адекватного тестування) у відсутності можливого шляху виконання програми, який може спричинити assertвиклик коду, що охороняється, без recordsвстановлення.

Перевага використання assertна відміну від того if, що, assertяк правило, може бути вимкнено у виробничому коді, що зменшує накладні витрати. Такі ситуації, з якими найкраще впоратися, ifцілком можливо, можуть виникнути під час роботи у виробничій системі, і тому нічого не втрачається, якщо не вдається їх вимкнути.


4
На додачу до цього, можливо, ви не захочете відключити твердження у своєму виробничому коді, оскільки вони допомагають переконатись, що умови «цього ніколи не повинно відбуватися» залишаються такими. Можливо, краще дозволити застосунку зупинитися на затвердженні, ніж дозволити своїм користувачам продовжувати йти шляхом виконання, який не повинен існувати.
derekerdmann

2
@derekerdmann: Правда. Для деяких тверджень може бути достатньо зареєструвати їх (у виробництві) або надрукувати попередження (у середовищі розробки). Але оскільки часто твердження також захищають відповідний код безпеки, ви можете також увімкнути assert_options(ASSERT_BAIL). Це все одно швидше, ніж ручні способи обходу / кидання.
mario

4
@derekerdmann Я б не погодився з цим (у контексті використання assert () у php). Це великий проміжок вразливості, оскільки assert () розглядає всі рядкові аргументи як PHP-код, отже (теоретично) можна вводити та запускати довільний код. ІМХО, твердження слід відключити на виробництві
Віталій Лебедєв

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. */');

Значення абсолютно однакові і призначені для точно тієї ж аудиторії, але перший коментар легко забути або проігнорувати (незалежно від того, скільки знаків оклику), тоді як "коментар влади" доступний не тільки для читання та розуміння людьми, він також постійно перевіряється машиною під час розробки, і його не ігноруватимуть, якщо ви налаштуєте хорошу обробку тверджень у коді та робочих звичках.

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

Так, ви повинні коментувати свій код, і так, ви повинні використовувати "власні коментарі" (твердження), коли це можливо.


що робити, якщо при розробці під час тестування ви завжди передаєте добрий стан до затвердження, але у виробництві, якщо затвердження вимкнено - якийсь користувач передає іншу умову, про яку ви не думали під час тестування? Або інакше вам потрібно продовжувати твердити постійно, але тоді це не те саме, що написати власний чек?
Дарій. V

Тоді ваша програма вийде з ладу. Правильно виправте це за допомогою операторів if та функцій обробки помилок вашої мови та середовища розробки. твердження можуть виявити проблеми, є кращі способи їх вирішення.
DaveWalley

Зверніть увагу, що станом на PHP 7.2 передача рядка в assert для оцінки застаріла. Сумно, бо це виглядало досить зручно.
Jannie Theunissen

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

Важлива примітка щодо затвердження в 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.

Рядки, передані для затвердження, оцінюються, з усіма наслідками для продуктивності та небезпеками, які з цим пов'язані, але це єдиний спосіб отримати твердження утвердження для роботи так, як вони повинні працювати в PHP (ця поведінка застаріла в PHP 7.2).

EDIT: Змінено вище, щоб зазначити зміни в 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) з мови Ейфеля на основі книги: Об’єктно-орієнтована конструкція програмного забезпечення, 2-е видання.

Твердженням, яким він користувався, було б {P} -частиною логіки Хоара або потрійним Хоаром: {P} C {Q}, де {P} - це передумова твердження (іонів) і {Q} твердження (іон) після умови.

Я хотів би взяти до уваги поради щодо функції затвердження в PHP, що має помилки. Ви не хочете використовувати код помилки. Ви дійсно хочете, щоб виробники PHP виправили помилку в твердженні. Поки вони цього не роблять, ви можете використовувати твердження, але використовуйте його, пам'ятаючи про його поточний стан помилок.

Більше того, якщо функція затвердження глючить, то я пропоную вам не використовувати її у виробничому коді. Тим не менше, я рекомендую використовувати його для розробки та тестування коду, де це доречно.

Нарешті - якщо ви вивчите дизайн за контрактом, ви виявите, що використання булевих тверджень має наслідки у світлі об’єктно-орієнтованого класичного успадкування - тобто - ви ніколи не повинні послаблювати передумови і не послаблювати пост-умови. Це може бути небезпечно для ваших поліморфних об’єктів-нащадків, які взаємодіють між собою. Поки ви не зрозумієте, що це означає - я б це залишив у спокої!

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

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

ПРИМІТКА: Навіть використання ifвами -заяви як заміни твердження (передумови) зазнає страшних наслідків, якщо воно використовується для посилення передумови або послаблення пост-стану. Щоб зрозуміти, що це означає, вам потрібно буде вивчити дизайн за контрактом, щоб знати! :-)

Щасливого навчання та навчання.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.