Completed
Pull Request — master (#75)
by
unknown
05:48
created

ApiClientContext::removePlaceHolder()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the Behat WebApiExtension.
5
 *
6
 * (c) Konstantin Kudryashov <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Behat\WebApiExtension\Context;
13
14
use Nyholm\Psr7\Request;
15
use Psr\Http\Client\ClientExceptionInterface;
16
use Psr\Http\Client\ClientInterface;
17
use Psr\Http\Message\RequestInterface;
18
use Psr\Http\Message\ResponseInterface;
19
20
/**
21
 * Provides methods without Features methods.
22
 *
23
 * @author Keyclic team <[email protected]>
24
 */
25
abstract class ApiClientContext implements ApiClientContextInterface
26
{
27
    /**
28
     * @var string
29
     */
30
    protected $baseUri;
31
32
    /**
33
     * @var ClientInterface
34
     */
35
    private $client;
36
37
    /**
38
     * @var array
39
     */
40
    private $headers = [];
41
42
    /**
43
     * @var array
44
     */
45
    private $placeHolders = [];
46
47
    /**
48
     * @var RequestInterface
49
     */
50
    private $request;
51
52
    /**
53
     * @var ResponseInterface
54
     */
55
    private $response;
56
57
    public function setBaseUri(string $baseUri): ApiClientContextInterface
58
    {
59
        $this->baseUri = $baseUri;
60
61
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Behat\WebApiExtension\Context\ApiClientContext) is incompatible with the return type declared by the interface Behat\WebApiExtension\Co...xtInterface::setBaseUri of type self.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
62
    }
63
64
    public function setClient(ClientInterface $client): ApiClientContextInterface
65
    {
66
        $this->client = $client;
67
68
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Behat\WebApiExtension\Context\ApiClientContext) is incompatible with the return type declared by the interface Behat\WebApiExtension\Co...extInterface::setClient of type self.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
69
    }
70
71
    protected function getHeaders(): array
72
    {
73
        return $this->headers;
74
    }
75
76
    protected function addHeader(string $name, string $value): ApiClientContextInterface
77
    {
78
        if (false === isset($this->headers[$name])) {
79
            $this->headers[$name] = $value;
80
81
            return $this;
82
        }
83
84
        if (true === is_array($this->headers[$name])) {
85
            array_push($this->headers[$name], $value);
86
87
            return $this;
88
        }
89
90
        if (false === is_array($this->headers[$name])) {
91
            $this->headers[$name] = [
92
                $this->headers[$name],
93
                $value,
94
            ];
95
96
            return $this;
97
        }
98
99
        return $this;
100
    }
101
102
    protected function removeHeader(string $name): ApiClientContextInterface
103
    {
104
        if (array_key_exists($name, $this->headers)) {
105
            unset($this->headers[$name]);
106
        }
107
108
        return $this;
109
    }
110
111
    /**
112
     * Add place holder for replacement.
113
     *
114
     * you can specify placeholders, which will
115
     * be replaced in URL, request or response body.
116
     */
117
    protected function addPlaceholder(string $key, string $value): ApiClientContextInterface
118
    {
119
        $this->placeHolders[$key] = $value;
120
121
        return $this;
122
    }
123
124
    /**
125
     * Removes a placeholder identified by $key.
126
     */
127
    protected function removePlaceHolder(string $key): ApiClientContext
128
    {
129
        if (array_key_exists($key, $this->placeHolders)) {
130
            unset($this->placeHolders[$key]);
131
        }
132
133
        return $this;
134
    }
135
136
    protected function getRequest(): RequestInterface
137
    {
138
        return $this->request;
139
    }
140
141
    /**
142
     * @return ResponseInterface
143
     */
144
    protected function getResponse()
145
    {
146
        return $this->response;
147
    }
148
149
    /**
150
     * @throws ClientExceptionInterface
151
     */
152
    protected function sendRequest(string $method, string $uri, array $headers = [], ?string $body = null)
153
    {
154
        $this->request = new Request($method, $this->baseUri.$uri, $headers, $body);
155
156
        $this->response = $this->getClient()->sendRequest($this->request);
157
    }
158
159
    /**
160
     * Replaces placeholders in provided text.
161
     */
162
    protected function replacePlaceHolder(string $string): string
163
    {
164
        foreach ($this->placeHolders as $key => $val) {
165
            $string = str_replace($key, $val, $string);
166
        }
167
168
        return $string;
169
    }
170
171
    /**
172
     * Prepare URL by replacing placeholders and trimming slashes.
173
     */
174
    protected function prepareUrl(string $url): string
175
    {
176
        return ltrim($this->replacePlaceHolder($url), '/');
177
    }
178
179
    protected function getClient(): ClientInterface
180
    {
181
        if (null === $this->client) {
182
            throw new \RuntimeException('Client has not been set in WebApiContext.');
183
        }
184
185
        return $this->client;
186
    }
187
}
188