Passed
Push — master ( 8efc25...9adcd8 )
by Sebastian
02:27
created

Request_AcceptHeaders::parseHeader()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 6
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 14
rs 10
1
<?php
2
/**
3
 * File containing the {@link Request_AcceptHeaders} class.
4
 * 
5
 * @package Application Utils
6
 * @subpackage Request
7
 * @see Request_AcceptHeaders
8
 */
9
10
declare(strict_types=1);
11
12
namespace AppUtils;
13
14
/**
15
 * Accept header parser: fetches the accept header string
16
 * and splits it into its composing mime types.
17
 *
18
 * @package Application Utils
19
 * @subpackage Request
20
 * @author Sebastian Mordziol <[email protected]>
21
 */
22
class Request_AcceptHeaders
23
{
24
    protected $headers = array();
25
    
26
    public function __construct()
27
    {
28
        $this->parse();
29
    }
30
    
31
   /**
32
    * Retrieves an indexed array with accept mime types
33
    * that the client sent, in the order of preference
34
    * the client specified.
35
    *
36
    * Example:
37
    *
38
    * array(
39
    *     'text/html',
40
    *     'application/xhtml+xml',
41
    *     'image/webp'
42
    *     ...
43
    * )
44
    */
45
    public function getMimeStrings() : array
46
    {
47
        $result = array();
48
        
49
        foreach($this->headers as $header)
50
        {
51
            $result[] = $header['type'];
52
        }
53
        
54
        return $result;
55
    }
56
    
57
   /**
58
    * Checks that an accept header string exists, and tries to parse it.
59
    */
60
    protected function parse() : void
61
    {
62
        // we may be in a CLI environment where the headers
63
        // are not populated.
64
        if(!isset($_SERVER['HTTP_ACCEPT'])) {
65
            return;
66
        }
67
        
68
        $this->headers = $this->parseHeader($_SERVER['HTTP_ACCEPT']);
69
    }
70
    
71
   /**
72
    * Splits the accept header string and parses the mime types.
73
    *  
74
    * @param string $acceptHeader 
75
    */
76
    protected function parseHeader(string $acceptHeader) : array
77
    {
78
        $tokens = preg_split('/\s*,\s*/', $acceptHeader);
79
        
80
        $accept = array();
81
        
82
        foreach($tokens as $i => $term)
83
        {
84
            $accept[] = $this->parseEntry($i, $term);
85
        }
86
        
87
        usort($accept, array($this, 'sortMimeTypes'));
88
        
89
        return $accept;
90
    }
91
    
92
   /**
93
    * Parses a single mime type entry.
94
    * 
95
    * @param int $i The position in the accept string
96
    * @param string $mime The mime type
97
    * @return array
98
    */
99
    protected function parseEntry(int $i, string $mime) : array
100
    {
101
        $entry = array(
102
            'pos' => $i,
103
            'params' => array(),
104
            'quality' => 0,
105
            'type' => null
106
        );
107
        
108
        if(strstr($mime, ';'))
109
        {
110
            $parts = explode(';', $mime);
111
            $mime = array_shift($parts);
112
            
113
            // several parameters are possible, and they can be parsed
114
            // like an URL query string if separated by ampersands;
115
            $entry['params'] = ConvertHelper::parseQueryString(implode('&', $parts));
116
                
117
            if(isset($entry['params']['q'])) 
118
            {
119
                $entry['quality'] = (double)$entry['params']['q'];
120
            } 
121
        }
122
        
123
        $entry['type'] = $mime;
124
        
125
        return $entry;
126
    }
127
    
128
   /**
129
    * Sorts the mime types collection, first by quality
130
    * and then by position in the list.
131
    * 
132
    * @param array $a
133
    * @param array $b
134
    * @return number
135
    */
136
    protected function sortMimeTypes(array $a, array $b)
137
    {
138
        /* first tier: highest q factor wins */
139
        $diff = $b['quality'] - $a['quality'];
140
        
141
        if ($diff > 0) 
142
        {
143
            $diff = 1;
144
        } 
145
        else 
146
        {
147
            if ($diff < 0) 
148
            {
149
                $diff = -1;
150
            } 
151
            else 
152
            {
153
                /* tie-breaker: first listed item wins */
154
                $diff = $a['pos'] - $b['pos'];
155
            }
156
        }
157
        
158
        return $diff;
159
    }
160
}
161