1 | <?php |
||
2 | /** |
||
3 | * PrivateBin |
||
4 | * |
||
5 | * a zero-knowledge paste bin |
||
6 | * |
||
7 | * @link https://github.com/PrivateBin/PrivateBin |
||
8 | * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net) |
||
9 | * @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License |
||
10 | * @version 1.2.1 |
||
11 | */ |
||
12 | |||
13 | namespace PrivateBin; |
||
14 | |||
15 | /** |
||
16 | * Request |
||
17 | * |
||
18 | * parses request parameters and provides helper functions for routing |
||
19 | */ |
||
20 | class Request |
||
21 | { |
||
22 | /** |
||
23 | * MIME type for JSON |
||
24 | * |
||
25 | * @const string |
||
26 | */ |
||
27 | const MIME_JSON = 'application/json'; |
||
28 | |||
29 | /** |
||
30 | * MIME type for HTML |
||
31 | * |
||
32 | * @const string |
||
33 | */ |
||
34 | const MIME_HTML = 'text/html'; |
||
35 | |||
36 | /** |
||
37 | * MIME type for XHTML |
||
38 | * |
||
39 | * @const string |
||
40 | */ |
||
41 | const MIME_XHTML = 'application/xhtml+xml'; |
||
42 | |||
43 | /** |
||
44 | * Input stream to use for PUT parameter parsing |
||
45 | * |
||
46 | * @access private |
||
47 | * @var string |
||
48 | */ |
||
49 | private static $_inputStream = 'php://input'; |
||
50 | |||
51 | /** |
||
52 | * Operation to perform |
||
53 | * |
||
54 | * @access private |
||
55 | * @var string |
||
56 | */ |
||
57 | private $_operation = 'view'; |
||
58 | |||
59 | /** |
||
60 | * Request parameters |
||
61 | * |
||
62 | * @access private |
||
63 | * @var array |
||
64 | */ |
||
65 | private $_params = array(); |
||
66 | |||
67 | /** |
||
68 | * If we are in a JSON API context |
||
69 | * |
||
70 | * @access private |
||
71 | * @var bool |
||
72 | */ |
||
73 | private $_isJsonApi = false; |
||
74 | |||
75 | /** |
||
76 | * Return the paste ID of the current paste. |
||
77 | * |
||
78 | * @access private |
||
79 | * @return string |
||
80 | */ |
||
81 | 27 | private function getPasteId() |
|
82 | { |
||
83 | // RegEx to check for valid paste ID (16 base64 chars) |
||
84 | 27 | $pasteIdRegEx = '/^[a-f0-9]{16}$/'; |
|
85 | |||
86 | 27 | foreach ($_GET as $key => $value) { |
|
87 | // only return if value is empty and key matches RegEx |
||
88 | 27 | if (($value === '') and preg_match($pasteIdRegEx, $key, $match)) { |
|
0 ignored issues
–
show
|
|||
89 | 27 | return $match[0]; |
|
90 | } |
||
91 | } |
||
92 | |||
93 | 2 | return 'invalid id'; |
|
94 | } |
||
95 | |||
96 | /** |
||
97 | * Constructor |
||
98 | * |
||
99 | * @access public |
||
100 | */ |
||
101 | 100 | public function __construct() |
|
102 | { |
||
103 | // decide if we are in JSON API or HTML context |
||
104 | 100 | $this->_isJsonApi = $this->_detectJsonRequest(); |
|
105 | |||
106 | // parse parameters, depending on request type |
||
107 | 100 | switch (array_key_exists('REQUEST_METHOD', $_SERVER) ? $_SERVER['REQUEST_METHOD'] : 'GET') { |
|
108 | 100 | case 'DELETE': |
|
109 | 99 | case 'PUT': |
|
110 | 3 | parse_str(file_get_contents(self::$_inputStream), $this->_params); |
|
111 | 3 | break; |
|
112 | 97 | case 'POST': |
|
113 | 50 | $this->_params = $_POST; |
|
114 | 50 | break; |
|
115 | default: |
||
116 | 47 | $this->_params = $_GET; |
|
117 | } |
||
118 | if ( |
||
119 | 100 | !array_key_exists('pasteid', $this->_params) && |
|
120 | 100 | !array_key_exists('jsonld', $this->_params) && |
|
121 | 100 | array_key_exists('QUERY_STRING', $_SERVER) && |
|
122 | 100 | !empty($_SERVER['QUERY_STRING']) |
|
123 | ) { |
||
124 | 27 | $this->_params['pasteid'] = $this->getPasteId(); |
|
125 | } |
||
126 | |||
127 | // prepare operation, depending on current parameters |
||
128 | if ( |
||
129 | 100 | (array_key_exists('data', $this->_params) && !empty($this->_params['data'])) || |
|
130 | 100 | (array_key_exists('attachment', $this->_params) && !empty($this->_params['attachment'])) |
|
131 | ) { |
||
132 | 46 | $this->_operation = 'create'; |
|
133 | 54 | } elseif (array_key_exists('pasteid', $this->_params) && !empty($this->_params['pasteid'])) { |
|
134 | 40 | if (array_key_exists('deletetoken', $this->_params) && !empty($this->_params['deletetoken'])) { |
|
135 | 20 | $this->_operation = 'delete'; |
|
136 | } else { |
||
137 | 40 | $this->_operation = 'read'; |
|
138 | } |
||
139 | 14 | } elseif (array_key_exists('jsonld', $this->_params) && !empty($this->_params['jsonld'])) { |
|
140 | 5 | $this->_operation = 'jsonld'; |
|
141 | } |
||
142 | 100 | } |
|
143 | |||
144 | /** |
||
145 | * Get current operation |
||
146 | * |
||
147 | * @access public |
||
148 | * @return string |
||
149 | */ |
||
150 | 99 | public function getOperation() |
|
151 | { |
||
152 | 99 | return $this->_operation; |
|
153 | } |
||
154 | |||
155 | /** |
||
156 | * Get a request parameter |
||
157 | * |
||
158 | * @access public |
||
159 | * @param string $param |
||
160 | * @param string $default |
||
161 | * @return string |
||
162 | */ |
||
163 | 91 | public function getParam($param, $default = '') |
|
164 | { |
||
165 | 91 | return array_key_exists($param, $this->_params) ? |
|
166 | 91 | $this->_params[$param] : $default; |
|
167 | } |
||
168 | |||
169 | /** |
||
170 | * Get request URI |
||
171 | * |
||
172 | * @access public |
||
173 | * @return string |
||
174 | */ |
||
175 | 88 | public function getRequestUri() |
|
176 | { |
||
177 | 88 | return array_key_exists('REQUEST_URI', $_SERVER) ? |
|
178 | 88 | htmlspecialchars($_SERVER['REQUEST_URI']) : '/'; |
|
179 | } |
||
180 | |||
181 | /** |
||
182 | * If we are in a JSON API context |
||
183 | * |
||
184 | * @access public |
||
185 | * @return bool |
||
186 | */ |
||
187 | 94 | public function isJsonApiCall() |
|
188 | { |
||
189 | 94 | return $this->_isJsonApi; |
|
190 | } |
||
191 | |||
192 | /** |
||
193 | * Override the default input stream source, used for unit testing |
||
194 | * |
||
195 | * @param string $input |
||
196 | */ |
||
197 | 3 | public static function setInputStream($input) |
|
198 | { |
||
199 | 3 | self::$_inputStream = $input; |
|
200 | 3 | } |
|
201 | |||
202 | /** |
||
203 | * Detect the clients supported media type and decide if its a JSON API call or not |
||
204 | * |
||
205 | * Adapted from: https://stackoverflow.com/questions/3770513/detect-browser-language-in-php#3771447 |
||
206 | * |
||
207 | * @access private |
||
208 | * @return bool |
||
209 | */ |
||
210 | 100 | private function _detectJsonRequest() |
|
211 | { |
||
212 | 100 | $hasAcceptHeader = array_key_exists('HTTP_ACCEPT', $_SERVER); |
|
213 | 100 | $acceptHeader = $hasAcceptHeader ? $_SERVER['HTTP_ACCEPT'] : ''; |
|
214 | |||
215 | // simple cases |
||
216 | if ( |
||
217 | 100 | (array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER) && |
|
218 | 65 | $_SERVER['HTTP_X_REQUESTED_WITH'] == 'JSONHttpRequest') || |
|
219 | 35 | ($hasAcceptHeader && |
|
220 | 35 | strpos($acceptHeader, self::MIME_JSON) !== false && |
|
221 | 35 | strpos($acceptHeader, self::MIME_HTML) === false && |
|
222 | 100 | strpos($acceptHeader, self::MIME_XHTML) === false) |
|
223 | ) { |
||
224 | 67 | return true; |
|
225 | } |
||
226 | |||
227 | // advanced case: media type negotiation |
||
228 | 33 | $mediaTypes = array(); |
|
229 | 33 | if ($hasAcceptHeader) { |
|
230 | 4 | $mediaTypeRanges = explode(',', trim($acceptHeader)); |
|
231 | 4 | foreach ($mediaTypeRanges as $mediaTypeRange) { |
|
232 | 4 | if (preg_match( |
|
233 | 4 | '#(\*/\*|[a-z\-]+/[a-z\-+*]+(?:\s*;\s*[^q]\S*)*)(?:\s*;\s*q\s*=\s*(0(?:\.\d{0,3})|1(?:\.0{0,3})))?#', |
|
234 | 4 | trim($mediaTypeRange), $match |
|
235 | )) { |
||
236 | 4 | if (!isset($match[2])) { |
|
237 | 4 | $match[2] = '1.0'; |
|
238 | } else { |
||
239 | 4 | $match[2] = (string) floatval($match[2]); |
|
240 | } |
||
241 | 4 | if (!isset($mediaTypes[$match[2]])) { |
|
242 | 4 | $mediaTypes[$match[2]] = array(); |
|
243 | } |
||
244 | 4 | $mediaTypes[$match[2]][] = strtolower($match[1]); |
|
245 | } |
||
246 | } |
||
247 | 4 | krsort($mediaTypes); |
|
248 | 4 | foreach ($mediaTypes as $acceptedQuality => $acceptedValues) { |
|
249 | 4 | if ($acceptedQuality === 0.0) { |
|
250 | continue; |
||
251 | } |
||
252 | 4 | foreach ($acceptedValues as $acceptedValue) { |
|
253 | if ( |
||
254 | 4 | strpos($acceptedValue, self::MIME_HTML) === 0 || |
|
255 | 4 | strpos($acceptedValue, self::MIME_XHTML) === 0 |
|
256 | ) { |
||
257 | 2 | return false; |
|
258 | 2 | } elseif (strpos($acceptedValue, self::MIME_JSON) === 0) { |
|
259 | 2 | return true; |
|
260 | } |
||
261 | } |
||
262 | } |
||
263 | } |
||
264 | 30 | return false; |
|
265 | } |
||
266 | } |
||
267 |
PHP has two types of connecting operators (logical operators, and boolean operators):
and
&&
or
||
The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like
&&
, or||
.Let’s take a look at a few examples:
Logical Operators are used for Control-Flow
One case where you explicitly want to use logical operators is for control-flow such as this:
Since
die
introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined withthrow
at this point:These limitations lead to logical operators rarely being of use in current PHP code.