HttpProcessUtility::selectMimeType()   C
last analyzed

Complexity

Conditions 12
Paths 6

Size

Total Lines 54
Code Lines 38

Duplication

Lines 16
Ratio 29.63 %

Importance

Changes 0
Metric Value
dl 16
loc 54
rs 6.7848
c 0
b 0
f 0
cc 12
eloc 38
nc 6
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace POData;
4
5
use POData\Common\Messages;
6
use POData\Common\HttpHeaderFailure;
7
use POData\Providers\Metadata\Type\Char;
8
9
10
/**
11
 * Class MediaType
12
 *
13
 * The Accept request-header field can be used to specify certain
14
 * media types which are acceptable for the response, this class
15
 * is used to hold details of such media type.
16
 * http://www.w3.org/Protocols/rfc1341/4_Content-Type.html
17
 *
18
 * @package POData
19
 */
20
class MediaType
21
{
22
    /**
23
     * The type part of media type.
24
     *
25
     * @var string
26
     */
27
    private $_type;
28
29
    /**
30
     * The sub-type part of media type.
31
     * 
32
     * @var string
33
     */
34
    private $_subType;
35
36
    /**
37
     * The parameters associated with the media type.
38
     * 
39
     * @var array(array(string, string))
40
     */
41
    private $_parameters;
42
43
    /**
44
     * Constructs a new instance of Media Type.
45
     * 
46
     * @param string $type       The type of media type
47
     * @param string $subType    The sub type of media type
48
     * @param array  $parameters The parameters associated with media type
49
     * 
50
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
51
     */
52
    public function  __construct($type, $subType, $parameters)
53
    {
54
        $this->_type = $type;
55
        $this->_subType = $subType;
56
        $this->_parameters = $parameters;
57
    }
58
59
    /**
60
     * Gets the MIME type.
61
     * 
62
     * @return string
63
     */
64
    public function getMimeType()
65
    {
66
        return $this->_type . '/' . $this->_subType;
67
    }
68
69
    /**
70
     * Gets the parameters associated with the media types.
71
     * 
72
     * @return array(array(string, string))
0 ignored issues
show
Documentation introduced by
The doc-type array(array(string, could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
73
     */
74
    public function getParameters()
75
    {
76
        return $this->_parameters;
77
    }
78
79
    /**
80
     * Gets the number of parts in this media type that matches with
81
     * the given candidate type.
82
     * 
83
     * @param string $candidate The candidate mime type.
84
     * 
85
     * @return int Returns -1 if this media type does not match with the
86
     *                        candidate media type, 0 if media type's type is '*'
87
     *                        (accept all types), 1 if media types's type matches
88
     *                        with the candidate MIME type's type and media type's
89
     *                        sub-types is '*' (accept all sub-type), 2 if both
90
     *                        type and sub-type matches.
91
     */
92
    public function getMatchingRating($candidate)
93
    {
94
        $result = -1;
95
        if (strlen($candidate) > 0) {
96
97
            //get the odata parameter (if there is one)
98
            $candidateODataValue = null;
99
            $candidateParts = explode(';', $candidate);
100
            if(count($candidateParts) > 1){
101
                //is it safe to assume the mime type is always the first part?
102
                $candidate = array_shift($candidateParts); //move off the first type matcher
103
                //the rest look like QSPs..kinda so we can do this
104
                parse_str(implode("&", $candidateParts), $candidateParts);
105
                if(array_key_exists('odata', $candidateParts)){
106
                   $candidateODataValue = $candidateParts['odata'];
107
                }
108
            }
109
110
            //ensure that the odata parameter values match
111
            if($this->getODataValue() !== $candidateODataValue){
112
                return -1;
113
            }
114
115
116
            if ($this->_type == '*') {
117
                $result = 0;
118
            } else {
119
                $separatorIdx = strpos($candidate, '/');
120
                if ($separatorIdx !== false) {
121
                    //if there's a subtype..look further
122
                    $candidateType = substr($candidate, 0, $separatorIdx);
123
                    if (strcasecmp($this->_type, $candidateType) == 0) {
124
                        //If main type matches
125
                        if ($this->_subType == '*') {
126
                            //and sub type matches with wildcard
127
                            $result = 1;
128
                        } else {
129
                            $candidateSubType = substr($candidate, strlen($candidateType) + 1);
130
                            if (strcasecmp($this->_subType, $candidateSubType) == 0) {
131
                                //if sub type matches
132
                                $result = 2;
133
134
                            }
135
                        }
136
                    }
137
                }
138
            }
139
        }
140
141
        return $result;
142
    }
143
144
    public function getODataValue()
145
    {
146
        foreach ($this->_parameters as $parameter) {
147
            foreach ($parameter as $key => $value) {
148
                if (strcasecmp($key, 'odata') === 0) {
149
                    return $value;
150
                }
151
            }
152
        }
153
154
        return null;
155
    }
156
157
    /**
158
     * Gets the quality factor associated with this media type.
159
     * 
160
     * @return int The value associated with 'q' parameter (0-1000),
161
     *             if absent return 1000.
162
     */
163
    public function getQualityValue()
164
    {
165
        foreach ($this->_parameters as $parameter) {
166
            foreach ($parameter as $key => $value) {
167
                if (strcasecmp($key, 'q') === 0) {
168
                    $textIndex = 0;
169
                    $result;
0 ignored issues
show
Bug introduced by
The variable $result seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
170
                    HttpProcessUtility::readQualityValue(
171
                        $value,
172
                        $textIndex,
173
                        $result
174
                    );
175
                    return $result;
176
                }
177
            }
178
        }
179
180
        return 1000;
181
    }
182
}
183
184
/**
185
 * Class HttpProcessUtility
186
 * @package POData
187
 */
188
class HttpProcessUtility
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
189
{
190
191
    /**
192
     * Gets the appropriate MIME type for the request, throwing if there is none.
193
     * 
194
     * @param string        $acceptTypesText    Text as it appears in an HTTP
195
     *                                          Accepts header.
196
     * @param string[] $exactContentTypes  Preferred content type to match if an exact media type is given - this is in descending order of preference.
197
     *
198
     * @param string        $inexactContentType Preferred fallback content type for inexact matches.
199
     *
200
     * @return string One of exactContentType or inexactContentType.
201
     */
202
    public static function selectRequiredMimeType($acceptTypesText,
203
        $exactContentTypes,
204
        $inexactContentType
205
    ) {
206
        $selectedContentType = null;
207
        $selectedMatchingParts = -1;
208
        $selectedQualityValue = 0;
209
        $acceptable = false;
210
        $acceptTypesEmpty = true;
211
        $foundExactMatch = false;
212
213
        if (!is_null($acceptTypesText)) {
214
            $acceptTypes = self::mimeTypesFromAcceptHeaders($acceptTypesText);
215
            foreach ($acceptTypes as $acceptType) {
216
                $acceptTypesEmpty = false;
217
                foreach ($exactContentTypes as $exactContentType) {
218
                    if (strcasecmp($acceptType->getMimeType(), $exactContentType) == 0) {
219
                        $selectedContentType = $exactContentType;
220
                        $selectedQualityValue = $acceptType->getQualityValue();
221
                        $acceptable = $selectedQualityValue != 0;
222
                        $foundExactMatch = true;
223
                        break;
224
                    }
225
                }
226
227
                if ($foundExactMatch) {
228
                    break;
229
                }
230
231
                $matchingParts 
232
                    = $acceptType->getMatchingRating($inexactContentType);
233
                if ($matchingParts < 0) {
234
                    continue;
235
                }
236
237 View Code Duplication
                if ($matchingParts > $selectedMatchingParts) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
238
                    // A more specific type wins.
239
                    $selectedContentType = $inexactContentType;
240
                    $selectedMatchingParts = $matchingParts;
241
                    $selectedQualityValue = $acceptType->getQualityValue();
242
                    $acceptable = $selectedQualityValue != 0;
243
                } else if ($matchingParts == $selectedMatchingParts) {
244
                    // A type with a higher q-value wins.
245
                    $candidateQualityValue = $acceptType->getQualityValue();
246
                    if ($candidateQualityValue > $selectedQualityValue) {
247
                        $selectedContentType = $inexactContentType;
248
                        $selectedQualityValue = $candidateQualityValue;
249
                        $acceptable = $selectedQualityValue != 0;
250
                    }
251
                }
252
            }
253
        }
254
255
        if (!$acceptable && !$acceptTypesEmpty) {
256
            throw new HttpHeaderFailure(
257
                Messages::unsupportedMediaType(),
258
                415
259
            );
260
        }
261
262
        if ($acceptTypesEmpty) {
263
            $selectedContentType = $inexactContentType;
264
        }
265
266
        return $selectedContentType;
267
    }
268
269
    /**
270
     * Selects an acceptable MIME type that satisfies the Accepts header.
271
     * 
272
     * @param string $acceptTypesText Text for Accepts header.
273
     * @param string[] $availableTypes  Types that the server is willing to return, in descending order of preference.
274
     * 
275
     * @return string The best MIME type for the client.
276
     *
277
     * @throws HttpHeaderFailure
278
     */
279
    public static function selectMimeType($acceptTypesText, array $availableTypes)
280
    {
281
        $selectedContentType = null;
282
        $selectedMatchingParts = -1;
283
        $selectedQualityValue = 0;
284
        $selectedPreferenceIndex = PHP_INT_MAX;
285
        $acceptable = false;
286
        $acceptTypesEmpty = true;
287
        if (!is_null($acceptTypesText)) {
288
            $acceptTypes = self::mimeTypesFromAcceptHeaders($acceptTypesText);
289
            foreach ($acceptTypes as $acceptType) {
290
                $acceptTypesEmpty = false;
291
                for ($i = 0; $i < count($availableTypes); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
292
                    $availableType = $availableTypes[$i];
293
                    $matchRating = $acceptType->getMatchingRating($availableType);
294
                    if ($matchRating < 0) {
295
                        continue;
296
                    }
297
298
                    if ($matchRating > $selectedMatchingParts) {
299
                        // A more specific type wins.
300
                        $selectedContentType = $availableType;
301
                        $selectedMatchingParts = $matchRating;
302
                        $selectedQualityValue = $acceptType->getQualityValue();
303
                        $selectedPreferenceIndex = $i;
304
                        $acceptable = $selectedQualityValue != 0;
305 View Code Duplication
                    } else if ($matchRating == $selectedMatchingParts) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
306
                        // A type with a higher q-value wins.
307
                        $candidateQualityValue = $acceptType->getQualityValue();
308
                        if ($candidateQualityValue > $selectedQualityValue) {
309
                            $selectedContentType = $availableType;
310
                            $selectedQualityValue = $candidateQualityValue;
311
                            $selectedPreferenceIndex = $i;
312
                            $acceptable = $selectedQualityValue != 0;
313
                        } else if ($candidateQualityValue == $selectedQualityValue) {
314
                            // A type that is earlier in the availableTypes array wins.
315
                            if ($i < $selectedPreferenceIndex) {
316
                                $selectedContentType = $availableType;
317
                                $selectedPreferenceIndex = $i;
318
                            }
319
                        }
320
                    }
321
                }
322
            }
323
        }
324
325
        if ($acceptTypesEmpty) {
326
            $selectedContentType = $availableTypes[0];
327
        } else if (!$acceptable) {
328
            $selectedContentType = null;
329
        }
330
331
        return $selectedContentType;
332
    }
333
334
    /**
335
     * Returns all MIME types from the $text.
336
     * 
337
     * @param string $text Text as it appears on an HTTP Accepts header.
338
     * 
339
     * @return MediaType[] Array of media (MIME) type description.
340
     *
341
     * @throws HttpHeaderFailure If found any syntax error in the given text.
342
     */
343
    public static function mimeTypesFromAcceptHeaders($text)
344
    {
345
        $mediaTypes = array();
346
        $textIndex = 0;
347
        while (!self::skipWhitespace($text, $textIndex)) {
348
            $type = null;
349
            $subType = null;
350
            self::readMediaTypeAndSubtype($text, $textIndex, $type, $subType);
351
352
            $parameters = array();
353
            while (!self::skipWhitespace($text, $textIndex)) {
354
                if ($text[$textIndex] == ',') {
355
                    $textIndex++;
356
                    break;
357
                }
358
359
                if ($text[$textIndex] != ';') {
360
                    throw new HttpHeaderFailure(
361
                        Messages::httpProcessUtilityMediaTypeRequiresSemicolonBeforeParameter(), 
362
                        400
363
                    );
364
                }
365
366
                $textIndex++;
367
                if (self::skipWhitespace($text, $textIndex)) {
368
                    break;
369
                }
370
371
                self::readMediaTypeParameter($text, $textIndex, $parameters);
372
            }
373
374
            $mediaTypes[] = new MediaType($type, $subType, $parameters);
375
        }
376
377
        return $mediaTypes;
378
    }
379
380
    /**
381
     * Skips whitespace in the specified text by advancing an index to
382
     * the next non-whitespace character.
383
     * 
384
     * @param string $text       Text to scan.
385
     * @param int    &$textIndex Index to begin scanning from.
386
     * 
387
     * @return boolean true if the end of the string was reached, false otherwise.
388
     */
389 View Code Duplication
    public static function skipWhiteSpace($text, &$textIndex)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
390
    {
391
        $textLen = strlen($text);
392
        while (($textIndex < $textLen) && Char::isWhiteSpace($text[$textIndex])) {
0 ignored issues
show
Documentation introduced by
$text[$textIndex] is of type string, but the function expects a object<POData\Providers\Metadata\Type\Char>.

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...
393
            $textIndex++;
394
        }
395
396
        return $textLen == $textIndex;
397
    }
398
399
    /**
400
     * Reads the type and subtype specifications for a MIME type.
401
     * 
402
     * @param string $text       Text in which specification exists.
403
     * @param int    &$textIndex Pointer into text.
404
     * @param string &$type      Type of media found.
405
     * @param string &$subType   Subtype of media found.
406
     *
407
     * @throws HttpHeaderFailure If failed to read type and sub-type.
408
     * 
409
     * @return void
410
     */
411
    public static function readMediaTypeAndSubtype($text, &$textIndex, 
412
        &$type, &$subType
413
    ) {
414
        $textStart = $textIndex;
415
        if (self::readToken($text, $textIndex)) {
416
            throw new HttpHeaderFailure(
417
                Messages::httpProcessUtilityMediaTypeUnspecified(), 
418
                400
419
            );
420
        }
421
422
        if ($text[$textIndex] != '/') {
423
            throw new HttpHeaderFailure(
424
                Messages::httpProcessUtilityMediaTypeRequiresSlash(), 
425
                400
426
            );
427
        }
428
429
        $type = substr($text, $textStart, $textIndex - $textStart);
430
        $textIndex++;
431
432
        $subTypeStart = $textIndex;
433
        self::readToken($text, $textIndex);
434
        if ($textIndex == $subTypeStart) {
435
            throw new HttpHeaderFailure(
436
                Messages::httpProcessUtilityMediaTypeRequiresSubType(), 
437
                400
438
            );
439
        }
440
441
        $subType = substr($text, $subTypeStart, $textIndex - $subTypeStart);
442
    }
443
444
    /**
445
     * Reads a token on the specified text by advancing an index on it.
446
     * 
447
     * @param string $text       Text to read token from.
448
     * @param int    &$textIndex Index for the position being scanned on text.
449
     * 
450
     * @return boolean true if the end of the text was reached; false otherwise.
451
     */
452 View Code Duplication
    public static function readToken($text, &$textIndex)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
453
    {
454
        $textLen = strlen($text);
455
        while (($textIndex < $textLen) && self::isHttpTokenChar($text[$textIndex])) {
0 ignored issues
show
Documentation introduced by
$text[$textIndex] is of type string, but the function expects a object<POData\char>.

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...
456
            $textIndex++;
457
        }
458
459
        return $textLen == $textIndex;
460
    }
461
462
    /**
463
     * To check whether the given character is a HTTP token character
464
     * or not.
465
     * 
466
     * @param char $char The character to inspect.
467
     * 
468
     * @return boolean True if the given character is a valid HTTP token
469
     *                 character, False otherwise.
470
     */
471
    public static function isHttpTokenChar($char)
472
    {
473
        return ord($char) < 126 && ord($char) > 31
474
            && !self::isHttpSeparator($char);
475
    }
476
477
    /**
478
     * To check whether the given character is a HTTP seperator character.
479
     *
480
     * @param char $char The character to inspect.
481
     * 
482
     * @return boolean True if the given character is a valid HTTP seperator
483
     *                 character, False otherwise.
484
     */
485
    public static function isHttpSeparator($char)
486
    {
487
        return
488
            $char == '(' || $char == ')' || $char == '<' || $char == '>' ||
489
            $char == '@' || $char == ',' || $char == ';' || $char == ':' ||
490
            $char == '\\' || $char == '"' || $char == '/' || $char == '[' ||
491
            $char == ']' || $char == '?' || $char == '=' || $char == '{' ||
492
            $char == '}' || $char == ' ' || ord($char) == Char::TAB;
493
    }
494
495
    /**
496
     * Read a parameter for a media type/range.
497
     * 
498
     * @param string $text        Text to read from.
499
     * @param int    &$textIndex  Pointer in text.
500
     * @param array  &$parameters Array with parameters.
501
     *
502
     * @throws HttpHeaderFailure If found parameter value missing.
503
     * @return void
504
     */
505
    public static function readMediaTypeParameter($text, &$textIndex, &$parameters)
506
    {
507
        $textStart = $textIndex;
508
        if (self::readToken($text, $textIndex)) {
509
            throw new HttpHeaderFailure(
510
                Messages::httpProcessUtilityMediaTypeMissingValue(), 
511
                400
512
            );
513
        }
514
515
        $parameterName = substr($text, $textStart, $textIndex - $textStart);
516
        if ($text[$textIndex] != '=') {
517
            throw new HttpHeaderFailure(
518
                Messages::httpProcessUtilityMediaTypeMissingValue(), 
519
                400
520
            );
521
        }
522
523
        $textIndex++;
524
        $parameterValue 
525
            = self::readQuotedParameterValue($parameterName, $text, $textIndex);
526
        $parameters[] = array($parameterName => $parameterValue);
527
    }
528
529
    /**
530
     * Reads Mime type parameter value for a particular parameter in the
531
     * Content-Type/Accept headers.
532
     * 
533
     * @param string $parameterName Name of parameter.
534
     * @param string $text          Header text.
535
     * @param int    &$textIndex    Parsing index in $text.
536
     * 
537
     * @return string String representing the value of the $parameterName parameter.
538
     *
539
     * @throws HttpHeaderFailure
540
     */
541
    public static function readQuotedParameterValue($parameterName, $text, 
542
        &$textIndex
543
    ) {
544
        $parameterValue = array();
545
        $textLen = strlen($text);
546
        $valueIsQuoted = false;
547
        if ($textIndex < $textLen) {
548
            if ($text[$textIndex] == '"') {
549
                $textIndex++;
550
                $valueIsQuoted = true;
551
            }
552
        }
553
554
        while ($textIndex < $textLen) {
555
            $currentChar = $text[$textIndex];
556
557
            if ($currentChar == '\\' || $currentChar == '"') {
558
                if (!$valueIsQuoted) {
559
                    throw new HttpHeaderFailure(
560
                        Messages::httpProcessUtilityEscapeCharWithoutQuotes(
561
                            $parameterName
562
                        ), 
563
                        400
564
                    );
565
                }
566
567
                $textIndex++;
568
569
                // End of quoted parameter value.
570
                if ($currentChar == '"') {
571
                    $valueIsQuoted = false;
572
                    break;
573
                }
574
575
                if ($textIndex >= $textLen) {
576
                    throw new HttpHeaderFailure(
577
                        Messages::httpProcessUtilityEscapeCharAtEnd($parameterName), 
578
                        400
579
                    );
580
                }
581
582
                $currentChar = $text[$textIndex];
583
            } else if (!self::isHttpTokenChar($currentChar)) {
0 ignored issues
show
Documentation introduced by
$currentChar is of type string, but the function expects a object<POData\char>.

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...
584
                // If the given character is special, we stop processing.
585
                break;
586
            }
587
588
            $parameterValue[] = $currentChar;
589
            $textIndex++;
590
        }
591
592
        if ($valueIsQuoted) {
593
            throw new HttpHeaderFailure(
594
                Messages::httpProcessUtilityClosingQuoteNotFound($parameterName), 
595
                400
596
            );
597
        }
598
599
        return empty($parameterValue) ? null : implode('', $parameterValue);
600
    }
601
602
    /**
603
     * Reads the numeric part of a quality value substring, normalizing it to 0-1000
604
       rather than the standard 0.000-1.000 ranges.
605
     * 
606
     * @param string $text          Text to read qvalue from.
607
     * @param int    &$textIndex    Index into text where the qvalue starts.
608
     * @param int    &$qualityValue After the method executes, the normalized qvalue.
609
     *
610
     * @throws HttpHeaderFailure If any error occured while reading and processing
611
     *                           the quality factor.
612
     * @return void
613
     */
614
    public static function readQualityValue($text, &$textIndex, &$qualityValue)
615
    {
616
        $digit = $text[$textIndex++];
617
        if ($digit == '0') {
618
            $qualityValue = 0;
619
        } else if ($digit == '1') {
620
            $qualityValue = 1;
621
        } else {
622
            throw new HttpHeaderFailure(
623
                Messages::httpProcessUtilityMalformedHeaderValue(), 
624
                400
625
            );
626
        }
627
628
        $textLen = strlen($text);
629
        if ($textIndex < $textLen && $text[$textIndex] == '.') {
630
            $textIndex++;
631
632
            $adjustFactor = 1000;
633
            while ($adjustFactor > 1 && $textIndex < $textLen) {
634
                $c = $text[$textIndex];
635
                $charValue = self::digitToInt32($c);
0 ignored issues
show
Documentation introduced by
$c is of type string, but the function expects a object<POData\char>.

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...
636
                if ($charValue >= 0) {
637
                    $textIndex++;
638
                    $adjustFactor /= 10;
639
                    $qualityValue *= 10;
640
                    $qualityValue += $charValue;
641
                } else {
642
                    break;
643
                }
644
            }
645
646
            $qualityValue = $qualityValue *= $adjustFactor;
647
            if ($qualityValue > 1000) {
648
                // Too high of a value in qvalue.
649
                throw new HttpHeaderFailure(
650
                    Messages::httpProcessUtilityMalformedHeaderValue(), 
651
                    400
652
                );
653
            }
654
        } else {
655
            $qualityValue *= 1000;
656
        }
657
    }
658
659
    /**
660
     * Converts the specified character from the ASCII range to a digit.
661
     * 
662
     * @param char $c Character to convert
663
     *
664
     * @return int The Int32 value for $c, or -1 if it is an element separator.
665
     *
666
     * @throws HttpHeaderFailure If $c is not ASCII value for digit or element
667
     *                           seperator.
668
     */
669
    public static function digitToInt32($c)
670
    {
671
        if ($c >= '0' && $c <= '9') {
672
                return intval($c);
673
        } else {
674
            if (self::isHttpElementSeparator($c)) {
675
                return -1;
676
            } else {
677
                throw new HttpHeaderFailure(
678
                    Messages::httpProcessUtilityMalformedHeaderValue(), 
679
                    400
680
                );
681
            }
682
        }
683
    }
684
685
    /**
686
     * Verifies whether the specified character is a valid separator in
687
       an HTTP header list of element.
688
     * 
689
     * @param char $c Character to verify
690
     * 
691
     * @return boolean true if c is a valid character for separating elements;
692
     *                 false otherwise.
693
     */
694
    public static function isHttpElementSeparator($c)
695
    {
696
        return $c == ',' || $c == ' ' || $c == '\t';
697
    }
698
699
700
	public static function headerToServerKey($headerName){
701
		return 'HTTP_' . strtoupper(str_replace('-', '_', $headerName));
702
	}
703
}