Merge branch 'ContinuousIntegration' of https://github.com/logmanoriginal/rss-bridge

This commit is contained in:
logmanoriginal 2016-09-10 21:07:13 +02:00
commit 88919a43fd
21 changed files with 1502 additions and 1396 deletions

View file

@ -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;
}
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));
//Re-encode datas to UTF-8
//$datas = Cache::utf8_encode_deep($datas);
$writeStream = file_put_contents($this->getCacheFile(), serialize($datas));
if(!$writeStream) {
throw new \Exception("Cannot write the cache... Do you have the right permissions ?");
}
return $this;
}
return $this;
}
public function getTime(){
$this->isPrepareCache();
public function getTime(){
$this->isPrepareCache();
$cacheFile = $this->getCacheFile();
if( file_exists($cacheFile) ){
return filemtime($cacheFile);
}
$cacheFile = $this->getCacheFile();
if(file_exists($cacheFile)){
return filemtime($cacheFile);
}
return false;
}
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');
}
/**
* 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 true;
}
/**
* Return cache path (and create if not exist)
* @return string Cache path
*/
protected function getCachePath(){
$cacheDir = __DIR__ . '/../cache/'; // FIXME : configuration ?
/**
* 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;
// FIXME : implement recursive dir creation
if(is_null($this->cacheDirCreated) && !is_dir($cacheDir)){
$this->cacheDirCreated = true;
mkdir($cacheDir,0705);
chmod($cacheDir,0705);
}
mkdir($cacheDir,0705);
chmod($cacheDir,0705);
}
return $cacheDir;
}
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();
}
/**
* 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();
/**
* 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';
}
$stringToEncode = $_SERVER['REQUEST_URI'] . http_build_query($this->param);
$stringToEncode = preg_replace('/(\?|&)format=[^&]*/i', '$1', $stringToEncode);
return hash('sha1', $stringToEncode) . '.cache';
}
}

View file

@ -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);
}
}

View file

@ -1,63 +1,77 @@
<?php
class HtmlFormat extends FormatAbstract{
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']));
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
$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 = '';
if(isset($item['timestamp'])){
$entryTimestamp = '<time datetime="'
. date(DATE_ATOM, $item['timestamp'])
. '">'
. date(DATE_ATOM, $item['timestamp'])
. '</time>';
}
$entryContent = '';
if(isset($item['content'])){
$entryContent = '<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();
}
}

View file

@ -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();
}
}

View file

@ -3,72 +3,81 @@
* 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']);
$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
if(!empty($extraInfos['uri'])){
$uri = $this->xml_encode($extraInfos['uri']);
} else {
$uri = 'https://github.com/sebsauvage/rss-bridge';
}
<item>
<title>{$itemTitle}</title>
<link>{$itemUri}</link>
<guid isPermaLink="true">{$itemUri}</guid>
<pubDate>{$itemTimestamp}</pubDate>
<description>{$itemContent}</description>
<author>{$itemAuthor}</author>
</item>
$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
<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
<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>
/* 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>
</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);
}
}

View file

@ -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();
}
}
return parent::display();
}
}

230
index.php
View file

@ -2,7 +2,7 @@
/*
TODO :
- factorize the annotation system
- factorize to adapter : Format, Bridge, Cache (actually code is almost the same)
- factorize to adapter : Format, Bridge, Cache(actually code is almost the same)
- implement annotation cache for entrance page
- Cache : I think logic must be change as least to avoid to reconvert object from json in FileCache case.
- add namespace to avoid futur problem ?
@ -12,9 +12,9 @@ TODO :
//define('PROXY_URL', 'tcp://192.168.0.0:28');
// Set to true if you allow users to disable proxy usage for specific bridges
define('PROXY_BYBRIDGE',false);
define('PROXY_BYBRIDGE', false);
// Comment this line or keep PROXY_NAME empty to display PROXY_URL instead
define('PROXY_NAME','Hidden Proxy Name');
define('PROXY_NAME', 'Hidden Proxy Name');
date_default_timezone_set('UTC');
error_reporting(0);
@ -22,37 +22,39 @@ error_reporting(0);
/*
Create a file named 'DEBUG' for enabling debug mode.
For further security, you may put whitelisted IP addresses
in the 'DEBUG' file, one IP per line. Empty file allows anyone (!).
in the 'DEBUG' file, one IP per line. Empty file allows anyone(!).
Debugging allows displaying PHP error messages and bypasses the cache: this can allow a malicious
client to retrieve data about your server and hammer a provider throught your rss-bridge instance.
*/
if (file_exists('DEBUG')) {
$debug_enabled = true;
$debug_whitelist = trim(file_get_contents('DEBUG'));
if (strlen($debug_whitelist) > 0) {
$debug_enabled = false;
foreach (explode("\n", $debug_whitelist) as $allowed_ip) {
if (trim($allowed_ip) === $_SERVER['REMOTE_ADDR']) {
$debug_enabled = true;
break;
}
}
}
if ($debug_enabled) {
ini_set('display_errors', '1');
error_reporting(E_ALL);
define('DEBUG', 'true');
}
if(file_exists('DEBUG')){
$debug_enabled = true;
$debug_whitelist = trim(file_get_contents('DEBUG'));
if(strlen($debug_whitelist) > 0){
$debug_enabled = false;
foreach(explode("\n", $debug_whitelist) as $allowed_ip){
if(trim($allowed_ip) === $_SERVER['REMOTE_ADDR']){
$debug_enabled = true;
break;
}
}
}
if($debug_enabled){
ini_set('display_errors', '1');
error_reporting(E_ALL);
define('DEBUG', 'true');
}
}
require_once __DIR__ . '/lib/RssBridge.php';
// extensions check
if (!extension_loaded('openssl'))
if(!extension_loaded('openssl'))
die('"openssl" extension not loaded. Please check "php.ini"');
// FIXME : beta test UA spoofing, please report any blacklisting by PHP-fopen-unfriendly websites
ini_set('user_agent', 'Mozilla/5.0 (X11; Linux x86_64; rv:30.0) Gecko/20121202 Firefox/30.0 (rss-bridge/0.1; +https://github.com/sebsauvage/rss-bridge)');
ini_set('user_agent', 'Mozilla/5.0(X11; Linux x86_64; rv:30.0)
Gecko/20121202 Firefox/30.0(rss-bridge/0.1;
+https://github.com/sebsauvage/rss-bridge)');
// default whitelist
$whitelist_file = './whitelist.txt';
@ -74,91 +76,89 @@ $whitelist_default = array(
"WikipediaBridge",
"YoutubeBridge");
if (!file_exists($whitelist_file)) {
if(!file_exists($whitelist_file)){
$whitelist_selection = $whitelist_default;
$whitelist_write = implode("\n", $whitelist_default);
file_put_contents($whitelist_file, $whitelist_write);
}
else {
} else {
$whitelist_selection = explode("\n", file_get_contents($whitelist_file));
}
Cache::purge();
try{
try {
Bridge::setDir(__DIR__ . '/bridges/');
Format::setDir(__DIR__ . '/formats/');
Cache::setDir(__DIR__ . '/caches/');
Bridge::setDir(__DIR__ . '/bridges/');
Format::setDir(__DIR__ . '/formats/');
Cache::setDir(__DIR__ . '/caches/');
$action=filter_input(INPUT_GET,'action');
$bridge=filter_input(INPUT_GET,'bridge');
if($action === 'display' && !empty($bridge)){
// DEPRECATED: 'nameBridge' scheme is replaced by 'name' in bridge parameter values
// this is to keep compatibility until futher complete removal
if(($pos=strpos($bridge,'Bridge'))===(strlen($bridge)-strlen('Bridge'))){
$bridge=substr($bridge,0,$pos);
}
$action = filter_input(INPUT_GET, 'action');
$bridge = filter_input(INPUT_GET, 'bridge');
$format = filter_input(INPUT_GET,'format');
// DEPRECATED: 'nameFormat' scheme is replaced by 'name' in format parameter values
// this is to keep compatibility until futher complete removal
if(($pos=strpos($format,'Format'))===(strlen($format)-strlen('Format'))){
$format=substr($format,0,$pos);
}
if($action === 'display' && !empty($bridge)){
// DEPRECATED: 'nameBridge' scheme is replaced by 'name' in bridge parameter values
// this is to keep compatibility until futher complete removal
if(($pos = strpos($bridge, 'Bridge')) === (strlen($bridge) - strlen('Bridge'))){
$bridge = substr($bridge, 0, $pos);
}
$format = filter_input(INPUT_GET, 'format');
// whitelist control
if(!Bridge::isWhitelisted($whitelist_selection, $bridge)) {
throw new \HttpException('This bridge is not whitelisted', 401);
die;
}
// DEPRECATED: 'nameFormat' scheme is replaced by 'name' in format parameter values
// this is to keep compatibility until futher complete removal
if(($pos = strpos($format, 'Format')) === (strlen($format) - strlen('Format'))){
$format = substr($format, 0, $pos);
}
$cache = Cache::create('FileCache');
// whitelist control
if(!Bridge::isWhitelisted($whitelist_selection, $bridge)){
throw new \HttpException('This bridge is not whitelisted', 401);
die;
}
// Data retrieval
$bridge = Bridge::create($bridge);
if(!defined("DEBUG")) {
$bridge->setCache($cache);
}
$cache = Cache::create('FileCache');
$noproxy=filter_input(INPUT_GET,'_noproxy',FILTER_VALIDATE_BOOLEAN);
if(defined('PROXY_URL') && PROXY_BYBRIDGE && $noproxy){
$bridge->useProxy=false;
}
// Data retrieval
$bridge = Bridge::create($bridge);
if(!defined("DEBUG"))
$bridge->setCache($cache);
$params=$_GET;
unset($params['action']);
unset($params['bridge']);
unset($params['format']);
unset($params['_noproxy']);
$bridge->setDatas($params);
// Data transformation
try {
$format = Format::create($format);
$format
->setItems($bridge->getItems())
->setExtraInfos(array(
'name' => $bridge->getName(),
'uri' => $bridge->getURI(),
))
->display();
} catch(Exception $e) {
$noproxy = filter_input(INPUT_GET, '_noproxy', FILTER_VALIDATE_BOOLEAN);
if(defined('PROXY_URL') && PROXY_BYBRIDGE && $noproxy)
$bridge->useProxy = false;
echo "The brige has crashed. You should report this to the bridges maintainer";
$params = $_GET;
unset($params['action']);
unset($params['bridge']);
unset($params['format']);
unset($params['_noproxy']);
$bridge->setDatas($params);
}
die;
}
// Data transformation
try {
$format = Format::create($format);
$format
->setItems($bridge->getItems())
->setExtraInfos(array(
'name' => $bridge->getName(),
'uri' => $bridge->getURI(),
))
->display();
} catch(Exception $e){
echo "The bridge has crashed. You should report this to the bridges maintainer";
}
die;
}
}
catch(HttpException $e){
header('HTTP/1.1 ' . $e->getCode() . ' ' . Http::getMessageForCode($e->getCode()));
header('Content-Type: text/plain');
die($e->getMessage());
header('HTTP/1.1 ' . $e->getCode() . ' ' . Http::getMessageForCode($e->getCode()));
header('Content-Type: text/plain');
die($e->getMessage());
}
catch(\Exception $e){
die($e->getMessage());
die($e->getMessage());
}
$formats = Format::searchInformation();
@ -167,51 +167,47 @@ $formats = Format::searchInformation();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Rss-bridge" />
<title>RSS-Bridge</title>
<link href="css/style.css" rel="stylesheet">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Rss-bridge" />
<title>RSS-Bridge</title>
<link href="css/style.css" rel="stylesheet">
</head>
<body>
<header>
<h1>RSS-Bridge</h1>
<h2>·Reconnecting the Web·</h2>
</header>
<header>
<h1>RSS-Bridge</h1>
<h2>·Reconnecting the Web·</h2>
</header>
<?php
$activeFoundBridgeCount = 0;
$showInactive = filter_input(INPUT_GET,'show_inactive',FILTER_VALIDATE_BOOLEAN);
$activeFoundBridgeCount = 0;
$showInactive = filter_input(INPUT_GET, 'show_inactive', FILTER_VALIDATE_BOOLEAN);
$inactiveBridges = '';
$bridgeList = Bridge::listBridges();
foreach($bridgeList as $bridgeName)
{
if(Bridge::isWhitelisted($whitelist_selection, $bridgeName))
{
foreach($bridgeList as $bridgeName){
if(Bridge::isWhitelisted($whitelist_selection, $bridgeName)){
echo HTMLUtils::displayBridgeCard($bridgeName, $formats);
$activeFoundBridgeCount++;
}
elseif ($showInactive)
{
$activeFoundBridgeCount++;
} elseif($showInactive) {
// inactive bridges
$inactiveBridges .= HTMLUtils::displayBridgeCard($bridgeName, $formats, false) . PHP_EOL;
}
}
echo $inactiveBridges;
?>
<section>
<a href="https://github.com/sebsauvage/rss-bridge">RSS-Bridge alpha 0.2 ~ Public Domain</a><br />
<section>
<a href="https://github.com/sebsauvage/rss-bridge">RSS-Bridge alpha 0.2 ~ Public Domain</a><br />
<?= $activeFoundBridgeCount; ?>/<?= count($bridgeList) ?> active bridges. <br />
<?php
if($activeFoundBridgeCount !== count($bridgeList)){
// FIXME: This should be done in pure CSS
if(!$showInactive)
echo '<a href="?show_inactive=1"><button class="small">Show inactive bridges</button></a><br />';
else
echo '<a href="?show_inactive=0"><button class="small">Hide inactive bridges</button></a><br />';
}
?>
</section>
</body>
<?php
if($activeFoundBridgeCount !== count($bridgeList)){
// FIXME: This should be done in pure CSS
if(!$showInactive)
echo '<a href="?show_inactive=1"><button class="small">Show inactive bridges</button></a><br />';
else
echo '<a href="?show_inactive=0"><button class="small">Hide inactive bridges</button></a><br />';
}
?>
</section>
</body>
</html>

View file

@ -2,103 +2,104 @@
require_once(__DIR__ . '/BridgeInterface.php');
class Bridge {
static protected $dirBridge;
static protected $dirBridge;
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.');
}
/**
* 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();
}
/**
* 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
/**
* 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);
}
throw new \InvalidArgumentException($message);
}
$nameBridge = $nameBridge . 'Bridge';
$pathBridge = self::getDir() . $nameBridge . '.php';
$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);
}
if(!file_exists($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)){
return new $nameBridge();
} else {
return false;
}
}
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.');
}
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.');
}
if(!file_exists($dirBridge)){
throw new \Exception('Dir bridge does not exist.');
}
self::$dirBridge = $dirBridge;
}
self::$dirBridge = $dirBridge;
}
static public function getDir(){
$dirBridge = self::$dirBridge;
static public function getDir(){
$dirBridge = self::$dirBridge;
if(is_null($dirBridge)){
throw new \LogicException(__CLASS__ . ' class need to know bridge path !');
}
if(is_null($dirBridge)){
throw new \LogicException(__CLASS__ . ' class need to know bridge path !');
}
return $dirBridge;
}
return $dirBridge;
}
/**
* Lists the available bridges.
* @return array List of the bridges
*/
static public function listBridges(){
$pathDirBridge = self::getDir();
$listBridge = array();
$dirFiles = scandir($pathDirBridge);
/**
* 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];
}
}
}
if($dirFiles !== false){
foreach($dirFiles as $fileName){
if(preg_match('@^([^.]+)Bridge\.php$@U', $fileName, $out)){
$listBridge[] = $out[1];
}
}
}
return $listBridge;
}
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;
}
}
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;
}
}
}

View file

@ -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);
}
}

View file

@ -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();
}

View file

@ -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);
} else if (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);
} elseif(is_array($input)){
foreach($input as &$value){
Cache::utf8_encode_deep($value);
}
unset($value);
} else if (is_object($input)) {
$vars = array_keys(get_object_vars($input));
unset($value);
} elseif(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() - 86400; // 86400 -> 24h
$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());
}
}
}
}

View file

@ -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;
}
}

View file

@ -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();
}

View file

@ -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',
);
}
}

View file

@ -2,175 +2,178 @@
require_once(__DIR__ . '/BridgeInterface.php');
abstract class FeedExpander extends BridgeAbstract {
private $name;
private $uri;
private $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');
}
public function collectExpandableDatas($url, $maxItems = -1){
if(empty($url)){
$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:
* 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);
/* 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!');
}
}
$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_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_RSS_2_0_data($rssContent, $maxItems){
$rssContent = $rssContent->channel[0];
$this->debugMessage('RSS content is ===========\n'
. var_export($rssContent, true)
. '===========');
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;
}
}
$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 RSS_2_0_time_to_timestamp($item){
return DateTime::createFromFormat('D, d M Y H:i:s e', $item->pubDate)->getTimestamp();
}
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;
}
}
// 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 RSS_2_0_time_to_timestamp($item){
return DateTime::createFromFormat('D, d M Y H:i:s e', $item->pubDate)->getTimestamp();
}
protected function load_ATOM_feed_data($content){
$this->name = $content->title;
// 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);
}
// 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;
}
}
}
protected function load_ATOM_feed_data($content){
$this->name = $content->title;
if(isset($content->subtitle))
$this->description = $content->subtitle;
}
// 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;
}
}
}
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;
}
if(isset($content->subtitle))
$this->description = $content->subtitle;
}
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 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_1_0_Item($feedItem){
// 1.0 adds optional elements around the 0.91 standard
$item = $this->parseRSS_0_9_1_Item($feedItem);
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;
}
$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_1_0_Item($feedItem){
// 1.0 adds optional elements around the 0.91 standard
$item = $this->parseRSS_0_9_1_Item($feedItem);
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){
// Primary data is compatible to 0.91 with some additional data
$item = $this->parseRSS_0_9_1_Item($feedItem);
return $item;
}
$namespaces = $feedItem->getNamespaces(true);
if(isset($namespaces['dc'])) $dc = $feedItem->children($namespaces['dc']);
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);
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;
}
$namespaces = $feedItem->getNamespaces(true);
if(isset($namespaces['dc'])) $dc = $feedItem->children($namespaces['dc']);
/**
* 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);
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;
}
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(){
return $this->name;
}
public function getURI(){
return $this->uri;
}
public function getDescription(){
return $this->description;
}
public function getName(){
return $this->name;
}
public function getDescription(){
return $this->description;
}
}

View file

@ -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;
}
}

View file

@ -1,107 +1,106 @@
<?php
require_once(__DIR__ . '/FormatInterface.php');
abstract class FormatAbstract implements FormatInterface{
const DEFAULT_CHARSET = 'UTF-8';
abstract class FormatAbstract implements FormatInterface {
const DEFAULT_CHARSET = 'UTF-8';
protected
$contentType,
$charset,
$items,
$extraInfos
;
protected
$contentType,
$charset,
$items,
$extraInfos;
public function setCharset($charset){
$this->charset = $charset;
public function setCharset($charset){
$this->charset = $charset;
return $this;
}
return $this;
}
public function getCharset(){
$charset = $this->charset;
public function getCharset(){
$charset = $this->charset;
return is_null($charset) ? self::DEFAULT_CHARSET : $charset;
}
return is_null($charset) ? self::DEFAULT_CHARSET : $charset;
}
protected function setContentType($contentType){
$this->contentType = $contentType;
protected function setContentType($contentType){
$this->contentType = $contentType;
return $this;
}
return $this;
}
protected function callContentType(){
header('Content-Type: ' . $this->contentType);
}
protected function callContentType(){
header('Content-Type: ' . $this->contentType);
}
public function display(){
echo $this->stringify();
public function display(){
echo $this->stringify();
return $this;
}
return $this;
}
public function setItems(array $items){
$this->items = array_map(array($this, 'array_trim'), $items);
public function setItems(array $items){
$this->items = array_map(array($this, 'array_trim'), $items);
return $this;
}
return $this;
}
public function getItems(){
if(!is_array($this->items))
throw new \LogicException('Feed the ' . get_class($this) . ' with "setItems" method before !');
public function getItems(){
if(!is_array($this->items))
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
* @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] = '';
}
}
/**
* 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;
$this->extraInfos = $extraInfos;
return $this;
}
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 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','<&zwnj;script',$html); // Disable scripts, but leave them visible.
$html = str_replace('<iframe','<&zwnj;iframe',$html);
$html = str_replace('<link','<&zwnj;link',$html);
// We leave alone object and embed so that videos can play in RSS readers.
return $html;
}
return $this->extraInfos;
}
protected function array_trim($elements){
foreach($elements as $key => $value){
if(is_string($value))
$elements[$key] = trim($value);
}
return $elements;
}
/**
* 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','<&zwnj;script',$html); // Disable scripts, but leave them visible.
$html = str_replace('<iframe','<&zwnj;iframe',$html);
$html = str_replace('<link','<&zwnj;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;
}
}

View file

@ -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);
}

View file

@ -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;
$idArg = 'arg-'
. urlencode($bridgeName)
. '-'
. urlencode($parameterName)
. '-'
. urlencode($id);
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 . '" >';
$card .= '<label for="'
. $idArg
. '">'
. $inputEntry['name']
. ' : </label>'
. PHP_EOL;
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>';
}
}
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;
}

View file

@ -19,28 +19,32 @@ 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);
require_once __DIR__ . '/lib/RssBridge.php';
// Data transformation
Format::setDir(__DIR__ . '/formats/');
$format = Format::create('Atom');
$format
->setItems($bridge->getItems())
->setExtraInfos(array(
'name' => $bridge->getName(),
'uri' => $bridge->getURI(),
))
->display();
// 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();
*/

View file

@ -18,39 +18,13 @@
<rule ref="Generic.NamingConventions.UpperCaseConstantName"/>
<rule ref="Generic.PHP.LowerCaseConstant"/>
<rule ref="Generic.Strings.UnnecessaryStringConcat"/>
<rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
<rule ref="Generic.WhiteSpace.ScopeIndent">
<properties>
<property name="indent" value="2"/>
</properties>
</rule>
<rule ref="PEAR.Functions.FunctionCallSignature">
<properties>
<property name="indent" value="2"/>
</properties>
</rule>
<rule ref="Generic.WhiteSpace.DisallowSpaceIndent"/>
<rule ref="PEAR.Functions.ValidDefaultValue"/>
<rule ref="PEAR.NamingConventions.ValidClassName"/>
<rule ref="PEAR.WhiteSpace.ObjectOperatorIndent">
<properties>
<property name="indent" value="2"/>
</properties>
</rule>
<rule ref="PEAR.WhiteSpace.ScopeClosingBrace">
<properties>
<property name="indent" value="2"/>
</properties>
</rule>
<rule ref="PSR2.ControlStructures.ElseIfDeclaration"/>
<rule ref="PSR2.ControlStructures.SwitchDeclaration">
<properties>
<property name="indent" value="2"/>
</properties>
</rule>
<rule ref="PSR2.Files.EndFileNewline"/>
<rule ref="Squiz.WhiteSpace.CastSpacing"/>
<rule ref="Squiz.WhiteSpace.ObjectOperatorSpacing"/>
<rule ref="Squiz.WhiteSpace.OperatorSpacing"/>
<rule ref="Squiz.WhiteSpace.SemicolonSpacing"/>
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace"/>
</ruleset>
</ruleset>