andy87 / knockknock
PHP Facade Curl library - developed by and_y87
Requires
- php: >=8.0.0
- ext-curl: *
- ext-json: *
Requires (Dev)
- phpunit/phpunit: ^10.5
This package is auto-updated.
Last update: 2024-10-28 07:12:51 UTC
README
KnockKnock - это простая библиотека, реализующая Фасад и предоставляющая удобный интерфейс для выполнения запросов в PHP, используя расширение cURL. Она упрощает работу, предоставляя более высокоуровневый API и быстрый доступ к настройкам.
Цель: сделать простой и лёгкий в настройке компонента и запроса пакет.
P.S. я знаю про существование таких библиотек как: Guzzle, Client (в моём любимом Yii2), но хотелось попробовать создать свою реализацию.
Без "лишних" данных, вызовов и настроек, nullWarningStyle - только то, что нужно: сухо, лаконично, минималистично.
Разумеется, это не конкурент, а просто попытка создать что-то своё
Содержание:
- Установка
- Объект - Базовый класс
- Объект - Запрос
- Объект - Ответ
- Дополнительные возможности
- Расширение на основе базового класса
- Пример Custom реализации
- Тесты
Установка
Требования
- php 8.0
- ext cURL
- ext JSON
Composer
Добавление пакета в проект
Используя: консольные команды. (Предпочтительней)
- при composer, установленном локально:
composer require andy87/knockknock
- при использовании composer.phar:
php composer.phar require andy87/knockknock
Далее: обновление зависимостей composer update
Используя: файл `composer.json`
Открыть файл composer.json
В раздел, ключ require
добавить строку
"andy87/knockknock": "*"
Далее: обновление зависимостей composer update
Используя: подключение авто загрузчика
В месте, где необходимо использовать библиотеку, подключите авто загрузчик:
require_once 'путь/к/корню/проекта/autoload.php';
Примечания:
- Убедитесь, что путь к autoload.php правильный и соответствует структуре вашего проекта.
- - - - -
Логика работы библиотеки (блок-схема)
Простой пример работы.
use andy87\knock_knock\lib\Method; use andy87\knock_knock\lib\ContentType; use andy87\knock_knock\core\Operator; use andy87\knock_knock\core\Response; // Получаем компонент реализующий отправку запросов $operator = new Operator( $_ENV['API_HOST'] )->disableSSL(); /** * Краткая форма записи (с не очевидным объектом запроса) */ $content = $operator->send( $operator->constructRequest(Method::GET, 'info/me') )->content; /** * Детальная форма записи с дополнительными возможностями */ $request = $operator->constructRequest(Method::GET, 'info/me'); // Создаём объект запроса $request->setCurlInfo([ CURLINFO_CONTENT_TYPE ]); // Назначаем опции cURL $response = $operator->send($request); // Отправляем запрос и получаем ответ $content = $response->content; // Получаем данные ответа $curlOptions = $response->request->curlOptions; // Получаем опции cURL $output = ( $curlOptions[CURLINFO_CONTENT_TYPE] === ContentType::JSON ) ? json_decode( $content ) : $content; print_r( $output );
- - - - -
Базовый класс
use andy87\knock_knock\core\Operator;
PHP Фасад\Адаптер для отправки запросов через ext cURL
ReadOnly свойства:
- commonRequest
- Объект содержащий параметры, назначаемые всем исходящим запросам
- realRequest
- Используемый запрос
- eventHandlers
- Список обработчиков событий
- host
- Хост, на который будет отправляться запросы
- logs
- Список логов
Возможности/фичи:
- Настройки параметров запросов
- Защита данных от перезаписи
- Обработчики событий
- Инкапсуляция
- Singleton
- логирование
ВАЖНЫЙ МОМЕНТ!
- В классах применяется инкапсуляция, поэтому для доступа к свойствам компонентов используются ReadOnly свойства.
CURL_OPTIONS
по умолчанию пустые! В большинстве случаев, для получения валидных ответов, требуется задать необходимые настройки.
"Получение" объекта/экземпляра класса
Передавая параметры напрямую в конструктор:
$operator = new Operator( $_ENV['API_HOST'], $commonRequestParams );
Применяя, паттерн Singleton:
$operator = Operator::getInstance( $_ENV['API_HOST'], $commonRequestParams );
Методы возвращают объект(экземпляр класса Operator
), принимая на вход два аргумента:
string $host
- хостarray $operatorConfig
- массив с настройками для всех исходящих запросов.
При создании объекта Operator
будет вызван метод init()
, который запускает пользовательские инструкции.
После выполнения init()
запускается обработчик события привязанный к ключу EVENT_AFTER_CONSTRUCT
Общие настройки запросов
Что бы указать настройки применяемые ко всем исходящим запросам, при создании объекта `Operator` передаётся массив (ключ - значение), с необходимыми настройками.Пример настройки:
// настройки для последующих исходящих запросов $commonRequestParams = [ Request::SETUP_PROTOCO => $_ENV['API_PROTOCOL'], Request::SETUP_CONTENT_TYPE => Request::CONTENT_TYPE_JSON, Request::SETUP_CURL_OPTIONS => [ CURLOPT_HEADER => false, CURLOPT_RETURNTRANSFER => true ] ]; // Получаем компонент для отправки запросов $operator = new Operator( $_ENV['API_HOST'], $commonRequestParams ); //Применяя, паттерн Singleton: $operator = Operator::getInstance( $_ENV['API_HOST'], $commonRequestParams );
Доступные ключи для настройки(константы класса Request
):
SETUP_PROTOCOL
SETUP_HOST
SETUP_METHOD
SETUP_HEADERS
SETUP_CONTENT_TYPE
SETUP_DATA
SETUP_CURL_OPTIONS
SETUP_CURL_INFO
Обработчики событий
Список событий
EVENT_AFTER_CONSTRUCT
после создания объекта knockKnockEVENT_CREATE_REQUEST
после создания объекта запросаEVENT_BEFORE_SEND
перед отправкой запросаEVENT_CURL_Operator
перед отправкой curl запросаEVENT_CREATE_RESPONSE
после создания объекта ответаEVENT_AFTER_SEND
после получения ответа
Пример установки обработчиков событий
$operator->setupEventHandlers([ Operator::EVENT_AFTER_CONSTRUCT => function( Operator $operator ) { // ... }, Operator::EVENT_CREATE_REQUEST => function( Operator $operator, Request $request ) { // ... }, Operator::EVENT_BEFORE_SEND => function( Operator $operator, Request $request ) { // ... }, Operator::EVENT_CURL_HANDLER => function( Operator $operator, resource $ch ) { // ... }, Operator::EVENT_CREATE_RESPONSE => function( Operator $operator, Response $response ) { // ... }, Operator::EVENT_AFTER_SEND => function( Operator $operator, Response $response ) { // ... } ]);
Первый аргумент - ключ события, второй - callback функция.
Все callback функции принимают первым аргументом объект/экземпляр класса Operaotr
.
Вторым аргументом передаётся объект/экземпляр класса в зависимости от события:
Request
- для событийEVENT_CREATE_REQUEST
,EVENT_BEFORE_SEND
Response
- для событийEVENT_CREATE_RESPONSE
,EVENT_AFTER_SEND
- - - - -
Запрос
use andy87\knock_knock\core\Request;
Объект запроса, содержащий данные для отправки запроса.
ReadOnly свойства:
- protocol - протокол
- host - хост
- endpoint - конечная точка
- method - метод
- headers - заголовки
- contentType - тип контента
- data - данные
- curlOptions - опции cURL
- curlInfo - информация cURL
- params - параметры запроса
- url - полный URL
- params - все свойства в виде массива
- fakeResponse - установленные фэйковые данные ответа
- errors - лог ошибок
Создание объекта запроса
Передавая параметры напрямую в конструктор:
$request = new Request( 'info/me', [ Request::METHOD => Method::POST, Request::DATA => [ 'client_id' => 34 ], Request::HEADERS => [ 'api-secret-key' => $_ENV['API_SECRET_KEY'] ], Request::CURL_OPTIONS => [ CURLOPT_TIMEOUT => 10 ], Request::CURL_INFO => [ CURLINFO_CONTENT_TYPE, CURLINFO_HEADER_SIZE, CURLINFO_TOTAL_TIME ], Request::CONTENT_TYPE => ContentType::FORM_DATA, ]);
Методом, который вызывает callback функцию, привязанную к ключу EVENT_CREATE_REQUEST
$request = $operator->constructRequest(Method::GET, 'info/me', [ Request::METHOD => Method::POST, Request::DATA => [ 'client_id' => 45 ], Request::HEADERS => [ 'api-secret-key' => $_ENV['API_SECRET_KEY'] ], Request::CURL_OPTIONS => [ CURLOPT_TIMEOUT => 10 ], Request::CURL_INFO => [ CURLINFO_CONTENT_TYPE, CURLINFO_HEADER_SIZE, CURLINFO_TOTAL_TIME ], Request::CONTENT_TYPE => ContentType::FORM_DATA, ]);
Клонируя существующий объект запроса:
$request = $operator->constructRequest(Method::GET, 'info/me'); $response = $operator->send($request); //Клонирование объекта запроса (без статуса отправки) $cloneRequest = $request->clone(); // Отправка клона запроса $response = $operator->setupRequest( $cloneRequest )->send();
Назначение/Изменение/Получение отдельных параметров запроса (set/get)
Таблица set/get методов для взаимодействия с отдельными свойствами запроса
$request = $operator->constructRequest(Method::GET, 'info/me'); $request->setMethod( Method::GET ); $request->setData(['client_id' => 67]); $request->setHeaders(['api-secret-key' => 'secretKey67']); $request->setCurlOptions([ CURLOPT_TIMEOUT => 10, CURLOPT_RETURNTRANSFER => true ]); $request->setCurlInfo([ CURLINFO_CONTENT_TYPE, CURLINFO_HEADER_SIZE, CURLINFO_TOTAL_TIME ]); $request->setContentType( ContentType::JSON ); $protocol = $request->getPrococol(); // String $host = $request->getHost(); // String // ... аналогичным образом доступны и другие подобные методы для получения свойств запроса
Назначение запроса с переназначением свойств
$operator->setupRequest( $request, [ Request::SETUP_HOST => $_ENV['API_HOST'], Request::SETUP_HEADERS => [ 'api-secret' => $_ENV['API_SECRET_KEY'] ], ]);
setupRequest( Request $request, array $options = [] ): self
addError( string $error )
Добавление ошибки в лог ошибок
$request = $operator->constructRequest(Method::GET, 'info/me'); $request->addError('Ошибка!');
- - - - -
Ответ
use andy87\knock_knock\core\Response;
Объект ответа, содержащий данные ответа на запрос.
ReadOnly свойства
- content
- данные ответа
- httpCode
- код ответа
- request
- объект запроса, содержащий данные о запросе
- curlOptions
- быстрый доступ к request->curlOptions
- curlInfo
- быстрый доступ к request->curlInfo
Создание объекта ответа
Передавая параметры напрямую в конструктор:
$response = new Response('{"id" => 806034, "name" => "and_y87"}', 200 );
Методом, который вызывает callback функцию, привязанную к ключу EVENT_CREATE_RESPONSE
$response = $operator->constructResponse([ Response::CONTENT => [ 'id' => 806034, 'name' => 'and_y87' ], Response::HTTP_CODE => 400, ], $request );
constructResponse( array $responseParams, ?Request $request = null ): Response
Отправка запроса
send( ?Request $request = null ): Response
Вызов возвращает объект/экземпляр класса Response
.
Срабатывают callback функции, привязанные к ключам:
EVENT_AFTER_SEND
EVENT_CREATE_RESPONSE
EVENT_BEFORE_SEND
EVENT_CURL_HANDLER
$operator = new Operator( $_ENV['API_HOST'] ); $request = $operator->constructRequest(Method::GET, 'info/me'); $response = $operator->send($request); // Аналог $operator = new Operator( $_ENV['API_HOST'] ); $response = $operator->send( $operator->constructRequest(Method::GET, 'info/me') );
Нельзя повторно отправить запрос, выбрасывается исключение RequestCompleteException
.
Для повторной отправки запроса, необходимо создать новый объект запроса и использовать его:
$operator = new Operator( $_ENV['API_HOST'] ); $request = $operator->constructRequest(Method::GET, 'info/me'); $response = $operator->send($request); // повторная отправка запроса $response = $operator->send($request->clone());
Отправка запроса с фэйковым ответом
// параметры возвращаемого ответа $fakeResponse = [ Response::HTTP_CODE => 200, Response::CONTENT => '{"id" => 8060345, "nickName" => "and_y87"}' ]; $request->setFakeResponse( $fakeResponse ); $response = $operator->send( $request );
объект $response
будет содержать в свойствах content
, httpCode
данные переданные в аргументе $fakeResponse
Данные в ответе
В созданный объект Response
, чей запрос не был отправлен, разрешено задавать данные, используя методы группы set
.
$response = $operator->send($request); $response ->setHttpCode(200) ->setContent('{"id" => 8060345, "nickName" => "and_y87"}');
Внимание! Если данные в объекте уже существуют, повторно задать их нельзя выбрасывается ParamUpdateException
.
В случае необходимости заменить данные, используется вызов метода replace( string $key, mixed $value )
см. далее
Подмена данных
Это сделано для явного действия, когда необходимо заменить данные в объекте `Response`.$response = $operator->send($request); $response ->replace( Response::HTTP_CODE, 200 ) ->replace( Response::CONTENT, '{"id" => 8060345, "nickName" => "and_y87"}' );
Данные запроса из ответа
Для получения из объекта Response
данных запроса, необходимо обратиться к ReadOnly свойству request
и далее взаимодействовать с ним аналогично объекту Request
$operator = new Operator( $_ENV['API_HOST'] ); $response = $operator->setRequest( $operator->constructRequest(Method::GET, 'info/me') )->send(); // Получение компонента запроса $request = $response->request; $method = $request->method; // получение метода запроса
Получения свойств cURL запроса
$operator = new Operator( $_ENV['API_HOST'] ); $response = $operator->setRequest( $operator->constructRequest(Method::GET, 'info/me') )->send(); $response->request; // Получение свойств через объект запроса $curlOptions = $response->request->curlOption; $curlInfo = $response->request->curlInfo; //Вариант с использованием быстрого доступа $curlOptions = $response->curlOption; $curlInfo = $response->curlInfo;
asArray()
Преобразование в массив.
- преобразование данных ответа на запрос
asArray()
- преобразование всего объекта в массив
asArray(true)
$response = $operator->send($request)->asArray(); // $response $array = $response->content; // Array$response
addError( string $error )
Добавление ошибки в лог ошибок
$request = $operator->constructRequest(Method::GET, 'info/me'); $response = $operator->send($request); $response->addError('Ошибка!');
- - - - -
- - - - -
Дополнительные возможности
SSL
Функционал включения/отключения SSL верификации в объектах Operaotr
& Request
.
В curlOptions
добавляется ключ CURLOPT_SSL_VERIFYPEER
и CURLOPT_SSL_VERIFYHOST
.
->disableSSL( bool $verifyPeer = false, int $verifyHost = 0 );
->enableSSL( bool $verifyPeer = true, int $verifyHost = 2 );
Operaotr
- для всех запросов
$operator = new Operator( $_ENV['API_HOST'] ); $operator->disableSSL(); $request = $operator->constructRequest(Method::GET, 'info/me'); $response = $operator->setupRequest( $request )->send();
Request
- для конкретного запроса
$operator = new Operator( $_ENV['API_HOST'] )->disableSSL(); $request = $operator->constructRequest(Method::GET, 'info/me'); $request->enableSSL(); $response = $operator->setupRequest( $request )->send();
Cookie
В объекте Operaotr
имеется функционал использования cookie.
Operaotr
- для всех запросов
$operator = new Operator( $_ENV['API_HOST'] ); $cookie = $_ENV['COOKIE']; $jar = $_ENV['COOKIE_JAR']; $operator->useCookie( $cookie, $jar );
$operator->useCookie( string $cookie, string $jar, ?string $file = null )
по умолчанию $file = null
и $file
приравнивается к $jar
Логирование
Добавление сообщений во внутренний массив logs
$operator = new Operator( $_ENV['API_HOST'] ); $operator->addLog( 'Какое то сообщение' );
$operator->addLog( string $message )
- - - - -
- - - - -
Расширения на основе базового класса
KnockKnockOctopus
Класс с функционалом простой реализации отправки запросов и минимальными настройками
Доступные методы.
Каждый метод принимает два аргумента:
Простой пример использования
$knockKnockOctopus = new KnockKnockOctopus($_ENV['API_URL']); $knockKnockOctopus->get( '/profile', [ 'id' => 806034 ] ); //GET запрос $knockKnockOctopus->post( '/new', [ //POST запрос 'name' => 'Новая новость', 'content' => 'Текст новости' ]);
- - - - -
KnockKnockSecurity
Расширяет класс KnockKnockOctopus, предоставляя доступ к функционалу для простой и
быстрой реализации авторизации, и настройки запросов.
$knockKnockSecurity = new KnockKnockSecurity($_ENV['API_URL']); // Настройка параметров запроса по умолчанию $knockKnockSecurity ->disableSSL() ->setupAuthorization( KnockKnockSecurity::TOKEN_BEARER, 'token' ) ->setupHeaders([ 'X-Api-Key' => $_ENV['X_API_KEY'] ]) ->setupContentType( ContentType::JSON ) ->on( Operator::EVENT_AFTER_SEND, function( Operator $operator, Response $response ) => { $logFilePath = $_SERVER['DOCUMENT_ROOT'] . '/api_log.txt'; file_put_contents( $logFilePath, $response->content, FILE_APPEND ); }); // Получение ответа на запрос методом `patch` $responsePatch = $knockKnockSecurity->patch( 'product', [ 'price' => 1000 ]); $product = $responsePatch->asArray(); $price = $product['price']; // Изменение типа контента на `application/json`, для следующего запроса $knockKnockSecurity->useContentType( ContentType::JSON ); // Отправка POST запроса и получение ответа $responsePost = $knockKnockSecurity->post( 'category', [ 'name' => 'Фреймворки' ]); $response = json_decode( $responsePost->content ); $category_id = $response->id;
- - - - -
- - - - -
Custom реализация
Custom реализация Базового класса, к примеру с добавлением логирования работающим "под капотом"
class KnockKnockYandex extends Operator { private const LOGGER = 'logger'; private string $host = 'https://api.yandex.ru/' private string $contentType = ContentType::JSON private YandexLogger $logger; /** * @return void */ public function init(): void { $this->setupYandexLoggerEventHandlers(); } /** * @param array $callbacks * * @return self */ private function setupYandexLoggerEventHandlers( array $callbacks ): self { $this->on( self::AFTER_CREATE_REQUEST, function( Request $request ) => { $logData = $this->getLogDataByRequest( $request ); $this->addYandexLog( $logData ); }; $this->on(self::EVENT_AFTER_SEND, function( Response $response ) => { $logData = $this->getLogDataByRequest( $response->request ); $this->addYandexLog( $logData ); }; } /** * @param Request $request * * @return array */ private function getLogDataByRequest( Request $request ): array { return $request->getParams(); } /** * @param array $logData * * @return void */ private function addYandexLog( array $logData ): bool { return $logger->log( $logData ); } }
Пример использования custom реализации
$knockKnockYandex = KnockKnockYandex::getInstanсe( $_ENV['API_HOST'], [ KnockKnockYandex::LOGGER => new YandexLogger(), ]); $response = $knockKnockYandex->setupRequest( 'profile', [ Request::METHOD => Method::PATCH, Request::DATA => [ 'city' => 'Moscow' ], ]); // Логирование `afterCreateRequest` $response = $knockKnockYandex->send(); // Логирование `afterSend`
- - - - -
- - - - -
Тесты
- tests: 100+
- assertions: 350+
Запуск тестов:
Нативный
vendor/bin/phpunit
Информационный
vendor/bin/phpunit --testdox
С логированием
vendor/bin/phpunit --log-junit "tests/logs/phpunit.xml"
Лицензия
https://github.com/andy87/KnockKnock под лицензией CC BY-SA 4.0
Для получения дополнительной информации смотрите http://creativecommons.org/licenses/by-sa/4.0/
Свободно для не коммерческого использования
С указанием авторства для коммерческого использования