Концепция Design by Contract

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

Например для некой функции задан аргумент. В предусловии мы проверяем тип этого аргумента, скажем число, и допустим некий диапазон. В постусловии мы проверяем что возвращаемое значение, скажем непустая строка.

Что предлагает PHP для DbC?

Оказывается, ещё со времён PHP4 существует специальная встроенная функция assert, которая берёт на себя эту задачу. А именно: выдать ошибку если результат выполнения кода из аргумента не равен true.

Зачем использовать assert если есть if/throw

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

Почему полезно использовать inline код?

Функция assert в качестве аргумента может принимать строку с PHP кодом, которая затем компилируется (eval).

assert(‘is_int($var) && $var > 10’);

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

assert_options(ASSERT_ACTIVE, false);

замедления быстродейтвия практически не будет. Имейте ввиду что DbC это методика во время разработки и попав на продакшн, проверки теряют смысл.

DbC vs TDD

Одно другому совершенно не помеха. Тесты запускают код, вылавливают возможные дефекты и не дают коду деградировать при рефакторинге. Контракты «рубят на корню» потенциальные баги и гарантируют определённую экосистему: либо контракт соблюдён, либо код не исполнится.

Простой пример использования DbC

Есть somefile.php, который подключается снаружи:

assert(‘isset($config[\’a\’]’);
// …
do_something($config[‘a’]);
// …

В данном примере assert проверяет предусловие: установленная переменная $config с ключом. Таким образом кто-бы не подключал этот файл, он сразу будет обязан определить эту переменную.