1
0
Fork 0
forked from blallo/rss-bridge

[core] Apply common indentation

All files are now using tabs for indentation
This commit is contained in:
logmanoriginal 2016-09-10 20:41:11 +02:00
parent 32ce2b6541
commit 62eec43980
19 changed files with 1365 additions and 1250 deletions

View file

@ -2,93 +2,88 @@
/** /**
* Cache with file system * Cache with file system
*/ */
class FileCache extends CacheAbstract{ class FileCache extends CacheAbstract {
protected $cacheDirCreated; // boolean to avoid always chck dir cache existance protected $cacheDirCreated; // boolean to avoid always chck dir cache existance
public function loadData(){ public function loadData(){
$this->isPrepareCache(); $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();
//Re-encode datas to UTF-8
//$datas = Cache::utf8_encode_deep($datas);
$writeStream = file_put_contents($this->getCacheFile(), serialize($datas));
if(!$writeStream) { if(!$writeStream) {
throw new \Exception("Cannot write the cache... Do you have the right permissions ?"); throw new \Exception("Cannot write the cache... Do you have the right permissions ?");
} }
return $this; return $this;
} }
public function getTime(){ public function getTime(){
$this->isPrepareCache(); $this->isPrepareCache();
$cacheFile = $this->getCacheFile(); $cacheFile = $this->getCacheFile();
if( file_exists($cacheFile) ){ if(file_exists($cacheFile)){
return filemtime($cacheFile); return filemtime($cacheFile);
} }
return false; return false;
} }
/** /**
* Cache is prepared ? * Cache is prepared ?
* Note : Cache name is based on request information, then cache must be prepare before use * Note : Cache name is based on request information, then cache must be prepare before use
* @return \Exception|true * @return \Exception|true
*/ */
protected function isPrepareCache(){ protected function isPrepareCache(){
if( is_null($this->param) ){ if(is_null($this->param)){
throw new \Exception('Please feed "prepare" method before try to load'); throw new \Exception('Please feed "prepare" method before try to load');
} }
return true; return true;
} }
/** /**
* Return cache path (and create if not exist) * Return cache path (and create if not exist)
* @return string Cache path * @return string Cache path
*/ */
protected function getCachePath(){ protected function getCachePath(){
$cacheDir = __DIR__ . '/../cache/'; // FIXME : configuration ? $cacheDir = __DIR__ . '/../cache/'; // FIXME : configuration ?
// FIXME : implement recursive dir creation // FIXME : implement recursive dir creation
if( is_null($this->cacheDirCreated) && !is_dir($cacheDir) ){ if(is_null($this->cacheDirCreated) && !is_dir($cacheDir)){
$this->cacheDirCreated = true; $this->cacheDirCreated = true;
mkdir($cacheDir,0705); mkdir($cacheDir,0705);
chmod($cacheDir,0705); chmod($cacheDir,0705);
} }
return $cacheDir; return $cacheDir;
} }
/** /**
* Get the file name use for cache store * Get the file name use for cache store
* @return string Path to the file cache * @return string Path to the file cache
*/ */
protected function getCacheFile(){ protected function getCacheFile(){
return $this->getCachePath() . $this->getCacheName(); return $this->getCachePath() . $this->getCacheName();
} }
/** /**
* Determines file name for store the cache * Determines file name for store the cache
* return string * return string
*/ */
protected function getCacheName(){ protected function getCacheName(){
$this->isPrepareCache(); $this->isPrepareCache();
$stringToEncode = $_SERVER['REQUEST_URI'] . http_build_query($this->param); $stringToEncode = $_SERVER['REQUEST_URI'] . http_build_query($this->param);
$stringToEncode = preg_replace('/(\?|&)format=[^&]*/i', '$1', $stringToEncode); $stringToEncode = preg_replace('/(\?|&)format=[^&]*/i', '$1', $stringToEncode);
return hash('sha1', $stringToEncode) . '.cache'; return hash('sha1', $stringToEncode) . '.cache';
} }
} }

View file

@ -1,79 +1,80 @@
<?php <?php
/** /**
* Atom * 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{ class AtomFormat extends FormatAbstract{
public function stringify(){ public function stringify(){
$https = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 's' : ''; $https = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 's' : '';
$httpHost = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ''; $httpHost = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
$httpInfo = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : ''; $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(); $extraInfos = $this->getExtraInfos();
$title = $this->xml_encode($extraInfos['name']); $title = $this->xml_encode($extraInfos['name']);
$uri = !empty($extraInfos['uri']) ? $extraInfos['uri'] : 'https://github.com/sebsauvage/rss-bridge'; $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'); $icon = $this->xml_encode('http://icons.better-idea.org/icon?url='. $uri .'&size=64');
$uri = $this->xml_encode($uri); $uri = $this->xml_encode($uri);
$entries = ''; $entries = '';
foreach($this->getItems() as $item){ foreach($this->getItems() as $item){
$entryAuthor = isset($item['author']) ? $this->xml_encode($item['author']) : ''; $entryAuthor = isset($item['author']) ? $this->xml_encode($item['author']) : '';
$entryTitle = isset($item['title']) ? $this->xml_encode($item['title']) : ''; $entryTitle = isset($item['title']) ? $this->xml_encode($item['title']) : '';
$entryUri = isset($item['uri']) ? $this->xml_encode($item['uri']) : ''; $entryUri = isset($item['uri']) ? $this->xml_encode($item['uri']) : '';
$entryTimestamp = isset($item['timestamp']) ? $this->xml_encode(date(DATE_ATOM, $item['timestamp'])) : ''; $entryTimestamp = isset($item['timestamp']) ? $this->xml_encode(date(DATE_ATOM, $item['timestamp'])) : '';
$entryContent = isset($item['content']) ? $this->xml_encode($this->sanitizeHtml($item['content'])) : ''; $entryContent = isset($item['content']) ? $this->xml_encode($this->sanitizeHtml($item['content'])) : '';
$entries .= <<<EOD $entries .= <<<EOD
<entry> <entry>
<author> <author>
<name>{$entryAuthor}</name> <name>{$entryAuthor}</name>
</author> </author>
<title type="html"><![CDATA[{$entryTitle}]]></title> <title type="html"><![CDATA[{$entryTitle}]]></title>
<link rel="alternate" type="text/html" href="{$entryUri}" /> <link rel="alternate" type="text/html" href="{$entryUri}" />
<id>{$entryUri}</id> <id>{$entryUri}</id>
<updated>{$entryTimestamp}</updated> <updated>{$entryTimestamp}</updated>
<content type="html">{$entryContent}</content> <content type="html">{$entryContent}</content>
</entry> </entry>
EOD; EOD;
} }
$feedTimestamp = date(DATE_ATOM, time()); $feedTimestamp = date(DATE_ATOM, time());
/* Data are prepared, now let's begin the "MAGIE !!!" */ /* Data are prepared, now let's begin the "MAGIE !!!" */
$toReturn = '<?xml version="1.0" encoding="UTF-8"?>'; $toReturn = '<?xml version="1.0" encoding="UTF-8"?>';
$toReturn .= <<<EOD $toReturn .= <<<EOD
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xml:lang="en-US"> <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> <title type="text">{$title}</title>
<id>http{$https}://{$httpHost}{$httpInfo}/</id> <id>http{$https}://{$httpHost}{$httpInfo}/</id>
<icon>{$icon}</icon> <icon>{$icon}</icon>
<logo>{$icon}</logo> <logo>{$icon}</logo>
<updated>{$feedTimestamp}</updated> <updated>{$feedTimestamp}</updated>
<link rel="alternate" type="text/html" href="{$uri}" /> <link rel="alternate" type="text/html" href="{$uri}" />
<link rel="self" href="http{$https}://{$httpHost}{$serverRequestUri}" /> <link rel="self" href="http{$https}://{$httpHost}{$serverRequestUri}" />
{$entries} {$entries}
</feed> </feed>
EOD; EOD;
// Remove invalid non-UTF8 characters // Remove invalid non-UTF8 characters
ini_set('mbstring.substitute_character', 'none'); ini_set('mbstring.substitute_character', 'none');
$toReturn= mb_convert_encoding($toReturn, 'UTF-8', 'UTF-8'); $toReturn = mb_convert_encoding($toReturn, 'UTF-8', 'UTF-8');
return $toReturn; return $toReturn;
} }
public function display(){ public function display(){
$this $this
->setContentType('application/atom+xml; charset=UTF-8') ->setContentType('application/atom+xml; charset=UTF-8')
->callContentType(); ->callContentType();
return parent::display(); return parent::display();
} }
private function xml_encode($text) { private function xml_encode($text){
return htmlspecialchars($text, ENT_XML1); return htmlspecialchars($text, ENT_XML1);
} }
} }

View file

@ -1,63 +1,62 @@
<?php <?php
class HtmlFormat extends FormatAbstract{ class HtmlFormat extends FormatAbstract {
public function stringify(){ public function stringify(){
$extraInfos = $this->getExtraInfos(); $extraInfos = $this->getExtraInfos();
$title = htmlspecialchars($extraInfos['name']); $title = htmlspecialchars($extraInfos['name']);
$uri = htmlspecialchars($extraInfos['uri']); $uri = htmlspecialchars($extraInfos['uri']);
$atomquery = str_replace('format=Html', 'format=Atom', htmlentities($_SERVER['QUERY_STRING'])); $atomquery = str_replace('format=Html', 'format=Atom', htmlentities($_SERVER['QUERY_STRING']));
$mrssquery = str_replace('format=Html', 'format=Mrss', htmlentities($_SERVER['QUERY_STRING'])); $mrssquery = str_replace('format=Html', 'format=Mrss', htmlentities($_SERVER['QUERY_STRING']));
$entries = ''; $entries = '';
foreach($this->getItems() as $item){ foreach($this->getItems() as $item){
$entryAuthor = isset($item['author']) ? '<br /><p class="author">by: ' . $item['author'] . '</p>' : ''; $entryAuthor = isset($item['author']) ? '<br /><p class="author">by: ' . $item['author'] . '</p>' : '';
$entryTitle = isset($item['title']) ? $this->sanitizeHtml(strip_tags($item['title'])) : ''; $entryTitle = isset($item['title']) ? $this->sanitizeHtml(strip_tags($item['title'])) : '';
$entryUri = isset($item['uri']) ? $item['uri'] : $uri; $entryUri = isset($item['uri']) ? $item['uri'] : $uri;
$entryTimestamp = isset($item['timestamp']) ? '<time datetime="' . date(DATE_ATOM, $item['timestamp']) . '">' . date(DATE_ATOM, $item['timestamp']) . '</time>' : ''; $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>' : ''; $entryContent = isset($item['content']) ? '<div class="content">' . $this->sanitizeHtml($item['content']). '</div>' : '';
$entries .= <<<EOD $entries .= <<<EOD
<section class="feeditem"> <section class="feeditem">
<h2><a class="itemtitle" href="{$entryUri}">{$entryTitle}</a></h2> <h2><a class="itemtitle" href="{$entryUri}">{$entryTitle}</a></h2>
{$entryTimestamp} {$entryTimestamp}
{$entryAuthor} {$entryAuthor}
{$entryContent} {$entryContent}
</section> </section>
EOD; EOD;
} }
/* Data are prepared, now let's begin the "MAGIE !!!" */
/* Data are prepared, now let's begin the "MAGIE !!!" */ $toReturn = <<<EOD
$toReturn = <<<EOD
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>{$title}</title> <title>{$title}</title>
<link href="css/HtmlFormat.css" rel="stylesheet"> <link href="css/HtmlFormat.css" rel="stylesheet">
<meta name="robots" content="noindex, follow"> <meta name="robots" content="noindex, follow">
</head> </head>
<body> <body>
<h1 class="pagetitle"><a href="{$uri}" target="_blank">{$title}</a></h1> <h1 class="pagetitle"><a href="{$uri}" target="_blank">{$title}</a></h1>
<div class="buttons"> <div class="buttons">
<a href="./#bridge-{$_GET['bridge']}"><button class="backbutton"> back to rss-bridge</button></a> <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="./?{$atomquery}"><button class="rss-feed">RSS feed (ATOM)</button></a>
<a href="./?{$mrssquery}"><button class="rss-feed">RSS feed (MRSS)</button></a> <a href="./?{$mrssquery}"><button class="rss-feed">RSS feed (MRSS)</button></a>
</div> </div>
{$entries} {$entries}
</body> </body>
</html> </html>
EOD; EOD;
return $toReturn; return $toReturn;
} }
public function display() { public function display() {
$this $this
->setContentType('text/html; charset=' . $this->getCharset()) ->setContentType('text/html; charset=' . $this->getCharset())
->callContentType(); ->callContentType();
return parent::display(); return parent::display();
} }
} }

View file

@ -3,19 +3,18 @@
* Json * Json
* Builds a JSON string from $this->items and return it to browser. * Builds a JSON string from $this->items and return it to browser.
*/ */
class JsonFormat extends FormatAbstract{ class JsonFormat extends FormatAbstract {
public function stringify(){ public function stringify(){
$items = $this->getItems(); $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(){ return parent::display();
$this }
->setContentType('application/json')
->callContentType();
return parent::display();
}
} }

View file

@ -3,72 +3,72 @@
* Mrss * Mrss
* Documentation Source http://www.rssboard.org/media-rss * Documentation Source http://www.rssboard.org/media-rss
*/ */
class MrssFormat extends FormatAbstract{ class MrssFormat extends FormatAbstract {
public function stringify(){ public function stringify(){
$https = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 's' : ''; $https = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 's' : '';
$httpHost = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ''; $httpHost = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
$httpInfo = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : ''; $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(); $extraInfos = $this->getExtraInfos();
$title = $this->xml_encode($extraInfos['name']); $title = $this->xml_encode($extraInfos['name']);
$uri = $this->xml_encode(!empty($extraInfos['uri']) ? $extraInfos['uri'] : 'https://github.com/sebsauvage/rss-bridge'); $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'); $icon = $this->xml_encode('http://icons.better-idea.org/icon?url='. $uri .'&size=64');
$items = ''; $items = '';
foreach($this->getItems() as $item){ foreach($this->getItems() as $item){
$itemAuthor = isset($item['author']) ? $this->xml_encode($item['author']) : ''; $itemAuthor = isset($item['author']) ? $this->xml_encode($item['author']) : '';
$itemTitle = strip_tags(isset($item['title']) ? $this->xml_encode($item['title']) : ''); $itemTitle = strip_tags(isset($item['title']) ? $this->xml_encode($item['title']) : '');
$itemUri = isset($item['uri']) ? $this->xml_encode($item['uri']) : ''; $itemUri = isset($item['uri']) ? $this->xml_encode($item['uri']) : '';
$itemTimestamp = isset($item['timestamp']) ? $this->xml_encode(date(DATE_RFC2822, $item['timestamp'])) : ''; $itemTimestamp = isset($item['timestamp']) ? $this->xml_encode(date(DATE_RFC2822, $item['timestamp'])) : '';
$itemContent = isset($item['content']) ? $this->xml_encode($this->sanitizeHtml($item['content'])) : ''; $itemContent = isset($item['content']) ? $this->xml_encode($this->sanitizeHtml($item['content'])) : '';
$items .= <<<EOD $items .= <<<EOD
<item> <item>
<title>{$itemTitle}</title> <title>{$itemTitle}</title>
<link>{$itemUri}</link> <link>{$itemUri}</link>
<guid isPermaLink="true">{$itemUri}</guid> <guid isPermaLink="true">{$itemUri}</guid>
<pubDate>{$itemTimestamp}</pubDate> <pubDate>{$itemTimestamp}</pubDate>
<description>{$itemContent}</description> <description>{$itemContent}</description>
<author>{$itemAuthor}</author> <author>{$itemAuthor}</author>
</item> </item>
EOD; EOD;
} }
/* Data are prepared, now let's begin the "MAGIE !!!" */ /* Data are prepared, now let's begin the "MAGIE !!!" */
$toReturn = '<?xml version="1.0" encoding="UTF-8"?>'; $toReturn = '<?xml version="1.0" encoding="UTF-8"?>';
$toReturn .= <<<EOD $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"> <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> <channel>
<title>{$title}</title> <title>{$title}</title>
<link>http{$https}://{$httpHost}{$httpInfo}/</link> <link>http{$https}://{$httpHost}{$httpInfo}/</link>
<description>{$title}</description> <description>{$title}</description>
<image url="{$icon}" title="{$title}" link="{$uri}"/> <image url="{$icon}" title="{$title}" link="{$uri}"/>
<atom:link rel="alternate" type="text/html" href="{$uri}" /> <atom:link rel="alternate" type="text/html" href="{$uri}" />
<atom:link rel="self" href="http{$https}://{$httpHost}{$serverRequestUri}" /> <atom:link rel="self" href="http{$https}://{$httpHost}{$serverRequestUri}" />
{$items} {$items}
</channel> </channel>
</rss> </rss>
EOD; EOD;
// Remove invalid non-UTF8 characters // Remove invalid non-UTF8 characters
ini_set('mbstring.substitute_character', 'none'); ini_set('mbstring.substitute_character', 'none');
$toReturn= mb_convert_encoding($toReturn, 'UTF-8', 'UTF-8'); $toReturn = mb_convert_encoding($toReturn, 'UTF-8', 'UTF-8');
return $toReturn; return $toReturn;
} }
public function display(){ public function display(){
$this $this
->setContentType('application/rss+xml; charset=UTF-8') ->setContentType('application/rss+xml; charset=UTF-8')
->callContentType(); ->callContentType();
return parent::display(); return parent::display();
} }
private function xml_encode($text) { private function xml_encode($text){
return htmlspecialchars($text, ENT_XML1); return htmlspecialchars($text, ENT_XML1);
} }
} }

View file

@ -3,18 +3,18 @@
* Plaintext * Plaintext
* Returns $this->items as raw php data. * Returns $this->items as raw php data.
*/ */
class PlaintextFormat extends FormatAbstract{ class PlaintextFormat extends FormatAbstract {
public function stringify(){ public function stringify(){
$items = $this->getItems(); $items = $this->getItems();
return print_r($items, true); return print_r($items, true);
} }
public function display(){ public function display(){
$this $this
->setContentType('text/plain;charset=' . $this->getCharset()) ->setContentType('text/plain;charset=' . $this->getCharset())
->callContentType(); ->callContentType();
return parent::display(); return parent::display();
} }
} }

View file

@ -2,103 +2,105 @@
require_once(__DIR__ . '/BridgeInterface.php'); require_once(__DIR__ . '/BridgeInterface.php');
class Bridge { class Bridge {
static protected $dirBridge; static protected $dirBridge;
public function __construct(){ public function __construct(){
throw new \LogicException('Please use ' . __CLASS__ . '::create for new object.'); throw new \LogicException('Please use ' . __CLASS__ . '::create for new object.');
} }
/** /**
* Checks if a bridge is an instantiable bridge. * Checks if a bridge is an instantiable bridge.
* @param string $nameBridge name of the bridge that you want to use * @param string $nameBridge name of the bridge that you want to use
* @return true if it is an instantiable bridge, false otherwise. * @return true if it is an instantiable bridge, false otherwise.
*/ */
static public function isInstantiable($nameBridge){ static public function isInstantiable($nameBridge){
$re = new ReflectionClass($nameBridge); $re = new ReflectionClass($nameBridge);
return $re->IsInstantiable(); return $re->IsInstantiable();
} }
/** /**
* Create a new bridge object * Create a new bridge object
* @param string $nameBridge Defined bridge name you want use * @param string $nameBridge Defined bridge name you want use
* @return Bridge object dedicated * @return Bridge object dedicated
*/ */
static public function create($nameBridge){ static public function create($nameBridge){
if(!preg_match('@^[A-Z][a-zA-Z0-9-]*$@', $nameBridge)){ if(!preg_match('@^[A-Z][a-zA-Z0-9-]*$@', $nameBridge)){
$message = <<<EOD $message = <<<EOD
'nameBridge' must start with one uppercase character followed or not by 'nameBridge' must start with one uppercase character followed or not by
alphanumeric or dash characters! alphanumeric or dash characters!
EOD; EOD;
throw new \InvalidArgumentException($message); throw new \InvalidArgumentException($message);
} }
$nameBridge = $nameBridge . 'Bridge'; $nameBridge = $nameBridge . 'Bridge';
$pathBridge = self::getDir() . $nameBridge . '.php'; $pathBridge = self::getDir() . $nameBridge . '.php';
if(!file_exists($pathBridge)){ if(!file_exists($pathBridge)){
throw new \Exception('The bridge you looking for does not exist. It should be at path ' . $pathBridge); throw new \Exception('The bridge you looking for does not exist.'
} . ' It should be at path '
. $pathBridge);
}
require_once $pathBridge; require_once $pathBridge;
if(Bridge::isInstantiable($nameBridge)){ if(Bridge::isInstantiable($nameBridge)){
return new $nameBridge(); return new $nameBridge();
} else { } else {
return false; return false;
} }
} }
static public function setDir($dirBridge){ static public function setDir($dirBridge){
if(!is_string($dirBridge)){ if(!is_string($dirBridge)){
throw new \InvalidArgumentException('Dir bridge must be a string.'); throw new \InvalidArgumentException('Dir bridge must be a string.');
} }
if(!file_exists($dirBridge)){ if(!file_exists($dirBridge)){
throw new \Exception('Dir bridge does not exist.'); throw new \Exception('Dir bridge does not exist.');
} }
self::$dirBridge = $dirBridge; self::$dirBridge = $dirBridge;
} }
static public function getDir(){ static public function getDir(){
$dirBridge = self::$dirBridge; $dirBridge = self::$dirBridge;
if(is_null($dirBridge)){ if(is_null($dirBridge)){
throw new \LogicException(__CLASS__ . ' class need to know bridge path !'); throw new \LogicException(__CLASS__ . ' class need to know bridge path !');
} }
return $dirBridge; return $dirBridge;
} }
/** /**
* Lists the available bridges. * Lists the available bridges.
* @return array List of the bridges * @return array List of the bridges
*/ */
static public function listBridges(){ static public function listBridges(){
$pathDirBridge = self::getDir(); $pathDirBridge = self::getDir();
$listBridge = array(); $listBridge = array();
$dirFiles = scandir($pathDirBridge); $dirFiles = scandir($pathDirBridge);
if($dirFiles !== false){ if($dirFiles !== false){
foreach($dirFiles as $fileName){ foreach($dirFiles as $fileName){
if(preg_match('@^([^.]+)Bridge\.php$@U', $fileName, $out)){ if(preg_match('@^([^.]+)Bridge\.php$@U', $fileName, $out)){
$listBridge[] = $out[1]; $listBridge[] = $out[1];
} }
} }
} }
return $listBridge; return $listBridge;
} }
static public function isWhitelisted($whitelist, $name){ static public function isWhitelisted($whitelist, $name){
if(in_array($name, $whitelist) if(in_array($name, $whitelist)
or in_array($name . '.php', $whitelist) || in_array($name . '.php', $whitelist)
or in_array($name . 'Bridge', $whitelist) // DEPRECATED || in_array($name . 'Bridge', $whitelist) // DEPRECATED
or in_array($name . 'Bridge.php', $whitelist) // DEPRECATED || in_array($name . 'Bridge.php', $whitelist) // DEPRECATED
or count($whitelist) === 1 and trim($whitelist[0]) === '*'){ || count($whitelist) === 1 and trim($whitelist[0]) === '*'){
return true; return true;
} else { } else {
return false; return false;
} }
} }
} }

View file

@ -2,444 +2,445 @@
require_once(__DIR__ . '/BridgeInterface.php'); require_once(__DIR__ . '/BridgeInterface.php');
abstract class BridgeAbstract implements BridgeInterface { abstract class BridgeAbstract implements BridgeInterface {
const NAME = 'Unnamed bridge'; const NAME = 'Unnamed bridge';
const URI = ''; const URI = '';
const DESCRIPTION = 'No description provided'; const DESCRIPTION = 'No description provided';
const MAINTAINER = 'No maintainer'; const MAINTAINER = 'No maintainer';
const PARAMETERS = array(); const PARAMETERS = array();
public $useProxy = true; public $useProxy = true;
protected $cache; protected $cache;
protected $items = array(); protected $items = array();
protected $inputs = array(); protected $inputs = array();
protected $queriedContext = ''; protected $queriedContext = '';
protected function returnError($message, $code){ protected function returnError($message, $code){
throw new \HttpException($message, $code); throw new \HttpException($message, $code);
} }
protected function returnClientError($message){ protected function returnClientError($message){
$this->returnError($message, 400); $this->returnError($message, 400);
} }
protected function returnServerError($message){ protected function returnServerError($message){
$this->returnError($message, 500); $this->returnError($message, 500);
} }
/** /**
* Return items stored in the bridge * Return items stored in the bridge
* @return mixed * @return mixed
*/ */
public function getItems(){ public function getItems(){
return $this->items; return $this->items;
} }
protected function validateTextValue($value, $pattern = null){ protected function validateTextValue($value, $pattern = null){
if(!is_null($pattern)){ if(!is_null($pattern)){
$filteredValue = filter_var($value, FILTER_VALIDATE_REGEXP, $filteredValue = filter_var($value
array('options' => array( , FILTER_VALIDATE_REGEXP
'regexp' => '/^' . $pattern . '$/' , array('options' => array(
)) 'regexp' => '/^' . $pattern . '$/'
); ))
} else { );
$filteredValue = filter_var($value); } else {
} $filteredValue = filter_var($value);
}
if($filteredValue === false)
return null; if($filteredValue === false)
return null;
return $filteredValue;
} return $filteredValue;
}
protected function validateNumberValue($value){
$filteredValue = filter_var($value, FILTER_VALIDATE_INT); protected function validateNumberValue($value){
$filteredValue = filter_var($value, FILTER_VALIDATE_INT);
if($filteredValue === false && !empty($value))
return null; if($filteredValue === false && !empty($value))
return null;
return $filteredValue;
} return $filteredValue;
}
protected function validateCheckboxValue($value){
$filteredValue = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); protected function validateCheckboxValue($value){
$filteredValue = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
if(is_null($filteredValue))
return null; if(is_null($filteredValue))
return null;
return $filteredValue;
} return $filteredValue;
}
protected function validateListValue($value, $expectedValues){
$filteredValue = filter_var($value); protected function validateListValue($value, $expectedValues){
$filteredValue = filter_var($value);
if($filteredValue === false)
return null; if($filteredValue === false)
return null;
if(!in_array($filteredValue, $expectedValues)){ // Check sub-values?
foreach($expectedValues as $subName => $subValue){ if(!in_array($filteredValue, $expectedValues)){ // Check sub-values?
if(is_array($subValue) && in_array($filteredValue, $subValue)) foreach($expectedValues as $subName => $subValue){
return $filteredValue; if(is_array($subValue) && in_array($filteredValue, $subValue))
} return $filteredValue;
return null; }
} return null;
}
return $filteredValue;
} return $filteredValue;
}
protected function validateData(&$data){
if(!is_array($data)) protected function validateData(&$data){
return false; if(!is_array($data))
return false;
foreach($data as $name=>$value){
$registered = false; foreach($data as $name => $value){
foreach(static::PARAMETERS as $context=>$set){ $registered = false;
if(array_key_exists($name,$set)){ foreach(static::PARAMETERS as $context => $set){
$registered = true; if(array_key_exists($name, $set)){
if(!isset($set[$name]['type'])){ $registered = true;
$set[$name]['type']='text'; if(!isset($set[$name]['type'])){
} $set[$name]['type'] = 'text';
}
switch($set[$name]['type']){
case 'number': switch($set[$name]['type']){
$data[$name] = $this->validateNumberValue($value); case 'number':
break; $data[$name] = $this->validateNumberValue($value);
case 'checkbox': break;
$data[$name] = $this->validateCheckboxValue($value); case 'checkbox':
break; $data[$name] = $this->validateCheckboxValue($value);
case 'list': break;
$data[$name] = $this->validateListValue($value, $set[$name]['values']); case 'list':
break; $data[$name] = $this->validateListValue($value, $set[$name]['values']);
default: break;
case 'text': default:
if(isset($set[$name]['pattern'])){ case 'text':
$data[$name] = $this->validateTextValue($value, $set[$name]['pattern']); if(isset($set[$name]['pattern'])){
} else { $data[$name] = $this->validateTextValue($value, $set[$name]['pattern']);
$data[$name] = $this->validateTextValue($value); } else {
} $data[$name] = $this->validateTextValue($value);
break; }
} break;
}
if(is_null($data[$name])){
echo 'Parameter \'' . $name . '\' is invalid!' . PHP_EOL; if(is_null($data[$name])){
return false; echo 'Parameter \'' . $name . '\' is invalid!' . PHP_EOL;
} return false;
} }
} }
}
if(!$registered)
return false; if(!$registered)
} return false;
}
return true;
} return true;
}
protected function setInputs(array $inputs, $queriedContext){
// Import and assign all inputs to their context protected function setInputs(array $inputs, $queriedContext){
foreach($inputs as $name => $value){ // Import and assign all inputs to their context
foreach(static::PARAMETERS as $context => $set){ foreach($inputs as $name => $value){
if(array_key_exists($name, static::PARAMETERS[$context])){ foreach(static::PARAMETERS as $context => $set){
$this->inputs[$context][$name]['value'] = $value; if(array_key_exists($name, static::PARAMETERS[$context])){
} $this->inputs[$context][$name]['value'] = $value;
} }
} }
}
// Apply default values to missing data
$contexts = array($queriedContext); // Apply default values to missing data
if(array_key_exists('global', static::PARAMETERS)){ $contexts = array($queriedContext);
$contexts[] = 'global'; if(array_key_exists('global', static::PARAMETERS)){
} $contexts[] = 'global';
}
foreach($contexts as $context){
foreach(static::PARAMETERS[$context] as $name => $properties){ foreach($contexts as $context){
if(isset($this->inputs[$context][$name]['value'])){ foreach(static::PARAMETERS[$context] as $name => $properties){
continue; if(isset($this->inputs[$context][$name]['value'])){
} continue;
}
$type = isset($properties['type']) ? $properties['type'] : 'text';
$type = isset($properties['type']) ? $properties['type'] : 'text';
switch($type){
case 'checkbox': switch($type){
if(!isset($properties['defaultValue'])){ case 'checkbox':
$this->inputs[$context][$name]['value'] = false; if(!isset($properties['defaultValue'])){
} else { $this->inputs[$context][$name]['value'] = false;
$this->inputs[$context][$name]['value'] = $properties['defaultValue']; } else {
} $this->inputs[$context][$name]['value'] = $properties['defaultValue'];
break; }
case 'list': break;
if(!isset($properties['defaultValue'])){ case 'list':
$firstItem = reset($properties['values']); if(!isset($properties['defaultValue'])){
if(is_array($firstItem)){ $firstItem = reset($properties['values']);
$firstItem = reset($firstItem); if(is_array($firstItem)){
} $firstItem = reset($firstItem);
$this->inputs[$context][$name]['value'] = $firstItem; }
} else { $this->inputs[$context][$name]['value'] = $firstItem;
$this->inputs[$context][$name]['value'] = $properties['defaultValue']; } else {
} $this->inputs[$context][$name]['value'] = $properties['defaultValue'];
break; }
default: break;
if(isset($properties['defaultValue'])){ default:
$this->inputs[$context][$name]['value'] = $properties['defaultValue']; if(isset($properties['defaultValue'])){
} $this->inputs[$context][$name]['value'] = $properties['defaultValue'];
break; }
} break;
} }
} }
}
// Copy global parameter values to the guessed context
if(array_key_exists('global', static::PARAMETERS)){ // Copy global parameter values to the guessed context
foreach(static::PARAMETERS['global'] as $name => $properties){ if(array_key_exists('global', static::PARAMETERS)){
if(isset($inputs[$name])){ foreach(static::PARAMETERS['global'] as $name => $properties){
$value = $inputs[$name]; if(isset($inputs[$name])){
} elseif (isset($properties['value'])){ $value = $inputs[$name];
$value = $properties['value']; } elseif (isset($properties['value'])){
} else { $value = $properties['value'];
continue; } else {
} continue;
$this->inputs[$queriedContext][$name]['value'] = $value; }
} $this->inputs[$queriedContext][$name]['value'] = $value;
} }
}
// Only keep guessed context parameters values
if(isset($this->inputs[$queriedContext])){ // Only keep guessed context parameters values
$this->inputs = array($queriedContext => $this->inputs[$queriedContext]); if(isset($this->inputs[$queriedContext])){
} else { $this->inputs = array($queriedContext => $this->inputs[$queriedContext]);
$this->inputs = array(); } else {
} $this->inputs = array();
} }
}
protected function getQueriedContext(array $inputs){
$queriedContexts=array(); protected function getQueriedContext(array $inputs){
foreach(static::PARAMETERS as $context=>$set){ $queriedContexts = array();
$queriedContexts[$context]=null; foreach(static::PARAMETERS as $context => $set){
foreach($set as $id=>$properties){ $queriedContexts[$context] = null;
if(isset($inputs[$id]) && !empty($inputs[$id])){ foreach($set as $id => $properties){
$queriedContexts[$context]=true; if(isset($inputs[$id]) && !empty($inputs[$id])){
}elseif(isset($properties['required']) && $queriedContexts[$context] = true;
$properties['required']===true){ } elseif(isset($properties['required'])
$queriedContexts[$context]=false; && $properties['required'] === true){
break; $queriedContexts[$context] = false;
} break;
} }
} }
}
if(array_key_exists('global',static::PARAMETERS) &&
$queriedContexts['global']===false){ if(array_key_exists('global', static::PARAMETERS)
return null; && $queriedContexts['global'] === false){
} return null;
unset($queriedContexts['global']); }
unset($queriedContexts['global']);
switch(array_sum($queriedContexts)){
case 0: switch(array_sum($queriedContexts)){
foreach($queriedContexts as $context=>$queried){ case 0:
if (is_null($queried)){ foreach($queriedContexts as $context => $queried){
return $context; if (is_null($queried)){
} return $context;
} }
return null; }
case 1: return array_search(true,$queriedContexts); return null;
default: return false; 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" * Defined datas with parameters depending choose bridge
* @param array array with expected bridge paramters * 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)){ public function setDatas(array $inputs){
$this->cache->prepare($inputs); if(!is_null($this->cache)){
$time = $this->cache->getTime(); $this->cache->prepare($inputs);
if($time !== false && (time() - $this->getCacheDuration() < $time)){ $time = $this->cache->getTime();
$this->items = $this->cache->loadData(); if($time !== false && (time() - $this->getCacheDuration() < $time)){
return; $this->items = $this->cache->loadData();
} return;
} }
}
if(empty(static::PARAMETERS)){
if(!empty($inputs)){ if(empty(static::PARAMETERS)){
$this->returnClientError('Invalid parameters value(s)'); if(!empty($inputs)){
} $this->returnClientError('Invalid parameters value(s)');
}
$this->collectData();
if(!is_null($this->cache)){ $this->collectData();
$this->cache->saveData($this->getItems()); if(!is_null($this->cache)){
} $this->cache->saveData($this->getItems());
return; }
} return;
}
if(!$this->validateData($inputs)){
$this->returnClientError('Invalid parameters value(s)'); if(!$this->validateData($inputs)){
} $this->returnClientError('Invalid parameters value(s)');
}
// Guess the paramter context from input data
$this->queriedContext = $this->getQueriedContext($inputs); // Guess the paramter context from input data
if(is_null($this->queriedContext)){ $this->queriedContext = $this->getQueriedContext($inputs);
$this->returnClientError('Required parameter(s) missing'); if(is_null($this->queriedContext)){
} elseif($this->queriedContext === false){ $this->returnClientError('Required parameter(s) missing');
$this->returnClientError('Mixed context parameters'); } elseif($this->queriedContext === false){
} $this->returnClientError('Mixed context parameters');
}
$this->setInputs($inputs, $this->queriedContext);
$this->setInputs($inputs, $this->queriedContext);
$this->collectData();
$this->collectData();
if(!is_null($this->cache)){
$this->cache->saveData($this->getItems()); if(!is_null($this->cache)){
} $this->cache->saveData($this->getItems());
} }
}
function getInput($input){
if(!isset($this->inputs[$this->queriedContext][$input]['value'])){ function getInput($input){
return null; if(!isset($this->inputs[$this->queriedContext][$input]['value'])){
} return null;
return $this->inputs[$this->queriedContext][$input]['value']; }
} return $this->inputs[$this->queriedContext][$input]['value'];
}
public function getName(){
return static::NAME; public function getName(){
} return static::NAME;
}
public function getURI(){
return static::URI; public function getURI(){
} return static::URI;
}
public function getCacheDuration(){
return 3600; public function getCacheDuration(){
} return 3600;
}
public function setCache(\CacheAbstract $cache){
$this->cache = $cache; public function setCache(\CacheAbstract $cache){
} $this->cache = $cache;
}
public function debugMessage($text){
if(!file_exists('DEBUG')) { public function debugMessage($text){
return; if(!file_exists('DEBUG')) {
} return;
}
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
$calling = $backtrace[2]; $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
$message = $calling['file'] . ':' $calling = $backtrace[2];
. $calling['line'] . ' class ' $message = $calling['file'] . ':'
. get_class($this) . '->' . $calling['line'] . ' class '
. $calling['function'] . ' - ' . get_class($this) . '->'
. $text; . $calling['function'] . ' - '
. $text;
error_log($message);
} error_log($message);
}
protected function getContents($url
, $use_include_path = false protected function getContents($url
, $context = null , $use_include_path = false
, $offset = 0 , $context = null
, $maxlen = null){ , $offset = 0
$contextOptions = array( , $maxlen = null){
'http' => array( $contextOptions = array(
'user_agent' => ini_get('user_agent') 'http' => array(
), 'user_agent' => ini_get('user_agent')
); )
);
if(defined('PROXY_URL') && $this->useProxy){
$contextOptions['http']['proxy'] = PROXY_URL; if(defined('PROXY_URL') && $this->useProxy){
$contextOptions['http']['request_fulluri'] = true; $contextOptions['http']['proxy'] = PROXY_URL;
$contextOptions['http']['request_fulluri'] = true;
if(is_null($context)){
$context = stream_context_create($contextOptions); if(is_null($context)){
} else { $context = stream_context_create($contextOptions);
$prevContext=$context; } else {
if(!stream_context_set_option($context, $contextOptions)){ $prevContext=$context;
$context = $prevContext; if(!stream_context_set_option($context, $contextOptions)){
} $context = $prevContext;
} }
} }
}
if(is_null($maxlen)){
$content = @file_get_contents($url, $use_include_path, $context, $offset); if(is_null($maxlen)){
} else { $content = @file_get_contents($url, $use_include_path, $context, $offset);
$content = @file_get_contents($url, $use_include_path, $context, $offset, $maxlen); } else {
} $content = @file_get_contents($url, $use_include_path, $context, $offset, $maxlen);
}
if($content === false)
$this->debugMessage('Cant\'t download ' . $url); if($content === false)
$this->debugMessage('Cant\'t download ' . $url);
return $content;
} return $content;
}
protected function getSimpleHTMLDOM($url
, $use_include_path = false protected function getSimpleHTMLDOM($url
, $context = null , $use_include_path = false
, $offset = 0 , $context = null
, $maxLen = null , $offset = 0
, $lowercase = true , $maxLen = null
, $forceTagsClosed = true , $lowercase = true
, $target_charset = DEFAULT_TARGET_CHARSET , $forceTagsClosed = true
, $stripRN = true , $target_charset = DEFAULT_TARGET_CHARSET
, $defaultBRText = DEFAULT_BR_TEXT , $stripRN = true
, $defaultSpanText = DEFAULT_SPAN_TEXT){ , $defaultBRText = DEFAULT_BR_TEXT
$content = $this->getContents($url, $use_include_path, $context, $offset, $maxLen); , $defaultSpanText = DEFAULT_SPAN_TEXT){
return str_get_html($content $content = $this->getContents($url, $use_include_path, $context, $offset, $maxLen);
, $lowercase return str_get_html($content
, $forceTagsClosed , $lowercase
, $target_charset , $forceTagsClosed
, $stripRN , $target_charset
, $defaultBRText , $stripRN
, $defaultSpanText); , $defaultBRText
} , $defaultSpanText);
}
/**
* Maintain locally cached versions of pages to avoid multiple downloads. /**
* @param url url to cache * Maintain locally cached versions of pages to avoid multiple downloads.
* @param duration duration of the cache file in seconds (default: 24h/86400s) * @param url url to cache
* @return content of the file as string * @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 public function getSimpleHTMLDOMCached($url
, $use_include_path = false , $duration = 86400
, $context = null , $use_include_path = false
, $offset = 0 , $context = null
, $maxLen = null , $offset = 0
, $lowercase = true , $maxLen = null
, $forceTagsClosed = true , $lowercase = true
, $target_charset = DEFAULT_TARGET_CHARSET , $forceTagsClosed = true
, $stripRN = true , $target_charset = DEFAULT_TARGET_CHARSET
, $defaultBRText = DEFAULT_BR_TEXT , $stripRN = true
, $defaultSpanText = DEFAULT_SPAN_TEXT){ , $defaultBRText = DEFAULT_BR_TEXT
$this->debugMessage('Caching url ' . $url . ', duration ' . $duration); , $defaultSpanText = DEFAULT_SPAN_TEXT){
$this->debugMessage('Caching url ' . $url . ', duration ' . $duration);
$filepath = __DIR__ . '/../cache/pages/' . sha1($url) . '.cache';
$this->debugMessage('Cache file ' . $filepath); $filepath = __DIR__ . '/../cache/pages/' . sha1($url) . '.cache';
$this->debugMessage('Cache file ' . $filepath);
if(file_exists($filepath) && filectime($filepath) < time() - $duration){
unlink ($filepath); if(file_exists($filepath) && filectime($filepath) < time() - $duration){
$this->debugMessage('Cached file deleted: ' . $filepath); unlink ($filepath);
} $this->debugMessage('Cached file deleted: ' . $filepath);
}
if(file_exists($filepath)){
$this->debugMessage('Loading cached file ' . $filepath); if(file_exists($filepath)){
touch($filepath); $this->debugMessage('Loading cached file ' . $filepath);
$content = file_get_contents($filepath); touch($filepath);
} else { $content = file_get_contents($filepath);
$this->debugMessage('Caching ' . $url . ' to ' . $filepath); } else {
$dir = substr($filepath, 0, strrpos($filepath, '/')); $this->debugMessage('Caching ' . $url . ' to ' . $filepath);
$dir = substr($filepath, 0, strrpos($filepath, '/'));
if(!is_dir($dir)){
$this->debugMessage('Creating directory ' . $dir); if(!is_dir($dir)){
mkdir($dir, 0777, true); $this->debugMessage('Creating directory ' . $dir);
} mkdir($dir, 0777, true);
}
$content = $this->getContents($url, $use_include_path, $context, $offset, $maxLen);
if($content !== false){ $content = $this->getContents($url, $use_include_path, $context, $offset, $maxLen);
file_put_contents($filepath, $content); if($content !== false){
} file_put_contents($filepath, $content);
} }
}
return str_get_html($content
, $lowercase return str_get_html($content
, $forceTagsClosed , $lowercase
, $target_charset , $forceTagsClosed
, $stripRN , $target_charset
, $defaultBRText , $stripRN
, $defaultSpanText); , $defaultBRText
} , $defaultSpanText);
}
} }

View file

@ -1,7 +1,7 @@
<?php <?php
interface BridgeInterface { interface BridgeInterface {
public function collectData(); public function collectData();
public function getCacheDuration(); public function getCacheDuration();
public function getName(); public function getName();
public function getURI(); public function getURI();
} }

View file

@ -1,91 +1,93 @@
<?php <?php
require_once(__DIR__ . '/CacheInterface.php'); require_once(__DIR__ . '/CacheInterface.php');
class Cache{ class Cache {
static protected $dirCache; static protected $dirCache;
public function __construct(){ public function __construct(){
throw new \LogicException('Please use ' . __CLASS__ . '::create for new object.'); throw new \LogicException('Please use ' . __CLASS__ . '::create for new object.');
} }
static public function create($nameCache){ static public function create($nameCache){
if( !static::isValidNameCache($nameCache) ){ if(!static::isValidNameCache($nameCache)){
throw new \InvalidArgumentException('Name cache must be at least one uppercase follow or not by alphanumeric or dash characters.'); 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) ){ if(!file_exists($pathCache)){
throw new \Exception('The cache you looking for does not exist.'); 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){ static public function setDir($dirCache){
if( !is_string($dirCache) ){ if(!is_string($dirCache)){
throw new \InvalidArgumentException('Dir cache must be a string.'); throw new \InvalidArgumentException('Dir cache must be a string.');
} }
if( !file_exists($dirCache) ){ if(!file_exists($dirCache)){
throw new \Exception('Dir cache does not exist.'); throw new \Exception('Dir cache does not exist.');
} }
self::$dirCache = $dirCache; self::$dirCache = $dirCache;
} }
static public function getDir(){ static public function getDir(){
$dirCache = self::$dirCache; $dirCache = self::$dirCache;
if( is_null($dirCache) ){ if(is_null($dirCache)){
throw new \LogicException(__CLASS__ . ' class need to know cache path !'); throw new \LogicException(__CLASS__ . ' class need to know cache path !');
} }
return $dirCache; return $dirCache;
} }
static public function isValidNameCache($nameCache){ static public function isValidNameCache($nameCache){
return preg_match('@^[A-Z][a-zA-Z0-9-]*$@', $nameCache); return preg_match('@^[A-Z][a-zA-Z0-9-]*$@', $nameCache);
} }
static public function utf8_encode_deep(&$input) { static public function utf8_encode_deep(&$input){
if (is_string($input)) { if (is_string($input)){
$input = utf8_encode($input); $input = utf8_encode($input);
} else if (is_array($input)) { } elseif(is_array($input)){
foreach ($input as &$value) { foreach($input as &$value){
Cache::utf8_encode_deep($value); Cache::utf8_encode_deep($value);
} }
unset($value); unset($value);
} else if (is_object($input)) { } elseif(is_object($input)){
$vars = array_keys(get_object_vars($input)); $vars = array_keys(get_object_vars($input));
foreach ($vars as $var) { foreach($vars as $var){
Cache::utf8_encode_deep($input->$var); Cache::utf8_encode_deep($input->$var);
} }
} }
} }
static public function purge() { static public function purge(){
$cacheTimeLimit = time() - 60*60*24 ; $cacheTimeLimit = time() - 60*60*24;
$cachePath = 'cache'; $cachePath = 'cache';
if(file_exists($cachePath)) { if(file_exists($cachePath)){
$cacheIterator = new RecursiveIteratorIterator( $cacheIterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($cachePath), new RecursiveDirectoryIterator($cachePath),
RecursiveIteratorIterator::CHILD_FIRST RecursiveIteratorIterator::CHILD_FIRST
); );
foreach ($cacheIterator as $cacheFile) {
if (in_array($cacheFile->getBasename(), array('.', '..'))) foreach($cacheIterator as $cacheFile){
continue; if(in_array($cacheFile->getBasename(), array('.', '..')))
elseif ($cacheFile->isFile()) { continue;
if( filemtime($cacheFile->getPathname()) < $cacheTimeLimit ) elseif($cacheFile->isFile()){
unlink( $cacheFile->getPathname() ); if(filemtime($cacheFile->getPathname()) < $cacheTimeLimit)
} unlink($cacheFile->getPathname());
} }
}
} }
} }

View file

@ -1,11 +1,11 @@
<?php <?php
require_once(__DIR__ . '/CacheInterface.php'); require_once(__DIR__ . '/CacheInterface.php');
abstract class CacheAbstract implements CacheInterface{ abstract class CacheAbstract implements CacheInterface {
protected $param; protected $param;
public function prepare(array $param){ public function prepare(array $param){
$this->param = $param; $this->param = $param;
return $this; return $this;
} }
} }

View file

@ -1,6 +1,6 @@
<?php <?php
interface CacheInterface{ interface CacheInterface {
public function loadData(); public function loadData();
public function saveData($datas); public function saveData($datas);
public function getTime(); public function getTime();
} }

View file

@ -6,56 +6,55 @@ class HttpException extends \Exception{}
*/ */
class Http{ class Http{
/** /**
* Return message corresponding to Http code * Return message corresponding to Http code
*/ */
static public function getMessageForCode($code){ static public function getMessageForCode($code){
$codes = self::getCodes(); $codes = self::getCodes();
if( isset($codes[$code]) ){ if(isset($codes[$code]))
return $codes[$code]; return $codes[$code];
}
return ''; return '';
} }
/** /**
* List of common Http code * List of common Http code
*/ */
static public function getCodes(){ static public function getCodes(){
return array( return array(
200 => 'OK', 200 => 'OK',
201 => 'Created', 201 => 'Created',
202 => 'Accepted', 202 => 'Accepted',
300 => 'Multiple Choices', 300 => 'Multiple Choices',
301 => 'Moved Permanently', 301 => 'Moved Permanently',
302 => 'Moved Temporarily', 302 => 'Moved Temporarily',
307 => 'Temporary Redirect', 307 => 'Temporary Redirect',
310 => 'Too many Redirects', 310 => 'Too many Redirects',
400 => 'Bad Request', 400 => 'Bad Request',
401 => 'Unauthorized', 401 => 'Unauthorized',
402 => 'Payment Required', 402 => 'Payment Required',
403 => 'Forbidden', 403 => 'Forbidden',
404 => 'Not Found', 404 => 'Not Found',
405 => 'Method Not', 405 => 'Method Not',
406 => 'Not Acceptable', 406 => 'Not Acceptable',
407 => 'Proxy Authentication Required', 407 => 'Proxy Authentication Required',
408 => 'Request Time-out', 408 => 'Request Time-out',
409 => 'Conflict', 409 => 'Conflict',
410 => 'Gone', 410 => 'Gone',
411 => 'Length Required', 411 => 'Length Required',
412 => 'Precondition Failed', 412 => 'Precondition Failed',
413 => 'Request Entity Too Large', 413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long', 414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type', 415 => 'Unsupported Media Type',
416 => 'Requested range unsatisfiable', 416 => 'Requested range unsatisfiable',
417 => 'Expectation failed', 417 => 'Expectation failed',
500 => 'Internal Server Error', 500 => 'Internal Server Error',
501 => 'Not Implemented', 501 => 'Not Implemented',
502 => 'Bad Gateway', 502 => 'Bad Gateway',
503 => 'Service Unavailable', 503 => 'Service Unavailable',
504 => 'Gateway Time-out', 504 => 'Gateway Time-out',
508 => 'Loop detected', 508 => 'Loop detected',
); );
} }
} }

View file

@ -2,175 +2,178 @@
require_once(__DIR__ . '/BridgeInterface.php'); require_once(__DIR__ . '/BridgeInterface.php');
abstract class FeedExpander extends BridgeAbstract { abstract class FeedExpander extends BridgeAbstract {
private $name; private $name;
private $uri; private $uri;
private $description; private $description;
public function collectExpandableDatas($url, $maxItems = -1){ public function collectExpandableDatas($url, $maxItems = -1){
if(empty($url)){ if(empty($url)){
$this->returnServerError('There is no $url for this RSS expander'); $this->returnServerError('There is no $url for this RSS expander');
} }
$this->debugMessage('Loading from ' . $url); $this->debugMessage('Loading from ' . $url);
/* Notice we do not use cache here on purpose: /* Notice we do not use cache here on purpose:
* we want a fresh view of the RSS stream each time * we want a fresh view of the RSS stream each time
*/ */
$content = $this->getContents($url) $content = $this->getContents($url)
or $this->returnServerError('Could not request ' . $url); or $this->returnServerError('Could not request ' . $url);
$rssContent = simplexml_load_string($content); $rssContent = simplexml_load_string($content);
$this->debugMessage('Detecting feed format/version'); $this->debugMessage('Detecting feed format/version');
if(isset($rssContent->channel[0])){ if(isset($rssContent->channel[0])){
$this->debugMessage('Detected RSS format'); $this->debugMessage('Detected RSS format');
if(isset($rssContent->item[0])){ if(isset($rssContent->item[0])){
$this->debugMessage('Detected RSS 1.0 format'); $this->debugMessage('Detected RSS 1.0 format');
$this->collect_RSS_1_0_data($rssContent, $maxItems); $this->collect_RSS_1_0_data($rssContent, $maxItems);
} else { } else {
$this->debugMessage('Detected RSS 0.9x or 2.0 format'); $this->debugMessage('Detected RSS 0.9x or 2.0 format');
$this->collect_RSS_2_0_data($rssContent, $maxItems); $this->collect_RSS_2_0_data($rssContent, $maxItems);
} }
} elseif(isset($rssContent->entry[0])){ } elseif(isset($rssContent->entry[0])){
$this->debugMessage('Detected ATOM format'); $this->debugMessage('Detected ATOM format');
$this->collect_ATOM_data($rssContent, $maxItems); $this->collect_ATOM_data($rssContent, $maxItems);
} else { } else {
$this->debugMessage('Unknown feed format/version'); $this->debugMessage('Unknown feed format/version');
$this->returnServerError('The feed format is unknown!'); $this->returnServerError('The feed format is unknown!');
} }
} }
protected function collect_RSS_1_0_data($rssContent, $maxItems){ protected function collect_RSS_1_0_data($rssContent, $maxItems){
$this->load_RSS_2_0_feed_data($rssContent->channel[0]); $this->load_RSS_2_0_feed_data($rssContent->channel[0]);
foreach($rssContent->item as $item){ foreach($rssContent->item as $item){
$this->debugMessage('parsing item ' . var_export($item, true)); $this->debugMessage('parsing item ' . var_export($item, true));
$this->items[] = $this->parseItem($item); $this->items[] = $this->parseItem($item);
if($maxItems !== -1 && count($this->items) >= $maxItems) break; if($maxItems !== -1 && count($this->items) >= $maxItems) break;
} }
} }
protected function collect_RSS_2_0_data($rssContent, $maxItems){ protected function collect_RSS_2_0_data($rssContent, $maxItems){
$rssContent = $rssContent->channel[0]; $rssContent = $rssContent->channel[0];
$this->debugMessage('RSS content is ===========\n' . var_export($rssContent, true) . '==========='); $this->debugMessage('RSS content is ===========\n'
$this->load_RSS_2_0_feed_data($rssContent); . var_export($rssContent, true)
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_RSS_2_0_feed_data($rssContent);
$this->load_ATOM_feed_data($content); foreach($rssContent->item as $item){
foreach($content->entry as $item){ $this->debugMessage('parsing item ' . var_export($item, true));
$this->debugMessage('parsing item ' . var_export($item, true)); $this->items[] = $this->parseItem($item);
$this->items[] = $this->parseItem($item); if($maxItems !== -1 && count($this->items) >= $maxItems) break;
if($maxItems !== -1 && count($this->items) >= $maxItems) break; }
} }
}
protected function RSS_2_0_time_to_timestamp($item){ protected function collect_ATOM_data($content, $maxItems){
return DateTime::createFromFormat('D, d M Y H:i:s e', $item->pubDate)->getTimestamp(); $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;
}
}
// TODO set title, link, description, language, and so on protected function RSS_2_0_time_to_timestamp($item){
protected function load_RSS_2_0_feed_data($rssContent){ return DateTime::createFromFormat('D, d M Y H:i:s e', $item->pubDate)->getTimestamp();
$this->name = trim($rssContent->title); }
$this->uri = trim($rssContent->link);
$this->description = trim($rssContent->description);
}
protected function load_ATOM_feed_data($content){ // TODO set title, link, description, language, and so on
$this->name = $content->title; protected function load_RSS_2_0_feed_data($rssContent){
$this->name = trim($rssContent->title);
$this->uri = trim($rssContent->link);
$this->description = trim($rssContent->description);
}
// Find best link (only one, or first of 'alternate') protected function load_ATOM_feed_data($content){
if(!isset($content->link)){ $this->name = $content->title;
$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)) // Find best link (only one, or first of 'alternate')
$this->description = $content->subtitle; 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;
}
}
}
protected function parseATOMItem($feedItem){ if(isset($content->subtitle))
$item = array(); $this->description = $content->subtitle;
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){ protected function parseATOMItem($feedItem){
$item = array(); $item = array();
if(isset($feedItem->link)) $item['uri'] = $feedItem->link; if(isset($feedItem->id)) $item['uri'] = $feedItem->id;
if(isset($feedItem->title)) $item['title'] = $feedItem->title; if(isset($feedItem->title)) $item['title'] = $feedItem->title;
// rss 0.91 doesn't support timestamps if(isset($feedItem->updated)) $item['timestamp'] = strtotime($feedItem->updated);
// rss 0.91 doesn't support authors if(isset($feedItem->author)) $item['author'] = $feedItem->author->name;
if(isset($feedItem->description)) $item['content'] = $feedItem->description; if(isset($feedItem->content)) $item['content'] = $feedItem->content;
return $item; return $item;
} }
protected function parseRSS_1_0_Item($feedItem){ protected function parseRSS_0_9_1_Item($feedItem){
// 1.0 adds optional elements around the 0.91 standard $item = array();
$item = $this->parseRSS_0_9_1_Item($feedItem); 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;
}
$namespaces = $feedItem->getNamespaces(true); protected function parseRSS_1_0_Item($feedItem){
if(isset($namespaces['dc'])){ // 1.0 adds optional elements around the 0.91 standard
$dc = $feedItem->children($namespaces['dc']); $item = $this->parseRSS_0_9_1_Item($feedItem);
if(isset($dc->date)) $item['timestamp'] = strtotime($dc->date);
if(isset($dc->creator)) $item['author'] = $dc->creator;
}
return $item; $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;
}
protected function parseRSS_2_0_Item($feedItem){ return $item;
// Primary data is compatible to 0.91 with some additional data }
$item = $this->parseRSS_0_9_1_Item($feedItem);
$namespaces = $feedItem->getNamespaces(true); protected function parseRSS_2_0_Item($feedItem){
if(isset($namespaces['dc'])) $dc = $feedItem->children($namespaces['dc']); // Primary data is compatible to 0.91 with some additional data
$item = $this->parseRSS_0_9_1_Item($feedItem);
if(isset($feedItem->pubDate)){ $namespaces = $feedItem->getNamespaces(true);
$item['timestamp'] = strtotime($feedItem->pubDate); if(isset($namespaces['dc'])) $dc = $feedItem->children($namespaces['dc']);
} 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;
}
/** if(isset($feedItem->pubDate)){
* Method should return, from a source RSS item given by lastRSS, one of our Items objects $item['timestamp'] = strtotime($feedItem->pubDate);
* @param $item the input rss item } elseif(isset($dc->date)){
* @return a RSS-Bridge Item, with (hopefully) the whole content) $item['timestamp'] = strtotime($dc->date);
*/ }
abstract protected function parseItem($item); if(isset($feedItem->author)){
$item['author'] = $feedItem->author;
} elseif(isset($dc->creator)){
$item['author'] = $dc->creator;
}
return $item;
}
public function getURI(){ /**
return $this->uri; * 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 getName(){ public function getURI(){
return $this->name; return $this->uri;
} }
public function getDescription(){ public function getName(){
return $this->description; return $this->name;
} }
public function getDescription(){
return $this->description;
}
} }

View file

@ -1,72 +1,73 @@
<?php <?php
require_once(__DIR__ . '/FormatInterface.php'); require_once(__DIR__ . '/FormatInterface.php');
class Format{ class Format {
static protected $dirFormat; static protected $dirFormat;
public function __construct(){ public function __construct(){
throw new \LogicException('Please use ' . __CLASS__ . '::create for new object.'); throw new \LogicException('Please use ' . __CLASS__ . '::create for new object.');
} }
static public function create($nameFormat){ static public function create($nameFormat){
if( !preg_match('@^[A-Z][a-zA-Z]*$@', $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.'); throw new \InvalidArgumentException('Name format must be at least '
} . 'one uppercase follow or not by alphabetic characters.');
}
$nameFormat=$nameFormat.'Format'; $nameFormat = $nameFormat . 'Format';
$pathFormat = self::getDir() . $nameFormat . '.php'; $pathFormat = self::getDir() . $nameFormat . '.php';
if( !file_exists($pathFormat) ){ if(!file_exists($pathFormat)){
throw new \Exception('The format you looking for does not exist.'); 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){ static public function setDir($dirFormat){
if( !is_string($dirFormat) ){ if(!is_string($dirFormat)){
throw new \InvalidArgumentException('Dir format must be a string.'); throw new \InvalidArgumentException('Dir format must be a string.');
} }
if( !file_exists($dirFormat) ){ if(!file_exists($dirFormat)){
throw new \Exception('Dir format does not exist.'); throw new \Exception('Dir format does not exist.');
} }
self::$dirFormat = $dirFormat; self::$dirFormat = $dirFormat;
} }
static public function getDir(){ static public function getDir(){
$dirFormat = self::$dirFormat; $dirFormat = self::$dirFormat;
if( is_null($dirFormat) ){ if(is_null($dirFormat)){
throw new \LogicException(__CLASS__ . ' class need to know format path !'); 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 * Read format dir and catch informations about each format depending annotation
* @return array Informations about each format * @return array Informations about each format
*/ */
static public function searchInformation(){ static public function searchInformation(){
$pathDirFormat = self::getDir(); $pathDirFormat = self::getDir();
$listFormat = array(); $listFormat = array();
$searchCommonPattern = array('name'); $searchCommonPattern = array('name');
$dirFiles = scandir($pathDirFormat); $dirFiles = scandir($pathDirFormat);
if( $dirFiles !== false ){ if($dirFiles !== false){
foreach( $dirFiles as $fileName ){ foreach($dirFiles as $fileName){
if( preg_match('@^([^.]+)Format\.php$@U', $fileName, $out) ){ // Is PHP file ? if(preg_match('@^([^.]+)Format\.php$@U', $fileName, $out)){ // Is PHP file ?
$listFormat[] = $out[1]; $listFormat[] = $out[1];
} }
} }
} }
return $listFormat; return $listFormat;
} }
} }

View file

@ -1,107 +1,107 @@
<?php <?php
require_once(__DIR__ . '/FormatInterface.php'); require_once(__DIR__ . '/FormatInterface.php');
abstract class FormatAbstract implements FormatInterface{ abstract class FormatAbstract implements FormatInterface {
const DEFAULT_CHARSET = 'UTF-8'; const DEFAULT_CHARSET = 'UTF-8';
protected protected
$contentType, $contentType,
$charset, $charset,
$items, $items,
$extraInfos $extraInfos
; ;
public function setCharset($charset){ public function setCharset($charset){
$this->charset = $charset; $this->charset = $charset;
return $this; return $this;
} }
public function getCharset(){ public function getCharset(){
$charset = $this->charset; $charset = $this->charset;
return is_null($charset) ? self::DEFAULT_CHARSET : $charset; return is_null($charset) ? self::DEFAULT_CHARSET : $charset;
} }
protected function setContentType($contentType){ protected function setContentType($contentType){
$this->contentType = $contentType; $this->contentType = $contentType;
return $this; return $this;
} }
protected function callContentType(){ protected function callContentType(){
header('Content-Type: ' . $this->contentType); header('Content-Type: ' . $this->contentType);
} }
public function display(){ public function display(){
echo $this->stringify(); echo $this->stringify();
return $this; return $this;
} }
public function setItems(array $items){ public function setItems(array $items){
$this->items = array_map(array($this, 'array_trim'), $items); $this->items = array_map(array($this, 'array_trim'), $items);
return $this; return $this;
} }
public function getItems(){ public function getItems(){
if(!is_array($this->items)) if(!is_array($this->items))
throw new \LogicException('Feed the ' . get_class($this) . ' with "setItems" method before !'); throw new \LogicException('Feed the ' . get_class($this) . ' with "setItems" method before !');
return $this->items; return $this->items;
} }
/** /**
* Define common informations can be required by formats and set default value for unknow values * 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 !!!) * @param array $extraInfos array with know informations (there isn't merge !!!)
* @return this * @return this
*/ */
public function setExtraInfos(array $extraInfos = array()){ public function setExtraInfos(array $extraInfos = array()){
foreach(array('name', 'uri') as $infoName){ foreach(array('name', 'uri') as $infoName){
if( !isset($extraInfos[$infoName]) ){ if( !isset($extraInfos[$infoName]) ){
$extraInfos[$infoName] = ''; $extraInfos[$infoName] = '';
} }
} }
$this->extraInfos = $extraInfos; $this->extraInfos = $extraInfos;
return $this; return $this;
} }
/** /**
* Return extra infos * Return extra infos
* @return array See "setExtraInfos" detail method to know what extra are disponibles * @return array See "setExtraInfos" detail method to know what extra are disponibles
*/ */
public function getExtraInfos(){ public function getExtraInfos(){
if( is_null($this->extraInfos) ){ // No extra info ? if( is_null($this->extraInfos) ){ // No extra info ?
$this->setExtraInfos(); // Define with default value $this->setExtraInfos(); // Define with default value
} }
return $this->extraInfos; return $this->extraInfos;
} }
/** /**
* Sanitized html while leaving it functionnal. * Sanitized html while leaving it functionnal.
* The aim is to keep html as-is (with clickable hyperlinks) * The aim is to keep html as-is (with clickable hyperlinks)
* while reducing annoying and potentially dangerous things. * while reducing annoying and potentially dangerous things.
* Yes, I know sanitizing HTML 100% is an impossible task. * Yes, I know sanitizing HTML 100% is an impossible task.
* Maybe we'll switch to http://htmlpurifier.org/ * Maybe we'll switch to http://htmlpurifier.org/
* or http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/index.php * or http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/index.php
*/ */
protected function sanitizeHtml($html) protected function sanitizeHtml($html)
{ {
$html = str_replace('<script','<&zwnj;script',$html); // Disable scripts, but leave them visible. $html = str_replace('<script','<&zwnj;script',$html); // Disable scripts, but leave them visible.
$html = str_replace('<iframe','<&zwnj;iframe',$html); $html = str_replace('<iframe','<&zwnj;iframe',$html);
$html = str_replace('<link','<&zwnj;link',$html); $html = str_replace('<link','<&zwnj;link',$html);
// We leave alone object and embed so that videos can play in RSS readers. // We leave alone object and embed so that videos can play in RSS readers.
return $html; return $html;
} }
protected function array_trim($elements){ protected function array_trim($elements){
foreach($elements as $key => $value){ foreach($elements as $key => $value){
if(is_string($value)) if(is_string($value))
$elements[$key] = trim($value); $elements[$key] = trim($value);
} }
return $elements; return $elements;
} }
} }

View file

@ -1,6 +1,6 @@
<?php <?php
interface FormatInterface{ interface FormatInterface{
public function stringify(); public function stringify();
public function display(); public function display();
public function setItems(array $bridges); public function setItems(array $bridges);
} }

View file

@ -1,9 +1,8 @@
<?php <?php
class HTMLUtils { class HTMLUtils {
public static function displayBridgeCard($bridgeName, $formats, $isActive = true){ public static function displayBridgeCard($bridgeName, $formats, $isActive = true){
$bridgeElement = Bridge::create($bridgeName); $bridgeElement = Bridge::create($bridgeName);
$bridgeClass=$bridgeName.'Bridge'; $bridgeClass = $bridgeName . 'Bridge';
if($bridgeElement == false) if($bridgeElement == false)
return ""; return "";
@ -22,15 +21,30 @@ class HTMLUtils {
CARD; CARD;
// If we don't have any parameter for the bridge, we print a generic form to load it. // 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); $card .= HTMLUtils::getFormHeader($bridgeName);
if ($isActive){ if($isActive){
if(defined('PROXY_URL') && PROXY_BYBRIDGE){ if(defined('PROXY_URL') && PROXY_BYBRIDGE){
$idArg = 'arg-' . urlencode($bridgeName) . '-' . urlencode('proxyoff') . '-' . urlencode('_noproxy'); $idArg = 'arg-'
$card .= '<input id="' . $idArg . '" type="checkbox" name="_noproxy" />' . PHP_EOL; . urlencode($bridgeName)
$card .= '<label for="' .$idArg. '">Disable proxy ('.((defined('PROXY_NAME') && PROXY_NAME)?PROXY_NAME:PROXY_URL).')</label><br />' . PHP_EOL; . '-'
. 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); $card .= HTMLUtils::getHelperButtonsFormat($formats);
@ -59,7 +73,7 @@ CARD;
$card .= HTMLUtils::getFormHeader($bridgeName); $card .= HTMLUtils::getFormHeader($bridgeName);
foreach($parameter as $id=>$inputEntry) { foreach($parameter as $id=>$inputEntry){
$additionalInfoString = ''; $additionalInfoString = '';
if(isset($inputEntry['required']) && $inputEntry['required'] === true) if(isset($inputEntry['required']) && $inputEntry['required'] === true)
@ -77,55 +91,140 @@ CARD;
if(!isset($inputEntry['defaultValue'])) if(!isset($inputEntry['defaultValue']))
$inputEntry['defaultValue'] = ''; $inputEntry['defaultValue'] = '';
$idArg = 'arg-' . urlencode($bridgeName) . '-' . urlencode($parameterName) . '-' . urlencode($id); $idArg = 'arg-'
$card .= '<label for="' . $idArg . '">' . $inputEntry['name'] . ' : </label>' . PHP_EOL; . urlencode($bridgeName)
. '-'
. urlencode($parameterName)
. '-'
. urlencode($id);
if(!isset($inputEntry['type']) || $inputEntry['type'] == 'text') { $card .= '<label for="'
$card .= '<input ' . $additionalInfoString . ' id="' . $idArg . '" type="text" value="' . $inputEntry['defaultValue'] . '" placeholder="' . $inputEntry['exampleValue'] . '" name="' . $id . '" /><br />' . PHP_EOL; . $idArg
} else if($inputEntry['type'] == 'number') { . '">'
$card .= '<input ' . $additionalInfoString . ' id="' . $idArg . '" type="number" value="' . $inputEntry['defaultValue'] . '" placeholder="' . $inputEntry['exampleValue'] . '" name="' . $id . '" /><br />' . PHP_EOL; . $inputEntry['name']
} else if($inputEntry['type'] == 'list') { . ' : </label>'
$card .= '<select ' . $additionalInfoString . ' id="' . $idArg . '" name="' . $id . '" >'; . PHP_EOL;
foreach($inputEntry['values'] as $name=>$value) { if(!isset($inputEntry['type']) || $inputEntry['type'] == 'text'){
if(is_array($value)){ $card .= '<input '
$card.='<optgroup label="'.htmlentities($name).'">'; . $additionalInfoString
foreach($value as $subname=>$subvalue){ . ' id="'
if($inputEntry['defaultValue'] === $subname || $inputEntry['defaultValue'] === $subvalue) . $idArg
$card .= '<option value="' . $subvalue . '" selected>' . $subname . '</option>'; . '" type="text" value="'
else . $inputEntry['defaultValue']
$card .= '<option value="' . $subvalue . '">' . $subname . '</option>'; . '" placeholder="'
} . $inputEntry['exampleValue']
$card.='</optgroup>'; . '" name="'
}else{ . $id
if($inputEntry['defaultValue'] === $name || $inputEntry['defaultValue'] === $value) . '" /><br />'
$card .= '<option value="' . $value . '" selected>' . $name . '</option>'; . PHP_EOL;
else } elseif($inputEntry['type'] == 'number'){
$card .= '<option value="' . $value . '">' . $name . '</option>'; $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 >'; $card .= '</select><br >';
} else if($inputEntry['type'] == 'checkbox') { } elseif($inputEntry['type'] == 'checkbox'){
if($inputEntry['defaultValue'] === 'checked') 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 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){ if(defined('PROXY_URL') && PROXY_BYBRIDGE){
$idArg = 'arg-' . urlencode($bridgeName) . '-' . urlencode('proxyoff') . '-' . urlencode('_noproxy'); $idArg = 'arg-'
$card .= '<input id="' . $idArg . '" type="checkbox" name="_noproxy" />' . PHP_EOL; . urlencode($bridgeName)
$card .= '<label for="' .$idArg. '">Disable proxy ('.((defined('PROXY_NAME') && PROXY_NAME)?PROXY_NAME:PROXY_URL).')</label><br />' . PHP_EOL; . '-'
} . 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); $card .= HTMLUtils::getHelperButtonsFormat($formats);
} else { } else {
$card .= '<span style="font-weight: bold;">Inactive</span>'; $card .= '<span style="font-weight: bold;">Inactive</span>';
} }
$card .= '</form>' . PHP_EOL; $card .= '</form>' . PHP_EOL;
} }
@ -138,8 +237,13 @@ CARD;
private static function getHelperButtonsFormat($formats){ private static function getHelperButtonsFormat($formats){
$buttons = ''; $buttons = '';
foreach( $formats as $name){ foreach($formats as $name){
$buttons .= '<button type="submit" name="format" value="' . $name . '">' . $name . '</button>' . PHP_EOL; $buttons .= '<button type="submit" name="format" value="'
. $name
. '">'
. $name
. '</button>'
. PHP_EOL;
} }
return $buttons; return $buttons;
@ -164,22 +268,24 @@ class HTMLSanitizer {
public static $KEPT_ATTRIBUTES = ["title", "href", "src"]; public static $KEPT_ATTRIBUTES = ["title", "href", "src"];
public static $ONLY_TEXT = []; public static $ONLY_TEXT = [];
public function __construct($tags_to_remove = null, $kept_attributes = null, $only_keep_text = null) { public function __construct($tags_to_remove = null
$this->tagsToRemove = $tags_to_remove == null ? HTMLSanitizer::$DEFAULT_CLEAR_TAGS : $tags_to_remove; , $kept_attributes = null
$this->keptAttributes = $kept_attributes == null ? HTMLSanitizer::$KEPT_ATTRIBUTES : $kept_attributes; , $only_keep_text = null){
$this->onlyKeepText = $only_keep_text == null ? HTMLSanitizer::$ONLY_TEXT : $only_keep_text; $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); $htmlContent = str_get_html($textToSanitize);
foreach($htmlContent->find('*[!b38fd2b1fe7f4747d6b1c1254ccd055e]') as $element) { foreach($htmlContent->find('*[!b38fd2b1fe7f4747d6b1c1254ccd055e]') as $element){
if(in_array($element->tag, $this->onlyKeepText)) { if(in_array($element->tag, $this->onlyKeepText)){
$element->outertext = $element->plaintext; $element->outertext = $element->plaintext;
} else if(in_array($element->tag, $this->tagsToRemove)) { } elseif(in_array($element->tag, $this->tagsToRemove)){
$element->outertext = ''; $element->outertext = '';
} else { } else {
foreach($element->getAllAttributes() as $attributeName => $attribute) { foreach($element->getAllAttributes() as $attributeName => $attribute){
if(!in_array($attributeName, $this->keptAttributes)) if(!in_array($attributeName, $this->keptAttributes))
$element->removeAttribute($attributeName); $element->removeAttribute($attributeName);
} }
@ -189,10 +295,12 @@ class HTMLSanitizer {
return $htmlContent; return $htmlContent;
} }
public static function defaultImageSrcTo($content, $server) { public static function defaultImageSrcTo($content, $server){
foreach($content->find('img') as $image) { foreach($content->find('img') as $image){
if(strpos($image->src, "http") == NULL && strpos($image->src, "//") == NULL && strpos($image->src, "data:") == NULL) if(is_null(strpos($image->src, "http"))
$image->src = $server.$image->src; && is_null(strpos($image->src, "//"))
&& is_null(strpos($image->src, "data:")))
$image->src = $server . $image->src;
} }
return $content; return $content;
} }

View file

@ -19,28 +19,33 @@ require __DIR__ . '/HTMLUtils.php';
$vendorLibSimpleHtmlDom = __DIR__ . PATH_VENDOR . '/simplehtmldom/simple_html_dom.php'; $vendorLibSimpleHtmlDom = __DIR__ . PATH_VENDOR . '/simplehtmldom/simple_html_dom.php';
if( !file_exists($vendorLibSimpleHtmlDom) ){ 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; require_once $vendorLibSimpleHtmlDom;
/* Example use /* Example use
require_once __DIR__ . '/lib/RssBridge.php'; require_once __DIR__ . '/lib/RssBridge.php';
// Data retrieval // Data retrieval
Bridge::setDir(__DIR__ . '/bridges/'); Bridge::setDir(__DIR__ . '/bridges/');
$bridge = Bridge::create('GoogleSearch'); $bridge = Bridge::create('GoogleSearch');
$bridge->collectData($_REQUEST); $bridge->collectData($_REQUEST);
// Data transformation // Data transformation
Format::setDir(__DIR__ . '/formats/'); Format::setDir(__DIR__ . '/formats/');
$format = Format::create('Atom'); $format = Format::create('Atom');
$format $format
->setItems($bridge->getItems()) ->setItems($bridge->getItems())
->setExtraInfos(array( ->setExtraInfos(array(
'name' => $bridge->getName(), 'name' => $bridge->getName(),
'uri' => $bridge->getURI(), 'uri' => $bridge->getURI(),
)) ))
->display(); ->display();
*/ */