Update vendor

This commit is contained in:
Frederic Guillot 2015-06-21 09:56:36 -04:00
parent be89b3e9fa
commit 154abcd57b
74 changed files with 1192 additions and 361 deletions

View File

@ -3,10 +3,10 @@
"preferred-install": "dist" "preferred-install": "dist"
}, },
"require": { "require": {
"fguillot/simple-validator": "dev-master", "fguillot/simple-validator": "v0.0.3",
"fguillot/json-rpc": "dev-master", "fguillot/json-rpc": "v0.0.3",
"fguillot/picodb": "0.0.2", "fguillot/picodb": "v0.0.3",
"fguillot/picofeed": "dev-master", "fguillot/picofeed": "v0.1.4",
"fguillot/picofarad": "dev-master" "fguillot/picofarad": "dev-master"
}, },
"autoload": { "autoload": {

2
vendor/autoload.php vendored
View File

@ -4,4 +4,4 @@
require_once __DIR__ . '/composer' . '/autoload_real.php'; require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInitf813f763f87d109605f7c55600b459d5::getLoader(); return ComposerAutoloaderInit3973a10bce966c867b4a041e7bc913b0::getLoader();

View File

@ -54,8 +54,6 @@ class ClassLoader
private $useIncludePath = false; private $useIncludePath = false;
private $classMap = array(); private $classMap = array();
private $classMapAuthoritative = false;
public function getPrefixes() public function getPrefixes()
{ {
if (!empty($this->prefixesPsr0)) { if (!empty($this->prefixesPsr0)) {
@ -250,27 +248,6 @@ class ClassLoader
return $this->useIncludePath; return $this->useIncludePath;
} }
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/** /**
* Registers this instance as an autoloader. * Registers this instance as an autoloader.
* *
@ -322,9 +299,6 @@ class ClassLoader
if (isset($this->classMap[$class])) { if (isset($this->classMap[$class])) {
return $this->classMap[$class]; return $this->classMap[$class];
} }
if ($this->classMapAuthoritative) {
return false;
}
$file = $this->findFileWithExtension($class, '.php'); $file = $this->findFileWithExtension($class, '.php');

View File

@ -6,6 +6,7 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(
'JsonRPC\\AuthenticationFailure' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php',
'JsonRPC\\Client' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Client.php', 'JsonRPC\\Client' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Client.php',
'JsonRPC\\InvalidJsonFormat' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php', 'JsonRPC\\InvalidJsonFormat' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php',
'JsonRPC\\InvalidJsonRpcFormat' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php', 'JsonRPC\\InvalidJsonRpcFormat' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php',

View File

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer // autoload_real.php @generated by Composer
class ComposerAutoloaderInitf813f763f87d109605f7c55600b459d5 class ComposerAutoloaderInit3973a10bce966c867b4a041e7bc913b0
{ {
private static $loader; private static $loader;
@ -19,9 +19,9 @@ class ComposerAutoloaderInitf813f763f87d109605f7c55600b459d5
return self::$loader; return self::$loader;
} }
spl_autoload_register(array('ComposerAutoloaderInitf813f763f87d109605f7c55600b459d5', 'loadClassLoader'), true, true); spl_autoload_register(array('ComposerAutoloaderInit3973a10bce966c867b4a041e7bc913b0', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(); self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInitf813f763f87d109605f7c55600b459d5', 'loadClassLoader')); spl_autoload_unregister(array('ComposerAutoloaderInit3973a10bce966c867b4a041e7bc913b0', 'loadClassLoader'));
$map = require __DIR__ . '/autoload_namespaces.php'; $map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) { foreach ($map as $namespace => $path) {
@ -42,14 +42,14 @@ class ComposerAutoloaderInitf813f763f87d109605f7c55600b459d5
$includeFiles = require __DIR__ . '/autoload_files.php'; $includeFiles = require __DIR__ . '/autoload_files.php';
foreach ($includeFiles as $file) { foreach ($includeFiles as $file) {
composerRequiref813f763f87d109605f7c55600b459d5($file); composerRequire3973a10bce966c867b4a041e7bc913b0($file);
} }
return $loader; return $loader;
} }
} }
function composerRequiref813f763f87d109605f7c55600b459d5($file) function composerRequire3973a10bce966c867b4a041e7bc913b0($file)
{ {
require $file; require $file;
} }

View File

@ -1,43 +1,4 @@
[ [
{
"name": "fguillot/picodb",
"version": "v0.0.2",
"version_normalized": "0.0.2.0",
"source": {
"type": "git",
"url": "https://github.com/fguillot/picoDb.git",
"reference": "b3ef5f79e7e5e33729fdbf9c02c8a252a3d76b6b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fguillot/picoDb/zipball/b3ef5f79e7e5e33729fdbf9c02c8a252a3d76b6b",
"reference": "b3ef5f79e7e5e33729fdbf9c02c8a252a3d76b6b",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2015-01-25 16:20:14",
"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/picofarad", "name": "fguillot/picofarad",
"version": "dev-master", "version": "dev-master",
@ -79,8 +40,8 @@
}, },
{ {
"name": "fguillot/simple-validator", "name": "fguillot/simple-validator",
"version": "dev-master", "version": "v0.0.3",
"version_normalized": "9999999-dev", "version_normalized": "0.0.3.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/fguillot/simpleValidator.git", "url": "https://github.com/fguillot/simpleValidator.git",
@ -117,23 +78,23 @@
}, },
{ {
"name": "fguillot/json-rpc", "name": "fguillot/json-rpc",
"version": "dev-master", "version": "v0.0.3",
"version_normalized": "9999999-dev", "version_normalized": "0.0.3.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/fguillot/JsonRPC.git", "url": "https://github.com/fguillot/JsonRPC.git",
"reference": "1a397be7739ddabba87b07f0354655bd91087518" "reference": "ef2f1aa1c07f0e3e8878c53b3a5fc81daedbebb8"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/1a397be7739ddabba87b07f0354655bd91087518", "url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/ef2f1aa1c07f0e3e8878c53b3a5fc81daedbebb8",
"reference": "1a397be7739ddabba87b07f0354655bd91087518", "reference": "ef2f1aa1c07f0e3e8878c53b3a5fc81daedbebb8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.0" "php": ">=5.3.0"
}, },
"time": "2015-04-14 01:50:16", "time": "2015-05-20 15:08:40",
"type": "library", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
@ -155,18 +116,57 @@
"homepage": "https://github.com/fguillot/JsonRPC" "homepage": "https://github.com/fguillot/JsonRPC"
}, },
{ {
"name": "fguillot/picofeed", "name": "fguillot/picodb",
"version": "dev-master", "version": "v0.0.3",
"version_normalized": "9999999-dev", "version_normalized": "0.0.3.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/fguillot/picoFeed.git", "url": "https://github.com/fguillot/picoDb.git",
"reference": "a6087e8264550891c1b8a6da77eca0cab9328709" "reference": "f65d11cb52de34e0fd236a34184ca1a310da244a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/fguillot/picoFeed/zipball/a6087e8264550891c1b8a6da77eca0cab9328709", "url": "https://api.github.com/repos/fguillot/picoDb/zipball/f65d11cb52de34e0fd236a34184ca1a310da244a",
"reference": "a6087e8264550891c1b8a6da77eca0cab9328709", "reference": "f65d11cb52de34e0fd236a34184ca1a310da244a",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2015-05-17 23:57:05",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"PicoDb": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"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": "v0.1.4",
"version_normalized": "0.1.4.0",
"source": {
"type": "git",
"url": "https://github.com/fguillot/picoFeed.git",
"reference": "efa4a3ff139d147ac294070d8b60005abefa19ad"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fguillot/picoFeed/zipball/efa4a3ff139d147ac294070d8b60005abefa19ad",
"reference": "efa4a3ff139d147ac294070d8b60005abefa19ad",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -180,7 +180,7 @@
"suggest": { "suggest": {
"ext-curl": "PicoFeed will use cURL if present" "ext-curl": "PicoFeed will use cURL if present"
}, },
"time": "2015-04-27 22:22:06", "time": "2015-06-21 13:45:50",
"bin": [ "bin": [
"picofeed" "picofeed"
], ],

View File

@ -57,6 +57,8 @@ $server->register('random', function ($start, $end) {
// Return the response to the client // Return the response to the client
echo $server->execute(); echo $server->execute();
?>
``` ```
Class/Method binding: Class/Method binding:
@ -85,9 +87,57 @@ $server->bind('mySecondProcedure', new Api, 'doSomething');
// The procedure and the method are the same // The procedure and the method are the same
$server->bind('doSomething', 'Api'); $server->bind('doSomething', 'Api');
// Attach the class, client will be able to call directly Api::doSomething()
$server->attach(new Api);
echo $server->execute(); echo $server->execute();
?>
``` ```
Before callback:
Before each procedure execution, a custom method can be called.
This method receive the following arguments: `$username, $password, $class, $method`.
```php
<?php
use JsonRPC\Server;
use JsonRPC\AuthenticationFailure;
class Api
{
public function beforeProcedure($username, $password, $class, $method)
{
if ($login_condition_failed) {
throw new AuthenticationFailure('Wrong credentials!');
}
}
public function addition($a, $b)
{
return $a + $b;
}
}
$server = new Server;
$server->authentication(['myuser' => 'mypassword']);
// Register the before callback
$server->before('beforeProcedure');
$server->attach(new Api);
echo $server->execute();
?>
```
You can use this method to implements a custom authentication system or anything else.
If you would like to reject the authentication, you can throw the exception `JsonRPC\AuthenticationFailure`.
### Client ### Client
Example with positional parameters: Example with positional parameters:
@ -108,8 +158,6 @@ Example with named arguments:
```php ```php
<?php <?php
require 'JsonRPC/Client.php';
use JsonRPC\Client; use JsonRPC\Client;
$client = new Client('http://localhost/server.php'); $client = new Client('http://localhost/server.php');
@ -166,7 +214,7 @@ All results are stored at the same position of the call.
- `BadFunctionCallException`: Procedure not found on the server - `BadFunctionCallException`: Procedure not found on the server
- `InvalidArgumentException`: Wrong procedure arguments - `InvalidArgumentException`: Wrong procedure arguments
- `RuntimeException`: Protocol error - `RuntimeException`: Protocol error, authentication failure or connection failure, the message describe the exact error
### Enable client debugging ### Enable client debugging
@ -240,7 +288,7 @@ use JsonRPC\Server;
$server = new Server; $server = new Server;
// List of users to allow // List of users to allow
$server->authentication(['jsonrpc' => 'toto']); $server->authentication(['user1' => 'password1', 'user2' => 'password2']);
// Procedures registration // Procedures registration
@ -258,7 +306,57 @@ On the client, set credentials like that:
use JsonRPC\Client; use JsonRPC\Client;
$client = new Client('http://localhost/server.php'); $client = new Client('http://localhost/server.php');
$client->authentication('jsonrpc', 'toto'); $client->authentication('user1', 'password1');
``` ```
If the authentication failed, the client throw a RuntimeException. If the authentication failed, the client throw a RuntimeException.
Using an alternative authentication header:
```php
use JsonRPC\Server;
$server = new Server;
$server->setAuthenticationHeader('X-Authentication');
$server->authentication(['myusername' => 'mypassword']);
```
The example above will use the HTTP header `X-Authentication` instead of the standard `Authorization: Basic [BASE64_CREDENTIALS]`.
The username/password values need be encoded in base64: `base64_encode('username:password')`.
### Exceptions
If you want to send an error to the client you can throw an exception.
You should configure which exceptions should be relayed to the client first:
```php
<?php
use JsonRPC\Server;
class MyException extends RuntimeException {};
$server = new Server;
// Exceptions that should be relayed to the client, if they occur
$server->attachException('MyException');
// Procedures registration
[...]
// Return the response to the client
echo $server->execute();
```
Then you can throw that exception inside your procedure:
```
throw new MyException("An error occured", 123);
```
To relay all exceptions regardless of type, leave out the exception class name:
```
$server->attachException();
```

View File

@ -23,6 +23,15 @@ class Client
*/ */
private $url; private $url;
/**
* If the only argument passed to a function is an array
* assume it contains named arguments
*
* @access public
* @var boolean
*/
public $named_arguments = true;
/** /**
* HTTP client timeout * HTTP client timeout
* *
@ -78,7 +87,6 @@ class Client
* @var array * @var array
*/ */
private $headers = array( private $headers = array(
'Connection: close',
'Content-Type: application/json', 'Content-Type: application/json',
'Accept: application/json' 'Accept: application/json'
); );
@ -89,6 +97,13 @@ class Client
*/ */
public $ssl_verify_peer = true; public $ssl_verify_peer = true;
/**
* cURL handle
*
* @access private
*/
private $ch;
/** /**
* Constructor * Constructor
* *
@ -102,6 +117,17 @@ class Client
$this->url = $url; $this->url = $url;
$this->timeout = $timeout; $this->timeout = $timeout;
$this->headers = array_merge($this->headers, $headers); $this->headers = array_merge($this->headers, $headers);
$this->ch = curl_init();
}
/**
* Destructor
*
* @access public
*/
public function __destruct()
{
curl_close($this->ch);
} }
/** /**
@ -115,7 +141,7 @@ class Client
public function __call($method, array $params) public function __call($method, array $params)
{ {
// Allow to pass an array and use named arguments // Allow to pass an array and use named arguments
if (count($params) === 1 && is_array($params[0])) { if ($this->named_arguments && count($params) === 1 && is_array($params[0])) {
$params = $params[0]; $params = $params[0];
} }
@ -284,25 +310,29 @@ class Client
*/ */
public function doRequest($payload) public function doRequest($payload)
{ {
$ch = curl_init(); curl_setopt_array($this->ch, array(
CURLOPT_URL => $this->url,
curl_setopt($ch, CURLOPT_URL, $this->url); CURLOPT_HEADER => false,
curl_setopt($ch, CURLOPT_HEADER, false); CURLOPT_RETURNTRANSFER => true,
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); CURLOPT_CONNECTTIMEOUT => $this->timeout,
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->timeout); CURLOPT_USERAGENT => 'JSON-RPC PHP Client',
curl_setopt($ch, CURLOPT_USERAGENT, 'JSON-RPC PHP Client'); CURLOPT_HTTPHEADER => $this->headers,
curl_setopt($ch, CURLOPT_HTTPHEADER, $this->headers); CURLOPT_FOLLOWLOCATION => false,
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); CURLOPT_CUSTOMREQUEST => 'POST',
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); CURLOPT_SSL_VERIFYPEER => $this->ssl_verify_peer,
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->ssl_verify_peer); CURLOPT_POSTFIELDS => json_encode($payload)
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); ));
if ($this->username && $this->password) { if ($this->username && $this->password) {
curl_setopt($ch, CURLOPT_USERPWD, $this->username.':'.$this->password); curl_setopt($this->ch, CURLOPT_USERPWD, $this->username.':'.$this->password);
} }
$http_body = curl_exec($ch); $http_body = curl_exec($this->ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); $http_code = curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
if (curl_errno($this->ch)) {
throw new RuntimeException(curl_error($this->ch));
}
if ($http_code === 401 || $http_code === 403) { if ($http_code === 401 || $http_code === 403) {
throw new RuntimeException('Access denied'); throw new RuntimeException('Access denied');
@ -315,8 +345,6 @@ class Client
error_log('==> Response: '.PHP_EOL.json_encode($response, JSON_PRETTY_PRINT)); error_log('==> Response: '.PHP_EOL.json_encode($response, JSON_PRETTY_PRINT));
} }
curl_close($ch);
return is_array($response) ? $response : array(); return is_array($response) ? $response : array();
} }
} }

View File

@ -12,6 +12,7 @@ use ReflectionMethod;
class InvalidJsonRpcFormat extends Exception {}; class InvalidJsonRpcFormat extends Exception {};
class InvalidJsonFormat extends Exception {}; class InvalidJsonFormat extends Exception {};
class AuthenticationFailure extends Exception {};
/** /**
* JsonRPC server class * JsonRPC server class
@ -33,7 +34,6 @@ class Server
/** /**
* List of procedures * List of procedures
* *
* @static
* @access private * @access private
* @var array * @var array
*/ */
@ -42,7 +42,6 @@ class Server
/** /**
* List of classes * List of classes
* *
* @static
* @access private * @access private
* @var array * @var array
*/ */
@ -51,12 +50,43 @@ class Server
/** /**
* List of instances * List of instances
* *
* @static
* @access private * @access private
* @var array * @var array
*/ */
private $instances = array(); private $instances = array();
/**
* List of exception classes that should be relayed to client
*
* @access private
* @var array
*/
private $exceptions = array();
/**
* Method name to execute before the procedure
*
* @access private
* @var string
*/
private $before = '';
/**
* Username
*
* @access private
* @var string
*/
private $username = '';
/**
* Password
*
* @access private
* @var string
*/
private $password = '';
/** /**
* Constructor * Constructor
* *
@ -70,6 +100,78 @@ class Server
$this->payload = $payload; $this->payload = $payload;
$this->callbacks = $callbacks; $this->callbacks = $callbacks;
$this->classes = $classes; $this->classes = $classes;
}
/**
* Define alternative authentication header
*
* @access public
* @param string $header Header name
* @return Server
*/
public function setAuthenticationHeader($header)
{
if (! empty($header)) {
$header = 'HTTP_'.str_replace('-', '_', strtoupper($header));
if (isset($_SERVER[$header])) {
list($this->username, $this->password) = explode(':', @base64_decode($_SERVER[$header]));
}
}
return $this;
}
/**
* Get username
*
* @access public
* @return string
*/
public function getUsername()
{
return $this->username ?: @$_SERVER['PHP_AUTH_USER'];
}
/**
* Get password
*
* @access public
* @return string
*/
public function getPassword()
{
return $this->password ?: @$_SERVER['PHP_AUTH_PW'];
}
/**
* Send authentication failure response
*
* @access public
*/
public function sendAuthenticationFailureResponse()
{
header('WWW-Authenticate: Basic realm="JsonRPC"');
header('Content-Type: application/json');
header('HTTP/1.0 401 Unauthorized');
echo '{"error": "Authentication failed"}';
exit;
}
/**
* Send forbidden response
*
* @access public
*/
public function sendForbiddenResponse()
{
header('Content-Type: application/json');
header('HTTP/1.0 403 Forbidden');
echo '{"error": "Access Forbidden"}';
exit;
} }
/** /**
@ -80,14 +182,10 @@ class Server
* @access public * @access public
* @param array $hosts List of hosts * @param array $hosts List of hosts
*/ */
public function allowHosts(array $hosts) { public function allowHosts(array $hosts)
{
if (! in_array($_SERVER['REMOTE_ADDR'], $hosts)) { if (! in_array($_SERVER['REMOTE_ADDR'], $hosts)) {
$this->sendForbiddenResponse();
header('Content-Type: application/json');
header('HTTP/1.0 403 Forbidden');
echo '{"error": "Access Forbidden"}';
exit;
} }
} }
@ -98,19 +196,15 @@ class Server
* *
* @access public * @access public
* @param array $users Map of username/password * @param array $users Map of username/password
* @return Server
*/ */
public function authentication(array $users) public function authentication(array $users)
{ {
if (! isset($_SERVER['PHP_AUTH_USER']) || if (! isset($users[$this->getUsername()]) || $users[$this->getUsername()] !== $this->getPassword()) {
! isset($users[$_SERVER['PHP_AUTH_USER']]) || $this->sendAuthenticationFailureResponse();
$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;
} }
return $this;
} }
/** /**
@ -119,10 +213,12 @@ class Server
* @access public * @access public
* @param string $procedure Procedure name * @param string $procedure Procedure name
* @param closure $callback Callback * @param closure $callback Callback
* @return Server
*/ */
public function register($name, Closure $callback) public function register($name, Closure $callback)
{ {
$this->callbacks[$name] = $callback; $this->callbacks[$name] = $callback;
return $this;
} }
/** /**
@ -132,6 +228,7 @@ class Server
* @param string $procedure Procedure name * @param string $procedure Procedure name
* @param mixed $class Class name or instance * @param mixed $class Class name or instance
* @param string $method Procedure name * @param string $method Procedure name
* @return Server
*/ */
public function bind($procedure, $class, $method = '') public function bind($procedure, $class, $method = '')
{ {
@ -140,6 +237,7 @@ class Server
} }
$this->classes[$procedure] = array($class, $method); $this->classes[$procedure] = array($class, $method);
return $this;
} }
/** /**
@ -147,10 +245,39 @@ class Server
* *
* @access public * @access public
* @param mixed $instance Instance name * @param mixed $instance Instance name
* @return Server
*/ */
public function attach($instance) public function attach($instance)
{ {
$this->instances[] = $instance; $this->instances[] = $instance;
return $this;
}
/**
* Bind an exception
* If this exception occurs it is relayed to the client as JSON-RPC error
*
* @access public
* @param mixed $exception Exception class. Defaults to all.
* @return Server
*/
public function attachException($exception = 'Exception')
{
$this->exceptions[] = $exception;
return $this;
}
/**
* Attach a method that will be called before the procedure
*
* @access public
* @param string $before
* @return Server
*/
public function before($before)
{
$this->before = $before;
return $this;
} }
/** /**
@ -327,6 +454,25 @@ class Server
$this->payload $this->payload
); );
} }
catch (AuthenticationFailure $e) {
$this->sendAuthenticationFailureResponse();
}
catch (Exception $e) {
foreach ($this->exceptions as $class) {
if ($e instanceof $class) {
return $this->getResponse(array(
'error' => array(
'code' => $e->getCode(),
'message' => $e->getMessage()
)),
$this->payload
);
}
}
throw $e;
}
} }
/** /**
@ -342,7 +488,7 @@ class Server
if (isset($this->callbacks[$procedure])) { if (isset($this->callbacks[$procedure])) {
return $this->executeCallback($this->callbacks[$procedure], $params); return $this->executeCallback($this->callbacks[$procedure], $params);
} }
else if (isset($this->classes[$procedure])) { else if (isset($this->classes[$procedure]) && method_exists($this->classes[$procedure][0], $this->classes[$procedure][1])) {
return $this->executeMethod($this->classes[$procedure][0], $this->classes[$procedure][1], $params); return $this->executeMethod($this->classes[$procedure][0], $this->classes[$procedure][1], $params);
} }
@ -388,6 +534,13 @@ class Server
*/ */
public function executeMethod($class, $method, $params) public function executeMethod($class, $method, $params)
{ {
$instance = is_string($class) ? new $class : $class;
// Execute before action
if (! empty($this->before) && method_exists($instance, $this->before)) {
$instance->{$this->before}($this->getUsername(), $this->getPassword(), get_class($class), $method);
}
$reflection = new ReflectionMethod($class, $method); $reflection = new ReflectionMethod($class, $method);
$arguments = $this->getArguments( $arguments = $this->getArguments(
@ -397,10 +550,7 @@ class Server
$reflection->getNumberOfParameters() $reflection->getNumberOfParameters()
); );
return $reflection->invokeArgs( return $reflection->invokeArgs($instance, $arguments);
is_string($class) ? new $class : $class,
$arguments
);
} }
/** /**

View File

@ -40,7 +40,7 @@ class ServerProcedureTest extends PHPUnit_Framework_TestCase
} }
/** /**
* @expectedException ReflectionException * @expectedException BadFunctionCallException
*/ */
public function testClassNotFound() public function testClassNotFound()
{ {
@ -50,7 +50,7 @@ class ServerProcedureTest extends PHPUnit_Framework_TestCase
} }
/** /**
* @expectedException ReflectionException * @expectedException BadFunctionCallException
*/ */
public function testMethodNotFound() public function testMethodNotFound()
{ {

21
vendor/fguillot/picodb/LICENSE vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Frederic Guillot
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
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 OR COPYRIGHT HOLDERS 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.

View File

@ -12,7 +12,7 @@ Features
- Requires only PDO - Requires only PDO
- Use prepared statements - Use prepared statements
- Handle schema versions (migrations) - Handle schema versions (migrations)
- License: [WTFPL](http://www.wtfpl.net) - License: MIT
Requirements Requirements
------------ ------------

View File

@ -3,7 +3,7 @@
"description": "Minimalist database query builder", "description": "Minimalist database query builder",
"homepage": "https://github.com/fguillot/picoDb", "homepage": "https://github.com/fguillot/picoDb",
"type": "library", "type": "library",
"license": "WTFPL", "license": "MIT",
"authors": [ "authors": [
{ {
"name": "Frédéric Guillot", "name": "Frédéric Guillot",

View File

@ -248,7 +248,7 @@ class Database
$this->setLogMessage($e->getMessage()); $this->setLogMessage($e->getMessage());
throw new RuntimeException('SQL error'); throw new RuntimeException('SQL error'.($this->log_queries ? ': '.$e->getMessage() : ''));
} }
} }

View File

@ -27,6 +27,10 @@ class Mysql extends PDO
$dsn = 'mysql:host='.$settings['hostname'].';dbname='.$settings['database'].';charset='.$settings['charset']; $dsn = 'mysql:host='.$settings['hostname'].';dbname='.$settings['database'].';charset='.$settings['charset'];
if (! empty($settings['port'])) {
$dsn .= ';port='.$settings['port'];
}
$options = array( $options = array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET sql_mode = STRICT_ALL_TABLES', PDO::MYSQL_ATTR_INIT_COMMAND => 'SET sql_mode = STRICT_ALL_TABLES',
); );

View File

@ -26,6 +26,10 @@ class Postgres extends PDO
$dsn = 'pgsql:host='.$settings['hostname'].';dbname='.$settings['database']; $dsn = 'pgsql:host='.$settings['hostname'].';dbname='.$settings['database'];
if (! empty($settings['port'])) {
$dsn .= ';port='.$settings['port'];
}
parent::__construct($dsn, $settings['username'], $settings['password']); parent::__construct($dsn, $settings['username'], $settings['password']);
if (isset($settings['schema_table'])) { if (isset($settings['schema_table'])) {
@ -66,7 +70,7 @@ class Postgres extends PDO
public function escapeIdentifier($value) public function escapeIdentifier($value)
{ {
return $value; return '"'.$value.'"';
} }
public function operatorLikeCaseSensitive() public function operatorLikeCaseSensitive()

View File

@ -13,16 +13,22 @@ class Table
protected $table_name = ''; protected $table_name = '';
protected $values = array(); protected $values = array();
private $columns = array();
private $sql_limit = ''; private $sql_limit = '';
private $sql_offset = ''; private $sql_offset = '';
private $sql_order = ''; private $sql_order = '';
private $joins = array(); private $joins = array();
private $condition = '';
private $conditions = array(); private $conditions = array();
private $or_conditions = array(); private $or_conditions = array();
private $is_or_condition = false; private $is_or_condition = false;
private $columns = array();
private $distinct = false; private $distinct = false;
private $group_by = array(); private $group_by = array();
private $filter_callback = null; private $filter_callback = null;
/** /**
@ -82,7 +88,7 @@ class Table
'UPDATE %s SET %s %s', 'UPDATE %s SET %s %s',
$this->db->escapeIdentifier($this->table_name), $this->db->escapeIdentifier($this->table_name),
implode(', ', $columns), implode(', ', $columns),
$this->conditions() $this->buildCondition()
); );
return $this->db->execute($sql, $values) !== false; return $this->db->execute($sql, $values) !== false;
@ -124,7 +130,7 @@ class Table
$sql = sprintf( $sql = sprintf(
'DELETE FROM %s %s', 'DELETE FROM %s %s',
$this->db->escapeIdentifier($this->table_name), $this->db->escapeIdentifier($this->table_name),
$this->conditions() $this->buildCondition()
); );
$result = $this->db->execute($sql, $this->values); $result = $this->db->execute($sql, $this->values);
@ -155,7 +161,7 @@ class Table
$rq = $this->db->execute($this->buildSelectQuery(), $this->values); $rq = $this->db->execute($this->buildSelectQuery(), $this->values);
$results = $rq->fetchAll(PDO::FETCH_ASSOC); $results = $rq->fetchAll(PDO::FETCH_ASSOC);
if (is_callable($this->filter_callback)) { if (is_callable($this->filter_callback) && ! empty($results)) {
return call_user_func($this->filter_callback, $results); return call_user_func($this->filter_callback, $results);
} }
@ -227,7 +233,7 @@ class Table
empty($this->columns) ? '*' : implode(', ', $this->columns), empty($this->columns) ? '*' : implode(', ', $this->columns),
$this->db->escapeIdentifier($this->table_name), $this->db->escapeIdentifier($this->table_name),
implode(' ', $this->joins), implode(' ', $this->joins),
$this->conditions(), $this->buildCondition(),
empty($this->group_by) ? '' : 'GROUP BY '.implode(', ', $this->group_by), empty($this->group_by) ? '' : 'GROUP BY '.implode(', ', $this->group_by),
$this->sql_order, $this->sql_order,
$this->sql_limit, $this->sql_limit,
@ -244,16 +250,37 @@ class Table
public function count() public function count()
{ {
$sql = sprintf( $sql = sprintf(
'SELECT COUNT(*) FROM %s '.implode(' ', $this->joins).$this->conditions().$this->sql_order.$this->sql_limit.$this->sql_offset, 'SELECT COUNT(*) FROM %s '.implode(' ', $this->joins).$this->buildCondition().$this->sql_order.$this->sql_limit.$this->sql_offset,
$this->db->escapeIdentifier($this->table_name) $this->db->escapeIdentifier($this->table_name)
); );
$rq = $this->db->execute($sql, $this->values); $rq = $this->db->execute($sql, $this->values);
$result = $rq->fetchColumn(); $result = $rq->fetchColumn();
return $result ? (int) $result : 0; return $result ? (int) $result : 0;
} }
/**
* Sum
*
* @access public
* @param string $column
* @return float
*/
public function sum($column)
{
$sql = sprintf(
'SELECT SUM(%s) FROM %s '.implode(' ', $this->joins).$this->buildCondition().$this->sql_order.$this->sql_limit.$this->sql_offset,
$this->db->escapeIdentifier($column),
$this->db->escapeIdentifier($this->table_name)
);
$rq = $this->db->execute($sql, $this->values);
$result = $rq->fetchColumn();
return $result ? (float) $result : 0;
}
/** /**
* Left join * Left join
* *
@ -262,14 +289,15 @@ class Table
* @param string $foreign_column Foreign key on the join table * @param string $foreign_column Foreign key on the join table
* @param string $local_column Local column * @param string $local_column Local column
* @param string $local_table Local table * @param string $local_table Local table
* @param string $alias Join table alias
* @return \PicoDb\Table * @return \PicoDb\Table
*/ */
public function join($table, $foreign_column, $local_column, $local_table = '') public function join($table, $foreign_column, $local_column, $local_table = '', $alias = '')
{ {
$this->joins[] = sprintf( $this->joins[] = sprintf(
'LEFT JOIN %s ON %s=%s', 'LEFT JOIN %s ON %s=%s',
$this->db->escapeIdentifier($table), $this->db->escapeIdentifier($table),
$this->db->escapeIdentifier($table).'.'.$this->db->escapeIdentifier($foreign_column), $this->db->escapeIdentifier($alias ?: $table).'.'.$this->db->escapeIdentifier($foreign_column),
$this->db->escapeIdentifier($local_table ?: $this->table_name).'.'.$this->db->escapeIdentifier($local_column) $this->db->escapeIdentifier($local_table ?: $this->table_name).'.'.$this->db->escapeIdentifier($local_column)
); );
@ -277,13 +305,53 @@ class Table
} }
/** /**
* Build conditions * Left join
* *
* @access public * @access public
* @param string $table1
* @param string $alias1
* @param string $column1
* @param string $table2
* @param string $column2
* @return \PicoDb\Table
*/
public function left($table1, $alias1, $column1, $table2, $column2)
{
$this->joins[] = sprintf(
'LEFT JOIN %s AS %s ON %s=%s',
$this->db->escapeIdentifier($table1),
$this->db->escapeIdentifier($alias1),
$this->db->escapeIdentifier($alias1).'.'.$this->db->escapeIdentifier($column1),
$this->db->escapeIdentifier($table2).'.'.$this->db->escapeIdentifier($column2)
);
return $this;
}
/**
* Add custom condition
*
* @access private
* @return Table
*/
private function condition($condition)
{
$this->condition = $condition;
return $this;
}
/**
* Build condition
*
* @access private
* @return string * @return string
*/ */
public function conditions() private function buildCondition()
{ {
if (! empty($this->condition)) {
return 'WHERE '.$this->condition;
}
return empty($this->conditions) ? '' : ' WHERE '.implode(' AND ', $this->conditions); return empty($this->conditions) ? '' : ' WHERE '.implode(' AND ', $this->conditions);
} }
@ -292,6 +360,7 @@ class Table
* *
* @access public * @access public
* @param string $sql * @param string $sql
* @return Table
*/ */
public function addCondition($sql) public function addCondition($sql)
{ {
@ -301,6 +370,8 @@ class Table
else { else {
$this->conditions[] = $sql; $this->conditions[] = $sql;
} }
return $this;
} }
/** /**

View File

@ -21,7 +21,7 @@ PicoFeed will try first to find the favicon from the meta tags and fallback to t
When the HTML page is parsed, relative links and protocol relative links are converted to absolute url. When the HTML page is parsed, relative links and protocol relative links are converted to absolute url.
Download a know favicon Download a known favicon
----------------------- -----------------------
It's possible to download a known favicon using the second optional parameter of Favicon::find(). The link to the favicon can be a relative or protocol relative url as well, but it has to be relative to the specified website. It's possible to download a known favicon using the second optional parameter of Favicon::find(). The link to the favicon can be a relative or protocol relative url as well, but it has to be relative to the specified website.

View File

@ -5,7 +5,7 @@ Versions
-------- --------
- Development version: master - Development version: master
- Stable version: v0.1.3 - Stable version: v0.1.4
Installation with Composer Installation with Composer
-------------------------- --------------------------
@ -15,7 +15,7 @@ Configure your `composer.json`:
```json ```json
{ {
"require": { "require": {
"fguillot/picofeed": "0.1.3" "fguillot/picofeed": "0.1.4"
} }
} }
``` ```
@ -23,7 +23,7 @@ Configure your `composer.json`:
Or simply: Or simply:
```bash ```bash
composer require fguillot/picofeed:0.1.3 composer require fguillot/picofeed:0.1.4
``` ```
And download the code: And download the code:

View File

@ -62,7 +62,7 @@ class Stream extends Client
{ {
foreach($headers as $header) { foreach($headers as $header) {
if (stripos($header, 'Location') === 0) { if (stripos($header, 'Location') === 0) {
list($name, $value) = explode(': ', $header); list(, $value) = explode(': ', $header);
$this->url = Url::resolve($value, $this->url); $this->url = Url::resolve($value, $this->url);
} }

View File

@ -15,7 +15,21 @@ class Encoding
return $input; return $input;
} }
// convert input to utf-8; ignore malformed characters // suppress all notices since it isn't possible to silence only the
return iconv($encoding, 'UTF-8//IGNORE', $input); // notice "Wrong charset, conversion from $in_encoding to $out_encoding is not allowed"
set_error_handler(function() {}, E_NOTICE);
// convert input to utf-8 and strip invalid characters
$value = iconv($encoding, 'UTF-8//IGNORE', $input);
// stop silencing of notices
restore_error_handler();
// return input if something went wrong, maybe it's usable anyway
if ($value === false) {
return $input;
}
return $value;
} }
} }

View File

@ -107,7 +107,7 @@ class Filter
} }
/** /**
* Dirty quickfixes before XML parsing * Fixes before XML parsing
* *
* @static * @static
* @access public * @access public
@ -116,17 +116,37 @@ class Filter
*/ */
public static function normalizeData($data) public static function normalizeData($data)
{ {
$invalid_chars = array( $entities = array(
"\x10", '/(&#)(\d+);/m', // decimal encoded
"\xc3\x20", '/(&#x)([a-f0-9]+);/mi', // hex encoded
"&#x1F;",
"\xe2\x80\x9c\x08",
); );
foreach ($invalid_chars as $needle) { // strip invalid XML 1.0 characters which are encoded as entities
$data = str_replace($needle, '', $data); $data = preg_replace_callback($entities, function($matches) {
} $code_point = $matches[2];
return $data; // convert hex entity to decimal
if (strtolower($matches[1]) === '&#x') {
$code_point = hexdec($code_point);
}
$code_point = (int) $code_point;
// replace invalid characters
if ($code_point < 9
|| ($code_point > 10 && $code_point < 13)
|| ($code_point > 13 && $code_point < 32)
|| ($code_point > 55295 && $code_point < 57344)
|| ($code_point > 65533 && $code_point < 65536)
|| $code_point > 1114111
) {
return '';
};
return $matches[0];
}, $data);
// strip every utf-8 character than isn't in the range of valid XML 1.0 characters
return (string) preg_replace('/[^\x{0009}\x{000A}\x{000D}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]/u', '', $data);
} }
} }

View File

@ -88,7 +88,7 @@ class DateParser
* @access public * @access public
* @param string $format Date format * @param string $format Date format
* @param string $value Original date value * @param string $value Original date value
* @return DateTime * @return DateTime|boolean
*/ */
public function getValidDate($format, $value) public function getValidDate($format, $value)
{ {

View File

@ -117,9 +117,6 @@ abstract class Parser
// Encode everything in UTF-8 // Encode everything in UTF-8
Logger::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); $this->content = Encoding::convert($this->content, $xml_encoding ?: $http_encoding);
// Workarounds
$this->content = Filter::normalizeData($this->content);
} }
/** /**
@ -135,9 +132,15 @@ abstract class Parser
$xml = XmlParser::getSimpleXml($this->content); $xml = XmlParser::getSimpleXml($this->content);
if ($xml === false) { if ($xml === false) {
Logger::setMessage(get_called_class().': XML parsing error'); Logger::setMessage(get_called_class().': Applying XML workarounds');
Logger::setMessage(XmlParser::getErrors()); $this->content = Filter::normalizeData($this->content);
throw new MalformedXmlException('XML parsing error'); $xml = XmlParser::getSimpleXml($this->content);
if ($xml === false) {
Logger::setMessage(get_called_class().': XML parsing error');
Logger::setMessage(XmlParser::getErrors());
throw new MalformedXmlException('XML parsing error');
}
} }
$this->namespaces = $xml->getNamespaces(true); $this->namespaces = $xml->getNamespaces(true);

View File

@ -23,7 +23,13 @@ class Rss20 extends Parser
*/ */
public function getItemsTree(SimpleXMLElement $xml) public function getItemsTree(SimpleXMLElement $xml)
{ {
return $xml->channel->item; $items = array();
if (isset($xml->channel->item)) {
$items = $xml->channel->item;
}
return $items;
} }
/** /**

View File

@ -0,0 +1,8 @@
<?php
return array(
'filter' => array(
'%.*%' => array(
'%alt="(.+)" title="(.+)" */>%' => "/><br/>$1<br/>$2"
)
)
);

View File

@ -0,0 +1,8 @@
<?php
return array(
'filter' => array(
'%.*%' => array(
'%title="(.+)" */>%' => "/><br/>$1"
)
)
);

View File

@ -0,0 +1,13 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'body' => array(
'//img[@id="comic_image"]',
'//div[@class="comment-wrapper"][position()=1]'
),
'strip' => array(),
'test_url' => 'http://www.anythingcomic.com/comics/2108929/stress-free/',
)
),
);

View File

@ -0,0 +1,13 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'test_url' => 'http://buttersafe.com/2015/04/21/the-incredible-flexible-man/',
'body' => array(
'//div[@id="comic"]',
'//div[@class="post-comic"]',
),
'strip' => array()
)
)
);

View File

@ -0,0 +1,8 @@
<?php
return array(
'filter' => array(
'%.*%' => array(
'%href="http://www.channelate.com/(\\d+)/(\\d+)/(\\d+)/[^"]*"%' => 'href="http://www.channelate.com/extra-panel/$1$2$3/"'
)
)
);

View File

@ -0,0 +1,8 @@
<?php
return array(
'filter' => array(
'%.*%' => array(
'%title="(.+)" */>%' => "/><br/>$1"
)
)
);

View File

@ -0,0 +1,15 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'body' => array('//img[@id="comicimage"]'),
'strip' => array(),
'test_url' => 'http://drawingboardcomic.com/index.php?comic=208',
)
),
'filter' => array(
'%.*%' => array(
'%title="(.+)" */>%' => "/><br/>$1"
)
)
);

View File

@ -0,0 +1,8 @@
<?php
return array(
'filter' => array(
'%.*%' => array(
'%-\\d+x\\d+%' => "",
)
)
);

View File

@ -35,7 +35,12 @@ return array(
'body' => array('//*[@class="body"]'), 'body' => array('//*[@class="body"]'),
'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/fraughtwithperil/12166-The-Escapist-Presents-Escapist-Comics-Critical-Miss-B-lyeh-Fhlop?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=articles', 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/fraughtwithperil/12166-The-Escapist-Presents-Escapist-Comics-Critical-Miss-B-lyeh-Fhlop?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=articles',
'strip' => array() 'strip' => array()
) ),
'%/articles/view/video-games/columns/.*%' => array(
'body' => array('//*[@id="article_content"]'),
'test_url' => 'http://www.escapistmagazine.com/articles/view/video-games/columns/experienced-points/13971-What-50-Shades-and-Batman-Have-in-Common.2',
'strip' => array()
),
) )
); );

View File

@ -0,0 +1,15 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'body' => array('//a[@class="comic"]/img'),
'strip' => array(),
'test_url' => 'http://www.exocomics.com/379',
)
),
'filter' => array(
'%.*%' => array(
'%title="(.+)" */>%' => "/><br/>$1"
)
)
);

View File

@ -5,6 +5,7 @@ return array(
'test_url' => 'http://explosm.net/comics/3803/', 'test_url' => 'http://explosm.net/comics/3803/',
'body' => array( 'body' => array(
'//div[@id="comic-container"]', '//div[@id="comic-container"]',
'//div[@id="comic-container"]//img/@src'
), ),
'strip' => array( 'strip' => array(
), ),

View File

@ -0,0 +1,8 @@
<?php
return array(
'filter' => array(
'%.*%' => array(
'%-\\d+x\\d+%' => "",
)
)
);

View File

@ -0,0 +1,12 @@
<?php
return array(
'grabber' => array(
'%/comics/oots.*%' => array(
'test_url' => 'http://www.giantitp.com/comics/oots0989.html',
'body' => array(
'//td[@align="center"]/img'
),
'strip' => array()
)
)
);

View File

@ -0,0 +1,12 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'test_url' => 'http://www.gocomics.com/pearlsbeforeswine/2015/05/30',
'body' => array(
'//div[1]/p[1]/a[1]/img',
),
'strip' => array(),
)
)
);

View File

@ -0,0 +1,18 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'body' => array(
'//div[@id="comic"]',
'//div[@class="entry"]'
),
'strip' => array('//div[@class="ssba"]'),
'test_url' => 'http://www.happletea.com/comic/mans-best-friend/',
)
),
'filter' => array(
'%.*%' => array(
'%title="(.+)" */>%' => "/><br/>$1"
)
)
);

View File

@ -0,0 +1,8 @@
<?php
return array(
'filter' => array(
'%.*%' => array(
'%title="(.+)" */>%' => "/><br/>$1"
)
)
);

View File

@ -0,0 +1,8 @@
<?php
return array(
'filter' => array(
'%.*%' => array(
'%(<img.+(\\d{4}-\\d{2}-\\d{2}).+/>)%' => '$1<img src="http://invisiblebread.com/eps/$2-extrapanel.png"/>'
)
)
);

View File

@ -0,0 +1,10 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'body' => array('//span[@class="ccbnTxt"]'),
'strip' => array(),
'test_url' => 'http://ir.amd.com/phoenix.zhtml?c=74093&p=RssLanding&cat=news&id=2055819',
)
),
);

View File

@ -0,0 +1,8 @@
<?php
return array(
'filter' => array(
'%.*%' => array(
'%-\\d+x\\d+%' => "",
)
)
);

View File

@ -1,10 +1,8 @@
<?php <?php
return array( return array(
'grabber' => array( 'filter' => array(
'%/comic.*%' => array( '%.*%' => array(
'test_url' => 'http://www.loadingartist.com/comic/lifted-spirits/', '%-\\d+x\\d+%' => "",
'body' => array('//div[@class="comic"]'),
'strip' => array(),
) )
) )
); );

View File

@ -0,0 +1,15 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'body' => array('//div[@id="comic"]//img'),
'strip' => array(),
'test_url' => 'http://www.lukesurl.com/archives/comic/665-3-of-clubs',
)
),
'filter' => array(
'%.*%' => array(
'%title="(.+)" */>%' => "/><br/>$1"
)
)
);

View File

@ -0,0 +1,12 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'test_url' => 'http://www.marriedtothesea.com/index.php?date=052915',
'body' => array(
'//div[@align]/a/img',
),
'strip' => array(),
)
)
);

View File

@ -0,0 +1,13 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'body' => array(
'//img[@id="cc-comic"]',
'//div[@class="cc-newsbody"]'
),
'strip' => array(),
'test_url' => 'http://www.marycagle.com/letsspeakenglish/74-grim-reality/',
)
),
);

View File

@ -0,0 +1,8 @@
<?php
return array(
'filter' => array(
'%.*%' => array(
'%alt="(.+)" */>%' => "/><br/>$1"
)
)
);

View File

@ -0,0 +1,8 @@
<?php
return array(
'filter' => array(
'%.*%' => array(
'%title="(.+)" */>%' => "/><br/>$1"
)
)
);

View File

@ -0,0 +1,19 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'body' => array(
'//img[@id="strip"]',
'//a/div[@id="nx"]/..'
),
'strip' => array(),
'test_url' => 'http://oglaf.com/slodging/'
)
),
'filter' => array(
'%.*%' => array(
'%alt="(.+)" title="(.+)" */>%' => "/><br/>$1<br/>$2<br/>",
'%</a>%' => 'Next page</a>',
)
)
);

View File

@ -0,0 +1,8 @@
<?php
return array(
'filter' => array(
'%.*%' => array(
'%title="(.+)" */>%' => "/><br/>$1"
)
)
);

View File

@ -0,0 +1,9 @@
<?php
return array(
'filter' => array(
'%.*%' => array(
// the extra space is required to strip the title cleanly
'%title="(.+) " */>%' => "/><br/>$1"
)
)
);

View File

@ -0,0 +1,12 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'test_url' => 'http://www.pixelbeat.org/programming/sigpipe_handling.html#1425573246',
'body' => array(
'//div[@class="contentText"]',
),
'strip' => array(),
)
)
);

View File

@ -0,0 +1,8 @@
<?php
return array(
'filter' => array(
'%.*%' => array(
'%(<img.+/s/[^"]+/)(.+)%' => '$1$2$1bonus.png"/>'
)
)
);

View File

@ -4,7 +4,8 @@ return array(
'%.*%' => array( '%.*%' => array(
'test_url' => 'http://satwcomic.com/day-at-the-beach', 'test_url' => 'http://satwcomic.com/day-at-the-beach',
'body' => array( 'body' => array(
'//div[@class="container"]/center/a/img' '//div[@class="container"]/center/a/img',
'//span[@itemprop="articleBody"]'
), ),
'strip' => array(), 'strip' => array(),
) )

View File

@ -0,0 +1,18 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'body' => array(
'//div[@class="comicpane"]/a/img',
'//div[@class="entry"]'
),
'strip' => array(),
'test_url' => 'http://sentfromthemoon.com/archives/1417'
)
),
'filter' => array(
'%.*%' => array(
'%title="(.+)" */>%' => "/><br/>$1"
)
)
);

View File

@ -0,0 +1,8 @@
<?php
return array(
'filter' => array(
'%.*%' => array(
'%(<img.+)(\.png"/>)%' => '$1$2$1after$2'
)
)
);

View File

@ -0,0 +1,13 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'test_url' => 'http://stupidfox.net/134-sleepy-time',
'body' => array(
'//div[@class="comicmid"]/center/a/img',
'//div[@class="stand_high"]'
),
'strip' => array(),
)
)
);

View File

@ -0,0 +1,15 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'test_url' => 'http://www.subtraction.com/2015/06/06/time-lapse-video-of-one-world-trade-center/',
'body' => array('//article/div[@class="entry-content"]'),
'strip' => array()
)
),
'filter' => array(
'%.*%' => array(
'%\+<h3.*/ul>%' => '',
)
)
);

View File

@ -0,0 +1,8 @@
<?php
return array(
'filter' => array(
'%.*%' => array(
'%-\\d+x\\d+%' => "",
)
)
);

View File

@ -0,0 +1,18 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'body' => array(
'//div[@class="comicpane"]/a/img',
'//div[@class="entry"]'
),
'strip' => array(),
'test_url' => 'http://sentfromthemoon.com/archives/1417'
)
),
'filter' => array(
'%.*%' => array(
'%title="(.+)" */>%' => "/><br/>$1"
)
)
);

View File

@ -0,0 +1,8 @@
<?php
return array(
'filter' => array(
'%.*%' => array(
'%title="(.+)" */>%' => "/><br/>$1"
)
)
);

View File

@ -0,0 +1,10 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'body' => array('//div[@class="entry-content"]'),
'strip' => array(),
'test_url' => 'http://voz.vn/2015/06/06/muon-trai-nghiem-bphone-hay-den-fpt-shop/',
)
),
);

View File

@ -1,17 +0,0 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'test_url' => array(
'http://www.lemonde.fr/societe/article/2013/08/30/boris-boillon-ancien-ambassadeur-de-sarkozy-arrete-avec-350-000-euros-en-liquide_3469109_3224.html',
'http://www.lemonde.fr/afrique/article/2015/04/06/plonge-dans-la-crise-l-angola-revele-son-vrai-visage_4610364_3212.html',
),
'body' => array(
'//div[@id="articleBody"]',
'//div[@itemprop="articleBody"]',
),
'strip' => array(
),
)
)
);

View File

@ -1,13 +0,0 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'test_url' => 'http://www.pcinpact.com/news/85954-air-france-ne-vous-demande-plus-deteindre-vos-appareils-electroniques.htm?utm_source=PCi_RSS_Feed&utm_medium=news&utm_campaign=pcinpact',
'body' => array(
'//div[contains(@id, "actu_content")]',
),
'strip' => array(
),
)
)
);

View File

@ -0,0 +1,41 @@
<?php
return array(
'grabber' => array(
'%^/zeit-magazin.*%' => array(
'test_url' => 'http://www.zeit.de/zeit-magazin/2015/15/pegida-kathrin-oertel-lutz-bachmann',
'body' => array(
'//article[@class="article"]'
),
'strip' => array(
'//header/div/h1',
'//header/div/div[@class="article__head__subtitle"]',
'//header/div/div[@class="article__column__author"]',
'//header/div/div[@class="article__column__author"]',
'//header/div/span[@class="article__head__meta-wrap"]',
'//form',
'//style',
'//div[contains(@class, "ad-tile")]',
'//div[@class="iqd-mobile-adplace"]',
'//div[@id="iq-artikelanker"]',
'//div[@id="js-social-services"]',
'//section[@id="js-comments"]',
'//aside'
)
),
'%.*%' => array(
'test_url' => 'http://www.zeit.de/politik/ausland/2015-04/thessaloniki-krise-griechenland-yannis-boutaris/',
'body' => array(
'//div[@class="article-body"]'
),
'strip' => array(
'//*[@class="articleheader"]',
'//*[@class="excerpt"]',
'//div[contains(@class, "ad")]',
'//div[@itemprop="video"]',
'//*[@class="articlemeta"]',
'//*[@class="articlemeta-clear"]',
'//*[@class="zol_inarticletools"]'
)
)
)
);

View File

@ -1,13 +1,8 @@
<?php <?php
return array( return array(
'grabber' => array( 'filter' => array(
'%.*%' => array( '%.*%' => array(
'test_url' => 'http://xkcd.com/1472/', '%alt="(.+)" */>%' => "/><br/>$1"
'body' => array(
'//div[@id="comic"]',
),
'strip' => array(
),
) )
) )
); );

View File

@ -4,6 +4,7 @@ namespace PicoFeed\Filter;
use PHPUnit_Framework_TestCase; use PHPUnit_Framework_TestCase;
use PicoFeed\Client\Url; use PicoFeed\Client\Url;
use PicoFeed\Config\Config;
class AttributeFilterTest extends PHPUnit_Framework_TestCase class AttributeFilterTest extends PHPUnit_Framework_TestCase
@ -190,4 +191,134 @@ class AttributeFilterTest extends PHPUnit_Framework_TestCase
$this->assertEquals('title="&quot;a&quot;"', $filter->toHtml(array('title' => '"a"'))); $this->assertEquals('title="&quot;a&quot;"', $filter->toHtml(array('title' => '"a"')));
$this->assertEquals('title="ç" alt="b"', $filter->toHtml(array('title' => 'ç', 'alt' => 'b'))); $this->assertEquals('title="ç" alt="b"', $filter->toHtml(array('title' => 'ç', 'alt' => 'b')));
} }
public function testNoImageProxySet()
{
$f = Filter::html('<p>Image <img src="/image.png" alt="My Image"/></p>', 'http://foo');
$this->assertEquals(
'<p>Image <img src="http://foo/image.png" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyWithHTTPLink()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$f = Filter::html('<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="http://myproxy/?url='.rawurlencode('http://localhost/image.png').'" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyWithHTTPSLink()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$f = Filter::html('<p>Image <img src="https://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="http://myproxy/?url='.rawurlencode('https://localhost/image.png').'" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyLimitedToUnknownProtocol()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$config->setFilterImageProxyProtocol('tripleX');
$f = Filter::html('<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyLimitedToHTTPwithHTTPLink()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$config->setFilterImageProxyProtocol('http');
$f = Filter::html('<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="http://myproxy/?url='.rawurlencode('http://localhost/image.png').'" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyLimitedToHTTPwithHTTPSLink()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$config->setFilterImageProxyProtocol('http');
$f = Filter::html('<p>Image <img src="https://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="https://localhost/image.png" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyLimitedToHTTPSwithHTTPLink()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$config->setFilterImageProxyProtocol('https');
$f = Filter::html('<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyLimitedToHTTPSwithHTTPSLink()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$config->setFilterImageProxyProtocol('https');
$f = Filter::html('<p>Image <img src="https://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="http://myproxy/?url='.rawurlencode('https://localhost/image.png').'" alt="My Image"/></p>',
$f->execute()
);
}
public function testsetFilterImageProxyCallback()
{
$config = new Config;
$config->setFilterImageProxyCallback(function ($image_url) {
$key = hash_hmac('sha1', $image_url, 'secret');
return 'https://mypublicproxy/'.$key.'/'.rawurlencode($image_url);
});
$f = Filter::html('<p>Image <img src="/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="https://mypublicproxy/4924964043f3119b3cf2b07b1922d491bcc20092/'.rawurlencode('http://foo/image.png').'" alt="My Image"/></p>',
$f->execute()
);
}
} }

View File

@ -91,133 +91,35 @@ class FilterTest extends PHPUnit_Framework_TestCase
$this->assertEquals('<p>Testboo</p>', $f->execute()); $this->assertEquals('<p>Testboo</p>', $f->execute());
} }
public function testNoImageProxySet() public function testNormalizeData()
{ {
$f = Filter::html('<p>Image <img src="/image.png" alt="My Image"/></p>', 'http://foo'); // invalid data link escape control character
$this->assertEquals('<xml>random text</xml>', Filter::normalizeData("<xml>random\x10 text</xml>"));
$this->assertEquals('<xml>random text</xml>', Filter::normalizeData("<xml>random&#x10; text</xml>"));
$this->assertEquals('<xml>random text</xml>', Filter::normalizeData("<xml>random&#16; text</xml>"));
$this->assertEquals( // invalid unit seperator control character (lower and upper case)
'<p>Image <img src="http://foo/image.png" alt="My Image"/></p>', $this->assertEquals('<xml>random text</xml>', Filter::normalizeData("<xml>random\x1f text</xml>"));
$f->execute() $this->assertEquals('<xml>random text</xml>', Filter::normalizeData("<xml>random\x1F text</xml>"));
); $this->assertEquals('<xml>random text</xml>', Filter::normalizeData("<xml>random&#x1f; text</xml>"));
} $this->assertEquals('<xml>random text</xml>', Filter::normalizeData("<xml>random&#x1F; text</xml>"));
$this->assertEquals('<xml>random text</xml>', Filter::normalizeData("<xml>random&#31; text</xml>"));
public function testImageProxyWithHTTPLink() /*
{ * Do not test invalid multibyte characters. The output depends on php
$config = new Config; * version and character.
$config->setFilterImageProxyUrl('http://myproxy/?url=%s'); *
* php 5.3: always null
* php >5.3: sometime null, sometimes the stripped string
*/
$f = Filter::html('<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>', 'http://foo'); // invalid backspace control character + valid multibyte character
$f->setConfig($config); $this->assertEquals('<xml>“random“ text</xml>', Filter::normalizeData("<xml>\xe2\x80\x9crandom\xe2\x80\x9c\x08 text</xml>"));
$this->assertEquals('<xml>&#x201C;random&#x201C; text</xml>', Filter::normalizeData("<xml>&#x201C;random&#x201C;&#x08; text</xml>"));
$this->assertEquals('<xml>&#8220;random&#8220; text</xml>', Filter::normalizeData("<xml>&#8220;random&#8220;&#08; text</xml>"));
$this->assertEquals( // do not convert valid entities to utf-8 character
'<p>Image <img src="http://myproxy/?url='.rawurlencode('http://localhost/image.png').'" alt="My Image"/></p>', $this->assertEquals('<xml attribute="&#34;value&#34;">random text</xml>', Filter::normalizeData('<xml attribute="&#34;value&#34;">random text</xml>'));
$f->execute() $this->assertEquals('<xml attribute="&#x22;value&#x22;">random text</xml>', Filter::normalizeData('<xml attribute="&#x22;value&#x22;">random text</xml>'));
);
}
public function testImageProxyWithHTTPSLink()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$f = Filter::html('<p>Image <img src="https://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="http://myproxy/?url='.rawurlencode('https://localhost/image.png').'" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyLimitedToUnknownProtocol()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$config->setFilterImageProxyProtocol('tripleX');
$f = Filter::html('<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyLimitedToHTTPwithHTTPLink()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$config->setFilterImageProxyProtocol('http');
$f = Filter::html('<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="http://myproxy/?url='.rawurlencode('http://localhost/image.png').'" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyLimitedToHTTPwithHTTPSLink()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$config->setFilterImageProxyProtocol('http');
$f = Filter::html('<p>Image <img src="https://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="https://localhost/image.png" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyLimitedToHTTPSwithHTTPLink()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$config->setFilterImageProxyProtocol('https');
$f = Filter::html('<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyLimitedToHTTPSwithHTTPSLink()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$config->setFilterImageProxyProtocol('https');
$f = Filter::html('<p>Image <img src="https://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="http://myproxy/?url='.rawurlencode('https://localhost/image.png').'" alt="My Image"/></p>',
$f->execute()
);
}
public function testsetFilterImageProxyCallback()
{
$config = new Config;
$config->setFilterImageProxyCallback(function ($image_url) {
$key = hash_hmac('sha1', $image_url, 'secret');
return 'https://mypublicproxy/'.$key.'/'.rawurlencode($image_url);
});
$f = Filter::html('<p>Image <img src="/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="https://mypublicproxy/4924964043f3119b3cf2b07b1922d491bcc20092/'.rawurlencode('http://foo/image.png').'" alt="My Image"/></p>',
$f->execute()
);
} }
} }

View File

@ -33,4 +33,15 @@ class ParserTest extends PHPUnit_Framework_TestCase
$this->assertEquals('Blandine Grosjean', XmlParser::getNamespaceValue($xml->channel->item[0], $namespaces, 'creator')); $this->assertEquals('Blandine Grosjean', XmlParser::getNamespaceValue($xml->channel->item[0], $namespaces, 'creator'));
$this->assertEquals('Pierre-Carl Langlais', XmlParser::getNamespaceValue($xml->channel->item[1], $namespaces, 'creator')); $this->assertEquals('Pierre-Carl Langlais', XmlParser::getNamespaceValue($xml->channel->item[1], $namespaces, 'creator'));
} }
public function testFeedsWithInvalidCharacters()
{
$parser = new Rss20(file_get_contents('tests/fixtures/lincoln_loop.xml'));
$feed = $parser->execute();
$this->assertNotEmpty($feed->items);
$parser = new Rss20(file_get_contents('tests/fixtures/next_inpact_full.xml'));
$feed = $parser->execute();
$this->assertNotEmpty($feed->items);
}
} }

View File

@ -257,14 +257,6 @@ class Rss20ParserTest extends PHPUnit_Framework_TestCase
$this->assertNotEmpty($feed->items); $this->assertNotEmpty($feed->items);
$this->assertEquals('http://geekstammtisch.de/#GST001', $feed->items[1]->getUrl()); $this->assertEquals('http://geekstammtisch.de/#GST001', $feed->items[1]->getUrl());
$parser = new Rss20(file_get_contents('tests/fixtures/lincoln_loop.xml'));
$feed = $parser->execute();
$this->assertNotEmpty($feed->items);
$parser = new Rss20(file_get_contents('tests/fixtures/next_inpact_full.xml'));
$feed = $parser->execute();
$this->assertNotEmpty($feed->items);
$parser = new Rss20(file_get_contents('tests/fixtures/jeux-linux.fr.xml')); $parser = new Rss20(file_get_contents('tests/fixtures/jeux-linux.fr.xml'));
$feed = $parser->execute(); $feed = $parser->execute();
$this->assertNotEmpty($feed->items); $this->assertNotEmpty($feed->items);

View File

@ -84,7 +84,7 @@ class FaviconTest extends PHPUnit_Framework_TestCase
{ {
$favicon = new Favicon; $favicon = new Favicon;
$this->assertTrue($favicon->exists('https://en.wikipedia.org/favicon.ico')); $this->assertTrue($favicon->exists('https://miniflux.net/favicon.ico'));
$this->assertFalse($favicon->exists('http://minicoders.com/favicon.ico')); $this->assertFalse($favicon->exists('http://minicoders.com/favicon.ico'));
$this->assertFalse($favicon->exists('http://blabla')); $this->assertFalse($favicon->exists('http://blabla'));
$this->assertFalse($favicon->exists('')); $this->assertFalse($favicon->exists(''));

View File

@ -231,7 +231,7 @@ class ReaderTest extends PHPUnit_Framework_TestCase
$reader = new Reader; $reader = new Reader;
$client = $reader->discover('http://planete-jquery.fr'); $client = $reader->discover('http://planete-jquery.fr');
$this->assertInstanceOf('PicoFeed\Parser\Rss20', $reader->getParser($client->getUrl(), $client->getContent(), $client->getEncoding())); $this->assertInstanceOf('PicoFeed\Parser\Rss10', $reader->getParser($client->getUrl(), $client->getContent(), $client->getEncoding()));
$reader = new Reader; $reader = new Reader;
$client = $reader->discover('http://cabinporn.com/'); $client = $reader->discover('http://cabinporn.com/');