1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
<?php /** * For buffering accidental output caused by themes and other plugins. * Also used in template rendering. */ class Loco_output_Buffer { /** * The output buffering level opened by this instance * @var int usually 1 unless another buffer was opened before this one. */ private $ob_level;
/** * Content buffered while our buffer was buffering * @var string */ private $output = '';
/** * @return string */ public function __toString(){ return $this->output; }
/** * @return Loco_output_Buffer */ public static function start(){ $buffer = new Loco_output_Buffer; return $buffer->open(); }
/** * @internal * Ensure buffers closed if something terminates before we close gracefully */ public function __destruct(){ $this->close(); }
/** * @return Loco_output_Buffer */ public function open(){ self::check(); if( ! ob_start() ){ throw new Loco_error_Exception('Failed to start output buffering'); } $this->ob_level = ob_get_level(); return $this; }
/** * @return Loco_output_Buffer */ public function close(){ if( is_int($this->ob_level) ){ // collect output from our nested buffers $this->output = self::collect( $this->ob_level ); $this->ob_level = null; } return $this; }
/** * Trash all open buffers, logging any junk output collected * @return void */ public function discard(){ $this->close(); if( '' !== $this->output ){ self::log_junk( $this->output ); $this->output = ''; } }
/** * Collect output buffered to a given level * @param int $min highest buffer to flush, 0 being the root * @return string */ public static function collect( $min ){ $last = 0; $output = ''; while( $level = ob_get_level() ){ // @codeCoverageIgnoreStart if( $level === $last ){ throw new Loco_error_Exception('Failed to close output buffer'); } // @codeCoverageIgnoreEnd if( $level < $min ){ break; } // output is appended inside out: $output = ob_get_clean().$output; $last = $level; } return $output; }
/** * Forcefully destroy all open buffers and log any bytes already buffered. * @return void */ public static function clear(){ $junk = self::collect(0); if( '' !== $junk ){ self::log_junk($junk); } }
/** * Check output has not already been flushed. * @throws Loco_error_Exception */ public static function check(){ if( headers_sent($file,$line) && 'cli' !== PHP_SAPI ){ $file = str_replace( trailingslashit( loco_constant('ABSPATH') ), '', $file ); // translators: (1) is the name of a PHP script, (2) is a line number in the file throw new Loco_error_Exception( sprintf( __('Loco was interrupted by output from %1$s:%2$u','loco-translate'), $file?:'Unknown', $line ) ); } }
/** * Debug collection of junk output * @param string $junk * @return void */ private static function log_junk( $junk ){ $bytes = strlen($junk); $message = sprintf("Cleared %s of buffered output", Loco_mvc_FileParams::renderBytes($bytes) ); Loco_error_AdminNotices::debug( $message ); do_action( 'loco_buffer_cleared', $junk ); }
}
|