PHP code example of simple-as-fuck / php-api-toolkit

1. Go to this page and download the library: Download simple-as-fuck/php-api-toolkit library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

simple-as-fuck / php-api-toolkit example snippets


    'some_api_name' => [ // this key is value of first parameter ApiClient::request method
        'base_url' => 'https://some-host/some-base-url', // ll, //  https://swagger.io/docs/specification/authentication/bearer-authentication/
        ],
        'deprecated_header' => 'Deprecated', // optional default 'Deprecated', define name of deprecated response header logged into deprecation log
    ],

/**
 * @var \SimpleAsFuck\ApiToolkit\Service\Client\Config $config
 * @var \Psr\Log\LoggerInterface $logger
 */

/** @var \SimpleAsFuck\ApiToolkit\Service\Client\DeprecationsLogger|null $deprecationsLogger */
$deprecationsLogger = new \SimpleAsFuck\ApiToolkit\Service\Client\DeprecationsLogger(
    $config,
    $logger,
);

$client = new \SimpleAsFuck\ApiToolkit\Service\Client\ApiClient(
    $config,
    new \GuzzleHttp\Client(),
    new \GuzzleHttp\Psr7\HttpFactory(),
    $deprecationsLogger
);

/**
 * @var RequestDataClass $dataForRequestBody
 * @var \SimpleAsFuck\ApiToolkit\Service\Transformation\Transformer<RequestDataClass> $transformerForRequestBody
 * @var \SimpleAsFuck\Validator\Rule\Custom\UserClassRule<ResponseDataClass> $classRuleForResponseBody
 */

try {
    $responseObject = $client->requestObject(
        'some_api_name',
        'POST',
        '/to-some-action',
        $dataForRequestBody,
        $transformerForRequestBody,
        options: [\GuzzleHttp\RequestOptions::TIMEOUT => 3600],
    );
    /*
     * response has getter for json decoded body which is validated after decoding by rule chain
     * request method return object rule, so you can easily validate response json structure
     * is recommended use some you class rule documented here: https://github.com/simple-as-fuck/php-validator#user-class-rule
     * and convert api data structure into some your specific object instance
     */
    $dataFromResponseBody = $responseObject->class($classRuleForResponseBody)->notNull();
}
catch (\SimpleAsFuck\ApiToolkit\Data\Client\ApiException $exception) {
    /*
     * if anything go wrong in request/response processing or response json parsing
     * \SimpleAsFuck\ApiToolkit\Model\Client\ApiException is thrown,
     * and you can handle any error from communication
     */
    // if exception contains http response, rfc9457 status or http status is here, otherwise zero is returned
    $exception->getCode();
    // exception message for logging or debugging is build from https://datatracker.ietf.org/doc/html/rfc9457
    // extended with optional message property, you SHOULD log this, so you know WTF is going wrong
    $logger->error($exception->getMessage()); 
    // short information for end user WTF just happened, if is not null you SHOULD show the tittle on your front end
    $exception->getProblemDetail()?->title;
    // information for end user with more detail, if is not null you SHOULD show the detail on your front end,
    // because detail can contain clue or information how user can solve error, mainly if error is his false :D
    $exception->getProblemDetail()?->detail;
    // parse from error response some extensions, is RECOMMENDED ignoring all errors from error response parsing
    // because you can lose another useful data from error response or if response si corrupted you can lose previous exception
    $exception->getProblemDetailExtensions()?->property('some_error_property')->string()->nullable(failAsNull: true);
}



/**
 * @var \SimpleAsFuck\ApiToolkit\Service\Client\ApiClient $client
 */

// method call POST /webhook request
$webhook = $client->addWebhookListener('some_api_name', 'some_webhook_event_type', 'https://some-client/listening-url');

// you can register listener URL with priority and first,
// if on server is more than one listener for same webhook type,
// this behaviours is implemented in server services in this package,
// but other server implementations can behave differently
// or webhook functionality may not be implemented, so always read specific API documentation!
$webhook = $client->addWebhookListener(
    'some_api_name',
    'some_webhook_event_type',
    'https://some-client/listening-url',
    \SimpleAsFuck\ApiToolkit\Model\Webhook\Priority::NORMAL,
    ['some_key' => '89']
);

// you can save webhook identifier for future use
// deletion while listening is no longer needed, or some data loading in listening url
$webhook->id;



/**
 * @var \SimpleAsFuck\ApiToolkit\Service\Client\ApiClient $client
 * @var non-empty-string $webhookId
 */

// method call DELETE /webhook request
$client->removeWebhookListener('some_api_name', $webhookId);


class YourListeningController
{
    public function handle(
        \Psr\Http\Message\ServerRequestInterface $request
        //\Symfony\Component\HttpFoundation\Request $request
    ): \Psr\Http\Message\ResponseInterface {
    //): \Symfony\Component\HttpFoundation\Response {
        $rules = \SimpleAsFuck\ApiToolkit\Factory\Server\Validator::make($request)->webhook();
        //$rules = \SimpleAsFuck\ApiToolkit\Factory\Symfony\Validator::make($request)->webhook();
        $webhook = $rules->notNull();
        $attribute = $rules->attributes()->key('some_attribute')->parseInt()->positive()->nullable();
        $attribute = $webhook->params->attributes['some_attribute'] ?? null;

        // run some you logic
        // you should expect than listening action can be called multiple times
        // because of some network error or another failure

        $response = \SimpleAsFuck\ApiToolkit\Factory\Server\ResponseFactory::makeWebhookResult();
        //$response = \SimpleAsFuck\ApiToolkit\Factory\Symfony\ResponseFactory::makeWebhookResult();
        $response = \SimpleAsFuck\ApiToolkit\Factory\Server\ResponseFactory::makeWebhookResult(
            // you can inform server site application to stop
            // dispatching webhook for another listener after current listener
            // which has less priority
            // server services in this package support this functionality
            stopDispatching: true
        );
        return $response;
    }
}


// star of your action

$rules = \SimpleAsFuck\ApiToolkit\Factory\Server\Validator::make($request);
//$rules = \SimpleAsFuck\ApiToolkit\Factory\Symfony\Validator::make($request);

// validate some query parameter
$someQueryValidValue = $rules->query()->key('someKey')->string()->parseInt()->positive()->notNull();

/** @var \SimpleAsFuck\ApiToolkit\Service\Server\UserQueryRule<YourClass> $yourQueryRule */
$yourObjectFromRequestQuery = $rules->query()->class($yourQueryRule)->notNull();

// validate something from request body with json format
$someJsonValidValue = $rules->json()->object()->property('someProperty')->string()->notEmpty()->maxChar(255)->notNull();

/** @var \SimpleAsFuck\Validator\Rule\Custom\UserClassRule<YourClass> $yourClassRule */
$yourObjectFromRequestBody = $rules->json()->object()->class($yourClassRule)->notNull();

// http error in your action
/** @var bool $shitHappens */
if ($shitHappens) {
    throw new \SimpleAsFuck\ApiToolkit\Model\Server\ApiException(
        'Shit Happens',
        new \SimpleAsFuck\ApiToolkit\DataObject\Common\ProblemDetail(
            'https://shit-happens.wtf/error',
            418,
            'Shit happens',
            'Developers are looking for some shit in their code.',
            '/error/418',
        ),
        (object) ['wtf' => 418],
        internalMessage: 'Shit Happens, enjoy looking for what happens in the code.'
    );
    //throw new \Symfony\Component\HttpKernel\Exception\HttpException(418, 'Shit Happens'),
}

// end of your action

/**
 * @var YourClass $yourDataForResponseBody
 * @var \SimpleAsFuck\ApiToolkit\Service\Transformation\Transformer<YourClass> $transformer 
 */

// response with one object
$response = \SimpleAsFuck\ApiToolkit\Factory\Server\ResponseFactory::makeJson($yourDataForResponseBody, $transformer, \Kayex\HttpCodes::HTTP_OK);
//$response = \SimpleAsFuck\ApiToolkit\Factory\Symfony\ResponseFactory::makeJson($yourDataForResponseBody, $transformer, \Kayex\HttpCodes::HTTP_OK);

// response with some array or collection (avoiding out of memory problem recommended some lazy loading iterator)
$response = \SimpleAsFuck\ApiToolkit\Factory\Server\ResponseFactory::makeJsonStream(new \ArrayIterator([$yourDataForResponseBody]), $transformer);
//$response = \SimpleAsFuck\ApiToolkit\Factory\Symfony\ResponseFactory::makeJsonStream(new \ArrayIterator([$yourDataForResponseBody]), $transformer);
//$response = \SimpleAsFuck\ApiToolkit\Factory\Symfony\ResponseFactory::makeJsonStream([$yourDataForResponseBody], $transformer);



/**
 * @var \SimpleAsFuck\ApiToolkit\Service\Config\Repository $configRepository
 * @var \Psr\Log\LoggerInterface $logger
 */

try {
    // some breakable logic
}
catch(\SimpleAsFuck\ApiToolkit\Model\Server\ApiException $exception) {
    // exception message for logging or debugging, you SHOULD log this, so you know WTF is going wrong
    $logger->error(implode(', ', [$exception->getMessage(), (string) $exception->getInternalMessage()]), [
        'type' => $exception->getProblemDetail()?->type,
        'status' => $exception->getCode(),
        'instance' => $exception->getProblemDetail()?->instance,
        'extensions' => $exception->getProblemDetailExtensions(),
    ]);

    $response = \SimpleAsFuck\ApiToolkit\Factory\Server\ResponseFactory::makeJson(
    //$response = \SimpleAsFuck\ApiToolkit\Factory\Symfony\ResponseFactory::makeJson(
        $exception,
        // transformer will convert exception in to https://datatracker.ietf.org/doc/html/rfc9457 json object with message and all another extensions
        new \SimpleAsFuck\ApiToolkit\Service\Server\ApiExceptionTransformer(),
        $exception->getCode()
    );
}
// if you use Symfony Http Exceptions you can use HttpExceptionTransformer
catch (\Symfony\Component\HttpKernel\Exception\HttpExceptionInterface $exception) {
    $logger->error($exception->getMessage(), ['status' => $exception->getStatusCode()]);

    $response = \SimpleAsFuck\ApiToolkit\Factory\Symfony\ResponseFactory::makeJson(
        $exception,
        // transformer will convert exception into json object
        // with message property contains message from http exception
        // and https://datatracker.ietf.org/doc/html/rfc9457#name-status property
        new \SimpleAsFuck\ApiToolkit\Service\Symfony\HttpExceptionTransformer(),
        $exception->getStatusCode()
    );
}
catch (\Throwable $exception) {
    $logger->error($exception->getMessage());

    $response = \SimpleAsFuck\ApiToolkit\Factory\Server\ResponseFactory::makeJson(
    //$response = \SimpleAsFuck\ApiToolkit\Factory\Symfony\ResponseFactory::makeJson(
        $exception,
        // transformer will convert exception in to json object
        // if application has turned off debug, message property contain only "Internal server error"
        // but with enabled debug message contains exception type, message, file and line where was exception thrown
        // with enabled debug json object also contains trace property with exception stacktrace
        // and json object contains https://datatracker.ietf.org/doc/html/rfc9457#name-status property always with 500 http code
        new \SimpleAsFuck\ApiToolkit\Service\Server\ExceptionTransformer($configRepository),
        \Kayex\HttpCodes::HTTP_INTERNAL_SERVER_ERROR
    );
}



/**
 * @var \SimpleAsFuck\ApiToolkit\Service\Webhook\Repository $webhookRepository
 * @var \SimpleAsFuck\ApiToolkit\Service\Webhook\WebhookClient $webhookClient
 */

$dispatcher = new \SimpleAsFuck\ApiToolkit\Service\Webhook\WebhookDispatcher($webhookRepository, $webhookClient);

// simplest dispatch, when something happened on the server side,
// webhooks calls are added into a queue
$dispatcher->dispatch('some_webhook_event_type', options: [\GuzzleHttp\RequestOptions::TIMEOUT => 20]);

// webhook call with some attribute,
// for example, you can dispatch an event type with some specific entity id
$dispatcher->dispatch('some_webhook_event_type', attributes: ['some_attribute' => '1256'], options: [\GuzzleHttp\RequestOptions::TIMEOUT => 20]);

// webhook call try immediately without adding call into queue
// only if the first call fails, webhook call is added into queue for retry
$dispatcher->call('some_webhook_event_type', options: [\GuzzleHttp\RequestOptions::TIMEOUT => 20]);

// simple dispatch when the first call will try after 1 minute
$dispatcher->dispatchWithDelay(60, 'some_webhook_event_type', options: [\GuzzleHttp\RequestOptions::TIMEOUT => 20]);

// you can build webhook sequence by your custom logic without using $webhookRepository
// beware you still need some queue for webhook retries if calls failed
/** @var iterable<\SimpleAsFuck\ApiToolkit\Model\Webhook\Webhook> $webhooks */
$webhookClient->dispatchWebhooks($webhooks, options: [\GuzzleHttp\RequestOptions::TIMEOUT => 20]);
// first try without queue
$webhookClient->callWebhooks($webhooks, options: [\GuzzleHttp\RequestOptions::TIMEOUT => 20]);

console
composer 
console
php artisan vendor:publish --tag=api-toolkit-config
console
php artisan vendor:publish --tag=api-toolkit-migration