Я работаю над некоторым кодом, который читается из сокета, и он ошибается, когда он получает определенный большой вход. Я пошел, чтобы добавить единичный тест для этого, прежде чем исправлять его, но застрял, потому что я не могу имитировать fread
(и другие встроенные функции PHP, которые я использую, например, fsockopen
, feof
и т. Д.).
Простыми словами, моя проблема заключается в том, что этот код не работает с « Неустранимая ошибка: не удается переопределить fgets () … »:
function fgets($fp){ return "xxx"; }
Я понимаю, что могу создать класс оболочки-сокета, который использует мой настоящий код, а затем я мог бы создать макет для этого класса-оболочки. Но это The Tail Wagging The Dog, и я могу думать о причинах, по которым это плохая идея, помимо принципа вещи. (Например, код более сложный, эффективность, наличие кода рефакторинга еще не тестировалось.)
Итак, мой вопрос заключается в том, как я могу заменить встроенную реализацию fgets()
моим собственным, в рамках единичного теста? (Или, если вы хотите думать нестандартно, вопрос может быть сформулирован так: как я могу управлять строкой, возвращаемой вызовом fgets($socket)
, когда $socket
является возвращаемым значением из вызова fsockopen
?)
В СТОРОНЕ
Установка apd, как того требует правильный ответ, – тяжелая работа; он был выпущен в последний раз в 2004 году и не поддерживает php 5.3 из коробки. Нет пакета Ubuntu для него, а также не pecl install apd
. Итак, вот процедуры его установки (это для ubuntu 10.04) (все делается как root):
pecl download apd tar xzf apd-1.0.1.tgz cd apd-1.0.1 phpize ./configure # Do edits (see below) make install
См. Https://bugs.php.net/bug.php?id=58798 для исправления, которое вам нужно сделать. NB. есть только одна строка, которую вы действительно должны изменить, поэтому вы можете сделать это вручную: следующим образом: откройте php_apd.c в текстовом редакторе, перейдите к строке 967 и замените строку CG(extended_info) = 1
следующим образом:
CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
Наконец, вам нужно добавить запись php.ini, чтобы активировать ее. См. Http://php.net/manual/en/apd.installation.php
Если у вас нет доступа к APD или Runkit, но они используют пространства имен, попробуйте это: https://stackoverflow.com/a/5337635/664108 (ответ в ссылке относится к time (), но это не имеет значения)
Посмотрите на них:
bool rename_function (строка $ original_name, строка $ new_name)
bool override_function (строка $ function_name, строка $ function_args, строка $ function_code)
Измените fsockopen
на fopen
чтобы сделать mock, и не меняйте никаких других функций.
$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
в
$fp = fopen("/path/to/your/dummy_data_file");
Я рассказал о своих опытах, которые содержат полный рабочий код, показывающий, как использовать override_function для достижения желаемой цели; как чтение файлов, так и чтение сокетов. Я не буду повторять эту статью здесь, но просто укажу, как вы должны использовать функции:
rename_function
чтобы дать имя старой, оригинальной функции, чтобы мы могли ее найти позже. override_function
для определения нового поведения rename_function
чтобы дать фиктивное имя __overridden__
Шаг 1 имеет решающее значение, если вы захотите восстановить исходное поведение после этого.
В разделе «Еда для размышлений» в конце я показываю альтернативный подход, который, по моему мнению, более надежный, и поэтому я считаю, что безопаснее заменять функции файлов при использовании phpUnit. Он создает постоянную замену функций, для которой по умолчанию используется пересылка встроенной функции. Затем он проверяет параметры ( $handle
ресурса $handle
в этом случае), чтобы решить, вызван ли он потоком, для которого требуется другое поведение. (Я думаю, вы могли бы назвать это примером шаблона проектирования Chain Of Responsibility.)