Сборка debian пакета для дополнения к PHP из PECL

Я довольно часто использую удобный класс HTTPRequest из дополнения pecl_http, который описан в официальной документации по PHP. Но в очередной раз устанавливая его из PECL поразился тому сколько шагов нужно сделать для его сборки на чистой системе, после чего решил подсобить админам, собрав для него стандартный debian-пакет.

Continue reading Сборка debian пакета для дополнения к PHP из PECL

Отлов ошибок PECL класса HTTPRequest

В модуле pecl_http есть очень удобный, но при этом не всегда хорошо задокументированный класс HTTPRequest. Одной из проблем, с которой я столкнулся при работе с ним, это то, что в случае ошибок при HTTP-соединениях, выбрасывались исключения с текстами:


(HttpInvalidParamException) Empty or too short HTTP message: ''

Понятное дело, для отладки они не подходят. Но, если вывести исключение полностью, то обычно там как-раз и были подробности проблемы:


exception 'HttpInvalidParamException' with message 'Empty or too short HTTP message: ''' in example.php:189 inner exception 'HttpRequestException' with message 'Timeout was reached; Operation timed out after 10000 milliseconds with 0 bytes received (https://example.ru/)' in example.php:160

Видно, что самое важное содержится в сообщении от inner exception. И его, оказывается, легко поймать, если поставить дополнительный блок catch на HTTPException, например:

try {
    $request->send();
} catch (HttpException $ex) {
    if (isset($ex->innerException)){
        echo $ex->innerException->getMessage();
        exit;
    } else {
        echo $ex;
        exit;
    }
}

Информация получена отсюда.

Unknown SSL protocol error

При запросе из PHP с помощью HTTPRequest к интерфейсу одной серьёзной биллинговой системы получал постоянно ошибку «Unknown SSL protocol error in connection to xxx.xxx.xxx.xxx:443». Быстрый взгляд в гугл подсказал только перебирать параметры, что оказалось вполне верной догадкой: оказывается хост нормальное работал с SSLv2, который не использовался при соединении. Пришлось указать принудительно строкой:

$req->setOptions(array('ssl' => array('version' => SSL_VERSION_SSLv2)));

Потом правда всплыли и другие прелести, вроде отдачи XML в UTF-8 с типом «text/html; charset=ISO-8859-1», но такие шутки находятся быстрее и решаются проще.

PHP и SOAP Message Security

Очередной случай из жизни натолкнул меня на мысль расписать, как из PHP создать подписанный по стандарту SOAP Message Security SOAP-запрос по SSL-соединению. Насколько я понимаю, стандарт этот не единственный, но используется достаточно широко и базируется на различных XML-стандартах W3C.

Сама суть механизма подписи хорошо описана в этой статье, из которой и я почерпнул для себя несколько полезных моментов, на которых возник ступор поначалу. Но предлагаемый мной вариант имеет одно важное дополнение: он добавляет в SOAP-сообщение ещё и информацию о сертификате, с помощью которого была сделана подпись. Кстати, меня удивила логика одной системы, для которой это делалось (кстати, написанной на Microsoft’овских компонентах): помимо сертификата в SSL-соединении они ещё проверяли сертификат и в самом запросе, хотя могли бы и воспользоваться данными HTTPS и тем самым получить ещё дополнительную проверку того, что соединение открыто тем самым агентом, который подписывал запрос.

Основным моментом в подписи является то, что собираются хэши (дайджесты) узлов с данными, которые будут подписаны (при этом узлы приводятся в канонический вид с помощью утилиты xmllint из комплекта libxml2). После чего собранные внутри одной «обёртки» дайджесты подписываются ключом сертификата, тем самым гарантируя, что если подпись верна, то данные в этих узлах не были изменены во время передачи запроса.

Класс так-же переписывает механизм HTTP-запроса к удалённому серверу, т.к. даже с помощью stream_context PHP не позволяет использовать совместно с SOAP сертификаты в формате PKCS12. В итоге используется HTTPRequest, результат которого уже транслируется далее в обработку.

Сколько же записей обновилось?

Вроде бы простой вопрос, сколько рядов было затронуто SQL-операцией UPDATE, но и здесь притаился подводный камень (о котором, правда честно предупреждают в документации). В MySQL в это число могут не попадать записи, у которых данные не потребовалось изменять. А значит, если REPLACE не подходит, то нужно проводить дополнительные проверки на тему того, сколько же данных вы обновили. Я споткнулся об этом в функции mysql_affected_rows в PHP. Там-же можно найти один из вариантов решения – использовать mysql_info, но в этом случае нужно парсить строку с ответом. Ну а дальше в вариантах SELECT‘ы и т.п.