PageRenderTime 62ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/system/core/Output.php

https://github.com/cyber2200/CodeIgniter
PHP | 868 lines | 413 code | 134 blank | 321 comment | 55 complexity | 6fef81caeea3c20ecdff2fdcc2ba66af MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. <?php
  2. /**
  3. * CodeIgniter
  4. *
  5. * An open source application development framework for PHP 5.2.4 or newer
  6. *
  7. * NOTICE OF LICENSE
  8. *
  9. * Licensed under the Open Software License version 3.0
  10. *
  11. * This source file is subject to the Open Software License (OSL 3.0) that is
  12. * bundled with this package in the files license.txt / license.rst. It is
  13. * also available through the world wide web at this URL:
  14. * http://opensource.org/licenses/OSL-3.0
  15. * If you did not receive a copy of the license and are unable to obtain it
  16. * through the world wide web, please send an email to
  17. * licensing@ellislab.com so we can send you a copy immediately.
  18. *
  19. * @package CodeIgniter
  20. * @author EllisLab Dev Team
  21. * @copyright Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
  22. * @license http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
  23. * @link http://codeigniter.com
  24. * @since Version 1.0
  25. * @filesource
  26. */
  27. defined('BASEPATH') OR exit('No direct script access allowed');
  28. /**
  29. * Output Class
  30. *
  31. * Responsible for sending final output to the browser.
  32. *
  33. * @package CodeIgniter
  34. * @subpackage Libraries
  35. * @category Output
  36. * @author EllisLab Dev Team
  37. * @link http://codeigniter.com/user_guide/libraries/output.html
  38. */
  39. class CI_Output {
  40. /**
  41. * Final output string
  42. *
  43. * @var string
  44. */
  45. public $final_output;
  46. /**
  47. * Cache expiration time
  48. *
  49. * @var int
  50. */
  51. public $cache_expiration = 0;
  52. /**
  53. * List of server headers
  54. *
  55. * @var array
  56. */
  57. public $headers = array();
  58. /**
  59. * List of mime types
  60. *
  61. * @var array
  62. */
  63. public $mimes = array();
  64. /**
  65. * Mime-type for the current page
  66. *
  67. * @var string
  68. */
  69. protected $mime_type = 'text/html';
  70. /**
  71. * Enable Profiler flag
  72. *
  73. * @var bool
  74. */
  75. public $enable_profiler = FALSE;
  76. /**
  77. * zLib output compression flag
  78. *
  79. * @var bool
  80. */
  81. protected $_zlib_oc = FALSE;
  82. /**
  83. * List of profiler sections
  84. *
  85. * @var array
  86. */
  87. protected $_profiler_sections = array();
  88. /**
  89. * Parse markers flag
  90. *
  91. * Whether or not to parse variables like {elapsed_time} and {memory_usage}.
  92. *
  93. * @var bool
  94. */
  95. public $parse_exec_vars = TRUE;
  96. /**
  97. * Class constructor
  98. *
  99. * Determines whether zLib output compression will be used.
  100. *
  101. * @return void
  102. */
  103. public function __construct()
  104. {
  105. $this->_zlib_oc = (bool) @ini_get('zlib.output_compression');
  106. // Get mime types for later
  107. $this->mimes =& get_mimes();
  108. log_message('debug', 'Output Class Initialized');
  109. }
  110. // --------------------------------------------------------------------
  111. /**
  112. * Get Output
  113. *
  114. * Returns the current output string.
  115. *
  116. * @return string
  117. */
  118. public function get_output()
  119. {
  120. return $this->final_output;
  121. }
  122. // --------------------------------------------------------------------
  123. /**
  124. * Set Output
  125. *
  126. * Sets the output string.
  127. *
  128. * @param string $output Output data
  129. * @return CI_Output
  130. */
  131. public function set_output($output)
  132. {
  133. $this->final_output = $output;
  134. return $this;
  135. }
  136. // --------------------------------------------------------------------
  137. /**
  138. * Append Output
  139. *
  140. * Appends data onto the output string.
  141. *
  142. * @param string $output Data to append
  143. * @return CI_Output
  144. */
  145. public function append_output($output)
  146. {
  147. if (empty($this->final_output))
  148. {
  149. $this->final_output = $output;
  150. }
  151. else
  152. {
  153. $this->final_output .= $output;
  154. }
  155. return $this;
  156. }
  157. // --------------------------------------------------------------------
  158. /**
  159. * Set Header
  160. *
  161. * Lets you set a server header which will be sent with the final output.
  162. *
  163. * Note: If a file is cached, headers will not be sent.
  164. * @todo We need to figure out how to permit headers to be cached.
  165. *
  166. * @param string $header Header
  167. * @param bool $replace Whether to replace the old header value, if already set
  168. * @return CI_Output
  169. */
  170. public function set_header($header, $replace = TRUE)
  171. {
  172. // If zlib.output_compression is enabled it will compress the output,
  173. // but it will not modify the content-length header to compensate for
  174. // the reduction, causing the browser to hang waiting for more data.
  175. // We'll just skip content-length in those cases.
  176. if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) === 0)
  177. {
  178. return $this;
  179. }
  180. $this->headers[] = array($header, $replace);
  181. return $this;
  182. }
  183. // --------------------------------------------------------------------
  184. /**
  185. * Set Content-Type Header
  186. *
  187. * @param string $mime_type Extension of the file we're outputting
  188. * @param string $charset Character set (default: NULL)
  189. * @return CI_Output
  190. */
  191. public function set_content_type($mime_type, $charset = NULL)
  192. {
  193. if (strpos($mime_type, '/') === FALSE)
  194. {
  195. $extension = ltrim($mime_type, '.');
  196. // Is this extension supported?
  197. if (isset($this->mimes[$extension]))
  198. {
  199. $mime_type =& $this->mimes[$extension];
  200. if (is_array($mime_type))
  201. {
  202. $mime_type = current($mime_type);
  203. }
  204. }
  205. }
  206. $this->mime_type = $mime_type;
  207. if (empty($charset))
  208. {
  209. $charset = config_item('charset');
  210. }
  211. $header = 'Content-Type: '.$mime_type
  212. .(empty($charset) ? NULL : '; charset='.strtolower($charset));
  213. $this->headers[] = array($header, TRUE);
  214. return $this;
  215. }
  216. // --------------------------------------------------------------------
  217. /**
  218. * Get Current Content-Type Header
  219. *
  220. * @return string 'text/html', if not already set
  221. */
  222. public function get_content_type()
  223. {
  224. for ($i = 0, $c = count($this->headers); $i < $c; $i++)
  225. {
  226. if (sscanf($this->headers[$i][0], 'Content-Type: %[^;]', $content_type) === 1)
  227. {
  228. return $content_type;
  229. }
  230. }
  231. return 'text/html';
  232. }
  233. // --------------------------------------------------------------------
  234. /**
  235. * Get Header
  236. *
  237. * @param string $header_name
  238. * @return string
  239. */
  240. public function get_header($header)
  241. {
  242. // Combine headers already sent with our batched headers
  243. $headers = array_merge(
  244. // We only need [x][0] from our multi-dimensional array
  245. array_map('array_shift', $this->headers),
  246. headers_list()
  247. );
  248. if (empty($headers) OR empty($header))
  249. {
  250. return NULL;
  251. }
  252. for ($i = 0, $c = count($headers); $i < $c; $i++)
  253. {
  254. if (strncasecmp($header, $headers[$i], $l = strlen($header)) === 0)
  255. {
  256. return trim(substr($headers[$i], $l+1));
  257. }
  258. }
  259. return NULL;
  260. }
  261. // --------------------------------------------------------------------
  262. /**
  263. * Set HTTP Status Header
  264. *
  265. * As of version 1.7.2, this is an alias for common function
  266. * set_status_header().
  267. *
  268. * @param int $code Status code (default: 200)
  269. * @param string $text Optional message
  270. * @return CI_Output
  271. */
  272. public function set_status_header($code = 200, $text = '')
  273. {
  274. set_status_header($code, $text);
  275. return $this;
  276. }
  277. // --------------------------------------------------------------------
  278. /**
  279. * Enable/disable Profiler
  280. *
  281. * @param bool $val TRUE to enable or FALSE to disable
  282. * @return CI_Output
  283. */
  284. public function enable_profiler($val = TRUE)
  285. {
  286. $this->enable_profiler = is_bool($val) ? $val : TRUE;
  287. return $this;
  288. }
  289. // --------------------------------------------------------------------
  290. /**
  291. * Set Profiler Sections
  292. *
  293. * Allows override of default/config settings for
  294. * Profiler section display.
  295. *
  296. * @param array $sections Profiler sections
  297. * @return CI_Output
  298. */
  299. public function set_profiler_sections($sections)
  300. {
  301. if (isset($sections['query_toggle_count']))
  302. {
  303. $this->_profiler_sections['query_toggle_count'] = (int) $sections['query_toggle_count'];
  304. unset($sections['query_toggle_count']);
  305. }
  306. foreach ($sections as $section => $enable)
  307. {
  308. $this->_profiler_sections[$section] = ($enable !== FALSE);
  309. }
  310. return $this;
  311. }
  312. // --------------------------------------------------------------------
  313. /**
  314. * Set Cache
  315. *
  316. * @param int $time Cache expiration time in seconds
  317. * @return CI_Output
  318. */
  319. public function cache($time)
  320. {
  321. $this->cache_expiration = is_numeric($time) ? $time : 0;
  322. return $this;
  323. }
  324. // --------------------------------------------------------------------
  325. /**
  326. * Display Output
  327. *
  328. * Processes sends the sends finalized output data to the browser along
  329. * with any server headers and profile data. It also stops benchmark
  330. * timers so the page rendering speed and memory usage can be shown.
  331. *
  332. * Note: All "view" data is automatically put into $this->final_output
  333. * by controller class.
  334. *
  335. * @uses CI_Output::$final_output
  336. * @param string $output Output data override
  337. * @return void
  338. */
  339. public function _display($output = '')
  340. {
  341. // Note: We use globals because we can't use $CI =& get_instance()
  342. // since this function is sometimes called by the caching mechanism,
  343. // which happens before the CI super object is available.
  344. global $BM, $CFG;
  345. // Grab the super object if we can.
  346. if (class_exists('CI_Controller'))
  347. {
  348. $CI =& get_instance();
  349. }
  350. // --------------------------------------------------------------------
  351. // Set the output data
  352. if ($output === '')
  353. {
  354. $output =& $this->final_output;
  355. }
  356. // --------------------------------------------------------------------
  357. // Is minify requested?
  358. if ($CFG->item('minify_output') === TRUE)
  359. {
  360. $output = $this->minify($output, $this->mime_type);
  361. }
  362. // --------------------------------------------------------------------
  363. // Do we need to write a cache file? Only if the controller does not have its
  364. // own _output() method and we are not dealing with a cache file, which we
  365. // can determine by the existence of the $CI object above
  366. if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output'))
  367. {
  368. $this->_write_cache($output);
  369. }
  370. // --------------------------------------------------------------------
  371. // Parse out the elapsed time and memory usage,
  372. // then swap the pseudo-variables with the data
  373. $elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end');
  374. if ($this->parse_exec_vars === TRUE)
  375. {
  376. $memory = round(memory_get_usage() / 1024 / 1024, 2).'MB';
  377. $output = str_replace(array('{elapsed_time}', '{memory_usage}'), array($elapsed, $memory), $output);
  378. }
  379. // --------------------------------------------------------------------
  380. // Is compression requested?
  381. if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc === FALSE
  382. && extension_loaded('zlib')
  383. && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
  384. {
  385. ob_start('ob_gzhandler');
  386. }
  387. // --------------------------------------------------------------------
  388. // Are there any server headers to send?
  389. if (count($this->headers) > 0)
  390. {
  391. foreach ($this->headers as $header)
  392. {
  393. @header($header[0], $header[1]);
  394. }
  395. }
  396. // --------------------------------------------------------------------
  397. // Does the $CI object exist?
  398. // If not we know we are dealing with a cache file so we'll
  399. // simply echo out the data and exit.
  400. if ( ! isset($CI))
  401. {
  402. echo $output;
  403. log_message('debug', 'Final output sent to browser');
  404. log_message('debug', 'Total execution time: '.$elapsed);
  405. return;
  406. }
  407. // --------------------------------------------------------------------
  408. // Do we need to generate profile data?
  409. // If so, load the Profile class and run it.
  410. if ($this->enable_profiler === TRUE)
  411. {
  412. $CI->load->library('profiler');
  413. if ( ! empty($this->_profiler_sections))
  414. {
  415. $CI->profiler->set_sections($this->_profiler_sections);
  416. }
  417. // If the output data contains closing </body> and </html> tags
  418. // we will remove them and add them back after we insert the profile data
  419. $output = preg_replace('|</body>.*?</html>|is', '', $output, -1, $count).$CI->profiler->run();
  420. if ($count > 0)
  421. {
  422. $output .= '</body></html>';
  423. }
  424. }
  425. // Does the controller contain a function named _output()?
  426. // If so send the output there. Otherwise, echo it.
  427. if (method_exists($CI, '_output'))
  428. {
  429. $CI->_output($output);
  430. }
  431. else
  432. {
  433. echo $output; // Send it to the browser!
  434. }
  435. log_message('debug', 'Final output sent to browser');
  436. log_message('debug', 'Total execution time: '.$elapsed);
  437. }
  438. // --------------------------------------------------------------------
  439. /**
  440. * Write Cache
  441. *
  442. * @param string $output Output data to cache
  443. * @return void
  444. */
  445. public function _write_cache($output)
  446. {
  447. $CI =& get_instance();
  448. $path = $CI->config->item('cache_path');
  449. $cache_path = ($path === '') ? APPPATH.'cache/' : $path;
  450. if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
  451. {
  452. log_message('error', 'Unable to write cache file: '.$cache_path);
  453. return;
  454. }
  455. $uri = $CI->config->item('base_url').
  456. $CI->config->item('index_page').
  457. $CI->uri->uri_string();
  458. $cache_path .= md5($uri);
  459. if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))
  460. {
  461. log_message('error', 'Unable to write cache file: '.$cache_path);
  462. return;
  463. }
  464. $expire = time() + ($this->cache_expiration * 60);
  465. if (flock($fp, LOCK_EX))
  466. {
  467. fwrite($fp, $expire.'TS--->'.$output);
  468. flock($fp, LOCK_UN);
  469. }
  470. else
  471. {
  472. log_message('error', 'Unable to secure a file lock for file at: '.$cache_path);
  473. return;
  474. }
  475. fclose($fp);
  476. @chmod($cache_path, FILE_WRITE_MODE);
  477. log_message('debug', 'Cache file written: '.$cache_path);
  478. // Send HTTP cache-control headers to browser to match file cache settings.
  479. $this->set_cache_header($_SERVER['REQUEST_TIME'], $expire);
  480. }
  481. // --------------------------------------------------------------------
  482. /**
  483. * Update/serve cached output
  484. *
  485. * @uses CI_Config
  486. * @uses CI_URI
  487. *
  488. * @param object &$CFG CI_Config class instance
  489. * @param object &$URI CI_URI class instance
  490. * @return bool TRUE on success or FALSE on failure
  491. */
  492. public function _display_cache(&$CFG, &$URI)
  493. {
  494. $cache_path = ($CFG->item('cache_path') === '') ? APPPATH.'cache/' : $CFG->item('cache_path');
  495. // Build the file path. The file name is an MD5 hash of the full URI
  496. $uri = $CFG->item('base_url').$CFG->item('index_page').$URI->uri_string;
  497. $filepath = $cache_path.md5($uri);
  498. if ( ! @file_exists($filepath) OR ! $fp = @fopen($filepath, FOPEN_READ))
  499. {
  500. return FALSE;
  501. }
  502. flock($fp, LOCK_SH);
  503. $cache = (filesize($filepath) > 0) ? fread($fp, filesize($filepath)) : '';
  504. flock($fp, LOCK_UN);
  505. fclose($fp);
  506. // Strip out the embedded timestamp
  507. if ( ! preg_match('/^(\d+)TS--->/', $cache, $match))
  508. {
  509. return FALSE;
  510. }
  511. $last_modified = filemtime($cache_path);
  512. $expire = $match[1];
  513. // Has the file expired?
  514. if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path))
  515. {
  516. // If so we'll delete it.
  517. @unlink($filepath);
  518. log_message('debug', 'Cache file has expired. File deleted.');
  519. return FALSE;
  520. }
  521. else
  522. {
  523. // Or else send the HTTP cache control headers.
  524. $this->set_cache_header($last_modified, $expire);
  525. }
  526. // Display the cache
  527. $this->_display(substr($cache, strlen($match[0])));
  528. log_message('debug', 'Cache file is current. Sending it to browser.');
  529. return TRUE;
  530. }
  531. // --------------------------------------------------------------------
  532. /**
  533. * Delete cache
  534. *
  535. * @param string $uri URI string
  536. * @return bool
  537. */
  538. public function delete_cache($uri = '')
  539. {
  540. $CI =& get_instance();
  541. $cache_path = $CI->config->item('cache_path');
  542. if ($cache_path === '')
  543. {
  544. $cache_path = APPPATH.'cache/';
  545. }
  546. if ( ! is_dir($cache_path))
  547. {
  548. log_message('error', 'Unable to find cache path: '.$cache_path);
  549. return FALSE;
  550. }
  551. if (empty($uri))
  552. {
  553. $uri = $CI->uri->uri_string();
  554. }
  555. $cache_path .= md5($CI->config->item('base_url').$CI->config->item('index_page').$uri);
  556. if ( ! @unlink($cache_path))
  557. {
  558. log_message('error', 'Unable to delete cache file for '.$uri);
  559. return FALSE;
  560. }
  561. return TRUE;
  562. }
  563. // --------------------------------------------------------------------
  564. /**
  565. * Set Cache Header
  566. *
  567. * Set the HTTP headers to match the server-side file cache settings
  568. * in order to reduce bandwidth.
  569. *
  570. * @param int $last_modified Timestamp of when the page was last modified
  571. * @param int $expiration Timestamp of when should the requested page expire from cache
  572. * @return void
  573. */
  574. public function set_cache_header($last_modified, $expiration)
  575. {
  576. $max_age = $expiration - $_SERVER['REQUEST_TIME'];
  577. if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $last_modified <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']))
  578. {
  579. $this->set_status_header(304);
  580. exit;
  581. }
  582. else
  583. {
  584. header('Pragma: public');
  585. header('Cache-Control: max-age=' . $max_age . ', public');
  586. header('Expires: '.gmdate('D, d M Y H:i:s', $expiration).' GMT');
  587. header('Last-modified: '.gmdate('D, d M Y H:i:s', $last_modified).' GMT');
  588. }
  589. }
  590. // --------------------------------------------------------------------
  591. /**
  592. * Minify
  593. *
  594. * Reduce excessive size of HTML/CSS/JavaScript content.
  595. *
  596. * @param string $output Output to minify
  597. * @param string $type Output content MIME type
  598. * @return string Minified output
  599. */
  600. public function minify($output, $type = 'text/html')
  601. {
  602. switch ($type)
  603. {
  604. case 'text/html':
  605. if (($size_before = strlen($output)) === 0)
  606. {
  607. return '';
  608. }
  609. // Find all the <pre>,<code>,<textarea>, and <javascript> tags
  610. // We'll want to return them to this unprocessed state later.
  611. preg_match_all('{<pre.+</pre>}msU', $output, $pres_clean);
  612. preg_match_all('{<code.+</code>}msU', $output, $codes_clean);
  613. preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_clean);
  614. preg_match_all('{<script.+</script>}msU', $output, $javascript_clean);
  615. // Minify the CSS in all the <style> tags.
  616. preg_match_all('{<style.+</style>}msU', $output, $style_clean);
  617. foreach ($style_clean[0] as $s)
  618. {
  619. $output = str_replace($s, $this->_minify_script_style($s, TRUE), $output);
  620. }
  621. // Minify the javascript in <script> tags.
  622. foreach ($javascript_clean[0] as $s)
  623. {
  624. $javascript_mini[] = $this->_minify_script_style($s, TRUE);
  625. }
  626. // Replace multiple spaces with a single space.
  627. $output = preg_replace('!\s{2,}!', ' ', $output);
  628. // Remove comments (non-MSIE conditionals)
  629. $output = preg_replace('{\s*<!--[^\[].*-->\s*}msU', '', $output);
  630. // Remove spaces around block-level elements.
  631. $output = preg_replace('/\s*(<\/?(html|head|title|meta|script|link|style|body|h[1-6]|div|p|br)[^>]*>)\s*/is', '$1', $output);
  632. // Replace mangled <pre> etc. tags with unprocessed ones.
  633. if ( ! empty($pres_clean))
  634. {
  635. preg_match_all('{<pre.+</pre>}msU', $output, $pres_messed);
  636. $output = str_replace($pres_messed[0], $pres_clean[0], $output);
  637. }
  638. if ( ! empty($codes_clean))
  639. {
  640. preg_match_all('{<code.+</code>}msU', $output, $codes_messed);
  641. $output = str_replace($codes_messed[0], $codes_clean[0], $output);
  642. }
  643. if ( ! empty($codes_clean))
  644. {
  645. preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_messed);
  646. $output = str_replace($textareas_messed[0], $textareas_clean[0], $output);
  647. }
  648. if (isset($javascript_mini))
  649. {
  650. preg_match_all('{<script.+</script>}msU', $output, $javascript_messed);
  651. $output = str_replace($javascript_messed[0], $javascript_mini, $output);
  652. }
  653. $size_removed = $size_before - strlen($output);
  654. $savings_percent = round(($size_removed / $size_before * 100));
  655. log_message('debug', 'Minifier shaved '.($size_removed / 1000).'KB ('.$savings_percent.'%) off final HTML output.');
  656. break;
  657. case 'text/css':
  658. case 'text/javascript':
  659. $output = $this->_minify_script_style($output);
  660. break;
  661. default: break;
  662. }
  663. return $output;
  664. }
  665. // --------------------------------------------------------------------
  666. /**
  667. * Minify Style and Script
  668. *
  669. * Reduce excessive size of CSS/JavaScript content. To remove spaces this
  670. * script walks the string as an array and determines if the pointer is inside
  671. * a string created by single quotes or double quotes. spaces inside those
  672. * strings are not stripped. Opening and closing tags are severed from
  673. * the string initially and saved without stripping whitespace to preserve
  674. * the tags and any associated properties if tags are present
  675. *
  676. * @param string $output Output to minify
  677. * @param bool $has_tags Specify if the output has style or script tags
  678. * @return string Minified output
  679. */
  680. protected function _minify_script_style($output, $has_tags = FALSE)
  681. {
  682. // We only need this if there are tags in the file
  683. if ($has_tags === TRUE)
  684. {
  685. // Remove opening tag and save for later
  686. $pos = strpos($output, '>');
  687. $open_tag = substr($output, 0, $pos);
  688. $output = substr_replace($output, '', 0, $pos);
  689. // Remove closing tag and save it for later
  690. $end_pos = strlen($output);
  691. $pos = strpos($output, '</');
  692. $closing_tag = substr($output, $pos, $end_pos);
  693. $output = substr_replace($output, '', $pos);
  694. }
  695. // Remove CSS comments
  696. $output = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!i', '', $output);
  697. // Remove spaces around curly brackets, colons,
  698. // semi-colons, parenthesis, commas
  699. $output = preg_replace('!\s*(:|;|,|}|{|\(|\))\s*!i', '$1', $output);
  700. // Remove spaces
  701. $in_string = $in_dstring = FALSE;
  702. $array_output = str_split($output);
  703. foreach ($array_output as $key => $value)
  704. {
  705. if ($in_string === FALSE && $in_dstring === FALSE)
  706. {
  707. if ($value === ' ')
  708. {
  709. unset($array_output[$key]);
  710. }
  711. }
  712. if ($value === "'")
  713. {
  714. $in_string = ! $in_string;
  715. }
  716. elseif ($value === '"')
  717. {
  718. $in_dstring = ! $in_dstring;
  719. }
  720. }
  721. // Remove breaklines and tabs
  722. $output = preg_replace('/[\r\n\t]/', '', implode($array_output));
  723. // Put the opening and closing tags back if applicable
  724. return isset($open_tag)
  725. ? $open_tag.$output.$closing_tag
  726. : $output;
  727. }
  728. }
  729. /* End of file Output.php */
  730. /* Location: ./system/core/Output.php */