aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--actions/ConnectivityAction.php12
-rw-r--r--actions/DetectAction.php12
-rw-r--r--actions/DisplayAction.php4
-rw-r--r--actions/ListAction.php28
-rw-r--r--actions/SetBridgeCacheAction.php12
-rw-r--r--bridges/DemoBridge.php2
-rw-r--r--lib/ApiAuthenticationMiddleware.php12
-rw-r--r--lib/AuthenticationMiddleware.php12
-rw-r--r--lib/BridgeAbstract.php162
-rw-r--r--lib/BridgeCard.php20
-rw-r--r--lib/BridgeFactory.php8
-rw-r--r--lib/Configuration.php12
-rw-r--r--lib/FeedExpander.php12
-rw-r--r--lib/FormatFactory.php12
-rw-r--r--lib/ParameterValidator.php272
-rw-r--r--lib/bootstrap.php14
-rw-r--r--lib/http.php6
-rw-r--r--lib/php8backports.php34
-rw-r--r--tests/BridgeImplementationTest.php (renamed from tests/Bridges/BridgeImplementationTest.php)105
-rw-r--r--tests/CacheImplementationTest.php36
-rw-r--r--tests/Caches/CacheImplementationTest.php48
-rw-r--r--tests/ParameterValidatorTest.php40
23 files changed, 310 insertions, 567 deletions
diff --git a/README.md b/README.md
index a1e5fdc7..7037095e 100644
--- a/README.md
+++ b/README.md
@@ -248,6 +248,8 @@ Modify `report_limit` so that an error must occur 3 times before it is reported.
; Defines how often an error must occur before it is reported to the user
report_limit = 3
+The report count is reset to 0 each day.
+
### How to password-protect the instance
HTTP basic access authentication:
diff --git a/actions/ConnectivityAction.php b/actions/ConnectivityAction.php
index cfffd195..1568333a 100644
--- a/actions/ConnectivityAction.php
+++ b/actions/ConnectivityAction.php
@@ -1,18 +1,6 @@
<?php
/**
- * This file is part of RSS-Bridge, a PHP project capable of generating RSS and
- * Atom feeds for websites that don't have one.
- *
- * For the full license information, please view the UNLICENSE file distributed
- * with this source code.
- *
- * @package Core
- * @license http://unlicense.org/ UNLICENSE
- * @link https://github.com/rss-bridge/rss-bridge
- */
-
-/**
* Checks if the website for a given bridge is reachable.
*
* **Remarks**
diff --git a/actions/DetectAction.php b/actions/DetectAction.php
index 49b7ced7..bbacde38 100644
--- a/actions/DetectAction.php
+++ b/actions/DetectAction.php
@@ -1,17 +1,5 @@
<?php
-/**
- * This file is part of RSS-Bridge, a PHP project capable of generating RSS and
- * Atom feeds for websites that don't have one.
- *
- * For the full license information, please view the UNLICENSE file distributed
- * with this source code.
- *
- * @package Core
- * @license http://unlicense.org/ UNLICENSE
- * @link https://github.com/rss-bridge/rss-bridge
- */
-
class DetectAction implements ActionInterface
{
public function execute(array $request)
diff --git a/actions/DisplayAction.php b/actions/DisplayAction.php
index 8c9dd057..e3b25fef 100644
--- a/actions/DisplayAction.php
+++ b/actions/DisplayAction.php
@@ -101,8 +101,8 @@ class DisplayAction implements ActionInterface
try {
$bridge->loadConfiguration();
// Remove parameters that don't concern bridges
- $bridgeData = array_diff_key($request, array_fill_keys(['action', 'bridge', 'format', '_noproxy', '_cache_timeout', '_error_time'], ''));
- $bridge->setDatas($bridgeData);
+ $input = array_diff_key($request, array_fill_keys(['action', 'bridge', 'format', '_noproxy', '_cache_timeout', '_error_time'], ''));
+ $bridge->setInput($input);
$bridge->collectData();
$items = $bridge->getItems();
if (isset($items[0]) && is_array($items[0])) {
diff --git a/actions/ListAction.php b/actions/ListAction.php
index 9025bf6e..19bb4d37 100644
--- a/actions/ListAction.php
+++ b/actions/ListAction.php
@@ -1,17 +1,5 @@
<?php
-/**
- * This file is part of RSS-Bridge, a PHP project capable of generating RSS and
- * Atom feeds for websites that don't have one.
- *
- * For the full license information, please view the UNLICENSE file distributed
- * with this source code.
- *
- * @package Core
- * @license http://unlicense.org/ UNLICENSE
- * @link https://github.com/rss-bridge/rss-bridge
- */
-
class ListAction implements ActionInterface
{
public function execute(array $request)
@@ -26,14 +14,14 @@ class ListAction implements ActionInterface
$bridge = $bridgeFactory->create($bridgeClassName);
$list->bridges[$bridgeClassName] = [
- 'status' => $bridgeFactory->isEnabled($bridgeClassName) ? 'active' : 'inactive',
- 'uri' => $bridge->getURI(),
- 'donationUri' => $bridge->getDonationURI(),
- 'name' => $bridge->getName(),
- 'icon' => $bridge->getIcon(),
- 'parameters' => $bridge->getParameters(),
- 'maintainer' => $bridge->getMaintainer(),
- 'description' => $bridge->getDescription()
+ 'status' => $bridgeFactory->isEnabled($bridgeClassName) ? 'active' : 'inactive',
+ 'uri' => $bridge->getURI(),
+ 'donationUri' => $bridge->getDonationURI(),
+ 'name' => $bridge->getName(),
+ 'icon' => $bridge->getIcon(),
+ 'parameters' => $bridge->getParameters(),
+ 'maintainer' => $bridge->getMaintainer(),
+ 'description' => $bridge->getDescription()
];
}
$list->total = count($list->bridges);
diff --git a/actions/SetBridgeCacheAction.php b/actions/SetBridgeCacheAction.php
index c9264a27..2e9d7147 100644
--- a/actions/SetBridgeCacheAction.php
+++ b/actions/SetBridgeCacheAction.php
@@ -1,17 +1,5 @@
<?php
-/**
- * This file is part of RSS-Bridge, a PHP project capable of generating RSS and
- * Atom feeds for websites that don't have one.
- *
- * For the full license information, please view the UNLICENSE file distributed
- * with this source code.
- *
- * @package Core
- * @license http://unlicense.org/ UNLICENSE
- * @link https://github.com/rss-bridge/rss-bridge
- */
-
class SetBridgeCacheAction implements ActionInterface
{
private CacheInterface $cache;
diff --git a/bridges/DemoBridge.php b/bridges/DemoBridge.php
index 15ab7377..18582aa6 100644
--- a/bridges/DemoBridge.php
+++ b/bridges/DemoBridge.php
@@ -4,7 +4,7 @@ class DemoBridge extends BridgeAbstract
{
const MAINTAINER = 'teromene';
const NAME = 'DemoBridge';
- const URI = 'http://github.com/rss-bridge/rss-bridge';
+ const URI = 'https://github.com/rss-bridge/rss-bridge';
const DESCRIPTION = 'Bridge used for demos';
const CACHE_TIMEOUT = 15;
diff --git a/lib/ApiAuthenticationMiddleware.php b/lib/ApiAuthenticationMiddleware.php
index 6a59e760..62886314 100644
--- a/lib/ApiAuthenticationMiddleware.php
+++ b/lib/ApiAuthenticationMiddleware.php
@@ -1,17 +1,5 @@
<?php
-/**
- * This file is part of RSS-Bridge, a PHP project capable of generating RSS and
- * Atom feeds for websites that don't have one.
- *
- * For the full license information, please view the UNLICENSE file distributed
- * with this source code.
- *
- * @package Core
- * @license http://unlicense.org/ UNLICENSE
- * @link https://github.com/rss-bridge/rss-bridge
- */
-
final class ApiAuthenticationMiddleware
{
public function __invoke($request): void
diff --git a/lib/AuthenticationMiddleware.php b/lib/AuthenticationMiddleware.php
index 8c2f6b29..a91420f8 100644
--- a/lib/AuthenticationMiddleware.php
+++ b/lib/AuthenticationMiddleware.php
@@ -1,17 +1,5 @@
<?php
-/**
- * This file is part of RSS-Bridge, a PHP project capable of generating RSS and
- * Atom feeds for websites that don't have one.
- *
- * For the full license information, please view the UNLICENSE file distributed
- * with this source code.
- *
- * @package Core
- * @license http://unlicense.org/ UNLICENSE
- * @link https://github.com/rss-bridge/rss-bridge
- */
-
final class AuthenticationMiddleware
{
public function __construct()
diff --git a/lib/BridgeAbstract.php b/lib/BridgeAbstract.php
index e074ce74..a7b811a8 100644
--- a/lib/BridgeAbstract.php
+++ b/lib/BridgeAbstract.php
@@ -90,17 +90,70 @@ abstract class BridgeAbstract
return static::CACHE_TIMEOUT;
}
- /**
- * Sets the input values for a given context.
- *
- * @param array $inputs Associative array of inputs
- * @param string $queriedContext The context name
- * @return void
- */
- protected function setInputs(array $inputs, $queriedContext)
+ public function loadConfiguration()
+ {
+ foreach (static::CONFIGURATION as $optionName => $optionValue) {
+ $section = $this->getShortName();
+ $configurationOption = Configuration::getConfig($section, $optionName);
+
+ if ($configurationOption !== null) {
+ $this->configuration[$optionName] = $configurationOption;
+ continue;
+ }
+
+ if (isset($optionValue['required']) && $optionValue['required'] === true) {
+ throw new \Exception(sprintf('Missing configuration option: %s', $optionName));
+ } elseif (isset($optionValue['defaultValue'])) {
+ $this->configuration[$optionName] = $optionValue['defaultValue'];
+ }
+ }
+ }
+
+ public function setInput(array $input)
+ {
+ $context = $input['context'] ?? null;
+ if ($context) {
+ // Context hinting (optional)
+ $this->queriedContext = $context;
+ unset($input['context']);
+ }
+
+ $parameters = $this->getParameters();
+
+ if (!$parameters) {
+ if ($input) {
+ throw new \Exception('Invalid parameters value(s)');
+ }
+ return;
+ }
+
+ $validator = new ParameterValidator();
+
+ // $input is passed by reference!
+ if (!$validator->validateInput($input, $parameters)) {
+ $invalidParameterKeys = array_column($validator->getInvalidParameters(), 'name');
+ throw new \Exception(sprintf('Invalid parameters value(s): %s', implode(', ', $invalidParameterKeys)));
+ }
+
+ // Guess the context from input data
+ if (empty($this->queriedContext)) {
+ $queriedContext = $validator->getQueriedContext($input, $parameters);
+ $this->queriedContext = $queriedContext;
+ }
+
+ if (is_null($this->queriedContext)) {
+ throw new \Exception('Required parameter(s) missing');
+ } elseif ($this->queriedContext === false) {
+ throw new \Exception('Mixed context parameters');
+ }
+
+ $this->setInputWithContext($input, $this->queriedContext);
+ }
+
+ private function setInputWithContext(array $input, $queriedContext)
{
// Import and assign all inputs to their context
- foreach ($inputs as $name => $value) {
+ foreach ($input as $name => $value) {
foreach (static::PARAMETERS as $context => $set) {
if (array_key_exists($name, static::PARAMETERS[$context])) {
$this->inputs[$context][$name]['value'] = $value;
@@ -128,7 +181,7 @@ abstract class BridgeAbstract
switch ($type) {
case 'checkbox':
- $this->inputs[$context][$name]['value'] = $inputs[$context][$name]['value'] ?? false;
+ $this->inputs[$context][$name]['value'] = $input[$context][$name]['value'] ?? false;
break;
case 'list':
if (!isset($properties['defaultValue'])) {
@@ -153,8 +206,8 @@ abstract class BridgeAbstract
// Copy global parameter values to the guessed context
if (array_key_exists('global', static::PARAMETERS)) {
foreach (static::PARAMETERS['global'] as $name => $properties) {
- if (isset($inputs[$name])) {
- $value = $inputs[$name];
+ if (isset($input[$name])) {
+ $value = $input[$name];
} else {
if ($properties['type'] ?? null === 'checkbox') {
$value = false;
@@ -176,91 +229,6 @@ abstract class BridgeAbstract
}
}
- /**
- * Set inputs for the bridge
- *
- * Returns errors and aborts execution if the provided input parameters are
- * invalid.
- *
- * @param array List of input parameters. Each element in this list must
- * relate to an item in {@see BridgeAbstract::PARAMETERS}
- * @return void
- */
- public function setDatas(array $inputs)
- {
- if (isset($inputs['context'])) { // Context hinting (optional)
- $this->queriedContext = $inputs['context'];
- unset($inputs['context']);
- }
-
- if (empty(static::PARAMETERS)) {
- if (!empty($inputs)) {
- throw new \Exception('Invalid parameters value(s)');
- }
-
- return;
- }
-
- $validator = new ParameterValidator();
-
- if (!$validator->validateData($inputs, static::PARAMETERS)) {
- $parameters = array_map(
- function ($i) {
- return $i['name'];
- }, // Just display parameter names
- $validator->getInvalidParameters()
- );
-
- throw new \Exception(sprintf('Invalid parameters value(s): %s', implode(', ', $parameters)));
- }
-
- // Guess the context from input data
- if (empty($this->queriedContext)) {
- $this->queriedContext = $validator->getQueriedContext($inputs, static::PARAMETERS);
- }
-
- if (is_null($this->queriedContext)) {
- throw new \Exception('Required parameter(s) missing');
- } elseif ($this->queriedContext === false) {
- throw new \Exception('Mixed context parameters');
- }
-
- $this->setInputs($inputs, $this->queriedContext);
- }
-
- /**
- * Loads configuration for the bridge
- *
- * Returns errors and aborts execution if the provided configuration is
- * invalid.
- *
- * @return void
- */
- public function loadConfiguration()
- {
- foreach (static::CONFIGURATION as $optionName => $optionValue) {
- $section = $this->getShortName();
- $configurationOption = Configuration::getConfig($section, $optionName);
-
- if ($configurationOption !== null) {
- $this->configuration[$optionName] = $configurationOption;
- continue;
- }
-
- if (isset($optionValue['required']) && $optionValue['required'] === true) {
- throw new \Exception(sprintf('Missing configuration option: %s', $optionName));
- } elseif (isset($optionValue['defaultValue'])) {
- $this->configuration[$optionName] = $optionValue['defaultValue'];
- }
- }
- }
-
- /**
- * Returns the value for the provided input
- *
- * @param string $input The input name
- * @return mixed|null The input value or null if the input is not defined
- */
protected function getInput($input)
{
return $this->inputs[$this->queriedContext][$input]['value'] ?? null;
diff --git a/lib/BridgeCard.php b/lib/BridgeCard.php
index 99c44fff..b2fda192 100644
--- a/lib/BridgeCard.php
+++ b/lib/BridgeCard.php
@@ -1,25 +1,5 @@
<?php
-/**
- * This file is part of RSS-Bridge, a PHP project capable of generating RSS and
- * Atom feeds for websites that don't have one.
- *
- * For the full license information, please view the UNLICENSE file distributed
- * with this source code.
- *
- * @package Core
- * @license http://unlicense.org/ UNLICENSE
- * @link https://github.com/rss-bridge/rss-bridge
- */
-
-/**
- * A generator class for a single bridge card on the home page of RSS-Bridge.
- *
- * This class generates the HTML content for a single bridge card for the home
- * page of RSS-Bridge.
- *
- * @todo Return error if a caller creates an object of this class.
- */
final class BridgeCard
{
/**
diff --git a/lib/BridgeFactory.php b/lib/BridgeFactory.php
index c3da4bfe..ad433287 100644
--- a/lib/BridgeFactory.php
+++ b/lib/BridgeFactory.php
@@ -4,9 +4,9 @@ final class BridgeFactory
{
private CacheInterface $cache;
private Logger $logger;
- private $bridgeClassNames = [];
- private $enabledBridges = [];
- private $missingEnabledBridges = [];
+ private array $bridgeClassNames = [];
+ private array $enabledBridges = [];
+ private array $missingEnabledBridges = [];
public function __construct()
{
@@ -22,7 +22,7 @@ final class BridgeFactory
$enabledBridges = Configuration::getConfig('system', 'enabled_bridges');
if ($enabledBridges === null) {
- throw new \Exception('No bridges are enabled... wtf?');
+ throw new \Exception('No bridges are enabled...');
}
foreach ($enabledBridges as $enabledBridge) {
if ($enabledBridge === '*') {
diff --git a/lib/Configuration.php b/lib/Configuration.php
index c38d7cc9..d699178f 100644
--- a/lib/Configuration.php
+++ b/lib/Configuration.php
@@ -1,18 +1,6 @@
<?php
/**
- * This file is part of RSS-Bridge, a PHP project capable of generating RSS and
- * Atom feeds for websites that don't have one.
- *
- * For the full license information, please view the UNLICENSE file distributed
- * with this source code.
- *
- * @package Core
- * @license http://unlicense.org/ UNLICENSE
- * @link https://github.com/rss-bridge/rss-bridge
- */
-
-/**
* Configuration module for RSS-Bridge.
*
* This class implements a configuration module for RSS-Bridge.
diff --git a/lib/FeedExpander.php b/lib/FeedExpander.php
index 14c931e6..76f570b6 100644
--- a/lib/FeedExpander.php
+++ b/lib/FeedExpander.php
@@ -1,18 +1,6 @@
<?php
/**
- * This file is part of RSS-Bridge, a PHP project capable of generating RSS and
- * Atom feeds for websites that don't have one.
- *
- * For the full license information, please view the UNLICENSE file distributed
- * with this source code.
- *
- * @package Core
- * @license http://unlicense.org/ UNLICENSE
- * @link https://github.com/rss-bridge/rss-bridge
- */
-
-/**
* An abstract class for bridges that need to transform existing RSS or Atom
* feeds.
*
diff --git a/lib/FormatFactory.php b/lib/FormatFactory.php
index 9cded40f..042dcf31 100644
--- a/lib/FormatFactory.php
+++ b/lib/FormatFactory.php
@@ -1,17 +1,5 @@
<?php
-/**
- * This file is part of RSS-Bridge, a PHP project capable of generating RSS and
- * Atom feeds for websites that don't have one.
- *
- * For the full license information, please view the UNLICENSE file distributed
- * with this source code.
- *
- * @package Core
- * @license http://unlicense.org/ UNLICENSE
- * @link https://github.com/rss-bridge/rss-bridge
- */
-
class FormatFactory
{
private $folder;
diff --git a/lib/ParameterValidator.php b/lib/ParameterValidator.php
index 31934432..e8de754c 100644
--- a/lib/ParameterValidator.php
+++ b/lib/ParameterValidator.php
@@ -1,154 +1,21 @@
<?php
-/**
- * This file is part of RSS-Bridge, a PHP project capable of generating RSS and
- * Atom feeds for websites that don't have one.
- *
- * For the full license information, please view the UNLICENSE file distributed
- * with this source code.
- *
- * @package Core
- * @license http://unlicense.org/ UNLICENSE
- * @link https://github.com/rss-bridge/rss-bridge
- */
-
-/**
- * Validator for bridge parameters
- */
class ParameterValidator
{
- /**
- * Holds the list of invalid parameters
- *
- * @var array
- */
- private $invalid = [];
-
- /**
- * Add item to list of invalid parameters
- *
- * @param string $name The name of the parameter
- * @param string $reason The reason for that parameter being invalid
- * @return void
- */
- private function addInvalidParameter($name, $reason)
- {
- $this->invalid[] = [
- 'name' => $name,
- 'reason' => $reason,
- ];
- }
-
- /**
- * Return list of invalid parameters.
- *
- * Each element is an array of 'name' and 'reason'.
- *
- * @return array List of invalid parameters
- */
- public function getInvalidParameters()
- {
- return $this->invalid;
- }
-
- /**
- * Validate value for a text input
- *
- * @param string $value The value of a text input
- * @param string|null $pattern (optional) A regex pattern
- * @return string|null The filtered value or null if the value is invalid
- */
- private function validateTextValue($value, $pattern = null)
- {
- if (!is_null($pattern)) {
- $filteredValue = filter_var(
- $value,
- FILTER_VALIDATE_REGEXP,
- ['options' => [
- 'regexp' => '/^' . $pattern . '$/'
- ]
- ]
- );
- } else {
- $filteredValue = filter_var($value);
- }
-
- if ($filteredValue === false) {
- return null;
- }
-
- return $filteredValue;
- }
+ private array $invalid = [];
/**
- * Validate value for a number input
+ * Check that inputs are actually present in the bridge parameters.
*
- * @param int $value The value of a number input
- * @return int|null The filtered value or null if the value is invalid
+ * Also check whether input values are allowed.
*/
- private function validateNumberValue($value)
+ public function validateInput(&$input, $parameters): bool
{
- $filteredValue = filter_var($value, FILTER_VALIDATE_INT);
-
- if ($filteredValue === false) {
- return null;
- }
-
- return $filteredValue;
- }
-
- /**
- * Validate value for a checkbox
- *
- * @param bool $value The value of a checkbox
- * @return bool The filtered value
- */
- private function validateCheckboxValue($value)
- {
- return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
- }
-
- /**
- * Validate value for a list
- *
- * @param string $value The value of a list
- * @param array $expectedValues A list of expected values
- * @return string|null The filtered value or null if the value is invalid
- */
- private function validateListValue($value, $expectedValues)
- {
- $filteredValue = filter_var($value);
-
- if ($filteredValue === false) {
- return null;
- }
-
- if (!in_array($filteredValue, $expectedValues)) { // Check sub-values?
- foreach ($expectedValues as $subName => $subValue) {
- if (is_array($subValue) && in_array($filteredValue, $subValue)) {
- return $filteredValue;
- }
- }
- return null;
- }
-
- return $filteredValue;
- }
-
- /**
- * Check if all required parameters are satisfied
- *
- * @param array $data (ref) A list of input values
- * @param array $parameters The bridge parameters
- * @return bool True if all parameters are satisfied
- */
- public function validateData(&$data, $parameters)
- {
- if (!is_array($data)) {
+ if (!is_array($input)) {
return false;
}
- foreach ($data as $name => $value) {
+ foreach ($input as $name => $value) {
// Some RSS readers add a cache-busting parameter (_=<timestamp>) to feed URLs, detect and ignore them.
if ($name === '_') {
continue;
@@ -156,54 +23,60 @@ class ParameterValidator
$registered = false;
foreach ($parameters as $context => $set) {
- if (array_key_exists($name, $set)) {
- $registered = true;
- if (!isset($set[$name]['type'])) {
- $set[$name]['type'] = 'text';
- }
+ if (!array_key_exists($name, $set)) {
+ continue;
+ }
+ $registered = true;
+ if (!isset($set[$name]['type'])) {
+ // Default type is text
+ $set[$name]['type'] = 'text';
+ }
- switch ($set[$name]['type']) {
- case 'number':
- $data[$name] = $this->validateNumberValue($value);
- break;
- case 'checkbox':
- $data[$name] = $this->validateCheckboxValue($value);
- break;
- case 'list':
- $data[$name] = $this->validateListValue($value, $set[$name]['values']);
- break;
- default:
- case 'text':
- if (isset($set[$name]['pattern'])) {
- $data[$name] = $this->validateTextValue($value, $set[$name]['pattern']);
- } else {
- $data[$name] = $this->validateTextValue($value);
- }
- break;
- }
+ switch ($set[$name]['type']) {
+ case 'number':
+ $input[$name] = $this->validateNumberValue($value);
+ break;
+ case 'checkbox':
+ $input[$name] = $this->validateCheckboxValue($value);
+ break;
+ case 'list':
+ $input[$name] = $this->validateListValue($value, $set[$name]['values']);
+ break;
+ default:
+ case 'text':
+ if (isset($set[$name]['pattern'])) {
+ $input[$name] = $this->validateTextValue($value, $set[$name]['pattern']);
+ } else {
+ $input[$name] = $this->validateTextValue($value);
+ }
+ break;
+ }
- if (is_null($data[$name]) && isset($set[$name]['required']) && $set[$name]['required']) {
- $this->addInvalidParameter($name, 'Parameter is invalid!');
- }
+ if (
+ is_null($input[$name])
+ && isset($set[$name]['required'])
+ && $set[$name]['required']
+ ) {
+ $this->invalid[] = ['name' => $name, 'reason' => 'Parameter is invalid!'];
}
}
if (!$registered) {
- $this->addInvalidParameter($name, 'Parameter is not registered!');
+ $this->invalid[] = ['name' => $name, 'reason' => 'Parameter is not registered!'];
}
}
- return empty($this->invalid);
+ return $this->invalid === [];
}
/**
* Get the name of the context matching the provided inputs
*
- * @param array $data Associative array of user data
+ * @param array $input Associative array of user data
* @param array $parameters Array of bridge parameters
* @return string|null Returns the context name or null if no match was found
*/
- public function getQueriedContext($data, $parameters)
+ public function getQueriedContext($input, $parameters)
{
$queriedContexts = [];
@@ -212,7 +85,7 @@ class ParameterValidator
$queriedContexts[$context] = null;
// Ensure all user data exist in the current context
- $notInContext = array_diff_key($data, $set);
+ $notInContext = array_diff_key($input, $set);
if (array_key_exists('global', $parameters)) {
$notInContext = array_diff_key($notInContext, $parameters['global']);
}
@@ -222,7 +95,7 @@ class ParameterValidator
// Check if all parameters of the context are satisfied
foreach ($set as $id => $properties) {
- if (isset($data[$id]) && !empty($data[$id])) {
+ if (isset($input[$id]) && !empty($input[$id])) {
$queriedContexts[$context] = true;
} elseif (
isset($properties['type'])
@@ -248,8 +121,8 @@ class ParameterValidator
switch (array_sum($queriedContexts)) {
case 0:
// Found no match, is there a context without parameters?
- if (isset($data['context'])) {
- return $data['context'];
+ if (isset($input['context'])) {
+ return $input['context'];
}
foreach ($queriedContexts as $context => $queried) {
if (is_null($queried)) {
@@ -264,4 +137,55 @@ class ParameterValidator
return false;
}
}
+
+ public function getInvalidParameters(): array
+ {
+ return $this->invalid;
+ }
+
+ private function validateTextValue($value, $pattern = null)
+ {
+ if (is_null($pattern)) {
+ // No filtering taking place
+ $filteredValue = filter_var($value);
+ } else {
+ $filteredValue = filter_var($value, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^' . $pattern . '$/']]);
+ }
+ if ($filteredValue === false) {
+ return null;
+ }
+ return $filteredValue;
+ }
+
+ private function validateNumberValue($value)
+ {
+ $filteredValue = filter_var($value, FILTER_VALIDATE_INT);
+ if ($filteredValue === false) {
+ return null;
+ }
+ return $filteredValue;
+ }
+
+ private function validateCheckboxValue($value)
+ {
+ return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
+ }
+
+ private function validateListValue($value, $expectedValues)
+ {
+ $filteredValue = filter_var($value);
+ if ($filteredValue === false) {
+ return null;
+ }
+ if (!in_array($filteredValue, $expectedValues)) {
+ // Check sub-values?
+ foreach ($expectedValues as $subName => $subValue) {
+ if (is_array($subValue) && in_array($filteredValue, $subValue)) {
+ return $filteredValue;
+ }
+ }
+ return null;
+ }
+ return $filteredValue;
+ }
}
diff --git a/lib/bootstrap.php b/lib/bootstrap.php
index bc584541..a95de9dd 100644
--- a/lib/bootstrap.php
+++ b/lib/bootstrap.php
@@ -1,18 +1,6 @@
<?php
-/**
- * This file is part of RSS-Bridge, a PHP project capable of generating RSS and
- * Atom feeds for websites that don't have one.
- *
- * For the full license information, please view the UNLICENSE file distributed
- * with this source code.
- *
- * @package Core
- * @license http://unlicense.org/ UNLICENSE
- * @link https://github.com/rss-bridge/rss-bridge
- */
-
-/** Path to the formats library */
+// Path to the formats library
const PATH_LIB_FORMATS = __DIR__ . '/../formats/';
/** Path to the caches library */
diff --git a/lib/http.php b/lib/http.php
index 10ce86c9..3d65b2d1 100644
--- a/lib/http.php
+++ b/lib/http.php
@@ -211,12 +211,12 @@ final class Response
}
}
- public function getBody()
+ public function getBody(): string
{
return $this->body;
}
- public function getCode()
+ public function getCode(): int
{
return $this->code;
}
@@ -226,7 +226,7 @@ final class Response
return self::STATUS_CODES[$this->code] ?? '';
}
- public function getHeaders()
+ public function getHeaders(): array
{
return $this->headers;
}
diff --git a/lib/php8backports.php b/lib/php8backports.php
index 5b103e3d..ccef6016 100644
--- a/lib/php8backports.php
+++ b/lib/php8backports.php
@@ -1,39 +1,5 @@
<?php
-/**
- * This file is part of RSS-Bridge, a PHP project capable of generating RSS and
- * Atom feeds for websites that don't have one.
- *
- * For the full license information, please view the UNLICENSE file distributed
- * with this source code.
- *
- * @package Core
- * @license http://unlicense.org/ UNLICENSE
- * @link https://github.com/rss-bridge/rss-bridge
- */
-
-// based on https://github.com/laravel/framework/blob/8.x/src/Illuminate/Support/Str.php
-//
-// Copyright (c) Taylor Otwell
-//
-// 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.
-
if (!function_exists('str_starts_with')) {
function str_starts_with($haystack, $needle)
{
diff --git a/tests/Bridges/BridgeImplementationTest.php b/tests/BridgeImplementationTest.php
index af9d7db1..d2f74931 100644
--- a/tests/Bridges/BridgeImplementationTest.php
+++ b/tests/BridgeImplementationTest.php
@@ -1,6 +1,6 @@
<?php
-namespace RssBridge\Tests\Bridges;
+namespace RssBridge\Tests;
use BridgeAbstract;
use FeedExpander;
@@ -8,8 +8,8 @@ use PHPUnit\Framework\TestCase;
class BridgeImplementationTest extends TestCase
{
- private $class;
- private $obj;
+ private string $className;
+ private BridgeAbstract $bridge;
/**
* @dataProvider dataBridgesProvider
@@ -17,9 +17,9 @@ class BridgeImplementationTest extends TestCase
public function testClassName($path)
{
$this->setBridge($path);
- $this->assertTrue($this->class === ucfirst($this->class), 'class name must start with uppercase character');
- $this->assertEquals(0, substr_count($this->class, ' '), 'class name must not contain spaces');
- $this->assertStringEndsWith('Bridge', $this->class, 'class name must end with "Bridge"');
+ $this->assertTrue($this->className === ucfirst($this->className), 'class name must start with uppercase character');
+ $this->assertEquals(0, substr_count($this->className, ' '), 'class name must not contain spaces');
+ $this->assertStringEndsWith('Bridge', $this->className, 'class name must end with "Bridge"');
}
/**
@@ -28,7 +28,7 @@ class BridgeImplementationTest extends TestCase
public function testClassType($path)
{
$this->setBridge($path);
- $this->assertInstanceOf(BridgeAbstract::class, $this->obj);
+ $this->assertInstanceOf(BridgeAbstract::class, $this->bridge);
}
/**
@@ -38,18 +38,18 @@ class BridgeImplementationTest extends TestCase
{
$this->setBridge($path);
- $this->assertIsString($this->obj::NAME, 'class::NAME');
- $this->assertNotEmpty($this->obj::NAME, 'class::NAME');
- $this->assertIsString($this->obj::URI, 'class::URI');
- $this->assertNotEmpty($this->obj::URI, 'class::URI');
- $this->assertIsString($this->obj::DESCRIPTION, 'class::DESCRIPTION');
- $this->assertNotEmpty($this->obj::DESCRIPTION, 'class::DESCRIPTION');
- $this->assertIsString($this->obj::MAINTAINER, 'class::MAINTAINER');
- $this->assertNotEmpty($this->obj::MAINTAINER, 'class::MAINTAINER');
-
- $this->assertIsArray($this->obj::PARAMETERS, 'class::PARAMETERS');
- $this->assertIsInt($this->obj::CACHE_TIMEOUT, 'class::CACHE_TIMEOUT');
- $this->assertGreaterThanOrEqual(0, $this->obj::CACHE_TIMEOUT, 'class::CACHE_TIMEOUT');
+ $this->assertIsString($this->bridge::NAME, 'class::NAME');
+ $this->assertNotEmpty($this->bridge::NAME, 'class::NAME');
+ $this->assertIsString($this->bridge::URI, 'class::URI');
+ $this->assertNotEmpty($this->bridge::URI, 'class::URI');
+ $this->assertIsString($this->bridge::DESCRIPTION, 'class::DESCRIPTION');
+ $this->assertNotEmpty($this->bridge::DESCRIPTION, 'class::DESCRIPTION');
+ $this->assertIsString($this->bridge::MAINTAINER, 'class::MAINTAINER');
+ $this->assertNotEmpty($this->bridge::MAINTAINER, 'class::MAINTAINER');
+
+ $this->assertIsArray($this->bridge::PARAMETERS, 'class::PARAMETERS');
+ $this->assertIsInt($this->bridge::CACHE_TIMEOUT, 'class::CACHE_TIMEOUT');
+ $this->assertGreaterThanOrEqual(0, $this->bridge::CACHE_TIMEOUT, 'class::CACHE_TIMEOUT');
}
/**
@@ -60,23 +60,22 @@ class BridgeImplementationTest extends TestCase
$this->setBridge($path);
$multiMinimum = 2;
- if (isset($this->obj::PARAMETERS['global'])) {
+ if (isset($this->bridge::PARAMETERS['global'])) {
++$multiMinimum;
}
- $multiContexts = (count($this->obj::PARAMETERS) >= $multiMinimum);
+ $multiContexts = (count($this->bridge::PARAMETERS) >= $multiMinimum);
$paramsSeen = [];
$allowedTypes = [
'text',
'number',
'list',
- 'checkbox'
+ 'checkbox',
];
- foreach ($this->obj::PARAMETERS as $context => $params) {
+ foreach ($this->bridge::PARAMETERS as $context => $params) {
if ($multiContexts) {
$this->assertIsString($context, 'invalid context name');
-
$this->assertNotEmpty($context, 'The context name cannot be empty');
}
@@ -152,11 +151,10 @@ class BridgeImplementationTest extends TestCase
}
}
- foreach ($this->obj::TEST_DETECT_PARAMETERS as $url => $params) {
- $this->assertEquals($this->obj->detectParameters($url), $params);
+ foreach ($this->bridge::TEST_DETECT_PARAMETERS as $url => $params) {
+ $detectedParameters = $this->bridge->detectParameters($url);
+ $this->assertEquals($detectedParameters, $params);
}
-
- $this->assertTrue(true);
}
/**
@@ -164,19 +162,21 @@ class BridgeImplementationTest extends TestCase
*/
public function testVisibleMethods($path)
{
- $allowedBridgeAbstract = get_class_methods(BridgeAbstract::class);
- sort($allowedBridgeAbstract);
- $allowedFeedExpander = get_class_methods(FeedExpander::class);
- sort($allowedFeedExpander);
+ $bridgeAbstractMethods = get_class_methods(BridgeAbstract::class);
+ sort($bridgeAbstractMethods);
+ $feedExpanderMethods = get_class_methods(FeedExpander::class);
+ sort($feedExpanderMethods);
$this->setBridge($path);
- $methods = get_class_methods($this->obj);
- sort($methods);
- if ($this->obj instanceof FeedExpander) {
- $this->assertEquals($allowedFeedExpander, $methods);
- } else {
- $this->assertEquals($allowedBridgeAbstract, $methods);
+ $publicMethods = get_class_methods($this->bridge);
+ sort($publicMethods);
+ foreach ($publicMethods as $publicMethod) {
+ if ($this->bridge instanceof FeedExpander) {
+ $this->assertContains($publicMethod, $feedExpanderMethods);
+ } else {
+ $this->assertContains($publicMethod, $bridgeAbstractMethods);
+ }
}
}
@@ -187,23 +187,23 @@ class BridgeImplementationTest extends TestCase
{
$this->setBridge($path);
- $value = $this->obj->getDescription();
+ $value = $this->bridge->getDescription();
$this->assertIsString($value, '$class->getDescription()');
$this->assertNotEmpty($value, '$class->getDescription()');
- $value = $this->obj->getMaintainer();
+ $value = $this->bridge->getMaintainer();
$this->assertIsString($value, '$class->getMaintainer()');
$this->assertNotEmpty($value, '$class->getMaintainer()');
- $value = $this->obj->getName();
+ $value = $this->bridge->getName();
$this->assertIsString($value, '$class->getName()');
$this->assertNotEmpty($value, '$class->getName()');
- $value = $this->obj->getURI();
+ $value = $this->bridge->getURI();
$this->assertIsString($value, '$class->getURI()');
$this->assertNotEmpty($value, '$class->getURI()');
- $value = $this->obj->getIcon();
+ $value = $this->bridge->getIcon();
$this->assertIsString($value, '$class->getIcon()');
}
@@ -214,14 +214,14 @@ class BridgeImplementationTest extends TestCase
{
$this->setBridge($path);
- $this->checkUrl($this->obj::URI);
- $this->checkUrl($this->obj->getURI());
+ $this->assertNotFalse(filter_var($this->bridge::URI, FILTER_VALIDATE_URL));
+ $this->assertNotFalse(filter_var($this->bridge->getURI(), FILTER_VALIDATE_URL));
}
public function dataBridgesProvider()
{
$bridges = [];
- foreach (glob(__DIR__ . '/../../bridges/*Bridge.php') as $path) {
+ foreach (glob(__DIR__ . '/../bridges/*Bridge.php') as $path) {
$bridges[basename($path, '.php')] = [$path];
}
return $bridges;
@@ -229,16 +229,11 @@ class BridgeImplementationTest extends TestCase
private function setBridge($path)
{
- $this->class = '\\' . basename($path, '.php');
- $this->assertTrue(class_exists($this->class), 'class ' . $this->class . ' doesn\'t exist');
- $this->obj = new $this->class(
+ $this->className = '\\' . basename($path, '.php');
+ $this->assertTrue(class_exists($this->className), 'class ' . $this->className . ' doesn\'t exist');
+ $this->bridge = new $this->className(
new \NullCache(),
- new \NullLogger()
+ new \NullLogger(),
);
}
-
- private function checkUrl($url)
- {
- $this->assertNotFalse(filter_var($url, FILTER_VALIDATE_URL), 'no valid URL: ' . $url);
- }
}
diff --git a/tests/CacheImplementationTest.php b/tests/CacheImplementationTest.php
new file mode 100644
index 00000000..e6ed352b
--- /dev/null
+++ b/tests/CacheImplementationTest.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace RssBridge\Tests;
+
+use CacheInterface;
+use PHPUnit\Framework\TestCase;
+
+class CacheImplementationTest extends TestCase
+{
+ public function getCacheClassNames()
+ {
+ $caches = [];
+ foreach (glob(PATH_LIB_CACHES . '*.php') as $path) {
+ $caches[] = [basename($path, '.php')];
+ }
+ return $caches;
+ }
+
+ /**
+ * @dataProvider getCacheClassNames
+ */
+ public function testClassName($path)
+ {
+ $this->assertTrue($path === ucfirst($path), 'class name must start with uppercase character');
+ $this->assertEquals(0, substr_count($path, ' '), 'class name must not contain spaces');
+ $this->assertStringEndsWith('Cache', $path, 'class name must end with "Cache"');
+ }
+
+ /**
+ * @dataProvider getCacheClassNames
+ */
+ public function testClassType($path)
+ {
+ $this->assertTrue(is_subclass_of($path, CacheInterface::class), 'class must be subclass of CacheInterface');
+ }
+}
diff --git a/tests/Caches/CacheImplementationTest.php b/tests/Caches/CacheImplementationTest.php
deleted file mode 100644
index a3ad5f79..00000000
--- a/tests/Caches/CacheImplementationTest.php
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-
-namespace RssBridge\Tests\Caches;
-
-use CacheInterface;
-use PHPUnit\Framework\TestCase;
-
-class CacheImplementationTest extends TestCase
-{
- private $class;
-
- /**
- * @dataProvider dataCachesProvider
- */
- public function testClassName($path)
- {
- $this->setCache($path);
- $this->assertTrue($this->class === ucfirst($this->class), 'class name must start with uppercase character');
- $this->assertEquals(0, substr_count($this->class, ' '), 'class name must not contain spaces');
- $this->assertStringEndsWith('Cache', $this->class, 'class name must end with "Cache"');
- }
-
- /**
- * @dataProvider dataCachesProvider
- */
- public function testClassType($path)
- {
- $this->setCache($path);
- $this->assertTrue(is_subclass_of($this->class, CacheInterface::class), 'class must be subclass of CacheInterface');
- }
-
- ////////////////////////////////////////////////////////////////////////////
-
- public function dataCachesProvider()
- {
- $caches = [];
- foreach (glob(PATH_LIB_CACHES . '*.php') as $path) {
- $caches[basename($path, '.php')] = [$path];
- }
- return $caches;
- }
-
- private function setCache($path)
- {
- $this->class = '\\' . basename($path, '.php');
- $this->assertTrue(class_exists($this->class), 'class ' . $this->class . ' doesn\'t exist');
- }
-}
diff --git a/tests/ParameterValidatorTest.php b/tests/ParameterValidatorTest.php
new file mode 100644
index 00000000..1b241c2c
--- /dev/null
+++ b/tests/ParameterValidatorTest.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+namespace RssBridge\Tests;
+
+use PHPUnit\Framework\TestCase;
+
+class ParameterValidatorTest extends TestCase
+{
+ public function test1()
+ {
+ $sut = new \ParameterValidator();
+ $input = ['user' => 'joe'];
+ $parameters = [
+ [
+ 'user' => [
+ 'name' => 'User',
+ 'type' => 'text',
+ ],
+ ]
+ ];
+ $this->assertTrue($sut->validateInput($input, $parameters));
+ }
+
+ public function test2()
+ {
+ $sut = new \ParameterValidator();
+ $input = ['username' => 'joe'];
+ $parameters = [
+ [
+ 'user' => [
+ 'name' => 'User',
+ 'type' => 'text',
+ ],
+ ]
+ ];
+ $this->assertFalse($sut->validateInput($input, $parameters));
+ }
+}