Как издеваться над встроенными функциями php socket?

Я работаю над некоторым кодом, который читается из сокета, и он ошибается, когда он получает определенный большой вход. Я пошел, чтобы добавить единичный тест для этого, прежде чем исправлять его, но застрял, потому что я не могу имитировать 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 для достижения желаемой цели; как чтение файлов, так и чтение сокетов. Я не буду повторять эту статью здесь, но просто укажу, как вы должны использовать функции:

  1. Используйте rename_function чтобы дать имя старой, оригинальной функции, чтобы мы могли ее найти позже.
  2. Используйте override_function для определения нового поведения
  3. Используйте rename_function чтобы дать фиктивное имя __overridden__

Шаг 1 имеет решающее значение, если вы захотите восстановить исходное поведение после этого.

В разделе «Еда для размышлений» в конце я показываю альтернативный подход, который, по моему мнению, более надежный, и поэтому я считаю, что безопаснее заменять функции файлов при использовании phpUnit. Он создает постоянную замену функций, для которой по умолчанию используется пересылка встроенной функции. Затем он проверяет параметры ( $handle ресурса $handle в этом случае), чтобы решить, вызван ли он потоком, для которого требуется другое поведение. (Я думаю, вы могли бы назвать это примером шаблона проектирования Chain Of Responsibility.)