Test Failed
Push — master ( 98997f...e65f7f )
by Dominik
02:18
created

app/Controller/Course/CourseSearchController.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Chubbyphp\ApiSkeleton\Controller\Course;
6
7
use Chubbyphp\ApiHttp\Error\Error;
8
use Chubbyphp\ApiHttp\Manager\RequestManagerInterface;
9
use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface;
10
use Chubbyphp\Model\RepositoryInterface;
11
use Chubbyphp\Translation\TranslatorInterface;
12
use Chubbyphp\Validation\Error\NestedErrorMessages;
13
use Chubbyphp\Validation\ValidatorInterface;
14
use Psr\Http\Message\ServerRequestInterface as Request;
15
use Psr\Http\Message\ResponseInterface as Response;
16
use Chubbyphp\ApiSkeleton\Search\CourseSearch;
17
18
final class CourseSearchController
19
{
20
    /**
21
     * @var string
22
     */
23
    private $defaultLanguage;
24
25
    /**
26
     * @var RepositoryInterface
27
     */
28
    private $repository;
29
30
    /**
31
     * @var RequestManagerInterface
32
     */
33
    private $requestManager;
34
35
    /**
36
     * @var ResponseManagerInterface
37
     */
38
    private $responseManager;
39
40
    /**
41
     * @var TranslatorInterface
42
     */
43
    private $translator;
44
45
    /**
46
     * @var ValidatorInterface
47
     */
48
    private $validator;
49
50
    /**
51
     * @param string                   $defaultLanguage
52
     * @param RepositoryInterface      $repository
53
     * @param RequestManagerInterface  $requestManager
54
     * @param ResponseManagerInterface $responseManager
55
     * @param TranslatorInterface      $translator
56
     * @param ValidatorInterface       $validator
57
     */
58
    public function __construct(
59
        string $defaultLanguage,
60
        RepositoryInterface $repository,
61
        RequestManagerInterface $requestManager,
62
        ResponseManagerInterface $responseManager,
63
        TranslatorInterface $translator,
64
        ValidatorInterface $validator
65
    ) {
66
        $this->defaultLanguage = $defaultLanguage;
67
        $this->repository = $repository;
68
        $this->requestManager = $requestManager;
69
        $this->responseManager = $responseManager;
70
        $this->translator = $translator;
71
        $this->validator = $validator;
72
    }
73
74
    /**
75
     * @param Request $request
76
     *
77
     * @return Response
78
     */
79
    public function __invoke(Request $request): Response
80
    {
81
        if (null === $accept = $this->requestManager->getAccept($request)) {
82
            return $this->responseManager->createAcceptNotSupportedResponse($request);
83
        }
84
85
        /** @var CourseSearch $courseSearch */
86
        $courseSearch = $this->requestManager->getDataFromRequestQuery($request, CourseSearch::class);
87
88 View Code Duplication
        if ([] !== $errors = $this->validator->validateObject($courseSearch)) {
89
            $locale = $this->requestManager->getAcceptLanguage($request, $this->defaultLanguage);
90
91
            return $this->responseManager->createValidationErrorResponse(
92
                $request,
93
                $accept,
94
                Error::SCOPE_QUERY,
95
                'course-search',
96
                (new NestedErrorMessages($errors, function (string $key, array $arguments) use ($locale) {
0 ignored issues
show
$errors is of type array<integer,string>, but the function expects a array<integer,object<Chu...\Error\ErrorInterface>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
97
                    return $this->translator->translate($locale, $key, $arguments);
98
                }))->getMessages()
99
            );
100
        }
101
102
        /** @var CourseSearch $courseSearch */
103
        $courseSearch = $this->repository->search($courseSearch);
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Chubbyphp\Model\RepositoryInterface as the method search() does only exist in the following implementations of said interface: Chubbyphp\ApiSkeleton\Repository\CourseRepository.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
104
105
        return $this->responseManager->createResponse($request, 200, $accept, $courseSearch);
106
    }
107
}
108