'Limit', 'type' => 'number', 'title' => 'Maximum number of items to return', ]; /** * Holds the list of items collected by the bridge * * Items must be collected by {@see BridgeInterface::collectData()} * * Use {@see BridgeAbstract::getItems()} to access items. * * @var array */ protected array $items = []; /** * Holds the list of input parameters used by the bridge * * Do not access this parameter directly! * Use {@see BridgeAbstract::setInputs()} and {@see BridgeAbstract::getInput()} instead! * * @var array */ protected array $inputs = []; /** * Holds the name of the queried context * * @var string */ protected $queriedContext = ''; /** {@inheritdoc} */ public function getItems() { return $this->items; } /** * 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) { // Import and assign all inputs to their context foreach ($inputs as $name => $value) { foreach (static::PARAMETERS as $context => $set) { if (array_key_exists($name, static::PARAMETERS[$context])) { $this->inputs[$context][$name]['value'] = $value; } } } // Apply default values to missing data $contexts = [$queriedContext]; if (array_key_exists('global', static::PARAMETERS)) { $contexts[] = 'global'; } foreach ($contexts as $context) { foreach (static::PARAMETERS[$context] as $name => $properties) { if (isset($this->inputs[$context][$name]['value'])) { continue; } $type = $properties['type'] ?? 'text'; switch ($type) { case 'checkbox': if (!isset($properties['defaultValue'])) { $this->inputs[$context][$name]['value'] = false; } else { $this->inputs[$context][$name]['value'] = $properties['defaultValue']; } break; case 'list': if (!isset($properties['defaultValue'])) { $firstItem = reset($properties['values']); if (is_array($firstItem)) { $firstItem = reset($firstItem); } $this->inputs[$context][$name]['value'] = $firstItem; } else { $this->inputs[$context][$name]['value'] = $properties['defaultValue']; } break; default: if (isset($properties['defaultValue'])) { $this->inputs[$context][$name]['value'] = $properties['defaultValue']; } break; } } } // Copy global parameter values to the guessed context if (array_key_exists('global', static::PARAMETERS)) { foreach (static::PARAMETERS['global'] as $name => $properties) { if (isset($inputs[$name])) { $value = $inputs[$name]; } elseif (isset($properties['defaultValue'])) { $value = $properties['defaultValue']; } else { continue; } $this->inputs[$queriedContext][$name]['value'] = $value; } } // Only keep guessed context parameters values if (isset($this->inputs[$queriedContext])) { $this->inputs = [$queriedContext => $this->inputs[$queriedContext]]; } else { $this->inputs = []; } } /** * 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) { if (!isset($this->inputs[$this->queriedContext][$input]['value'])) { return null; } return $this->inputs[$this->queriedContext][$input]['value']; } /** * Get the key name of a given input * Can process multilevel arrays with two levels, the max level a list can have * * @param string $input The input name * @return string|null The accompaning key to a given input or null if the input is not defined */ public function getKey($input) { if (!isset($this->inputs[$this->queriedContext][$input]['value'])) { return null; } if (array_key_exists('global', static::PARAMETERS)) { if (array_key_exists($input, static::PARAMETERS['global'])) { $context = 'global'; } } if (!isset($context)) { $context = $this->queriedContext; } $needle = $this->inputs[$this->queriedContext][$input]['value']; foreach (static::PARAMETERS[$context][$input]['values'] as $first_level_key => $first_level_value) { if ($needle === (string)$first_level_value) { return $first_level_key; } elseif (is_array($first_level_value)) { foreach ($first_level_value as $second_level_key => $second_level_value) { if ($needle === (string)$second_level_value) { return $second_level_key; } } } } } /** * Get bridge configuration value */ public function getOption($name) { return $this->configuration[$name] ?? null; } /** {@inheritdoc} */ public function getDescription() { return static::DESCRIPTION; } /** {@inheritdoc} */ public function getMaintainer() { return static::MAINTAINER; } /** {@inheritdoc} */ public function getName() { return static::NAME; } /** {@inheritdoc} */ public function getIcon() { return static::URI . '/favicon.ico'; } /** {@inheritdoc} */ public function getConfiguration() { return static::CONFIGURATION; } /** {@inheritdoc} */ public function getParameters() { return static::PARAMETERS; } /** {@inheritdoc} */ public function getURI() { return static::URI; } /** {@inheritdoc} */ public function getDonationURI() { return static::DONATION_URI; } /** {@inheritdoc} */ public function getCacheTimeout() { return static::CACHE_TIMEOUT; } /** {@inheritdoc} */ public function detectParameters($url) { $regex = '/^(https?:\/\/)?(www\.)?(.+?)(\/)?$/'; if ( empty(static::PARAMETERS) && preg_match($regex, $url, $urlMatches) > 0 && preg_match($regex, static::URI, $bridgeUriMatches) > 0 && $urlMatches[3] === $bridgeUriMatches[3] ) { return []; } return null; } /** * Loads a cached value for the specified key * * @param string $key Key name * @param int $duration Cache duration (optional) * @return mixed Cached value or null if the key doesn't exist or has expired */ protected function loadCacheValue($key, $duration = null) { $cacheFactory = new CacheFactory(); $cache = $cacheFactory->create(); // Create class name without the namespace part $scope = $this->getShortName(); $cache->setScope($scope); $cache->setKey($key); if ($duration && $cache->getTime() < time() - $duration) { return null; } return $cache->loadData(); } /** * Stores a value to cache with the specified key * * @param string $key Key name * @param mixed $value Value to cache */ protected function saveCacheValue($key, $value) { $cacheFactory = new CacheFactory(); $cache = $cacheFactory->create(); $scope = $this->getShortName(); $cache->setScope($scope); $cache->setKey($key); $cache->saveData($value); } public function getShortName(): string { return (new \ReflectionClass($this))->getShortName(); } }