Move to Composer and update to the last version of PicoFeed
This commit is contained in:
parent
86743febf6
commit
c6ed11c116
1
.gitignore
vendored
1
.gitignore
vendored
@ -43,6 +43,5 @@ Thumbs.db
|
||||
# App specific #
|
||||
################
|
||||
config.php
|
||||
!vendor/PicoFeed/*
|
||||
!models/*
|
||||
!controllers/*
|
||||
|
@ -59,5 +59,5 @@ if (! is_writable(DATA_DIRECTORY)) {
|
||||
|
||||
// Include password_compat for PHP < 5.5
|
||||
if (version_compare(PHP_VERSION, '5.5.0', '<')) {
|
||||
require __DIR__.'/vendor/password.php';
|
||||
require __DIR__.'/lib/password.php';
|
||||
}
|
||||
|
23
common.php
23
common.php
@ -1,27 +1,6 @@
|
||||
<?php
|
||||
|
||||
require __DIR__.'/lib/Translator.php';
|
||||
require __DIR__.'/vendor/PicoDb/Database.php';
|
||||
require __DIR__.'/vendor/PicoFeed/PicoFeed.php';
|
||||
|
||||
require __DIR__.'/vendor/SimpleValidator/Validator.php';
|
||||
require __DIR__.'/vendor/SimpleValidator/Base.php';
|
||||
require __DIR__.'/vendor/SimpleValidator/Validators/Required.php';
|
||||
require __DIR__.'/vendor/SimpleValidator/Validators/Unique.php';
|
||||
require __DIR__.'/vendor/SimpleValidator/Validators/MaxLength.php';
|
||||
require __DIR__.'/vendor/SimpleValidator/Validators/MinLength.php';
|
||||
require __DIR__.'/vendor/SimpleValidator/Validators/Integer.php';
|
||||
require __DIR__.'/vendor/SimpleValidator/Validators/Equals.php';
|
||||
require __DIR__.'/vendor/SimpleValidator/Validators/AlphaNumeric.php';
|
||||
|
||||
require __DIR__.'/models/config.php';
|
||||
require __DIR__.'/models/user.php';
|
||||
require __DIR__.'/models/feed.php';
|
||||
require __DIR__.'/models/item.php';
|
||||
require __DIR__.'/models/schema.php';
|
||||
require __DIR__.'/models/auto_update.php';
|
||||
require __DIR__.'/models/database.php';
|
||||
require __DIR__.'/models/remember_me.php';
|
||||
require __DIR__.'/vendor/autoload.php';
|
||||
|
||||
if (file_exists(__DIR__.'/config.php')) {
|
||||
require __DIR__.'/config.php';
|
||||
|
31
composer.json
Normal file
31
composer.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"config": {
|
||||
"preferred-install": "dist"
|
||||
},
|
||||
"require": {
|
||||
"fguillot/simple-validator": "dev-master",
|
||||
"fguillot/json-rpc": "dev-master",
|
||||
"fguillot/picodb": "dev-master",
|
||||
"fguillot/picofeed": "dev-master",
|
||||
"fguillot/picofarad": "dev-master"
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"lib/Translator.php",
|
||||
"models/config.php",
|
||||
"models/user.php",
|
||||
"models/feed.php",
|
||||
"models/item.php",
|
||||
"models/schema.php",
|
||||
"models/auto_update.php",
|
||||
"models/database.php",
|
||||
"models/remember_me.php"
|
||||
],
|
||||
"classmap": [
|
||||
"vendor/fguillot/json-rpc/src/",
|
||||
"vendor/fguillot/picodb/lib/",
|
||||
"vendor/fguillot/picofeed/lib/",
|
||||
"vendor/fguillot/simple-validator/src/"
|
||||
]
|
||||
}
|
||||
}
|
@ -52,5 +52,4 @@ define('PROXY_PASSWORD', '');
|
||||
|
||||
// ENABLE_AUTO_UPDATE => default is true (enable Miniflux update from the user interface)
|
||||
define('ENABLE_AUTO_UPDATE', true);
|
||||
|
||||
```
|
@ -20,7 +20,7 @@ However the content grabber doesn't work very well with all websites.
|
||||
How to write a grabber rules file?
|
||||
----------------------------------
|
||||
|
||||
Add a PHP file to the directory `PicoFeed\Rules`, the filename must be the domain name:
|
||||
Add a PHP file to the directory `vendor\fguillot\PicoFeed\Rules`, the filename must be the domain name:
|
||||
|
||||
Example with the BBC website, `www.bbc.co.uk.php`:
|
||||
|
||||
|
10
index.php
10
index.php
@ -1,11 +1,11 @@
|
||||
<?php
|
||||
|
||||
require __DIR__.'/common.php';
|
||||
require __DIR__.'/vendor/PicoFarad/Template.php';
|
||||
require __DIR__.'/vendor/PicoFarad/Response.php';
|
||||
require __DIR__.'/vendor/PicoFarad/Request.php';
|
||||
require __DIR__.'/vendor/PicoFarad/Session.php';
|
||||
require __DIR__.'/vendor/PicoFarad/Router.php';
|
||||
require __DIR__.'/vendor/fguillot/picofarad/lib/PicoFarad/Template.php';
|
||||
require __DIR__.'/vendor/fguillot/picofarad/lib/PicoFarad/Response.php';
|
||||
require __DIR__.'/vendor/fguillot/picofarad/lib/PicoFarad/Request.php';
|
||||
require __DIR__.'/vendor/fguillot/picofarad/lib/PicoFarad/Session.php';
|
||||
require __DIR__.'/vendor/fguillot/picofarad/lib/PicoFarad/Router.php';
|
||||
require __DIR__.'/lib/helpers.php';
|
||||
|
||||
use PicoFarad\Router;
|
||||
|
@ -1,7 +1,6 @@
|
||||
<?php
|
||||
|
||||
require __DIR__.'/common.php';
|
||||
require __DIR__.'/vendor/JsonRPC/Server.php';
|
||||
|
||||
use JsonRPC\Server;
|
||||
|
||||
|
@ -4,7 +4,7 @@ namespace Helper;
|
||||
|
||||
function isRTL(array $item)
|
||||
{
|
||||
return ! empty($item['rtl']) || \PicoFeed\Parser::isLanguageRTL($item['language']);
|
||||
return ! empty($item['rtl']) || \PicoFeed\Parser\Parser::isLanguageRTL($item['language']);
|
||||
}
|
||||
|
||||
function css()
|
||||
|
@ -6,8 +6,8 @@ use DirectoryIterator;
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
use PicoDb\Database;
|
||||
use PicoFeed\Config as ReaderConfig;
|
||||
use PicoFeed\Logging;
|
||||
use PicoFeed\Config\Config as ReaderConfig;
|
||||
use PicoFeed\Logging\Logger;
|
||||
|
||||
const DB_VERSION = 30;
|
||||
const HTTP_USER_AGENT = 'Miniflux (http://miniflux.net)';
|
||||
@ -18,17 +18,23 @@ function get_reader_config()
|
||||
$config = new ReaderConfig;
|
||||
$config->setTimezone(get('timezone'));
|
||||
|
||||
// Client
|
||||
$config->setClientTimeout(HTTP_TIMEOUT);
|
||||
$config->setClientUserAgent(HTTP_USER_AGENT);
|
||||
$config->setGrabberUserAgent(HTTP_USER_AGENT);
|
||||
|
||||
// Proxy
|
||||
$config->setProxyHostname(PROXY_HOSTNAME);
|
||||
$config->setProxyPort(PROXY_PORT);
|
||||
$config->setProxyUsername(PROXY_USERNAME);
|
||||
$config->setProxyPassword(PROXY_PASSWORD);
|
||||
|
||||
// Filter
|
||||
$config->setFilterIframeWhitelist(get_iframe_whitelist());
|
||||
|
||||
// Parser
|
||||
$config->setParserHashAlgo('crc32b');
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
@ -47,7 +53,7 @@ function get_iframe_whitelist()
|
||||
// Send a debug message to the console
|
||||
function debug($line)
|
||||
{
|
||||
Logging::setMessage($line);
|
||||
Logger::setMessage($line);
|
||||
write_debug();
|
||||
}
|
||||
|
||||
@ -55,7 +61,7 @@ function debug($line)
|
||||
function write_debug()
|
||||
{
|
||||
if (DEBUG) {
|
||||
file_put_contents(DEBUG_FILENAME, implode(PHP_EOL, Logging::getMessages()));
|
||||
file_put_contents(DEBUG_FILENAME, implode(PHP_EOL, Logger::getMessages()));
|
||||
}
|
||||
}
|
||||
|
||||
|
174
models/feed.php
174
models/feed.php
@ -5,12 +5,12 @@ namespace Model\Feed;
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
use PicoDb\Database;
|
||||
use PicoFeed\Export;
|
||||
use PicoFeed\Import;
|
||||
use PicoFeed\Reader;
|
||||
use PicoFeed\Logging;
|
||||
use Model\Config;
|
||||
use Model\Item;
|
||||
use PicoFeed\Serialization\Export;
|
||||
use PicoFeed\Serialization\Import;
|
||||
use PicoFeed\Reader\Reader;
|
||||
use PicoFeed\PicoFeedException;
|
||||
|
||||
const LIMIT_ALL = -1;
|
||||
|
||||
@ -40,7 +40,6 @@ function export_opml()
|
||||
// Import OPML file
|
||||
function import_opml($content)
|
||||
{
|
||||
Logging::setTimezone(Config\get('timezone'));
|
||||
$import = new Import($content);
|
||||
$feeds = $import->execute();
|
||||
|
||||
@ -76,12 +75,24 @@ function import_opml($content)
|
||||
// Add a new feed from an URL
|
||||
function create($url, $enable_grabber = false, $force_rtl = false)
|
||||
{
|
||||
$reader = new Reader(Config\get_reader_config());
|
||||
$resource = $reader->download($url);
|
||||
try {
|
||||
$db = Database::get('db');
|
||||
|
||||
$parser = $reader->getParser();
|
||||
// Discover the feed
|
||||
$reader = new Reader(Config\get_reader_config());
|
||||
$resource = $reader->discover($url);
|
||||
|
||||
if ($parser !== false) {
|
||||
// Feed already there
|
||||
if ($db->table('feeds')->eq('feed_url', $resource->getUrl())->count()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the feed
|
||||
$parser = $reader->getParser(
|
||||
$resource->getUrl(),
|
||||
$resource->getContent(),
|
||||
$resource->getEncoding()
|
||||
);
|
||||
|
||||
if ($enable_grabber) {
|
||||
$parser->enableContentGrabber();
|
||||
@ -89,56 +100,38 @@ function create($url, $enable_grabber = false, $force_rtl = false)
|
||||
|
||||
$feed = $parser->execute();
|
||||
|
||||
if ($feed === false) {
|
||||
// Save the feed
|
||||
$result = $db->table('feeds')->save(array(
|
||||
'title' => $feed->getTitle(),
|
||||
'site_url' => $feed->getSiteUrl(),
|
||||
'feed_url' => $feed->getFeedUrl(),
|
||||
'download_content' => $enable_grabber ? 1 : 0,
|
||||
'rtl' => $force_rtl ? 1 : 0,
|
||||
'last_modified' => $resource->getLastModified(),
|
||||
'last_checked' => time(),
|
||||
'etag' => $resource->getEtag(),
|
||||
));
|
||||
|
||||
if ($result) {
|
||||
|
||||
$feed_id = $db->getConnection()->getLastId();
|
||||
|
||||
Item\update_all($feed_id, $feed->getItems());
|
||||
Config\write_debug();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $feed->getUrl()) {
|
||||
$feed->url = $reader->getUrl();
|
||||
}
|
||||
|
||||
if (! $feed->getTitle()) {
|
||||
Config\write_debug();
|
||||
return false;
|
||||
}
|
||||
|
||||
$db = Database::get('db');
|
||||
|
||||
// Check if the feed is already there
|
||||
if (! $db->table('feeds')->eq('feed_url', $reader->getUrl())->count()) {
|
||||
|
||||
// Etag and LastModified are added the next update
|
||||
$rs = $db->table('feeds')->save(array(
|
||||
'title' => $feed->getTitle(),
|
||||
'site_url' => $feed->getUrl(),
|
||||
'feed_url' => $reader->getUrl(),
|
||||
'download_content' => $enable_grabber ? 1 : 0,
|
||||
'rtl' => $force_rtl ? 1 : 0,
|
||||
));
|
||||
|
||||
if ($rs) {
|
||||
|
||||
$feed_id = $db->getConnection()->getLastId();
|
||||
Item\update_all($feed_id, $feed->getItems(), $enable_grabber);
|
||||
Config\write_debug();
|
||||
|
||||
return (int) $feed_id;
|
||||
}
|
||||
return (int) $feed_id;
|
||||
}
|
||||
}
|
||||
catch (PicoFeedException $e) {}
|
||||
|
||||
Config\write_debug();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Refresh all feeds
|
||||
function refresh_all($limit = LIMIT_ALL)
|
||||
{
|
||||
$feeds_id = get_ids($limit);
|
||||
|
||||
foreach ($feeds_id as $feed_id) {
|
||||
foreach (@get_ids($limit) as $feed_id) {
|
||||
refresh($feed_id);
|
||||
}
|
||||
|
||||
@ -151,53 +144,57 @@ function refresh_all($limit = LIMIT_ALL)
|
||||
// Refresh one feed
|
||||
function refresh($feed_id)
|
||||
{
|
||||
$feed = get($feed_id);
|
||||
try {
|
||||
|
||||
if (empty($feed)) {
|
||||
return false;
|
||||
}
|
||||
$feed = get($feed_id);
|
||||
|
||||
$reader = new Reader(Config\get_reader_config());
|
||||
|
||||
$resource = $reader->download(
|
||||
$feed['feed_url'],
|
||||
$feed['last_modified'],
|
||||
$feed['etag']
|
||||
);
|
||||
|
||||
// Update the `last_checked` column each time, HTTP cache or not
|
||||
update_last_checked($feed_id);
|
||||
|
||||
if (! $resource->isModified()) {
|
||||
update_parsing_error($feed_id, 0);
|
||||
Config\write_debug();
|
||||
return true;
|
||||
}
|
||||
|
||||
$parser = $reader->getParser();
|
||||
|
||||
if ($parser !== false) {
|
||||
|
||||
if ($feed['download_content']) {
|
||||
|
||||
// Don't fetch previous items, only new one
|
||||
$parser->enableContentGrabber();
|
||||
$parser->setGrabberIgnoreUrls(Database::get('db')->table('items')->eq('feed_id', $feed_id)->findAllByColumn('url'));
|
||||
if (empty($feed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $parser->execute();
|
||||
$reader = new Reader(Config\get_reader_config());
|
||||
|
||||
if ($result !== false) {
|
||||
$resource = $reader->download(
|
||||
$feed['feed_url'],
|
||||
$feed['last_modified'],
|
||||
$feed['etag']
|
||||
);
|
||||
|
||||
// Update the `last_checked` column each time, HTTP cache or not
|
||||
update_last_checked($feed_id);
|
||||
|
||||
// Feed modified
|
||||
if ($resource->isModified()) {
|
||||
|
||||
$parser = $reader->getParser(
|
||||
$resource->getUrl(),
|
||||
$resource->getContent(),
|
||||
$resource->getEncoding()
|
||||
);
|
||||
|
||||
if ($feed['download_content']) {
|
||||
|
||||
$parser->enableContentGrabber();
|
||||
|
||||
// Don't fetch previous items, only new one
|
||||
$parser->setGrabberIgnoreUrls(
|
||||
Database::get('db')->table('items')->eq('feed_id', $feed_id)->findAllByColumn('url')
|
||||
);
|
||||
}
|
||||
|
||||
$feed = $parser->execute();
|
||||
|
||||
update_parsing_error($feed_id, 0);
|
||||
update_cache($feed_id, $resource->getLastModified(), $resource->getEtag());
|
||||
|
||||
Item\update_all($feed_id, $result->getItems(), $feed['download_content']);
|
||||
Config\write_debug();
|
||||
|
||||
return true;
|
||||
Item\update_all($feed_id, $feed->getItems());
|
||||
}
|
||||
|
||||
update_parsing_error($feed_id, 0);
|
||||
Config\write_debug();
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (PicoFeedException $e) {}
|
||||
|
||||
update_parsing_error($feed_id, 1);
|
||||
Config\write_debug();
|
||||
@ -208,15 +205,13 @@ function refresh($feed_id)
|
||||
// Get the list of feeds ID to refresh
|
||||
function get_ids($limit = LIMIT_ALL)
|
||||
{
|
||||
$table_feeds = Database::get('db')->table('feeds')
|
||||
->eq('enabled', 1)
|
||||
->asc('last_checked');
|
||||
$query = Database::get('db')->table('feeds')->eq('enabled', 1)->asc('last_checked');
|
||||
|
||||
if ($limit !== LIMIT_ALL) {
|
||||
$table_feeds->limit((int) $limit);
|
||||
$query->limit((int) $limit);
|
||||
}
|
||||
|
||||
return $table_feeds->listing('id', 'id');
|
||||
return $query->listing('id', 'id');
|
||||
}
|
||||
|
||||
// Get feeds with no item
|
||||
@ -231,7 +226,6 @@ function get_all_empty()
|
||||
->findAll();
|
||||
|
||||
foreach ($feeds as $key => &$feed) {
|
||||
|
||||
if ($feed['nb_items'] > 0) {
|
||||
unset($feeds[$key]);
|
||||
}
|
||||
|
@ -4,10 +4,9 @@ namespace Model\Item;
|
||||
|
||||
use Model\Config;
|
||||
use PicoDb\Database;
|
||||
use PicoFeed\Logging;
|
||||
use PicoFeed\Grabber;
|
||||
use PicoFeed\Client;
|
||||
use PicoFeed\Filter;
|
||||
use PicoFeed\Logging\Logger;
|
||||
use PicoFeed\Client\Grabber;
|
||||
use PicoFeed\Filter\Filter;
|
||||
|
||||
// Get all items without filtering
|
||||
function get_everything()
|
||||
@ -424,7 +423,7 @@ function autoflush_unread()
|
||||
}
|
||||
|
||||
// Update all items
|
||||
function update_all($feed_id, array $items, $enable_grabber = false)
|
||||
function update_all($feed_id, array $items)
|
||||
{
|
||||
$nocontent = (bool) Config\get('nocontent');
|
||||
|
||||
@ -435,12 +434,12 @@ function update_all($feed_id, array $items, $enable_grabber = false)
|
||||
|
||||
foreach ($items as $item) {
|
||||
|
||||
Logging::setMessage('Item => '.$item->getId().' '.$item->getUrl());
|
||||
Logger::setMessage('Item => '.$item->getId().' '.$item->getUrl());
|
||||
|
||||
// Item parsed correctly?
|
||||
if ($item->getId() && $item->getUrl()) {
|
||||
|
||||
Logging::setMessage('Item parsed correctly');
|
||||
Logger::setMessage('Item parsed correctly');
|
||||
|
||||
// Get item record in database, if any
|
||||
$itemrec = $db
|
||||
@ -452,11 +451,7 @@ function update_all($feed_id, array $items, $enable_grabber = false)
|
||||
// Insert a new item
|
||||
if ($itemrec === null) {
|
||||
|
||||
Logging::setMessage('Item added to the database');
|
||||
|
||||
if ($enable_grabber && ! $nocontent && ! $item->getContent()) {
|
||||
$item->content = download_content_url($item->getUrl());
|
||||
}
|
||||
Logger::setMessage('Item added to the database');
|
||||
|
||||
$db->table('items')->save(array(
|
||||
'id' => $item->getId(),
|
||||
@ -474,7 +469,7 @@ function update_all($feed_id, array $items, $enable_grabber = false)
|
||||
}
|
||||
else if (! $itemrec['enclosure'] && $item->getEnclosureUrl()) {
|
||||
|
||||
Logging::setMessage('Update item enclosure');
|
||||
Logger::setMessage('Update item enclosure');
|
||||
|
||||
$db->table('items')->eq('id', $item->getId())->save(array(
|
||||
'status' => 'unread',
|
||||
@ -483,7 +478,7 @@ function update_all($feed_id, array $items, $enable_grabber = false)
|
||||
));
|
||||
}
|
||||
else {
|
||||
Logging::setMessage('Item already in the database');
|
||||
Logger::setMessage('Item already in the database');
|
||||
}
|
||||
|
||||
// Items inside this feed
|
||||
@ -523,7 +518,7 @@ function cleanup($feed_id, array $items_in_feed)
|
||||
if (! empty($items_to_remove)) {
|
||||
|
||||
$nb_items = count($items_to_remove);
|
||||
Logging::setMessage('There is '.$nb_items.' items to remove');
|
||||
Logger::setMessage('There is '.$nb_items.' items to remove');
|
||||
|
||||
// Handle the case when there is a huge number of items to remove
|
||||
// Sqlite have a limit of 1000 sql variables by default
|
||||
@ -554,7 +549,7 @@ function download_content_url($url)
|
||||
$grabber->download();
|
||||
|
||||
if ($grabber->parse()) {
|
||||
$content = $grabber->getcontent();
|
||||
$content = $grabber->getFilteredcontent();
|
||||
}
|
||||
|
||||
if (! empty($content)) {
|
||||
|
@ -10,12 +10,18 @@ git clone --depth 1 https://github.com/fguillot/$APP.git
|
||||
|
||||
rm -rf $APP/data/*.sqlite \
|
||||
$APP/.git \
|
||||
$APP/.gitignore \
|
||||
$APP/scripts \
|
||||
$APP/docs \
|
||||
$APP/README.* \
|
||||
$APP/Dockerfile
|
||||
$APP/Dockerfile \
|
||||
$APP/vendor/composer/installed.json
|
||||
|
||||
find $APP -name docs -type d -exec rm -rf {} +;
|
||||
find $APP -name tests -type d -exec rm -rf {} +;
|
||||
|
||||
find $APP -name composer.json -delete
|
||||
find $APP -name phpunit.xml -delete
|
||||
find $APP -name .travis.yml -delete
|
||||
find $APP -name README.* -delete
|
||||
find $APP -name .gitignore -delete
|
||||
find $APP -name *.less -delete
|
||||
find $APP -name *.scss -delete
|
||||
find $APP -name *.rb -delete
|
||||
|
1
vendor/.htaccess
vendored
1
vendor/.htaccess
vendored
@ -1 +0,0 @@
|
||||
Deny from all
|
352
vendor/JsonRPC/Server.php
vendored
352
vendor/JsonRPC/Server.php
vendored
@ -1,352 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC;
|
||||
|
||||
use ReflectionFunction;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* JsonRPC server class
|
||||
*
|
||||
* @package JsonRPC
|
||||
* @author Frederic Guillot
|
||||
* @license Unlicense http://unlicense.org/
|
||||
*/
|
||||
class Server
|
||||
{
|
||||
/**
|
||||
* Data received from the client
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $payload;
|
||||
|
||||
/**
|
||||
* List of procedures
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
static private $procedures = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param string $payload Client data
|
||||
*/
|
||||
public function __construct($payload = '')
|
||||
{
|
||||
$this->payload = $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* IP based client restrictions
|
||||
*
|
||||
* Return an HTTP error 403 if the client is not allowed
|
||||
*
|
||||
* @access public
|
||||
* @param array $hosts List of hosts
|
||||
*/
|
||||
public function allowHosts(array $hosts) {
|
||||
|
||||
if (! in_array($_SERVER['REMOTE_ADDR'], $hosts)) {
|
||||
|
||||
header('Content-Type: application/json');
|
||||
header('HTTP/1.0 403 Forbidden');
|
||||
echo '["Access Forbidden"]';
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP Basic authentication
|
||||
*
|
||||
* Return an HTTP error 401 if the client is not allowed
|
||||
*
|
||||
* @access public
|
||||
* @param array $users Map of username/password
|
||||
*/
|
||||
public function authentication(array $users)
|
||||
{
|
||||
// OVH workaround
|
||||
if (isset($_SERVER['REMOTE_USER'])) {
|
||||
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':', base64_decode(substr($_SERVER['REMOTE_USER'], 6)));
|
||||
}
|
||||
|
||||
if (! isset($_SERVER['PHP_AUTH_USER']) ||
|
||||
! isset($users[$_SERVER['PHP_AUTH_USER']]) ||
|
||||
$users[$_SERVER['PHP_AUTH_USER']] !== $_SERVER['PHP_AUTH_PW']) {
|
||||
|
||||
header('WWW-Authenticate: Basic realm="JsonRPC"');
|
||||
header('Content-Type: application/json');
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
echo '["Authentication failed"]';
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new procedure
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Procedure name
|
||||
* @param closure $callback Callback
|
||||
*/
|
||||
public function register($name, Closure $callback)
|
||||
{
|
||||
self::$procedures[$name] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a procedure
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Procedure name
|
||||
*/
|
||||
public function unregister($name)
|
||||
{
|
||||
if (isset(self::$procedures[$name])) {
|
||||
unset(self::$procedures[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister all procedures
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function unregisterAll()
|
||||
{
|
||||
self::$procedures = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the response to the client
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Data to send to the client
|
||||
* @param array $payload Incoming data
|
||||
* @return string
|
||||
*/
|
||||
public function getResponse(array $data, array $payload = array())
|
||||
{
|
||||
if (! array_key_exists('id', $payload)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$response = array(
|
||||
'jsonrpc' => '2.0',
|
||||
'id' => $payload['id']
|
||||
);
|
||||
|
||||
$response = array_merge($response, $data);
|
||||
|
||||
@header('Content-Type: application/json');
|
||||
return json_encode($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map arguments to the procedure
|
||||
*
|
||||
* @access public
|
||||
* @param array $request_params Incoming arguments
|
||||
* @param array $method_params Procedure arguments
|
||||
* @param array $params Arguments to pass to the callback
|
||||
* @param integer $nb_required_params Number of required parameters
|
||||
* @return bool
|
||||
*/
|
||||
public function mapParameters(array $request_params, array $method_params, array &$params, $nb_required_params)
|
||||
{
|
||||
if (count($request_params) < $nb_required_params) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Positional parameters
|
||||
if (array_keys($request_params) === range(0, count($request_params) - 1)) {
|
||||
$params = $request_params;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Named parameters
|
||||
foreach ($method_params as $p) {
|
||||
|
||||
$name = $p->getName();
|
||||
|
||||
if (isset($request_params[$name])) {
|
||||
$params[$name] = $request_params[$name];
|
||||
}
|
||||
else if ($p->isDefaultValueAvailable()) {
|
||||
$params[$name] = $p->getDefaultValue();
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the payload and test if the parsed JSON is ok
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function isValidJsonFormat()
|
||||
{
|
||||
if (empty($this->payload)) {
|
||||
$this->payload = file_get_contents('php://input');
|
||||
}
|
||||
|
||||
if (is_string($this->payload)) {
|
||||
$this->payload = json_decode($this->payload, true);
|
||||
}
|
||||
|
||||
return is_array($this->payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if all required JSON-RPC parameters are here
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function isValidJsonRpcFormat()
|
||||
{
|
||||
if (! isset($this->payload['jsonrpc']) ||
|
||||
! isset($this->payload['method']) ||
|
||||
! is_string($this->payload['method']) ||
|
||||
$this->payload['jsonrpc'] !== '2.0' ||
|
||||
(isset($this->payload['params']) && ! is_array($this->payload['params']))) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we have a batch request
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
private function isBatchRequest()
|
||||
{
|
||||
return array_keys($this->payload) === range(0, count($this->payload) - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle batch request
|
||||
*
|
||||
* @access private
|
||||
* @return string
|
||||
*/
|
||||
private function handleBatchRequest()
|
||||
{
|
||||
$responses = array();
|
||||
|
||||
foreach ($this->payload as $payload) {
|
||||
|
||||
if (! is_array($payload)) {
|
||||
|
||||
$responses[] = $this->getResponse(array(
|
||||
'error' => array(
|
||||
'code' => -32600,
|
||||
'message' => 'Invalid Request'
|
||||
)),
|
||||
array('id' => null)
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
||||
$server = new Server($payload);
|
||||
$response = $server->execute();
|
||||
|
||||
if ($response) {
|
||||
$responses[] = $response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return empty($responses) ? '' : '['.implode(',', $responses).']';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse incoming requests
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
// Invalid Json
|
||||
if (! $this->isValidJsonFormat()) {
|
||||
return $this->getResponse(array(
|
||||
'error' => array(
|
||||
'code' => -32700,
|
||||
'message' => 'Parse error'
|
||||
)),
|
||||
array('id' => null)
|
||||
);
|
||||
}
|
||||
|
||||
// Handle batch request
|
||||
if ($this->isBatchRequest()){
|
||||
return $this->handleBatchRequest();
|
||||
}
|
||||
|
||||
// Invalid JSON-RPC format
|
||||
if (! $this->isValidJsonRpcFormat()) {
|
||||
|
||||
return $this->getResponse(array(
|
||||
'error' => array(
|
||||
'code' => -32600,
|
||||
'message' => 'Invalid Request'
|
||||
)),
|
||||
array('id' => null)
|
||||
);
|
||||
}
|
||||
|
||||
// Procedure not found
|
||||
if (! isset(self::$procedures[$this->payload['method']])) {
|
||||
|
||||
return $this->getResponse(array(
|
||||
'error' => array(
|
||||
'code' => -32601,
|
||||
'message' => 'Method not found'
|
||||
)),
|
||||
$this->payload
|
||||
);
|
||||
}
|
||||
|
||||
// Execute the procedure
|
||||
$callback = self::$procedures[$this->payload['method']];
|
||||
$params = array();
|
||||
|
||||
$reflection = new ReflectionFunction($callback);
|
||||
|
||||
if (isset($this->payload['params'])) {
|
||||
|
||||
$parameters = $reflection->getParameters();
|
||||
|
||||
if (! $this->mapParameters($this->payload['params'], $parameters, $params, $reflection->getNumberOfRequiredParameters())) {
|
||||
|
||||
return $this->getResponse(array(
|
||||
'error' => array(
|
||||
'code' => -32602,
|
||||
'message' => 'Invalid params'
|
||||
)),
|
||||
$this->payload
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$result = $reflection->invokeArgs($params);
|
||||
|
||||
return $this->getResponse(array('result' => $result), $this->payload);
|
||||
}
|
||||
}
|
155
vendor/PicoDb/Database.php
vendored
155
vendor/PicoDb/Database.php
vendored
@ -1,155 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb;
|
||||
|
||||
class Database
|
||||
{
|
||||
private static $instances = array();
|
||||
private $logs = array();
|
||||
private $pdo;
|
||||
|
||||
|
||||
public function __construct(array $settings)
|
||||
{
|
||||
if (! isset($settings['driver'])) {
|
||||
throw new \LogicException('You must define a database driver.');
|
||||
}
|
||||
|
||||
switch ($settings['driver']) {
|
||||
|
||||
case 'sqlite':
|
||||
require_once __DIR__.'/Drivers/Sqlite.php';
|
||||
$this->pdo = new Sqlite($settings);
|
||||
break;
|
||||
|
||||
case 'mysql':
|
||||
require_once __DIR__.'/Drivers/Mysql.php';
|
||||
$this->pdo = new Mysql($settings);
|
||||
break;
|
||||
|
||||
case 'postgres':
|
||||
require_once __DIR__.'/Drivers/Postgres.php';
|
||||
$this->pdo = new Postgres($settings);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \LogicException('This database driver is not supported.');
|
||||
}
|
||||
|
||||
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
|
||||
|
||||
public static function bootstrap($name, \Closure $callback)
|
||||
{
|
||||
self::$instances[$name] = $callback;
|
||||
}
|
||||
|
||||
|
||||
public static function get($name)
|
||||
{
|
||||
if (! isset(self::$instances[$name])) {
|
||||
throw new \LogicException('No database instance created with that name.');
|
||||
}
|
||||
|
||||
if (is_callable(self::$instances[$name])) {
|
||||
self::$instances[$name] = call_user_func(self::$instances[$name]);
|
||||
}
|
||||
|
||||
return self::$instances[$name];
|
||||
}
|
||||
|
||||
|
||||
public function setLogMessage($message)
|
||||
{
|
||||
$this->logs[] = $message;
|
||||
}
|
||||
|
||||
|
||||
public function getLogMessages()
|
||||
{
|
||||
return $this->logs;
|
||||
}
|
||||
|
||||
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->pdo;
|
||||
}
|
||||
|
||||
|
||||
public function closeConnection()
|
||||
{
|
||||
$this->pdo = null;
|
||||
}
|
||||
|
||||
|
||||
public function escapeIdentifier($value)
|
||||
{
|
||||
// Do not escape custom query
|
||||
if (strpos($value, '.') !== false || strpos($value, ' ') !== false) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $this->pdo->escapeIdentifier($value);
|
||||
}
|
||||
|
||||
|
||||
public function execute($sql, array $values = array())
|
||||
{
|
||||
try {
|
||||
|
||||
$this->setLogMessage($sql);
|
||||
$this->setLogMessage(implode(', ', $values));
|
||||
|
||||
$rq = $this->pdo->prepare($sql);
|
||||
$rq->execute($values);
|
||||
|
||||
return $rq;
|
||||
}
|
||||
catch (\PDOException $e) {
|
||||
|
||||
if ($this->pdo->inTransaction()) $this->pdo->rollback();
|
||||
$this->setLogMessage($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function startTransaction()
|
||||
{
|
||||
if (! $this->pdo->inTransaction()) {
|
||||
$this->pdo->beginTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function closeTransaction()
|
||||
{
|
||||
if ($this->pdo->inTransaction()) {
|
||||
$this->pdo->commit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function cancelTransaction()
|
||||
{
|
||||
if ($this->pdo->inTransaction()) {
|
||||
$this->pdo->rollback();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function table($table_name)
|
||||
{
|
||||
require_once __DIR__.'/Table.php';
|
||||
return new Table($this, $table_name);
|
||||
}
|
||||
|
||||
|
||||
public function schema()
|
||||
{
|
||||
require_once __DIR__.'/Schema.php';
|
||||
return new Schema($this);
|
||||
}
|
||||
}
|
17
vendor/PicoFeed/Parsers/Rss91.php
vendored
17
vendor/PicoFeed/Parsers/Rss91.php
vendored
@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed\Parsers;
|
||||
|
||||
require_once __DIR__.'/Rss20.php';
|
||||
|
||||
use PicoFeed\Parsers\Rss20;
|
||||
|
||||
/**
|
||||
* RSS 0.91 Parser
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package parser
|
||||
*/
|
||||
class Rss91 extends Rss20
|
||||
{
|
||||
}
|
17
vendor/PicoFeed/Parsers/Rss92.php
vendored
17
vendor/PicoFeed/Parsers/Rss92.php
vendored
@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed\Parsers;
|
||||
|
||||
require_once __DIR__.'/Rss20.php';
|
||||
|
||||
use PicoFeed\Parsers\Rss20;
|
||||
|
||||
/**
|
||||
* RSS 0.92 Parser
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package parser
|
||||
*/
|
||||
class Rss92 extends Rss20
|
||||
{
|
||||
}
|
25
vendor/PicoFeed/PicoFeed.php
vendored
25
vendor/PicoFeed/PicoFeed.php
vendored
@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
// Include this file if you don't want to use an autoloader
|
||||
|
||||
require __DIR__.'/Config.php';
|
||||
require __DIR__.'/Logging.php';
|
||||
require __DIR__.'/Url.php';
|
||||
require __DIR__.'/Item.php';
|
||||
require __DIR__.'/Feed.php';
|
||||
require __DIR__.'/Client.php';
|
||||
require __DIR__.'/Filter.php';
|
||||
require __DIR__.'/Filter/Attribute.php';
|
||||
require __DIR__.'/Filter/Tag.php';
|
||||
require __DIR__.'/Filter/Html.php';
|
||||
require __DIR__.'/XmlParser.php';
|
||||
require __DIR__.'/Encoding.php';
|
||||
require __DIR__.'/Grabber.php';
|
||||
require __DIR__.'/Reader.php';
|
||||
require __DIR__.'/Import.php';
|
||||
require __DIR__.'/Export.php';
|
||||
require __DIR__.'/Writer.php';
|
||||
require __DIR__.'/Writers/Rss20.php';
|
||||
require __DIR__.'/Writers/Atom.php';
|
||||
require __DIR__.'/Parser.php';
|
||||
require __DIR__.'/Favicon.php';
|
310
vendor/PicoFeed/Reader.php
vendored
310
vendor/PicoFeed/Reader.php
vendored
@ -1,310 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed;
|
||||
|
||||
use DOMXPath;
|
||||
use PicoFeed\Config;
|
||||
use PicoFeed\XmlParser;
|
||||
use PicoFeed\Logging;
|
||||
use PicoFeed\Filter;
|
||||
use PicoFeed\Client;
|
||||
use PicoFeed\Parser;
|
||||
use PicoFeed\Url;
|
||||
|
||||
/**
|
||||
* Reader class
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package picofeed
|
||||
*/
|
||||
class Reader
|
||||
{
|
||||
/**
|
||||
* Feed or site URL
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $url = '';
|
||||
|
||||
/**
|
||||
* Feed content
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $content = '';
|
||||
|
||||
/**
|
||||
* HTTP encoding
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $encoding = '';
|
||||
|
||||
/**
|
||||
* Config class instance
|
||||
*
|
||||
* @access private
|
||||
* @var \PicoFeed\Config
|
||||
*/
|
||||
private $config = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \PicoFeed\Config $config Config class instance
|
||||
*/
|
||||
public function __construct(Config $config = null)
|
||||
{
|
||||
$this->config = $config ?: new Config;
|
||||
Logging::setTimezone($this->config->getTimezone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a feed
|
||||
*
|
||||
* @access public
|
||||
* @param string $url Feed content
|
||||
* @param string $last_modified Last modified HTTP header
|
||||
* @param string $etag Etag HTTP header
|
||||
* @return \PicoFeed\Client
|
||||
*/
|
||||
public function download($url, $last_modified = '', $etag = '')
|
||||
{
|
||||
if (strpos($url, 'http') !== 0) {
|
||||
$url = 'http://'.$url;
|
||||
}
|
||||
|
||||
$client = Client::getInstance();
|
||||
$client->setConfig($this->config)
|
||||
->setLastModified($last_modified)
|
||||
->setEtag($etag);
|
||||
|
||||
if ($client->execute($url)) {
|
||||
$this->content = $client->getContent();
|
||||
$this->url = $client->getUrl();
|
||||
$this->encoding = $client->getEncoding();
|
||||
}
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a parser instance with a custom config
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Parser name
|
||||
* @return \PicoFeed\Parser
|
||||
*/
|
||||
public function getParserInstance($name)
|
||||
{
|
||||
require_once __DIR__.'/Parsers/'.ucfirst($name).'.php';
|
||||
$name = '\PicoFeed\Parsers\\'.$name;
|
||||
|
||||
$parser = new $name($this->content, $this->encoding, $this->getUrl());
|
||||
$parser->setHashAlgo($this->config->getParserHashAlgo());
|
||||
$parser->setTimezone($this->config->getTimezone());
|
||||
$parser->setConfig($this->config);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first XML tag
|
||||
*
|
||||
* @access public
|
||||
* @param string $data Feed content
|
||||
* @return string
|
||||
*/
|
||||
public function getFirstTag($data)
|
||||
{
|
||||
// Strip HTML comments (max of 5,000 characters long to prevent crashing)
|
||||
$data = preg_replace('/<!--(.{0,5000}?)-->/Uis', '', $data);
|
||||
|
||||
/* Strip Doctype:
|
||||
* Doctype needs to be within the first 100 characters. (Ideally the first!)
|
||||
* If it's not found by then, we need to stop looking to prevent PREG
|
||||
* from reaching max backtrack depth and crashing.
|
||||
*/
|
||||
$data = preg_replace('/^.{0,100}<!DOCTYPE([^>]*)>/Uis', '', $data);
|
||||
|
||||
// Strip <?xml version....
|
||||
$data = Filter::stripXmlTag($data);
|
||||
|
||||
// Find the first tag
|
||||
$open_tag = strpos($data, '<');
|
||||
$close_tag = strpos($data, '>');
|
||||
|
||||
return substr($data, $open_tag, $close_tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the feed format
|
||||
*
|
||||
* @access public
|
||||
* @param string $parser_name Parser name
|
||||
* @param string $haystack First XML tag
|
||||
* @param array $needles List of strings that need to be there
|
||||
* @return mixed False on failure or Parser instance
|
||||
*/
|
||||
public function detectFormat($parser_name, $haystack, array $needles)
|
||||
{
|
||||
$results = array();
|
||||
|
||||
foreach ($needles as $needle) {
|
||||
$results[] = strpos($haystack, $needle) !== false;
|
||||
}
|
||||
|
||||
if (! in_array(false, $results, true)) {
|
||||
Logging::setMessage(get_called_class().': Format detected => '.$parser_name);
|
||||
return $this->getParserInstance($parser_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discover feed format and return a parser instance
|
||||
*
|
||||
* @access public
|
||||
* @param boolean $discover Enable feed autodiscovery in HTML document
|
||||
* @return mixed False on failure or Parser instance
|
||||
*/
|
||||
public function getParser($discover = false)
|
||||
{
|
||||
$formats = array(
|
||||
array('parser' => 'Atom', 'needles' => array('<feed')),
|
||||
array('parser' => 'Rss20', 'needles' => array('<rss', '2.0')),
|
||||
array('parser' => 'Rss92', 'needles' => array('<rss', '0.92')),
|
||||
array('parser' => 'Rss91', 'needles' => array('<rss', '0.91')),
|
||||
array('parser' => 'Rss10', 'needles' => array('<rdf:'/*, 'xmlns="http://purl.org/rss/1.0/"'*/)),
|
||||
);
|
||||
|
||||
$first_tag = $this->getFirstTag($this->content);
|
||||
|
||||
foreach ($formats as $format) {
|
||||
|
||||
$parser = $this->detectFormat($format['parser'], $first_tag, $format['needles']);
|
||||
|
||||
if ($parser !== false) {
|
||||
return $parser;
|
||||
}
|
||||
}
|
||||
|
||||
if ($discover === true) {
|
||||
|
||||
Logging::setMessage(get_called_class().': Format not supported or feed malformed');
|
||||
Logging::setMessage(get_called_class().': Content => '.PHP_EOL.$this->content);
|
||||
|
||||
return false;
|
||||
}
|
||||
else if ($this->discover()) {
|
||||
return $this->getParser(true);
|
||||
}
|
||||
|
||||
Logging::setMessage(get_called_class().': Subscription not found');
|
||||
Logging::setMessage(get_called_class().': Content => '.PHP_EOL.$this->content);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discover the feed url inside a HTML document and download the feed
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function discover()
|
||||
{
|
||||
if (! $this->content) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Logging::setMessage(get_called_class().': Try to discover a subscription');
|
||||
|
||||
$dom = XmlParser::getHtmlDocument($this->content);
|
||||
$xpath = new DOMXPath($dom);
|
||||
|
||||
$queries = array(
|
||||
'//link[@type="application/rss+xml"]',
|
||||
'//link[@type="application/atom+xml"]',
|
||||
);
|
||||
|
||||
foreach ($queries as $query) {
|
||||
|
||||
$nodes = $xpath->query($query);
|
||||
|
||||
if ($nodes->length !== 0) {
|
||||
|
||||
$link = $nodes->item(0)->getAttribute('href');
|
||||
|
||||
if (! empty($link)) {
|
||||
|
||||
$feedUrl = new Url($link);
|
||||
$siteUrl = new Url($this->url);
|
||||
|
||||
$link = $feedUrl->getAbsoluteUrl($feedUrl->isRelativeUrl() ? $siteUrl->getBaseUrl() : '');
|
||||
|
||||
Logging::setMessage(get_called_class().': Find subscription link: '.$link);
|
||||
|
||||
$this->download($link);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the downloaded content
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the page content
|
||||
*
|
||||
* @access public
|
||||
* @param string $content Page content
|
||||
* @return \PicoFeed\Reader
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->content = $content;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get final URL
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the URL
|
||||
*
|
||||
* @access public
|
||||
* @param string $url URL
|
||||
* @return \PicoFeed\Reader
|
||||
*/
|
||||
public function setUrl($url)
|
||||
{
|
||||
$this->url = $url;
|
||||
return $this;
|
||||
}
|
||||
}
|
7
vendor/autoload.php
vendored
Normal file
7
vendor/autoload.php
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
require_once __DIR__ . '/composer' . '/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit1f0385c01a6e1fee49b051180b96fd66::getLoader();
|
383
vendor/composer/ClassLoader.php
vendored
Normal file
383
vendor/composer/ClassLoader.php
vendored
Normal file
@ -0,0 +1,383 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0 class loader
|
||||
*
|
||||
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
// PSR-4
|
||||
private $prefixLengthsPsr4 = array();
|
||||
private $prefixDirsPsr4 = array();
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
private $prefixesPsr0 = array();
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
private $useIncludePath = false;
|
||||
private $classMap = array();
|
||||
|
||||
public function getPrefixes()
|
||||
{
|
||||
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
||||
}
|
||||
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $classMap Class to filename map
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return bool|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
|
||||
if ('\\' == $class[0]) {
|
||||
$class = substr($class, 1);
|
||||
}
|
||||
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if ($file === null && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if ($file === null) {
|
||||
// Remember that this class does not exist.
|
||||
return $this->classMap[$class] = false;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*/
|
||||
function includeFile($file)
|
||||
{
|
||||
include $file;
|
||||
}
|
81
vendor/composer/autoload_classmap.php
vendored
Normal file
81
vendor/composer/autoload_classmap.php
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'JsonRPC\\Client' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Client.php',
|
||||
'JsonRPC\\InvalidJsonFormat' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php',
|
||||
'JsonRPC\\InvalidJsonRpcFormat' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php',
|
||||
'JsonRPC\\Server' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php',
|
||||
'PicoDb\\Database' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Database.php',
|
||||
'PicoDb\\Driver\\Mysql' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Mysql.php',
|
||||
'PicoDb\\Driver\\Postgres' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Postgres.php',
|
||||
'PicoDb\\Driver\\Sqlite' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php',
|
||||
'PicoDb\\Schema' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Schema.php',
|
||||
'PicoDb\\Table' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Table.php',
|
||||
'PicoFeed\\Client\\Client' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Client/Client.php',
|
||||
'PicoFeed\\Client\\ClientException' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Client/ClientException.php',
|
||||
'PicoFeed\\Client\\Curl' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Client/Curl.php',
|
||||
'PicoFeed\\Client\\Grabber' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Client/Grabber.php',
|
||||
'PicoFeed\\Client\\HttpHeaders' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Client/HttpHeaders.php',
|
||||
'PicoFeed\\Client\\InvalidCertificateException' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php',
|
||||
'PicoFeed\\Client\\InvalidUrlException' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Client/InvalidUrlException.php',
|
||||
'PicoFeed\\Client\\MaxRedirectException' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Client/MaxRedirectException.php',
|
||||
'PicoFeed\\Client\\MaxSizeException' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Client/MaxSizeException.php',
|
||||
'PicoFeed\\Client\\Stream' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Client/Stream.php',
|
||||
'PicoFeed\\Client\\TimeoutException' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Client/TimeoutException.php',
|
||||
'PicoFeed\\Client\\Url' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Client/Url.php',
|
||||
'PicoFeed\\Config\\Config' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Config/Config.php',
|
||||
'PicoFeed\\Encoding\\Encoding' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Encoding/Encoding.php',
|
||||
'PicoFeed\\Filter\\Attribute' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Filter/Attribute.php',
|
||||
'PicoFeed\\Filter\\Filter' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Filter/Filter.php',
|
||||
'PicoFeed\\Filter\\Html' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Filter/Html.php',
|
||||
'PicoFeed\\Filter\\Tag' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Filter/Tag.php',
|
||||
'PicoFeed\\Logging\\Logger' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Logging/Logger.php',
|
||||
'PicoFeed\\Parser\\Atom' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Parser/Atom.php',
|
||||
'PicoFeed\\Parser\\Feed' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Parser/Feed.php',
|
||||
'PicoFeed\\Parser\\Item' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Parser/Item.php',
|
||||
'PicoFeed\\Parser\\MalformedXmlException' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php',
|
||||
'PicoFeed\\Parser\\Parser' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Parser/Parser.php',
|
||||
'PicoFeed\\Parser\\ParserException' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Parser/ParserException.php',
|
||||
'PicoFeed\\Parser\\Rss10' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Parser/Rss10.php',
|
||||
'PicoFeed\\Parser\\Rss20' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Parser/Rss20.php',
|
||||
'PicoFeed\\Parser\\Rss91' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Parser/Rss91.php',
|
||||
'PicoFeed\\Parser\\Rss92' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Parser/Rss92.php',
|
||||
'PicoFeed\\Parser\\XmlParser' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Parser/XmlParser.php',
|
||||
'PicoFeed\\PicoFeedException' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/PicoFeedException.php',
|
||||
'PicoFeed\\Reader\\Favicon' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Reader/Favicon.php',
|
||||
'PicoFeed\\Reader\\Reader' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Reader/Reader.php',
|
||||
'PicoFeed\\Reader\\ReaderException' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Reader/ReaderException.php',
|
||||
'PicoFeed\\Reader\\SubscriptionNotFoundException' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Reader/SubscriptionNotFoundException.php',
|
||||
'PicoFeed\\Reader\\UnsupportedFeedFormatException' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Reader/UnsupportedFeedFormatException.php',
|
||||
'PicoFeed\\Serialization\\Export' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Serialization/Export.php',
|
||||
'PicoFeed\\Serialization\\Import' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Serialization/Import.php',
|
||||
'PicoFeed\\Syndication\\Atom' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Syndication/Atom.php',
|
||||
'PicoFeed\\Syndication\\Rss20' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Syndication/Rss20.php',
|
||||
'PicoFeed\\Syndication\\Writer' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Syndication/Writer.php',
|
||||
'SimpleValidator\\Base' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Base.php',
|
||||
'SimpleValidator\\Validator' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validator.php',
|
||||
'SimpleValidator\\Validators\\Alpha' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Alpha.php',
|
||||
'SimpleValidator\\Validators\\AlphaNumeric' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/AlphaNumeric.php',
|
||||
'SimpleValidator\\Validators\\Date' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Date.php',
|
||||
'SimpleValidator\\Validators\\Email' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Email.php',
|
||||
'SimpleValidator\\Validators\\Equals' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Equals.php',
|
||||
'SimpleValidator\\Validators\\GreaterThan' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/GreaterThan.php',
|
||||
'SimpleValidator\\Validators\\InArray' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/InArray.php',
|
||||
'SimpleValidator\\Validators\\Integer' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Integer.php',
|
||||
'SimpleValidator\\Validators\\Ip' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Ip.php',
|
||||
'SimpleValidator\\Validators\\Length' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Length.php',
|
||||
'SimpleValidator\\Validators\\MacAddress' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/MacAddress.php',
|
||||
'SimpleValidator\\Validators\\MaxLength' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/MaxLength.php',
|
||||
'SimpleValidator\\Validators\\MinLength' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/MinLength.php',
|
||||
'SimpleValidator\\Validators\\NotInArray' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/NotInArray.php',
|
||||
'SimpleValidator\\Validators\\Numeric' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Numeric.php',
|
||||
'SimpleValidator\\Validators\\Range' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Range.php',
|
||||
'SimpleValidator\\Validators\\Required' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Required.php',
|
||||
'SimpleValidator\\Validators\\Unique' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Unique.php',
|
||||
'SimpleValidator\\Validators\\Version' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Version.php',
|
||||
);
|
18
vendor/composer/autoload_files.php
vendored
Normal file
18
vendor/composer/autoload_files.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
// autoload_files.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
$baseDir . '/lib/Translator.php',
|
||||
$baseDir . '/models/config.php',
|
||||
$baseDir . '/models/user.php',
|
||||
$baseDir . '/models/feed.php',
|
||||
$baseDir . '/models/item.php',
|
||||
$baseDir . '/models/schema.php',
|
||||
$baseDir . '/models/auto_update.php',
|
||||
$baseDir . '/models/database.php',
|
||||
$baseDir . '/models/remember_me.php',
|
||||
);
|
14
vendor/composer/autoload_namespaces.php
vendored
Normal file
14
vendor/composer/autoload_namespaces.php
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'SimpleValidator' => array($vendorDir . '/fguillot/simple-validator/src'),
|
||||
'PicoFeed' => array($vendorDir . '/fguillot/picofeed/lib'),
|
||||
'PicoFarad' => array($vendorDir . '/fguillot/picofarad/lib'),
|
||||
'PicoDb' => array($vendorDir . '/fguillot/picodb/lib'),
|
||||
'JsonRPC' => array($vendorDir . '/fguillot/json-rpc/src'),
|
||||
);
|
9
vendor/composer/autoload_psr4.php
vendored
Normal file
9
vendor/composer/autoload_psr4.php
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
55
vendor/composer/autoload_real.php
vendored
Normal file
55
vendor/composer/autoload_real.php
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit1f0385c01a6e1fee49b051180b96fd66
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit1f0385c01a6e1fee49b051180b96fd66', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit1f0385c01a6e1fee49b051180b96fd66', 'loadClassLoader'));
|
||||
|
||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->set($namespace, $path);
|
||||
}
|
||||
|
||||
$map = require __DIR__ . '/autoload_psr4.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->setPsr4($namespace, $path);
|
||||
}
|
||||
|
||||
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||
if ($classMap) {
|
||||
$loader->addClassMap($classMap);
|
||||
}
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||
foreach ($includeFiles as $file) {
|
||||
composerRequire1f0385c01a6e1fee49b051180b96fd66($file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
|
||||
function composerRequire1f0385c01a6e1fee49b051180b96fd66($file)
|
||||
{
|
||||
require $file;
|
||||
}
|
197
vendor/composer/installed.json
vendored
Normal file
197
vendor/composer/installed.json
vendored
Normal file
@ -0,0 +1,197 @@
|
||||
[
|
||||
{
|
||||
"name": "fguillot/simple-validator",
|
||||
"version": "dev-master",
|
||||
"version_normalized": "9999999-dev",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fguillot/simpleValidator.git",
|
||||
"reference": "3bfa1ef0062906c83824ce8db1219914996d9bd4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fguillot/simpleValidator/zipball/3bfa1ef0062906c83824ce8db1219914996d9bd4",
|
||||
"reference": "3bfa1ef0062906c83824ce8db1219914996d9bd4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"time": "2014-11-25 22:58:14",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"SimpleValidator": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Frédéric Guillot",
|
||||
"homepage": "http://fredericguillot.com"
|
||||
}
|
||||
],
|
||||
"description": "The most easy to use validator library for PHP :)",
|
||||
"homepage": "https://github.com/fguillot/simpleValidator"
|
||||
},
|
||||
{
|
||||
"name": "fguillot/json-rpc",
|
||||
"version": "dev-master",
|
||||
"version_normalized": "9999999-dev",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fguillot/JsonRPC.git",
|
||||
"reference": "86e8339205616ad9b09d581957cc084a99c0ed27"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/86e8339205616ad9b09d581957cc084a99c0ed27",
|
||||
"reference": "86e8339205616ad9b09d581957cc084a99c0ed27",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"time": "2014-11-22 20:32:14",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"JsonRPC": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Unlicense"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Frédéric Guillot",
|
||||
"homepage": "http://fredericguillot.com"
|
||||
}
|
||||
],
|
||||
"description": "A simple Json-RPC client/server library that just works",
|
||||
"homepage": "https://github.com/fguillot/JsonRPC"
|
||||
},
|
||||
{
|
||||
"name": "fguillot/picodb",
|
||||
"version": "dev-master",
|
||||
"version_normalized": "9999999-dev",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fguillot/picoDb.git",
|
||||
"reference": "ebe721de0002b7ff86b7f66df0065224bf896eb2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fguillot/picoDb/zipball/ebe721de0002b7ff86b7f66df0065224bf896eb2",
|
||||
"reference": "ebe721de0002b7ff86b7f66df0065224bf896eb2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"time": "2014-11-22 04:15:43",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"PicoDb": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"WTFPL"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Frédéric Guillot",
|
||||
"homepage": "http://fredericguillot.com"
|
||||
}
|
||||
],
|
||||
"description": "Minimalist database query builder",
|
||||
"homepage": "https://github.com/fguillot/picoDb"
|
||||
},
|
||||
{
|
||||
"name": "fguillot/picofeed",
|
||||
"version": "dev-master",
|
||||
"version_normalized": "9999999-dev",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fguillot/picoFeed.git",
|
||||
"reference": "11589851f91cc3f04c84ba873484486d1457e638"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fguillot/picoFeed/zipball/11589851f91cc3f04c84ba873484486d1457e638",
|
||||
"reference": "11589851f91cc3f04c84ba873484486d1457e638",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"time": "2014-12-22 03:23:04",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"PicoFeed": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Unlicense"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Frédéric Guillot",
|
||||
"homepage": "http://fredericguillot.com"
|
||||
}
|
||||
],
|
||||
"description": "Modern library to write or read feeds (RSS/Atom)",
|
||||
"homepage": "http://fguillot.github.io/picoFeed"
|
||||
},
|
||||
{
|
||||
"name": "fguillot/picofarad",
|
||||
"version": "dev-master",
|
||||
"version_normalized": "9999999-dev",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fguillot/picoFarad.git",
|
||||
"reference": "df30333d5bf3b02f8f654c988c7c43305d5e6662"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fguillot/picoFarad/zipball/df30333d5bf3b02f8f654c988c7c43305d5e6662",
|
||||
"reference": "df30333d5bf3b02f8f654c988c7c43305d5e6662",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"time": "2014-11-01 15:01:02",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"PicoFarad": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Unlicense"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Frédéric Guillot",
|
||||
"homepage": "http://fredericguillot.com"
|
||||
}
|
||||
],
|
||||
"description": "Minimalist micro-framework",
|
||||
"homepage": "https://github.com/fguillot/picoFarad"
|
||||
}
|
||||
]
|
261
vendor/fguillot/json-rpc/README.markdown
vendored
Normal file
261
vendor/fguillot/json-rpc/README.markdown
vendored
Normal file
@ -0,0 +1,261 @@
|
||||
JsonRPC PHP Client and Server
|
||||
=============================
|
||||
|
||||
A simple Json-RPC client/server that just works.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- JSON-RPC 2.0 protocol only
|
||||
- The server support batch requests and notifications
|
||||
- Authentication and IP based client restrictions
|
||||
- Minimalist: there is only 2 files
|
||||
- Fully unit tested
|
||||
- License: Unlicense http://unlicense.org/
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
- The only dependency is the cURL extension
|
||||
- PHP >= 5.3
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
[Frédéric Guillot](http://fredericguillot.com)
|
||||
|
||||
Installation with Composer
|
||||
--------------------------
|
||||
|
||||
```bash
|
||||
composer require fguillot/json-rpc dev-master
|
||||
```
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
### Server
|
||||
|
||||
Callback binding:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Server;
|
||||
|
||||
$server = new Server;
|
||||
|
||||
// Procedures registration
|
||||
|
||||
$server->register('addition', function ($a, $b) {
|
||||
return $a + $b;
|
||||
});
|
||||
|
||||
$server->register('random', function ($start, $end) {
|
||||
return mt_rand($start, $end);
|
||||
});
|
||||
|
||||
// Return the response to the client
|
||||
echo $server->execute();
|
||||
```
|
||||
|
||||
Class/Method binding:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Server;
|
||||
|
||||
class Api
|
||||
{
|
||||
public function doSomething($arg1, $arg2 = 3)
|
||||
{
|
||||
return $arg1 + $arg2;
|
||||
}
|
||||
}
|
||||
|
||||
$server = new Server;
|
||||
|
||||
// Bind the method Api::doSomething() to the procedure myProcedure
|
||||
$server->bind('myProcedure', 'Api', 'doSomething');
|
||||
|
||||
// Use a class instance instead of the class name
|
||||
$server->bind('mySecondProcedure', new Api, 'doSomething');
|
||||
|
||||
echo $server->execute();
|
||||
```
|
||||
|
||||
### Client
|
||||
|
||||
Example with positional parameters:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Client;
|
||||
|
||||
$client = new Client('http://localhost/server.php');
|
||||
$result = $client->execute('addition', [3, 5]);
|
||||
|
||||
var_dump($result);
|
||||
```
|
||||
|
||||
Example with named arguments:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
require 'JsonRPC/Client.php';
|
||||
|
||||
use JsonRPC\Client;
|
||||
|
||||
$client = new Client('http://localhost/server.php');
|
||||
$result = $client->execute('random', ['end' => 10, 'start' => 1]);
|
||||
|
||||
var_dump($result);
|
||||
```
|
||||
|
||||
Arguments are called in the right order.
|
||||
|
||||
Examples with shortcut methods:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Client;
|
||||
|
||||
$client = new Client('http://localhost/server.php');
|
||||
$result = $client->random(50, 100);
|
||||
|
||||
var_dump($result);
|
||||
```
|
||||
|
||||
The example above use positional arguments for the request and this one use named arguments:
|
||||
|
||||
```php
|
||||
$result = $client->random(['end' => 10, 'start' => 1]);
|
||||
```
|
||||
|
||||
### Client batch requests
|
||||
|
||||
Call several procedures in a single HTTP request:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Client;
|
||||
|
||||
$client = new Client('http://localhost/server.php');
|
||||
|
||||
$results = $client->batch();
|
||||
->foo(['arg1' => 'bar'])
|
||||
->random(1, 100);
|
||||
->add(4, 3);
|
||||
->execute('add', [2, 5])
|
||||
->send();
|
||||
|
||||
print_r($results);
|
||||
```
|
||||
|
||||
All results are stored at the same position of the call.
|
||||
|
||||
### Client exceptions
|
||||
|
||||
- `BadFunctionCallException`: Procedure not found on the server
|
||||
- `InvalidArgumentException`: Wrong procedure arguments
|
||||
- `RuntimeException`: Protocol error
|
||||
|
||||
### Enable client debugging
|
||||
|
||||
You can enable the debug to see the JSON request and response:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Client;
|
||||
|
||||
$client = new Client('http://localhost/server.php');
|
||||
$client->debug = true;
|
||||
```
|
||||
|
||||
The debug output is sent to the PHP's system logger.
|
||||
You can configure the log destination in your `php.ini`.
|
||||
|
||||
Output example:
|
||||
|
||||
```json
|
||||
==> Request:
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "removeCategory",
|
||||
"id": 486782327,
|
||||
"params": [
|
||||
1
|
||||
]
|
||||
}
|
||||
==> Response:
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 486782327,
|
||||
"result": true
|
||||
}
|
||||
```
|
||||
|
||||
### IP based client restrictions
|
||||
|
||||
The server can allow only some IP adresses:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Server;
|
||||
|
||||
$server = new Server;
|
||||
|
||||
// IP client restrictions
|
||||
$server->allowHosts(['192.168.0.1', '127.0.0.1']);
|
||||
|
||||
// Procedures registration
|
||||
|
||||
[...]
|
||||
|
||||
// Return the response to the client
|
||||
echo $server->execute();
|
||||
```
|
||||
|
||||
If the client is blocked, you got a 403 Forbidden HTTP response.
|
||||
|
||||
### HTTP Basic Authentication
|
||||
|
||||
If you use HTTPS, you can allow client by using a username/password.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Server;
|
||||
|
||||
$server = new Server;
|
||||
|
||||
// List of users to allow
|
||||
$server->authentication(['jsonrpc' => 'toto']);
|
||||
|
||||
// Procedures registration
|
||||
|
||||
[...]
|
||||
|
||||
// Return the response to the client
|
||||
echo $server->execute();
|
||||
```
|
||||
|
||||
On the client, set credentials like that:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Client;
|
||||
|
||||
$client = new Client('http://localhost/server.php');
|
||||
$client->authentication('jsonrpc', 'toto');
|
||||
```
|
||||
|
||||
If the authentication failed, the client throw a RuntimeException.
|
19
vendor/fguillot/json-rpc/composer.json
vendored
Normal file
19
vendor/fguillot/json-rpc/composer.json
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "fguillot/json-rpc",
|
||||
"description": "A simple Json-RPC client/server library that just works",
|
||||
"homepage": "https://github.com/fguillot/JsonRPC",
|
||||
"type": "library",
|
||||
"license": "Unlicense",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Frédéric Guillot",
|
||||
"homepage": "http://fredericguillot.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {"JsonRPC": "src/"}
|
||||
}
|
||||
}
|
@ -2,7 +2,9 @@
|
||||
|
||||
namespace JsonRPC;
|
||||
|
||||
use RuntimeException;
|
||||
use BadFunctionCallException;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* JsonRPC client class
|
||||
@ -45,6 +47,22 @@ class Client
|
||||
*/
|
||||
private $password;
|
||||
|
||||
/**
|
||||
* True for a batch request
|
||||
*
|
||||
* @access public
|
||||
* @var boolean
|
||||
*/
|
||||
public $is_batch = false;
|
||||
|
||||
/**
|
||||
* Batch payload
|
||||
*
|
||||
* @access public
|
||||
* @var array
|
||||
*/
|
||||
public $batch = array();
|
||||
|
||||
/**
|
||||
* Enable debug output to the php error log
|
||||
*
|
||||
@ -88,8 +106,13 @@ class Client
|
||||
* @param array $params Procedure arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $params)
|
||||
public function __call($method, array $params)
|
||||
{
|
||||
// Allow to pass an array and use named arguments
|
||||
if (count($params) === 1 && is_array($params[0])) {
|
||||
$params = $params[0];
|
||||
}
|
||||
|
||||
return $this->execute($method, $params);
|
||||
}
|
||||
|
||||
@ -107,35 +130,144 @@ class Client
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute
|
||||
* Start a batch request
|
||||
*
|
||||
* @access public
|
||||
* @return Client
|
||||
*/
|
||||
public function batch()
|
||||
{
|
||||
$this->is_batch = true;
|
||||
$this->batch = array();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a batch request
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function send()
|
||||
{
|
||||
$this->is_batch = false;
|
||||
|
||||
return $this->parseResponse(
|
||||
$this->doRequest($this->batch)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a procedure
|
||||
*
|
||||
* @access public
|
||||
* @throws BadFunctionCallException Exception thrown when a bad request is made (missing argument/procedure)
|
||||
* @param string $procedure Procedure name
|
||||
* @param array $params Procedure arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function execute($procedure, array $params = array())
|
||||
{
|
||||
$id = mt_rand();
|
||||
if ($this->is_batch) {
|
||||
$this->batch[] = $this->prepareRequest($procedure, $params);
|
||||
return $this;
|
||||
}
|
||||
|
||||
return $this->parseResponse(
|
||||
$this->doRequest($this->prepareRequest($procedure, $params))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the payload
|
||||
*
|
||||
* @access public
|
||||
* @param string $procedure Procedure name
|
||||
* @param array $params Procedure arguments
|
||||
* @return array
|
||||
*/
|
||||
public function prepareRequest($procedure, array $params = array())
|
||||
{
|
||||
$payload = array(
|
||||
'jsonrpc' => '2.0',
|
||||
'method' => $procedure,
|
||||
'id' => $id
|
||||
'id' => mt_rand()
|
||||
);
|
||||
|
||||
if (! empty($params)) {
|
||||
$payload['params'] = $params;
|
||||
}
|
||||
|
||||
$result = $this->doRequest($payload);
|
||||
return $payload;
|
||||
}
|
||||
|
||||
if (isset($result['id']) && $result['id'] == $id && array_key_exists('result', $result)) {
|
||||
return $result['result'];
|
||||
/**
|
||||
* Parse the response and return the procedure result
|
||||
*
|
||||
* @access public
|
||||
* @param array $payload
|
||||
* @return mixed
|
||||
*/
|
||||
public function parseResponse(array $payload)
|
||||
{
|
||||
if ($this->isBatchResponse($payload)) {
|
||||
|
||||
$results = array();
|
||||
|
||||
foreach ($payload as $response) {
|
||||
$results[] = $this->getResult($response);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
throw new BadFunctionCallException('Bad Request');
|
||||
return $this->getResult($payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we have a batch response
|
||||
*
|
||||
* @access public
|
||||
* @param array $payload
|
||||
* @return boolean
|
||||
*/
|
||||
private function isBatchResponse(array $payload)
|
||||
{
|
||||
return array_keys($payload) === range(0, count($payload) - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a RPC call result
|
||||
*
|
||||
* @access public
|
||||
* @param array $payload
|
||||
* @return mixed
|
||||
*/
|
||||
public function getResult(array $payload)
|
||||
{
|
||||
if (isset($payload['error']['code'])) {
|
||||
$this->handleRpcErrors($payload['error']['code']);
|
||||
}
|
||||
|
||||
return isset($payload['result']) ? $payload['result'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw an exception according the RPC error
|
||||
*
|
||||
* @access public
|
||||
* @param integer $code
|
||||
*/
|
||||
public function handleRpcErrors($code)
|
||||
{
|
||||
switch ($code) {
|
||||
case -32601:
|
||||
throw new BadFunctionCallException('Procedure not found');
|
||||
case -32602:
|
||||
throw new InvalidArgumentException('Invalid arguments');
|
||||
default:
|
||||
throw new RuntimeException('Invalid request/response');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -162,8 +294,14 @@ class Client
|
||||
curl_setopt($ch, CURLOPT_USERPWD, $this->username.':'.$this->password);
|
||||
}
|
||||
|
||||
$result = curl_exec($ch);
|
||||
$response = json_decode($result, true);
|
||||
$http_body = curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
if ($http_code === 401 || $http_code === 403) {
|
||||
throw new RuntimeException('Access denied');
|
||||
}
|
||||
|
||||
$response = json_decode($http_body, true);
|
||||
|
||||
if ($this->debug) {
|
||||
error_log('==> Request: '.PHP_EOL.json_encode($payload, JSON_PRETTY_PRINT));
|
447
vendor/fguillot/json-rpc/src/JsonRPC/Server.php
vendored
Normal file
447
vendor/fguillot/json-rpc/src/JsonRPC/Server.php
vendored
Normal file
@ -0,0 +1,447 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC;
|
||||
|
||||
use Closure;
|
||||
use BadFunctionCallException;
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use ReflectionFunction;
|
||||
use ReflectionMethod;
|
||||
|
||||
class InvalidJsonRpcFormat extends Exception {};
|
||||
class InvalidJsonFormat extends Exception {};
|
||||
|
||||
/**
|
||||
* JsonRPC server class
|
||||
*
|
||||
* @package JsonRPC
|
||||
* @author Frederic Guillot
|
||||
* @license Unlicense http://unlicense.org/
|
||||
*/
|
||||
class Server
|
||||
{
|
||||
/**
|
||||
* Data received from the client
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $payload;
|
||||
|
||||
/**
|
||||
* List of procedures
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $callbacks = array();
|
||||
|
||||
/**
|
||||
* List of classes
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $classes = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param string $payload Client data
|
||||
* @param array $callbacks Callbacks
|
||||
* @param array $classes Classes
|
||||
*/
|
||||
public function __construct($payload = '', array $callbacks = array(), array $classes = array())
|
||||
{
|
||||
$this->payload = $payload;
|
||||
$this->callbacks = $callbacks;
|
||||
$this->classes = $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* IP based client restrictions
|
||||
*
|
||||
* Return an HTTP error 403 if the client is not allowed
|
||||
*
|
||||
* @access public
|
||||
* @param array $hosts List of hosts
|
||||
*/
|
||||
public function allowHosts(array $hosts) {
|
||||
|
||||
if (! in_array($_SERVER['REMOTE_ADDR'], $hosts)) {
|
||||
|
||||
header('Content-Type: application/json');
|
||||
header('HTTP/1.0 403 Forbidden');
|
||||
echo '{"error": "Access Forbidden"}';
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP Basic authentication
|
||||
*
|
||||
* Return an HTTP error 401 if the client is not allowed
|
||||
*
|
||||
* @access public
|
||||
* @param array $users Map of username/password
|
||||
*/
|
||||
public function authentication(array $users)
|
||||
{
|
||||
if (! isset($_SERVER['PHP_AUTH_USER']) ||
|
||||
! isset($users[$_SERVER['PHP_AUTH_USER']]) ||
|
||||
$users[$_SERVER['PHP_AUTH_USER']] !== $_SERVER['PHP_AUTH_PW']) {
|
||||
|
||||
header('WWW-Authenticate: Basic realm="JsonRPC"');
|
||||
header('Content-Type: application/json');
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
echo '{"error": "Authentication failed"}';
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new procedure
|
||||
*
|
||||
* @access public
|
||||
* @param string $procedure Procedure name
|
||||
* @param closure $callback Callback
|
||||
*/
|
||||
public function register($name, Closure $callback)
|
||||
{
|
||||
$this->callbacks[$name] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a procedure to a class
|
||||
*
|
||||
* @access public
|
||||
* @param string $procedure Procedure name
|
||||
* @param mixed $class Class name or instance
|
||||
* @param string $method Procedure name
|
||||
*/
|
||||
public function bind($procedure, $class, $method)
|
||||
{
|
||||
$this->classes[$procedure] = array($class, $method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the response to the client
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Data to send to the client
|
||||
* @param array $payload Incoming data
|
||||
* @return string
|
||||
*/
|
||||
public function getResponse(array $data, array $payload = array())
|
||||
{
|
||||
if (! array_key_exists('id', $payload)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$response = array(
|
||||
'jsonrpc' => '2.0',
|
||||
'id' => $payload['id']
|
||||
);
|
||||
|
||||
$response = array_merge($response, $data);
|
||||
|
||||
@header('Content-Type: application/json');
|
||||
return json_encode($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the payload and test if the parsed JSON is ok
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function checkJsonFormat()
|
||||
{
|
||||
if (empty($this->payload)) {
|
||||
$this->payload = file_get_contents('php://input');
|
||||
}
|
||||
|
||||
if (is_string($this->payload)) {
|
||||
$this->payload = json_decode($this->payload, true);
|
||||
}
|
||||
|
||||
if (! is_array($this->payload)) {
|
||||
throw new InvalidJsonFormat('Malformed payload');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if all required JSON-RPC parameters are here
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function checkRpcFormat()
|
||||
{
|
||||
if (! isset($this->payload['jsonrpc']) ||
|
||||
! isset($this->payload['method']) ||
|
||||
! is_string($this->payload['method']) ||
|
||||
$this->payload['jsonrpc'] !== '2.0' ||
|
||||
(isset($this->payload['params']) && ! is_array($this->payload['params']))) {
|
||||
|
||||
throw new InvalidJsonRpcFormat('Invalid JSON RPC payload');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we have a batch request
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
private function isBatchRequest()
|
||||
{
|
||||
return array_keys($this->payload) === range(0, count($this->payload) - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle batch request
|
||||
*
|
||||
* @access private
|
||||
* @return string
|
||||
*/
|
||||
private function handleBatchRequest()
|
||||
{
|
||||
$responses = array();
|
||||
|
||||
foreach ($this->payload as $payload) {
|
||||
|
||||
if (! is_array($payload)) {
|
||||
|
||||
$responses[] = $this->getResponse(array(
|
||||
'error' => array(
|
||||
'code' => -32600,
|
||||
'message' => 'Invalid Request'
|
||||
)),
|
||||
array('id' => null)
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
||||
$server = new Server($payload, $this->callbacks, $this->classes);
|
||||
$response = $server->execute();
|
||||
|
||||
if ($response) {
|
||||
$responses[] = $response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return empty($responses) ? '' : '['.implode(',', $responses).']';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse incoming requests
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
try {
|
||||
|
||||
$this->checkJsonFormat();
|
||||
|
||||
if ($this->isBatchRequest()){
|
||||
return $this->handleBatchRequest();
|
||||
}
|
||||
|
||||
$this->checkRpcFormat();
|
||||
|
||||
$result = $this->executeProcedure(
|
||||
$this->payload['method'],
|
||||
empty($this->payload['params']) ? array() : $this->payload['params']
|
||||
);
|
||||
|
||||
return $this->getResponse(array('result' => $result), $this->payload);
|
||||
}
|
||||
catch (InvalidJsonFormat $e) {
|
||||
|
||||
return $this->getResponse(array(
|
||||
'error' => array(
|
||||
'code' => -32700,
|
||||
'message' => 'Parse error'
|
||||
)),
|
||||
array('id' => null)
|
||||
);
|
||||
}
|
||||
catch (InvalidJsonRpcFormat $e) {
|
||||
|
||||
return $this->getResponse(array(
|
||||
'error' => array(
|
||||
'code' => -32600,
|
||||
'message' => 'Invalid Request'
|
||||
)),
|
||||
array('id' => null)
|
||||
);
|
||||
}
|
||||
catch (BadFunctionCallException $e) {
|
||||
|
||||
return $this->getResponse(array(
|
||||
'error' => array(
|
||||
'code' => -32601,
|
||||
'message' => 'Method not found'
|
||||
)),
|
||||
$this->payload
|
||||
);
|
||||
}
|
||||
catch (InvalidArgumentException $e) {
|
||||
|
||||
return $this->getResponse(array(
|
||||
'error' => array(
|
||||
'code' => -32602,
|
||||
'message' => 'Invalid params'
|
||||
)),
|
||||
$this->payload
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the procedure
|
||||
*
|
||||
* @access public
|
||||
* @param string $procedure Procedure name
|
||||
* @param array $params Procedure params
|
||||
* @return mixed
|
||||
*/
|
||||
public function executeProcedure($procedure, array $params = array())
|
||||
{
|
||||
if (isset($this->callbacks[$procedure])) {
|
||||
return $this->executeCallback($this->callbacks[$procedure], $params);
|
||||
}
|
||||
else if (isset($this->classes[$procedure])) {
|
||||
return $this->executeMethod($this->classes[$procedure][0], $this->classes[$procedure][1], $params);
|
||||
}
|
||||
|
||||
throw new BadFunctionCallException('Unable to find the procedure');
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a callback
|
||||
*
|
||||
* @access public
|
||||
* @param Closure $callback Callback
|
||||
* @param array $params Procedure params
|
||||
* @return mixed
|
||||
*/
|
||||
public function executeCallback(Closure $callback, $params)
|
||||
{
|
||||
$reflection = new ReflectionFunction($callback);
|
||||
|
||||
$arguments = $this->getArguments(
|
||||
$params,
|
||||
$reflection->getParameters(),
|
||||
$reflection->getNumberOfRequiredParameters(),
|
||||
$reflection->getNumberOfParameters()
|
||||
);
|
||||
|
||||
return $reflection->invokeArgs($arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a method
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $class Class name or instance
|
||||
* @param string $method Method name
|
||||
* @param array $params Procedure params
|
||||
* @return mixed
|
||||
*/
|
||||
public function executeMethod($class, $method, $params)
|
||||
{
|
||||
$reflection = new ReflectionMethod($class, $method);
|
||||
|
||||
$arguments = $this->getArguments(
|
||||
$params,
|
||||
$reflection->getParameters(),
|
||||
$reflection->getNumberOfRequiredParameters(),
|
||||
$reflection->getNumberOfParameters()
|
||||
);
|
||||
|
||||
return $reflection->invokeArgs(
|
||||
is_string($class) ? new $class : $class,
|
||||
$arguments
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get procedure arguments
|
||||
*
|
||||
* @access public
|
||||
* @param array $request_params Incoming arguments
|
||||
* @param array $method_params Procedure arguments
|
||||
* @param integer $nb_required_params Number of required parameters
|
||||
* @param integer $nb_max_params Maximum number of parameters
|
||||
* @return array
|
||||
*/
|
||||
public function getArguments(array $request_params, array $method_params, $nb_required_params, $nb_max_params)
|
||||
{
|
||||
$nb_params = count($request_params);
|
||||
|
||||
if ($nb_params < $nb_required_params) {
|
||||
throw new InvalidArgumentException('Wrong number of arguments');
|
||||
}
|
||||
|
||||
if ($nb_params > $nb_max_params) {
|
||||
throw new InvalidArgumentException('Too many arguments');
|
||||
}
|
||||
|
||||
if ($this->isPositionalArguments($request_params, $method_params)) {
|
||||
return $request_params;
|
||||
}
|
||||
|
||||
return $this->getNamedArguments($request_params, $method_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we have positional parametes
|
||||
*
|
||||
* @access public
|
||||
* @param array $request_params Incoming arguments
|
||||
* @param array $method_params Procedure arguments
|
||||
* @return bool
|
||||
*/
|
||||
public function isPositionalArguments(array $request_params, array $method_params)
|
||||
{
|
||||
return array_keys($request_params) === range(0, count($request_params) - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get named arguments
|
||||
*
|
||||
* @access public
|
||||
* @param array $request_params Incoming arguments
|
||||
* @param array $method_params Procedure arguments
|
||||
* @return array
|
||||
*/
|
||||
public function getNamedArguments(array $request_params, array $method_params)
|
||||
{
|
||||
$params = array();
|
||||
|
||||
foreach ($method_params as $p) {
|
||||
|
||||
$name = $p->getName();
|
||||
|
||||
if (isset($request_params[$name])) {
|
||||
$params[$name] = $request_params[$name];
|
||||
}
|
||||
else if ($p->isDefaultValueAvailable()) {
|
||||
$params[$name] = $p->getDefaultValue();
|
||||
}
|
||||
else {
|
||||
throw new InvalidArgumentException('Missing argument: '.$name);
|
||||
}
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
114
vendor/fguillot/json-rpc/tests/ClientTest.php
vendored
Normal file
114
vendor/fguillot/json-rpc/tests/ClientTest.php
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
require_once 'src/JsonRPC/Client.php';
|
||||
|
||||
use JsonRPC\Client;
|
||||
|
||||
class ClientTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testParseReponse()
|
||||
{
|
||||
$client = new Client('http://localhost/');
|
||||
|
||||
$this->assertEquals(
|
||||
-19,
|
||||
$client->parseResponse(json_decode('{"jsonrpc": "2.0", "result": -19, "id": 1}', true))
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
null,
|
||||
$client->parseResponse(json_decode('{"jsonrpc": "2.0", "id": 1}', true))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException BadFunctionCallException
|
||||
*/
|
||||
public function testBadProcedure()
|
||||
{
|
||||
$client = new Client('http://localhost/');
|
||||
$client->parseResponse(json_decode('{"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "1"}', true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testInvalidArgs()
|
||||
{
|
||||
$client = new Client('http://localhost/');
|
||||
$client->parseResponse(json_decode('{"jsonrpc": "2.0", "error": {"code": -32602, "message": "Invalid params"}, "id": "1"}', true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException RuntimeException
|
||||
*/
|
||||
public function testInvalidRequest()
|
||||
{
|
||||
$client = new Client('http://localhost/');
|
||||
$client->parseResponse(json_decode('{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}', true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException RuntimeException
|
||||
*/
|
||||
public function testParseError()
|
||||
{
|
||||
$client = new Client('http://localhost/');
|
||||
$client->parseResponse(json_decode('{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}', true));
|
||||
}
|
||||
|
||||
public function testPrepareRequest()
|
||||
{
|
||||
$client = new Client('http://localhost/');
|
||||
|
||||
$payload = $client->prepareRequest('myProcedure');
|
||||
$this->assertNotEmpty($payload);
|
||||
$this->assertArrayHasKey('jsonrpc', $payload);
|
||||
$this->assertEquals('2.0', $payload['jsonrpc']);
|
||||
$this->assertArrayHasKey('method', $payload);
|
||||
$this->assertEquals('myProcedure', $payload['method']);
|
||||
$this->assertArrayHasKey('id', $payload);
|
||||
$this->assertArrayNotHasKey('params', $payload);
|
||||
|
||||
$payload = $client->prepareRequest('myProcedure', array('p1' => 3));
|
||||
$this->assertNotEmpty($payload);
|
||||
$this->assertArrayHasKey('jsonrpc', $payload);
|
||||
$this->assertEquals('2.0', $payload['jsonrpc']);
|
||||
$this->assertArrayHasKey('method', $payload);
|
||||
$this->assertEquals('myProcedure', $payload['method']);
|
||||
$this->assertArrayHasKey('id', $payload);
|
||||
$this->assertArrayHasKey('params', $payload);
|
||||
$this->assertEquals(array('p1' => 3), $payload['params']);
|
||||
}
|
||||
|
||||
public function testBatchRequest()
|
||||
{
|
||||
$client = new Client('http://localhost/');
|
||||
|
||||
$batch = $client->batch();
|
||||
|
||||
$this->assertInstanceOf('JsonRpc\Client', $batch);
|
||||
$this->assertTrue($client->is_batch);
|
||||
|
||||
$batch->random(1, 30);
|
||||
$batch->add(3, 5);
|
||||
$batch->execute('foo', array('p1' => 42, 'p3' => 3));
|
||||
|
||||
$this->assertNotEmpty($client->batch);
|
||||
$this->assertEquals(3, count($client->batch));
|
||||
|
||||
$this->assertEquals('random', $client->batch[0]['method']);
|
||||
$this->assertEquals('add', $client->batch[1]['method']);
|
||||
$this->assertEquals('foo', $client->batch[2]['method']);
|
||||
|
||||
$this->assertEquals(array(1, 30), $client->batch[0]['params']);
|
||||
$this->assertEquals(array(3, 5), $client->batch[1]['params']);
|
||||
$this->assertEquals(array('p1' => 42, 'p3' => 3), $client->batch[2]['params']);
|
||||
|
||||
$batch = $client->batch();
|
||||
|
||||
$this->assertInstanceOf('JsonRpc\Client', $batch);
|
||||
$this->assertTrue($client->is_batch);
|
||||
$this->assertEmpty($client->batch);
|
||||
}
|
||||
}
|
142
vendor/fguillot/json-rpc/tests/ServerProcedureTest.php
vendored
Normal file
142
vendor/fguillot/json-rpc/tests/ServerProcedureTest.php
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
require_once 'src/JsonRPC/Server.php';
|
||||
|
||||
use JsonRPC\Server;
|
||||
|
||||
class A
|
||||
{
|
||||
public function getAll($p1, $p2, $p3 = 4)
|
||||
{
|
||||
return $p1 + $p2 + $p3;
|
||||
}
|
||||
}
|
||||
|
||||
class B
|
||||
{
|
||||
public function getAll($p1)
|
||||
{
|
||||
return $p1 + 2;
|
||||
}
|
||||
}
|
||||
|
||||
class ServerProcedureTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @expectedException BadFunctionCallException
|
||||
*/
|
||||
public function testProcedureNotFound()
|
||||
{
|
||||
$server = new Server;
|
||||
$server->executeProcedure('a');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException BadFunctionCallException
|
||||
*/
|
||||
public function testCallbackNotFound()
|
||||
{
|
||||
$server = new Server;
|
||||
$server->register('b', function() {});
|
||||
$server->executeProcedure('a');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException ReflectionException
|
||||
*/
|
||||
public function testClassNotFound()
|
||||
{
|
||||
$server = new Server;
|
||||
$server->bind('getAllTasks', 'c', 'getAll');
|
||||
$server->executeProcedure('getAllTasks');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException ReflectionException
|
||||
*/
|
||||
public function testMethodNotFound()
|
||||
{
|
||||
$server = new Server;
|
||||
$server->bind('getAllTasks', 'A', 'getNothing');
|
||||
$server->executeProcedure('getAllTasks');
|
||||
}
|
||||
|
||||
public function testIsPositionalArguments()
|
||||
{
|
||||
$server = new Server;
|
||||
$this->assertFalse($server->isPositionalArguments(
|
||||
array('a' => 'b', 'c' => 'd'),
|
||||
array('a' => 'b', 'c' => 'd')
|
||||
));
|
||||
|
||||
$server = new Server;
|
||||
$this->assertTrue($server->isPositionalArguments(
|
||||
array('a', 'b', 'c'),
|
||||
array('a' => 'b', 'c' => 'd')
|
||||
));
|
||||
}
|
||||
|
||||
public function testBindNamedArguments()
|
||||
{
|
||||
$server = new Server;
|
||||
$server->bind('getAllA', 'A', 'getAll');
|
||||
$server->bind('getAllB', 'B', 'getAll');
|
||||
$server->bind('getAllC', new B, 'getAll');
|
||||
$this->assertEquals(6, $server->executeProcedure('getAllA', array('p2' => 4, 'p1' => -2)));
|
||||
$this->assertEquals(10, $server->executeProcedure('getAllA', array('p2' => 4, 'p3' => 8, 'p1' => -2)));
|
||||
$this->assertEquals(6, $server->executeProcedure('getAllB', array('p1' => 4)));
|
||||
$this->assertEquals(5, $server->executeProcedure('getAllC', array('p1' => 3)));
|
||||
}
|
||||
|
||||
public function testBindPositionalArguments()
|
||||
{
|
||||
$server = new Server;
|
||||
$server->bind('getAllA', 'A', 'getAll');
|
||||
$server->bind('getAllB', 'B', 'getAll');
|
||||
$this->assertEquals(6, $server->executeProcedure('getAllA', array(4, -2)));
|
||||
$this->assertEquals(2, $server->executeProcedure('getAllA', array(4, 0, -2)));
|
||||
$this->assertEquals(4, $server->executeProcedure('getAllB', array(2)));
|
||||
}
|
||||
|
||||
public function testRegisterNamedArguments()
|
||||
{
|
||||
$server = new Server;
|
||||
$server->register('getAllA', function($p1, $p2, $p3 = 4) {
|
||||
return $p1 + $p2 + $p3;
|
||||
});
|
||||
|
||||
$this->assertEquals(6, $server->executeProcedure('getAllA', array('p2' => 4, 'p1' => -2)));
|
||||
$this->assertEquals(10, $server->executeProcedure('getAllA', array('p2' => 4, 'p3' => 8, 'p1' => -2)));
|
||||
}
|
||||
|
||||
public function testRegisterPositionalArguments()
|
||||
{
|
||||
$server = new Server;
|
||||
$server->register('getAllA', function($p1, $p2, $p3 = 4) {
|
||||
return $p1 + $p2 + $p3;
|
||||
});
|
||||
|
||||
$this->assertEquals(6, $server->executeProcedure('getAllA', array(4, -2)));
|
||||
$this->assertEquals(2, $server->executeProcedure('getAllA', array(4, 0, -2)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testTooManyArguments()
|
||||
{
|
||||
$server = new Server;
|
||||
$server->bind('getAllC', new B, 'getAll');
|
||||
$server->executeProcedure('getAllC', array('p1' => 3, 'p2' => 5));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testNotEnoughArguments()
|
||||
{
|
||||
$server = new Server;
|
||||
$server->bind('getAllC', new B, 'getAll');
|
||||
$server->executeProcedure('getAllC');
|
||||
}
|
||||
}
|
217
vendor/fguillot/json-rpc/tests/ServerProtocolTest.php
vendored
Normal file
217
vendor/fguillot/json-rpc/tests/ServerProtocolTest.php
vendored
Normal file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
require_once 'src/JsonRPC/Server.php';
|
||||
|
||||
use JsonRPC\Server;
|
||||
|
||||
class ServerProtocolTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testPositionalParameters()
|
||||
{
|
||||
$subtract = function($minuend, $subtrahend) {
|
||||
return $minuend - $subtrahend;
|
||||
};
|
||||
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}');
|
||||
$server->register('subtract', $subtract);
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0", "result": 19, "id": 1}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": "subtract", "params": [23, 42], "id": 1}');
|
||||
$server->register('subtract', $subtract);
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0", "result": -19, "id": 1}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testNamedParameters()
|
||||
{
|
||||
$subtract = function($minuend, $subtrahend) {
|
||||
return $minuend - $subtrahend;
|
||||
};
|
||||
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3}');
|
||||
$server->register('subtract', $subtract);
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0", "result": 19, "id": 3}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "id": 4}');
|
||||
$server->register('subtract', $subtract);
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0", "result": 19, "id": 4}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testNotification()
|
||||
{
|
||||
$update = function($p1, $p2, $p3, $p4, $p5) {};
|
||||
$foobar = function() {};
|
||||
|
||||
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": "update", "params": [1,2,3,4,5]}');
|
||||
$server->register('update', $update);
|
||||
$server->register('foobar', $foobar);
|
||||
|
||||
$this->assertEquals('', $server->execute());
|
||||
|
||||
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": "foobar"}');
|
||||
$server->register('update', $update);
|
||||
$server->register('foobar', $foobar);
|
||||
|
||||
$this->assertEquals('', $server->execute());
|
||||
}
|
||||
|
||||
|
||||
public function testNoMethod()
|
||||
{
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": "foobar", "id": "1"}');
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "1"}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testInvalidJson()
|
||||
{
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": "foobar, "params": "bar", "baz]');
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testInvalidRequest()
|
||||
{
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": 1, "params": "bar"}');
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testBatchInvalidJson()
|
||||
{
|
||||
$server = new Server('[
|
||||
{"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},
|
||||
{"jsonrpc": "2.0", "method"
|
||||
]');
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testBatchEmptyArray()
|
||||
{
|
||||
$server = new Server('[]');
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testBatchNotEmptyButInvalid()
|
||||
{
|
||||
$server = new Server('[1]');
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('[{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}]', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testBatchInvalid()
|
||||
{
|
||||
$server = new Server('[1,2,3]');
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('[
|
||||
{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null},
|
||||
{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null},
|
||||
{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}
|
||||
]', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testBatchOk()
|
||||
{
|
||||
$server = new Server('[
|
||||
{"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},
|
||||
{"jsonrpc": "2.0", "method": "notify_hello", "params": [7]},
|
||||
{"jsonrpc": "2.0", "method": "subtract", "params": [42,23], "id": "2"},
|
||||
{"foo": "boo"},
|
||||
{"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"},
|
||||
{"jsonrpc": "2.0", "method": "get_data", "id": "9"}
|
||||
]');
|
||||
|
||||
$server->register('sum', function($a, $b, $c) {
|
||||
return $a + $b + $c;
|
||||
});
|
||||
|
||||
$server->register('subtract', function($minuend, $subtrahend) {
|
||||
return $minuend - $subtrahend;
|
||||
});
|
||||
|
||||
$server->register('get_data', function() {
|
||||
return array('hello', 5);
|
||||
});
|
||||
|
||||
$response = $server->execute();
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('[
|
||||
{"jsonrpc": "2.0", "result": 7, "id": "1"},
|
||||
{"jsonrpc": "2.0", "result": 19, "id": "2"},
|
||||
{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null},
|
||||
{"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "5"},
|
||||
{"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"}
|
||||
]', true),
|
||||
json_decode($response, true)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testBatchNotifications()
|
||||
{
|
||||
$server = new Server('[
|
||||
{"jsonrpc": "2.0", "method": "notify_sum", "params": [1,2,4]},
|
||||
{"jsonrpc": "2.0", "method": "notify_hello", "params": [7]}
|
||||
]');
|
||||
|
||||
$server->register('notify_sum', function($a, $b, $c) {
|
||||
|
||||
});
|
||||
|
||||
$server->register('notify_hello', function($id) {
|
||||
|
||||
});
|
||||
|
||||
$this->assertEquals('', $server->execute());
|
||||
}
|
||||
}
|
42
vendor/fguillot/picodb/.gitignore
vendored
Normal file
42
vendor/fguillot/picodb/.gitignore
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
# Compiled source #
|
||||
###################
|
||||
*.com
|
||||
*.class
|
||||
*.dll
|
||||
*.exe
|
||||
*.o
|
||||
*.so
|
||||
|
||||
# Packages #
|
||||
############
|
||||
# it's better to unpack these files and commit the raw source
|
||||
# git has its own built in compression methods
|
||||
*.7z
|
||||
*.dmg
|
||||
*.gz
|
||||
*.iso
|
||||
*.jar
|
||||
*.rar
|
||||
*.tar
|
||||
*.zip
|
||||
|
||||
# Logs and databases #
|
||||
######################
|
||||
*.log
|
||||
*.sql
|
||||
*.sqlite
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
ehthumbs.db
|
||||
Icon?
|
||||
Thumbs.db
|
||||
*.swp
|
||||
*~
|
||||
*.lock
|
||||
|
||||
# App specific #
|
||||
################
|
||||
example.php
|
275
vendor/fguillot/picodb/README.md
vendored
Normal file
275
vendor/fguillot/picodb/README.md
vendored
Normal file
@ -0,0 +1,275 @@
|
||||
PicoDb
|
||||
======
|
||||
|
||||
PicoDb is a minimalist database query builder for PHP
|
||||
**It's not an ORM**.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- No dependency
|
||||
- Easy to use, fast and very lightweight
|
||||
- Use prepared statements
|
||||
- Handle schema versions (migrations)
|
||||
- License: [WTFPL](http://www.wtfpl.net)
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
- PHP >= 5.3
|
||||
- PDO
|
||||
- A database: Sqlite, Mysql or Postgresql
|
||||
|
||||
Todo
|
||||
----
|
||||
|
||||
- Add driver for Postgresql
|
||||
- Add support for Distinct...
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
## Connect to your database
|
||||
|
||||
use PicoDb\Database;
|
||||
|
||||
// Sqlite driver
|
||||
$db = new Database(['driver' => 'sqlite', 'filename' => ':memory:']);
|
||||
|
||||
// Mysql driver
|
||||
// Optional options: "schema_table" (the default table name is "schema_version")
|
||||
$db = new Database(array(
|
||||
'driver' => 'mysql',
|
||||
'hostname' => 'localhost',
|
||||
'username' => 'root',
|
||||
'password' => '',
|
||||
'database' => 'my_db_name',
|
||||
'charset' => 'utf8',
|
||||
));
|
||||
|
||||
## Execute a SQL request
|
||||
|
||||
$db->execute('CREATE TABLE toto (column1 TEXT)');
|
||||
|
||||
## Insert some data
|
||||
|
||||
$db->table('toto')->save(['column1' => 'hey']);
|
||||
|
||||
## Transations
|
||||
|
||||
$db->transaction(function($db) {
|
||||
$db->table('toto')->save(['column1' => 'foo']);
|
||||
$db->table('toto')->save(['column1' => 'bar']);
|
||||
});
|
||||
|
||||
## Fetch all data
|
||||
|
||||
$records = $db->table('toto')->findAll();
|
||||
|
||||
foreach ($records as $record) {
|
||||
var_dump($record['column1']);
|
||||
}
|
||||
|
||||
## Update something
|
||||
|
||||
$db->table('toto')->eq('id', 1)->save(['column1' => 'hey']);
|
||||
|
||||
You just need to add a condition to perform an update.
|
||||
|
||||
## Remove rows
|
||||
|
||||
$db->table('toto')->lowerThan('column1', 10)->remove();
|
||||
|
||||
## Sorting
|
||||
|
||||
$db->table('toto')->asc('column1')->findAll();
|
||||
|
||||
or
|
||||
|
||||
$db->table('toto')->desc('column1')->findAll();
|
||||
|
||||
## Limit and offset
|
||||
|
||||
$db->table('toto')->limit(10)->offset(5)->findAll();
|
||||
|
||||
## Fetch only some columns
|
||||
|
||||
$db->table('toto')->columns('column1', 'column2')->findAll();
|
||||
|
||||
## Conditions
|
||||
|
||||
### Equals condition
|
||||
|
||||
$db->table('toto')
|
||||
->equals('column1', 'hey')
|
||||
->findAll();
|
||||
|
||||
or
|
||||
|
||||
$db->table('toto')
|
||||
->eq('column1', 'hey')
|
||||
->findAll();
|
||||
|
||||
Yout got: 'SELECT * FROM toto WHERE column1=?'
|
||||
|
||||
### IN condition
|
||||
|
||||
$db->table('toto')
|
||||
->in('column1', ['hey', 'bla'])
|
||||
->findAll();
|
||||
|
||||
### Like condition
|
||||
|
||||
$db->table('toto')
|
||||
->like('column1', '%hey%')
|
||||
->findAll();
|
||||
|
||||
### Lower than
|
||||
|
||||
$db->table('toto')
|
||||
->lowerThan('column1', 2)
|
||||
->findAll();
|
||||
|
||||
or
|
||||
|
||||
$db->table('toto')
|
||||
->lt('column1', 2)
|
||||
->findAll();
|
||||
|
||||
### Lower than or equals
|
||||
|
||||
$db->table('toto')
|
||||
->lowerThanOrEquals('column1', 2)
|
||||
->findAll();
|
||||
|
||||
or
|
||||
|
||||
$db->table('toto')
|
||||
->lte('column1', 2)
|
||||
->findAll();
|
||||
|
||||
### Greater than
|
||||
|
||||
$db->table('toto')
|
||||
->greaterThan('column1', 3)
|
||||
->findAll();
|
||||
|
||||
or
|
||||
|
||||
$db->table('toto')
|
||||
->gt('column1', 3)
|
||||
->findAll();
|
||||
|
||||
### Greater than or equals
|
||||
|
||||
$db->table('toto')
|
||||
->greaterThanOrEquals('column1', 3)
|
||||
->findAll();
|
||||
|
||||
or
|
||||
|
||||
$db->table('toto')
|
||||
->gte('column1', 3)
|
||||
->findAll();
|
||||
|
||||
### Multiple conditions
|
||||
|
||||
Each condition is joined by a AND.
|
||||
|
||||
$db->table('toto')
|
||||
->like('column2', '%toto')
|
||||
->gte('column1', 3)
|
||||
->findAll();
|
||||
|
||||
How to make a OR condition:
|
||||
|
||||
$db->table('toto')
|
||||
->beginOr()
|
||||
->like('column2', '%toto')
|
||||
->gte('column1', 3)
|
||||
->closeOr()
|
||||
->eq('column5', 'titi')
|
||||
->findAll();
|
||||
|
||||
## Schema migrations
|
||||
|
||||
### Define a migration
|
||||
|
||||
- Migrations are defined in simple functions inside a namespace named "Schema".
|
||||
- An instance of PDO is passed to first argument of the function.
|
||||
- Function names has the version number at the end.
|
||||
|
||||
Example:
|
||||
|
||||
namespace Schema;
|
||||
|
||||
function version_1($pdo)
|
||||
{
|
||||
$pdo->exec('
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT UNIQUE,
|
||||
email TEXT UNIQUE,
|
||||
password TEXT
|
||||
)
|
||||
');
|
||||
}
|
||||
|
||||
|
||||
function version_2($pdo)
|
||||
{
|
||||
$pdo->exec('
|
||||
CREATE TABLE tags (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT UNIQUE
|
||||
)
|
||||
');
|
||||
}
|
||||
|
||||
### Run schema update automatically
|
||||
|
||||
- The method "check()" executes all migrations until to reach the correct version number.
|
||||
- If we are already on the last version nothing will happen.
|
||||
- The schema version for the driver Sqlite is stored inside a variable (PRAGMA user_version)
|
||||
- You can use that with a dependency injection controller.
|
||||
|
||||
Example:
|
||||
|
||||
$last_schema_version = 5;
|
||||
|
||||
$db = new PicoDb\Database(array(
|
||||
'driver' => 'sqlite',
|
||||
'filename' => '/tmp/mydb.sqlite'
|
||||
));
|
||||
|
||||
if ($db->schema()->check($last_schema_version)) {
|
||||
|
||||
// Do something...
|
||||
}
|
||||
else {
|
||||
|
||||
die('Unable to migrate database schema.');
|
||||
}
|
||||
|
||||
### Use a singleton to handle database instances
|
||||
|
||||
Setup a new instance:
|
||||
|
||||
PicoDb\Database::bootstrap('myinstance', function() {
|
||||
|
||||
$db = new PicoDb\Database(array(
|
||||
'driver' => 'sqlite',
|
||||
'filename' => DB_FILENAME
|
||||
));
|
||||
|
||||
if ($db->schema()->check(DB_VERSION)) {
|
||||
return $db;
|
||||
}
|
||||
else {
|
||||
die('Unable to migrate database schema.');
|
||||
}
|
||||
});
|
||||
|
||||
Get this instance anywhere in your code:
|
||||
|
||||
PicoDb\Database::get('myinstance')->table(...)
|
19
vendor/fguillot/picodb/composer.json
vendored
Normal file
19
vendor/fguillot/picodb/composer.json
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "fguillot/picodb",
|
||||
"description": "Minimalist database query builder",
|
||||
"homepage": "https://github.com/fguillot/picoDb",
|
||||
"type": "library",
|
||||
"license": "WTFPL",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Frédéric Guillot",
|
||||
"homepage": "http://fredericguillot.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {"PicoDb": "lib/"}
|
||||
}
|
||||
}
|
292
vendor/fguillot/picodb/lib/PicoDb/Database.php
vendored
Normal file
292
vendor/fguillot/picodb/lib/PicoDb/Database.php
vendored
Normal file
@ -0,0 +1,292 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb;
|
||||
|
||||
use Closure;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use LogicException;
|
||||
use Picodb\Driver\Sqlite;
|
||||
use Picodb\Driver\Mysql;
|
||||
use Picodb\Driver\Postgres;
|
||||
|
||||
class Database
|
||||
{
|
||||
/**
|
||||
* Database instances
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @var array
|
||||
*/
|
||||
private static $instances = array();
|
||||
|
||||
/**
|
||||
* Queries logs
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $logs = array();
|
||||
|
||||
/**
|
||||
* PDO instance
|
||||
*
|
||||
* @access private
|
||||
* @var PDO
|
||||
*/
|
||||
private $pdo;
|
||||
|
||||
/**
|
||||
* Constructor, iniatlize a PDO driver
|
||||
*
|
||||
* @access public
|
||||
* @param array $settings Connection settings
|
||||
*/
|
||||
public function __construct(array $settings)
|
||||
{
|
||||
if (! isset($settings['driver'])) {
|
||||
throw new LogicException('You must define a database driver.');
|
||||
}
|
||||
|
||||
switch ($settings['driver']) {
|
||||
|
||||
case 'sqlite':
|
||||
require_once __DIR__.'/Driver/Sqlite.php';
|
||||
$this->pdo = new Sqlite($settings);
|
||||
break;
|
||||
|
||||
case 'mysql':
|
||||
require_once __DIR__.'/Driver/Mysql.php';
|
||||
$this->pdo = new Mysql($settings);
|
||||
break;
|
||||
|
||||
case 'postgres':
|
||||
require_once __DIR__.'/Driver/Postgres.php';
|
||||
$this->pdo = new Postgres($settings);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new LogicException('This database driver is not supported.');
|
||||
}
|
||||
|
||||
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->closeConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new database instance
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param string $name Instance name
|
||||
* @param Closure $callback Callback
|
||||
*/
|
||||
public static function bootstrap($name, Closure $callback)
|
||||
{
|
||||
self::$instances[$name] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a database instance
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param string $name Instance name
|
||||
* @return Database
|
||||
*/
|
||||
public static function get($name)
|
||||
{
|
||||
if (! isset(self::$instances[$name])) {
|
||||
throw new LogicException('No database instance created with that name.');
|
||||
}
|
||||
|
||||
if (is_callable(self::$instances[$name])) {
|
||||
self::$instances[$name] = call_user_func(self::$instances[$name]);
|
||||
}
|
||||
|
||||
return self::$instances[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a log message
|
||||
*
|
||||
* @access public
|
||||
* @param string $message Message
|
||||
*/
|
||||
public function setLogMessage($message)
|
||||
{
|
||||
$this->logs[] = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all queries logs
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getLogMessages()
|
||||
{
|
||||
return $this->logs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the PDO connection
|
||||
*
|
||||
* @access public
|
||||
* @return PDO
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->pdo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the PDO connection
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function closeConnection()
|
||||
{
|
||||
$this->pdo = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape an identifier (column, table name...)
|
||||
*
|
||||
* @access public
|
||||
* @param string $value Value
|
||||
* @return string
|
||||
*/
|
||||
public function escapeIdentifier($value)
|
||||
{
|
||||
// Do not escape custom query
|
||||
if (strpos($value, '.') !== false || strpos($value, ' ') !== false) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $this->pdo->escapeIdentifier($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a prepared statement
|
||||
*
|
||||
* @access public
|
||||
* @param string $sql SQL query
|
||||
* @param array $values Values
|
||||
* @return PDOStatement
|
||||
*/
|
||||
public function execute($sql, array $values = array())
|
||||
{
|
||||
try {
|
||||
|
||||
$this->setLogMessage($sql);
|
||||
$rq = $this->pdo->prepare($sql);
|
||||
$rq->execute($values);
|
||||
return $rq;
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$this->setLogMessage($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a transaction
|
||||
*
|
||||
* @access public
|
||||
* @param Closure $callback Callback
|
||||
* @return mixed
|
||||
*/
|
||||
public function transaction(Closure $callback)
|
||||
{
|
||||
try {
|
||||
|
||||
$this->pdo->beginTransaction();
|
||||
$result = $callback($this);
|
||||
|
||||
if ($result === false) {
|
||||
$this->pdo->rollback();
|
||||
}
|
||||
else {
|
||||
$this->pdo->commit();
|
||||
}
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$this->pdo->rollback();
|
||||
$this->setLogMessage($e->getMessage());
|
||||
$result = false;
|
||||
}
|
||||
|
||||
return $result === null ? true : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin a transaction
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function startTransaction()
|
||||
{
|
||||
if (! $this->pdo->inTransaction()) {
|
||||
$this->pdo->beginTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit a transaction
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function closeTransaction()
|
||||
{
|
||||
if ($this->pdo->inTransaction()) {
|
||||
$this->pdo->commit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback a transaction
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function cancelTransaction()
|
||||
{
|
||||
if ($this->pdo->inTransaction()) {
|
||||
$this->pdo->rollback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a table instance
|
||||
*
|
||||
* @access public
|
||||
* @return Picodb\Table
|
||||
*/
|
||||
public function table($table_name)
|
||||
{
|
||||
require_once __DIR__.'/Table.php';
|
||||
return new Table($this, $table_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a schema instance
|
||||
*
|
||||
* @access public
|
||||
* @return Picodb\Schema
|
||||
*/
|
||||
public function schema()
|
||||
{
|
||||
require_once __DIR__.'/Schema.php';
|
||||
return new Schema($this);
|
||||
}
|
||||
}
|
@ -1,12 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb;
|
||||
namespace PicoDb\Driver;
|
||||
|
||||
class Mysql extends \PDO {
|
||||
use PDO;
|
||||
use LogicException;
|
||||
|
||||
class Mysql extends PDO
|
||||
{
|
||||
private $schema_table = 'schema_version';
|
||||
|
||||
|
||||
public function __construct(array $settings)
|
||||
{
|
||||
$required_atttributes = array(
|
||||
@ -19,13 +21,14 @@ class Mysql extends \PDO {
|
||||
|
||||
foreach ($required_atttributes as $attribute) {
|
||||
if (! isset($settings[$attribute])) {
|
||||
throw new \LogicException('This configuration parameter is missing: "'.$attribute.'"');
|
||||
throw new LogicException('This configuration parameter is missing: "'.$attribute.'"');
|
||||
}
|
||||
}
|
||||
|
||||
$dsn = 'mysql:host='.$settings['hostname'].';dbname='.$settings['database'];
|
||||
$dsn = 'mysql:host='.$settings['hostname'].';dbname='.$settings['database'].';charset='.$settings['charset'];
|
||||
|
||||
$options = array(
|
||||
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES '.$settings['charset']
|
||||
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET sql_mode = STRICT_ALL_TABLES',
|
||||
);
|
||||
|
||||
parent::__construct($dsn, $settings['username'], $settings['password'], $options);
|
||||
@ -42,7 +45,7 @@ class Mysql extends \PDO {
|
||||
|
||||
$rq = $this->prepare('SELECT `version` FROM `'.$this->schema_table.'`');
|
||||
$rq->execute();
|
||||
$result = $rq->fetch(\PDO::FETCH_ASSOC);
|
||||
$result = $rq->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (isset($result['version'])) {
|
||||
return (int) $result['version'];
|
@ -1,9 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb;
|
||||
namespace PicoDb\Driver;
|
||||
|
||||
class Postgres extends \PDO {
|
||||
use PDO;
|
||||
use LogicException;
|
||||
|
||||
class Postgres extends PDO
|
||||
{
|
||||
private $schema_table = 'schema_version';
|
||||
|
||||
|
||||
@ -18,7 +21,7 @@ class Postgres extends \PDO {
|
||||
|
||||
foreach ($required_atttributes as $attribute) {
|
||||
if (! isset($settings[$attribute])) {
|
||||
throw new \LogicException('This configuration parameter is missing: "'.$attribute.'"');
|
||||
throw new LogicException('This configuration parameter is missing: "'.$attribute.'"');
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,7 +41,7 @@ class Postgres extends \PDO {
|
||||
|
||||
$rq = $this->prepare('SELECT version FROM '.$this->schema_table.'');
|
||||
$rq->execute();
|
||||
$result = $rq->fetch(\PDO::FETCH_ASSOC);
|
||||
$result = $rq->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (isset($result['version'])) {
|
||||
return (int) $result['version'];
|
@ -1,10 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb;
|
||||
|
||||
class Sqlite extends \PDO {
|
||||
namespace PicoDb\Driver;
|
||||
|
||||
use PDO;
|
||||
use LogicException;
|
||||
|
||||
class Sqlite extends PDO
|
||||
{
|
||||
public function __construct(array $settings)
|
||||
{
|
||||
$required_atttributes = array(
|
||||
@ -13,7 +15,7 @@ class Sqlite extends \PDO {
|
||||
|
||||
foreach ($required_atttributes as $attribute) {
|
||||
if (! isset($settings[$attribute])) {
|
||||
throw new \LogicException('This configuration parameter is missing: "'.$attribute.'"');
|
||||
throw new LogicException('This configuration parameter is missing: "'.$attribute.'"');
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +29,7 @@ class Sqlite extends \PDO {
|
||||
{
|
||||
$rq = $this->prepare('PRAGMA user_version');
|
||||
$rq->execute();
|
||||
$result = $rq->fetch(\PDO::FETCH_ASSOC);
|
||||
$result = $rq->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (isset($result['user_version'])) {
|
||||
return (int) $result['user_version'];
|
@ -2,17 +2,17 @@
|
||||
|
||||
namespace PicoDb;
|
||||
|
||||
use PDOException;
|
||||
|
||||
class Schema
|
||||
{
|
||||
protected $db = null;
|
||||
|
||||
|
||||
public function __construct(Database $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
|
||||
public function check($last_version = 1)
|
||||
{
|
||||
$current_version = $this->db->getConnection()->getSchemaVersion();
|
||||
@ -24,7 +24,6 @@ class Schema
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public function migrateTo($current_version, $next_version)
|
||||
{
|
||||
try {
|
||||
@ -43,7 +42,7 @@ class Schema
|
||||
|
||||
$this->db->closeTransaction();
|
||||
}
|
||||
catch (\PDOException $e) {
|
||||
catch (PDOException $e) {
|
||||
$this->db->setLogMessage($function_name.' => '.$e->getMessage());
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
@ -51,4 +50,4 @@ class Schema
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -207,13 +207,13 @@ class Table
|
||||
}
|
||||
|
||||
|
||||
public function join($table, $foreign_column, $local_column)
|
||||
public function join($table, $foreign_column, $local_column, $local_table = null)
|
||||
{
|
||||
$this->joins[] = sprintf(
|
||||
'LEFT JOIN %s ON %s=%s',
|
||||
$this->db->escapeIdentifier($table),
|
||||
$this->db->escapeIdentifier($table).'.'.$this->db->escapeIdentifier($foreign_column),
|
||||
$this->db->escapeIdentifier($this->table_name).'.'.$this->db->escapeIdentifier($local_column)
|
||||
$this->db->escapeIdentifier($local_table ?: $this->table_name).'.'.$this->db->escapeIdentifier($local_column)
|
||||
);
|
||||
|
||||
return $this;
|
38
vendor/fguillot/picofarad/.gitignore
vendored
Normal file
38
vendor/fguillot/picofarad/.gitignore
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
# Compiled source #
|
||||
###################
|
||||
*.com
|
||||
*.class
|
||||
*.dll
|
||||
*.exe
|
||||
*.o
|
||||
*.so
|
||||
|
||||
# Packages #
|
||||
############
|
||||
# it's better to unpack these files and commit the raw source
|
||||
# git has its own built in compression methods
|
||||
*.7z
|
||||
*.dmg
|
||||
*.gz
|
||||
*.iso
|
||||
*.jar
|
||||
*.rar
|
||||
*.tar
|
||||
*.zip
|
||||
|
||||
# Logs and databases #
|
||||
######################
|
||||
*.log
|
||||
*.sql
|
||||
*.sqlite
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
ehthumbs.db
|
||||
Icon?
|
||||
Thumbs.db
|
||||
*.swp
|
||||
*~
|
||||
*.lock
|
306
vendor/fguillot/picofarad/README.md
vendored
Normal file
306
vendor/fguillot/picofarad/README.md
vendored
Normal file
@ -0,0 +1,306 @@
|
||||
PicoFarad
|
||||
=========
|
||||
|
||||
PicoFarad is a minimalist micro-framework for PHP.
|
||||
Perfect to build a REST API or a small webapp.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- No dependency
|
||||
- Easy to use, fast and very lightweight
|
||||
- Only 4 files: Request, Response, Router and Session
|
||||
- License: Do what the fuck you want with that
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
- PHP >= 5.3
|
||||
|
||||
Router
|
||||
------
|
||||
|
||||
### Example for a single file webapp:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use PicoFarad\Router;
|
||||
use PicoFarad\Response;
|
||||
use PicoFarad\Request;
|
||||
use PicoFarad\Session;
|
||||
|
||||
// Called before each action
|
||||
Router\before(function($action) {
|
||||
|
||||
// Open a session only for the specified directory
|
||||
Session\open(dirname($_SERVER['PHP_SELF']));
|
||||
|
||||
// HTTP secure headers
|
||||
Response\csp();
|
||||
Response\xframe();
|
||||
Response\xss();
|
||||
Response\nosniff();
|
||||
});
|
||||
|
||||
// GET ?action=show-help
|
||||
Router\get_action('show-help', function() {
|
||||
Response\text('help me!');
|
||||
});
|
||||
|
||||
// POST ?action=hello (with a form value "name")
|
||||
Router\post_action('show-help', function() {
|
||||
Response\text('Hello '.Request\value('name'));
|
||||
});
|
||||
|
||||
// Default action executed
|
||||
Router\notfound(function() {
|
||||
Response\text('Sorry, page not found!');
|
||||
})
|
||||
```
|
||||
|
||||
### Split your webapp in different files:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use PicoFarad\Router;
|
||||
use PicoFarad\Response;
|
||||
|
||||
// Include automatically those files:
|
||||
// __DIR__.'/controllers/controller1.php'
|
||||
// __DIR__.'/controllers/controller2.php'
|
||||
Router\bootstrap(__DIR__.'/controllers', 'controller1', 'controller2');
|
||||
|
||||
// Page not found
|
||||
Router\notfound(function() {
|
||||
Response\redirect('?action=unread');
|
||||
});
|
||||
```
|
||||
|
||||
### Example for a REST API:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
// POST /foo
|
||||
Router\post('/foo', function() {
|
||||
$values = Request\values();
|
||||
...
|
||||
Response\json(['result' => true], 201);
|
||||
});
|
||||
|
||||
// GET /foo/123
|
||||
Router\get('/foo/:id', function($id) {
|
||||
Response\json(['result' => true]);
|
||||
});
|
||||
|
||||
// PUT /foo/123
|
||||
Router\put('/foo/:id', function($id) {
|
||||
$values = Request\values();
|
||||
...
|
||||
Response\json(['result' => true]);
|
||||
});
|
||||
|
||||
// DELETE /foo/123
|
||||
Router\delete('/foo/:id', function($id) {
|
||||
Response\json(['result' => true]);
|
||||
});
|
||||
```
|
||||
|
||||
Response
|
||||
--------
|
||||
|
||||
### Send a JSON response
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use PicoFarad\Response;
|
||||
|
||||
$data = array(....);
|
||||
|
||||
// Output the encoded JSON data with a HTTP status 200 Ok
|
||||
Response\json($data);
|
||||
|
||||
// Change the default HTTP status code by a 400 Bad Request
|
||||
Response\json($data, 400);
|
||||
```
|
||||
|
||||
### Send text response
|
||||
|
||||
```php
|
||||
Response\text('my text data');
|
||||
```
|
||||
|
||||
### Send HTML response
|
||||
|
||||
```php
|
||||
Response\html('<html...>');
|
||||
```
|
||||
|
||||
### Send XML response
|
||||
|
||||
```php
|
||||
Response\xml('<xml ... >');
|
||||
```
|
||||
|
||||
### Send a binary response
|
||||
|
||||
```php
|
||||
Response\binary($my_file_content);
|
||||
```
|
||||
|
||||
### Force browser download
|
||||
|
||||
```php
|
||||
Response\force_download('The name of the ouput file');
|
||||
```
|
||||
|
||||
### Modify the HTTP status code
|
||||
|
||||
```php
|
||||
Response\status(403);
|
||||
```
|
||||
|
||||
### Temporary redirection
|
||||
|
||||
```php
|
||||
Response\redirect('http://....');
|
||||
```
|
||||
|
||||
### Permanent redirection
|
||||
|
||||
```php
|
||||
Response\redirect('http://....', 301);
|
||||
```
|
||||
|
||||
### Secure headers
|
||||
|
||||
```php
|
||||
// Send the header X-Content-Type-Options: nosniff
|
||||
Response\nosniff();
|
||||
|
||||
// Send the header X-XSS-Protection: 1; mode=block
|
||||
Response\xss()
|
||||
|
||||
// Send the header Strict-Transport-Security: max-age=31536000
|
||||
Response\hsts();
|
||||
|
||||
// Send the header X-Frame-Options: DENY
|
||||
Response\xframe();
|
||||
```
|
||||
|
||||
### Content Security Policies
|
||||
|
||||
```php
|
||||
Response\csp(array(
|
||||
'img-src' => '*'
|
||||
));
|
||||
|
||||
// Send these headers:
|
||||
Content-Security-Policy: img-src *; default-src 'self';
|
||||
X-Content-Security-Policy: img-src *; default-src 'self';
|
||||
X-WebKit-CSP: img-src *; default-src 'self';
|
||||
```
|
||||
|
||||
Request
|
||||
-------
|
||||
|
||||
### Get querystring variables
|
||||
|
||||
```php
|
||||
use PicoFarad\Request;
|
||||
|
||||
// Get from the URL: ?toto=value
|
||||
echo Request\param('toto');
|
||||
|
||||
// Get only integer value: ?toto=2
|
||||
echo Request\int_param('toto');
|
||||
```
|
||||
|
||||
### Get the raw body
|
||||
|
||||
```php
|
||||
echo Request\body();
|
||||
```
|
||||
|
||||
### Get decoded JSON body or form values
|
||||
|
||||
If a form is submited, you got an array of values.
|
||||
If the body is a JSON encoded string you got an array of the decoded JSON.
|
||||
|
||||
```php
|
||||
print_r(Request\values());
|
||||
```
|
||||
|
||||
### Get a form variable
|
||||
|
||||
```php
|
||||
echo Request\value('myvariable');
|
||||
```
|
||||
|
||||
### Get the content of a uploaded file
|
||||
|
||||
```php
|
||||
echo Request\file_content('field_form_name');
|
||||
```
|
||||
|
||||
### Check if the request is a POST
|
||||
|
||||
```php
|
||||
if (Request\is_post()) {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Check if the request is a GET
|
||||
|
||||
```php
|
||||
if (Request\is_get()) {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Get the request uri
|
||||
|
||||
```php
|
||||
echo Request\uri();
|
||||
```
|
||||
|
||||
Session
|
||||
-------
|
||||
|
||||
### Open and close a session
|
||||
|
||||
The session cookie have the following settings:
|
||||
|
||||
- Cookie lifetime: 2678400 seconds (31 days)
|
||||
- Limited to a specified path (http://domain/mywebapp/) or not (http://domain/)
|
||||
- If the connection is HTTPS, the cookie use the secure flag
|
||||
- The cookie is HttpOnly, not available from Javascript
|
||||
|
||||
Example:
|
||||
|
||||
```php
|
||||
use PicoFarad\Session;
|
||||
|
||||
// Session start
|
||||
Session\open('mywebappdirectory');
|
||||
|
||||
// Destroy the session
|
||||
Session\close();
|
||||
```
|
||||
|
||||
### Flash messages
|
||||
|
||||
Set the session variables: `$_SESSION['flash_message']` and `$_SESSION['flash_error_message']`.
|
||||
In your template, use a helper to display and delete these messages.
|
||||
|
||||
```php
|
||||
// Standard message
|
||||
Session\flash('My message');
|
||||
|
||||
// Error message
|
||||
Session\flash_error('My error message');
|
||||
```
|
19
vendor/fguillot/picofarad/composer.json
vendored
Normal file
19
vendor/fguillot/picofarad/composer.json
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "fguillot/picofarad",
|
||||
"description": "Minimalist micro-framework",
|
||||
"homepage": "https://github.com/fguillot/picoFarad",
|
||||
"type": "library",
|
||||
"license": "Unlicense",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Frédéric Guillot",
|
||||
"homepage": "http://fredericguillot.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {"PicoFarad": "lib/"}
|
||||
}
|
||||
}
|
@ -78,7 +78,19 @@ function file_move($field, $destination)
|
||||
}
|
||||
|
||||
|
||||
function uri()
|
||||
{
|
||||
return $_SERVER['REQUEST_URI'];
|
||||
}
|
||||
|
||||
|
||||
function is_post()
|
||||
{
|
||||
return isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST';
|
||||
return $_SERVER['REQUEST_METHOD'] === 'POST';
|
||||
}
|
||||
|
||||
|
||||
function is_get()
|
||||
{
|
||||
return $_SERVER['REQUEST_METHOD'] === 'GET';
|
||||
}
|
@ -28,9 +28,9 @@ function status($status_code)
|
||||
}
|
||||
|
||||
|
||||
function redirect($url)
|
||||
function redirect($url, $status_code = 302)
|
||||
{
|
||||
header('Location: '.$url);
|
||||
header('Location: '.$url, true, $status_code);
|
||||
exit;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace PicoFarad\Session;
|
||||
|
||||
const SESSION_LIFETIME = 0;
|
||||
const SESSION_LIFETIME = 2678400;
|
||||
|
||||
|
||||
function open($base_path = '/', $save_path = '')
|
||||
@ -41,25 +41,6 @@ function open($base_path = '/', $save_path = '')
|
||||
|
||||
function close()
|
||||
{
|
||||
// Flush all sessions variables
|
||||
$_SESSION = array();
|
||||
|
||||
// Destroy the session cookie
|
||||
if (ini_get('session.use_cookies')) {
|
||||
$params = session_get_cookie_params();
|
||||
|
||||
setcookie(
|
||||
session_name(),
|
||||
'',
|
||||
time() - 42000,
|
||||
$params['path'],
|
||||
$params['domain'],
|
||||
$params['secure'],
|
||||
$params['httponly']
|
||||
);
|
||||
}
|
||||
|
||||
// Destroy session data
|
||||
session_destroy();
|
||||
}
|
||||
|
2
vendor/fguillot/picofeed/.gitignore
vendored
Normal file
2
vendor/fguillot/picofeed/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
.DS_Store
|
||||
vendor/
|
12
vendor/fguillot/picofeed/.travis.yml
vendored
Normal file
12
vendor/fguillot/picofeed/.travis.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- "5.6"
|
||||
- "5.5"
|
||||
- "5.4"
|
||||
- "5.3"
|
||||
|
||||
before_script: wget https://phar.phpunit.de/phpunit.phar
|
||||
script:
|
||||
- composer dump-autoload
|
||||
- php phpunit.phar
|
67
vendor/fguillot/picofeed/README.markdown
vendored
Normal file
67
vendor/fguillot/picofeed/README.markdown
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
PicoFeed
|
||||
========
|
||||
|
||||
PicoFeed was originally developed for [Miniflux](http://miniflux.net), a minimalist and open source news reader.
|
||||
|
||||
However, this library can be used inside any project.
|
||||
PicoFeed is tested with a lot of different feeds and it's simple and easy to use.
|
||||
|
||||
[![Build Status](https://travis-ci.org/fguillot/picoFeed.svg?branch=master)](https://travis-ci.org/fguillot/picoFeed)
|
||||
|
||||
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/fguillot/picoFeed/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/fguillot/picoFeed/?branch=master)
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- Simple and fast
|
||||
- Feed parser for Atom 1.0 and RSS 0.91, 0.92, 1.0 and 2.0
|
||||
- Feed writer for Atom 1.0 and RSS 2.0
|
||||
- Favicon fetcher
|
||||
- Import/Export OPML subscriptions
|
||||
- Content filter: HTML cleanup, remove pixel trackers and Ads
|
||||
- Multiple HTTP client adapters: cURL or Stream Context
|
||||
- Proxy support
|
||||
- Content grabber: download from the original website the full content
|
||||
- Enclosure detection
|
||||
- RTL languages support
|
||||
- License: Unlicense <http://unlicense.org/>
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
- PHP >= 5.3
|
||||
- libxml >= 2.7
|
||||
- XML PHP extensions: DOM and SimpleXML
|
||||
- cURL or Stream Context (`allow_url_fopen=On`)
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
- Original author: [Frédéric Guillot](http://fredericguillot.com/)
|
||||
- Major Contributors:
|
||||
- [Bernhard Posselt](https://github.com/Raydiation)
|
||||
- [David Pennington](https://github.com/Xeoncross)
|
||||
- [Mathias Kresin](https://github.com/mkresin)
|
||||
|
||||
Real world usage
|
||||
----------------
|
||||
|
||||
- [AnythingNew](http://anythingnew.co)
|
||||
- [Miniflux](http://miniflux.net)
|
||||
- [Owncloud News](https://github.com/owncloud/news)
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
- [Installation](docs/installation.markdown)
|
||||
- [Running unit tests](docs/tests.markdown)
|
||||
- [Feed parsing](docs/feed-parsing.markdown)
|
||||
- [Feed creation](docs/feed-creation.markdown)
|
||||
- [Favicon fetcher](docs/favicon.markdown)
|
||||
- [OPML file importation](docs/opml-import.markdown)
|
||||
- [OPML file exportation](docs/opml-export.markdown)
|
||||
- [Image proxy](docs/image-proxy.markdown) (avoid SSL mixed content warnings)
|
||||
- [Web scraping](docs/grabber.markdown)
|
||||
- [Exceptions](docs/exceptions.markdown)
|
||||
- [Debugging](docs/debugging.markdown)
|
||||
- [Configuration](docs/config.markdown)
|
24
vendor/fguillot/picofeed/UNLICENSE
vendored
Normal file
24
vendor/fguillot/picofeed/UNLICENSE
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
19
vendor/fguillot/picofeed/composer.json
vendored
Normal file
19
vendor/fguillot/picofeed/composer.json
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "fguillot/picofeed",
|
||||
"description": "Modern library to write or read feeds (RSS/Atom)",
|
||||
"homepage": "http://fguillot.github.io/picoFeed",
|
||||
"type": "library",
|
||||
"license": "Unlicense",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Frédéric Guillot",
|
||||
"homepage": "http://fredericguillot.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {"PicoFeed": "lib/"}
|
||||
}
|
||||
}
|
286
vendor/fguillot/picofeed/docs/config.markdown
vendored
Normal file
286
vendor/fguillot/picofeed/docs/config.markdown
vendored
Normal file
@ -0,0 +1,286 @@
|
||||
Configuration
|
||||
=============
|
||||
|
||||
How to use the Config object
|
||||
----------------------------
|
||||
|
||||
To change the default parameters, you have to use the Config class.
|
||||
Create a new instance and pass it to the Reader object like that:
|
||||
|
||||
```php
|
||||
use PicoFeed\Reader\Reader;
|
||||
use PicoFeed\Config\Config;
|
||||
|
||||
$config = new Config;
|
||||
$config->setClientUserAgent('My custom RSS Reader')
|
||||
->setProxyHostname('127.0.0.1')
|
||||
->setProxyPort(8118);
|
||||
|
||||
$reader = new Reader($config);
|
||||
...
|
||||
```
|
||||
|
||||
HTTP Client parameters
|
||||
----------------------
|
||||
|
||||
### Connection timeout
|
||||
|
||||
- Method name: `setClientTimeout()`
|
||||
- Default value: 10 seconds
|
||||
- Argument value: number of seconds (integer)
|
||||
|
||||
```php
|
||||
$config->setClientTimeout(20); // 20 seconds
|
||||
```
|
||||
|
||||
### User Agent
|
||||
|
||||
- Method name: `setClientUserAgent()`
|
||||
- Default value: `PicoFeed (https://github.com/fguillot/picoFeed)`
|
||||
- Argument value: string
|
||||
|
||||
```php
|
||||
$config->setClientUserAgent('My RSS reader');
|
||||
```
|
||||
|
||||
### Maximum HTTP redirections
|
||||
|
||||
- Method name: `setMaxRedirections()`
|
||||
- Default value: 5
|
||||
- Argument value: integer
|
||||
|
||||
```php
|
||||
$config->setMaxRedirections(10);
|
||||
```
|
||||
|
||||
### Maximum HTTP body response size
|
||||
|
||||
- Method name: `setMaxBodySize()`
|
||||
- Default value: 2097152 (2MB)
|
||||
- Argument value: value in bytes (integer)
|
||||
|
||||
```php
|
||||
$config->setMaxBodySize(10485760); // 10MB
|
||||
```
|
||||
|
||||
### Proxy hostname
|
||||
|
||||
- Method name: `setProxyHostname()`
|
||||
- Default value: empty
|
||||
- Argument value: string
|
||||
|
||||
```php
|
||||
$config->setProxyHostname('proxy.example.org');
|
||||
```
|
||||
|
||||
### Proxy port
|
||||
|
||||
- Method name: `setProxyPort()`
|
||||
- Default value: 3128
|
||||
- Argument value: port number (integer)
|
||||
|
||||
```php
|
||||
$config->setProxyPort(8118);
|
||||
```
|
||||
|
||||
### Proxy username
|
||||
|
||||
- Method name: `setProxyUsername()`
|
||||
- Default value: empty
|
||||
- Argument value: string
|
||||
|
||||
```php
|
||||
$config->setProxyUsername('myuser');
|
||||
```
|
||||
|
||||
### Proxy password
|
||||
|
||||
- Method name: `setProxyPassword()`
|
||||
- Default value: empty
|
||||
- Argument value: string
|
||||
|
||||
```php
|
||||
$config->setProxyPassword('mysecret');
|
||||
```
|
||||
|
||||
Content grabber
|
||||
---------------
|
||||
|
||||
### Connection timeout
|
||||
|
||||
- Method name: `setGrabberTimeout()`
|
||||
- Default value: 10 seconds
|
||||
- Argument value: number of seconds (integer)
|
||||
|
||||
```php
|
||||
$config->setGrabberTimeout(20); // 20 seconds
|
||||
```
|
||||
|
||||
### User Agent
|
||||
|
||||
- Method name: `setGrabberUserAgent()`
|
||||
- Default value: `PicoFeed (https://github.com/fguillot/picoFeed)`
|
||||
- Argument value: string
|
||||
|
||||
```php
|
||||
$config->setGrabberUserAgent('My content scraper');
|
||||
```
|
||||
|
||||
Parser
|
||||
------
|
||||
|
||||
### Hash algorithm used for item id generation
|
||||
|
||||
- Method name: `setParserHashAlgo()`
|
||||
- Default value: `sha256`
|
||||
- Argument value: any value returned by the function `hash_algos()` (string)
|
||||
- See: http://php.net/hash_algos
|
||||
|
||||
```php
|
||||
$config->setParserHashAlgo('sha1');
|
||||
```
|
||||
|
||||
### Disable item content filtering
|
||||
|
||||
- Method name: `setContentFiltering()`
|
||||
- Default value: true (filtering is enabled by default)
|
||||
- Argument value: boolean
|
||||
|
||||
```php
|
||||
$config->setContentFiltering(false);
|
||||
```
|
||||
|
||||
### Timezone
|
||||
|
||||
- Method name: `setTimezone()`
|
||||
- Default value: UTC
|
||||
- Argument value: See https://php.net/manual/en/timezones.php (string)
|
||||
- Note: define the timezone for items/feeds
|
||||
|
||||
```php
|
||||
$config->setTimezone('Europe/Paris');
|
||||
```
|
||||
|
||||
Logging
|
||||
-------
|
||||
|
||||
### Timezone
|
||||
|
||||
- Method name: `setTimezone()`
|
||||
- Default value: UTC
|
||||
- Argument value: See https://php.net/manual/en/timezones.php (string)
|
||||
- Note: define the timezone for the logging class
|
||||
|
||||
```php
|
||||
$config->setTimezone('Europe/Paris');
|
||||
```
|
||||
|
||||
Filter
|
||||
------
|
||||
|
||||
### Set the iframe whitelist (allowed iframe sources)
|
||||
|
||||
- Method name: `setFilterIframeWhitelist()`
|
||||
- Default value: See the Filter class source code
|
||||
- Argument value: array
|
||||
|
||||
```php
|
||||
$config->setFilterIframeWhitelist(['http://www.youtube.com', 'http://www.vimeo.com']);
|
||||
```
|
||||
|
||||
### Define HTML integer attributes
|
||||
|
||||
- Method name: `setFilterIntegerAttributes()`
|
||||
- Default value: See the Filter class source code
|
||||
- Argument value: array
|
||||
|
||||
```php
|
||||
$config->setFilterIntegerAttributes(['width', 'height']);
|
||||
```
|
||||
|
||||
### Add HTML attributes automatically
|
||||
|
||||
- Method name: `setFilterAttributeOverrides()`
|
||||
- Default value: See the Filter class source code
|
||||
- Argument value: array
|
||||
|
||||
```php
|
||||
$config->setFilterAttributeOverrides(['a' => ['target' => '_blank']);
|
||||
```
|
||||
|
||||
### Set the list of required attributes for tags
|
||||
|
||||
- Method name: `setFilterRequiredAttributes()`
|
||||
- Default value: See the Filter class source code
|
||||
- Argument value: array
|
||||
- Note: If the required attributes are not there, the tag is stripped
|
||||
|
||||
```php
|
||||
$config->setFilterRequiredAttributes(['a' => 'href', 'img' => 'src']);
|
||||
```
|
||||
|
||||
### Set the resource blacklist (Ads blocker)
|
||||
|
||||
- Method name: `setFilterMediaBlacklist()`
|
||||
- Default value: See the Filter class source code
|
||||
- Argument value: array
|
||||
- Note: Tags are stripped if they have those URLs
|
||||
|
||||
```php
|
||||
$config->setFilterMediaBlacklist(['feeds.feedburner.com', 'share.feedsportal.com']);
|
||||
```
|
||||
|
||||
### Define which attributes are used for external resources
|
||||
|
||||
- Method name: `setFilterMediaAttributes()`
|
||||
- Default value: See the Filter class source code
|
||||
- Argument value: array
|
||||
|
||||
```php
|
||||
$config->setFilterMediaAttributes(['src', 'href']);
|
||||
```
|
||||
|
||||
### Define the scheme whitelist
|
||||
|
||||
- Method name: `setFilterSchemeWhitelist()`
|
||||
- Default value: See the Filter class source code
|
||||
- Argument value: array
|
||||
- See: http://en.wikipedia.org/wiki/URI_scheme
|
||||
|
||||
```php
|
||||
$config->setFilterSchemeWhitelist(['http://', 'ftp://']);
|
||||
```
|
||||
|
||||
### Define the tags and attributes whitelist
|
||||
|
||||
- Method name: `setFilterWhitelistedTags()`
|
||||
- Default value: See the Filter class source code
|
||||
- Argument value: array
|
||||
- Note: Only those tags are allowed everything else is stripped
|
||||
|
||||
```php
|
||||
$config->setFilterWhitelistedTags(['a' => ['href'], 'img' => ['src', 'title']]);
|
||||
```
|
||||
|
||||
### Define a image proxy url
|
||||
|
||||
- Method name: `setFilterImageProxyUrl()`
|
||||
- Default value: Empty
|
||||
- Argument value: string
|
||||
|
||||
```php
|
||||
$config->setFilterImageProxyUrl('http://myproxy.example.org/?url=%s');
|
||||
```
|
||||
|
||||
### Define a image proxy callback
|
||||
|
||||
- Method name: `setFilterImageProxyCallback()`
|
||||
- Default value: null
|
||||
- Argument value: Closure
|
||||
|
||||
```php
|
||||
$config->setFilterImageProxyCallback(function ($image_url) {
|
||||
$key = hash_hmac('sha1', $image_url, 'secret');
|
||||
return 'https://mypublicproxy/'.$key.'/'.urlencode($image_url);
|
||||
});
|
||||
```
|
86
vendor/fguillot/picofeed/docs/debugging.markdown
vendored
Normal file
86
vendor/fguillot/picofeed/docs/debugging.markdown
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
Debugging
|
||||
=========
|
||||
|
||||
Logging
|
||||
-------
|
||||
|
||||
PicoFeed log in memory the execution flow, if a feed doesn't work correctly it's easy to see what is wrong.
|
||||
|
||||
### Reading messages
|
||||
|
||||
```php
|
||||
use PicoFeed\Logging\Logger;
|
||||
|
||||
// All messages are stored inside an Array
|
||||
print_r(Logger::getMessages());
|
||||
```
|
||||
|
||||
You will got an output like that:
|
||||
|
||||
```php
|
||||
Array
|
||||
(
|
||||
[0] => Fetch URL: http://petitcodeur.fr/feed.xml
|
||||
[1] => Etag:
|
||||
[2] => Last-Modified:
|
||||
[3] => cURL total time: 0.711378
|
||||
[4] => cURL dns lookup time: 0.001064
|
||||
[5] => cURL connect time: 0.100733
|
||||
[6] => cURL speed download: 74825
|
||||
[7] => HTTP status code: 200
|
||||
[8] => HTTP headers: Set-Cookie => start=R2701971637; path=/; expires=Sat, 06-Jul-2013 05:16:33 GMT
|
||||
[9] => HTTP headers: Date => Sat, 06 Jul 2013 03:55:52 GMT
|
||||
[10] => HTTP headers: Content-Type => application/xml
|
||||
[11] => HTTP headers: Content-Length => 53229
|
||||
[12] => HTTP headers: Connection => close
|
||||
[13] => HTTP headers: Server => Apache
|
||||
[14] => HTTP headers: Last-Modified => Tue, 02 Jul 2013 03:26:02 GMT
|
||||
[15] => HTTP headers: ETag => "393e79c-cfed-4e07ee78b2680"
|
||||
[16] => HTTP headers: Accept-Ranges => bytes
|
||||
....
|
||||
)
|
||||
```
|
||||
|
||||
### Remove messages
|
||||
|
||||
All messages are stored in memory, if you need to clear them just call the method `Logger::deleteMessages()`:
|
||||
|
||||
```php
|
||||
Logger::deleteMessages();
|
||||
```
|
||||
|
||||
Command line utility
|
||||
====================
|
||||
|
||||
PicoFeed provides a basic command line tool to debug feeds quickly.
|
||||
The tool is located in the root directory project.
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
$ ./picofeed
|
||||
Usage:
|
||||
./picofeed feed <feed-url> # Parse a feed a dump the ouput on stdout
|
||||
./picofeed debug <feed-url> # Display all logging messages for a feed
|
||||
./picofeed item <feed-url> <item-id> # Fetch only one item
|
||||
./picofeed nofilter <feed-url> <item-id> # Fetch an item but with no content filtering
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```bash
|
||||
$ ./picofeed debug https://linuxfr.org/
|
||||
Exception thrown ===> "Invalid SSL certificate"
|
||||
Array
|
||||
(
|
||||
[0] => [2014-11-08 14:04:14] PicoFeed\Client\Curl Fetch URL: https://linuxfr.org/
|
||||
[1] => [2014-11-08 14:04:14] PicoFeed\Client\Curl Etag provided:
|
||||
[2] => [2014-11-08 14:04:14] PicoFeed\Client\Curl Last-Modified provided:
|
||||
[3] => [2014-11-08 14:04:16] PicoFeed\Client\Curl cURL total time: 1.850634
|
||||
[4] => [2014-11-08 14:04:16] PicoFeed\Client\Curl cURL dns lookup time: 0.00093
|
||||
[5] => [2014-11-08 14:04:16] PicoFeed\Client\Curl cURL connect time: 0.115213
|
||||
[6] => [2014-11-08 14:04:16] PicoFeed\Client\Curl cURL speed download: 0
|
||||
[7] => [2014-11-08 14:04:16] PicoFeed\Client\Curl cURL effective url: https://linuxfr.org/
|
||||
[8] => [2014-11-08 14:04:16] PicoFeed\Client\Curl cURL error: SSL certificate problem: Invalid certificate chain
|
||||
)
|
||||
```
|
28
vendor/fguillot/picofeed/docs/exceptions.markdown
vendored
Normal file
28
vendor/fguillot/picofeed/docs/exceptions.markdown
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
Exceptions
|
||||
==========
|
||||
|
||||
All exceptions inherits from the standard `Exception` class.
|
||||
|
||||
### Library Exceptions
|
||||
|
||||
- `PicoFeed\PicoFeedException`: Base class exception for the library
|
||||
|
||||
### Client Exceptions
|
||||
|
||||
- `PicoFeed\Client\ClientException`: Base exception class for the Client class
|
||||
- `PicoFeed\Client\InvalidCertificateException`: Invalid SSL certificate
|
||||
- `PicoFeed\Client\InvalidUrlException`: Malformed URL, page not found (404), unable to establish a connection
|
||||
- `PicoFeed\Client\MaxRedirectException`: Maximum of HTTP redirections reached
|
||||
- `PicoFeed\Client\MaxSizeException`: The response size exceeds to maximum allowed
|
||||
- `PicoFeed\Client\TimeoutException`: Connection timeout
|
||||
|
||||
### Parser Exceptions
|
||||
|
||||
- `PicoFeed\Parser\ParserException`: Base exception class for the Parser class
|
||||
- `PicoFeed\Parser\MalformedXmlException`: XML Parser error
|
||||
|
||||
### Reader Exceptions
|
||||
|
||||
- `PicoFeed\Reader\ReaderException`: Base exception class for the Reader
|
||||
- `PicoFeed\Reader\SubscriptionNotFoundException`: Unable to find a feed for the given website
|
||||
- `PicoFeed\Reader\UnsupportedFeedFormatException`: Unable to detect the feed format
|
81
vendor/fguillot/picofeed/docs/favicon.markdown
vendored
Normal file
81
vendor/fguillot/picofeed/docs/favicon.markdown
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
Favicon fetcher
|
||||
===============
|
||||
|
||||
Find and download the favicon
|
||||
-----------------------------
|
||||
|
||||
```php
|
||||
use PicoFeed\Reader\Favicon;
|
||||
|
||||
$favicon = new Favicon;
|
||||
|
||||
// The icon link is https://bits.wikimedia.org/favicon/wikipedia.ico
|
||||
$icon_link = $favicon->find('https://en.wikipedia.org/');
|
||||
$icon_content = $favicon->getContent();
|
||||
```
|
||||
|
||||
PicoFeed will try first to find the favicon from the meta tags and fallback to the `favicon.ico` located in the website's root if nothing is found.
|
||||
|
||||
- `Favicon::find()` returns the favicon absolute url or an empty string if nothing is found.
|
||||
- `Favicon::getContent()` returns the favicon file content (binary content)
|
||||
|
||||
When the HTML page is parsed, relative links and protocol relative links are converted to absolute url.
|
||||
|
||||
Get Favicon file type
|
||||
---------------------
|
||||
|
||||
It's possible to fetch the image type, this information come from the Content-Type HTTP header:
|
||||
|
||||
```php
|
||||
$favicon = new Favicon;
|
||||
$favicon->find('http://example.net/');
|
||||
|
||||
echo $favicon->getType();
|
||||
|
||||
// Will output the content type, by example "image/png"
|
||||
```
|
||||
|
||||
Get the Favicon as Data URI
|
||||
---------------------------
|
||||
|
||||
You can also get the whole image as Data URI.
|
||||
It's useful if you want to store the icon in your database and avoid too many HTTP requests.
|
||||
|
||||
```php
|
||||
$favicon = new Favicon;
|
||||
$favicon->find('http://example.net/');
|
||||
|
||||
echo $favicon->getDataUri();
|
||||
|
||||
// Output something like that: data:image/png;base64,iVBORw0KGgoAAAANSUh.....
|
||||
```
|
||||
|
||||
See: http://en.wikipedia.org/wiki/Data_URI_scheme
|
||||
|
||||
Check if a favicon link exists
|
||||
------------------------------
|
||||
|
||||
```php
|
||||
use PicoFeed\Reader\Favicon;
|
||||
|
||||
$favicon = new Favicon;
|
||||
|
||||
// Return true if the file exists
|
||||
var_dump($favicon->exists('http://php.net/favicon.ico'));
|
||||
```
|
||||
|
||||
Use personalized HTTP settings
|
||||
------------------------------
|
||||
|
||||
Like other classes, the Favicon class support the Config object as constructor argument:
|
||||
|
||||
```php
|
||||
use PicoFeed\Config\Config;
|
||||
use PicoFeed\Reader\Favicon;
|
||||
|
||||
$config = new Config;
|
||||
$config->setClientUserAgent('My RSS Reader');
|
||||
|
||||
$favicon = new Favicon($config);
|
||||
$favicon->find('https://github.com');
|
||||
```
|
74
vendor/fguillot/picofeed/docs/feed-creation.markdown
vendored
Normal file
74
vendor/fguillot/picofeed/docs/feed-creation.markdown
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
Feed creation
|
||||
=============
|
||||
|
||||
PicoFeed can also generate Atom and RSS feeds.
|
||||
|
||||
Generate RSS 2.0 feed
|
||||
----------------------
|
||||
|
||||
```php
|
||||
use PicoFeed\Syndication\Rss20;
|
||||
|
||||
$writer = new Rss20();
|
||||
$writer->title = 'My site';
|
||||
$writer->site_url = 'http://boo/';
|
||||
$writer->feed_url = 'http://boo/feed.atom';
|
||||
$writer->author = array(
|
||||
'name' => 'Me',
|
||||
'url' => 'http://me',
|
||||
'email' => 'me@here'
|
||||
);
|
||||
|
||||
$writer->items[] = array(
|
||||
'title' => 'My article 1',
|
||||
'updated' => strtotime('-2 days'),
|
||||
'url' => 'http://foo/bar',
|
||||
'summary' => 'Super summary',
|
||||
'content' => '<p>content</p>'
|
||||
);
|
||||
|
||||
$writer->items[] = array(
|
||||
'title' => 'My article 2',
|
||||
'updated' => strtotime('-1 day'),
|
||||
'url' => 'http://foo/bar2',
|
||||
'summary' => 'Super summary 2',
|
||||
'content' => '<p>content 2 © 2015</p>',
|
||||
'author' => array(
|
||||
'name' => 'Me too',
|
||||
)
|
||||
);
|
||||
|
||||
$writer->items[] = array(
|
||||
'title' => 'My article 3',
|
||||
'url' => 'http://foo/bar3'
|
||||
);
|
||||
|
||||
echo $writer->execute();
|
||||
```
|
||||
|
||||
Generate Atom feed
|
||||
------------------
|
||||
|
||||
```php
|
||||
use PicoFeed\Syndication\Atom;
|
||||
|
||||
$writer = new Atom();
|
||||
$writer->title = 'My site';
|
||||
$writer->site_url = 'http://boo/';
|
||||
$writer->feed_url = 'http://boo/feed.atom';
|
||||
$writer->author = array(
|
||||
'name' => 'Me',
|
||||
'url' => 'http://me',
|
||||
'email' => 'me@here'
|
||||
);
|
||||
|
||||
$writer->items[] = array(
|
||||
'title' => 'My article 1',
|
||||
'updated' => strtotime('-2 days'),
|
||||
'url' => 'http://foo/bar',
|
||||
'summary' => 'Super summary',
|
||||
'content' => '<p>content</p>'
|
||||
);
|
||||
|
||||
echo $writer->execute();
|
||||
```
|
226
vendor/fguillot/picofeed/docs/feed-parsing.markdown
vendored
Normal file
226
vendor/fguillot/picofeed/docs/feed-parsing.markdown
vendored
Normal file
@ -0,0 +1,226 @@
|
||||
Feed parsing
|
||||
============
|
||||
|
||||
Parsing a subscription
|
||||
----------------------
|
||||
|
||||
```php
|
||||
use PicoFeed\Reader\Reader;
|
||||
use PicoFeed\PicoFeedException;
|
||||
|
||||
try {
|
||||
|
||||
$reader = new Reader;
|
||||
|
||||
// Return a resource
|
||||
$resource = $reader->download('http://linuxfr.org/news.atom');
|
||||
|
||||
// Return the right parser instance according to the feed format
|
||||
$parser = $reader->getParser(
|
||||
$resource->getUrl(),
|
||||
$resource->getContent(),
|
||||
$resource->getEncoding()
|
||||
);
|
||||
|
||||
// Return a Feed object
|
||||
$feed = $parser->execute();
|
||||
|
||||
// Print the feed properties with the magic method __toString()
|
||||
echo $feed;
|
||||
}
|
||||
catch (PicoFeedException $e) {
|
||||
// Do Something...
|
||||
}
|
||||
```
|
||||
|
||||
- The Reader class is the entry point for feed reading
|
||||
- The method `download()` fetch the remote content and return a resource, an instance of `PicoFeed\Client\Client`
|
||||
- The method `getParser()` returns a Parser instance according to the feed format Atom, Rss 2.0...
|
||||
- The parser itself returns a `Feed` object that contains feed and item properties
|
||||
|
||||
Output:
|
||||
|
||||
```bash
|
||||
Feed::id = tag:linuxfr.org,2005:/news
|
||||
Feed::title = LinuxFr.org : les dépêches
|
||||
Feed::feed_url = http://linuxfr.org/news.atom
|
||||
Feed::site_url = http://linuxfr.org/news
|
||||
Feed::date = 1415138079
|
||||
Feed::language = en-US
|
||||
Feed::description =
|
||||
Feed::logo =
|
||||
Feed::items = 15 items
|
||||
Feed::isRTL() = false
|
||||
----
|
||||
Item::id = 38d8f48284fb03940cbb3aff9101089b81e44efb1281641bdd7c3e7e4bf3b0cd
|
||||
Item::title = openSUSE 13.2 : nouvelle version du caméléon disponible !
|
||||
Item::url = http://linuxfr.org/news/opensuse-13-2-nouvelle-version-du-cameleon-disponible
|
||||
Item::date = 1415122640
|
||||
Item::language = en-US
|
||||
Item::author = Syvolc
|
||||
Item::enclosure_url =
|
||||
Item::enclosure_type =
|
||||
Item::isRTL() = false
|
||||
Item::content = 18307 bytes
|
||||
....
|
||||
```
|
||||
|
||||
Get the list of available subscriptions for a website
|
||||
-----------------------------------------------------
|
||||
|
||||
The example below will returns all available subscriptions for the website:
|
||||
|
||||
```php
|
||||
use PicoFeed\Reader\Reader;
|
||||
|
||||
try {
|
||||
|
||||
$reader = new Reader;
|
||||
$resource = $reader->download('http://www.cnn.com');
|
||||
|
||||
$feeds = $reader->find(
|
||||
$resource->getUrl(),
|
||||
$resource->getContent()
|
||||
);
|
||||
|
||||
print_r($feeds);
|
||||
}
|
||||
catch (PicoFeedException $e) {
|
||||
// Do something...
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```php
|
||||
Array
|
||||
(
|
||||
[0] => http://rss.cnn.com/rss/cnn_topstories.rss
|
||||
[1] => http://rss.cnn.com/rss/cnn_latest.rss
|
||||
)
|
||||
```
|
||||
|
||||
Feed discovery and parsing
|
||||
--------------------------
|
||||
|
||||
This example will discover automatically the subscription and parse the feed:
|
||||
|
||||
```php
|
||||
try {
|
||||
|
||||
$reader = new Reader;
|
||||
$resource = $reader->discover('http://linuxfr.org');
|
||||
|
||||
$parser = $reader->getParser(
|
||||
$resource->getUrl(),
|
||||
$resource->getContent(),
|
||||
$resource->getEncoding()
|
||||
);
|
||||
|
||||
$feed = $parser->execute();
|
||||
echo $feed;
|
||||
}
|
||||
catch (PicoFeedException $e) {
|
||||
}
|
||||
```
|
||||
|
||||
HTTP caching
|
||||
------------
|
||||
|
||||
PicoFeed supports HTTP caching to avoid unnecessary processing.
|
||||
|
||||
1. After the first download, save in your database the values of the Etag and LastModified HTTP headers
|
||||
2. For the next requests, provide those values to the `download()` method and check if the feed was modified or not
|
||||
|
||||
Here an example:
|
||||
|
||||
```php
|
||||
try {
|
||||
|
||||
// Fetch from your database the previous values of the Etag and LastModified headers
|
||||
$etag = '...';
|
||||
$last_modified = '...';
|
||||
|
||||
$reader = new Reader;
|
||||
|
||||
// Provide those values to the download method
|
||||
$resource = $reader->download('http://linuxfr.org/news.atom', $last_modified, $etag);
|
||||
|
||||
// Return true if the remote content has changed
|
||||
if ($resource->isModified()) {
|
||||
|
||||
$parser = $reader->getParser(
|
||||
$resource->getUrl(),
|
||||
$resource->getContent(),
|
||||
$resource->getEncoding()
|
||||
);
|
||||
|
||||
$feed = $parser->execute();
|
||||
|
||||
// Save your feed in your database
|
||||
// ...
|
||||
|
||||
// Store the Etag and the LastModified headers in your database for the next requests
|
||||
$etag = $resource->getEtag();
|
||||
$last_modified = $resource->getLastModified();
|
||||
|
||||
// ...
|
||||
}
|
||||
else {
|
||||
|
||||
echo 'Not modified, nothing to do!';
|
||||
}
|
||||
}
|
||||
catch (PicoFeedException $e) {
|
||||
// Do something...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Feed and item properties
|
||||
------------------------
|
||||
|
||||
```php
|
||||
// Feed object
|
||||
$feed->getId(); // Unique feed id
|
||||
$feed->getTitle(); // Feed title
|
||||
$feed->getFeedUrl(); // Feed url
|
||||
$feed->getSiteUrl(); // Website url
|
||||
$feed->getDate(); // Feed last updated date
|
||||
$feed->getLanguage(); // Feed language
|
||||
$feed->getDescription(); // Feed description
|
||||
$feed->getLogo(); // Feed logo (can be a large image, different from icon)
|
||||
$feed->getItems(); // List of item objects
|
||||
|
||||
// Item object
|
||||
$feed->items[0]->getId(); // Item unique id (hash)
|
||||
$feed->items[0]->getTitle(); // Item title
|
||||
$feed->items[0]->getUrl(); // Item url
|
||||
$feed->items[0]->getDate(); // Item published date (timestamp)
|
||||
$feed->items[0]->getLanguage(); // Item language
|
||||
$feed->items[0]->getAuthor(); // Item author
|
||||
$feed->items[0]->getEnclosureUrl(); // Enclosure url
|
||||
$feed->items[0]->getEnclosureType(); // Enclosure mime-type (audio/mp3, image/png...)
|
||||
$feed->items[0]->getContent(); // Item content (filtered or raw)
|
||||
$feed->items[0]->isRTL(); // Return true if the item language is Right-To-Left
|
||||
```
|
||||
|
||||
RTL language detection
|
||||
----------------------
|
||||
|
||||
Use the method `Item::isRTL()` to test if an item is RTL or not:
|
||||
|
||||
```php
|
||||
var_dump($item->isRTL()); // true or false
|
||||
```
|
||||
|
||||
Known RTL languages are:
|
||||
|
||||
- Arabic (ar-**)
|
||||
- Farsi (fa-**)
|
||||
- Urdu (ur-**)
|
||||
- Pashtu (ps-**)
|
||||
- Syriac (syr-**)
|
||||
- Divehi (dv-**)
|
||||
- Hebrew (he-**)
|
||||
- Yiddish (yi-**)
|
136
vendor/fguillot/picofeed/docs/grabber.markdown
vendored
Normal file
136
vendor/fguillot/picofeed/docs/grabber.markdown
vendored
Normal file
@ -0,0 +1,136 @@
|
||||
Web scraper
|
||||
===========
|
||||
|
||||
The web scraper is useful for feeds that display only a summary of articles, the scraper can download and parse the full content from the original website.
|
||||
|
||||
How the content grabber works?
|
||||
------------------------------
|
||||
|
||||
1. Try with rules first (XPath queries) for the domain name (see `PicoFeed\Rules\`)
|
||||
2. Try to find the text content by using common attributes for class and id
|
||||
3. Finally, if nothing is found, the feed content is displayed
|
||||
|
||||
**The best results are obtained with XPath rules file.**
|
||||
|
||||
Standalone usage
|
||||
----------------
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use PicoFeed\Client\Grabber;
|
||||
|
||||
$grabber = new Grabber($item_url);
|
||||
$grabber->download();
|
||||
$grabber->parse();
|
||||
|
||||
// Get raw HTML content
|
||||
echo $grabber->getRawContent();
|
||||
|
||||
// Get relevant content
|
||||
echo $grabber->getContent();
|
||||
|
||||
// Get filtered relevant content
|
||||
echo $grabber->getFilteredContent();
|
||||
```
|
||||
|
||||
Fetch full item contents during feed parsing
|
||||
--------------------------------------------
|
||||
|
||||
Before parsing all items, just call the method `$parser->enableContentGrabber()`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use PicoFeed\Reader\Reader;
|
||||
use PicoFeed\PicoFeedException;
|
||||
|
||||
try {
|
||||
|
||||
$reader = new Reader;
|
||||
|
||||
// Return a resource
|
||||
$resource = $reader->download('http://www.egscomics.com/rss.php');
|
||||
|
||||
// Return the right parser instance according to the feed format
|
||||
$parser = $reader->getParser(
|
||||
$resource->getUrl(),
|
||||
$resource->getContent(),
|
||||
$resource->getEncoding()
|
||||
);
|
||||
|
||||
// Enable content grabber before parsing items
|
||||
$parser->enableContentGrabber();
|
||||
|
||||
// Return a Feed object
|
||||
$feed = $parser->execute();
|
||||
}
|
||||
catch (PicoFeedException $e) {
|
||||
// Do Something...
|
||||
}
|
||||
```
|
||||
|
||||
When the content scraper is enabled, everything will be slower.
|
||||
**For each item a new HTTP request is made** and the HTML downloaded is parsed with XML/XPath.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
### Enable content grabber for items
|
||||
|
||||
- Method name: `enableContentGrabber()`
|
||||
- Default value: false (content grabber is disabled by default)
|
||||
- Argument value: none
|
||||
|
||||
```php
|
||||
$parser->enableContentGrabber();
|
||||
```
|
||||
|
||||
### Ignore item urls for the content grabber
|
||||
|
||||
- Method name: `setGrabberIgnoreUrls()`
|
||||
- Default value: empty (fetch all item urls)
|
||||
- Argument value: array (list of item urls to ignore)
|
||||
|
||||
```php
|
||||
$parser->setGrabberIgnoreUrls(['http://foo', 'http://bar']);
|
||||
```
|
||||
|
||||
How to write a grabber rules file?
|
||||
----------------------------------
|
||||
|
||||
Add a PHP file to the directory `PicoFeed\Rules`, the filename must be the same as the domain name:
|
||||
|
||||
Example with the BBC website, `www.bbc.co.uk.php`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
return array(
|
||||
'test_url' => 'http://www.bbc.co.uk/news/world-middle-east-23911833',
|
||||
'body' => array(
|
||||
'//div[@class="story-body"]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//script',
|
||||
'//form',
|
||||
'//style',
|
||||
'//*[@class="story-date"]',
|
||||
'//*[@class="story-header"]',
|
||||
'//*[@class="story-related"]',
|
||||
'//*[contains(@class, "byline")]',
|
||||
'//*[contains(@class, "story-feature")]',
|
||||
'//*[@id="video-carousel-container"]',
|
||||
'//*[@id="also-related-links"]',
|
||||
'//*[contains(@class, "share") or contains(@class, "hidden") or contains(@class, "hyper")]',
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
Actually, only `body`, `strip` and `test_url` are supported.
|
||||
|
||||
Don't forget to send a pull request or a ticket to share your contribution with everybody,
|
||||
|
||||
List of content grabber rules
|
||||
-----------------------------
|
||||
|
||||
Rules are stored inside the directory [lib/PicoFeed/Rules](https://github.com/fguillot/picoFeed/tree/master/lib/PicoFeed/Rules)
|
66
vendor/fguillot/picofeed/docs/image-proxy.markdown
vendored
Normal file
66
vendor/fguillot/picofeed/docs/image-proxy.markdown
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
Image Proxy
|
||||
===========
|
||||
|
||||
To prevent mixed content warnings on SSL pages served from your RSS reader you might want to use an assets proxy.
|
||||
|
||||
Images url will be rewritten to be downloaded through the proxy.
|
||||
|
||||
Example:
|
||||
|
||||
```html
|
||||
<img src="http://example.org/image.png"/>
|
||||
```
|
||||
|
||||
Can be rewritten like that:
|
||||
|
||||
```html
|
||||
<img src="http://myproxy.example.org/?url=http%3A%2F%2Fexample.org%2Fimage.png"/>
|
||||
```
|
||||
|
||||
Currently this feature is only compatible with images.
|
||||
|
||||
There is several open source SSL image proxy available like [Camo](https://github.com/atmos/camo).
|
||||
You can also write your own proxy.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
There two different ways to use this feature, define a proxy url or a callback.
|
||||
|
||||
### Define a proxy url
|
||||
|
||||
A proxy url must be defined with a placeholder `%s`.
|
||||
The placeholder will be replaced by the image source urlencoded.
|
||||
|
||||
```php
|
||||
$config = new Config;
|
||||
$config->setFilterImageProxyUrl('http://myproxy.example.org/?url=%s');
|
||||
```
|
||||
|
||||
Will rewrite the image source like that:
|
||||
|
||||
```html
|
||||
<img src="http://myproxy.example.org/?url=http%3A%2F%2Fexample.org%2Fimage.png"/>
|
||||
```
|
||||
|
||||
### Define a callback
|
||||
|
||||
Your callback will be called each time an image url need to be rewritten.
|
||||
The first argument is the original image url and your function must returns the new image url.
|
||||
|
||||
Here an example if your proxy need a shared secret key:
|
||||
|
||||
```php
|
||||
$config = new Config;
|
||||
|
||||
$config->setFilterImageProxyCallback(function ($image_url) {
|
||||
$key = hash_hmac('sha1', $image_url, 'secret');
|
||||
return 'https://mypublicproxy/'.$key.'/'.urlencode($image_url);
|
||||
});
|
||||
```
|
||||
|
||||
Will generate an image url like that:
|
||||
|
||||
```html
|
||||
<img src="https://mypublicproxy/4924964043f3119b3cf2b07b1922d491bcc20092/http%3A%2F%2Ffoo%2Fimage.png"/>
|
||||
```
|
67
vendor/fguillot/picofeed/docs/installation.markdown
vendored
Normal file
67
vendor/fguillot/picofeed/docs/installation.markdown
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
Versions
|
||||
--------
|
||||
|
||||
- Development version: master
|
||||
- Available versions:
|
||||
- v0.1.0 (stable)
|
||||
- v0.0.2
|
||||
- v0.0.1
|
||||
|
||||
Note: The public API has changed between 0.0.x and 0.1.0
|
||||
|
||||
Installation with Composer
|
||||
--------------------------
|
||||
|
||||
Configure your `composer.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"require": {
|
||||
"fguillot/picofeed": "0.1.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or simply:
|
||||
|
||||
```bash
|
||||
composer require fguillot/picofeed:0.1.0
|
||||
```
|
||||
|
||||
And download the code:
|
||||
|
||||
```bash
|
||||
composer install # or update
|
||||
```
|
||||
|
||||
Usage example with the Composer autoloader:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
use PicoFeed\Reader\Reader;
|
||||
|
||||
try {
|
||||
|
||||
$reader = new Reader;
|
||||
$resource = $reader->download('http://linuxfr.org/news.atom');
|
||||
|
||||
$parser = $reader->getParser(
|
||||
$resource->getUrl(),
|
||||
$resource->getContent(),
|
||||
$resource->getEncoding()
|
||||
);
|
||||
|
||||
$feed = $parser->execute();
|
||||
|
||||
echo $feed;
|
||||
}
|
||||
catch (Exception $e) {
|
||||
// Do something...
|
||||
}
|
||||
```
|
46
vendor/fguillot/picofeed/docs/opml-export.markdown
vendored
Normal file
46
vendor/fguillot/picofeed/docs/opml-export.markdown
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
OPML export
|
||||
===========
|
||||
|
||||
Example with no categories
|
||||
--------------------------
|
||||
|
||||
```php
|
||||
use PicoFeed\Serialization\Export;
|
||||
|
||||
$feeds = array(
|
||||
array(
|
||||
'title' => 'Site title',
|
||||
'description' => 'Optional description',
|
||||
'site_url' => 'http://petitcodeur.fr/',
|
||||
'site_feed' => 'http://petitcodeur.fr/feed.xml'
|
||||
)
|
||||
);
|
||||
|
||||
$export = new Export($feeds);
|
||||
$opml = $export->execute();
|
||||
|
||||
echo $opml; // XML content
|
||||
```
|
||||
|
||||
Example with categories
|
||||
-----------------------
|
||||
|
||||
```php
|
||||
use PicoFeed\Serialization\Export;
|
||||
|
||||
$feeds = array(
|
||||
'my category' => array(
|
||||
array(
|
||||
'title' => 'Site title',
|
||||
'description' => 'Optional description',
|
||||
'site_url' => 'http://petitcodeur.fr/',
|
||||
'site_feed' => 'http://petitcodeur.fr/feed.xml'
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$export = new Export($feeds);
|
||||
$opml = $export->execute();
|
||||
|
||||
echo $opml; // XML content
|
||||
```
|
19
vendor/fguillot/picofeed/docs/opml-import.markdown
vendored
Normal file
19
vendor/fguillot/picofeed/docs/opml-import.markdown
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Import OPML file
|
||||
================
|
||||
|
||||
Importing a list of subscriptions is pretty straightforward:
|
||||
|
||||
```php
|
||||
use PicoFeed\Serialization\Import;
|
||||
|
||||
$opml = file_get_contents('mySubscriptions.opml');
|
||||
$import = new Import($opml);
|
||||
$entries = $import->execute();
|
||||
|
||||
if ($entries !== false) {
|
||||
print_r($entries);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The method `execute()` return `false` if there is a parsing error.
|
14
vendor/fguillot/picofeed/docs/tests.markdown
vendored
Normal file
14
vendor/fguillot/picofeed/docs/tests.markdown
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
Running unit tests
|
||||
==================
|
||||
|
||||
If the autoloader is not yet installed run:
|
||||
|
||||
```php
|
||||
composer dump-autoload
|
||||
```
|
||||
|
||||
Then run:
|
||||
|
||||
```php
|
||||
phpunit tests
|
||||
```
|
@ -1,10 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed;
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
use LogicException;
|
||||
use Clients\Curl;
|
||||
use Clients\Stream;
|
||||
use PicoFeed\Logging\Logger;
|
||||
|
||||
/**
|
||||
* Client class
|
||||
@ -23,12 +22,12 @@ abstract class Client
|
||||
private $is_modified = true;
|
||||
|
||||
/**
|
||||
* Flag that say if the resource is a 404
|
||||
* HTTP Content-Type
|
||||
*
|
||||
* @access private
|
||||
* @var bool
|
||||
* @var string
|
||||
*/
|
||||
private $is_not_found = false;
|
||||
private $content_type = '';
|
||||
|
||||
/**
|
||||
* HTTP encoding
|
||||
@ -148,19 +147,15 @@ abstract class Client
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return \PicoFeed\Client
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (function_exists('curl_init')) {
|
||||
|
||||
require_once __DIR__.'/Clients/Curl.php';
|
||||
return new Clients\Curl;
|
||||
return new Curl;
|
||||
}
|
||||
else if (ini_get('allow_url_fopen')) {
|
||||
|
||||
require_once __DIR__.'/Clients/Stream.php';
|
||||
return new Clients\Stream;
|
||||
return new Stream;
|
||||
}
|
||||
|
||||
throw new LogicException('You must have "allow_url_fopen=1" or curl extension installed');
|
||||
@ -171,7 +166,7 @@ abstract class Client
|
||||
*
|
||||
* @access public
|
||||
* @param string $url URL
|
||||
* @return bool
|
||||
* @return Client
|
||||
*/
|
||||
public function execute($url = '')
|
||||
{
|
||||
@ -179,20 +174,17 @@ abstract class Client
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
Logging::setMessage(get_called_class().' Fetch URL: '.$this->url);
|
||||
Logging::setMessage(get_called_class().' Etag provided: '.$this->etag);
|
||||
Logging::setMessage(get_called_class().' Last-Modified provided: '.$this->last_modified);
|
||||
Logger::setMessage(get_called_class().' Fetch URL: '.$this->url);
|
||||
Logger::setMessage(get_called_class().' Etag provided: '.$this->etag);
|
||||
Logger::setMessage(get_called_class().' Last-Modified provided: '.$this->last_modified);
|
||||
|
||||
$response = $this->doRequest();
|
||||
|
||||
if (is_array($response)) {
|
||||
$this->handleNotModifiedResponse($response);
|
||||
$this->handleNotFoundResponse($response);
|
||||
$this->handleNormalResponse($response);
|
||||
return true;
|
||||
}
|
||||
$this->handleNotModifiedResponse($response);
|
||||
$this->handleNotFoundResponse($response);
|
||||
$this->handleNormalResponse($response);
|
||||
|
||||
return false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -207,20 +199,13 @@ abstract class Client
|
||||
$this->is_modified = false;
|
||||
}
|
||||
else if ($response['status'] == 200) {
|
||||
|
||||
$etag = $this->getHeader($response, 'ETag');
|
||||
$last_modified = $this->getHeader($response, 'Last-Modified');
|
||||
|
||||
if ($this->isPropertyEquals('etag', $etag) || $this->isPropertyEquals('last_modified', $last_modified)) {
|
||||
$this->is_modified = false;
|
||||
}
|
||||
|
||||
$this->etag = $etag;
|
||||
$this->last_modified = $last_modified;
|
||||
$this->is_modified = $this->hasBeenModified($response, $this->etag, $this->last_modified);
|
||||
$this->etag = $this->getHeader($response, 'ETag');
|
||||
$this->last_modified = $this->getHeader($response, 'Last-Modified');
|
||||
}
|
||||
|
||||
if ($this->is_modified === false) {
|
||||
Logging::setMessage(get_called_class().' Resource not modified');
|
||||
Logger::setMessage(get_called_class().' Resource not modified');
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,8 +218,7 @@ abstract class Client
|
||||
public function handleNotFoundResponse(array $response)
|
||||
{
|
||||
if ($response['status'] == 404) {
|
||||
$this->is_not_found = true;
|
||||
Logging::setMessage(get_called_class().' Resource not found');
|
||||
throw new InvalidUrlException('Resource not found');
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,32 +232,68 @@ abstract class Client
|
||||
{
|
||||
if ($response['status'] == 200) {
|
||||
$this->content = $response['body'];
|
||||
$this->encoding = $this->findCharset($response);
|
||||
$this->content_type = $this->findContentType($response);
|
||||
$this->encoding = $this->findCharset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a class property equals to a value
|
||||
* Check if a request has been modified according to the parameters
|
||||
*
|
||||
* @access public
|
||||
* @param string $property Class property
|
||||
* @param string $value Value
|
||||
* @param array $response
|
||||
* @param string $etag
|
||||
* @param string $lastModified
|
||||
* @return boolean
|
||||
*/
|
||||
private function isPropertyEquals($property, $value)
|
||||
private function hasBeenModified($response, $etag, $lastModified)
|
||||
{
|
||||
return $this->$property && $this->$property === $value;
|
||||
$headers = array(
|
||||
'Etag' => $etag,
|
||||
'Last-Modified' => $lastModified
|
||||
);
|
||||
|
||||
// Compare the values for each header that is present
|
||||
$presentCacheHeaderCount = 0;
|
||||
foreach ($headers as $key => $value) {
|
||||
if (isset($response['headers'][$key])) {
|
||||
if ($response['headers'][$key] !== $value) {
|
||||
return true;
|
||||
}
|
||||
$presentCacheHeaderCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// If at least one header is present and the values match, the response
|
||||
// was not modified
|
||||
if ($presentCacheHeaderCount > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find content type from response headers
|
||||
*
|
||||
* @access public
|
||||
* @param array $response Client response
|
||||
* @return string
|
||||
*/
|
||||
public function findContentType(array $response)
|
||||
{
|
||||
return strtolower($this->getHeader($response, 'Content-Type'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find charset from response headers
|
||||
*
|
||||
* @access public
|
||||
* @param array $response Client response
|
||||
* @return string
|
||||
*/
|
||||
public function findCharset(array $response)
|
||||
public function findCharset()
|
||||
{
|
||||
$result = explode('charset=', strtolower($this->getHeader($response, 'Content-Type')));
|
||||
$result = explode('charset=', $this->content_type);
|
||||
return isset($result[1]) ? $result[1] : '';
|
||||
}
|
||||
|
||||
@ -314,13 +334,13 @@ abstract class Client
|
||||
}
|
||||
}
|
||||
|
||||
Logging::setMessage(get_called_class().' HTTP status code: '.$status);
|
||||
Logger::setMessage(get_called_class().' HTTP status code: '.$status);
|
||||
|
||||
foreach ($headers as $name => $value) {
|
||||
Logging::setMessage(get_called_class().' HTTP header: '.$name.' => '.$value);
|
||||
Logger::setMessage(get_called_class().' HTTP header: '.$name.' => '.$value);
|
||||
}
|
||||
|
||||
return array($status, $headers);
|
||||
return array($status, new HttpHeaders($headers));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -328,7 +348,7 @@ abstract class Client
|
||||
*
|
||||
* @access public
|
||||
* @param string $last_modified Header value
|
||||
* @return \PicoFeed\Client
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setLastModified($last_modified)
|
||||
{
|
||||
@ -352,7 +372,7 @@ abstract class Client
|
||||
*
|
||||
* @access public
|
||||
* @param string $etag Etag HTTP header value
|
||||
* @return \PicoFeed\Client
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setEtag($etag)
|
||||
{
|
||||
@ -387,7 +407,7 @@ abstract class Client
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
* @return \PicoFeed\Client
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setUrl($url)
|
||||
{
|
||||
@ -406,6 +426,17 @@ abstract class Client
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content type value from HTTP headers
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getContentType()
|
||||
{
|
||||
return $this->content_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the encoding value from HTTP headers
|
||||
*
|
||||
@ -428,23 +459,12 @@ abstract class Client
|
||||
return $this->is_modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the remote resource is not found
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isNotFound()
|
||||
{
|
||||
return $this->is_not_found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set connection timeout
|
||||
*
|
||||
* @access public
|
||||
* @param integer $timeout Connection timeout
|
||||
* @return \PicoFeed\Client
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setTimeout($timeout)
|
||||
{
|
||||
@ -457,7 +477,7 @@ abstract class Client
|
||||
*
|
||||
* @access public
|
||||
* @param string $user_agent User Agent
|
||||
* @return \PicoFeed\Client
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setUserAgent($user_agent)
|
||||
{
|
||||
@ -470,7 +490,7 @@ abstract class Client
|
||||
*
|
||||
* @access public
|
||||
* @param integer $max Maximum
|
||||
* @return \PicoFeed\Client
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setMaxRedirections($max)
|
||||
{
|
||||
@ -483,7 +503,7 @@ abstract class Client
|
||||
*
|
||||
* @access public
|
||||
* @param integer $max Maximum
|
||||
* @return \PicoFeed\Client
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setMaxBodySize($max)
|
||||
{
|
||||
@ -496,7 +516,7 @@ abstract class Client
|
||||
*
|
||||
* @access public
|
||||
* @param string $hostname Proxy hostname
|
||||
* @return \PicoFeed\Client
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setProxyHostname($hostname)
|
||||
{
|
||||
@ -509,7 +529,7 @@ abstract class Client
|
||||
*
|
||||
* @access public
|
||||
* @param integer $port Proxy port
|
||||
* @return \PicoFeed\Client
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setProxyPort($port)
|
||||
{
|
||||
@ -522,7 +542,7 @@ abstract class Client
|
||||
*
|
||||
* @access public
|
||||
* @param string $username Proxy username
|
||||
* @return \PicoFeed\Client
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setProxyUsername($username)
|
||||
{
|
||||
@ -535,7 +555,7 @@ abstract class Client
|
||||
*
|
||||
* @access public
|
||||
* @param string $password Password
|
||||
* @return \PicoFeed\Client
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setProxyPassword($password)
|
||||
{
|
||||
@ -547,8 +567,8 @@ abstract class Client
|
||||
* Set config object
|
||||
*
|
||||
* @access public
|
||||
* @param \PicoFeed\Config $config Config instance
|
||||
* @return \PicoFeed\Client
|
||||
* @param \PicoFeed\Config\Config $config Config instance
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setConfig($config)
|
||||
{
|
16
vendor/fguillot/picofeed/lib/PicoFeed/Client/ClientException.php
vendored
Normal file
16
vendor/fguillot/picofeed/lib/PicoFeed/Client/ClientException.php
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
use PicoFeed\PicoFeedException;
|
||||
|
||||
|
||||
/**
|
||||
* ClientException Exception
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package Client
|
||||
*/
|
||||
abstract class ClientException extends PicoFeedException
|
||||
{
|
||||
}
|
@ -1,15 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed\Clients;
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
use \PicoFeed\Logging;
|
||||
use \PicoFeed\Client;
|
||||
use PicoFeed\Logging\Logger;
|
||||
|
||||
/**
|
||||
* cURL HTTP client
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package client
|
||||
* @package Client
|
||||
*/
|
||||
class Curl extends Client
|
||||
{
|
||||
@ -100,7 +99,7 @@ class Curl extends Client
|
||||
* Prepare HTTP headers
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
* @return string[]
|
||||
*/
|
||||
private function prepareHeaders()
|
||||
{
|
||||
@ -124,24 +123,24 @@ class Curl extends Client
|
||||
* Prepare curl proxy context
|
||||
*
|
||||
* @access private
|
||||
* @return resource
|
||||
* @return resource $ch
|
||||
*/
|
||||
private function prepareProxyContext($ch)
|
||||
{
|
||||
if ($this->proxy_hostname) {
|
||||
|
||||
Logging::setMessage(get_called_class().' Proxy: '.$this->proxy_hostname.':'.$this->proxy_port);
|
||||
Logger::setMessage(get_called_class().' Proxy: '.$this->proxy_hostname.':'.$this->proxy_port);
|
||||
|
||||
curl_setopt($ch, CURLOPT_PROXYPORT, $this->proxy_port);
|
||||
curl_setopt($ch, CURLOPT_PROXYTYPE, 'HTTP');
|
||||
curl_setopt($ch, CURLOPT_PROXY, $this->proxy_hostname);
|
||||
|
||||
if ($this->proxy_username) {
|
||||
Logging::setMessage(get_called_class().' Proxy credentials: Yes');
|
||||
Logger::setMessage(get_called_class().' Proxy credentials: Yes');
|
||||
curl_setopt($ch, CURLOPT_PROXYUSERPWD, $this->proxy_username.':'.$this->proxy_password);
|
||||
}
|
||||
else {
|
||||
Logging::setMessage(get_called_class().' Proxy credentials: No');
|
||||
Logger::setMessage(get_called_class().' Proxy credentials: No');
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,12 +160,10 @@ class Curl extends Client
|
||||
curl_setopt($ch, CURLOPT_URL, $this->url);
|
||||
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->timeout);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $this->prepareHeaders());
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, ini_get('open_basedir') === '');
|
||||
curl_setopt($ch, CURLOPT_MAXREDIRS, $this->max_redirects);
|
||||
curl_setopt($ch, CURLOPT_ENCODING, '');
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // For auto-signed certificates...
|
||||
curl_setopt($ch, CURLOPT_WRITEFUNCTION, array($this, 'readBody'));
|
||||
curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, 'readHeaders'));
|
||||
curl_setopt($ch, CURLOPT_COOKIEJAR, 'php://memory');
|
||||
@ -181,28 +178,31 @@ class Curl extends Client
|
||||
* Execute curl context
|
||||
*
|
||||
* @access private
|
||||
* @return resource
|
||||
*/
|
||||
private function executeContext()
|
||||
{
|
||||
$ch = $this->prepareContext();
|
||||
curl_exec($ch);
|
||||
|
||||
Logging::setMessage(get_called_class().' cURL total time: '.curl_getinfo($ch, CURLINFO_TOTAL_TIME));
|
||||
Logging::setMessage(get_called_class().' cURL dns lookup time: '.curl_getinfo($ch, CURLINFO_NAMELOOKUP_TIME));
|
||||
Logging::setMessage(get_called_class().' cURL connect time: '.curl_getinfo($ch, CURLINFO_CONNECT_TIME));
|
||||
Logging::setMessage(get_called_class().' cURL speed download: '.curl_getinfo($ch, CURLINFO_SPEED_DOWNLOAD));
|
||||
Logging::setMessage(get_called_class().' cURL effective url: '.curl_getinfo($ch, CURLINFO_EFFECTIVE_URL));
|
||||
Logger::setMessage(get_called_class().' cURL total time: '.curl_getinfo($ch, CURLINFO_TOTAL_TIME));
|
||||
Logger::setMessage(get_called_class().' cURL dns lookup time: '.curl_getinfo($ch, CURLINFO_NAMELOOKUP_TIME));
|
||||
Logger::setMessage(get_called_class().' cURL connect time: '.curl_getinfo($ch, CURLINFO_CONNECT_TIME));
|
||||
Logger::setMessage(get_called_class().' cURL speed download: '.curl_getinfo($ch, CURLINFO_SPEED_DOWNLOAD));
|
||||
Logger::setMessage(get_called_class().' cURL effective url: '.curl_getinfo($ch, CURLINFO_EFFECTIVE_URL));
|
||||
|
||||
if (curl_errno($ch)) {
|
||||
Logging::setMessage(get_called_class().' cURL error: '.curl_error($ch));
|
||||
$curl_errno = curl_errno($ch);
|
||||
|
||||
if ($curl_errno) {
|
||||
Logger::setMessage(get_called_class().' cURL error: '.curl_error($ch));
|
||||
curl_close($ch);
|
||||
return false;
|
||||
|
||||
$this->handleError($curl_errno);
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
// Update the url if there where redirects
|
||||
$this->url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
|
||||
|
||||
return true;
|
||||
curl_close($ch);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -214,13 +214,11 @@ class Curl extends Client
|
||||
*/
|
||||
public function doRequest($follow_location = true)
|
||||
{
|
||||
if (! $this->executeContext()) {
|
||||
return false;
|
||||
}
|
||||
$this->executeContext();
|
||||
|
||||
list($status, $headers) = $this->parseHeaders(explode("\r\n", $this->headers[$this->headers_counter - 1]));
|
||||
|
||||
// When resticted with open_basedir
|
||||
// When restricted with open_basedir
|
||||
if ($this->needToHandleRedirection($follow_location, $status)) {
|
||||
return $this->handleRedirection($headers['Location']);
|
||||
}
|
||||
@ -250,11 +248,12 @@ class Curl extends Client
|
||||
*
|
||||
* @access private
|
||||
* @param string $location Redirected URL
|
||||
* @return boolean|array
|
||||
* @return array
|
||||
*/
|
||||
private function handleRedirection($location)
|
||||
{
|
||||
$nb_redirects = 0;
|
||||
$result = array();
|
||||
$this->url = $location;
|
||||
$this->body = '';
|
||||
$this->body_length = 0;
|
||||
@ -266,7 +265,7 @@ class Curl extends Client
|
||||
$nb_redirects++;
|
||||
|
||||
if ($nb_redirects >= $this->max_redirects) {
|
||||
return false;
|
||||
throw new MaxRedirectException('Maximum number of redirections reached');
|
||||
}
|
||||
|
||||
$result = $this->doRequest(false);
|
||||
@ -279,10 +278,50 @@ class Curl extends Client
|
||||
$this->headers_counter = 0;
|
||||
}
|
||||
else {
|
||||
return $result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle cURL errors (throw individual exceptions)
|
||||
*
|
||||
* We don't use constants because they are not necessary always available
|
||||
* (depends of the version of libcurl linked to php)
|
||||
*
|
||||
* @see http://curl.haxx.se/libcurl/c/libcurl-errors.html
|
||||
* @access private
|
||||
* @param integer $errno cURL error code
|
||||
*/
|
||||
private function handleError($errno)
|
||||
{
|
||||
switch ($errno) {
|
||||
case 78: // CURLE_REMOTE_FILE_NOT_FOUND
|
||||
throw new InvalidUrlException('Resource not found');
|
||||
case 6: // CURLE_COULDNT_RESOLVE_HOST
|
||||
throw new InvalidUrlException('Unable to resolve hostname');
|
||||
case 7: // CURLE_COULDNT_CONNECT
|
||||
throw new InvalidUrlException('Unable to connect to the remote host');
|
||||
case 28: // CURLE_OPERATION_TIMEDOUT
|
||||
throw new TimeoutException('Operation timeout');
|
||||
case 35: // CURLE_SSL_CONNECT_ERROR
|
||||
case 51: // CURLE_PEER_FAILED_VERIFICATION
|
||||
case 58: // CURLE_SSL_CERTPROBLEM
|
||||
case 60: // CURLE_SSL_CACERT
|
||||
case 59: // CURLE_SSL_CIPHER
|
||||
case 64: // CURLE_USE_SSL_FAILED
|
||||
case 66: // CURLE_SSL_ENGINE_INITFAILED
|
||||
case 77: // CURLE_SSL_CACERT_BADFILE
|
||||
case 83: // CURLE_SSL_ISSUER_ERROR
|
||||
throw new InvalidCertificateException('Invalid SSL certificate');
|
||||
case 47: // CURLE_TOO_MANY_REDIRECTS
|
||||
throw new MaxRedirectException('Maximum number of redirections reached');
|
||||
case 63: // CURLE_FILESIZE_EXCEEDED
|
||||
throw new MaxSizeException('Maximum response size exceeded');
|
||||
default:
|
||||
throw new InvalidUrlException('Unable to fetch the URL');
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed;
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
use DOMXPath;
|
||||
use PicoFeed\Encoding\Encoding;
|
||||
use PicoFeed\Logging\Logger;
|
||||
use PicoFeed\Filter\Filter;
|
||||
use PicoFeed\Parser\XmlParser;
|
||||
|
||||
/**
|
||||
* Grabber class
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package picofeed
|
||||
* @package Client
|
||||
*/
|
||||
class Grabber
|
||||
{
|
||||
@ -119,9 +123,9 @@ class Grabber
|
||||
* Config object
|
||||
*
|
||||
* @access private
|
||||
* @var \PicoFeed\Config
|
||||
* @var \PicoFeed\Config\Config
|
||||
*/
|
||||
private $config = null;
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -142,8 +146,8 @@ class Grabber
|
||||
* Set config object
|
||||
*
|
||||
* @access public
|
||||
* @param \PicoFeed\Config $config Config instance
|
||||
* @return \PicoFeed\Grabber
|
||||
* @param \PicoFeed\Config\Config $config Config instance
|
||||
* @return Grabber
|
||||
*/
|
||||
public function setConfig($config)
|
||||
{
|
||||
@ -173,6 +177,19 @@ class Grabber
|
||||
return $this->html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filtered relevant content
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getFilteredContent()
|
||||
{
|
||||
$filter = Filter::html($this->content, $this->url);
|
||||
$filter->setConfig($this->config);
|
||||
return $filter->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the HTML content
|
||||
*
|
||||
@ -183,30 +200,30 @@ class Grabber
|
||||
{
|
||||
if ($this->html) {
|
||||
|
||||
Logging::setMessage(get_called_class().' Fix encoding');
|
||||
Logging::setMessage(get_called_class().': HTTP Encoding "'.$this->encoding.'"');
|
||||
Logger::setMessage(get_called_class().' Fix encoding');
|
||||
Logger::setMessage(get_called_class().': HTTP Encoding "'.$this->encoding.'"');
|
||||
|
||||
$this->html = Filter::stripHeadTags($this->html);
|
||||
$this->html = Encoding::convert($this->html, $this->encoding);
|
||||
$this->html = Filter::stripHeadTags($this->html);
|
||||
|
||||
Logging::setMessage(get_called_class().' Content length: '.strlen($this->html).' bytes');
|
||||
Logger::setMessage(get_called_class().' Content length: '.strlen($this->html).' bytes');
|
||||
$rules = $this->getRules();
|
||||
|
||||
if (is_array($rules)) {
|
||||
Logging::setMessage(get_called_class().' Parse content with rules');
|
||||
Logger::setMessage(get_called_class().' Parse content with rules');
|
||||
$this->parseContentWithRules($rules);
|
||||
}
|
||||
else {
|
||||
Logging::setMessage(get_called_class().' Parse content with candidates');
|
||||
Logger::setMessage(get_called_class().' Parse content with candidates');
|
||||
$this->parseContentWithCandidates();
|
||||
}
|
||||
}
|
||||
else {
|
||||
Logging::setMessage(get_called_class().' No content fetched');
|
||||
Logger::setMessage(get_called_class().' No content fetched');
|
||||
}
|
||||
|
||||
Logging::setMessage(get_called_class().' Content length: '.strlen($this->content).' bytes');
|
||||
Logging::setMessage(get_called_class().' Grabber done');
|
||||
Logger::setMessage(get_called_class().' Content length: '.strlen($this->content).' bytes');
|
||||
Logger::setMessage(get_called_class().' Grabber done');
|
||||
|
||||
return $this->content !== '';
|
||||
}
|
||||
@ -223,6 +240,7 @@ class Grabber
|
||||
$client->setConfig($this->config);
|
||||
$client->execute($this->url);
|
||||
|
||||
$this->url = $client->getUrl();
|
||||
$this->html = $client->getContent();
|
||||
$this->encoding = $client->getEncoding();
|
||||
|
||||
@ -255,14 +273,12 @@ class Grabber
|
||||
$files[] = substr($hostname, 0, $pos);
|
||||
}
|
||||
|
||||
// Logging::setMessage(var_export($files, true));
|
||||
|
||||
foreach ($files as $file) {
|
||||
|
||||
$filename = __DIR__.'/Rules/'.$file.'.php';
|
||||
$filename = __DIR__.'/../Rules/'.$file.'.php';
|
||||
|
||||
if (file_exists($filename)) {
|
||||
Logging::setMessage(get_called_class().' Load rule: '.$file);
|
||||
Logger::setMessage(get_called_class().' Load rule: '.$file);
|
||||
return include $filename;
|
||||
}
|
||||
}
|
||||
@ -278,7 +294,7 @@ class Grabber
|
||||
*/
|
||||
public function parseContentWithRules(array $rules)
|
||||
{
|
||||
// Logging::setMessage($this->html);
|
||||
// Logger::setMessage($this->html);
|
||||
$dom = XmlParser::getHtmlDocument('<?xml version="1.0" encoding="UTF-8">'.$this->html);
|
||||
$xpath = new DOMXPath($dom);
|
||||
|
||||
@ -324,13 +340,13 @@ class Grabber
|
||||
// Try to lookup in each tag
|
||||
foreach ($this->candidatesAttributes as $candidate) {
|
||||
|
||||
Logging::setMessage(get_called_class().' Try this candidate: "'.$candidate.'"');
|
||||
Logger::setMessage(get_called_class().' Try this candidate: "'.$candidate.'"');
|
||||
|
||||
$nodes = $xpath->query('//*[(contains(@class, "'.$candidate.'") or @id="'.$candidate.'") and not (contains(@class, "nav") or contains(@class, "page"))]');
|
||||
|
||||
if ($nodes !== false && $nodes->length > 0) {
|
||||
$this->content = $dom->saveXML($nodes->item(0));
|
||||
Logging::setMessage(get_called_class().' Find candidate "'.$candidate.'" ('.strlen($this->content).' bytes)');
|
||||
Logger::setMessage(get_called_class().' Find candidate "'.$candidate.'" ('.strlen($this->content).' bytes)');
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -342,16 +358,16 @@ class Grabber
|
||||
|
||||
if ($nodes !== false && $nodes->length > 0) {
|
||||
$this->content = $dom->saveXML($nodes->item(0));
|
||||
Logging::setMessage(get_called_class().' Find <article/> tag ('.strlen($this->content).' bytes)');
|
||||
Logger::setMessage(get_called_class().' Find <article/> tag ('.strlen($this->content).' bytes)');
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen($this->content) < 50) {
|
||||
Logging::setMessage(get_called_class().' No enought content fetched, get the full body');
|
||||
Logger::setMessage(get_called_class().' No enought content fetched, get the full body');
|
||||
$this->content = $dom->saveXML($dom->firstChild);
|
||||
}
|
||||
|
||||
Logging::setMessage(get_called_class().' Strip garbage');
|
||||
Logger::setMessage(get_called_class().' Strip garbage');
|
||||
$this->stripGarbage();
|
||||
}
|
||||
|
||||
@ -373,7 +389,7 @@ class Grabber
|
||||
$nodes = $xpath->query('//'.$tag);
|
||||
|
||||
if ($nodes !== false && $nodes->length > 0) {
|
||||
Logging::setMessage(get_called_class().' Strip tag: "'.$tag.'"');
|
||||
Logger::setMessage(get_called_class().' Strip tag: "'.$tag.'"');
|
||||
foreach ($nodes as $node) {
|
||||
$node->parentNode->removeChild($node);
|
||||
}
|
||||
@ -385,7 +401,7 @@ class Grabber
|
||||
$nodes = $xpath->query('//*[contains(@class, "'.$attribute.'") or contains(@id, "'.$attribute.'")]');
|
||||
|
||||
if ($nodes !== false && $nodes->length > 0) {
|
||||
Logging::setMessage(get_called_class().' Strip attribute: "'.$attribute.'"');
|
||||
Logger::setMessage(get_called_class().' Strip attribute: "'.$attribute.'"');
|
||||
foreach ($nodes as $node) {
|
||||
$node->parentNode->removeChild($node);
|
||||
}
|
43
vendor/fguillot/picofeed/lib/PicoFeed/Client/HttpHeaders.php
vendored
Normal file
43
vendor/fguillot/picofeed/lib/PicoFeed/Client/HttpHeaders.php
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
use ArrayAccess;
|
||||
|
||||
/**
|
||||
* Class to handle http headers case insensitivity
|
||||
*
|
||||
* @author Bernhard Posselt
|
||||
* @package Client
|
||||
*/
|
||||
class HttpHeaders implements ArrayAccess
|
||||
{
|
||||
private $headers = array();
|
||||
|
||||
public function __construct(array $headers)
|
||||
{
|
||||
foreach ($headers as $key => $value) {
|
||||
$this->headers[strtolower($key)] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->headers[strtolower($offset)];
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->headers[strtolower($offset)] = $value;
|
||||
}
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->headers[strtolower($offset)]);
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->headers[strtolower($offset)]);
|
||||
}
|
||||
}
|
13
vendor/fguillot/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php
vendored
Normal file
13
vendor/fguillot/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
/**
|
||||
* InvalidCertificateException Exception
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package Client
|
||||
*/
|
||||
class InvalidCertificateException extends ClientException
|
||||
{
|
||||
}
|
13
vendor/fguillot/picofeed/lib/PicoFeed/Client/InvalidUrlException.php
vendored
Normal file
13
vendor/fguillot/picofeed/lib/PicoFeed/Client/InvalidUrlException.php
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
/**
|
||||
* InvalidUrlException Exception
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package Client
|
||||
*/
|
||||
class InvalidUrlException extends ClientException
|
||||
{
|
||||
}
|
13
vendor/fguillot/picofeed/lib/PicoFeed/Client/MaxRedirectException.php
vendored
Normal file
13
vendor/fguillot/picofeed/lib/PicoFeed/Client/MaxRedirectException.php
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
/**
|
||||
* MaxRedirectException Exception
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package Client
|
||||
*/
|
||||
class MaxRedirectException extends ClientException
|
||||
{
|
||||
}
|
13
vendor/fguillot/picofeed/lib/PicoFeed/Client/MaxSizeException.php
vendored
Normal file
13
vendor/fguillot/picofeed/lib/PicoFeed/Client/MaxSizeException.php
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
/**
|
||||
* MaxSizeException Exception
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package Client
|
||||
*/
|
||||
class MaxSizeException extends ClientException
|
||||
{
|
||||
}
|
@ -1,15 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed\Clients;
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
use \PicoFeed\Logging;
|
||||
use \PicoFeed\Client;
|
||||
use PicoFeed\Logging\Logger;
|
||||
|
||||
/**
|
||||
* Stream context HTTP client
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package client
|
||||
* @package Client
|
||||
*/
|
||||
class Stream extends Client
|
||||
{
|
||||
@ -17,7 +16,7 @@ class Stream extends Client
|
||||
* Prepare HTTP headers
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
* @return string[]
|
||||
*/
|
||||
private function prepareHeaders()
|
||||
{
|
||||
@ -64,16 +63,16 @@ class Stream extends Client
|
||||
|
||||
if ($this->proxy_hostname) {
|
||||
|
||||
Logging::setMessage(get_called_class().' Proxy: '.$this->proxy_hostname.':'.$this->proxy_port);
|
||||
Logger::setMessage(get_called_class().' Proxy: '.$this->proxy_hostname.':'.$this->proxy_port);
|
||||
|
||||
$context['http']['proxy'] = 'tcp://'.$this->proxy_hostname.':'.$this->proxy_port;
|
||||
$context['http']['request_fulluri'] = true;
|
||||
|
||||
if ($this->proxy_username) {
|
||||
Logging::setMessage(get_called_class().' Proxy credentials: Yes');
|
||||
Logger::setMessage(get_called_class().' Proxy credentials: Yes');
|
||||
}
|
||||
else {
|
||||
Logging::setMessage(get_called_class().' Proxy credentials: No');
|
||||
Logger::setMessage(get_called_class().' Proxy credentials: No');
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,7 +95,7 @@ class Stream extends Client
|
||||
// Make HTTP request
|
||||
$stream = @fopen($this->url, 'r', false, $context);
|
||||
if (! is_resource($stream)) {
|
||||
return false;
|
||||
throw new InvalidUrlException('Unable to establish a connection');
|
||||
}
|
||||
|
||||
// Get the entire body until the max size
|
||||
@ -104,12 +103,16 @@ class Stream extends Client
|
||||
|
||||
// If the body size is too large abort everything
|
||||
if (strlen($body) > $this->max_body_size) {
|
||||
return false;
|
||||
throw new MaxSizeException('Content size too large');
|
||||
}
|
||||
|
||||
// Get HTTP headers response
|
||||
$metadata = stream_get_meta_data($stream);
|
||||
|
||||
if ($metadata['timed_out']) {
|
||||
throw new TimeoutException('Operation timeout');
|
||||
}
|
||||
|
||||
list($status, $headers) = $this->parseHeaders($metadata['wrapper_data']);
|
||||
|
||||
fclose($stream);
|
||||
@ -125,11 +128,11 @@ class Stream extends Client
|
||||
* Decode body response according to the HTTP headers
|
||||
*
|
||||
* @access public
|
||||
* @param string $body Raw body
|
||||
* @param array $headers HTTP headers
|
||||
* @param string $body Raw body
|
||||
* @param HttpHeaders $headers HTTP headers
|
||||
* @return string
|
||||
*/
|
||||
public function decodeBody($body, array $headers)
|
||||
public function decodeBody($body, HttpHeaders $headers)
|
||||
{
|
||||
if (isset($headers['Transfer-Encoding']) && $headers['Transfer-Encoding'] === 'chunked') {
|
||||
$body = $this->decodeChunked($body);
|
13
vendor/fguillot/picofeed/lib/PicoFeed/Client/TimeoutException.php
vendored
Normal file
13
vendor/fguillot/picofeed/lib/PicoFeed/Client/TimeoutException.php
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
/**
|
||||
* TimeoutException Exception
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package Client
|
||||
*/
|
||||
class TimeoutException extends ClientException
|
||||
{
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed;
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
/**
|
||||
* URL class
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package picofeed
|
||||
* @package Client
|
||||
*/
|
||||
class Url
|
||||
{
|
||||
@ -79,6 +79,20 @@ class Url
|
||||
return $link->getAbsoluteUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut method to get a base url
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
public static function base($url)
|
||||
{
|
||||
$link = new Url($url);
|
||||
return $link->getBaseUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base URL
|
||||
*
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed;
|
||||
namespace PicoFeed\Config;
|
||||
|
||||
/**
|
||||
* Config class
|
||||
@ -8,28 +8,30 @@ namespace PicoFeed;
|
||||
* @author Frederic Guillot
|
||||
* @package picofeed
|
||||
*
|
||||
* @method \PicoFeed\Config setClientTimeout(integer $value)
|
||||
* @method \PicoFeed\Config setClientUserAgent(string $value)
|
||||
* @method \PicoFeed\Config setMaxRedirections(integer $value)
|
||||
* @method \PicoFeed\Config setMaxBodySize(integer $value)
|
||||
* @method \PicoFeed\Config setProxyHostname(string $value)
|
||||
* @method \PicoFeed\Config setProxyPort(integer $value)
|
||||
* @method \PicoFeed\Config setProxyUsername(string $value)
|
||||
* @method \PicoFeed\Config setProxyPassword(string $value)
|
||||
* @method \PicoFeed\Config setGrabberTimeout(integer $value)
|
||||
* @method \PicoFeed\Config setGrabberUserAgent(string $value)
|
||||
* @method \PicoFeed\Config setParserHashAlgo(string $value)
|
||||
* @method \PicoFeed\Config setContentFiltering(boolean $value)
|
||||
* @method \PicoFeed\Config setTimezone(string $value)
|
||||
* @method \PicoFeed\Config setFilterIframeWhitelist(array $value)
|
||||
* @method \PicoFeed\Config setFilterIntegerAttributes(array $value)
|
||||
* @method \PicoFeed\Config setFilterAttributeOverrides(array $value)
|
||||
* @method \PicoFeed\Config setFilterRequiredAttributes(array $value)
|
||||
* @method \PicoFeed\Config setFilterMediaBlacklist(array $value)
|
||||
* @method \PicoFeed\Config setFilterMediaAttributes(array $value)
|
||||
* @method \PicoFeed\Config setFilterSchemeWhitelist(array $value)
|
||||
* @method \PicoFeed\Config setFilterWhitelistedTags(array $value)
|
||||
* @method \PicoFeed\Config setFilterBlacklistedTags(array $value)
|
||||
* @method \PicoFeed\Config\Config setClientTimeout(integer $value)
|
||||
* @method \PicoFeed\Config\Config setClientUserAgent(string $value)
|
||||
* @method \PicoFeed\Config\Config setMaxRedirections(integer $value)
|
||||
* @method \PicoFeed\Config\Config setMaxBodySize(integer $value)
|
||||
* @method \PicoFeed\Config\Config setProxyHostname(string $value)
|
||||
* @method \PicoFeed\Config\Config setProxyPort(integer $value)
|
||||
* @method \PicoFeed\Config\Config setProxyUsername(string $value)
|
||||
* @method \PicoFeed\Config\Config setProxyPassword(string $value)
|
||||
* @method \PicoFeed\Config\Config setGrabberTimeout(integer $value)
|
||||
* @method \PicoFeed\Config\Config setGrabberUserAgent(string $value)
|
||||
* @method \PicoFeed\Config\Config setParserHashAlgo(string $value)
|
||||
* @method \PicoFeed\Config\Config setContentFiltering(boolean $value)
|
||||
* @method \PicoFeed\Config\Config setTimezone(string $value)
|
||||
* @method \PicoFeed\Config\Config setFilterIframeWhitelist(array $value)
|
||||
* @method \PicoFeed\Config\Config setFilterIntegerAttributes(array $value)
|
||||
* @method \PicoFeed\Config\Config setFilterAttributeOverrides(array $value)
|
||||
* @method \PicoFeed\Config\Config setFilterRequiredAttributes(array $value)
|
||||
* @method \PicoFeed\Config\Config setFilterMediaBlacklist(array $value)
|
||||
* @method \PicoFeed\Config\Config setFilterMediaAttributes(array $value)
|
||||
* @method \PicoFeed\Config\Config setFilterSchemeWhitelist(array $value)
|
||||
* @method \PicoFeed\Config\Config setFilterWhitelistedTags(array $value)
|
||||
* @method \PicoFeed\Config\Config setFilterBlacklistedTags(array $value)
|
||||
* @method \PicoFeed\Config\Config setFilterImageProxyUrl($value)
|
||||
* @method \PicoFeed\Config\Config setFilterImageProxyCallback($closure)
|
||||
*
|
||||
* @method integer getClientTimeout()
|
||||
* @method string getClientUserAgent()
|
||||
@ -53,6 +55,8 @@ namespace PicoFeed;
|
||||
* @method array getFilterSchemeWhitelist(array $default_value)
|
||||
* @method array getFilterWhitelistedTags(array $default_value)
|
||||
* @method array getFilterBlacklistedTags(array $default_value)
|
||||
* @method string getFilterImageProxyUrl()
|
||||
* @method \Closure getFilterImageProxyCallback()
|
||||
*/
|
||||
class Config
|
||||
{
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed;
|
||||
namespace PicoFeed\Encoding;
|
||||
|
||||
/**
|
||||
* @author "Sebastián Grignoli" <grignoli@framework2.com.ar>
|
||||
@ -152,20 +152,16 @@ class Encoding
|
||||
return $cc1.$cc2;
|
||||
}
|
||||
|
||||
public static function convert_CP_1251($input)
|
||||
{
|
||||
return iconv('CP1251', 'UTF-8//TRANSLIT', $input);
|
||||
}
|
||||
|
||||
public static function convert($input, $encoding)
|
||||
{
|
||||
if ($encoding === 'windows-1251') {
|
||||
return self::convert_CP_1251($input);
|
||||
switch ($encoding) {
|
||||
case 'utf-8':
|
||||
return $input;
|
||||
case 'windows-1251':
|
||||
case 'windows-1255':
|
||||
return iconv($encoding, 'UTF-8//TRANSLIT', $input);
|
||||
default:
|
||||
return self::toUTF8($input);
|
||||
}
|
||||
else if ($encoding === '' || $encoding !== 'utf-8') {
|
||||
return self::toUTF8($input);
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
}
|
@ -2,17 +2,32 @@
|
||||
|
||||
namespace PicoFeed\Filter;
|
||||
|
||||
use \PicoFeed\Url;
|
||||
use \PicoFeed\Filter;
|
||||
use \PicoFeed\Client\Url;
|
||||
|
||||
/**
|
||||
* Attribute Filter class
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package filter
|
||||
* @package Filter
|
||||
*/
|
||||
class Attribute
|
||||
{
|
||||
/**
|
||||
* Image proxy url
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $image_proxy_url = '';
|
||||
|
||||
/**
|
||||
* Image proxy callback
|
||||
*
|
||||
* @access private
|
||||
* @var \Closure|null
|
||||
*/
|
||||
private $image_proxy_callback = null;
|
||||
|
||||
/**
|
||||
* Tags and attribute whitelist
|
||||
*
|
||||
@ -205,25 +220,26 @@ class Attribute
|
||||
'filterEmptyAttribute',
|
||||
'filterAllowedAttribute',
|
||||
'filterIntegerAttribute',
|
||||
'filterAbsoluteUrlAttribute',
|
||||
'rewriteAbsoluteUrl',
|
||||
'filterIframeAttribute',
|
||||
'filterBlacklistResourceAttribute',
|
||||
'filterProtocolUrlAttribute',
|
||||
'rewriteImageProxyUrl',
|
||||
);
|
||||
|
||||
/**
|
||||
* Add attributes to specified tags
|
||||
*
|
||||
* @access private
|
||||
* @var \PicoFeed\Url
|
||||
* @var \PicoFeed\Client\Url
|
||||
*/
|
||||
private $website = null;
|
||||
private $website;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \PicoFeed\Url $website Website url instance
|
||||
* @param \PicoFeed\Client\Url $website Website url instance
|
||||
*/
|
||||
public function __construct(Url $website)
|
||||
{
|
||||
@ -350,7 +366,7 @@ class Attribute
|
||||
* @param string $value Atttribute value
|
||||
* @return boolean
|
||||
*/
|
||||
public function filterAbsoluteUrlAttribute($tag, $attribute, &$value)
|
||||
public function rewriteAbsoluteUrl($tag, $attribute, &$value)
|
||||
{
|
||||
if ($this->isResource($attribute)) {
|
||||
$value = Url::resolve($value, $this->website);
|
||||
@ -359,6 +375,30 @@ class Attribute
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite image url to use with a proxy
|
||||
*
|
||||
* @access public
|
||||
* @param string $tag Tag name
|
||||
* @param string $attribute Atttribute name
|
||||
* @param string $value Atttribute value
|
||||
* @return boolean
|
||||
*/
|
||||
public function rewriteImageProxyUrl($tag, $attribute, &$value)
|
||||
{
|
||||
if ($tag === 'img' && $attribute === 'src') {
|
||||
|
||||
if ($this->image_proxy_url) {
|
||||
$value = sprintf($this->image_proxy_url, urlencode($value));
|
||||
}
|
||||
else if (is_callable($this->image_proxy_callback)) {
|
||||
$value = call_user_func($this->image_proxy_callback, $value);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the scheme is authorized
|
||||
*
|
||||
@ -420,7 +460,7 @@ class Attribute
|
||||
* Check if an attribute name is an external resource
|
||||
*
|
||||
* @access public
|
||||
* @param string $data Attribute name
|
||||
* @param string $attribute Attribute name
|
||||
* @return boolean
|
||||
*/
|
||||
public function isResource($attribute)
|
||||
@ -451,7 +491,7 @@ class Attribute
|
||||
* Detect if an url is blacklisted
|
||||
*
|
||||
* @access public
|
||||
* @param string $resouce Attribute value (URL)
|
||||
* @param string $resource Attribute value (URL)
|
||||
* @return boolean
|
||||
*/
|
||||
public function isBlacklistedMedia($resource)
|
||||
@ -485,11 +525,11 @@ class Attribute
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whitelisted tags adn attributes for each tag
|
||||
* Set whitelisted tags and attributes for each tag
|
||||
*
|
||||
* @access public
|
||||
* @param array $values List of tags: ['video' => ['src', 'cover'], 'img' => ['src']]
|
||||
* @return \PicoFeed\Filter
|
||||
* @return Attribute
|
||||
*/
|
||||
public function setWhitelistedAttributes(array $values)
|
||||
{
|
||||
@ -502,7 +542,7 @@ class Attribute
|
||||
*
|
||||
* @access public
|
||||
* @param array $values List of scheme: ['http://', 'ftp://']
|
||||
* @return \PicoFeed\Filter
|
||||
* @return Attribute
|
||||
*/
|
||||
public function setSchemeWhitelist(array $values)
|
||||
{
|
||||
@ -515,7 +555,7 @@ class Attribute
|
||||
*
|
||||
* @access public
|
||||
* @param array $values List of values: ['src', 'href']
|
||||
* @return \PicoFeed\Filter
|
||||
* @return Attribute
|
||||
*/
|
||||
public function setMediaAttributes(array $values)
|
||||
{
|
||||
@ -528,7 +568,7 @@ class Attribute
|
||||
*
|
||||
* @access public
|
||||
* @param array $values List of tags: ['http://google.com/', '...']
|
||||
* @return \PicoFeed\Filter
|
||||
* @return Attribute
|
||||
*/
|
||||
public function setMediaBlacklist(array $values)
|
||||
{
|
||||
@ -541,7 +581,7 @@ class Attribute
|
||||
*
|
||||
* @access public
|
||||
* @param array $values List of tags: ['img' => 'src']
|
||||
* @return \PicoFeed\Filter
|
||||
* @return Attribute
|
||||
*/
|
||||
public function setRequiredAttributes(array $values)
|
||||
{
|
||||
@ -554,7 +594,7 @@ class Attribute
|
||||
*
|
||||
* @access public
|
||||
* @param array $values List of tags: ['a' => 'target="_blank"']
|
||||
* @return \PicoFeed\Filter
|
||||
* @return Attribute
|
||||
*/
|
||||
public function setAttributeOverrides(array $values)
|
||||
{
|
||||
@ -567,7 +607,7 @@ class Attribute
|
||||
*
|
||||
* @access public
|
||||
* @param array $values List of tags: ['width', 'height']
|
||||
* @return \PicoFeed\Filter
|
||||
* @return Attribute
|
||||
*/
|
||||
public function setIntegerAttributes(array $values)
|
||||
{
|
||||
@ -580,11 +620,39 @@ class Attribute
|
||||
*
|
||||
* @access public
|
||||
* @param array $values List of tags: ['http://www.youtube.com']
|
||||
* @return \PicoFeed\Filter
|
||||
* @return Attribute
|
||||
*/
|
||||
public function setIframeWhitelist(array $values)
|
||||
{
|
||||
$this->iframe_whitelist = $values ?: $this->iframe_whitelist;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set image proxy URL
|
||||
*
|
||||
* The original image url will be urlencoded
|
||||
*
|
||||
* @access public
|
||||
* @param string $url Proxy URL
|
||||
* @return Attribute
|
||||
*/
|
||||
public function setImageProxyUrl($url)
|
||||
{
|
||||
$this->image_proxy_url = $url ?: $this->image_proxy_url;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set image proxy callback
|
||||
*
|
||||
* @access public
|
||||
* @param \Closure $callback
|
||||
* @return Attribute
|
||||
*/
|
||||
public function setImageProxyCallback($callback)
|
||||
{
|
||||
$this->image_proxy_callback = $callback ?: $this->image_proxy_callback;
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -1,14 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed;
|
||||
|
||||
use PicoFeed\Filter\Html;
|
||||
namespace PicoFeed\Filter;
|
||||
|
||||
/**
|
||||
* Filter class
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package picofeed
|
||||
* @package Filter
|
||||
*/
|
||||
class Filter
|
||||
{
|
||||
@ -19,7 +17,7 @@ class Filter
|
||||
* @access public
|
||||
* @param string $html HTML content
|
||||
* @param string $website Site URL (used to build absolute URL)
|
||||
* @return PicoFeed\Filter\Html
|
||||
* @return Html
|
||||
*/
|
||||
public static function html($html, $website)
|
||||
{
|
||||
@ -88,16 +86,7 @@ class Filter
|
||||
*/
|
||||
public static function stripHeadTags($data)
|
||||
{
|
||||
$start = strpos($data, '<head>');
|
||||
$end = strpos($data, '</head>');
|
||||
|
||||
if ($start !== false && $end !== false) {
|
||||
$before = substr($data, 0, $start);
|
||||
$after = substr($data, $end + 7);
|
||||
$data = $before.$after;
|
||||
}
|
||||
|
||||
return $data;
|
||||
return preg_replace('@<head[^>]*?>.*?</head>@siu','', $data );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,10 +102,7 @@ class Filter
|
||||
$value = str_replace("\r", ' ', $value);
|
||||
$value = str_replace("\t", ' ', $value);
|
||||
$value = str_replace("\n", ' ', $value);
|
||||
|
||||
// Break UTF-8 strings (TODO: find a better way)
|
||||
// $value = preg_replace('/\s+/', ' ', $value);
|
||||
|
||||
// $value = preg_replace('/\s+/', ' ', $value); <= break utf-8
|
||||
return trim($value);
|
||||
}
|
||||
|
@ -2,15 +2,14 @@
|
||||
|
||||
namespace PicoFeed\Filter;
|
||||
|
||||
use \PicoFeed\Url;
|
||||
use \PicoFeed\Filter;
|
||||
use \PicoFeed\XmlParser;
|
||||
use \PicoFeed\Client\Url;
|
||||
use \PicoFeed\Parser\XmlParser;
|
||||
|
||||
/**
|
||||
* HTML Filter class
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package filter
|
||||
* @package Filter
|
||||
*/
|
||||
class Html
|
||||
{
|
||||
@ -18,9 +17,9 @@ class Html
|
||||
* Config object
|
||||
*
|
||||
* @access private
|
||||
* @var \PicoFeed\Config
|
||||
* @var \PicoFeed\Config\Config
|
||||
*/
|
||||
private $config = null;
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* Unfiltered XML data
|
||||
@ -89,14 +88,16 @@ class Html
|
||||
* Set config object
|
||||
*
|
||||
* @access public
|
||||
* @param \PicoFeed\Config $config Config instance
|
||||
* @return \PicoFeed\Html
|
||||
* @param \PicoFeed\Config\Config $config Config instance
|
||||
* @return \PicoFeed\Filter\Html
|
||||
*/
|
||||
public function setConfig($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
|
||||
if ($this->config !== null) {
|
||||
$this->attribute->setImageProxyCallback($this->config->getFilterImageProxyCallback());
|
||||
$this->attribute->setImageProxyUrl($this->config->getFilterImageProxyUrl());
|
||||
$this->attribute->setIframeWhitelist($this->config->getFilterIframeWhitelist(array()));
|
||||
$this->attribute->setIntegerAttributes($this->config->getFilterIntegerAttributes(array()));
|
||||
$this->attribute->setAttributeOverrides($this->config->getFilterAttributeOverrides(array()));
|
||||
@ -133,6 +134,11 @@ class Html
|
||||
return $this->output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after XML parsing
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function postFilter()
|
||||
{
|
||||
$this->output = $this->tag->removeEmptyTags($this->output);
|
||||
@ -144,7 +150,7 @@ class Html
|
||||
*
|
||||
* @access public
|
||||
* @param resource $parser XML parser
|
||||
* @param string $name Tag name
|
||||
* @param string $tag Tag name
|
||||
* @param array $attributes Tag attributes
|
||||
*/
|
||||
public function startTag($parser, $tag, array $attributes)
|
||||
@ -172,7 +178,7 @@ class Html
|
||||
*
|
||||
* @access public
|
||||
* @param resource $parser XML parser
|
||||
* @param string $name Tag name
|
||||
* @param string $tag Tag name
|
||||
*/
|
||||
public function endTag($parser, $tag)
|
||||
{
|
@ -6,7 +6,7 @@ namespace PicoFeed\Filter;
|
||||
* Tag Filter class
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package filter
|
||||
* @package Filter
|
||||
*/
|
||||
class Tag
|
||||
{
|
||||
@ -163,7 +163,7 @@ class Tag
|
||||
*
|
||||
* @access public
|
||||
* @param array $values List of tags: ['video' => ['src', 'cover'], 'img' => ['src']]
|
||||
* @return \PicoFeed\Filter
|
||||
* @return Tag
|
||||
*/
|
||||
public function setWhitelistedTags(array $values)
|
||||
{
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed;
|
||||
namespace PicoFeed\Logging;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
@ -9,9 +9,9 @@ use DateTimeZone;
|
||||
* Logging class
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package picofeed
|
||||
* @package Logging
|
||||
*/
|
||||
class Logging
|
||||
class Logger
|
||||
{
|
||||
/**
|
||||
* List of messages
|
||||
@ -80,4 +80,16 @@ class Logging
|
||||
{
|
||||
self::$timezone = $timezone ?: self::$timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all messages serialized into a string
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public static function toString()
|
||||
{
|
||||
return implode(PHP_EOL, self::$messages).PHP_EOL;
|
||||
}
|
||||
}
|
@ -1,21 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed\Parsers;
|
||||
namespace PicoFeed\Parser;
|
||||
|
||||
use SimpleXMLElement;
|
||||
use PicoFeed\Parser;
|
||||
use PicoFeed\XmlParser;
|
||||
use PicoFeed\Logging;
|
||||
use PicoFeed\Feed;
|
||||
use PicoFeed\Filter;
|
||||
use PicoFeed\Item;
|
||||
use PicoFeed\Url;
|
||||
use PicoFeed\Filter\Filter;
|
||||
use PicoFeed\Client\Url;
|
||||
|
||||
/**
|
||||
* Atom parser
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package parser
|
||||
* @package Parser
|
||||
*/
|
||||
class Atom extends Parser
|
||||
{
|
||||
@ -35,20 +30,32 @@ class Atom extends Parser
|
||||
* Find the feed url
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findFeedUrl(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
$feed->url = $this->getLink($xml);
|
||||
$feed->feed_url = $this->getUrl($xml, 'self');
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the site url
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findSiteUrl(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
$feed->site_url = $this->getUrl($xml, 'alternate', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the feed description
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findFeedDescription(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
@ -59,8 +66,8 @@ class Atom extends Parser
|
||||
* Find the feed logo url
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findFeedLogo(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
@ -71,20 +78,20 @@ class Atom extends Parser
|
||||
* Find the feed title
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findFeedTitle(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
$feed->title = Filter::stripWhiteSpace((string) $xml->title) ?: $feed->url;
|
||||
$feed->title = Filter::stripWhiteSpace((string) $xml->title) ?: $feed->getSiteUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the feed language
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findFeedLanguage(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
@ -95,8 +102,8 @@ class Atom extends Parser
|
||||
* Find the feed id
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findFeedId(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
@ -107,8 +114,8 @@ class Atom extends Parser
|
||||
* Find the feed date
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findFeedDate(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
@ -120,11 +127,14 @@ class Atom extends Parser
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param Item $item Item object
|
||||
* @param Item $item Item object
|
||||
*/
|
||||
public function findItemDate(SimpleXMLElement $entry, Item $item)
|
||||
{
|
||||
$item->date = $this->parseDate((string) $entry->updated);
|
||||
$published = isset($entry->published) ? $this->parseDate((string) $entry->published) : 0;
|
||||
$updated = isset($entry->updated) ? $this->parseDate((string) $entry->updated) : 0;
|
||||
|
||||
$item->date = max($published, $updated) ?: time();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -147,9 +157,9 @@ class Atom extends Parser
|
||||
* Find the item author
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param SimpleXMLElement $xml Feed
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
*/
|
||||
public function findItemAuthor(SimpleXMLElement $xml, SimpleXMLElement $entry, Item $item)
|
||||
{
|
||||
@ -166,7 +176,7 @@ class Atom extends Parser
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
*/
|
||||
public function findItemContent(SimpleXMLElement $entry, Item $item)
|
||||
{
|
||||
@ -178,11 +188,11 @@ class Atom extends Parser
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
*/
|
||||
public function findItemUrl(SimpleXMLElement $entry, Item $item)
|
||||
{
|
||||
$item->url = $this->getLink($entry);
|
||||
$item->url = $this->getUrl($entry, 'alternate', true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -190,28 +200,21 @@ class Atom extends Parser
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed)
|
||||
{
|
||||
$id = (string) $entry->id;
|
||||
|
||||
if ($id !== $item->url) {
|
||||
$item_permalink = $id;
|
||||
if ($id) {
|
||||
$item->id = $this->generateId($id);
|
||||
}
|
||||
else {
|
||||
$item_permalink = $item->url;
|
||||
$item->id = $this->generateId(
|
||||
$item->getTitle(), $item->getUrl(), $item->getContent()
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->isExcludedFromId($feed->url)) {
|
||||
$feed_permalink = '';
|
||||
}
|
||||
else {
|
||||
$feed_permalink = $feed->url;
|
||||
}
|
||||
|
||||
$item->id = $this->generateId($item_permalink, $feed_permalink);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -219,18 +222,16 @@ class Atom extends Parser
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed)
|
||||
{
|
||||
foreach ($entry->link as $link) {
|
||||
if ((string) $link['rel'] === 'enclosure') {
|
||||
$enclosure = $this->findLink($entry, 'enclosure');
|
||||
|
||||
$item->enclosure_url = Url::resolve((string) $link['href'], $feed->url);
|
||||
$item->enclosure_type = (string) $link['type'];
|
||||
break;
|
||||
}
|
||||
if ($enclosure) {
|
||||
$item->enclosure_url = Url::resolve((string) $enclosure['href'], $feed->getSiteUrl());
|
||||
$item->enclosure_type = (string) $enclosure['type'];
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,40 +240,71 @@ class Atom extends Parser
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed)
|
||||
{
|
||||
$item->language = $feed->language;
|
||||
$language = (string) $entry->attributes('xml', true)->{'lang'};
|
||||
|
||||
if ($language === '') {
|
||||
$language = $feed->language;
|
||||
}
|
||||
|
||||
$item->language = $language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL from a link tag
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml XML tag
|
||||
* @access private
|
||||
* @param SimpleXMLElement $xml XML tag
|
||||
* @param string $rel Link relationship: alternate, enclosure, related, self, via
|
||||
* @return string
|
||||
*/
|
||||
public function getLink(SimpleXMLElement $xml)
|
||||
private function getUrl(SimpleXMLElement $xml, $rel, $fallback = false)
|
||||
{
|
||||
$link = $this->findLink($xml, $rel);
|
||||
|
||||
if ($link) {
|
||||
return (string) $link['href'];
|
||||
}
|
||||
|
||||
if ($fallback) {
|
||||
$link = $this->findLink($xml, '');
|
||||
return $link ? (string) $link['href'] : '';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a link tag that match a relationship
|
||||
*
|
||||
* @access private
|
||||
* @param SimpleXMLElement $xml XML tag
|
||||
* @param string $rel Link relationship: alternate, enclosure, related, self, via
|
||||
* @return SimpleXMLElement|null
|
||||
*/
|
||||
private function findLink(SimpleXMLElement $xml, $rel)
|
||||
{
|
||||
foreach ($xml->link as $link) {
|
||||
if ((string) $link['type'] === 'text/html' || (string) $link['type'] === 'application/xhtml+xml') {
|
||||
return (string) $link['href'];
|
||||
if ($rel === (string) $link['rel']) {
|
||||
return $link;
|
||||
}
|
||||
}
|
||||
|
||||
return (string) $xml->link['href'];
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry content
|
||||
*
|
||||
* @access public
|
||||
* @access private
|
||||
* @param SimpleXMLElement $entry XML Entry
|
||||
* @return string
|
||||
*/
|
||||
public function getContent(SimpleXMLElement $entry)
|
||||
private function getContent(SimpleXMLElement $entry)
|
||||
{
|
||||
if (isset($entry->content) && ! empty($entry->content)) {
|
||||
|
@ -1,12 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed;
|
||||
namespace PicoFeed\Parser;
|
||||
|
||||
/**
|
||||
* Feed
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package picofeed
|
||||
* @package Parser
|
||||
*/
|
||||
class Feed
|
||||
{
|
||||
@ -48,7 +48,15 @@ class Feed
|
||||
* @access public
|
||||
* @var string
|
||||
*/
|
||||
public $url = '';
|
||||
public $feed_url = '';
|
||||
|
||||
/**
|
||||
* Site url
|
||||
*
|
||||
* @access public
|
||||
* @var string
|
||||
*/
|
||||
public $site_url = '';
|
||||
|
||||
/**
|
||||
* Feed date
|
||||
@ -84,10 +92,11 @@ class Feed
|
||||
{
|
||||
$output = '';
|
||||
|
||||
foreach (array('id', 'title', 'url', 'date', 'language', 'description', 'logo') as $property) {
|
||||
foreach (array('id', 'title', 'feed_url', 'site_url', 'date', 'language', 'description', 'logo') as $property) {
|
||||
$output .= 'Feed::'.$property.' = '.$this->$property.PHP_EOL;
|
||||
}
|
||||
|
||||
$output .= 'Feed::isRTL() = '.($this->isRTL() ? 'true' : 'false').PHP_EOL;
|
||||
$output .= 'Feed::items = '.count($this->items).' items'.PHP_EOL;
|
||||
|
||||
foreach ($this->items as $item) {
|
||||
@ -132,14 +141,25 @@ class Feed
|
||||
}
|
||||
|
||||
/**
|
||||
* Get url
|
||||
* Get feed url
|
||||
*
|
||||
* @access public
|
||||
* $return string
|
||||
*/
|
||||
public function getUrl()
|
||||
public function getFeedUrl()
|
||||
{
|
||||
return $this->url;
|
||||
return $this->feed_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get site url
|
||||
*
|
||||
* @access public
|
||||
* $return string
|
||||
*/
|
||||
public function getSiteUrl()
|
||||
{
|
||||
return $this->site_url;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -185,4 +205,15 @@ class Feed
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the feed is "Right to Left"
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isRTL()
|
||||
{
|
||||
return Parser::isLanguageRTL($this->language);
|
||||
}
|
||||
}
|
@ -1,15 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed;
|
||||
namespace PicoFeed\Parser;
|
||||
|
||||
/**
|
||||
* Feed Item
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package picofeed
|
||||
* @package Parser
|
||||
*/
|
||||
class Item
|
||||
{
|
||||
/**
|
||||
* List of known RTL languages
|
||||
*
|
||||
* @access public
|
||||
* @var public
|
||||
*/
|
||||
public $rtl = array(
|
||||
'ar', // Arabic (ar-**)
|
||||
'fa', // Farsi (fa-**)
|
||||
'ur', // Urdu (ur-**)
|
||||
'ps', // Pashtu (ps-**)
|
||||
'syr', // Syriac (syr-**)
|
||||
'dv', // Divehi (dv-**)
|
||||
'he', // Hebrew (he-**)
|
||||
'yi', // Yiddish (yi-**)
|
||||
);
|
||||
|
||||
/**
|
||||
* Item id
|
||||
*
|
||||
@ -96,6 +113,7 @@ class Item
|
||||
$output .= 'Item::'.$property.' = '.$this->$property.PHP_EOL;
|
||||
}
|
||||
|
||||
$output .= 'Item::isRTL() = '.($this->isRTL() ? 'true' : 'false').PHP_EOL;
|
||||
$output .= 'Item::content = '.strlen($this->content).' bytes'.PHP_EOL;
|
||||
|
||||
return $output;
|
||||
@ -199,4 +217,15 @@ class Item
|
||||
{
|
||||
return $this->author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the item is "Right to Left"
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isRTL()
|
||||
{
|
||||
return Parser::isLanguageRTL($this->language);
|
||||
}
|
||||
}
|
13
vendor/fguillot/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php
vendored
Normal file
13
vendor/fguillot/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed\Parser;
|
||||
|
||||
/**
|
||||
* MalformedXmlException Exception
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package Parser
|
||||
*/
|
||||
class MalformedXmlException extends ParserException
|
||||
{
|
||||
}
|
@ -1,16 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed;
|
||||
namespace PicoFeed\Parser;
|
||||
|
||||
use SimpleXMLElement;
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
use PicoFeed\Encoding\Encoding;
|
||||
use PicoFeed\Filter\Filter;
|
||||
use PicoFeed\Logging\Logger;
|
||||
use PicoFeed\Client\Url;
|
||||
use PicoFeed\Client\Grabber;
|
||||
|
||||
/**
|
||||
* Base parser class
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package parser
|
||||
* @package Parser
|
||||
*/
|
||||
abstract class Parser
|
||||
{
|
||||
@ -18,9 +23,9 @@ abstract class Parser
|
||||
* Config object
|
||||
*
|
||||
* @access private
|
||||
* @var \PicoFeed\Config
|
||||
* @var \PicoFeed\Config\Config
|
||||
*/
|
||||
private $config = null;
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* Hash algorithm used to generate item id, any value supported by PHP, see hash_algos()
|
||||
@ -28,7 +33,7 @@ abstract class Parser
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $hash_algo = 'crc32b'; // crc32b seems to be faster and shorter than other hash algorithms
|
||||
private $hash_algo = 'sha256';
|
||||
|
||||
/**
|
||||
* Timezone used to parse feed dates
|
||||
@ -90,9 +95,9 @@ abstract class Parser
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param string $content Feed content
|
||||
* @param string $http_encoding HTTP encoding (headers)
|
||||
* @param string $base_url Fallback url when the feed provide relative or broken url
|
||||
* @param string $content Feed content
|
||||
* @param string $http_encoding HTTP encoding (headers)
|
||||
* @param string $fallback_url Fallback url when the feed provide relative or broken url
|
||||
*/
|
||||
public function __construct($content, $http_encoding = '', $fallback_url = '')
|
||||
{
|
||||
@ -103,7 +108,7 @@ abstract class Parser
|
||||
$this->content = Filter::stripXmlTag($content);
|
||||
|
||||
// Encode everything in UTF-8
|
||||
Logging::setMessage(get_called_class().': HTTP Encoding "'.$http_encoding.'" ; XML Encoding "'.$xml_encoding.'"');
|
||||
Logger::setMessage(get_called_class().': HTTP Encoding "'.$http_encoding.'" ; XML Encoding "'.$xml_encoding.'"');
|
||||
$this->content = Encoding::convert($this->content, $xml_encoding ?: $http_encoding);
|
||||
|
||||
// Workarounds
|
||||
@ -114,18 +119,18 @@ abstract class Parser
|
||||
* Parse the document
|
||||
*
|
||||
* @access public
|
||||
* @return mixed \PicoFeed\Feed instance or false
|
||||
* @return \PicoFeed\Parser\Feed
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
Logging::setMessage(get_called_class().': begin parsing');
|
||||
Logger::setMessage(get_called_class().': begin parsing');
|
||||
|
||||
$xml = XmlParser::getSimpleXml($this->content);
|
||||
|
||||
if ($xml === false) {
|
||||
Logging::setMessage(get_called_class().': XML parsing error');
|
||||
Logging::setMessage(XmlParser::getErrors());
|
||||
return false;
|
||||
Logger::setMessage(get_called_class().': XML parsing error');
|
||||
Logger::setMessage(XmlParser::getErrors());
|
||||
throw new MalformedXmlException('XML parsing error');
|
||||
}
|
||||
|
||||
$this->namespaces = $xml->getNamespaces(true);
|
||||
@ -135,6 +140,9 @@ abstract class Parser
|
||||
$this->findFeedUrl($xml, $feed);
|
||||
$this->checkFeedUrl($feed);
|
||||
|
||||
$this->findSiteUrl($xml, $feed);
|
||||
$this->checkSiteUrl($feed);
|
||||
|
||||
$this->findFeedTitle($xml, $feed);
|
||||
$this->findFeedDescription($xml, $feed);
|
||||
$this->findFeedLanguage($xml, $feed);
|
||||
@ -151,9 +159,12 @@ abstract class Parser
|
||||
$this->checkItemUrl($feed, $item);
|
||||
|
||||
$this->findItemTitle($entry, $item);
|
||||
$this->findItemId($entry, $item, $feed);
|
||||
$this->findItemDate($entry, $item);
|
||||
$this->findItemContent($entry, $item);
|
||||
|
||||
// Id generation can use the item url/title/content (order is important)
|
||||
$this->findItemId($entry, $item, $feed);
|
||||
|
||||
$this->findItemDate($entry, $item);
|
||||
$this->findItemEnclosure($entry, $item, $feed);
|
||||
$this->findItemLanguage($entry, $item, $feed);
|
||||
|
||||
@ -163,7 +174,7 @@ abstract class Parser
|
||||
$feed->items[] = $item;
|
||||
}
|
||||
|
||||
Logging::setMessage(get_called_class().PHP_EOL.$feed);
|
||||
Logger::setMessage(get_called_class().PHP_EOL.$feed);
|
||||
|
||||
return $feed;
|
||||
}
|
||||
@ -176,10 +187,27 @@ abstract class Parser
|
||||
*/
|
||||
public function checkFeedUrl(Feed $feed)
|
||||
{
|
||||
$url = new Url($feed->getUrl());
|
||||
if ($feed->getFeedUrl() === '') {
|
||||
$feed->feed_url = $this->fallback_url;
|
||||
}
|
||||
else {
|
||||
$feed->feed_url = Url::resolve($feed->getFeedUrl(), $this->fallback_url);
|
||||
}
|
||||
}
|
||||
|
||||
if ($url->isRelativeUrl()) {
|
||||
$feed->url = $this->fallback_url;
|
||||
/**
|
||||
* Check if the site url is correct
|
||||
*
|
||||
* @access public
|
||||
* @param Feed $feed Feed object
|
||||
*/
|
||||
public function checkSiteUrl(Feed $feed)
|
||||
{
|
||||
if ($feed->getSiteUrl() === '') {
|
||||
$feed->site_url = Url::base($feed->getFeedUrl());
|
||||
}
|
||||
else {
|
||||
$feed->site_url = Url::resolve($feed->getSiteUrl(), $this->fallback_url);
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,11 +220,7 @@ abstract class Parser
|
||||
*/
|
||||
public function checkItemUrl(Feed $feed, Item $item)
|
||||
{
|
||||
$url = new Url($item->getUrl());
|
||||
|
||||
if ($url->isRelativeUrl()) {
|
||||
$item->url = Url::resolve($item->getUrl(), $feed->getUrl());
|
||||
}
|
||||
$item->url = Url::resolve($item->getUrl(), $feed->getSiteUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -229,12 +253,12 @@ abstract class Parser
|
||||
public function filterItemContent(Feed $feed, Item $item)
|
||||
{
|
||||
if ($this->isFilteringEnabled()) {
|
||||
$filter = Filter::html($item->getContent(), $feed->getUrl());
|
||||
$filter = Filter::html($item->getContent(), $feed->getSiteUrl());
|
||||
$filter->setConfig($this->config);
|
||||
$item->content = $filter->execute();
|
||||
}
|
||||
else {
|
||||
Logging::setMessage(get_called_class().': Content filtering disabled');
|
||||
Logger::setMessage(get_called_class().': Content filtering disabled');
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,7 +267,7 @@ abstract class Parser
|
||||
*
|
||||
* @access public
|
||||
* @param string $args Pieces of data to hash
|
||||
* @return string Id
|
||||
* @return string
|
||||
*/
|
||||
public function generateId()
|
||||
{
|
||||
@ -331,24 +355,6 @@ abstract class Parser
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hardcoded list of hostname/token to exclude from id generation
|
||||
*
|
||||
* @access public
|
||||
* @param string $url URL
|
||||
* @return boolean
|
||||
*/
|
||||
public function isExcludedFromId($url)
|
||||
{
|
||||
$exclude_list = array('ap.org', 'jacksonville.com');
|
||||
|
||||
foreach ($exclude_list as $token) {
|
||||
if (strpos($url, $token) !== false) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given language is "Right to Left"
|
||||
*
|
||||
@ -386,7 +392,7 @@ abstract class Parser
|
||||
*
|
||||
* @access public
|
||||
* @param string $algo Algorithm name
|
||||
* @return \PicoFeed\Parser
|
||||
* @return \PicoFeed\Parser\Parser
|
||||
*/
|
||||
public function setHashAlgo($algo)
|
||||
{
|
||||
@ -400,7 +406,7 @@ abstract class Parser
|
||||
* @see http://php.net/manual/en/timezones.php
|
||||
* @access public
|
||||
* @param string $timezone Timezone
|
||||
* @return \PicoFeed\Parser
|
||||
* @return \PicoFeed\Parser\Parser
|
||||
*/
|
||||
public function setTimezone($timezone)
|
||||
{
|
||||
@ -412,8 +418,8 @@ abstract class Parser
|
||||
* Set config object
|
||||
*
|
||||
* @access public
|
||||
* @param \PicoFeed\Config $config Config instance
|
||||
* @return \PicoFeed\Parser
|
||||
* @param \PicoFeed\Config\Config $config Config instance
|
||||
* @return \PicoFeed\Parser\Parser
|
||||
*/
|
||||
public function setConfig($config)
|
||||
{
|
||||
@ -425,7 +431,7 @@ abstract class Parser
|
||||
* Enable the content grabber
|
||||
*
|
||||
* @access public
|
||||
* @return \PicoFeed\Parser
|
||||
* @return \PicoFeed\Parser\Parser
|
||||
*/
|
||||
public function disableContentFiltering()
|
||||
{
|
||||
@ -451,7 +457,7 @@ abstract class Parser
|
||||
* Enable the content grabber
|
||||
*
|
||||
* @access public
|
||||
* @return \PicoFeed\Parser
|
||||
* @return \PicoFeed\Parser\Parser
|
||||
*/
|
||||
public function enableContentGrabber()
|
||||
{
|
||||
@ -463,7 +469,7 @@ abstract class Parser
|
||||
*
|
||||
* @access public
|
||||
* @param array $urls URLs
|
||||
* @return \PicoFeed\Parser
|
||||
* @return \PicoFeed\Parser\Parser
|
||||
*/
|
||||
public function setGrabberIgnoreUrls(array $urls)
|
||||
{
|
||||
@ -474,17 +480,26 @@ abstract class Parser
|
||||
* Find the feed url
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public abstract function findFeedUrl(SimpleXMLElement $xml, Feed $feed);
|
||||
|
||||
/**
|
||||
* Find the site url
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public abstract function findSiteUrl(SimpleXMLElement $xml, Feed $feed);
|
||||
|
||||
/**
|
||||
* Find the feed title
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public abstract function findFeedTitle(SimpleXMLElement $xml, Feed $feed);
|
||||
|
||||
@ -492,8 +507,8 @@ abstract class Parser
|
||||
* Find the feed description
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public abstract function findFeedDescription(SimpleXMLElement $xml, Feed $feed);
|
||||
|
||||
@ -501,8 +516,8 @@ abstract class Parser
|
||||
* Find the feed language
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public abstract function findFeedLanguage(SimpleXMLElement $xml, Feed $feed);
|
||||
|
||||
@ -510,8 +525,8 @@ abstract class Parser
|
||||
* Find the feed id
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public abstract function findFeedId(SimpleXMLElement $xml, Feed $feed);
|
||||
|
||||
@ -519,8 +534,8 @@ abstract class Parser
|
||||
* Find the feed date
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public abstract function findFeedDate(SimpleXMLElement $xml, Feed $feed);
|
||||
|
||||
@ -528,8 +543,8 @@ abstract class Parser
|
||||
* Find the feed logo url
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public abstract function findFeedLogo(SimpleXMLElement $xml, Feed $feed);
|
||||
|
||||
@ -546,9 +561,9 @@ abstract class Parser
|
||||
* Find the item author
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param SimpleXMLElement $xml Feed
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
*/
|
||||
public abstract function findItemAuthor(SimpleXMLElement $xml, SimpleXMLElement $entry, Item $item);
|
||||
|
||||
@ -556,8 +571,8 @@ abstract class Parser
|
||||
* Find the item URL
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
*/
|
||||
public abstract function findItemUrl(SimpleXMLElement $entry, Item $item);
|
||||
|
||||
@ -565,8 +580,8 @@ abstract class Parser
|
||||
* Find the item title
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
*/
|
||||
public abstract function findItemTitle(SimpleXMLElement $entry, Item $item);
|
||||
|
||||
@ -574,9 +589,9 @@ abstract class Parser
|
||||
* Genereate the item id
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public abstract function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed);
|
||||
|
||||
@ -584,8 +599,8 @@ abstract class Parser
|
||||
* Find the item date
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
*/
|
||||
public abstract function findItemDate(SimpleXMLElement $entry, Item $item);
|
||||
|
||||
@ -593,8 +608,8 @@ abstract class Parser
|
||||
* Find the item content
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
*/
|
||||
public abstract function findItemContent(SimpleXMLElement $entry, Item $item);
|
||||
|
||||
@ -602,9 +617,9 @@ abstract class Parser
|
||||
* Find the item enclosure
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public abstract function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed);
|
||||
|
||||
@ -612,9 +627,9 @@ abstract class Parser
|
||||
* Find the item language
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public abstract function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed);
|
||||
}
|
16
vendor/fguillot/picofeed/lib/PicoFeed/Parser/ParserException.php
vendored
Normal file
16
vendor/fguillot/picofeed/lib/PicoFeed/Parser/ParserException.php
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed\Parser;
|
||||
|
||||
use PicoFeed\PicoFeedException;
|
||||
|
||||
|
||||
/**
|
||||
* ParserException Exception
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package Parser
|
||||
*/
|
||||
abstract class ParserException extends PicoFeedException
|
||||
{
|
||||
}
|
@ -1,20 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed\Parsers;
|
||||
|
||||
require_once __DIR__.'/Rss20.php';
|
||||
namespace PicoFeed\Parser;
|
||||
|
||||
use SimpleXMLElement;
|
||||
use PicoFeed\Feed;
|
||||
use PicoFeed\Item;
|
||||
use PicoFeed\XmlParser;
|
||||
use PicoFeed\Parsers\Rss20;
|
||||
|
||||
/**
|
||||
* RSS 1.0 parser
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package parser
|
||||
* @package Parser
|
||||
*/
|
||||
class Rss10 extends Rss20
|
||||
{
|
||||
@ -35,7 +29,7 @@ class Rss10 extends Rss20
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findFeedDate(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
@ -47,7 +41,7 @@ class Rss10 extends Rss20
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findFeedLanguage(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
@ -59,19 +53,14 @@ class Rss10 extends Rss20
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed)
|
||||
{
|
||||
if ($this->isExcludedFromId($feed->url)) {
|
||||
$feed_permalink = '';
|
||||
}
|
||||
else {
|
||||
$feed_permalink = $feed->url;
|
||||
}
|
||||
|
||||
$item->id = $this->generateId($item->url, $feed_permalink);
|
||||
$item->id = $this->generateId(
|
||||
$item->getTitle(), $item->getUrl(), $item->getContent()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,8 +68,8 @@ class Rss10 extends Rss20
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed)
|
||||
{
|
@ -1,21 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed\Parsers;
|
||||
namespace PicoFeed\Parser;
|
||||
|
||||
use SimpleXMLElement;
|
||||
use PicoFeed\Parser;
|
||||
use PicoFeed\XmlParser;
|
||||
use PicoFeed\Logging;
|
||||
use PicoFeed\Feed;
|
||||
use PicoFeed\Filter;
|
||||
use PicoFeed\Item;
|
||||
use PicoFeed\Url;
|
||||
use PicoFeed\Filter\Filter;
|
||||
use PicoFeed\Client\Url;
|
||||
|
||||
/**
|
||||
* RSS 2.0 Parser
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package parser
|
||||
* @package Parser
|
||||
*/
|
||||
class Rss20 extends Parser
|
||||
{
|
||||
@ -35,35 +30,32 @@ class Rss20 extends Parser
|
||||
* Find the feed url
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findFeedUrl(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
if ($xml->channel->link && $xml->channel->link->count() > 1) {
|
||||
$feed->feed_url = '';
|
||||
}
|
||||
|
||||
foreach ($xml->channel->link as $xml_link) {
|
||||
|
||||
$link = (string) $xml_link;
|
||||
|
||||
if ($link !== '') {
|
||||
$feed->url = $link;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
$feed->url = (string) $xml->channel->link;
|
||||
}
|
||||
/**
|
||||
* Find the site url
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findSiteUrl(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
$feed->site_url = (string) $xml->channel->link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the feed description
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findFeedDescription(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
@ -74,8 +66,8 @@ class Rss20 extends Parser
|
||||
* Find the feed logo url
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findFeedLogo(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
@ -88,20 +80,20 @@ class Rss20 extends Parser
|
||||
* Find the feed title
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findFeedTitle(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
$feed->title = Filter::stripWhiteSpace((string) $xml->channel->title) ?: $feed->url;
|
||||
$feed->title = Filter::stripWhiteSpace((string) $xml->channel->title) ?: $feed->getSiteUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the feed language
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findFeedLanguage(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
@ -112,20 +104,20 @@ class Rss20 extends Parser
|
||||
* Find the feed id
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findFeedId(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
$feed->id = $feed->url;
|
||||
$feed->id = $feed->getFeedUrl() ?: $feed->getSiteUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the feed date
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findFeedDate(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
@ -137,8 +129,8 @@ class Rss20 extends Parser
|
||||
* Find the item date
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
*/
|
||||
public function findItemDate(SimpleXMLElement $entry, Item $item)
|
||||
{
|
||||
@ -159,8 +151,8 @@ class Rss20 extends Parser
|
||||
* Find the item title
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
*/
|
||||
public function findItemTitle(SimpleXMLElement $entry, Item $item)
|
||||
{
|
||||
@ -175,9 +167,9 @@ class Rss20 extends Parser
|
||||
* Find the item author
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml Feed
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param SimpleXMLElement $xml Feed
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
*/
|
||||
public function findItemAuthor(SimpleXMLElement $xml, SimpleXMLElement $entry, Item $item)
|
||||
{
|
||||
@ -197,8 +189,8 @@ class Rss20 extends Parser
|
||||
* Find the item content
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
*/
|
||||
public function findItemContent(SimpleXMLElement $entry, Item $item)
|
||||
{
|
||||
@ -215,8 +207,8 @@ class Rss20 extends Parser
|
||||
* Find the item URL
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
*/
|
||||
public function findItemUrl(SimpleXMLElement $entry, Item $item)
|
||||
{
|
||||
@ -239,26 +231,21 @@ class Rss20 extends Parser
|
||||
* Genereate the item id
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed)
|
||||
{
|
||||
$item_permalink = $item->url;
|
||||
$id = (string) $entry->guid;
|
||||
|
||||
if ($this->isExcludedFromId($feed->url)) {
|
||||
$feed_permalink = '';
|
||||
if ($id) {
|
||||
$item->id = $this->generateId($id);
|
||||
}
|
||||
else {
|
||||
$feed_permalink = $feed->url;
|
||||
}
|
||||
|
||||
if ($entry->guid->count() > 0 && ((string) $entry->guid['isPermaLink'] === 'false' || ! isset($entry->guid['isPermaLink']))) {
|
||||
$item->id = $this->generateId($item_permalink, $feed_permalink, (string) $entry->guid);
|
||||
}
|
||||
else {
|
||||
$item->id = $this->generateId($item_permalink, $feed_permalink);
|
||||
$item->id = $this->generateId(
|
||||
$item->getTitle(), $item->getUrl(), $item->getContent()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,9 +253,9 @@ class Rss20 extends Parser
|
||||
* Find the item enclosure
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed)
|
||||
{
|
||||
@ -281,7 +268,7 @@ class Rss20 extends Parser
|
||||
}
|
||||
|
||||
$item->enclosure_type = isset($entry->enclosure['type']) ? (string) $entry->enclosure['type'] : '';
|
||||
$item->enclosure_url = Url::resolve($item->enclosure_url, $feed->url);
|
||||
$item->enclosure_url = Url::resolve($item->enclosure_url, $feed->getSiteUrl());
|
||||
}
|
||||
}
|
||||
|
||||
@ -290,8 +277,8 @@ class Rss20 extends Parser
|
||||
*
|
||||
* @access public
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param \PicoFeed\Item $item Item object
|
||||
* @param \PicoFeed\Feed $feed Feed object
|
||||
* @param \PicoFeed\Parser\Item $item Item object
|
||||
* @param \PicoFeed\Parser\Feed $feed Feed object
|
||||
*/
|
||||
public function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed)
|
||||
{
|
13
vendor/fguillot/picofeed/lib/PicoFeed/Parser/Rss91.php
vendored
Normal file
13
vendor/fguillot/picofeed/lib/PicoFeed/Parser/Rss91.php
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed\Parser;
|
||||
|
||||
/**
|
||||
* RSS 0.91 Parser
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package Parser
|
||||
*/
|
||||
class Rss91 extends Rss20
|
||||
{
|
||||
}
|
13
vendor/fguillot/picofeed/lib/PicoFeed/Parser/Rss92.php
vendored
Normal file
13
vendor/fguillot/picofeed/lib/PicoFeed/Parser/Rss92.php
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed\Parser;
|
||||
|
||||
/**
|
||||
* RSS 0.92 Parser
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package Parser
|
||||
*/
|
||||
class Rss92 extends Rss20
|
||||
{
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace PicoFeed;
|
||||
namespace PicoFeed\Parser;
|
||||
|
||||
use Closure;
|
||||
use DomDocument;
|
||||
use DOMXPath;
|
||||
use SimpleXmlElement;
|
||||
@ -12,7 +13,7 @@ use SimpleXmlElement;
|
||||
* Checks for XML eXternal Entity (XXE) and XML Entity Expansion (XEE) attacks on XML documents
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
* @package picofeed
|
||||
* @package Parser
|
||||
*/
|
||||
class XmlParser
|
||||
{
|
||||
@ -43,14 +44,16 @@ class XmlParser
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a DomDocument instance or return false
|
||||
* Scan the input for XXE attacks
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param string $input XML content
|
||||
* @return mixed
|
||||
* @param string $input Unsafe input
|
||||
* @param Closure $callback Callback called to build the dom.
|
||||
* Must be an instance of DomDocument and receives the input as argument
|
||||
*
|
||||
* @return bool|DomDocument False if an XXE attack was discovered,
|
||||
* otherwise the return of the callback
|
||||
*/
|
||||
public static function getDomDocument($input)
|
||||
private static function scanInput($input, Closure $callback)
|
||||
{
|
||||
if (substr(php_sapi_name(), 0, 3) === 'fpm') {
|
||||
|
||||
@ -67,13 +70,7 @@ class XmlParser
|
||||
|
||||
libxml_use_internal_errors(true);
|
||||
|
||||
$dom = new DomDocument;
|
||||
$dom->loadXml($input, LIBXML_NONET);
|
||||
|
||||
// The document is empty, there is probably some parsing errors
|
||||
if ($dom->childNodes->length === 0) {
|
||||
return false;
|
||||
}
|
||||
$dom = $callback($input);
|
||||
|
||||
// Scan for potential XEE attacks using ENTITY
|
||||
foreach ($dom->childNodes as $child) {
|
||||
@ -87,28 +84,56 @@ class XmlParser
|
||||
return $dom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a DomDocument instance or return false
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param string $input XML content
|
||||
* @return \DOMNode
|
||||
*/
|
||||
public static function getDomDocument($input)
|
||||
{
|
||||
$dom = self::scanInput($input, function ($in) {
|
||||
$dom = new DomDocument;
|
||||
$dom->loadXml($in, LIBXML_NONET);
|
||||
return $dom;
|
||||
});
|
||||
|
||||
// The document is empty, there is probably some parsing errors
|
||||
if ($dom && $dom->childNodes->length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $dom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load HTML document by using a DomDocument instance or return false on failure
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param string $input XML content
|
||||
* @return mixed
|
||||
* @return \DOMDocument
|
||||
*/
|
||||
public static function getHtmlDocument($input)
|
||||
{
|
||||
libxml_use_internal_errors(true);
|
||||
|
||||
$dom = new DomDocument;
|
||||
|
||||
if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
|
||||
$dom->loadHTML($input, LIBXML_NONET);
|
||||
$callback = function ($in) {
|
||||
$dom = new DomDocument;
|
||||
$dom->loadHTML($in, LIBXML_NONET);
|
||||
return $dom;
|
||||
};
|
||||
}
|
||||
else {
|
||||
$dom->loadHTML($input);
|
||||
$callback = function ($in) {
|
||||
$dom = new DomDocument;
|
||||
$dom->loadHTML($in);
|
||||
return $dom;
|
||||
};
|
||||
}
|
||||
|
||||
return $dom;
|
||||
return self::scanInput($input, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -201,7 +226,7 @@ class XmlParser
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param SimpleXMLElement $xml XML element
|
||||
* @param \SimpleXMLElement $xml XML element
|
||||
* @param array $namespaces XML namespaces
|
||||
* @param string $property XML tag name
|
||||
* @param string $attribute XML attribute name
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user