Completed
Branch master (ffe81d)
by Neomerx
04:33
created

MediaType::paramValuesEqual()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 3
crap 2
1
<?php namespace Neomerx\JsonApi\Http\Headers;
2
3
/**
4
 * Copyright 2015-2018 [email protected]
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
use InvalidArgumentException;
20
use Neomerx\JsonApi\Contracts\Http\Headers\MediaTypeInterface;
21
22
/**
23
 * @package Neomerx\JsonApi
24
 */
25
class MediaType implements MediaTypeInterface
26
{
27
    /**
28
     * @var string
29
     */
30
    private $type;
31
32
    /**
33
     * @var string
34
     */
35
    private $subType;
36
37
    /**
38
     * @var string?
39
     */
40
    private $mediaType = null;
41
42
    /**
43
     * @var array<string,string>|null
44
     */
45
    private $parameters;
46
47
    /**
48
     * A list of parameter names for case-insensitive compare. Keys must be lower-cased.
49
     *
50
     * @var array
51
     */
52
    protected const PARAMETER_NAMES = [
53
        'charset' => true,
54
    ];
55
56
    /**
57
     * @param string $type
58
     * @param string $subType
59
     * @param array<string,string>|null $parameters
60
     */
61 42
    public function __construct(string $type, string $subType, array $parameters = null)
62
    {
63 42
        $type = trim($type);
64 42
        if (empty($type) === true) {
65 1
            throw new InvalidArgumentException('type');
66
        }
67
68 41
        $subType = trim($subType);
69 41
        if (empty($subType) === true) {
70 1
            throw new InvalidArgumentException('subType');
71
        }
72
73 40
        $this->type       = $type;
74 40
        $this->subType    = $subType;
75 40
        $this->parameters = $parameters;
76 40
    }
77
78
    /**
79
     * @inheritdoc
80
     */
81 38
    public function getType(): string
82
    {
83 38
        return $this->type;
84
    }
85
86
    /**
87
     * @inheritdoc
88
     */
89 38
    public function getSubType(): string
90
    {
91 38
        return $this->subType;
92
    }
93
94
    /**
95
     * @inheritdoc
96
     */
97 35
    public function getMediaType(): string
98
    {
99 35
        if ($this->mediaType === null) {
100 35
            $this->mediaType = $this->getType() . '/' . $this->getSubType();
101
        }
102
103 35
        return $this->mediaType;
104
    }
105
106
    /**
107
     * @inheritdoc
108
     */
109 19
    public function getParameters(): ?array
110
    {
111 19
        return $this->parameters;
112
    }
113
114
    /**
115
     * @inheritdoc
116
     */
117 2
    public function matchesTo(MediaTypeInterface $mediaType): bool
118
    {
119
        return
120 2
            $this->isTypeMatches($mediaType) &&
121 2
            $this->isSubTypeMatches($mediaType) &&
122 2
            $this->isMediaParametersEqual($mediaType);
123
    }
124
125
    /**
126
     * @inheritdoc
127
     */
128 1
    public function equalsTo(MediaTypeInterface $mediaType): bool
129
    {
130
        return
131 1
            $this->isTypeEquals($mediaType) &&
132 1
            $this->isSubTypeEquals($mediaType) &&
133 1
            $this->isMediaParametersEqual($mediaType);
134
    }
135
136
    /**
137
     * @param MediaTypeInterface $mediaType
138
     *
139
     * @return bool
140
     */
141 2
    private function isTypeMatches(MediaTypeInterface $mediaType): bool
142
    {
143 2
        return $mediaType->getType() === '*' || $this->isTypeEquals($mediaType);
144
    }
145
146
    /**
147
     * @param MediaTypeInterface $mediaType
148
     *
149
     * @return bool
150
     */
151 3
    private function isTypeEquals(MediaTypeInterface $mediaType): bool
152
    {
153
        // Type, subtype and param name should be compared case-insensitive
154
        // https://tools.ietf.org/html/rfc7231#section-3.1.1.1
155 3
        return strcasecmp($this->getType(), $mediaType->getType()) === 0;
156
    }
157
158
    /**
159
     * @param MediaTypeInterface $mediaType
160
     *
161
     * @return bool
162
     */
163 2
    private function isSubTypeMatches(MediaTypeInterface $mediaType): bool
164
    {
165 2
        return $mediaType->getSubType() === '*' || $this->isSubTypeEquals($mediaType);
166
    }
167
168
    /**
169
     * @param MediaTypeInterface $mediaType
170
     *
171
     * @return bool
172
     */
173 3
    private function isSubTypeEquals(MediaTypeInterface $mediaType): bool
174
    {
175
        // Type, subtype and param name should be compared case-insensitive
176
        // https://tools.ietf.org/html/rfc7231#section-3.1.1.1
177 3
        return strcasecmp($this->getSubType(), $mediaType->getSubType()) === 0;
178
    }
179
180
    /**
181
     * @param MediaTypeInterface $mediaType
182
     *
183
     * @return bool
184
     */
185 3
    private function isMediaParametersEqual(MediaTypeInterface $mediaType): bool
186
    {
187 3
        if ($this->bothMediaTypeParamsEmpty($mediaType) === true) {
188 1
            return true;
189 2
        } elseif ($this->bothMediaTypeParamsNotEmptyAndEqualInSize($mediaType)) {
190
            // Type, subtype and param name should be compared case-insensitive
191
            // https://tools.ietf.org/html/rfc7231#section-3.1.1.1
192 2
            $ourParameters       = array_change_key_case($this->getParameters());
193 2
            $parametersToCompare = array_change_key_case($mediaType->getParameters());
194
195
            // if at least one name are different they are not equal
196 2
            if (empty(array_diff_key($ourParameters, $parametersToCompare)) === false) {
197 1
                return false;
198
            }
199
200
            // If we are here we have to compare values. Also some of the values should be compared case-insensitive
201
            // according to https://tools.ietf.org/html/rfc7231#section-3.1.1.1
202
            // > 'Parameter values might or might not be case-sensitive, depending on
203
            // the semantics of the parameter name.'
204 2
            foreach ($ourParameters as $name => $value) {
205 2
                if ($this->paramValuesEqual($name, $value, $parametersToCompare[$name]) === false) {
206 2
                    return false;
207
                }
208
            }
209
210 2
            return true;
211
        }
212
213 1
        return false;
214
    }
215
216
    /**
217
     * @param MediaTypeInterface $mediaType
218
     *
219
     * @return bool
220
     */
221 3
    private function bothMediaTypeParamsEmpty(MediaTypeInterface $mediaType): bool
222
    {
223 3
        return $this->getParameters() === null && $mediaType->getParameters() === null;
224
    }
225
226
    /**
227
     * @param MediaTypeInterface $mediaType
228
     *
229
     * @return bool
230
     */
231 2
    private function bothMediaTypeParamsNotEmptyAndEqualInSize(MediaTypeInterface $mediaType): bool
232
    {
233 2
        $pr1 = $this->getParameters();
234 2
        $pr2 = $mediaType->getParameters();
235
236 2
        return (empty($pr1) === false && empty($pr2) === false) && (count($pr1) === count($pr2));
237
    }
238
239
    /**
240
     * @param string $name
241
     *
242
     * @return bool
243
     */
244 2
    private function isParamCaseInsensitive(string $name): bool
245
    {
246 2
        return isset(static::PARAMETER_NAMES[$name]);
247
    }
248
249
    /**
250
     * @param string $name
251
     * @param string $value
252
     * @param string $valueToCompare
253
     *
254
     * @return bool
255
     */
256 2
    private function paramValuesEqual(string $name, string $value, string $valueToCompare): bool
257
    {
258 2
        $valuesEqual = $this->isParamCaseInsensitive($name) === true ?
259 2
            strcasecmp($value, $valueToCompare) === 0 : $value === $valueToCompare;
260
261 2
        return $valuesEqual;
262
    }
263
}
264