diff options
-rw-r--r-- | caches/FileCache.php | 153 | ||||
-rw-r--r-- | formats/AtomFormat.php | 113 | ||||
-rw-r--r-- | formats/HtmlFormat.php | 85 | ||||
-rw-r--r-- | formats/JsonFormat.php | 23 | ||||
-rw-r--r-- | formats/MrssFormat.php | 106 | ||||
-rw-r--r-- | formats/PlaintextFormat.php | 24 | ||||
-rw-r--r-- | lib/Bridge.php | 194 | ||||
-rw-r--r-- | lib/BridgeAbstract.php | 881 | ||||
-rw-r--r-- | lib/BridgeInterface.php | 8 | ||||
-rw-r--r-- | lib/Cache.php | 134 | ||||
-rw-r--r-- | lib/CacheAbstract.php | 12 | ||||
-rw-r--r-- | lib/CacheInterface.php | 8 | ||||
-rw-r--r-- | lib/Exceptions.php | 97 | ||||
-rw-r--r-- | lib/FeedExpander.php | 345 | ||||
-rw-r--r-- | lib/Format.php | 103 | ||||
-rw-r--r-- | lib/FormatAbstract.php | 208 | ||||
-rw-r--r-- | lib/FormatInterface.php | 6 | ||||
-rw-r--r-- | lib/HTMLUtils.php | 228 | ||||
-rw-r--r-- | lib/RssBridge.php | 43 |
19 files changed, 1443 insertions, 1328 deletions
diff --git a/caches/FileCache.php b/caches/FileCache.php index b6e04c37..a8ab88d7 100644 --- a/caches/FileCache.php +++ b/caches/FileCache.php @@ -2,93 +2,88 @@ /** * Cache with file system */ -class FileCache extends CacheAbstract{ - protected $cacheDirCreated; // boolean to avoid always chck dir cache existance +class FileCache extends CacheAbstract { + protected $cacheDirCreated; // boolean to avoid always chck dir cache existance - public function loadData(){ - $this->isPrepareCache(); + public function loadData(){ + $this->isPrepareCache(); + $datas = unserialize(file_get_contents($this->getCacheFile())); + return $datas; + } - $datas = unserialize(file_get_contents($this->getCacheFile())); + public function saveData($datas){ + $this->isPrepareCache(); - return $datas; - } + //Re-encode datas to UTF-8 + //$datas = Cache::utf8_encode_deep($datas); + $writeStream = file_put_contents($this->getCacheFile(), serialize($datas)); - public function saveData($datas){ - $this->isPrepareCache(); + if(!$writeStream) { + throw new \Exception("Cannot write the cache... Do you have the right permissions ?"); + } - //Re-encode datas to UTF-8 - //$datas = Cache::utf8_encode_deep($datas); - - $writeStream = file_put_contents($this->getCacheFile(), serialize($datas)); + return $this; + } - if(!$writeStream) { + public function getTime(){ + $this->isPrepareCache(); - throw new \Exception("Cannot write the cache... Do you have the right permissions ?"); + $cacheFile = $this->getCacheFile(); + if(file_exists($cacheFile)){ + return filemtime($cacheFile); + } + + return false; + } + + /** + * Cache is prepared ? + * Note : Cache name is based on request information, then cache must be prepare before use + * @return \Exception|true + */ + protected function isPrepareCache(){ + if(is_null($this->param)){ + throw new \Exception('Please feed "prepare" method before try to load'); + } + + return true; + } + + /** + * Return cache path (and create if not exist) + * @return string Cache path + */ + protected function getCachePath(){ + $cacheDir = __DIR__ . '/../cache/'; // FIXME : configuration ? + + // FIXME : implement recursive dir creation + if(is_null($this->cacheDirCreated) && !is_dir($cacheDir)){ + $this->cacheDirCreated = true; + mkdir($cacheDir,0705); + chmod($cacheDir,0705); } - return $this; - } - - public function getTime(){ - $this->isPrepareCache(); - - $cacheFile = $this->getCacheFile(); - if( file_exists($cacheFile) ){ - return filemtime($cacheFile); - } - - return false; - } - - /** - * Cache is prepared ? - * Note : Cache name is based on request information, then cache must be prepare before use - * @return \Exception|true - */ - protected function isPrepareCache(){ - if( is_null($this->param) ){ - throw new \Exception('Please feed "prepare" method before try to load'); - } - - return true; - } - - /** - * Return cache path (and create if not exist) - * @return string Cache path - */ - protected function getCachePath(){ - $cacheDir = __DIR__ . '/../cache/'; // FIXME : configuration ? - - // FIXME : implement recursive dir creation - if( is_null($this->cacheDirCreated) && !is_dir($cacheDir) ){ - $this->cacheDirCreated = true; - - mkdir($cacheDir,0705); - chmod($cacheDir,0705); - } - - return $cacheDir; - } - - /** - * Get the file name use for cache store - * @return string Path to the file cache - */ - protected function getCacheFile(){ - return $this->getCachePath() . $this->getCacheName(); - } - - /** - * Determines file name for store the cache - * return string - */ - protected function getCacheName(){ - $this->isPrepareCache(); - - $stringToEncode = $_SERVER['REQUEST_URI'] . http_build_query($this->param); - $stringToEncode = preg_replace('/(\?|&)format=[^&]*/i', '$1', $stringToEncode); - return hash('sha1', $stringToEncode) . '.cache'; - } + return $cacheDir; + } + + /** + * Get the file name use for cache store + * @return string Path to the file cache + */ + protected function getCacheFile(){ + return $this->getCachePath() . $this->getCacheName(); + } + + /** + * Determines file name for store the cache + * return string + */ + protected function getCacheName(){ + $this->isPrepareCache(); + + $stringToEncode = $_SERVER['REQUEST_URI'] . http_build_query($this->param); + $stringToEncode = preg_replace('/(\?|&)format=[^&]*/i', '$1', $stringToEncode); + return hash('sha1', $stringToEncode) . '.cache'; + } } diff --git a/formats/AtomFormat.php b/formats/AtomFormat.php index 238dc884..fd2016bc 100644 --- a/formats/AtomFormat.php +++ b/formats/AtomFormat.php @@ -1,79 +1,80 @@ <?php /** * Atom -* Documentation Source http://en.wikipedia.org/wiki/Atom_%28standard%29 and http://tools.ietf.org/html/rfc4287 +* Documentation Source http://en.wikipedia.org/wiki/Atom_%28standard%29 and +* http://tools.ietf.org/html/rfc4287 */ class AtomFormat extends FormatAbstract{ - public function stringify(){ - $https = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 's' : ''; - $httpHost = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ''; - $httpInfo = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : ''; + public function stringify(){ + $https = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 's' : ''; + $httpHost = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ''; + $httpInfo = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : ''; - $serverRequestUri = $this->xml_encode($_SERVER['REQUEST_URI']); + $serverRequestUri = $this->xml_encode($_SERVER['REQUEST_URI']); - $extraInfos = $this->getExtraInfos(); - $title = $this->xml_encode($extraInfos['name']); - $uri = !empty($extraInfos['uri']) ? $extraInfos['uri'] : 'https://github.com/sebsauvage/rss-bridge'; - $icon = $this->xml_encode('http://icons.better-idea.org/icon?url='. $uri .'&size=64'); - $uri = $this->xml_encode($uri); + $extraInfos = $this->getExtraInfos(); + $title = $this->xml_encode($extraInfos['name']); + $uri = !empty($extraInfos['uri']) ? $extraInfos['uri'] : 'https://github.com/sebsauvage/rss-bridge'; + $icon = $this->xml_encode('http://icons.better-idea.org/icon?url='. $uri .'&size=64'); + $uri = $this->xml_encode($uri); - $entries = ''; - foreach($this->getItems() as $item){ - $entryAuthor = isset($item['author']) ? $this->xml_encode($item['author']) : ''; - $entryTitle = isset($item['title']) ? $this->xml_encode($item['title']) : ''; - $entryUri = isset($item['uri']) ? $this->xml_encode($item['uri']) : ''; - $entryTimestamp = isset($item['timestamp']) ? $this->xml_encode(date(DATE_ATOM, $item['timestamp'])) : ''; - $entryContent = isset($item['content']) ? $this->xml_encode($this->sanitizeHtml($item['content'])) : ''; - $entries .= <<<EOD + $entries = ''; + foreach($this->getItems() as $item){ + $entryAuthor = isset($item['author']) ? $this->xml_encode($item['author']) : ''; + $entryTitle = isset($item['title']) ? $this->xml_encode($item['title']) : ''; + $entryUri = isset($item['uri']) ? $this->xml_encode($item['uri']) : ''; + $entryTimestamp = isset($item['timestamp']) ? $this->xml_encode(date(DATE_ATOM, $item['timestamp'])) : ''; + $entryContent = isset($item['content']) ? $this->xml_encode($this->sanitizeHtml($item['content'])) : ''; + $entries .= <<<EOD - <entry> - <author> - <name>{$entryAuthor}</name> - </author> - <title type="html"><![CDATA[{$entryTitle}]]></title> - <link rel="alternate" type="text/html" href="{$entryUri}" /> - <id>{$entryUri}</id> - <updated>{$entryTimestamp}</updated> - <content type="html">{$entryContent}</content> - </entry> + <entry> + <author> + <name>{$entryAuthor}</name> + </author> + <title type="html"><![CDATA[{$entryTitle}]]></title> + <link rel="alternate" type="text/html" href="{$entryUri}" /> + <id>{$entryUri}</id> + <updated>{$entryTimestamp}</updated> + <content type="html">{$entryContent}</content> + </entry> EOD; - } + } - $feedTimestamp = date(DATE_ATOM, time()); + $feedTimestamp = date(DATE_ATOM, time()); - /* Data are prepared, now let's begin the "MAGIE !!!" */ - $toReturn = '<?xml version="1.0" encoding="UTF-8"?>'; - $toReturn .= <<<EOD + /* Data are prepared, now let's begin the "MAGIE !!!" */ + $toReturn = '<?xml version="1.0" encoding="UTF-8"?>'; + $toReturn .= <<<EOD <feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xml:lang="en-US"> - <title type="text">{$title}</title> - <id>http{$https}://{$httpHost}{$httpInfo}/</id> - <icon>{$icon}</icon> - <logo>{$icon}</logo> - <updated>{$feedTimestamp}</updated> - <link rel="alternate" type="text/html" href="{$uri}" /> - <link rel="self" href="http{$https}://{$httpHost}{$serverRequestUri}" /> + <title type="text">{$title}</title> + <id>http{$https}://{$httpHost}{$httpInfo}/</id> + <icon>{$icon}</icon> + <logo>{$icon}</logo> + <updated>{$feedTimestamp}</updated> + <link rel="alternate" type="text/html" href="{$uri}" /> + <link rel="self" href="http{$https}://{$httpHost}{$serverRequestUri}" /> {$entries} </feed> EOD; - - // Remove invalid non-UTF8 characters - ini_set('mbstring.substitute_character', 'none'); - $toReturn= mb_convert_encoding($toReturn, 'UTF-8', 'UTF-8'); - return $toReturn; - } - public function display(){ - $this - ->setContentType('application/atom+xml; charset=UTF-8') - ->callContentType(); + // Remove invalid non-UTF8 characters + ini_set('mbstring.substitute_character', 'none'); + $toReturn = mb_convert_encoding($toReturn, 'UTF-8', 'UTF-8'); + return $toReturn; + } - return parent::display(); - } + public function display(){ + $this + ->setContentType('application/atom+xml; charset=UTF-8') + ->callContentType(); - private function xml_encode($text) { - return htmlspecialchars($text, ENT_XML1); - } + return parent::display(); + } + + private function xml_encode($text){ + return htmlspecialchars($text, ENT_XML1); + } } diff --git a/formats/HtmlFormat.php b/formats/HtmlFormat.php index d7c927b0..1001acfc 100644 --- a/formats/HtmlFormat.php +++ b/formats/HtmlFormat.php @@ -1,63 +1,62 @@ <?php -class HtmlFormat extends FormatAbstract{ - - public function stringify(){ - $extraInfos = $this->getExtraInfos(); - $title = htmlspecialchars($extraInfos['name']); - $uri = htmlspecialchars($extraInfos['uri']); - $atomquery = str_replace('format=Html', 'format=Atom', htmlentities($_SERVER['QUERY_STRING'])); - $mrssquery = str_replace('format=Html', 'format=Mrss', htmlentities($_SERVER['QUERY_STRING'])); - - $entries = ''; - foreach($this->getItems() as $item){ - $entryAuthor = isset($item['author']) ? '<br /><p class="author">by: ' . $item['author'] . '</p>' : ''; - $entryTitle = isset($item['title']) ? $this->sanitizeHtml(strip_tags($item['title'])) : ''; - $entryUri = isset($item['uri']) ? $item['uri'] : $uri; - $entryTimestamp = isset($item['timestamp']) ? '<time datetime="' . date(DATE_ATOM, $item['timestamp']) . '">' . date(DATE_ATOM, $item['timestamp']) . '</time>' : ''; - $entryContent = isset($item['content']) ? '<div class="content">' . $this->sanitizeHtml($item['content']). '</div>' : ''; - $entries .= <<<EOD +class HtmlFormat extends FormatAbstract { + + public function stringify(){ + $extraInfos = $this->getExtraInfos(); + $title = htmlspecialchars($extraInfos['name']); + $uri = htmlspecialchars($extraInfos['uri']); + $atomquery = str_replace('format=Html', 'format=Atom', htmlentities($_SERVER['QUERY_STRING'])); + $mrssquery = str_replace('format=Html', 'format=Mrss', htmlentities($_SERVER['QUERY_STRING'])); + + $entries = ''; + foreach($this->getItems() as $item){ + $entryAuthor = isset($item['author']) ? '<br /><p class="author">by: ' . $item['author'] . '</p>' : ''; + $entryTitle = isset($item['title']) ? $this->sanitizeHtml(strip_tags($item['title'])) : ''; + $entryUri = isset($item['uri']) ? $item['uri'] : $uri; + $entryTimestamp = isset($item['timestamp']) ? '<time datetime="' . date(DATE_ATOM, $item['timestamp']) . '">' . date(DATE_ATOM, $item['timestamp']) . '</time>' : ''; + $entryContent = isset($item['content']) ? '<div class="content">' . $this->sanitizeHtml($item['content']). '</div>' : ''; + $entries .= <<<EOD <section class="feeditem"> - <h2><a class="itemtitle" href="{$entryUri}">{$entryTitle}</a></h2> - {$entryTimestamp} - {$entryAuthor} - {$entryContent} + <h2><a class="itemtitle" href="{$entryUri}">{$entryTitle}</a></h2> + {$entryTimestamp} + {$entryAuthor} + {$entryContent} </section> EOD; - } + } - - /* Data are prepared, now let's begin the "MAGIE !!!" */ - $toReturn = <<<EOD + /* Data are prepared, now let's begin the "MAGIE !!!" */ + $toReturn = <<<EOD <!DOCTYPE html> <html> <head> - <meta charset="UTF-8"> - <title>{$title}</title> - <link href="css/HtmlFormat.css" rel="stylesheet"> - <meta name="robots" content="noindex, follow"> + <meta charset="UTF-8"> + <title>{$title}</title> + <link href="css/HtmlFormat.css" rel="stylesheet"> + <meta name="robots" content="noindex, follow"> </head> <body> - <h1 class="pagetitle"><a href="{$uri}" target="_blank">{$title}</a></h1> - <div class="buttons"> - <a href="./#bridge-{$_GET['bridge']}"><button class="backbutton">← back to rss-bridge</button></a> - <a href="./?{$atomquery}"><button class="rss-feed">RSS feed (ATOM)</button></a> - <a href="./?{$mrssquery}"><button class="rss-feed">RSS feed (MRSS)</button></a> - </div> + <h1 class="pagetitle"><a href="{$uri}" target="_blank">{$title}</a></h1> + <div class="buttons"> + <a href="./#bridge-{$_GET['bridge']}"><button class="backbutton">← back to rss-bridge</button></a> + <a href="./?{$atomquery}"><button class="rss-feed">RSS feed (ATOM)</button></a> + <a href="./?{$mrssquery}"><button class="rss-feed">RSS feed (MRSS)</button></a> + </div> {$entries} </body> </html> EOD; - return $toReturn; - } + return $toReturn; + } - public function display() { - $this - ->setContentType('text/html; charset=' . $this->getCharset()) - ->callContentType(); + public function display() { + $this + ->setContentType('text/html; charset=' . $this->getCharset()) + ->callContentType(); - return parent::display(); - } + return parent::display(); + } } diff --git a/formats/JsonFormat.php b/formats/JsonFormat.php index e173f230..ac6e450a 100644 --- a/formats/JsonFormat.php +++ b/formats/JsonFormat.php @@ -3,19 +3,18 @@ * Json * Builds a JSON string from $this->items and return it to browser. */ -class JsonFormat extends FormatAbstract{ +class JsonFormat extends FormatAbstract { - public function stringify(){ - $items = $this->getItems(); + public function stringify(){ + $items = $this->getItems(); + return json_encode($items, JSON_PRETTY_PRINT); + } - return json_encode($items, JSON_PRETTY_PRINT); - } + public function display(){ + $this + ->setContentType('application/json') + ->callContentType(); - public function display(){ - $this - ->setContentType('application/json') - ->callContentType(); - - return parent::display(); - } + return parent::display(); + } } diff --git a/formats/MrssFormat.php b/formats/MrssFormat.php index ddbd5d35..fddbf0ac 100644 --- a/formats/MrssFormat.php +++ b/formats/MrssFormat.php @@ -3,72 +3,72 @@ * Mrss * Documentation Source http://www.rssboard.org/media-rss */ -class MrssFormat extends FormatAbstract{ +class MrssFormat extends FormatAbstract { - public function stringify(){ - $https = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 's' : ''; - $httpHost = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ''; - $httpInfo = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : ''; + public function stringify(){ + $https = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 's' : ''; + $httpHost = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ''; + $httpInfo = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : ''; - $serverRequestUri = $this->xml_encode($_SERVER['REQUEST_URI']); + $serverRequestUri = $this->xml_encode($_SERVER['REQUEST_URI']); - $extraInfos = $this->getExtraInfos(); - $title = $this->xml_encode($extraInfos['name']); - $uri = $this->xml_encode(!empty($extraInfos['uri']) ? $extraInfos['uri'] : 'https://github.com/sebsauvage/rss-bridge'); - $icon = $this->xml_encode('http://icons.better-idea.org/icon?url='. $uri .'&size=64'); + $extraInfos = $this->getExtraInfos(); + $title = $this->xml_encode($extraInfos['name']); + $uri = $this->xml_encode(!empty($extraInfos['uri']) ? $extraInfos['uri'] : 'https://github.com/sebsauvage/rss-bridge'); + $icon = $this->xml_encode('http://icons.better-idea.org/icon?url='. $uri .'&size=64'); - $items = ''; - foreach($this->getItems() as $item){ - $itemAuthor = isset($item['author']) ? $this->xml_encode($item['author']) : ''; - $itemTitle = strip_tags(isset($item['title']) ? $this->xml_encode($item['title']) : ''); - $itemUri = isset($item['uri']) ? $this->xml_encode($item['uri']) : ''; - $itemTimestamp = isset($item['timestamp']) ? $this->xml_encode(date(DATE_RFC2822, $item['timestamp'])) : ''; - $itemContent = isset($item['content']) ? $this->xml_encode($this->sanitizeHtml($item['content'])) : ''; - $items .= <<<EOD + $items = ''; + foreach($this->getItems() as $item){ + $itemAuthor = isset($item['author']) ? $this->xml_encode($item['author']) : ''; + $itemTitle = strip_tags(isset($item['title']) ? $this->xml_encode($item['title']) : ''); + $itemUri = isset($item['uri']) ? $this->xml_encode($item['uri']) : ''; + $itemTimestamp = isset($item['timestamp']) ? $this->xml_encode(date(DATE_RFC2822, $item['timestamp'])) : ''; + $itemContent = isset($item['content']) ? $this->xml_encode($this->sanitizeHtml($item['content'])) : ''; + $items .= <<<EOD - <item> - <title>{$itemTitle}</title> - <link>{$itemUri}</link> - <guid isPermaLink="true">{$itemUri}</guid> - <pubDate>{$itemTimestamp}</pubDate> - <description>{$itemContent}</description> - <author>{$itemAuthor}</author> - </item> + <item> + <title>{$itemTitle}</title> + <link>{$itemUri}</link> + <guid isPermaLink="true">{$itemUri}</guid> + <pubDate>{$itemTimestamp}</pubDate> + <description>{$itemContent}</description> + <author>{$itemAuthor}</author> + </item> EOD; - } + } - /* Data are prepared, now let's begin the "MAGIE !!!" */ - $toReturn = '<?xml version="1.0" encoding="UTF-8"?>'; - $toReturn .= <<<EOD + /* Data are prepared, now let's begin the "MAGIE !!!" */ + $toReturn = '<?xml version="1.0" encoding="UTF-8"?>'; + $toReturn .= <<<EOD <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:atom="http://www.w3.org/2005/Atom"> - <channel> - <title>{$title}</title> - <link>http{$https}://{$httpHost}{$httpInfo}/</link> - <description>{$title}</description> - <image url="{$icon}" title="{$title}" link="{$uri}"/> - <atom:link rel="alternate" type="text/html" href="{$uri}" /> - <atom:link rel="self" href="http{$https}://{$httpHost}{$serverRequestUri}" /> - {$items} - </channel> + <channel> + <title>{$title}</title> + <link>http{$https}://{$httpHost}{$httpInfo}/</link> + <description>{$title}</description> + <image url="{$icon}" title="{$title}" link="{$uri}"/> + <atom:link rel="alternate" type="text/html" href="{$uri}" /> + <atom:link rel="self" href="http{$https}://{$httpHost}{$serverRequestUri}" /> + {$items} + </channel> </rss> EOD; - // Remove invalid non-UTF8 characters - ini_set('mbstring.substitute_character', 'none'); - $toReturn= mb_convert_encoding($toReturn, 'UTF-8', 'UTF-8'); - return $toReturn; - } + // Remove invalid non-UTF8 characters + ini_set('mbstring.substitute_character', 'none'); + $toReturn = mb_convert_encoding($toReturn, 'UTF-8', 'UTF-8'); + return $toReturn; + } - public function display(){ - $this - ->setContentType('application/rss+xml; charset=UTF-8') - ->callContentType(); + public function display(){ + $this + ->setContentType('application/rss+xml; charset=UTF-8') + ->callContentType(); - return parent::display(); - } + return parent::display(); + } - private function xml_encode($text) { - return htmlspecialchars($text, ENT_XML1); - } + private function xml_encode($text){ + return htmlspecialchars($text, ENT_XML1); + } } diff --git a/formats/PlaintextFormat.php b/formats/PlaintextFormat.php index e2cf0b94..593e938d 100644 --- a/formats/PlaintextFormat.php +++ b/formats/PlaintextFormat.php @@ -3,18 +3,18 @@ * Plaintext * Returns $this->items as raw php data. */ -class PlaintextFormat extends FormatAbstract{ +class PlaintextFormat extends FormatAbstract { - public function stringify(){ - $items = $this->getItems(); - return print_r($items, true); - } + public function stringify(){ + $items = $this->getItems(); + return print_r($items, true); + } - public function display(){ - $this - ->setContentType('text/plain;charset=' . $this->getCharset()) - ->callContentType(); + public function display(){ + $this + ->setContentType('text/plain;charset=' . $this->getCharset()) + ->callContentType(); - return parent::display(); - } -}
\ No newline at end of file + return parent::display(); + } +} diff --git a/lib/Bridge.php b/lib/Bridge.php index bacb77a0..a9645810 100644 --- a/lib/Bridge.php +++ b/lib/Bridge.php @@ -2,103 +2,105 @@ require_once(__DIR__ . '/BridgeInterface.php'); class Bridge { - static protected $dirBridge; - - public function __construct(){ - throw new \LogicException('Please use ' . __CLASS__ . '::create for new object.'); - } - - /** - * Checks if a bridge is an instantiable bridge. - * @param string $nameBridge name of the bridge that you want to use - * @return true if it is an instantiable bridge, false otherwise. - */ - static public function isInstantiable($nameBridge){ - $re = new ReflectionClass($nameBridge); - return $re->IsInstantiable(); - } - - /** - * Create a new bridge object - * @param string $nameBridge Defined bridge name you want use - * @return Bridge object dedicated - */ - static public function create($nameBridge){ - if(!preg_match('@^[A-Z][a-zA-Z0-9-]*$@', $nameBridge)){ - $message = <<<EOD + static protected $dirBridge; + + public function __construct(){ + throw new \LogicException('Please use ' . __CLASS__ . '::create for new object.'); + } + + /** + * Checks if a bridge is an instantiable bridge. + * @param string $nameBridge name of the bridge that you want to use + * @return true if it is an instantiable bridge, false otherwise. + */ + static public function isInstantiable($nameBridge){ + $re = new ReflectionClass($nameBridge); + return $re->IsInstantiable(); + } + + /** + * Create a new bridge object + * @param string $nameBridge Defined bridge name you want use + * @return Bridge object dedicated + */ + static public function create($nameBridge){ + if(!preg_match('@^[A-Z][a-zA-Z0-9-]*$@', $nameBridge)){ + $message = <<<EOD 'nameBridge' must start with one uppercase character followed or not by alphanumeric or dash characters! EOD; - throw new \InvalidArgumentException($message); - } - - $nameBridge = $nameBridge . 'Bridge'; - $pathBridge = self::getDir() . $nameBridge . '.php'; - - if(!file_exists($pathBridge)){ - throw new \Exception('The bridge you looking for does not exist. It should be at path ' . $pathBridge); - } - - require_once $pathBridge; - - if(Bridge::isInstantiable($nameBridge)){ - return new $nameBridge(); - } else { - return false; - } - } - - static public function setDir($dirBridge){ - if(!is_string($dirBridge)){ - throw new \InvalidArgumentException('Dir bridge must be a string.'); - } - - if(!file_exists($dirBridge)){ - throw new \Exception('Dir bridge does not exist.'); - } - - self::$dirBridge = $dirBridge; - } - - static public function getDir(){ - $dirBridge = self::$dirBridge; - - if(is_null($dirBridge)){ - throw new \LogicException(__CLASS__ . ' class need to know bridge path !'); - } - - return $dirBridge; - } - - /** - * Lists the available bridges. - * @return array List of the bridges - */ - static public function listBridges(){ - $pathDirBridge = self::getDir(); - $listBridge = array(); - $dirFiles = scandir($pathDirBridge); - - if($dirFiles !== false){ - foreach($dirFiles as $fileName){ - if(preg_match('@^([^.]+)Bridge\.php$@U', $fileName, $out)){ - $listBridge[] = $out[1]; - } - } - } - - return $listBridge; - } - - static public function isWhitelisted($whitelist, $name){ - if(in_array($name, $whitelist) - or in_array($name . '.php', $whitelist) - or in_array($name . 'Bridge', $whitelist) // DEPRECATED - or in_array($name . 'Bridge.php', $whitelist) // DEPRECATED - or count($whitelist) === 1 and trim($whitelist[0]) === '*'){ - return true; - } else { - return false; - } - } + throw new \InvalidArgumentException($message); + } + + $nameBridge = $nameBridge . 'Bridge'; + $pathBridge = self::getDir() . $nameBridge . '.php'; + + if(!file_exists($pathBridge)){ + throw new \Exception('The bridge you looking for does not exist.' + . ' It should be at path ' + . $pathBridge); + } + + require_once $pathBridge; + + if(Bridge::isInstantiable($nameBridge)){ + return new $nameBridge(); + } else { + return false; + } + } + + static public function setDir($dirBridge){ + if(!is_string($dirBridge)){ + throw new \InvalidArgumentException('Dir bridge must be a string.'); + } + + if(!file_exists($dirBridge)){ + throw new \Exception('Dir bridge does not exist.'); + } + + self::$dirBridge = $dirBridge; + } + + static public function getDir(){ + $dirBridge = self::$dirBridge; + + if(is_null($dirBridge)){ + throw new \LogicException(__CLASS__ . ' class need to know bridge path !'); + } + + return $dirBridge; + } + + /** + * Lists the available bridges. + * @return array List of the bridges + */ + static public function listBridges(){ + $pathDirBridge = self::getDir(); + $listBridge = array(); + $dirFiles = scandir($pathDirBridge); + + if($dirFiles !== false){ + foreach($dirFiles as $fileName){ + if(preg_match('@^([^.]+)Bridge\.php$@U', $fileName, $out)){ + $listBridge[] = $out[1]; + } + } + } + + return $listBridge; + } + + static public function isWhitelisted($whitelist, $name){ + if(in_array($name, $whitelist) + || in_array($name . '.php', $whitelist) + || in_array($name . 'Bridge', $whitelist) // DEPRECATED + || in_array($name . 'Bridge.php', $whitelist) // DEPRECATED + || count($whitelist) === 1 and trim($whitelist[0]) === '*'){ + return true; + } else { + return false; + } + } } diff --git a/lib/BridgeAbstract.php b/lib/BridgeAbstract.php index abcda79d..d17c6bd2 100644 --- a/lib/BridgeAbstract.php +++ b/lib/BridgeAbstract.php @@ -2,444 +2,445 @@ require_once(__DIR__ . '/BridgeInterface.php'); abstract class BridgeAbstract implements BridgeInterface { - const NAME = 'Unnamed bridge'; - const URI = ''; - const DESCRIPTION = 'No description provided'; - const MAINTAINER = 'No maintainer'; - const PARAMETERS = array(); - - public $useProxy = true; - - protected $cache; - protected $items = array(); - protected $inputs = array(); - protected $queriedContext = ''; - - protected function returnError($message, $code){ - throw new \HttpException($message, $code); - } - - protected function returnClientError($message){ - $this->returnError($message, 400); - } - - protected function returnServerError($message){ - $this->returnError($message, 500); - } - - /** - * Return items stored in the bridge - * @return mixed - */ - public function getItems(){ - return $this->items; - } - - protected function validateTextValue($value, $pattern = null){ - if(!is_null($pattern)){ - $filteredValue = filter_var($value, FILTER_VALIDATE_REGEXP, - array('options' => array( - 'regexp' => '/^' . $pattern . '$/' - )) - ); - } else { - $filteredValue = filter_var($value); - } - - if($filteredValue === false) - return null; - - return $filteredValue; - } - - protected function validateNumberValue($value){ - $filteredValue = filter_var($value, FILTER_VALIDATE_INT); - - if($filteredValue === false && !empty($value)) - return null; - - return $filteredValue; - } - - protected function validateCheckboxValue($value){ - $filteredValue = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); - - if(is_null($filteredValue)) - return null; - - return $filteredValue; - } - - protected function validateListValue($value, $expectedValues){ - $filteredValue = filter_var($value); - - if($filteredValue === false) - return null; - - if(!in_array($filteredValue, $expectedValues)){ // Check sub-values? - foreach($expectedValues as $subName => $subValue){ - if(is_array($subValue) && in_array($filteredValue, $subValue)) - return $filteredValue; - } - return null; - } - - return $filteredValue; - } - - protected function validateData(&$data){ - if(!is_array($data)) - return false; - - foreach($data as $name=>$value){ - $registered = false; - foreach(static::PARAMETERS as $context=>$set){ - if(array_key_exists($name,$set)){ - $registered = true; - if(!isset($set[$name]['type'])){ - $set[$name]['type']='text'; - } - - switch($set[$name]['type']){ - case 'number': - $data[$name] = $this->validateNumberValue($value); - break; - case 'checkbox': - $data[$name] = $this->validateCheckboxValue($value); - break; - case 'list': - $data[$name] = $this->validateListValue($value, $set[$name]['values']); - break; - default: - case 'text': - if(isset($set[$name]['pattern'])){ - $data[$name] = $this->validateTextValue($value, $set[$name]['pattern']); - } else { - $data[$name] = $this->validateTextValue($value); - } - break; - } - - if(is_null($data[$name])){ - echo 'Parameter \'' . $name . '\' is invalid!' . PHP_EOL; - return false; - } - } - } - - if(!$registered) - return false; - } - - return true; - } - - protected function setInputs(array $inputs, $queriedContext){ - // Import and assign all inputs to their context - foreach($inputs as $name => $value){ - foreach(static::PARAMETERS as $context => $set){ - if(array_key_exists($name, static::PARAMETERS[$context])){ - $this->inputs[$context][$name]['value'] = $value; - } - } - } - - // Apply default values to missing data - $contexts = array($queriedContext); - if(array_key_exists('global', static::PARAMETERS)){ - $contexts[] = 'global'; - } - - foreach($contexts as $context){ - foreach(static::PARAMETERS[$context] as $name => $properties){ - if(isset($this->inputs[$context][$name]['value'])){ - continue; - } - - $type = isset($properties['type']) ? $properties['type'] : 'text'; - - switch($type){ - case 'checkbox': - if(!isset($properties['defaultValue'])){ - $this->inputs[$context][$name]['value'] = false; - } else { - $this->inputs[$context][$name]['value'] = $properties['defaultValue']; - } - break; - case 'list': - if(!isset($properties['defaultValue'])){ - $firstItem = reset($properties['values']); - if(is_array($firstItem)){ - $firstItem = reset($firstItem); - } - $this->inputs[$context][$name]['value'] = $firstItem; - } else { - $this->inputs[$context][$name]['value'] = $properties['defaultValue']; - } - break; - default: - if(isset($properties['defaultValue'])){ - $this->inputs[$context][$name]['value'] = $properties['defaultValue']; - } - break; - } - } - } - - // Copy global parameter values to the guessed context - if(array_key_exists('global', static::PARAMETERS)){ - foreach(static::PARAMETERS['global'] as $name => $properties){ - if(isset($inputs[$name])){ - $value = $inputs[$name]; - } elseif (isset($properties['value'])){ - $value = $properties['value']; - } else { - continue; - } - $this->inputs[$queriedContext][$name]['value'] = $value; - } - } - - // Only keep guessed context parameters values - if(isset($this->inputs[$queriedContext])){ - $this->inputs = array($queriedContext => $this->inputs[$queriedContext]); - } else { - $this->inputs = array(); - } - } - - protected function getQueriedContext(array $inputs){ - $queriedContexts=array(); - foreach(static::PARAMETERS as $context=>$set){ - $queriedContexts[$context]=null; - foreach($set as $id=>$properties){ - if(isset($inputs[$id]) && !empty($inputs[$id])){ - $queriedContexts[$context]=true; - }elseif(isset($properties['required']) && - $properties['required']===true){ - $queriedContexts[$context]=false; - break; - } - } - } - - if(array_key_exists('global',static::PARAMETERS) && - $queriedContexts['global']===false){ - return null; - } - unset($queriedContexts['global']); - - switch(array_sum($queriedContexts)){ - case 0: - foreach($queriedContexts as $context=>$queried){ - if (is_null($queried)){ - return $context; - } - } - return null; - case 1: return array_search(true,$queriedContexts); - default: return false; - } - } - - /** - * Defined datas with parameters depending choose bridge - * Note : you can define a cache with "setCache" - * @param array array with expected bridge paramters - */ - public function setDatas(array $inputs){ - if(!is_null($this->cache)){ - $this->cache->prepare($inputs); - $time = $this->cache->getTime(); - if($time !== false && (time() - $this->getCacheDuration() < $time)){ - $this->items = $this->cache->loadData(); - return; - } - } - - if(empty(static::PARAMETERS)){ - if(!empty($inputs)){ - $this->returnClientError('Invalid parameters value(s)'); - } - - $this->collectData(); - if(!is_null($this->cache)){ - $this->cache->saveData($this->getItems()); - } - return; - } - - if(!$this->validateData($inputs)){ - $this->returnClientError('Invalid parameters value(s)'); - } - - // Guess the paramter context from input data - $this->queriedContext = $this->getQueriedContext($inputs); - if(is_null($this->queriedContext)){ - $this->returnClientError('Required parameter(s) missing'); - } elseif($this->queriedContext === false){ - $this->returnClientError('Mixed context parameters'); - } - - $this->setInputs($inputs, $this->queriedContext); - - $this->collectData(); - - if(!is_null($this->cache)){ - $this->cache->saveData($this->getItems()); - } - } - - function getInput($input){ - if(!isset($this->inputs[$this->queriedContext][$input]['value'])){ - return null; - } - return $this->inputs[$this->queriedContext][$input]['value']; - } - - public function getName(){ - return static::NAME; - } - - public function getURI(){ - return static::URI; - } - - public function getCacheDuration(){ - return 3600; - } - - public function setCache(\CacheAbstract $cache){ - $this->cache = $cache; - } - - public function debugMessage($text){ - if(!file_exists('DEBUG')) { - return; - } - - $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3); - $calling = $backtrace[2]; - $message = $calling['file'] . ':' - . $calling['line'] . ' class ' - . get_class($this) . '->' - . $calling['function'] . ' - ' - . $text; - - error_log($message); - } - - protected function getContents($url - , $use_include_path = false - , $context = null - , $offset = 0 - , $maxlen = null){ - $contextOptions = array( - 'http' => array( - 'user_agent' => ini_get('user_agent') - ), - ); - - if(defined('PROXY_URL') && $this->useProxy){ - $contextOptions['http']['proxy'] = PROXY_URL; - $contextOptions['http']['request_fulluri'] = true; - - if(is_null($context)){ - $context = stream_context_create($contextOptions); - } else { - $prevContext=$context; - if(!stream_context_set_option($context, $contextOptions)){ - $context = $prevContext; - } - } - } - - if(is_null($maxlen)){ - $content = @file_get_contents($url, $use_include_path, $context, $offset); - } else { - $content = @file_get_contents($url, $use_include_path, $context, $offset, $maxlen); - } - - if($content === false) - $this->debugMessage('Cant\'t download ' . $url); - - return $content; - } - - protected function getSimpleHTMLDOM($url - , $use_include_path = false - , $context = null - , $offset = 0 - , $maxLen = null - , $lowercase = true - , $forceTagsClosed = true - , $target_charset = DEFAULT_TARGET_CHARSET - , $stripRN = true - , $defaultBRText = DEFAULT_BR_TEXT - , $defaultSpanText = DEFAULT_SPAN_TEXT){ - $content = $this->getContents($url, $use_include_path, $context, $offset, $maxLen); - return str_get_html($content - , $lowercase - , $forceTagsClosed - , $target_charset - , $stripRN - , $defaultBRText - , $defaultSpanText); - } - - /** - * Maintain locally cached versions of pages to avoid multiple downloads. - * @param url url to cache - * @param duration duration of the cache file in seconds (default: 24h/86400s) - * @return content of the file as string - */ - public function getSimpleHTMLDOMCached($url - , $duration = 86400 - , $use_include_path = false - , $context = null - , $offset = 0 - , $maxLen = null - , $lowercase = true - , $forceTagsClosed = true - , $target_charset = DEFAULT_TARGET_CHARSET - , $stripRN = true - , $defaultBRText = DEFAULT_BR_TEXT - , $defaultSpanText = DEFAULT_SPAN_TEXT){ - $this->debugMessage('Caching url ' . $url . ', duration ' . $duration); - - $filepath = __DIR__ . '/../cache/pages/' . sha1($url) . '.cache'; - $this->debugMessage('Cache file ' . $filepath); - - if(file_exists($filepath) && filectime($filepath) < time() - $duration){ - unlink ($filepath); - $this->debugMessage('Cached file deleted: ' . $filepath); - } - - if(file_exists($filepath)){ - $this->debugMessage('Loading cached file ' . $filepath); - touch($filepath); - $content = file_get_contents($filepath); - } else { - $this->debugMessage('Caching ' . $url . ' to ' . $filepath); - $dir = substr($filepath, 0, strrpos($filepath, '/')); - - if(!is_dir($dir)){ - $this->debugMessage('Creating directory ' . $dir); - mkdir($dir, 0777, true); - } - - $content = $this->getContents($url, $use_include_path, $context, $offset, $maxLen); - if($content !== false){ - file_put_contents($filepath, $content); - } - } - - return str_get_html($content - , $lowercase - , $forceTagsClosed - , $target_charset - , $stripRN - , $defaultBRText - , $defaultSpanText); - } + const NAME = 'Unnamed bridge'; + const URI = ''; + const DESCRIPTION = 'No description provided'; + const MAINTAINER = 'No maintainer'; + const PARAMETERS = array(); + + public $useProxy = true; + + protected $cache; + protected $items = array(); + protected $inputs = array(); + protected $queriedContext = ''; + + protected function returnError($message, $code){ + throw new \HttpException($message, $code); + } + + protected function returnClientError($message){ + $this->returnError($message, 400); + } + + protected function returnServerError($message){ + $this->returnError($message, 500); + } + + /** + * Return items stored in the bridge + * @return mixed + */ + public function getItems(){ + return $this->items; + } + + protected function validateTextValue($value, $pattern = null){ + if(!is_null($pattern)){ + $filteredValue = filter_var($value + , FILTER_VALIDATE_REGEXP + , array('options' => array( + 'regexp' => '/^' . $pattern . '$/' + )) + ); + } else { + $filteredValue = filter_var($value); + } + + if($filteredValue === false) + return null; + + return $filteredValue; + } + + protected function validateNumberValue($value){ + $filteredValue = filter_var($value, FILTER_VALIDATE_INT); + + if($filteredValue === false && !empty($value)) + return null; + + return $filteredValue; + } + + protected function validateCheckboxValue($value){ + $filteredValue = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + + if(is_null($filteredValue)) + return null; + + return $filteredValue; + } + + protected function validateListValue($value, $expectedValues){ + $filteredValue = filter_var($value); + + if($filteredValue === false) + return null; + + if(!in_array($filteredValue, $expectedValues)){ // Check sub-values? + foreach($expectedValues as $subName => $subValue){ + if(is_array($subValue) && in_array($filteredValue, $subValue)) + return $filteredValue; + } + return null; + } + + return $filteredValue; + } + + protected function validateData(&$data){ + if(!is_array($data)) + return false; + + foreach($data as $name => $value){ + $registered = false; + foreach(static::PARAMETERS as $context => $set){ + if(array_key_exists($name, $set)){ + $registered = true; + if(!isset($set[$name]['type'])){ + $set[$name]['type'] = 'text'; + } + + switch($set[$name]['type']){ + case 'number': + $data[$name] = $this->validateNumberValue($value); + break; + case 'checkbox': + $data[$name] = $this->validateCheckboxValue($value); + break; + case 'list': + $data[$name] = $this->validateListValue($value, $set[$name]['values']); + break; + default: + case 'text': + if(isset($set[$name]['pattern'])){ + $data[$name] = $this->validateTextValue($value, $set[$name]['pattern']); + } else { + $data[$name] = $this->validateTextValue($value); + } + break; + } + + if(is_null($data[$name])){ + echo 'Parameter \'' . $name . '\' is invalid!' . PHP_EOL; + return false; + } + } + } + + if(!$registered) + return false; + } + + return true; + } + + protected function setInputs(array $inputs, $queriedContext){ + // Import and assign all inputs to their context + foreach($inputs as $name => $value){ + foreach(static::PARAMETERS as $context => $set){ + if(array_key_exists($name, static::PARAMETERS[$context])){ + $this->inputs[$context][$name]['value'] = $value; + } + } + } + + // Apply default values to missing data + $contexts = array($queriedContext); + if(array_key_exists('global', static::PARAMETERS)){ + $contexts[] = 'global'; + } + + foreach($contexts as $context){ + foreach(static::PARAMETERS[$context] as $name => $properties){ + if(isset($this->inputs[$context][$name]['value'])){ + continue; + } + + $type = isset($properties['type']) ? $properties['type'] : 'text'; + + switch($type){ + case 'checkbox': + if(!isset($properties['defaultValue'])){ + $this->inputs[$context][$name]['value'] = false; + } else { + $this->inputs[$context][$name]['value'] = $properties['defaultValue']; + } + break; + case 'list': + if(!isset($properties['defaultValue'])){ + $firstItem = reset($properties['values']); + if(is_array($firstItem)){ + $firstItem = reset($firstItem); + } + $this->inputs[$context][$name]['value'] = $firstItem; + } else { + $this->inputs[$context][$name]['value'] = $properties['defaultValue']; + } + break; + default: + if(isset($properties['defaultValue'])){ + $this->inputs[$context][$name]['value'] = $properties['defaultValue']; + } + break; + } + } + } + + // Copy global parameter values to the guessed context + if(array_key_exists('global', static::PARAMETERS)){ + foreach(static::PARAMETERS['global'] as $name => $properties){ + if(isset($inputs[$name])){ + $value = $inputs[$name]; + } elseif (isset($properties['value'])){ + $value = $properties['value']; + } else { + continue; + } + $this->inputs[$queriedContext][$name]['value'] = $value; + } + } + + // Only keep guessed context parameters values + if(isset($this->inputs[$queriedContext])){ + $this->inputs = array($queriedContext => $this->inputs[$queriedContext]); + } else { + $this->inputs = array(); + } + } + + protected function getQueriedContext(array $inputs){ + $queriedContexts = array(); + foreach(static::PARAMETERS as $context => $set){ + $queriedContexts[$context] = null; + foreach($set as $id => $properties){ + if(isset($inputs[$id]) && !empty($inputs[$id])){ + $queriedContexts[$context] = true; + } elseif(isset($properties['required']) + && $properties['required'] === true){ + $queriedContexts[$context] = false; + break; + } + } + } + + if(array_key_exists('global', static::PARAMETERS) + && $queriedContexts['global'] === false){ + return null; + } + unset($queriedContexts['global']); + + switch(array_sum($queriedContexts)){ + case 0: + foreach($queriedContexts as $context => $queried){ + if (is_null($queried)){ + return $context; + } + } + return null; + case 1: return array_search(true, $queriedContexts); + default: return false; + } + } + + /** + * Defined datas with parameters depending choose bridge + * Note : you can define a cache with "setCache" + * @param array array with expected bridge paramters + */ + public function setDatas(array $inputs){ + if(!is_null($this->cache)){ + $this->cache->prepare($inputs); + $time = $this->cache->getTime(); + if($time !== false && (time() - $this->getCacheDuration() < $time)){ + $this->items = $this->cache->loadData(); + return; + } + } + + if(empty(static::PARAMETERS)){ + if(!empty($inputs)){ + $this->returnClientError('Invalid parameters value(s)'); + } + + $this->collectData(); + if(!is_null($this->cache)){ + $this->cache->saveData($this->getItems()); + } + return; + } + + if(!$this->validateData($inputs)){ + $this->returnClientError('Invalid parameters value(s)'); + } + + // Guess the paramter context from input data + $this->queriedContext = $this->getQueriedContext($inputs); + if(is_null($this->queriedContext)){ + $this->returnClientError('Required parameter(s) missing'); + } elseif($this->queriedContext === false){ + $this->returnClientError('Mixed context parameters'); + } + + $this->setInputs($inputs, $this->queriedContext); + + $this->collectData(); + + if(!is_null($this->cache)){ + $this->cache->saveData($this->getItems()); + } + } + + function getInput($input){ + if(!isset($this->inputs[$this->queriedContext][$input]['value'])){ + return null; + } + return $this->inputs[$this->queriedContext][$input]['value']; + } + + public function getName(){ + return static::NAME; + } + + public function getURI(){ + return static::URI; + } + + public function getCacheDuration(){ + return 3600; + } + + public function setCache(\CacheAbstract $cache){ + $this->cache = $cache; + } + + public function debugMessage($text){ + if(!file_exists('DEBUG')) { + return; + } + + $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3); + $calling = $backtrace[2]; + $message = $calling['file'] . ':' + . $calling['line'] . ' class ' + . get_class($this) . '->' + . $calling['function'] . ' - ' + . $text; + + error_log($message); + } + + protected function getContents($url + , $use_include_path = false + , $context = null + , $offset = 0 + , $maxlen = null){ + $contextOptions = array( + 'http' => array( + 'user_agent' => ini_get('user_agent') + ) + ); + + if(defined('PROXY_URL') && $this->useProxy){ + $contextOptions['http']['proxy'] = PROXY_URL; + $contextOptions['http']['request_fulluri'] = true; + + if(is_null($context)){ + $context = stream_context_create($contextOptions); + } else { + $prevContext=$context; + if(!stream_context_set_option($context, $contextOptions)){ + $context = $prevContext; + } + } + } + + if(is_null($maxlen)){ + $content = @file_get_contents($url, $use_include_path, $context, $offset); + } else { + $content = @file_get_contents($url, $use_include_path, $context, $offset, $maxlen); + } + + if($content === false) + $this->debugMessage('Cant\'t download ' . $url); + + return $content; + } + + protected function getSimpleHTMLDOM($url + , $use_include_path = false + , $context = null + , $offset = 0 + , $maxLen = null + , $lowercase = true + , $forceTagsClosed = true + , $target_charset = DEFAULT_TARGET_CHARSET + , $stripRN = true + , $defaultBRText = DEFAULT_BR_TEXT + , $defaultSpanText = DEFAULT_SPAN_TEXT){ + $content = $this->getContents($url, $use_include_path, $context, $offset, $maxLen); + return str_get_html($content + , $lowercase + , $forceTagsClosed + , $target_charset + , $stripRN + , $defaultBRText + , $defaultSpanText); + } + + /** + * Maintain locally cached versions of pages to avoid multiple downloads. + * @param url url to cache + * @param duration duration of the cache file in seconds (default: 24h/86400s) + * @return content of the file as string + */ + public function getSimpleHTMLDOMCached($url + , $duration = 86400 + , $use_include_path = false + , $context = null + , $offset = 0 + , $maxLen = null + , $lowercase = true + , $forceTagsClosed = true + , $target_charset = DEFAULT_TARGET_CHARSET + , $stripRN = true + , $defaultBRText = DEFAULT_BR_TEXT + , $defaultSpanText = DEFAULT_SPAN_TEXT){ + $this->debugMessage('Caching url ' . $url . ', duration ' . $duration); + + $filepath = __DIR__ . '/../cache/pages/' . sha1($url) . '.cache'; + $this->debugMessage('Cache file ' . $filepath); + + if(file_exists($filepath) && filectime($filepath) < time() - $duration){ + unlink ($filepath); + $this->debugMessage('Cached file deleted: ' . $filepath); + } + + if(file_exists($filepath)){ + $this->debugMessage('Loading cached file ' . $filepath); + touch($filepath); + $content = file_get_contents($filepath); + } else { + $this->debugMessage('Caching ' . $url . ' to ' . $filepath); + $dir = substr($filepath, 0, strrpos($filepath, '/')); + + if(!is_dir($dir)){ + $this->debugMessage('Creating directory ' . $dir); + mkdir($dir, 0777, true); + } + + $content = $this->getContents($url, $use_include_path, $context, $offset, $maxLen); + if($content !== false){ + file_put_contents($filepath, $content); + } + } + + return str_get_html($content + , $lowercase + , $forceTagsClosed + , $target_charset + , $stripRN + , $defaultBRText + , $defaultSpanText); + } } diff --git a/lib/BridgeInterface.php b/lib/BridgeInterface.php index 5054d102..a63b63f5 100644 --- a/lib/BridgeInterface.php +++ b/lib/BridgeInterface.php @@ -1,7 +1,7 @@ <?php interface BridgeInterface { - public function collectData(); - public function getCacheDuration(); - public function getName(); - public function getURI(); + public function collectData(); + public function getCacheDuration(); + public function getName(); + public function getURI(); } diff --git a/lib/Cache.php b/lib/Cache.php index 572b96dc..b04f53ee 100644 --- a/lib/Cache.php +++ b/lib/Cache.php @@ -1,91 +1,93 @@ <?php require_once(__DIR__ . '/CacheInterface.php'); -class Cache{ +class Cache { - static protected $dirCache; + static protected $dirCache; - public function __construct(){ - throw new \LogicException('Please use ' . __CLASS__ . '::create for new object.'); - } + public function __construct(){ + throw new \LogicException('Please use ' . __CLASS__ . '::create for new object.'); + } - static public function create($nameCache){ - if( !static::isValidNameCache($nameCache) ){ - throw new \InvalidArgumentException('Name cache must be at least one uppercase follow or not by alphanumeric or dash characters.'); - } + static public function create($nameCache){ + if(!static::isValidNameCache($nameCache)){ + throw new \InvalidArgumentException('Name cache must be at least' + . ' one uppercase follow or not by alphanumeric or dash characters.'); + } - $pathCache = self::getDir() . $nameCache . '.php'; + $pathCache = self::getDir() . $nameCache . '.php'; - if( !file_exists($pathCache) ){ - throw new \Exception('The cache you looking for does not exist.'); - } + if(!file_exists($pathCache)){ + throw new \Exception('The cache you looking for does not exist.'); + } - require_once $pathCache; + require_once $pathCache; - return new $nameCache(); - } + return new $nameCache(); + } - static public function setDir($dirCache){ - if( !is_string($dirCache) ){ - throw new \InvalidArgumentException('Dir cache must be a string.'); - } + static public function setDir($dirCache){ + if(!is_string($dirCache)){ + throw new \InvalidArgumentException('Dir cache must be a string.'); + } - if( !file_exists($dirCache) ){ - throw new \Exception('Dir cache does not exist.'); - } + if(!file_exists($dirCache)){ + throw new \Exception('Dir cache does not exist.'); + } - self::$dirCache = $dirCache; - } + self::$dirCache = $dirCache; + } - static public function getDir(){ - $dirCache = self::$dirCache; + static public function getDir(){ + $dirCache = self::$dirCache; - if( is_null($dirCache) ){ - throw new \LogicException(__CLASS__ . ' class need to know cache path !'); - } + if(is_null($dirCache)){ + throw new \LogicException(__CLASS__ . ' class need to know cache path !'); + } + + return $dirCache; + } - return $dirCache; - } + static public function isValidNameCache($nameCache){ + return preg_match('@^[A-Z][a-zA-Z0-9-]*$@', $nameCache); + } - static public function isValidNameCache($nameCache){ - return preg_match('@^[A-Z][a-zA-Z0-9-]*$@', $nameCache); - } + static public function utf8_encode_deep(&$input){ + if (is_string($input)){ + $input = utf8_encode($input); + } elseif(is_array($input)){ + foreach($input as &$value){ + Cache::utf8_encode_deep($value); + } - static public function utf8_encode_deep(&$input) { - if (is_string($input)) { - $input = utf8_encode($input); - } else if (is_array($input)) { - foreach ($input as &$value) { - Cache::utf8_encode_deep($value); - } + unset($value); + } elseif(is_object($input)){ + $vars = array_keys(get_object_vars($input)); - unset($value); - } else if (is_object($input)) { - $vars = array_keys(get_object_vars($input)); + foreach($vars as $var){ + Cache::utf8_encode_deep($input->$var); + } + } + } - foreach ($vars as $var) { - Cache::utf8_encode_deep($input->$var); - } - } - } - - static public function purge() { - $cacheTimeLimit = time() - 60*60*24 ; + static public function purge(){ + $cacheTimeLimit = time() - 60*60*24; $cachePath = 'cache'; - if(file_exists($cachePath)) { - $cacheIterator = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($cachePath), - RecursiveIteratorIterator::CHILD_FIRST - ); - foreach ($cacheIterator as $cacheFile) { - if (in_array($cacheFile->getBasename(), array('.', '..'))) - continue; - elseif ($cacheFile->isFile()) { - if( filemtime($cacheFile->getPathname()) < $cacheTimeLimit ) - unlink( $cacheFile->getPathname() ); - } - } + if(file_exists($cachePath)){ + $cacheIterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($cachePath), + RecursiveIteratorIterator::CHILD_FIRST + ); + + foreach($cacheIterator as $cacheFile){ + if(in_array($cacheFile->getBasename(), array('.', '..'))) + continue; + elseif($cacheFile->isFile()){ + if(filemtime($cacheFile->getPathname()) < $cacheTimeLimit) + unlink($cacheFile->getPathname()); + } + } } } diff --git a/lib/CacheAbstract.php b/lib/CacheAbstract.php index 98608b25..e6c39d8c 100644 --- a/lib/CacheAbstract.php +++ b/lib/CacheAbstract.php @@ -1,11 +1,11 @@ <?php require_once(__DIR__ . '/CacheInterface.php'); -abstract class CacheAbstract implements CacheInterface{ - protected $param; +abstract class CacheAbstract implements CacheInterface { + protected $param; - public function prepare(array $param){ - $this->param = $param; + public function prepare(array $param){ + $this->param = $param; - return $this; - } + return $this; + } } diff --git a/lib/CacheInterface.php b/lib/CacheInterface.php index 89d78ecc..4c59df30 100644 --- a/lib/CacheInterface.php +++ b/lib/CacheInterface.php @@ -1,6 +1,6 @@ <?php -interface CacheInterface{ - public function loadData(); - public function saveData($datas); - public function getTime(); +interface CacheInterface { + public function loadData(); + public function saveData($datas); + public function getTime(); } diff --git a/lib/Exceptions.php b/lib/Exceptions.php index 74124eaa..9b906940 100644 --- a/lib/Exceptions.php +++ b/lib/Exceptions.php @@ -6,56 +6,55 @@ class HttpException extends \Exception{} */ class Http{ - /** - * Return message corresponding to Http code - */ - static public function getMessageForCode($code){ - $codes = self::getCodes(); + /** + * Return message corresponding to Http code + */ + static public function getMessageForCode($code){ + $codes = self::getCodes(); - if( isset($codes[$code]) ){ - return $codes[$code]; - } + if(isset($codes[$code])) + return $codes[$code]; - return ''; - } + return ''; + } - /** - * List of common Http code - */ - static public function getCodes(){ - return array( - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Moved Temporarily', - 307 => 'Temporary Redirect', - 310 => 'Too many Redirects', - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Time-out', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Requested range unsatisfiable', - 417 => 'Expectation failed', - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Time-out', - 508 => 'Loop detected', - ); - } + /** + * List of common Http code + */ + static public function getCodes(){ + return array( + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Moved Temporarily', + 307 => 'Temporary Redirect', + 310 => 'Too many Redirects', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Time-out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested range unsatisfiable', + 417 => 'Expectation failed', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Time-out', + 508 => 'Loop detected', + ); + } } diff --git a/lib/FeedExpander.php b/lib/FeedExpander.php index 5566f7c1..f0ec4c62 100644 --- a/lib/FeedExpander.php +++ b/lib/FeedExpander.php @@ -2,175 +2,178 @@ require_once(__DIR__ . '/BridgeInterface.php'); abstract class FeedExpander extends BridgeAbstract { - private $name; - private $uri; - private $description; - - public function collectExpandableDatas($url, $maxItems = -1){ - if(empty($url)){ - $this->returnServerError('There is no $url for this RSS expander'); - } - - $this->debugMessage('Loading from ' . $url); - - /* Notice we do not use cache here on purpose: - * we want a fresh view of the RSS stream each time - */ - $content = $this->getContents($url) - or $this->returnServerError('Could not request ' . $url); - $rssContent = simplexml_load_string($content); - - $this->debugMessage('Detecting feed format/version'); - if(isset($rssContent->channel[0])){ - $this->debugMessage('Detected RSS format'); - if(isset($rssContent->item[0])){ - $this->debugMessage('Detected RSS 1.0 format'); - $this->collect_RSS_1_0_data($rssContent, $maxItems); - } else { - $this->debugMessage('Detected RSS 0.9x or 2.0 format'); - $this->collect_RSS_2_0_data($rssContent, $maxItems); - } - } elseif(isset($rssContent->entry[0])){ - $this->debugMessage('Detected ATOM format'); - $this->collect_ATOM_data($rssContent, $maxItems); - } else { - $this->debugMessage('Unknown feed format/version'); - $this->returnServerError('The feed format is unknown!'); - } - } - - protected function collect_RSS_1_0_data($rssContent, $maxItems){ - $this->load_RSS_2_0_feed_data($rssContent->channel[0]); - foreach($rssContent->item as $item){ - $this->debugMessage('parsing item ' . var_export($item, true)); - $this->items[] = $this->parseItem($item); - if($maxItems !== -1 && count($this->items) >= $maxItems) break; - } - } - - protected function collect_RSS_2_0_data($rssContent, $maxItems){ - $rssContent = $rssContent->channel[0]; - $this->debugMessage('RSS content is ===========\n' . var_export($rssContent, true) . '==========='); - $this->load_RSS_2_0_feed_data($rssContent); - foreach($rssContent->item as $item){ - $this->debugMessage('parsing item ' . var_export($item, true)); - $this->items[] = $this->parseItem($item); - if($maxItems !== -1 && count($this->items) >= $maxItems) break; - } - } - - protected function collect_ATOM_data($content, $maxItems){ - $this->load_ATOM_feed_data($content); - foreach($content->entry as $item){ - $this->debugMessage('parsing item ' . var_export($item, true)); - $this->items[] = $this->parseItem($item); - if($maxItems !== -1 && count($this->items) >= $maxItems) break; - } - } - - protected function RSS_2_0_time_to_timestamp($item){ - return DateTime::createFromFormat('D, d M Y H:i:s e', $item->pubDate)->getTimestamp(); - } - - // TODO set title, link, description, language, and so on - protected function load_RSS_2_0_feed_data($rssContent){ - $this->name = trim($rssContent->title); - $this->uri = trim($rssContent->link); - $this->description = trim($rssContent->description); - } - - protected function load_ATOM_feed_data($content){ - $this->name = $content->title; - - // Find best link (only one, or first of 'alternate') - if(!isset($content->link)){ - $this->uri = ''; - } elseif (count($content->link) === 1){ - $this->uri = $content->link[0]['href']; - } else { - $this->uri = ''; - foreach($content->link as $link){ - if(strtolower($link['rel']) === 'alternate'){ - $this->uri = $link['href']; - break; - } - } - } - - if(isset($content->subtitle)) - $this->description = $content->subtitle; - } - - protected function parseATOMItem($feedItem){ - $item = array(); - if(isset($feedItem->id)) $item['uri'] = $feedItem->id; - if(isset($feedItem->title)) $item['title'] = $feedItem->title; - if(isset($feedItem->updated)) $item['timestamp'] = strtotime($feedItem->updated); - if(isset($feedItem->author)) $item['author'] = $feedItem->author->name; - if(isset($feedItem->content)) $item['content'] = $feedItem->content; - return $item; - } - - protected function parseRSS_0_9_1_Item($feedItem){ - $item = array(); - if(isset($feedItem->link)) $item['uri'] = $feedItem->link; - if(isset($feedItem->title)) $item['title'] = $feedItem->title; - // rss 0.91 doesn't support timestamps - // rss 0.91 doesn't support authors - if(isset($feedItem->description)) $item['content'] = $feedItem->description; - return $item; - } - - protected function parseRSS_1_0_Item($feedItem){ - // 1.0 adds optional elements around the 0.91 standard - $item = $this->parseRSS_0_9_1_Item($feedItem); - - $namespaces = $feedItem->getNamespaces(true); - if(isset($namespaces['dc'])){ - $dc = $feedItem->children($namespaces['dc']); - if(isset($dc->date)) $item['timestamp'] = strtotime($dc->date); - if(isset($dc->creator)) $item['author'] = $dc->creator; - } - - return $item; - } - - protected function parseRSS_2_0_Item($feedItem){ - // Primary data is compatible to 0.91 with some additional data - $item = $this->parseRSS_0_9_1_Item($feedItem); - - $namespaces = $feedItem->getNamespaces(true); - if(isset($namespaces['dc'])) $dc = $feedItem->children($namespaces['dc']); - - if(isset($feedItem->pubDate)){ - $item['timestamp'] = strtotime($feedItem->pubDate); - } elseif(isset($dc->date)){ - $item['timestamp'] = strtotime($dc->date); - } - if(isset($feedItem->author)){ - $item['author'] = $feedItem->author; - } elseif(isset($dc->creator)){ - $item['author'] = $dc->creator; - } - return $item; - } - - /** - * Method should return, from a source RSS item given by lastRSS, one of our Items objects - * @param $item the input rss item - * @return a RSS-Bridge Item, with (hopefully) the whole content) - */ - abstract protected function parseItem($item); - - public function getURI(){ - return $this->uri; - } - - public function getName(){ - return $this->name; - } - - public function getDescription(){ - return $this->description; - } + private $name; + private $uri; + private $description; + + public function collectExpandableDatas($url, $maxItems = -1){ + if(empty($url)){ + $this->returnServerError('There is no $url for this RSS expander'); + } + + $this->debugMessage('Loading from ' . $url); + + /* Notice we do not use cache here on purpose: + * we want a fresh view of the RSS stream each time + */ + $content = $this->getContents($url) + or $this->returnServerError('Could not request ' . $url); + $rssContent = simplexml_load_string($content); + + $this->debugMessage('Detecting feed format/version'); + if(isset($rssContent->channel[0])){ + $this->debugMessage('Detected RSS format'); + if(isset($rssContent->item[0])){ + $this->debugMessage('Detected RSS 1.0 format'); + $this->collect_RSS_1_0_data($rssContent, $maxItems); + } else { + $this->debugMessage('Detected RSS 0.9x or 2.0 format'); + $this->collect_RSS_2_0_data($rssContent, $maxItems); + } + } elseif(isset($rssContent->entry[0])){ + $this->debugMessage('Detected ATOM format'); + $this->collect_ATOM_data($rssContent, $maxItems); + } else { + $this->debugMessage('Unknown feed format/version'); + $this->returnServerError('The feed format is unknown!'); + } + } + + protected function collect_RSS_1_0_data($rssContent, $maxItems){ + $this->load_RSS_2_0_feed_data($rssContent->channel[0]); + foreach($rssContent->item as $item){ + $this->debugMessage('parsing item ' . var_export($item, true)); + $this->items[] = $this->parseItem($item); + if($maxItems !== -1 && count($this->items) >= $maxItems) break; + } + } + + protected function collect_RSS_2_0_data($rssContent, $maxItems){ + $rssContent = $rssContent->channel[0]; + $this->debugMessage('RSS content is ===========\n' + . var_export($rssContent, true) + . '==========='); + + $this->load_RSS_2_0_feed_data($rssContent); + foreach($rssContent->item as $item){ + $this->debugMessage('parsing item ' . var_export($item, true)); + $this->items[] = $this->parseItem($item); + if($maxItems !== -1 && count($this->items) >= $maxItems) break; + } + } + + protected function collect_ATOM_data($content, $maxItems){ + $this->load_ATOM_feed_data($content); + foreach($content->entry as $item){ + $this->debugMessage('parsing item ' . var_export($item, true)); + $this->items[] = $this->parseItem($item); + if($maxItems !== -1 && count($this->items) >= $maxItems) break; + } + } + + protected function RSS_2_0_time_to_timestamp($item){ + return DateTime::createFromFormat('D, d M Y H:i:s e', $item->pubDate)->getTimestamp(); + } + + // TODO set title, link, description, language, and so on + protected function load_RSS_2_0_feed_data($rssContent){ + $this->name = trim($rssContent->title); + $this->uri = trim($rssContent->link); + $this->description = trim($rssContent->description); + } + + protected function load_ATOM_feed_data($content){ + $this->name = $content->title; + + // Find best link (only one, or first of 'alternate') + if(!isset($content->link)){ + $this->uri = ''; + } elseif (count($content->link) === 1){ + $this->uri = $content->link[0]['href']; + } else { + $this->uri = ''; + foreach($content->link as $link){ + if(strtolower($link['rel']) === 'alternate'){ + $this->uri = $link['href']; + break; + } + } + } + + if(isset($content->subtitle)) + $this->description = $content->subtitle; + } + + protected function parseATOMItem($feedItem){ + $item = array(); + if(isset($feedItem->id)) $item['uri'] = $feedItem->id; + if(isset($feedItem->title)) $item['title'] = $feedItem->title; + if(isset($feedItem->updated)) $item['timestamp'] = strtotime($feedItem->updated); + if(isset($feedItem->author)) $item['author'] = $feedItem->author->name; + if(isset($feedItem->content)) $item['content'] = $feedItem->content; + return $item; + } + + protected function parseRSS_0_9_1_Item($feedItem){ + $item = array(); + if(isset($feedItem->link)) $item['uri'] = $feedItem->link; + if(isset($feedItem->title)) $item['title'] = $feedItem->title; + // rss 0.91 doesn't support timestamps + // rss 0.91 doesn't support authors + if(isset($feedItem->description)) $item['content'] = $feedItem->description; + return $item; + } + + protected function parseRSS_1_0_Item($feedItem){ + // 1.0 adds optional elements around the 0.91 standard + $item = $this->parseRSS_0_9_1_Item($feedItem); + + $namespaces = $feedItem->getNamespaces(true); + if(isset($namespaces['dc'])){ + $dc = $feedItem->children($namespaces['dc']); + if(isset($dc->date)) $item['timestamp'] = strtotime($dc->date); + if(isset($dc->creator)) $item['author'] = $dc->creator; + } + + return $item; + } + + protected function parseRSS_2_0_Item($feedItem){ + // Primary data is compatible to 0.91 with some additional data + $item = $this->parseRSS_0_9_1_Item($feedItem); + + $namespaces = $feedItem->getNamespaces(true); + if(isset($namespaces['dc'])) $dc = $feedItem->children($namespaces['dc']); + + if(isset($feedItem->pubDate)){ + $item['timestamp'] = strtotime($feedItem->pubDate); + } elseif(isset($dc->date)){ + $item['timestamp'] = strtotime($dc->date); + } + if(isset($feedItem->author)){ + $item['author'] = $feedItem->author; + } elseif(isset($dc->creator)){ + $item['author'] = $dc->creator; + } + return $item; + } + + /** + * Method should return, from a source RSS item given by lastRSS, one of our Items objects + * @param $item the input rss item + * @return a RSS-Bridge Item, with (hopefully) the whole content) + */ + abstract protected function parseItem($item); + + public function getURI(){ + return $this->uri; + } + + public function getName(){ + return $this->name; + } + + public function getDescription(){ + return $this->description; + } } diff --git a/lib/Format.php b/lib/Format.php index 618c0a30..182bf0be 100644 --- a/lib/Format.php +++ b/lib/Format.php @@ -1,72 +1,73 @@ <?php require_once(__DIR__ . '/FormatInterface.php'); -class Format{ +class Format { - static protected $dirFormat; + static protected $dirFormat; - public function __construct(){ - throw new \LogicException('Please use ' . __CLASS__ . '::create for new object.'); - } + public function __construct(){ + throw new \LogicException('Please use ' . __CLASS__ . '::create for new object.'); + } - static public function create($nameFormat){ - if( !preg_match('@^[A-Z][a-zA-Z]*$@', $nameFormat)){ - throw new \InvalidArgumentException('Name format must be at least one uppercase follow or not by alphabetic characters.'); - } + static public function create($nameFormat){ + if(!preg_match('@^[A-Z][a-zA-Z]*$@', $nameFormat)){ + throw new \InvalidArgumentException('Name format must be at least ' + . 'one uppercase follow or not by alphabetic characters.'); + } - $nameFormat=$nameFormat.'Format'; - $pathFormat = self::getDir() . $nameFormat . '.php'; + $nameFormat = $nameFormat . 'Format'; + $pathFormat = self::getDir() . $nameFormat . '.php'; - if( !file_exists($pathFormat) ){ - throw new \Exception('The format you looking for does not exist.'); - } + if(!file_exists($pathFormat)){ + throw new \Exception('The format you looking for does not exist.'); + } - require_once $pathFormat; + require_once $pathFormat; - return new $nameFormat(); - } + return new $nameFormat(); + } - static public function setDir($dirFormat){ - if( !is_string($dirFormat) ){ - throw new \InvalidArgumentException('Dir format must be a string.'); - } + static public function setDir($dirFormat){ + if(!is_string($dirFormat)){ + throw new \InvalidArgumentException('Dir format must be a string.'); + } - if( !file_exists($dirFormat) ){ - throw new \Exception('Dir format does not exist.'); - } + if(!file_exists($dirFormat)){ + throw new \Exception('Dir format does not exist.'); + } - self::$dirFormat = $dirFormat; - } + self::$dirFormat = $dirFormat; + } - static public function getDir(){ - $dirFormat = self::$dirFormat; + static public function getDir(){ + $dirFormat = self::$dirFormat; - if( is_null($dirFormat) ){ - throw new \LogicException(__CLASS__ . ' class need to know format path !'); - } + if(is_null($dirFormat)){ + throw new \LogicException(__CLASS__ . ' class need to know format path !'); + } - return $dirFormat; - } + return $dirFormat; + } - /** - * Read format dir and catch informations about each format depending annotation - * @return array Informations about each format - */ - static public function searchInformation(){ - $pathDirFormat = self::getDir(); + /** + * Read format dir and catch informations about each format depending annotation + * @return array Informations about each format + */ + static public function searchInformation(){ + $pathDirFormat = self::getDir(); - $listFormat = array(); + $listFormat = array(); - $searchCommonPattern = array('name'); + $searchCommonPattern = array('name'); - $dirFiles = scandir($pathDirFormat); - if( $dirFiles !== false ){ - foreach( $dirFiles as $fileName ){ - if( preg_match('@^([^.]+)Format\.php$@U', $fileName, $out) ){ // Is PHP file ? - $listFormat[] = $out[1]; - } - } - } + $dirFiles = scandir($pathDirFormat); + if($dirFiles !== false){ + foreach($dirFiles as $fileName){ + if(preg_match('@^([^.]+)Format\.php$@U', $fileName, $out)){ // Is PHP file ? + $listFormat[] = $out[1]; + } + } + } - return $listFormat; - } + return $listFormat; + } } diff --git a/lib/FormatAbstract.php b/lib/FormatAbstract.php index e4a911c1..19629587 100644 --- a/lib/FormatAbstract.php +++ b/lib/FormatAbstract.php @@ -1,107 +1,107 @@ <?php require_once(__DIR__ . '/FormatInterface.php'); -abstract class FormatAbstract implements FormatInterface{ - const DEFAULT_CHARSET = 'UTF-8'; - - protected - $contentType, - $charset, - $items, - $extraInfos - ; - - public function setCharset($charset){ - $this->charset = $charset; - - return $this; - } - - public function getCharset(){ - $charset = $this->charset; - - return is_null($charset) ? self::DEFAULT_CHARSET : $charset; - } - - protected function setContentType($contentType){ - $this->contentType = $contentType; - - return $this; - } - - protected function callContentType(){ - header('Content-Type: ' . $this->contentType); - } - - public function display(){ - echo $this->stringify(); - - return $this; - } - - public function setItems(array $items){ - $this->items = array_map(array($this, 'array_trim'), $items); - - return $this; - } - - public function getItems(){ - if(!is_array($this->items)) - throw new \LogicException('Feed the ' . get_class($this) . ' with "setItems" method before !'); - - return $this->items; - } - - /** - * Define common informations can be required by formats and set default value for unknow values - * @param array $extraInfos array with know informations (there isn't merge !!!) - * @return this - */ - public function setExtraInfos(array $extraInfos = array()){ - foreach(array('name', 'uri') as $infoName){ - if( !isset($extraInfos[$infoName]) ){ - $extraInfos[$infoName] = ''; - } - } - - $this->extraInfos = $extraInfos; - - return $this; - } - - /** - * Return extra infos - * @return array See "setExtraInfos" detail method to know what extra are disponibles - */ - public function getExtraInfos(){ - if( is_null($this->extraInfos) ){ // No extra info ? - $this->setExtraInfos(); // Define with default value - } - - return $this->extraInfos; - } - - /** - * Sanitized html while leaving it functionnal. - * The aim is to keep html as-is (with clickable hyperlinks) - * while reducing annoying and potentially dangerous things. - * Yes, I know sanitizing HTML 100% is an impossible task. - * Maybe we'll switch to http://htmlpurifier.org/ - * or http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/index.php - */ - protected function sanitizeHtml($html) - { - $html = str_replace('<script','<‌script',$html); // Disable scripts, but leave them visible. - $html = str_replace('<iframe','<‌iframe',$html); - $html = str_replace('<link','<‌link',$html); - // We leave alone object and embed so that videos can play in RSS readers. - return $html; - } - - protected function array_trim($elements){ - foreach($elements as $key => $value){ - if(is_string($value)) - $elements[$key] = trim($value); - } - return $elements; - } +abstract class FormatAbstract implements FormatInterface { + const DEFAULT_CHARSET = 'UTF-8'; + + protected + $contentType, + $charset, + $items, + $extraInfos + ; + + public function setCharset($charset){ + $this->charset = $charset; + + return $this; + } + + public function getCharset(){ + $charset = $this->charset; + + return is_null($charset) ? self::DEFAULT_CHARSET : $charset; + } + + protected function setContentType($contentType){ + $this->contentType = $contentType; + + return $this; + } + + protected function callContentType(){ + header('Content-Type: ' . $this->contentType); + } + + public function display(){ + echo $this->stringify(); + + return $this; + } + + public function setItems(array $items){ + $this->items = array_map(array($this, 'array_trim'), $items); + + return $this; + } + + public function getItems(){ + if(!is_array($this->items)) + throw new \LogicException('Feed the ' . get_class($this) . ' with "setItems" method before !'); + + return $this->items; + } + + /** + * Define common informations can be required by formats and set default value for unknow values + * @param array $extraInfos array with know informations (there isn't merge !!!) + * @return this + */ + public function setExtraInfos(array $extraInfos = array()){ + foreach(array('name', 'uri') as $infoName){ + if( !isset($extraInfos[$infoName]) ){ + $extraInfos[$infoName] = ''; + } + } + + $this->extraInfos = $extraInfos; + + return $this; + } + + /** + * Return extra infos + * @return array See "setExtraInfos" detail method to know what extra are disponibles + */ + public function getExtraInfos(){ + if( is_null($this->extraInfos) ){ // No extra info ? + $this->setExtraInfos(); // Define with default value + } + + return $this->extraInfos; + } + + /** + * Sanitized html while leaving it functionnal. + * The aim is to keep html as-is (with clickable hyperlinks) + * while reducing annoying and potentially dangerous things. + * Yes, I know sanitizing HTML 100% is an impossible task. + * Maybe we'll switch to http://htmlpurifier.org/ + * or http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/index.php + */ + protected function sanitizeHtml($html) + { + $html = str_replace('<script','<‌script',$html); // Disable scripts, but leave them visible. + $html = str_replace('<iframe','<‌iframe',$html); + $html = str_replace('<link','<‌link',$html); + // We leave alone object and embed so that videos can play in RSS readers. + return $html; + } + + protected function array_trim($elements){ + foreach($elements as $key => $value){ + if(is_string($value)) + $elements[$key] = trim($value); + } + return $elements; + } } diff --git a/lib/FormatInterface.php b/lib/FormatInterface.php index ea0cf233..2fac3fcc 100644 --- a/lib/FormatInterface.php +++ b/lib/FormatInterface.php @@ -1,6 +1,6 @@ <?php interface FormatInterface{ - public function stringify(); - public function display(); - public function setItems(array $bridges); + public function stringify(); + public function display(); + public function setItems(array $bridges); } diff --git a/lib/HTMLUtils.php b/lib/HTMLUtils.php index 7e0061d4..e2e582f3 100644 --- a/lib/HTMLUtils.php +++ b/lib/HTMLUtils.php @@ -1,9 +1,8 @@ <?php class HTMLUtils { - public static function displayBridgeCard($bridgeName, $formats, $isActive = true){ $bridgeElement = Bridge::create($bridgeName); - $bridgeClass=$bridgeName.'Bridge'; + $bridgeClass = $bridgeName . 'Bridge'; if($bridgeElement == false) return ""; @@ -22,15 +21,30 @@ class HTMLUtils { CARD; // If we don't have any parameter for the bridge, we print a generic form to load it. - if(count($bridgeClass::PARAMETERS) == 0) { + if(count($bridgeClass::PARAMETERS) == 0){ $card .= HTMLUtils::getFormHeader($bridgeName); - if ($isActive){ + if($isActive){ if(defined('PROXY_URL') && PROXY_BYBRIDGE){ - $idArg = 'arg-' . urlencode($bridgeName) . '-' . urlencode('proxyoff') . '-' . urlencode('_noproxy'); - $card .= '<input id="' . $idArg . '" type="checkbox" name="_noproxy" />' . PHP_EOL; - $card .= '<label for="' .$idArg. '">Disable proxy ('.((defined('PROXY_NAME') && PROXY_NAME)?PROXY_NAME:PROXY_URL).')</label><br />' . PHP_EOL; + $idArg = 'arg-' + . urlencode($bridgeName) + . '-' + . urlencode('proxyoff') + . '-' + . urlencode('_noproxy'); + + $card .= '<input id="' + . $idArg + . '" type="checkbox" name="_noproxy" />' + . PHP_EOL; + + $card .= '<label for="' + . $idArg + . '">Disable proxy (' + . ((defined('PROXY_NAME') && PROXY_NAME) ? PROXY_NAME : PROXY_URL) + . ')</label><br />' + . PHP_EOL; } $card .= HTMLUtils::getHelperButtonsFormat($formats); @@ -59,7 +73,7 @@ CARD; $card .= HTMLUtils::getFormHeader($bridgeName); - foreach($parameter as $id=>$inputEntry) { + foreach($parameter as $id=>$inputEntry){ $additionalInfoString = ''; if(isset($inputEntry['required']) && $inputEntry['required'] === true) @@ -77,55 +91,140 @@ CARD; if(!isset($inputEntry['defaultValue'])) $inputEntry['defaultValue'] = ''; - $idArg = 'arg-' . urlencode($bridgeName) . '-' . urlencode($parameterName) . '-' . urlencode($id); - $card .= '<label for="' . $idArg . '">' . $inputEntry['name'] . ' : </label>' . PHP_EOL; - - if(!isset($inputEntry['type']) || $inputEntry['type'] == 'text') { - $card .= '<input ' . $additionalInfoString . ' id="' . $idArg . '" type="text" value="' . $inputEntry['defaultValue'] . '" placeholder="' . $inputEntry['exampleValue'] . '" name="' . $id . '" /><br />' . PHP_EOL; - } else if($inputEntry['type'] == 'number') { - $card .= '<input ' . $additionalInfoString . ' id="' . $idArg . '" type="number" value="' . $inputEntry['defaultValue'] . '" placeholder="' . $inputEntry['exampleValue'] . '" name="' . $id . '" /><br />' . PHP_EOL; - } else if($inputEntry['type'] == 'list') { - $card .= '<select ' . $additionalInfoString . ' id="' . $idArg . '" name="' . $id . '" >'; - - foreach($inputEntry['values'] as $name=>$value) { - if(is_array($value)){ - $card.='<optgroup label="'.htmlentities($name).'">'; - foreach($value as $subname=>$subvalue){ - if($inputEntry['defaultValue'] === $subname || $inputEntry['defaultValue'] === $subvalue) - $card .= '<option value="' . $subvalue . '" selected>' . $subname . '</option>'; - else - $card .= '<option value="' . $subvalue . '">' . $subname . '</option>'; - } - $card.='</optgroup>'; - }else{ - if($inputEntry['defaultValue'] === $name || $inputEntry['defaultValue'] === $value) - $card .= '<option value="' . $value . '" selected>' . $name . '</option>'; - else - $card .= '<option value="' . $value . '">' . $name . '</option>'; - } - } - + $idArg = 'arg-' + . urlencode($bridgeName) + . '-' + . urlencode($parameterName) + . '-' + . urlencode($id); + + $card .= '<label for="' + . $idArg + . '">' + . $inputEntry['name'] + . ' : </label>' + . PHP_EOL; + + if(!isset($inputEntry['type']) || $inputEntry['type'] == 'text'){ + $card .= '<input ' + . $additionalInfoString + . ' id="' + . $idArg + . '" type="text" value="' + . $inputEntry['defaultValue'] + . '" placeholder="' + . $inputEntry['exampleValue'] + . '" name="' + . $id + . '" /><br />' + . PHP_EOL; + } elseif($inputEntry['type'] == 'number'){ + $card .= '<input ' + . $additionalInfoString + . ' id="' + . $idArg + . '" type="number" value="' + . $inputEntry['defaultValue'] + . '" placeholder="' + . $inputEntry['exampleValue'] + . '" name="' + . $id + . '" /><br />' + . PHP_EOL; + } else if($inputEntry['type'] == 'list'){ + $card .= '<select ' + . $additionalInfoString + . ' id="' + . $idArg + . '" name="' + . $id + . '" >'; + + foreach($inputEntry['values'] as $name => $value){ + if(is_array($value)){ + $card .= '<optgroup label="' . htmlentities($name) . '">'; + foreach($value as $subname => $subvalue){ + if($inputEntry['defaultValue'] === $subname + || $inputEntry['defaultValue'] === $subvalue){ + $card .= '<option value="' + . $subvalue + . '" selected>' + . $subname + . '</option>'; + } else { + $card .= '<option value="' + . $subvalue + . '">' + . $subname + . '</option>'; + } + } + $card .= '</optgroup>'; + } else { + if($inputEntry['defaultValue'] === $name + || $inputEntry['defaultValue'] === $value){ + $card .= '<option value="' + . $value + . '" selected>' + . $name + . '</option>'; + } else { + $card .= '<option value="' + . $value + . '">' + . $name + . '</option>'; + } + } + } $card .= '</select><br >'; - } else if($inputEntry['type'] == 'checkbox') { + } elseif($inputEntry['type'] == 'checkbox'){ if($inputEntry['defaultValue'] === 'checked') - $card .= '<input ' . $additionalInfoString . ' id="' . $idArg . '" type="checkbox" name="' . $id . '" checked /><br />' . PHP_EOL; + $card .= '<input ' + . $additionalInfoString + . ' id="' + . $idArg + . '" type="checkbox" name="' + . $id + . '" checked /><br />' + . PHP_EOL; else - $card .= '<input ' . $additionalInfoString . ' id="' . $idArg . '" type="checkbox" name="' . $id . '" /><br />' . PHP_EOL; + $card .= '<input ' + . $additionalInfoString + . ' id="' + . $idArg + . '" type="checkbox" name="' + . $id + . '" /><br />' + . PHP_EOL; } } - if ($isActive){ + if($isActive){ if(defined('PROXY_URL') && PROXY_BYBRIDGE){ - $idArg = 'arg-' . urlencode($bridgeName) . '-' . urlencode('proxyoff') . '-' . urlencode('_noproxy'); - $card .= '<input id="' . $idArg . '" type="checkbox" name="_noproxy" />' . PHP_EOL; - $card .= '<label for="' .$idArg. '">Disable proxy ('.((defined('PROXY_NAME') && PROXY_NAME)?PROXY_NAME:PROXY_URL).')</label><br />' . PHP_EOL; + $idArg = 'arg-' + . urlencode($bridgeName) + . '-' + . urlencode('proxyoff') + . '-' + . urlencode('_noproxy'); + + $card .= '<input id="' + . $idArg + . '" type="checkbox" name="_noproxy" />' + . PHP_EOL; + + $card .= '<label for="' + . $idArg + . '">Disable proxy (' + . ((defined('PROXY_NAME') && PROXY_NAME) ? PROXY_NAME : PROXY_URL) + . ')</label><br />' + . PHP_EOL; } - $card .= HTMLUtils::getHelperButtonsFormat($formats); } else { $card .= '<span style="font-weight: bold;">Inactive</span>'; } - $card .= '</form>' . PHP_EOL; } @@ -138,8 +237,13 @@ CARD; private static function getHelperButtonsFormat($formats){ $buttons = ''; - foreach( $formats as $name){ - $buttons .= '<button type="submit" name="format" value="' . $name . '">' . $name . '</button>' . PHP_EOL; + foreach($formats as $name){ + $buttons .= '<button type="submit" name="format" value="' + . $name + . '">' + . $name + . '</button>' + . PHP_EOL; } return $buttons; @@ -164,22 +268,24 @@ class HTMLSanitizer { public static $KEPT_ATTRIBUTES = ["title", "href", "src"]; public static $ONLY_TEXT = []; - public function __construct($tags_to_remove = null, $kept_attributes = null, $only_keep_text = null) { - $this->tagsToRemove = $tags_to_remove == null ? HTMLSanitizer::$DEFAULT_CLEAR_TAGS : $tags_to_remove; - $this->keptAttributes = $kept_attributes == null ? HTMLSanitizer::$KEPT_ATTRIBUTES : $kept_attributes; - $this->onlyKeepText = $only_keep_text == null ? HTMLSanitizer::$ONLY_TEXT : $only_keep_text; + public function __construct($tags_to_remove = null + , $kept_attributes = null + , $only_keep_text = null){ + $this->tagsToRemove = is_null($tags_to_remove) ? HTMLSanitizer::$DEFAULT_CLEAR_TAGS : $tags_to_remove; + $this->keptAttributes = is_null($kept_attributes) ? HTMLSanitizer::$KEPT_ATTRIBUTES : $kept_attributes; + $this->onlyKeepText = is_null($only_keep_text) ? HTMLSanitizer::$ONLY_TEXT : $only_keep_text; } - public function sanitize($textToSanitize) { + public function sanitize($textToSanitize){ $htmlContent = str_get_html($textToSanitize); - foreach($htmlContent->find('*[!b38fd2b1fe7f4747d6b1c1254ccd055e]') as $element) { - if(in_array($element->tag, $this->onlyKeepText)) { + foreach($htmlContent->find('*[!b38fd2b1fe7f4747d6b1c1254ccd055e]') as $element){ + if(in_array($element->tag, $this->onlyKeepText)){ $element->outertext = $element->plaintext; - } else if(in_array($element->tag, $this->tagsToRemove)) { + } elseif(in_array($element->tag, $this->tagsToRemove)){ $element->outertext = ''; } else { - foreach($element->getAllAttributes() as $attributeName => $attribute) { + foreach($element->getAllAttributes() as $attributeName => $attribute){ if(!in_array($attributeName, $this->keptAttributes)) $element->removeAttribute($attributeName); } @@ -189,10 +295,12 @@ class HTMLSanitizer { return $htmlContent; } - public static function defaultImageSrcTo($content, $server) { - foreach($content->find('img') as $image) { - if(strpos($image->src, "http") == NULL && strpos($image->src, "//") == NULL && strpos($image->src, "data:") == NULL) - $image->src = $server.$image->src; + public static function defaultImageSrcTo($content, $server){ + foreach($content->find('img') as $image){ + if(is_null(strpos($image->src, "http")) + && is_null(strpos($image->src, "//")) + && is_null(strpos($image->src, "data:"))) + $image->src = $server . $image->src; } return $content; } diff --git a/lib/RssBridge.php b/lib/RssBridge.php index 07286831..d7ef70f2 100644 --- a/lib/RssBridge.php +++ b/lib/RssBridge.php @@ -19,28 +19,33 @@ require __DIR__ . '/HTMLUtils.php'; $vendorLibSimpleHtmlDom = __DIR__ . PATH_VENDOR . '/simplehtmldom/simple_html_dom.php'; if( !file_exists($vendorLibSimpleHtmlDom) ){ - throw new \HttpException('"PHP Simple HTML DOM Parser" library is missing. Get it from http://simplehtmldom.sourceforge.net and place the script "simple_html_dom.php" in '.substr(PATH_VENDOR,4) . '/simplehtmldom/', 500); + throw new \HttpException('"PHP Simple HTML DOM Parser" library is missing.' + . ' Get it from http://simplehtmldom.sourceforge.net and place the script' + . ' "simple_html_dom.php" in ' + . substr(PATH_VENDOR,4) + . '/simplehtmldom/' + , 500); } require_once $vendorLibSimpleHtmlDom; /* Example use - - require_once __DIR__ . '/lib/RssBridge.php'; - - // Data retrieval - Bridge::setDir(__DIR__ . '/bridges/'); - $bridge = Bridge::create('GoogleSearch'); - $bridge->collectData($_REQUEST); - - // Data transformation - Format::setDir(__DIR__ . '/formats/'); - $format = Format::create('Atom'); - $format - ->setItems($bridge->getItems()) - ->setExtraInfos(array( - 'name' => $bridge->getName(), - 'uri' => $bridge->getURI(), - )) - ->display(); + + require_once __DIR__ . '/lib/RssBridge.php'; + + // Data retrieval + Bridge::setDir(__DIR__ . '/bridges/'); + $bridge = Bridge::create('GoogleSearch'); + $bridge->collectData($_REQUEST); + + // Data transformation + Format::setDir(__DIR__ . '/formats/'); + $format = Format::create('Atom'); + $format + ->setItems($bridge->getItems()) + ->setExtraInfos(array( + 'name' => $bridge->getName(), + 'uri' => $bridge->getURI(), + )) + ->display(); */ |