diff --git a/composer.json b/composer.json index 69f404a..d682fc1 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ "source": "https://github.com/picocms/Pico" }, "require": { - "php": ">=7.0.8", + "php": ">=8.0", "ext-mbstring": "*", "twig/twig": "^2.12", "symfony/yaml" : "^3.4", diff --git a/lib/AbstractPicoPlugin.php b/lib/AbstractPicoPlugin.php index 4cc318b..e7558d2 100644 --- a/lib/AbstractPicoPlugin.php +++ b/lib/AbstractPicoPlugin.php @@ -31,7 +31,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface * @see PicoPluginInterface::getPico() * @var Pico */ - protected $pico; + protected Pico $pico; /** * Boolean indicating if this plugin is enabled (TRUE) or disabled (FALSE) @@ -40,7 +40,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface * @see PicoPluginInterface::setEnabled() * @var bool|null */ - protected $enabled; + protected ?bool $enabled; /** * Boolean indicating if this plugin was ever enabled/disabled manually @@ -48,7 +48,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface * @see PicoPluginInterface::isStatusChanged() * @var bool */ - protected $statusChanged = false; + protected bool $statusChanged = false; /** * Boolean indicating whether this plugin matches Pico's API version @@ -56,7 +56,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface * @see AbstractPicoPlugin::checkCompatibility() * @var bool|null */ - protected $nativePlugin; + protected ?bool $nativePlugin; /** * List of plugins which this plugin depends on @@ -65,7 +65,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface * @see PicoPluginInterface::getDependencies() * @var string[] */ - protected $dependsOn = array(); + protected array $dependsOn = array(); /** * List of plugin which depend on this plugin @@ -74,7 +74,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface * @see PicoPluginInterface::getDependants() * @var object[]|null */ - protected $dependants; + protected ?array $dependants; /** * Constructs a new instance of a Pico plugin @@ -89,7 +89,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface /** * {@inheritDoc} */ - public function handleEvent($eventName, array $params) + public function handleEvent(string $eventName, array $params) { // plugins can be enabled/disabled using the config if ($eventName === 'onConfigLoaded') { @@ -132,10 +132,10 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface /** * {@inheritDoc} */ - public function setEnabled($enabled, $recursive = true, $auto = false) + public function setEnabled(bool $enabled, bool $recursive = true, bool $auto = false) { - $this->statusChanged = (!$this->statusChanged) ? !$auto : true; - $this->enabled = (bool) $enabled; + $this->statusChanged = !(!$this->statusChanged) || !$auto; + $this->enabled = $enabled; if ($enabled) { $this->checkCompatibility(); @@ -148,7 +148,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface /** * {@inheritDoc} */ - public function isEnabled() + public function isEnabled(): bool|null { return $this->enabled; } @@ -156,7 +156,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface /** * {@inheritDoc} */ - public function isStatusChanged() + public function isStatusChanged(): bool { return $this->statusChanged; } @@ -164,7 +164,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface /** * {@inheritDoc} */ - public function getPico() + public function getPico(): Pico { return $this->pico; } @@ -262,21 +262,21 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface /** * {@inheritDoc} */ - public function getDependencies() + public function getDependencies(): array { - return (array) $this->dependsOn; + return $this->dependsOn; } /** * Disables all plugins which depend on this plugin * - * @see PicoPluginInterface::getDependants() - * * @param bool $recursive disabled dependant plugins automatically * * @throws RuntimeException thrown when a dependency fails + * @see PicoPluginInterface::getDependants() + * */ - protected function checkDependants($recursive) + protected function checkDependants(bool $recursive) { $dependants = $this->getDependants(); if ($dependants) { @@ -307,7 +307,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface /** * {@inheritDoc} */ - public function getDependants() + public function getDependants(): array { if ($this->dependants === null) { $this->dependants = array(); diff --git a/lib/Pico.php b/lib/Pico.php index 4b3a450..c8d220e 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -95,7 +95,7 @@ class Pico * @see Pico::getRootDir() * @var string */ - protected $rootDir; + protected string $rootDir; /** * Vendor directory of this Pico instance @@ -103,7 +103,7 @@ class Pico * @see Pico::getVendorDir() * @var string */ - protected $vendorDir; + protected string $vendorDir; /** * Config directory of this Pico instance @@ -111,7 +111,7 @@ class Pico * @see Pico::getConfigDir() * @var string */ - protected $configDir; + protected string $configDir; /** * Plugins directory of this Pico instance @@ -119,7 +119,7 @@ class Pico * @see Pico::getPluginsDir() * @var string */ - protected $pluginsDir; + protected string $pluginsDir; /** * Themes directory of this Pico instance @@ -127,14 +127,14 @@ class Pico * @see Pico::getThemesDir() * @var string */ - protected $themesDir; + protected string $themesDir; /** * Boolean indicating whether Pico started processing yet * * @var bool */ - protected $locked = false; + protected bool $locked = false; /** * List of loaded plugins @@ -142,14 +142,14 @@ class Pico * @see Pico::getPlugins() * @var object[] */ - protected $plugins = array(); + protected array $plugins = array(); /** * List of loaded plugins using the current API version * * @var PicoPluginInterface[] */ - protected $nativePlugins = array(); + protected array $nativePlugins = array(); /** * Boolean indicating whether Pico loads plugins from the filesystem @@ -158,7 +158,7 @@ class Pico * @see Pico::loadLocalPlugins() * @var bool */ - protected $enableLocalPlugins = true; + protected bool $enableLocalPlugins = true; /** * Current configuration of this Pico instance @@ -166,7 +166,7 @@ class Pico * @see Pico::getConfig() * @var array|null */ - protected $config; + protected ?array $config; /** * Theme in use @@ -174,7 +174,7 @@ class Pico * @see Pico::getTheme() * @var string */ - protected $theme; + protected string $theme; /** * API version of the current theme @@ -182,14 +182,14 @@ class Pico * @see Pico::getThemeApiVersion() * @var int */ - protected $themeApiVersion; + protected int $themeApiVersion; /** * Additional meta headers of the current theme * * @var array|null */ - protected $themeMetaHeaders; + protected ?array $themeMetaHeaders; /** * Part of the URL describing the requested contents @@ -197,7 +197,7 @@ class Pico * @see Pico::getRequestUrl() * @var string|null */ - protected $requestUrl; + protected ?string $requestUrl; /** * Absolute path to the content file being served @@ -205,7 +205,7 @@ class Pico * @see Pico::getRequestFile() * @var string|null */ - protected $requestFile; + protected ?string $requestFile; /** * Raw, not yet parsed contents to serve @@ -213,7 +213,7 @@ class Pico * @see Pico::getRawContent() * @var string|null */ - protected $rawContent; + protected ?string $rawContent; /** * Boolean indicating whether Pico is serving a 404 page @@ -221,7 +221,7 @@ class Pico * @see Pico::is404Content() * @var bool */ - protected $is404Content = false; + protected bool $is404Content = false; /** * Symfony YAML instance used for meta header parsing @@ -229,7 +229,7 @@ class Pico * @see Pico::getYamlParser() * @var \Symfony\Component\Yaml\Parser|null */ - protected $yamlParser; + protected ?\Symfony\Component\Yaml\Parser $yamlParser; /** * List of known meta headers @@ -237,7 +237,7 @@ class Pico * @see Pico::getMetaHeaders() * @var array|null */ - protected $metaHeaders; + protected ?array $metaHeaders; /** * Meta data of the page to serve @@ -245,7 +245,7 @@ class Pico * @see Pico::getFileMeta() * @var array|null */ - protected $meta; + protected ?array $meta; /** * Parsedown Extra instance used for markdown parsing @@ -253,7 +253,7 @@ class Pico * @see Pico::getParsedown() * @var Parsedown|null */ - protected $parsedown; + protected ?Parsedown $parsedown; /** * Parsed content being served @@ -261,7 +261,7 @@ class Pico * @see Pico::getFileContent() * @var string|null */ - protected $content; + protected ?string $content; /** * List of known pages @@ -269,7 +269,7 @@ class Pico * @see Pico::getPages() * @var array[]|null */ - protected $pages; + protected ?array $pages; /** * Data of the page being served @@ -277,7 +277,7 @@ class Pico * @see Pico::getCurrentPage() * @var array|null */ - protected $currentPage; + protected ?array $currentPage; /** * Data of the previous page relative to the page being served @@ -285,7 +285,7 @@ class Pico * @see Pico::getPreviousPage() * @var array|null */ - protected $previousPage; + protected ?array $previousPage; /** * Data of the next page relative to the page being served @@ -293,7 +293,7 @@ class Pico * @see Pico::getNextPage() * @var array|null */ - protected $nextPage; + protected ?array $nextPage; /** * Tree structure of known pages @@ -301,7 +301,7 @@ class Pico * @see Pico::getPageTree() * @var array[]|null */ - protected $pageTree; + protected ?array $pageTree; /** * Twig instance used for template parsing @@ -309,7 +309,7 @@ class Pico * @see Pico::getTwig() * @var Twig_Environment|null */ - protected $twig; + protected ?Twig_Environment $twig; /** * Variables passed to the twig template @@ -317,7 +317,7 @@ class Pico * @see Pico::getTwigVariables() * @var array|null */ - protected $twigVariables; + protected ?array $twigVariables; /** * Name of the Twig template to render @@ -325,7 +325,7 @@ class Pico * @see Pico::getTwigTemplate() * @var string|null */ - protected $twigTemplate; + protected ?string $twigTemplate; /** * Constructs a new Pico instance @@ -336,10 +336,10 @@ class Pico * @param string $configDir config dir of this Pico instance * @param string $pluginsDir plugins dir of this Pico instance * @param string $themesDir themes dir of this Pico instance - * @param bool $enableLocalPlugins enables (TRUE; default) or disables + * @param bool $enableLocalPlugins enables (TRUE; default) or disables * (FALSE) loading plugins from the filesystem */ - public function __construct($rootDir, $configDir, $pluginsDir, $themesDir, $enableLocalPlugins = true) + public function __construct(string $rootDir, string $configDir, string $pluginsDir, string $themesDir, bool $enableLocalPlugins = true) { $this->rootDir = rtrim($rootDir, '/\\') . '/'; $this->vendorDir = dirname(__DIR__) . '/'; @@ -354,7 +354,7 @@ class Pico * * @return string root directory path */ - public function getRootDir() + public function getRootDir(): string { return $this->rootDir; } @@ -364,7 +364,7 @@ class Pico * * @return string vendor directory path */ - public function getVendorDir() + public function getVendorDir(): string { return $this->vendorDir; } @@ -374,7 +374,7 @@ class Pico * * @return string config directory path */ - public function getConfigDir() + public function getConfigDir(): string { return $this->configDir; } @@ -384,7 +384,7 @@ class Pico * * @return string plugins directory path */ - public function getPluginsDir() + public function getPluginsDir(): string { return $this->pluginsDir; } @@ -394,7 +394,7 @@ class Pico * * @return string themes directory path */ - public function getThemesDir() + public function getThemesDir(): string { return $this->themesDir; } @@ -410,7 +410,7 @@ class Pico * * @throws Exception thrown when a irrecoverable error occurs */ - public function run() + public function run(): string { // check lock if ($this->locked) { @@ -576,16 +576,12 @@ class Pico * * @throws RuntimeException thrown when a plugin couldn't be loaded */ - protected function loadComposerPlugins(array $pluginBlacklist = array()) + protected function loadComposerPlugins(array $pluginBlacklist = array()): array { - $composerPlugins = array(); - if (is_file($this->getVendorDir() . 'vendor/pico-plugin.php')) { - // composer root package - $composerPlugins = require($this->getVendorDir() . 'vendor/pico-plugin.php') ?: array(); - } elseif (is_file($this->getVendorDir() . '../../../vendor/pico-plugin.php')) { - // composer dependency package - $composerPlugins = require($this->getVendorDir() . '../../../vendor/pico-plugin.php') ?: array(); - } + + $route_vendor_pico = (is_file($this->getVendorDir() . 'vendor/pico-plugin.php'))? 'vendor/pico-plugin.php' : '../../../vendor/pico-plugin.php'; + + $composerPlugins = require($this->getVendorDir() . $route_vendor_pico) ?: array(); $pluginBlacklist = array_fill_keys($pluginBlacklist, true); @@ -737,7 +733,7 @@ class Pico * * @throws RuntimeException thrown when the plugin couldn't be loaded */ - public function loadPlugin($plugin) + public function loadPlugin(PicoPluginInterface|string $plugin): PicoPluginInterface { if (!is_object($plugin)) { $className = (string) $plugin; @@ -860,16 +856,16 @@ class Pico * Plugins SHOULD implement {@see PicoPluginInterface}, but you MUST NOT * rely on it. For more information see {@see PicoPluginInterface}. * - * @see Pico::loadPlugins() - * @see Pico::getPlugins() - * * @param string $pluginName name of the plugin * * @return object instance of the plugin * * @throws RuntimeException thrown when the plugin wasn't found + *@see Pico::getPlugins() + * + * @see Pico::loadPlugins() */ - public function getPlugin($pluginName) + public function getPlugin(string $pluginName): object { if (isset($this->plugins[$pluginName])) { return $this->plugins[$pluginName]; @@ -1055,22 +1051,22 @@ class Pico * Returns either the value of the specified config variable or * the config array * - * @see Pico::setConfig() - * @see Pico::loadConfig() - * - * @param string $configName optional name of a config variable - * @param mixed $default optional default value to return when the + * @param string|null $configName optional name of a config variable + * @param mixed|null $default optional default value to return when the * named config variable doesn't exist * * @return mixed if no name of a config variable has been supplied, the * config array is returned; otherwise it returns either the value of * the named config variable, or, if the named config variable doesn't * exist, the provided default value or NULL + *@see Pico::loadConfig() + * + * @see Pico::setConfig() */ - public function getConfig($configName = null, $default = null) + public function getConfig(string $configName = null, mixed $default = null): mixed { if ($configName !== null) { - return isset($this->config[$configName]) ? $this->config[$configName] : $default; + return $this->config[$configName] ?? $default; } else { return $this->config; } @@ -1161,7 +1157,7 @@ class Pico * * @return string */ - public function getTheme() + public function getTheme(): string { return $this->theme; } @@ -1174,7 +1170,7 @@ class Pico * * @return int */ - public function getThemeApiVersion() + public function getThemeApiVersion(): int { return $this->themeApiVersion; } @@ -1219,23 +1215,23 @@ class Pico protected function evaluateRequestUrl() { // use QUERY_STRING; e.g. /pico/?sub/page - $pathComponent = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : ''; + $pathComponent = $_SERVER['QUERY_STRING'] ?? ''; if ($pathComponent) { $pathComponent = strstr($pathComponent, '&', true) ?: $pathComponent; - if (strpos($pathComponent, '=') === false) { + if (!str_contains($pathComponent, '=')) { $this->requestUrl = trim(rawurldecode($pathComponent), '/'); } } // use REQUEST_URI (requires URL rewriting); e.g. /pico/sub/page if (($this->requestUrl === null) && $this->isUrlRewritingEnabled()) { - $scriptName = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : '/index.php'; + $scriptName = $_SERVER['SCRIPT_NAME'] ?? '/index.php'; $basePath = dirname($scriptName); $basePath = !in_array($basePath, array('.', '/', '\\'), true) ? $basePath . '/' : '/'; $basePathLength = strlen($basePath); - $requestUri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; + $requestUri = $_SERVER['REQUEST_URI'] ?? ''; if ($requestUri && (substr($requestUri, 0, $basePathLength) === $basePath)) { $requestUri = substr($requestUri, $basePathLength); if ($requestUri && (($queryStringPos = strpos($requestUri, '?')) !== false)) { @@ -1259,7 +1255,7 @@ class Pico * * @return string|null request URL */ - public function getRequestUrl() + public function getRequestUrl(): ?string { return $this->requestUrl; } @@ -1274,13 +1270,13 @@ class Pico * RECOMMENDED to use PHP's `open_basedir` feature - always, not just * with Pico! * - * @see Pico::getRequestFile() - * * @param string $requestUrl path name (likely from a URL) to resolve * * @return string path to the resolved content file + *@see Pico::getRequestFile() + * */ - public function resolveFilePath($requestUrl) + public function resolveFilePath(string $requestUrl): string { $contentDir = $this->getConfig('content_dir'); $contentExt = $this->getConfig('content_ext'); @@ -1314,7 +1310,7 @@ class Pico * * @return string|null file pat */ - public function getRequestFile() + public function getRequestFile(): ?string { return $this->requestFile; } @@ -1322,13 +1318,13 @@ class Pico /** * Returns the raw contents of a file * - * @see Pico::getRawContent() - * * @param string $file file path * * @return string raw contents of the file + *@see Pico::getRawContent() + * */ - public function loadFileContent($file) + public function loadFileContent(string $file): string { return file_get_contents($file); } @@ -1339,13 +1335,13 @@ class Pico * * If no suitable `404.md` is found, fallback to a built-in error message. * - * @see Pico::getRawContent() - * * @param string $file path to requested (but not existing) file * * @return string raw contents of the 404 file + *@see Pico::getRawContent() + * */ - public function load404Content($file) + public function load404Content(string $file): string { $contentDir = $this->getConfig('content_dir'); $contentDirLength = strlen($contentDir); @@ -1369,13 +1365,12 @@ class Pico } // fallback to built-in error message - $rawErrorContent = "---\n" + return "---\n" . "Title: Error 404\n" . "Robots: noindex,nofollow\n" . "---\n\n" . "# Error 404\n\n" . "Woops. Looks like this page doesn't exist.\n"; - return $rawErrorContent; } /** @@ -1386,7 +1381,7 @@ class Pico * * @return string|null raw contents */ - public function getRawContent() + public function getRawContent(): ?string { return $this->rawContent; } @@ -1398,7 +1393,7 @@ class Pico * * @return bool TRUE if Pico is serving a 404 page, FALSE otherwise */ - public function is404Content() + public function is404Content(): bool { return $this->is404Content; } @@ -1413,7 +1408,7 @@ class Pico * key to search for, the array value is later used to access the * found value */ - public function getMetaHeaders() + public function getMetaHeaders(): ?array { if ($this->metaHeaders === null) { $this->metaHeaders = array( @@ -1446,7 +1441,7 @@ class Pico * * @return \Symfony\Component\Yaml\Parser Symfony YAML parser */ - public function getYamlParser() + public function getYamlParser(): ?\Symfony\Component\Yaml\Parser { if ($this->yamlParser === null) { $this->yamlParser = new \Symfony\Component\Yaml\Parser(); @@ -1466,17 +1461,17 @@ class Pico * event first. The implicit availability of headers is for users and * pure (!) theme developers ONLY. * - * @see Pico::getFileMeta() - * * @param string $rawContent the raw file contents - * @param string[] $headers known meta headers + * @param string[] $headers known meta headers * * @return array parsed meta data * * @throws \Symfony\Component\Yaml\Exception\ParseException thrown when the * meta data is invalid + *@see Pico::getFileMeta() + * */ - public function parseFileMeta($rawContent, array $headers) + public function parseFileMeta(string $rawContent, array $headers): array { $meta = array(); $pattern = "/^(?:\xEF\xBB\xBF)?(\/(\*)|---)[[:blank:]]*(?:\r)?\n" @@ -1545,7 +1540,7 @@ class Pico * * @return array|null parsed meta data */ - public function getFileMeta() + public function getFileMeta(): ?array { return $this->meta; } @@ -1556,9 +1551,9 @@ class Pico * This method triggers the `onParsedownRegistered` event when the * Parsedown markdown parser wasn't initiated yet. * - * @return Parsedown Parsedown markdown parser + * @return Parsedown|ParsedownExtra|null Parsedown markdown parser */ - public function getParsedown() + public function getParsedown(): Parsedown|ParsedownExtra|null { if ($this->parsedown === null) { $className = $this->config['content_config']['extra'] ? 'ParsedownExtra' : 'Parsedown'; @@ -1580,16 +1575,16 @@ class Pico * This method removes the meta header and replaces `%...%` placeholders * by calling the {@see Pico::substituteFileContent()} method. * - * @see Pico::substituteFileContent() - * @see Pico::parseFileContent() - * @see Pico::getFileContent() - * * @param string $rawContent raw contents of a page * @param array $meta meta data to use for %meta.*% replacement * - * @return string prepared Markdown contents + * @return string prepared Markdown contents + *@see Pico::substituteFileContent() + * @see Pico::parseFileContent() + * @see Pico::getFileContent() + * */ - public function prepareFileContent($rawContent, array $meta = array()) + public function prepareFileContent(string $rawContent, array $meta = array()): string { // remove meta header $metaHeaderPattern = "/^(?:\xEF\xBB\xBF)?(\/(\*)|---)[[:blank:]]*(?:\r)?\n" @@ -1597,9 +1592,7 @@ class Pico $markdown = preg_replace($metaHeaderPattern, '', $rawContent, 1); // replace placeholders - $markdown = $this->substituteFileContent($markdown, $meta); - - return $markdown; + return $this->substituteFileContent($markdown, $meta); } /** @@ -1610,7 +1603,7 @@ class Pico * * @return string substituted Markdown contents */ - public function substituteFileContent($markdown, array $meta = array()) + public function substituteFileContent(string $markdown, array $meta = array()): string { $variables = array(); @@ -1661,16 +1654,16 @@ class Pico /** * Parses the contents of a page using ParsedownExtra * - * @see Pico::prepareFileContent() + * @param string $markdown Markdown contents of a page + * @param bool $singleLine whether to parse just a single line of markup + * + * @return string parsed contents (HTML) + *@see Pico::prepareFileContent() * @see Pico::substituteFileContent() * @see Pico::getFileContent() * - * @param string $markdown Markdown contents of a page - * @param bool $singleLine whether to parse just a single line of markup - * - * @return string parsed contents (HTML) */ - public function parseFileContent($markdown, $singleLine = false) + public function parseFileContent(string $markdown, bool $singleLine = false): string { $markdownParser = $this->getParsedown(); return !$singleLine ? @$markdownParser->text($markdown) : @$markdownParser->line($markdown); @@ -1685,7 +1678,7 @@ class Pico * * @return string|null parsed contents */ - public function getFileContent() + public function getFileContent(): ?string { return $this->content; } @@ -1831,10 +1824,10 @@ class Pico // sort by arbitrary meta value $orderByMeta = $this->getConfig('pages_order_by_meta'); uasort($this->pages, function ($a, $b) use ($alphaSortClosure, $order, $orderByMeta) { - $aSortValue = isset($a['meta'][$orderByMeta]) ? $a['meta'][$orderByMeta] : null; + $aSortValue = $a['meta'][$orderByMeta] ?? null; $aSortValueNull = ($aSortValue === null); - $bSortValue = isset($b['meta'][$orderByMeta]) ? $b['meta'][$orderByMeta] : null; + $bSortValue = $b['meta'][$orderByMeta] ?? null; $bSortValueNull = ($bSortValue === null); $cmp = 0; @@ -1920,7 +1913,7 @@ class Pico * * @return array[]|null the data of all pages */ - public function getPages() + public function getPages(): ?array { return $this->pages; } @@ -1950,7 +1943,7 @@ class Pico * * @return array|null page data */ - public function getCurrentPage() + public function getCurrentPage(): ?array { return $this->currentPage; } @@ -1962,7 +1955,7 @@ class Pico * * @return array|null page data */ - public function getPreviousPage() + public function getPreviousPage(): ?array { return $this->previousPage; } @@ -1974,7 +1967,7 @@ class Pico * * @return array|null page data */ - public function getNextPage() + public function getNextPage(): ?array { return $this->nextPage; } @@ -2078,7 +2071,7 @@ class Pico * * @return array[]|null the tree structure of all pages */ - public function getPageTree() + public function getPageTree(): ?array { return $this->pageTree; } @@ -2097,7 +2090,7 @@ class Pico * * @return Twig_Environment|null Twig template engine */ - public function getTwig() + public function getTwig(): ?Twig_Environment { if ($this->twig === null) { $twigConfig = $this->getConfig('twig_config'); @@ -2145,7 +2138,7 @@ class Pico * * @return array template variables */ - protected function getTwigVariables() + protected function getTwigVariables(): array { return array( 'config' => $this->getConfig(), @@ -2170,7 +2163,7 @@ class Pico * * @return string template name */ - protected function getTwigTemplate() + protected function getTwigTemplate(): string { $templateName = !empty($this->meta['template']) ? $this->meta['template'] : 'index'; return $templateName . '.twig'; @@ -2185,7 +2178,7 @@ class Pico * * @return string the base url */ - public function getBaseUrl() + public function getBaseUrl(): string { $baseUrl = $this->getConfig('base_url'); if ($baseUrl) { @@ -2240,7 +2233,7 @@ class Pico * * @return bool TRUE if URL rewriting is enabled, FALSE otherwise */ - public function isUrlRewritingEnabled() + public function isUrlRewritingEnabled(): bool { $urlRewritingEnabled = $this->getConfig('rewrite_url'); if ($urlRewritingEnabled !== null) { @@ -2263,7 +2256,7 @@ class Pico * * @return bool TRUE if Pico's debug mode is enabled, FALSE otherwise */ - public function isDebugModeEnabled() + public function isDebugModeEnabled(): bool { $debugModeEnabled = $this->getConfig('debug'); if ($debugModeEnabled !== null) { @@ -2287,17 +2280,17 @@ class Pico * This method can be used in Twig templates by applying the `link` filter * to a string representing a page ID. * - * @param string $page ID of the page to link to + * @param string $page ID of the page to link to * @param array|string|null $queryData either an array of properties to * create a URL-encoded query string from, or a already encoded string - * @param bool $dropIndex if the last path component is + * @param bool $dropIndex if the last path component is * "index", passing TRUE (default) will remove this path component * * @return string URL * * @throws InvalidArgumentException thrown when invalid arguments got passed */ - public function getPageUrl($page, $queryData = null, $dropIndex = true) + public function getPageUrl(string $page, array|string $queryData = null, bool $dropIndex = true): string { if (is_array($queryData)) { $queryData = http_build_query($queryData, '', '&'); @@ -2341,7 +2334,7 @@ class Pico * * @return string|null either the corresponding page ID or NULL */ - public function getPageId($path) + public function getPageId(string $path): ?string { $contentDir = $this->getConfig('content_dir'); $contentDirLength = strlen($contentDir); @@ -2374,7 +2367,7 @@ class Pico * * @return string URL with replaced placeholders */ - public function substituteUrl($url) + public function substituteUrl(string $url): string { $variables = array( '%base_url%?' => $this->getBaseUrl() . (!$this->isUrlRewritingEnabled() ? '?' : ''), @@ -2397,7 +2390,7 @@ class Pico * * @return string */ - public function getBaseThemeUrl() + public function getBaseThemeUrl(): string { return $this->getConfig('themes_url'); } @@ -2420,7 +2413,7 @@ class Pico * * @return string the URL of the given folder */ - public function getUrlFromPath($absolutePath) + public function getUrlFromPath(string $absolutePath): string { $absolutePath = str_replace('\\', '/', $absolutePath); @@ -2454,21 +2447,21 @@ class Pico * {@see Pico::filterVariable()} for a detailed description. It can be * used in Twig templates by calling the `url_param` function. * - * @see Pico::filterVariable() - * * @param string $name name of the URL GET parameter * to filter - * @param int|string $filter the filter to apply - * @param mixed|array $options either a associative options + * @param int|string $filter the filter to apply + * @param mixed|null $options either a associative options * array to be used by the filter or a scalar default value - * @param int|string|int[]|string[] $flags flags and flag strings to be + * @param int|string|int[]|string[]|null $flags flags and flag strings to be * used by the filter * * @return mixed either the filtered data, FALSE if the filter fails, or * NULL if the URL GET parameter doesn't exist and no default value is * given + *@see Pico::filterVariable() + * */ - public function getUrlParameter($name, $filter = '', $options = null, $flags = null) + public function getUrlParameter(string $name, int|string $filter = '', mixed $options = null, array|int|string $flags = null): mixed { $variable = (isset($_GET[$name]) && is_scalar($_GET[$name])) ? $_GET[$name] : null; return $this->filterVariable($variable, $filter, $options, $flags); @@ -2481,21 +2474,21 @@ class Pico * {@see Pico::filterVariable()} for a detailed description. It can be * used in Twig templates by calling the `form_param` function. * - * @see Pico::filterVariable() - * * @param string $name name of the HTTP POST * parameter to filter - * @param int|string $filter the filter to apply - * @param mixed|array $options either a associative options + * @param int|string $filter the filter to apply + * @param mixed|null $options either a associative options * array to be used by the filter or a scalar default value - * @param int|string|int[]|string[] $flags flags and flag strings to be + * @param int|string|int[]|string[]|null $flags flags and flag strings to be * used by the filter * * @return mixed either the filtered data, FALSE if the filter fails, or * NULL if the HTTP POST parameter doesn't exist and no default value * is given + *@see Pico::filterVariable() + * */ - public function getFormParameter($name, $filter = '', $options = null, $flags = null) + public function getFormParameter(string $name, int|string $filter = '', mixed $options = null, array|int|string $flags = null): mixed { $variable = (isset($_POST[$name]) && is_scalar($_POST[$name])) ? $_POST[$name] : null; return $this->filterVariable($variable, $filter, $options, $flags); @@ -2526,13 +2519,13 @@ class Pico * Sanitize filters * * @param mixed $variable value to filter - * @param int|string $filter ID (int) or name (string) of + * @param int|string $filter ID (int) or name (string) of * the filter to apply; if omitted, the method will return FALSE - * @param mixed|array $options either a associative array + * @param mixed|null $options either a associative array * of options to be used by the filter (e.g. `array('default' => 42)`), * or a scalar default value that will be returned when the passed * value is NULL (optional) - * @param int|string|int[]|string[] $flags either a bitwise disjunction + * @param int|string|int[]|string[]|null $flags either a bitwise disjunction * of flags or a string with the significant part of a flag constant * (the constant name is the result of "FILTER_FLAG_" and the given * string in ASCII-only uppercase); you may also pass an array of flags @@ -2544,11 +2537,11 @@ class Pico * returns the sanitized value; if no value (i.e. NULL) was given, the * method always returns either the provided default value or NULL */ - protected function filterVariable($variable, $filter = '', $options = null, $flags = null) + protected function filterVariable(mixed $variable, int|string $filter = '', mixed $options = null, array|int|string $flags = null): mixed { $defaultValue = null; if (is_array($options)) { - $defaultValue = isset($options['default']) ? $options['default'] : null; + $defaultValue = $options['default'] ?? null; } elseif ($options !== null) { $defaultValue = $options; $options = array('default' => $defaultValue); @@ -2587,14 +2580,14 @@ class Pico * @param string $directory start directory * @param string $fileExtension return files with the given file extension * only (optional) - * @param int $order specify whether and how files should be + * @param int $order specify whether and how files should be * sorted; use Pico::SORT_ASC for a alphabetical ascending order (this * is the default behaviour), Pico::SORT_DESC for a descending order * or Pico::SORT_NONE to leave the result unsorted * * @return array list of found files */ - public function getFiles($directory, $fileExtension = '', $order = self::SORT_ASC) + public function getFiles(string $directory, string $fileExtension = '', int $order = self::SORT_ASC): array { $directory = rtrim($directory, '/'); $fileExtensionLength = strlen($fileExtension); @@ -2636,7 +2629,7 @@ class Pico * * @return array list of found files */ - public function getFilesGlob($pattern, $order = self::SORT_ASC) + public function getFilesGlob(string $pattern, int $order = self::SORT_ASC): array { $result = array(); $sortFlag = ($order === self::SORT_NONE) ? GLOB_NOSORT : 0; @@ -2659,15 +2652,15 @@ class Pico /** * Makes a relative path absolute to Pico's root dir * - * @param string $path relative or absolute path - * @param string $basePath treat relative paths relative to the given path; + * @param string $path relative or absolute path + * @param string|null $basePath treat relative paths relative to the given path; * defaults to Pico::$rootDir - * @param bool $endSlash whether to add a trailing slash to the absolute + * @param bool $endSlash whether to add a trailing slash to the absolute * path or not (defaults to TRUE) * * @return string absolute path */ - public function getAbsolutePath($path, $basePath = null, $endSlash = true) + public function getAbsolutePath(string $path, string $basePath = null, bool $endSlash = true): string { if ($basePath === null) { $basePath = $this->getRootDir(); @@ -2699,7 +2692,7 @@ class Pico * @throws UnexpectedValueException thrown when a absolute path is passed * although absolute paths aren't allowed */ - public function getNormalizedPath($path, $allowAbsolutePath = false, $endSlash = true) + public function getNormalizedPath(string $path, bool $allowAbsolutePath = false, bool $endSlash = true): string { $absolutePath = ''; if (DIRECTORY_SEPARATOR === '\\') { @@ -2748,14 +2741,14 @@ class Pico * even though they don't include a scheme and host. * * @param string $url relative or absolute URL - * @param string $baseUrl treat relative URLs relative to the given URL; + * @param string|null $baseUrl treat relative URLs relative to the given URL; * defaults to Pico::getBaseUrl() * @param bool $endSlash whether to add a trailing slash to the absolute * URL or not (defaults to TRUE) * * @return string absolute URL */ - public function getAbsoluteUrl($url, $baseUrl = null, $endSlash = true) + public function getAbsoluteUrl(string $url, string $baseUrl = null, bool $endSlash = true): string { if (($url[0] !== '/') && !preg_match('#^[A-Za-z][A-Za-z0-9+\-.]*://#', $url)) { $url = (($baseUrl !== null) ? $baseUrl : $this->getBaseUrl()) . $url; @@ -2775,14 +2768,14 @@ class Pico * * You MUST NOT trigger events of Pico's core with a plugin! * - * @see PicoPluginInterface - * @see AbstractPicoPlugin - * @see DummyPlugin - * * @param string $eventName name of the event to trigger * @param array $params optional parameters to pass + *@see DummyPlugin + * + * @see PicoPluginInterface + * @see AbstractPicoPlugin */ - public function triggerEvent($eventName, array $params = array()) + public function triggerEvent(string $eventName, array $params = array()) { foreach ($this->nativePlugins as $plugin) { $plugin->handleEvent($eventName, $params); diff --git a/lib/PicoPluginInterface.php b/lib/PicoPluginInterface.php index 323be86..465b7b9 100644 --- a/lib/PicoPluginInterface.php +++ b/lib/PicoPluginInterface.php @@ -36,14 +36,11 @@ interface PicoPluginInterface * @param string $eventName name of the triggered event * @param array $params passed parameters */ - public function handleEvent($eventName, array $params); + public function handleEvent(string $eventName, array $params); /** * Enables or disables this plugin * - * @see PicoPluginInterface::isEnabled() - * @see PicoPluginInterface::isStatusChanged() - * * @param bool $enabled enable (TRUE) or disable (FALSE) this plugin * @param bool $recursive when TRUE, enable or disable recursively. * In other words, if you enable a plugin, all required plugins are @@ -55,8 +52,11 @@ interface PicoPluginInterface * parameter is optional and defaults to FALSE. * * @throws RuntimeException thrown when a dependency fails + *@see PicoPluginInterface::isEnabled() + * @see PicoPluginInterface::isStatusChanged() + * */ - public function setEnabled($enabled, $recursive = true, $auto = false); + public function setEnabled(bool $enabled, bool $recursive = true, bool $auto = false); /** * Returns a boolean indicating whether this plugin is enabled or not @@ -69,7 +69,7 @@ interface PicoPluginInterface * * @return bool|null plugin is enabled (TRUE) or disabled (FALSE) */ - public function isEnabled(); + public function isEnabled(): ?bool; /** * Returns TRUE if the plugin was ever enabled/disabled manually @@ -78,21 +78,21 @@ interface PicoPluginInterface * * @return bool plugin is in its default state (TRUE), FALSE otherwise */ - public function isStatusChanged(); + public function isStatusChanged(): bool; /** * Returns a list of names of plugins required by this plugin * * @return string[] required plugins */ - public function getDependencies(); + public function getDependencies(): array; /** * Returns a list of plugins which depend on this plugin * * @return object[] dependant plugins */ - public function getDependants(); + public function getDependants(): array; /** * Returns the plugin's instance of Pico @@ -101,5 +101,5 @@ interface PicoPluginInterface * * @return Pico the plugin's instance of Pico */ - public function getPico(); + public function getPico(): Pico; } diff --git a/lib/PicoTwigExtension.php b/lib/PicoTwigExtension.php index d87a6e1..a14f39d 100644 --- a/lib/PicoTwigExtension.php +++ b/lib/PicoTwigExtension.php @@ -10,6 +10,8 @@ * License-Filename: LICENSE */ +use JetBrains\PhpStorm\ArrayShape; + /** * Pico's Twig extension to implement additional filters * @@ -26,7 +28,7 @@ class PicoTwigExtension extends Twig_Extension * @see PicoTwigExtension::getPico() * @var Pico */ - private $pico; + private Pico $pico; /** * Constructs a new instance of this Twig extension @@ -45,7 +47,7 @@ class PicoTwigExtension extends Twig_Extension * * @return Pico the extension's instance of Pico */ - public function getPico() + public function getPico(): Pico { return $this->pico; } @@ -57,7 +59,7 @@ class PicoTwigExtension extends Twig_Extension * * @return string the extension name */ - public function getName() + public function getName(): string { return 'PicoTwigExtension'; } @@ -69,7 +71,8 @@ class PicoTwigExtension extends Twig_Extension * * @return Twig_SimpleFilter[] array of Pico's Twig filters */ - public function getFilters() + #[ArrayShape(['markdown' => "\Twig_SimpleFilter", 'map' => "\Twig_SimpleFilter", 'sort_by' => "\Twig_SimpleFilter", 'link' => "\Twig_SimpleFilter", 'url' => "\Twig_SimpleFilter"])] + public function getFilters(): array { return array( 'markdown' => new Twig_SimpleFilter( @@ -91,7 +94,8 @@ class PicoTwigExtension extends Twig_Extension * * @return Twig_SimpleFunction[] array of Pico's Twig functions */ - public function getFunctions() + #[ArrayShape(['url_param' => "\Twig_SimpleFunction", 'form_param' => "\Twig_SimpleFunction", 'pages' => "\Twig_SimpleFunction"])] + public function getFunctions(): array { return array( 'url_param' => new Twig_SimpleFunction('url_param', array($this, 'urlParamFunction')), @@ -108,16 +112,16 @@ class PicoTwigExtension extends Twig_Extension * Don't use it to parse the contents of a page, use the `content` filter * instead, what ensures the proper preparation of the contents. * + * @param string $markdown markdown to parse + * @param array $meta meta data to use for %meta.*% replacement + * @param bool $singleLine whether to parse just a single line of markup + * + * @return string parsed HTML * @see Pico::substituteFileContent() * @see Pico::parseFileContent() * - * @param string $markdown markdown to parse - * @param array $meta meta data to use for %meta.*% replacement - * @param bool $singleLine whether to parse just a single line of markup - * - * @return string parsed HTML */ - public function markdownFilter($markdown, array $meta = array(), $singleLine = false) + public function markdownFilter(string $markdown, array $meta = array(), bool $singleLine = false): string { $markdown = $this->getPico()->substituteFileContent($markdown, $meta); return $this->getPico()->parseFileContent($markdown, $singleLine); @@ -129,7 +133,7 @@ class PicoTwigExtension extends Twig_Extension * This method is registered as the Twig `map` filter. You can use this * filter to e.g. get all page titles (`{{ pages|map("title") }}`). * - * @param array|Traversable $var variable to map + * @param Traversable|array $var variable to map * @param mixed $mapKeyPath key to map; either a scalar or a * array interpreted as key path (i.e. ['foo', 'bar'] will return all * $item['foo']['bar'] values) @@ -138,7 +142,7 @@ class PicoTwigExtension extends Twig_Extension * * @throws Twig_Error_Runtime */ - public function mapFilter($var, $mapKeyPath) + public function mapFilter(Traversable|array $var, mixed $mapKeyPath): array { if (!is_array($var) && (!is_object($var) || !($var instanceof Traversable))) { throw new Twig_Error_Runtime(sprintf( @@ -166,11 +170,11 @@ class PicoTwigExtension extends Twig_Extension * always sorted in ascending order, apply Twigs `reverse` filter to * achieve a descending order. * - * @param array|Traversable $var variable to sort + * @param Traversable|array $var variable to sort * @param mixed $sortKeyPath key to use for sorting; either * a scalar or a array interpreted as key path (i.e. ['foo', 'bar'] * will sort $var by $item['foo']['bar']) - * @param string $fallback specify what to do with items + * @param string $fallback specify what to do with items * which don't contain the specified sort key; use "bottom" (default) * to move these items to the end of the sorted array, "top" to rank * them first, "keep" to keep the original order, or "remove" to remove @@ -180,7 +184,7 @@ class PicoTwigExtension extends Twig_Extension * * @throws Twig_Error_Runtime */ - public function sortByFilter($var, $sortKeyPath, $fallback = 'bottom') + public function sortByFilter(Traversable|array $var, mixed $sortKeyPath, string $fallback = 'bottom'): Traversable|array { if (is_object($var) && ($var instanceof Traversable)) { $var = iterator_to_array($var, true); @@ -243,7 +247,7 @@ class PicoTwigExtension extends Twig_Extension * Returns the value of a variable item specified by a scalar key or a * arbitrary deep sub-key using a key path * - * @param array|Traversable|ArrayAccess|object $var base variable + * @param Traversable|ArrayAccess|array $var base variable * @param mixed $keyPath scalar key or a * array interpreted as key path (when passing e.g. ['foo', 'bar'], * the method will return $var['foo']['bar']) specifying the value @@ -251,7 +255,7 @@ class PicoTwigExtension extends Twig_Extension * @return mixed the requested value or NULL when the given key or key path * didn't match */ - public static function getKeyOfVar($var, $keyPath) + public static function getKeyOfVar(Traversable|ArrayAccess|array $var, mixed $keyPath): mixed { if (!$keyPath) { return null; @@ -298,21 +302,21 @@ class PicoTwigExtension extends Twig_Extension * * The Twig function disallows the use of the `callback` filter. * - * @see Pico::getUrlParameter() - * * @param string $name name of the URL GET parameter * to filter - * @param int|string $filter the filter to apply - * @param mixed|array $options either a associative options + * @param int|string $filter the filter to apply + * @param mixed|null $options either a associative options * array to be used by the filter or a scalar default value - * @param int|string|int[]|string[] $flags flags and flag strings to be + * @param int|string|int[]|string[]|null $flags flags and flag strings to be * used by the filter * * @return mixed either the filtered data, FALSE if the filter fails, or * NULL if the URL GET parameter doesn't exist and no default value is * given + *@see Pico::getUrlParameter() + * */ - public function urlParamFunction($name, $filter = '', $options = null, $flags = null) + public function urlParamFunction(string $name, int|string $filter = '', mixed $options = null, array|int|string $flags = null): mixed { $filter = $filter ? (is_string($filter) ? filter_id($filter) : (int) $filter) : false; if (!$filter || ($filter === FILTER_CALLBACK)) { @@ -327,21 +331,21 @@ class PicoTwigExtension extends Twig_Extension * * The Twig function disallows the use of the `callback` filter. * - * @see Pico::getFormParameter() - * * @param string $name name of the HTTP POST * parameter to filter - * @param int|string $filter the filter to apply - * @param mixed|array $options either a associative options + * @param int|string $filter the filter to apply + * @param mixed|null $options either a associative options * array to be used by the filter or a scalar default value - * @param int|string|int[]|string[] $flags flags and flag strings to be + * @param int|string|int[]|string[]|null $flags flags and flag strings to be * used by the filter * * @return mixed either the filtered data, FALSE if the filter fails, or * NULL if the HTTP POST parameter doesn't exist and no default value * is given + *@see Pico::getFormParameter() + * */ - public function formParamFunction($name, $filter = '', $options = null, $flags = null) + public function formParamFunction(string $name, int|string $filter = '', mixed $options = null, array|int|string $flags = null): mixed { $filter = $filter ? (is_string($filter) ? filter_id($filter) : (int) $filter) : false; if (!$filter || ($filter === FILTER_CALLBACK)) { @@ -411,19 +415,19 @@ class PicoTwigExtension extends Twig_Extension * this together with `$offset = -1` is equivalent to `$start = ""` and * `$offset = 0`. * - * @param string $start name of the node to start from + * @param string $start name of the node to start from * @param int|null $depth return pages until the given maximum depth; * pass NULL to return all descendant pages; defaults to 0 - * @param int $depthOffset start returning pages from the given + * @param int $depthOffset start returning pages from the given * minimum depth; defaults to 0 - * @param int $offset ascend (positive) or descend (negative) the + * @param int $offset ascend (positive) or descend (negative) the * given number of branches before returning pages; defaults to 1 * * @return array[] the data of the matched pages * * @throws Twig_Error_Runtime */ - public function pagesFunction($start = '', $depth = 0, $depthOffset = 0, $offset = 1) + public function pagesFunction(string $start = '', ?int $depth = 0, int $depthOffset = 0, int $offset = 1): array { $start = (string) $start; if (basename($start) === 'index') { diff --git a/plugins/DummyPlugin.php b/plugins/DummyPlugin.php index 3812014..8e15f8a 100644 --- a/plugins/DummyPlugin.php +++ b/plugins/DummyPlugin.php @@ -55,7 +55,7 @@ class DummyPlugin extends AbstractPicoPlugin * @see AbstractPicoPlugin::$enabled * @var bool|null */ - protected $enabled = false; + protected ?bool $enabled = false; /** * This plugin depends on ...