2

I am making an SDK client application that will work with a third-party API, and during the development process I try to adhere to PSR standards. I have two questions that I don't fully understand, and I hope to get an answer.

Composer depends:

{
    "require": {
        "php": ">=8.2",
        "php-http/client-common": "^2.5",
        "php-http/discovery": "^1.14.3",
        "php-http/httplug": "^2.3",
        "php-http/multipart-stream-builder": "^1.2",
        "psr/http-client-implementation": "^1.0",
        "psr/http-factory-implementation": "^1.0",
        "psr/http-message": "^1.0.1"
    },
    "require-dev": {
        "phpunit/phpunit": "^8.5",
        "guzzlehttp/guzzle": "^7.5",
        "guzzlehttp/psr7": "^2.4.1",
        "http-interop/http-factory-guzzle": "^1.2"
    }
}

Application tree:

./src/
├── Api
│   ├── AbstractApi.php
│   ├── Achievement.php
│   ├── AchievementInterface.php
│   ├── Clan.php
│   ├── ClanInterface.php
│   ├── Game.php
│   ├── GameInterface.php
│   ├── Rating.php
│   ├── RatingInterface.php
│   ├── User.php
│   ├── UserInterface.php
│   ├── Weapon.php
│   └── WeaponInterface.php
├── Client.php
├── ClientExtraInterface.php
├── Enum
│   ├── GameClassEnum.php
│   ├── GameClassList.php
│   ├── Http
│   │   └── RegionList.php
│   └── Leagues.php
├── Exception
│   ├── Response
│   │   ├── ApiException.php
│   │   └── InvalidJsonException.php
│   ├── UndefinedBranchException.php
│   └── UnknownMethodCallException.php
├── Helper
│   └── QueryParamsHelper.php
└── HttpClient
    ├── Builder.php
    ├── Message
    │   └── ResponseMediator.php
    └── Plugin
        ├── BypassTimeoutResponsePlugin.php
        └── ServerSupportPlugin.php
  1. In which directory is it correct to store interfaces for classes from the src/Api directory. Is it correct to leave it as it is now in the tree, or should it be moved to a separate directory, for example, src/Contacts?

  2. The question is about the mutation of the received answer. For example, how can I, adhering to the PSR, implement the modification of the received response from the API, and is it acceptable to change the received response at all. Can I do this within the ResponseMediator class?

    final class ResponseMediator
    {
        /**
         * @throws ApiException|InvalidJsonException
         */
        public static function getContent(ResponseInterface $response): array
        {
            $body = $response->getBody()->__toString();
    
            if (!str_starts_with($response->getHeaderLine('Content-Type'), 'application/json')) {
                throw new ApiException(sprintf('The response is not JSON: %s', $body));
            }
    
            try {
                /** @var array<string|int, string|int> $content */
                return json_decode($body, true, 512, JSON_THROW_ON_ERROR);
            } catch (JsonException $e) {
                throw new InvalidJsonException(
                    sprintf('Invalid JSON: %s', $e->getMessage())
                );
            }
        }
    }
    

In the last question, I am interested in the correctness of the approach to such manipulations. In fact, I can implement this using plugins (src/HttpClient/Plugin/. I hope you can help me figure it out, thank you.

wnull
  • 217
  • 6
  • 21
  • 2
    Your first question is subjective. I personally prefer to have my interfaces side-by-side with my classes, but I think I've seen the Laravel community recommend a "Contracts" folder sometimes. As long as you've got good documentation, and are consistent, the consumers of your SDK will figure it out. – Chris Haas Jan 20 '23 at 14:12
  • 2
    To your second question, I'm not seeing you change anything about PSR, you just have a method that accepts `ResponseInterface` object and are returning array. If you actually implemented an interface from the PSR, and say changed the return type from `string` to `array`, that would violate the PSR, and potentially something called LSP. – Chris Haas Jan 20 '23 at 14:22

0 Answers0