1<?php 2 3/** 4 * Simple HTTP Streamer. 5 * 6 * This class manages the stream of a generic content 7 * taking into account the correct http headers settings 8 * 9 * Currently it supports only 4 content (mime) types: 10 * - PDF: application/pdf 11 * - HTML5: text/html 12 * - XML: text/xml 13 * - CSV: text/csv 14 * - Generic file: application/octet-stream 15 * 16 * This Source Code Form is subject to the terms of the Mozilla Public License, 17 * v. 2.0. If a copy of the MPL was not distributed with this file, You can 18 * obtain one at http://mozilla.org/MPL/2.0/. 19 * 20 * @package phpMyFAQ 21 * @author Matteo Scaramuccia <matteo@scaramuccia.com> 22 * @copyright 2005-2020 phpMyFAQ Team 23 * @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 24 * @link https://www.phpmyfaq.de 25 * @since 2005-11-02 26 */ 27 28namespace phpMyFAQ; 29 30/** 31 * Class HttpStreamer 32 * 33 * @package phpMyFAQ 34 */ 35class HttpStreamer 36{ 37 /** 38 * HTTP content disposition attachment constant. 39 * 40 * @var string 41 */ 42 public const HTTP_CONTENT_DISPOSITION_ATTACHMENT = 'attachment'; 43 44 /** 45 * HTTP content disposition inline constant. 46 * 47 * @var string 48 */ 49 public const HTTP_CONTENT_DISPOSITION_INLINE = 'inline'; 50 51 /** 52 * Disposition attachment constant. 53 * 54 * @var string 55 */ 56 public const EXPORT_DISPOSITION_ATTACHMENT = 'attachment'; 57 58 /** 59 * Disposition inline constant. 60 * 61 * @var string 62 */ 63 public const EXPORT_DISPOSITION_INLINE = 'inline'; 64 65 /** 66 * Enable buffer. 67 * 68 * @var bool 69 */ 70 private const EXPORT_BUFFER_ENABLE = true; 71 72 /** 73 * PMF export data type. 74 * 75 * @var string 76 */ 77 private $type; 78 79 /** 80 * HTTP Content Disposition. 81 * 82 * @var string 83 */ 84 private $disposition; 85 86 /** 87 * HTTP streaming data. 88 * 89 * @var string 90 */ 91 private $content; 92 93 /** 94 * HTTP streaming data length. 95 * 96 * @var int 97 */ 98 private $size; 99 100 /** 101 * Constructor. 102 * 103 * @param string $type Type 104 * @param string $content Content 105 */ 106 public function __construct($type, $content) 107 { 108 $this->type = $type; 109 $this->disposition = self::HTTP_CONTENT_DISPOSITION_INLINE; 110 $this->content = $content; 111 $this->size = strlen($this->content); 112 } 113 114 /** 115 * Sends data. 116 * 117 * @param string $disposition Disposition 118 */ 119 public function send($disposition) 120 { 121 if (isset($disposition)) { 122 $this->disposition = $disposition; 123 } 124 125 // Sanity checks 126 if (headers_sent()) { 127 die('<b>PMF_HttpStreamer Class</b> error: unable to send my headers: someone already sent other headers!'); 128 } 129 if (self::EXPORT_BUFFER_ENABLE) { 130 if (ob_get_contents()) { 131 die('<b>PMF_HttpStreamer Class</b> error: unable to send my data: someone already sent other data!'); 132 } 133 } 134 135 // Manage output buffering 136 if (self::EXPORT_BUFFER_ENABLE) { 137 ob_start(); 138 } 139 // Send the right HTTP headers 140 $this->setHttpHeaders(); 141 // Send the raw content 142 $this->streamContent(); 143 // Manage output buffer flushing 144 if (self::EXPORT_BUFFER_ENABLE) { 145 ob_end_flush(); 146 } 147 } 148 149 /** 150 * Sends HTTP Headers. 151 */ 152 private function setHttpHeaders() 153 { 154 // Evaluate data upon export type request 155 switch ($this->type) { 156 case 'pdf': 157 $filename = 'phpmyfaq.pdf'; 158 $description = 'phpMyFaq PDF export file'; 159 $mimeType = 'application/pdf'; 160 break; 161 case 'html5': 162 $filename = 'phpmyfaq.html'; 163 $description = 'phpMyFaq HTML5 export file'; 164 $mimeType = 'text/html'; 165 break; 166 case 'xml': 167 $filename = 'phpmyfaq.xml'; 168 $description = 'phpMyFaq XML export file'; 169 $mimeType = 'text/xml'; 170 break; 171 case 'csv': 172 $filename = 'phpmyfaq.csv'; 173 $description = 'phpMyFaq CSV export file'; 174 $mimeType = 'text/csv'; 175 break; 176 case 'json': 177 $filename = 'phpmyfaq.json'; 178 $description = 'phpMyFaq JSON export file'; 179 $mimeType = 'application/json'; 180 break; 181 // In this case no default statement is required: 182 // the one above is just for clean coding style 183 default: 184 $filename = 'phpmyfaq.pmf'; 185 $description = 'Generic file'; 186 $mimeType = 'application/octet-stream'; 187 break; 188 } 189 190 $filename = Export::getExportTimestamp() . '_' . $filename; 191 192 // Set the correct HTTP headers: 193 // 1. Prevent proxies&browsers caching 194 header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); 195 header('Expires: 0'); 196 header('Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0'); 197 header('Pragma: no-cache'); 198 199 // 2. Set the correct values for file streaming 200 header('Content-Type: ' . $mimeType); 201 if ( 202 ($this->disposition == self::HTTP_CONTENT_DISPOSITION_ATTACHMENT) 203 && isset($_SERVER['HTTP_USER_AGENT']) && !(strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') === false) 204 ) { 205 header('Content-Type: application/force-download'); 206 } 207 // RFC2616, �19.5.1: $filename must be a quoted-string 208 header('Content-Disposition: ' . $this->disposition . '; filename="phpMyFAQ_' . $filename . '"'); 209 if (!empty($description)) { 210 header('Content-Description: ' . $description); 211 } 212 header('Content-Transfer-Encoding: binary'); 213 header('Accept-Ranges: none'); 214 header('Content-Length: ' . $this->size); 215 } 216 217 /** 218 * Streams the content. 219 */ 220 private function streamContent() 221 { 222 echo $this->content; 223 } 224} 225