'http://foo.bar', 'title'=>'My beautiful foobar', 'content'='Hello, world !','timestamp'=>'1375864834'), * array('uri'=>'http://toto.com', 'title'=>'Welcome to toto', 'content'='What is this website about ?','timestamp'=>'1375868313') * ) * Keys in dictionnaries: * uri (string;mandatory) = The URI the item points to. * title (string;mandatory) = Title of item * content (string;optionnal) = item content (usually HTML code) * timestamp (string;optionnal) = item date. Must be in EPOCH format. * Other keys can be added, but will be ignored. * $items will be used to build the ATOM feed, json and other outputs. */ public $items; private $contentType; // MIME type returned to browser. /** * Sets the content-type returns to browser. * * @param string Content-type returns to browser - Example: $this->setContentType('text/html; charset=UTF-8') * @return this */ private function setContentType($value){ $this->contentType = $value; header('Content-Type: '.$value); return $this; } /** * collectData() will be called to ask the bridge to go collect data on the net. * All derived classes must implement this method. * This method must fill $this->items with collected items. * @param mixed $request : The incoming request (=$_GET). This can be used or ignored by the bridge. */ abstract protected function collectData($request); /** * Returns a HTTP error to user, with a message. * Example: $this->returnError(404, 'no results.'); * @param integer $code * @param string $message */ protected function returnError($code, $message){ $errors = array( 400 => 'Bad Request', 404 => 'Not Found', 501 => 'Not Implemented', ); header('HTTP/1.1 ' . $code . ( isset($errors[$code]) ? ' ' . $errors[$code] : '')); header('Content-Type: text/plain;charset=' . CHARSET); die('ERROR : ' . $message); } /** * Builds an ATOM feed from $this->items and return it to browser. */ private function returnATOM(){ $this->setContentType('application/atom+xml; charset=' . CHARSET); $https = ( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 's' : '' ); $httpHost = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ''; $httpInfo = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : ''; echo ''."\n"; echo ''.htmlspecialchars($this->bridgeName).''."\n"; echo 'http' . $https . '://' . $httpHost . $httpInfo . './'."\n"; echo ''."\n"; // FIXME echo ''."\n"; echo ''."\n"."\n"; foreach($this->items as $item) { echo ''.htmlspecialchars($this->bridgeName).''.htmlspecialchars($this->bridgeURI).''."\n"; echo '<![CDATA['.$item['title'].']]>'."\n"; echo ''."\n"; echo ''.$item['uri'].''."\n"; echo '' . ( isset($item['timestamp']) ? date(DATE_ATOM, $item['timestamp']) : '' ) . ''."\n"; echo '' . ( isset($item['content']) ? '' : '') . ''."\n"; // FIXME: Security: Disable Javascript ? echo ''."\n\n"; } echo ''; } private function returnHTML(){ $this->setContentType('text/html; charset=' . CHARSET); echo ''.htmlspecialchars($this->bridgeName).''; echo ''; echo '

'.htmlspecialchars($this->bridgeName).'

'; foreach($this->items as $item) { echo '

'.htmlspecialchars(strip_tags($item['title'])).'

'; if (isset($item['timestamp'])) { echo ''.date(DATE_ATOM, $item['timestamp']).''; } if (isset($item['content'])) { echo '

'.$item['content'].'

'; } echo "
\n\n"; } echo ''; } /** * Builds a JSON string from $this->items and return it to browser. */ private function returnJSON(){ $this->setContentType('application/json'); echo json_encode($this->items); } /** * Returns $this->items as raw php data. */ private function returnPlaintext(){ $this->setContentType('text/plain;charset=' . CHARSET); print_r($this->items); } /** * Start processing request and return response to browser. */ public function process(){ $this->serveCachedVersion(); // Cache file does not exists or has expired: We re-fetch the results and cache it. $this->collectData($_REQUEST); if (empty($this->items)) { $this->returnError(404, 'no results.'); } $format = isset($_REQUEST['format']) ? $_REQUEST['format'] : 'atom'; switch($format) { case 'plaintext': $this->returnPlaintext(); break; case 'json': $this->returnJSON(); break; case 'html': $this->returnHTML(); break; default: $this->returnATOM(); } $this->storeReponseInCache(); } private function getCacheName(){ if( !isset($_REQUEST) ){ $this->returnError(501, 'WTF ?'); } $stringToEncode = $_SERVER['REQUEST_URI'] . http_build_query($_REQUEST); return CACHEDIR.hash('sha1',$stringToEncode).'.cache'; } /** * Returns the cached version of current request URI directly to the browser * if it exists and if cache has not expired. * Continues execution no cached version available. */ private function serveCachedVersion(){ // See if cache exists for this request $cachefile = $this->getCacheName(); // Cache path and filename if (file_exists($cachefile)) { // The cache file exists. if (time() - ($this->cacheDuration*60) < filemtime($cachefile)) { // Cache file has not expired. Serve it. $data = json_decode(file_get_contents($cachefile),true); header('Content-Type: '.$data['Content-Type']); // Send proper MIME Type header('X-Cached-Version: '.date(DATE_ATOM, filemtime($cachefile))); echo $data['data']; exit(); } } } /** * Stores currently generated page in cache. * @return this */ private function storeReponseInCache(){ $cachefile = $this->getCacheName(); // Cache path and filename $data = array('data'=>ob_get_contents(), 'Content-Type'=>$this->contentType); file_put_contents($cachefile,json_encode($data)); ob_end_flush(); return $this; } }