98 $headers = [], $sendErrors =
true, $optHeaders = [], $flags = 0
100 $headless = ( $flags & self::STREAM_HEADLESS );
103 if ( $headers && headers_sent() ) {
104 echo
"Headers already sent, terminating.\n";
108 $headerFunc = $headless
109 ?
static function (
$header ) {
112 : [ $this,
'header' ];
114 AtEase::suppressWarnings();
115 $info = stat( $this->path );
116 AtEase::restoreWarnings();
118 if ( !is_array( $info ) ) {
120 self::send404Message( $this->path, $flags );
126 $mtimeCT =
new ConvertibleTimestamp( $info[
'mtime'] );
127 $headerFunc(
'Last-Modified: ' . $mtimeCT->getTimestamp( TS_RFC2822 ) );
129 if ( ( $flags & self::STREAM_ALLOW_OB ) == 0 ) {
130 call_user_func( $this->obResetFunc );
133 $type = call_user_func( $this->streamMimeFunc, $this->path );
134 if ( $type && $type !=
'unknown/unknown' ) {
135 $headerFunc(
"Content-type: $type" );
141 $headerFunc(
'Content-type: application/x-wiki' );
145 if ( isset( $optHeaders[
'if-modified-since'] ) ) {
146 $modsince = preg_replace(
'/;.*$/',
'', $optHeaders[
'if-modified-since'] );
147 if ( $mtimeCT->getTimestamp( TS_UNIX ) <= strtotime( $modsince ) ) {
148 ini_set(
'zlib.output_compression', 0 );
155 foreach ( $headers as
$header ) {
159 if ( isset( $optHeaders[
'range'] ) ) {
160 $range = self::parseRange( $optHeaders[
'range'], $info[
'size'] );
161 if ( is_array( $range ) ) {
163 $headerFunc(
'Content-Length: ' . $range[2] );
164 $headerFunc(
"Content-Range: bytes {$range[0]}-{$range[1]}/{$info['size']}" );
165 } elseif ( $range ===
'invalid' ) {
168 $headerFunc(
'Cache-Control: no-cache' );
169 $headerFunc(
'Content-Type: text/html; charset=utf-8' );
170 $headerFunc(
'Content-Range: bytes */' . $info[
'size'] );
175 $headerFunc(
'Content-Length: ' . $info[
'size'] );
179 $headerFunc(
'Content-Length: ' . $info[
'size'] );
182 if ( is_array( $range ) ) {
183 $handle = fopen( $this->path,
'rb' );
186 fseek( $handle, $range[0] );
187 $remaining = $range[2];
188 while ( $remaining > 0 && $ok ) {
189 $bytes = min( $remaining, 8 * 1024 );
190 $data = fread( $handle, $bytes );
191 $remaining -= $bytes;
192 $ok = ( $data !== false );
199 return readfile( $this->path ) !==
false;
238 if ( preg_match(
'#^bytes=(\d*)-(\d*)$#', $range, $m ) ) {
239 [ , $start, $end ] = $m;
240 if ( $start ===
'' && $end ===
'' ) {
241 $absRange = [ 0, $size - 1 ];
242 } elseif ( $start ===
'' ) {
243 $absRange = [ $size - (int)$end, $size - 1 ];
244 } elseif ( $end ===
'' ) {
245 $absRange = [ (int)$start, $size - 1 ];
247 $absRange = [ (int)$start, (
int)$end ];
249 if ( $absRange[0] >= 0 && $absRange[1] >= $absRange[0] ) {
250 if ( $absRange[0] < $size ) {
251 $absRange[1] = min( $absRange[1], $size - 1 );
252 $absRange[2] = $absRange[1] - $absRange[0] + 1;
254 } elseif ( $absRange[0] == 0 && $size == 0 ) {
255 return 'unrecognized';
260 return 'unrecognized';