1<?php 2/** 3 * Output buffering wrapper 4 */ 5 6declare(strict_types=1); 7 8namespace PhpMyAdmin; 9 10use function defined; 11use function flush; 12use function function_exists; 13use function header; 14use function ini_get; 15use function ob_end_clean; 16use function ob_flush; 17use function ob_get_contents; 18use function ob_get_length; 19use function ob_get_level; 20use function ob_get_status; 21use function ob_start; 22use function register_shutdown_function; 23 24/** 25 * Output buffering wrapper class 26 */ 27class OutputBuffering 28{ 29 /** @var self */ 30 private static $instance; 31 32 /** @var int */ 33 private $mode; 34 35 /** @var string */ 36 private $content; 37 38 /** @var bool */ 39 private $on; 40 41 /** 42 * Initializes class 43 */ 44 private function __construct() 45 { 46 $this->mode = $this->getMode(); 47 $this->on = false; 48 } 49 50 /** 51 * This function could be used eventually to support more modes. 52 * 53 * @return int the output buffer mode 54 */ 55 private function getMode() 56 { 57 $mode = 0; 58 if ($GLOBALS['cfg']['OBGzip'] && function_exists('ob_start')) { 59 if (ini_get('output_handler') === 'ob_gzhandler') { 60 // If a user sets the output_handler in php.ini to ob_gzhandler, then 61 // any right frame file in phpMyAdmin will not be handled properly by 62 // the browser. My fix was to check the ini file within the 63 // PMA_outBufferModeGet() function. 64 $mode = 0; 65 } elseif (function_exists('ob_get_level') && ob_get_level() > 0) { 66 // happens when php.ini's output_buffering is not Off 67 ob_end_clean(); 68 $mode = 1; 69 } else { 70 $mode = 1; 71 } 72 } 73 74 // Zero (0) is no mode or in other words output buffering is OFF. 75 // Follow 2^0, 2^1, 2^2, 2^3 type values for the modes. 76 // Useful if we ever decide to combine modes. Then a bitmask field of 77 // the sum of all modes will be the natural choice. 78 return $mode; 79 } 80 81 /** 82 * Returns the singleton OutputBuffering object 83 * 84 * @return OutputBuffering object 85 */ 86 public static function getInstance() 87 { 88 if (empty(self::$instance)) { 89 self::$instance = new OutputBuffering(); 90 } 91 92 return self::$instance; 93 } 94 95 /** 96 * This function will need to run at the top of all pages if output 97 * output buffering is turned on. It also needs to be passed $mode from 98 * the PMA_outBufferModeGet() function or it will be useless. 99 * 100 * @return void 101 */ 102 public function start() 103 { 104 if ($this->on) { 105 return; 106 } 107 108 if ($this->mode && function_exists('ob_gzhandler')) { 109 ob_start('ob_gzhandler'); 110 } 111 ob_start(); 112 if (! defined('TESTSUITE')) { 113 header('X-ob_mode: ' . $this->mode); 114 } 115 register_shutdown_function( 116 [ 117 self::class, 118 'stop', 119 ] 120 ); 121 $this->on = true; 122 } 123 124 /** 125 * This function will need to run at the bottom of all pages if output 126 * buffering is turned on. It also needs to be passed $mode from the 127 * PMA_outBufferModeGet() function or it will be useless. 128 * 129 * @return void 130 */ 131 public static function stop() 132 { 133 $buffer = self::getInstance(); 134 if (! $buffer->on) { 135 return; 136 } 137 138 $buffer->on = false; 139 $buffer->content = ob_get_contents(); 140 if (ob_get_length() <= 0) { 141 return; 142 } 143 144 ob_end_clean(); 145 } 146 147 /** 148 * Gets buffer content 149 * 150 * @return string buffer content 151 */ 152 public function getContents() 153 { 154 return $this->content; 155 } 156 157 /** 158 * Flushes output buffer 159 * 160 * @return void 161 */ 162 public function flush() 163 { 164 if (ob_get_status() && $this->mode) { 165 ob_flush(); 166 } else { 167 flush(); 168 } 169 } 170} 171