These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * CodeIgniter |
||
4 | * |
||
5 | * An open source application development framework for PHP |
||
6 | * |
||
7 | * This content is released under the MIT License (MIT) |
||
8 | * |
||
9 | * Copyright (c) 2014 - 2018, British Columbia Institute of Technology |
||
10 | * |
||
11 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
||
12 | * of this software and associated documentation files (the "Software"), to deal |
||
13 | * in the Software without restriction, including without limitation the rights |
||
14 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||
15 | * copies of the Software, and to permit persons to whom the Software is |
||
16 | * furnished to do so, subject to the following conditions: |
||
17 | * |
||
18 | * The above copyright notice and this permission notice shall be included in |
||
19 | * all copies or substantial portions of the Software. |
||
20 | * |
||
21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||
22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||
23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||
24 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||
25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||
26 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||
27 | * THE SOFTWARE. |
||
28 | * |
||
29 | * @package CodeIgniter |
||
30 | * @author EllisLab Dev Team |
||
31 | * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) |
||
32 | * @copyright Copyright (c) 2014 - 2018, British Columbia Institute of Technology (http://bcit.ca/) |
||
33 | * @license http://opensource.org/licenses/MIT MIT License |
||
34 | * @link https://codeigniter.com |
||
35 | * @since Version 1.0.0 |
||
36 | * @filesource |
||
37 | */ |
||
38 | defined('BASEPATH') OR exit('No direct script access allowed'); |
||
39 | |||
40 | /** |
||
41 | * Output Class |
||
42 | * |
||
43 | * Responsible for sending final output to the browser. |
||
44 | * |
||
45 | * @package CodeIgniter |
||
46 | * @subpackage Libraries |
||
47 | * @category Output |
||
48 | * @author EllisLab Dev Team |
||
49 | * @link https://codeigniter.com/user_guide/libraries/output.html |
||
50 | */ |
||
51 | View Code Duplication | class CI_Output { |
|
52 | |||
53 | /** |
||
54 | * Final output string |
||
55 | * |
||
56 | * @var string |
||
57 | */ |
||
58 | public $final_output; |
||
59 | |||
60 | /** |
||
61 | * Cache expiration time |
||
62 | * |
||
63 | * @var int |
||
64 | */ |
||
65 | public $cache_expiration = 0; |
||
66 | |||
67 | /** |
||
68 | * List of server headers |
||
69 | * |
||
70 | * @var array |
||
71 | */ |
||
72 | public $headers = array(); |
||
73 | |||
74 | /** |
||
75 | * List of mime types |
||
76 | * |
||
77 | * @var array |
||
78 | */ |
||
79 | public $mimes = array(); |
||
80 | |||
81 | /** |
||
82 | * Mime-type for the current page |
||
83 | * |
||
84 | * @var string |
||
85 | */ |
||
86 | protected $mime_type = 'text/html'; |
||
87 | |||
88 | /** |
||
89 | * Enable Profiler flag |
||
90 | * |
||
91 | * @var bool |
||
92 | */ |
||
93 | public $enable_profiler = FALSE; |
||
94 | |||
95 | /** |
||
96 | * php.ini zlib.output_compression flag |
||
97 | * |
||
98 | * @var bool |
||
99 | */ |
||
100 | protected $_zlib_oc = FALSE; |
||
101 | |||
102 | /** |
||
103 | * CI output compression flag |
||
104 | * |
||
105 | * @var bool |
||
106 | */ |
||
107 | protected $_compress_output = FALSE; |
||
108 | |||
109 | /** |
||
110 | * List of profiler sections |
||
111 | * |
||
112 | * @var array |
||
113 | */ |
||
114 | protected $_profiler_sections = array(); |
||
115 | |||
116 | /** |
||
117 | * Parse markers flag |
||
118 | * |
||
119 | * Whether or not to parse variables like {elapsed_time} and {memory_usage}. |
||
120 | * |
||
121 | * @var bool |
||
122 | */ |
||
123 | public $parse_exec_vars = TRUE; |
||
124 | |||
125 | /** |
||
126 | * mbstring.func_overload flag |
||
127 | * |
||
128 | * @var bool |
||
129 | */ |
||
130 | protected static $func_overload; |
||
131 | |||
132 | /** |
||
133 | * Response status (status code, text and is redirection?) |
||
134 | * |
||
135 | * @var array |
||
136 | * |
||
137 | * added by ci-phpunit-test |
||
138 | */ |
||
139 | public $_status; |
||
140 | |||
141 | /** |
||
142 | * Cookies |
||
143 | * |
||
144 | * @var array |
||
145 | * |
||
146 | * added by ci-phpunit-test |
||
147 | */ |
||
148 | public $_cookies; |
||
149 | |||
150 | /** |
||
151 | * Class constructor |
||
152 | * |
||
153 | * Determines whether zLib output compression will be used. |
||
154 | * |
||
155 | * @return void |
||
156 | */ |
||
157 | public function __construct() |
||
158 | { |
||
159 | $this->_zlib_oc = (bool) ini_get('zlib.output_compression'); |
||
160 | $this->_compress_output = ( |
||
161 | $this->_zlib_oc === FALSE |
||
162 | && config_item('compress_output') === TRUE |
||
163 | && extension_loaded('zlib') |
||
164 | ); |
||
165 | |||
166 | isset(self::$func_overload) OR self::$func_overload = (extension_loaded('mbstring') && ini_get('mbstring.func_overload')); |
||
167 | |||
168 | // Get mime types for later |
||
169 | $this->mimes =& get_mimes(); |
||
170 | |||
171 | log_message('info', 'Output Class Initialized'); |
||
172 | } |
||
173 | |||
174 | // -------------------------------------------------------------------- |
||
175 | |||
176 | /** |
||
177 | * Get Output |
||
178 | * |
||
179 | * Returns the current output string. |
||
180 | * |
||
181 | * @return string |
||
182 | */ |
||
183 | public function get_output() |
||
184 | { |
||
185 | return $this->final_output; |
||
186 | } |
||
187 | |||
188 | // -------------------------------------------------------------------- |
||
189 | |||
190 | /** |
||
191 | * Set Output |
||
192 | * |
||
193 | * Sets the output string. |
||
194 | * |
||
195 | * @param string $output Output data |
||
196 | * @return CI_Output |
||
197 | */ |
||
198 | public function set_output($output) |
||
199 | { |
||
200 | $this->final_output = $output; |
||
201 | return $this; |
||
202 | } |
||
203 | |||
204 | // -------------------------------------------------------------------- |
||
205 | |||
206 | /** |
||
207 | * Append Output |
||
208 | * |
||
209 | * Appends data onto the output string. |
||
210 | * |
||
211 | * @param string $output Data to append |
||
212 | * @return CI_Output |
||
213 | */ |
||
214 | public function append_output($output) |
||
215 | { |
||
216 | $this->final_output .= $output; |
||
217 | return $this; |
||
218 | } |
||
219 | |||
220 | // -------------------------------------------------------------------- |
||
221 | |||
222 | /** |
||
223 | * Set Header |
||
224 | * |
||
225 | * Lets you set a server header which will be sent with the final output. |
||
226 | * |
||
227 | * Note: If a file is cached, headers will not be sent. |
||
228 | * @todo We need to figure out how to permit headers to be cached. |
||
229 | * |
||
230 | * @param string $header Header |
||
231 | * @param bool $replace Whether to replace the old header value, if already set |
||
232 | * @return CI_Output |
||
233 | */ |
||
234 | public function set_header($header, $replace = TRUE) |
||
235 | { |
||
236 | // If zlib.output_compression is enabled it will compress the output, |
||
237 | // but it will not modify the content-length header to compensate for |
||
238 | // the reduction, causing the browser to hang waiting for more data. |
||
239 | // We'll just skip content-length in those cases. |
||
240 | if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) === 0) |
||
241 | { |
||
242 | return $this; |
||
243 | } |
||
244 | |||
245 | $this->headers[] = array($header, $replace); |
||
246 | return $this; |
||
247 | } |
||
248 | |||
249 | // -------------------------------------------------------------------- |
||
250 | |||
251 | /** |
||
252 | * Set Content-Type Header |
||
253 | * |
||
254 | * @param string $mime_type Extension of the file we're outputting |
||
255 | * @param string $charset Character set (default: NULL) |
||
256 | * @return CI_Output |
||
257 | */ |
||
258 | public function set_content_type($mime_type, $charset = NULL) |
||
259 | { |
||
260 | if (strpos($mime_type, '/') === FALSE) |
||
261 | { |
||
262 | $extension = ltrim($mime_type, '.'); |
||
263 | |||
264 | // Is this extension supported? |
||
265 | if (isset($this->mimes[$extension])) |
||
266 | { |
||
267 | $mime_type =& $this->mimes[$extension]; |
||
268 | |||
269 | if (is_array($mime_type)) |
||
270 | { |
||
271 | $mime_type = current($mime_type); |
||
272 | } |
||
273 | } |
||
274 | } |
||
275 | |||
276 | $this->mime_type = $mime_type; |
||
277 | |||
278 | if (empty($charset)) |
||
279 | { |
||
280 | $charset = config_item('charset'); |
||
281 | } |
||
282 | |||
283 | $header = 'Content-Type: '.$mime_type |
||
284 | .(empty($charset) ? '' : '; charset='.$charset); |
||
285 | |||
286 | $this->headers[] = array($header, TRUE); |
||
287 | return $this; |
||
288 | } |
||
289 | |||
290 | // -------------------------------------------------------------------- |
||
291 | |||
292 | /** |
||
293 | * Get Current Content-Type Header |
||
294 | * |
||
295 | * @return string 'text/html', if not already set |
||
296 | */ |
||
297 | public function get_content_type() |
||
298 | { |
||
299 | for ($i = 0, $c = count($this->headers); $i < $c; $i++) |
||
300 | { |
||
301 | if (sscanf($this->headers[$i][0], 'Content-Type: %[^;]', $content_type) === 1) |
||
302 | { |
||
303 | return $content_type; |
||
304 | } |
||
305 | } |
||
306 | |||
307 | return 'text/html'; |
||
308 | } |
||
309 | |||
310 | // -------------------------------------------------------------------- |
||
311 | |||
312 | /** |
||
313 | * Get Header |
||
314 | * |
||
315 | * @param string $header |
||
316 | * @return string |
||
317 | */ |
||
318 | public function get_header($header) |
||
319 | { |
||
320 | // Combine headers already sent with our batched headers |
||
321 | $headers = array_merge( |
||
322 | // We only need [x][0] from our multi-dimensional array |
||
323 | array_map('array_shift', $this->headers), |
||
324 | headers_list() |
||
325 | ); |
||
326 | |||
327 | if (empty($headers) OR empty($header)) |
||
328 | { |
||
329 | return NULL; |
||
330 | } |
||
331 | |||
332 | // Count backwards, in order to get the last matching header |
||
333 | for ($c = count($headers) - 1; $c > -1; $c--) |
||
334 | { |
||
335 | if (strncasecmp($header, $headers[$c], $l = self::strlen($header)) === 0) |
||
336 | { |
||
337 | return trim(self::substr($headers[$c], $l+1)); |
||
338 | } |
||
339 | } |
||
340 | |||
341 | return NULL; |
||
342 | } |
||
343 | |||
344 | // -------------------------------------------------------------------- |
||
345 | |||
346 | /** |
||
347 | * Set HTTP Status Header |
||
348 | * |
||
349 | * As of version 1.7.2, this is an alias for common function |
||
350 | * set_status_header(). |
||
351 | * |
||
352 | * @param int $code Status code (default: 200) |
||
353 | * @param string $text Optional message |
||
354 | * @return CI_Output |
||
355 | */ |
||
356 | public function set_status_header($code = 200, $text = '') |
||
357 | { |
||
358 | set_status_header($code, $text); |
||
359 | return $this; |
||
360 | } |
||
361 | |||
362 | // -------------------------------------------------------------------- |
||
363 | |||
364 | /** |
||
365 | * Enable/disable Profiler |
||
366 | * |
||
367 | * @param bool $val TRUE to enable or FALSE to disable |
||
368 | * @return CI_Output |
||
369 | */ |
||
370 | public function enable_profiler($val = TRUE) |
||
371 | { |
||
372 | $this->enable_profiler = is_bool($val) ? $val : TRUE; |
||
373 | return $this; |
||
374 | } |
||
375 | |||
376 | // -------------------------------------------------------------------- |
||
377 | |||
378 | /** |
||
379 | * Set Profiler Sections |
||
380 | * |
||
381 | * Allows override of default/config settings for |
||
382 | * Profiler section display. |
||
383 | * |
||
384 | * @param array $sections Profiler sections |
||
385 | * @return CI_Output |
||
386 | */ |
||
387 | public function set_profiler_sections($sections) |
||
388 | { |
||
389 | if (isset($sections['query_toggle_count'])) |
||
390 | { |
||
391 | $this->_profiler_sections['query_toggle_count'] = (int) $sections['query_toggle_count']; |
||
392 | unset($sections['query_toggle_count']); |
||
393 | } |
||
394 | |||
395 | foreach ($sections as $section => $enable) |
||
396 | { |
||
397 | $this->_profiler_sections[$section] = ($enable !== FALSE); |
||
398 | } |
||
399 | |||
400 | return $this; |
||
401 | } |
||
402 | |||
403 | // -------------------------------------------------------------------- |
||
404 | |||
405 | /** |
||
406 | * Set Cache |
||
407 | * |
||
408 | * @param int $time Cache expiration time in minutes |
||
409 | * @return CI_Output |
||
410 | */ |
||
411 | public function cache($time) |
||
412 | { |
||
413 | $this->cache_expiration = is_numeric($time) ? $time : 0; |
||
414 | return $this; |
||
415 | } |
||
416 | |||
417 | // -------------------------------------------------------------------- |
||
418 | |||
419 | /** |
||
420 | * Display Output |
||
421 | * |
||
422 | * Processes and sends finalized output data to the browser along |
||
423 | * with any server headers and profile data. It also stops benchmark |
||
424 | * timers so the page rendering speed and memory usage can be shown. |
||
425 | * |
||
426 | * Note: All "view" data is automatically put into $this->final_output |
||
427 | * by controller class. |
||
428 | * |
||
429 | * @uses CI_Output::$final_output |
||
430 | * @param string $output Output data override |
||
431 | * @return void |
||
432 | * |
||
433 | * modified by ci-phpunit-test |
||
434 | */ |
||
435 | public function _display($output = '') |
||
436 | { |
||
437 | // Note: We use load_class() because we can't use $CI =& get_instance() |
||
438 | // since this function is sometimes called by the caching mechanism, |
||
439 | // which happens before the CI super object is available. |
||
440 | $BM =& load_class('Benchmark', 'core'); |
||
441 | $CFG =& load_class('Config', 'core'); |
||
442 | |||
443 | // Grab the super object if we can. |
||
444 | if (class_exists('CI_Controller', FALSE)) |
||
445 | { |
||
446 | $CI =& get_instance(); |
||
447 | } |
||
448 | |||
449 | // -------------------------------------------------------------------- |
||
450 | |||
451 | // Set the output data |
||
452 | if ($output === '') |
||
453 | { |
||
454 | $output =& $this->final_output; |
||
455 | } |
||
456 | |||
457 | // -------------------------------------------------------------------- |
||
458 | |||
459 | // Do we need to write a cache file? Only if the controller does not have its |
||
460 | // own _output() method and we are not dealing with a cache file, which we |
||
461 | // can determine by the existence of the $CI object above |
||
462 | if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output')) |
||
463 | { |
||
464 | $this->_write_cache($output); |
||
465 | } |
||
466 | |||
467 | // -------------------------------------------------------------------- |
||
468 | |||
469 | // Parse out the elapsed time and memory usage, |
||
470 | // then swap the pseudo-variables with the data |
||
471 | |||
472 | $elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end'); |
||
473 | |||
474 | if ($this->parse_exec_vars === TRUE) |
||
475 | { |
||
476 | $memory = round(memory_get_usage() / 1024 / 1024, 2).'MB'; |
||
477 | $output = str_replace(array('{elapsed_time}', '{memory_usage}'), array($elapsed, $memory), $output); |
||
478 | } |
||
479 | |||
480 | // -------------------------------------------------------------------- |
||
481 | |||
482 | // Is compression requested? |
||
483 | if (isset($CI) // This means that we're not serving a cache file, if we were, it would already be compressed |
||
484 | && $this->_compress_output === TRUE |
||
485 | && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) |
||
486 | { |
||
487 | ob_start('ob_gzhandler'); |
||
488 | } |
||
489 | |||
490 | // -------------------------------------------------------------------- |
||
491 | |||
492 | // Are there any server headers to send? |
||
493 | if (count($this->headers) > 0) |
||
494 | { |
||
495 | foreach ($this->headers as $header) |
||
496 | { |
||
497 | // @header($header[0], $header[1]); |
||
498 | } |
||
499 | } |
||
500 | |||
501 | // -------------------------------------------------------------------- |
||
502 | |||
503 | // Does the $CI object exist? |
||
504 | // If not we know we are dealing with a cache file so we'll |
||
505 | // simply echo out the data and exit. |
||
506 | if ( ! isset($CI)) |
||
507 | { |
||
508 | if ($this->_compress_output === TRUE) |
||
509 | { |
||
510 | if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) |
||
511 | { |
||
512 | // header('Content-Encoding: gzip'); |
||
513 | // header('Content-Length: '.self::strlen($output)); |
||
514 | } |
||
515 | else |
||
516 | { |
||
517 | // User agent doesn't support gzip compression, |
||
518 | // so we'll have to decompress our cache |
||
519 | $output = gzinflate(self::substr($output, 10, -8)); |
||
520 | } |
||
521 | } |
||
522 | |||
523 | echo $output; |
||
524 | log_message('info', 'Final output sent to browser'); |
||
525 | log_message('debug', 'Total execution time: '.$elapsed); |
||
526 | return; |
||
527 | } |
||
528 | |||
529 | // -------------------------------------------------------------------- |
||
530 | |||
531 | // Do we need to generate profile data? |
||
532 | // If so, load the Profile class and run it. |
||
533 | if ($this->enable_profiler === TRUE) |
||
534 | { |
||
535 | $CI->load->library('profiler'); |
||
536 | if ( ! empty($this->_profiler_sections)) |
||
537 | { |
||
538 | $CI->profiler->set_sections($this->_profiler_sections); |
||
539 | } |
||
540 | |||
541 | // If the output data contains closing </body> and </html> tags |
||
542 | // we will remove them and add them back after we insert the profile data |
||
543 | $output = preg_replace('|</body>.*?</html>|is', '', $output, -1, $count).$CI->profiler->run(); |
||
544 | if ($count > 0) |
||
545 | { |
||
546 | $output .= '</body></html>'; |
||
547 | } |
||
548 | } |
||
549 | |||
550 | // Does the controller contain a function named _output()? |
||
551 | // If so send the output there. Otherwise, echo it. |
||
552 | if (method_exists($CI, '_output')) |
||
553 | { |
||
554 | $CI->_output($output); |
||
555 | } |
||
556 | else |
||
557 | { |
||
558 | echo $output; // Send it to the browser! |
||
559 | } |
||
560 | |||
561 | log_message('info', 'Final output sent to browser'); |
||
562 | log_message('debug', 'Total execution time: '.$elapsed); |
||
563 | } |
||
564 | |||
565 | // -------------------------------------------------------------------- |
||
566 | |||
567 | /** |
||
568 | * Write Cache |
||
569 | * |
||
570 | * @param string $output Output data to cache |
||
571 | * @return void |
||
572 | */ |
||
573 | public function _write_cache($output) |
||
574 | { |
||
575 | $CI =& get_instance(); |
||
576 | $path = $CI->config->item('cache_path'); |
||
577 | $cache_path = ($path === '') ? APPPATH.'cache/' : $path; |
||
578 | |||
579 | if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path)) |
||
580 | { |
||
581 | log_message('error', 'Unable to write cache file: '.$cache_path); |
||
582 | return; |
||
583 | } |
||
584 | |||
585 | $uri = $CI->config->item('base_url') |
||
586 | .$CI->config->item('index_page') |
||
587 | .$CI->uri->uri_string(); |
||
588 | |||
589 | if (($cache_query_string = $CI->config->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING'])) |
||
590 | { |
||
591 | if (is_array($cache_query_string)) |
||
592 | { |
||
593 | $uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string))); |
||
594 | } |
||
595 | else |
||
596 | { |
||
597 | $uri .= '?'.$_SERVER['QUERY_STRING']; |
||
598 | } |
||
599 | } |
||
600 | |||
601 | $cache_path .= md5($uri); |
||
602 | |||
603 | if ( ! $fp = @fopen($cache_path, 'w+b')) |
||
604 | { |
||
605 | log_message('error', 'Unable to write cache file: '.$cache_path); |
||
606 | return; |
||
607 | } |
||
608 | |||
609 | if ( ! flock($fp, LOCK_EX)) |
||
610 | { |
||
611 | log_message('error', 'Unable to secure a file lock for file at: '.$cache_path); |
||
612 | fclose($fp); |
||
613 | return; |
||
614 | } |
||
615 | |||
616 | // If output compression is enabled, compress the cache |
||
617 | // itself, so that we don't have to do that each time |
||
618 | // we're serving it |
||
619 | if ($this->_compress_output === TRUE) |
||
620 | { |
||
621 | $output = gzencode($output); |
||
622 | |||
623 | if ($this->get_header('content-type') === NULL) |
||
624 | { |
||
625 | $this->set_content_type($this->mime_type); |
||
626 | } |
||
627 | } |
||
628 | |||
629 | $expire = time() + ($this->cache_expiration * 60); |
||
630 | |||
631 | // Put together our serialized info. |
||
632 | $cache_info = serialize(array( |
||
633 | 'expire' => $expire, |
||
634 | 'headers' => $this->headers |
||
635 | )); |
||
636 | |||
637 | $output = $cache_info.'ENDCI--->'.$output; |
||
638 | |||
639 | for ($written = 0, $length = self::strlen($output); $written < $length; $written += $result) |
||
640 | { |
||
641 | if (($result = fwrite($fp, self::substr($output, $written))) === FALSE) |
||
642 | { |
||
643 | break; |
||
644 | } |
||
645 | } |
||
646 | |||
647 | flock($fp, LOCK_UN); |
||
648 | fclose($fp); |
||
649 | |||
650 | if ( ! is_int($result)) |
||
0 ignored issues
–
show
|
|||
651 | { |
||
652 | @unlink($cache_path); |
||
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
|
|||
653 | log_message('error', 'Unable to write the complete cache content at: '.$cache_path); |
||
654 | return; |
||
655 | } |
||
656 | |||
657 | chmod($cache_path, 0640); |
||
658 | log_message('debug', 'Cache file written: '.$cache_path); |
||
659 | |||
660 | // Send HTTP cache-control headers to browser to match file cache settings. |
||
661 | $this->set_cache_header($_SERVER['REQUEST_TIME'], $expire); |
||
662 | } |
||
663 | |||
664 | // -------------------------------------------------------------------- |
||
665 | |||
666 | /** |
||
667 | * Update/serve cached output |
||
668 | * |
||
669 | * @uses CI_Config |
||
670 | * @uses CI_URI |
||
671 | * |
||
672 | * @param object &$CFG CI_Config class instance |
||
673 | * @param object &$URI CI_URI class instance |
||
674 | * @return bool TRUE on success or FALSE on failure |
||
675 | */ |
||
676 | public function _display_cache(&$CFG, &$URI) |
||
677 | { |
||
678 | $cache_path = ($CFG->item('cache_path') === '') ? APPPATH.'cache/' : $CFG->item('cache_path'); |
||
679 | |||
680 | // Build the file path. The file name is an MD5 hash of the full URI |
||
681 | $uri = $CFG->item('base_url').$CFG->item('index_page').$URI->uri_string; |
||
682 | |||
683 | if (($cache_query_string = $CFG->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING'])) |
||
684 | { |
||
685 | if (is_array($cache_query_string)) |
||
686 | { |
||
687 | $uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string))); |
||
688 | } |
||
689 | else |
||
690 | { |
||
691 | $uri .= '?'.$_SERVER['QUERY_STRING']; |
||
692 | } |
||
693 | } |
||
694 | |||
695 | $filepath = $cache_path.md5($uri); |
||
696 | |||
697 | if ( ! file_exists($filepath) OR ! $fp = @fopen($filepath, 'rb')) |
||
698 | { |
||
699 | return FALSE; |
||
700 | } |
||
701 | |||
702 | flock($fp, LOCK_SH); |
||
703 | |||
704 | $cache = (filesize($filepath) > 0) ? fread($fp, filesize($filepath)) : ''; |
||
705 | |||
706 | flock($fp, LOCK_UN); |
||
707 | fclose($fp); |
||
708 | |||
709 | // Look for embedded serialized file info. |
||
710 | if ( ! preg_match('/^(.*)ENDCI--->/', $cache, $match)) |
||
711 | { |
||
712 | return FALSE; |
||
713 | } |
||
714 | |||
715 | $cache_info = unserialize($match[1]); |
||
716 | $expire = $cache_info['expire']; |
||
717 | |||
718 | $last_modified = filemtime($filepath); |
||
719 | |||
720 | // Has the file expired? |
||
721 | if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path)) |
||
722 | { |
||
723 | // If so we'll delete it. |
||
724 | @unlink($filepath); |
||
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
|
|||
725 | log_message('debug', 'Cache file has expired. File deleted.'); |
||
726 | return FALSE; |
||
727 | } |
||
728 | |||
729 | // Send the HTTP cache control headers |
||
730 | $this->set_cache_header($last_modified, $expire); |
||
731 | |||
732 | // Add headers from cache file. |
||
733 | foreach ($cache_info['headers'] as $header) |
||
734 | { |
||
735 | $this->set_header($header[0], $header[1]); |
||
736 | } |
||
737 | |||
738 | // Display the cache |
||
739 | $this->_display(self::substr($cache, self::strlen($match[0]))); |
||
740 | log_message('debug', 'Cache file is current. Sending it to browser.'); |
||
741 | return TRUE; |
||
742 | } |
||
743 | |||
744 | // -------------------------------------------------------------------- |
||
745 | |||
746 | /** |
||
747 | * Delete cache |
||
748 | * |
||
749 | * @param string $uri URI string |
||
750 | * @return bool |
||
751 | */ |
||
752 | public function delete_cache($uri = '') |
||
753 | { |
||
754 | $CI =& get_instance(); |
||
755 | $cache_path = $CI->config->item('cache_path'); |
||
756 | if ($cache_path === '') |
||
757 | { |
||
758 | $cache_path = APPPATH.'cache/'; |
||
759 | } |
||
760 | |||
761 | if ( ! is_dir($cache_path)) |
||
762 | { |
||
763 | log_message('error', 'Unable to find cache path: '.$cache_path); |
||
764 | return FALSE; |
||
765 | } |
||
766 | |||
767 | if (empty($uri)) |
||
768 | { |
||
769 | $uri = $CI->uri->uri_string(); |
||
770 | |||
771 | if (($cache_query_string = $CI->config->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING'])) |
||
772 | { |
||
773 | if (is_array($cache_query_string)) |
||
774 | { |
||
775 | $uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string))); |
||
776 | } |
||
777 | else |
||
778 | { |
||
779 | $uri .= '?'.$_SERVER['QUERY_STRING']; |
||
780 | } |
||
781 | } |
||
782 | } |
||
783 | |||
784 | $cache_path .= md5($CI->config->item('base_url').$CI->config->item('index_page').ltrim($uri, '/')); |
||
785 | |||
786 | if ( ! @unlink($cache_path)) |
||
787 | { |
||
788 | log_message('error', 'Unable to delete cache file for '.$uri); |
||
789 | return FALSE; |
||
790 | } |
||
791 | |||
792 | return TRUE; |
||
793 | } |
||
794 | |||
795 | // -------------------------------------------------------------------- |
||
796 | |||
797 | /** |
||
798 | * Set Cache Header |
||
799 | * |
||
800 | * Set the HTTP headers to match the server-side file cache settings |
||
801 | * in order to reduce bandwidth. |
||
802 | * |
||
803 | * @param int $last_modified Timestamp of when the page was last modified |
||
804 | * @param int $expiration Timestamp of when should the requested page expire from cache |
||
805 | * @return void |
||
806 | * |
||
807 | * modified by ci-phpunit-test |
||
808 | */ |
||
809 | public function set_cache_header($last_modified, $expiration) |
||
810 | { |
||
811 | $max_age = $expiration - $_SERVER['REQUEST_TIME']; |
||
0 ignored issues
–
show
$max_age is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the
Loading history...
|
|||
812 | |||
813 | if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $last_modified <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) |
||
814 | { |
||
815 | $this->set_status_header(304); |
||
816 | exit; |
||
0 ignored issues
–
show
The method
set_cache_header() contains an exit expression.
An exit expression should only be used in rare cases. For example, if you write a short command line script. In most cases however, using an
Loading history...
|
|||
817 | } |
||
818 | |||
819 | // header('Pragma: public'); |
||
820 | // header('Cache-Control: max-age='.$max_age.', public'); |
||
821 | // header('Expires: '.gmdate('D, d M Y H:i:s', $expiration).' GMT'); |
||
822 | // header('Last-modified: '.gmdate('D, d M Y H:i:s', $last_modified).' GMT'); |
||
823 | } |
||
824 | |||
825 | // -------------------------------------------------------------------- |
||
826 | |||
827 | /** |
||
828 | * Byte-safe strlen() |
||
829 | * |
||
830 | * @param string $str |
||
831 | * @return int |
||
832 | */ |
||
833 | protected static function strlen($str) |
||
834 | { |
||
835 | return (self::$func_overload) |
||
836 | ? mb_strlen($str, '8bit') |
||
837 | : strlen($str); |
||
838 | } |
||
839 | |||
840 | // -------------------------------------------------------------------- |
||
841 | |||
842 | /** |
||
843 | * Byte-safe substr() |
||
844 | * |
||
845 | * @param string $str |
||
846 | * @param int $start |
||
847 | * @param int $length |
||
848 | * @return string |
||
849 | */ |
||
850 | protected static function substr($str, $start, $length = NULL) |
||
851 | { |
||
852 | if (self::$func_overload) |
||
853 | { |
||
854 | // mb_substr($str, $start, null, '8bit') returns an empty |
||
855 | // string on PHP 5.3 |
||
856 | isset($length) OR $length = ($start >= 0 ? self::strlen($str) - $start : -$start); |
||
857 | return mb_substr($str, $start, $length, '8bit'); |
||
858 | } |
||
859 | |||
860 | return isset($length) |
||
861 | ? substr($str, $start, $length) |
||
862 | : substr($str, $start); |
||
863 | } |
||
864 | } |
||
865 |
If you define a variable conditionally, it can happen that it is not defined for all execution paths.
Let’s take a look at an example:
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.
Available Fixes
Check for existence of the variable explicitly:
Define a default value for the variable:
Add a value for the missing path: