From 347bb91f2acc9121d47de3ac442d27f7b397562e Mon Sep 17 00:00:00 2001 From: Dan Kleine Date: Tue, 22 Oct 2024 11:40:53 +0200 Subject: [PATCH] feat: Upgrade Pico to PHP 8.2/8.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make Pico compatible with PHP 8.2 and 8.3. - Upgrade symfony/yaml config reader - Upgrade twig template engine, - Raise supported PHP version, - Remove pico/pico-deprecated - Remove redundant index files - Use pico-composer to set up a project Required Changes for users running version 2.1: - Replace composer.json from pico/pico-composer to load Pico Core via Composer - Add all required folders to project root (config/, plugins/, themes/) - Convert config file from PHP to YAML - Errors may happen due to wrong value types (eg. multiline text is not supported) - Themes may need some tweaks: - Themes need a YAML config as well - HTML files in themes need to be renamed to TWIG files - Replace twig condition »is_front_page« with »current_page.id == "index"« - Replace twig variable »page.excerpt« with »page.meta.Summary« --- composer.json | 13 +++--------- lib/Pico.php | 14 ++++++------- lib/PicoTwigExtension.php | 44 +++++++++++++++++++-------------------- 3 files changed, 32 insertions(+), 39 deletions(-) diff --git a/composer.json b/composer.json index b9ff27c7a..80eefab27 100644 --- a/composer.json +++ b/composer.json @@ -31,16 +31,15 @@ "source": "https://github.com/picocms/Pico" }, "require": { - "php": ">=5.3.6", + "php": ">=8.2.20", "ext-mbstring": "*", - "twig/twig": "^1.36", - "symfony/yaml" : "^2.8", + "twig/twig": "^3.14", + "symfony/yaml" : "^7.1", "erusev/parsedown": "1.8.0-beta-7", "erusev/parsedown-extra": "0.8.0-beta-1" }, "suggest": { "picocms/pico-theme": "Pico requires a theme to actually display the contents of your website. This is Pico's official default theme.", - "picocms/pico-deprecated": "PicoDeprecated's purpose is to maintain backward compatibility to older versions of Pico.", "picocms/composer-installer": "This Composer plugin is responsible for installing Pico plugins and themes using the Composer package manager." }, "autoload": { @@ -49,11 +48,5 @@ "PicoPluginInterface": "lib/", "AbstractPicoPlugin": "lib/" } - }, - "extra": { - "branch-alias": { - "dev-master": "2.1.x-dev", - "dev-pico-3.0": "3.0.x-dev" - } } } diff --git a/lib/Pico.php b/lib/Pico.php index 2e1ac3701..8e86f4590 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -307,7 +307,7 @@ class Pico * Twig instance used for template parsing * * @see Pico::getTwig() - * @var Twig_Environment|null + * @var \TwigEnvironment|null */ protected $twig; @@ -2095,19 +2095,19 @@ public function getPageTree() * @see https://twig.symfony.com/ Twig website * @see https://github.com/twigphp/Twig Twig on GitHub * - * @return Twig_Environment|null Twig template engine + * @return \TwigEnvironment|null Twig template engine */ public function getTwig() { if ($this->twig === null) { $twigConfig = $this->getConfig('twig_config'); - $twigLoader = new Twig_Loader_Filesystem($this->getThemesDir() . $this->getTheme()); - $this->twig = new Twig_Environment($twigLoader, $twigConfig); + $twigLoader = new \Twig\Loader\FilesystemLoader($this->getThemesDir() . $this->getTheme()); + $this->twig = new \Twig\Environment($twigLoader, $twigConfig); $this->twig->addExtension(new PicoTwigExtension($this)); if (!empty($twigConfig['debug'])) { - $this->twig->addExtension(new Twig_Extension_Debug()); + $this->twig->addExtension(new \Twig\Extension\DebugExtension()); } // register content filter @@ -2115,7 +2115,7 @@ public function getTwig() // this is the reason why we can't register this filter as part of PicoTwigExtension $pico = $this; $pages = &$this->pages; - $this->twig->addFilter(new Twig_SimpleFilter( + $this->twig->addFilter(new \Twig\TwigFilter( 'content', function ($page) use ($pico, &$pages) { if (isset($pages[$page])) { @@ -2156,7 +2156,7 @@ protected function getTwigVariables() 'theme_url' => $this->getConfig('themes_url') . $this->getTheme(), 'site_title' => $this->getConfig('site_title'), 'meta' => $this->meta, - 'content' => new Twig_Markup($this->content, 'UTF-8'), + 'content' => new \Twig\Markup($this->content, 'UTF-8'), 'pages' => $this->pages, 'previous_page' => $this->previousPage, 'current_page' => $this->currentPage, diff --git a/lib/PicoTwigExtension.php b/lib/PicoTwigExtension.php index 098811024..7cf80fcd8 100644 --- a/lib/PicoTwigExtension.php +++ b/lib/PicoTwigExtension.php @@ -18,7 +18,7 @@ * @license http://opensource.org/licenses/MIT The MIT License * @version 2.1 */ -class PicoTwigExtension extends Twig_Extension +class PicoTwigExtension extends \Twig\Extension\AbstractExtension { /** * Current instance of Pico @@ -53,7 +53,7 @@ public function getPico() /** * Returns the name of the extension * - * @see Twig_ExtensionInterface::getName() + * @see \Twig\Extension\AbstractExtension::getName() * * @return string the extension name */ @@ -65,38 +65,38 @@ public function getName() /** * Returns a list of Pico-specific Twig filters * - * @see Twig_ExtensionInterface::getFilters() + * @see \Twig\Extension\ExtensionInterface::getFilters() * - * @return Twig_SimpleFilter[] array of Pico's Twig filters + * @return \Twig\TwigFilter[] array of Pico's Twig filters */ public function getFilters() { return array( - 'markdown' => new Twig_SimpleFilter( + 'markdown' => new \Twig\TwigFilter( 'markdown', array($this, 'markdownFilter'), array('is_safe' => array('html')) ), - 'map' => new Twig_SimpleFilter('map', array($this, 'mapFilter')), - 'sort_by' => new Twig_SimpleFilter('sort_by', array($this, 'sortByFilter')), - 'link' => new Twig_SimpleFilter('link', array($this->pico, 'getPageUrl')), - 'url' => new Twig_SimpleFilter('url', array($this->pico, 'substituteUrl')) + 'map' => new \Twig\TwigFilter('map', array($this, 'mapFilter')), + 'sort_by' => new \Twig\TwigFilter('sort_by', array($this, 'sortByFilter')), + 'link' => new \Twig\TwigFilter('link', array($this->pico, 'getPageUrl')), + 'url' => new \Twig\TwigFilter('url', array($this->pico, 'substituteUrl')) ); } /** * Returns a list of Pico-specific Twig functions * - * @see Twig_ExtensionInterface::getFunctions() + * @see \Twig\Extension\ExtensionInterface::getFunctions() * - * @return Twig_SimpleFunction[] array of Pico's Twig functions + * @return \Twig\TwigFunction[] array of Pico's Twig functions */ public function getFunctions() { return array( - 'url_param' => new Twig_SimpleFunction('url_param', array($this, 'urlParamFunction')), - 'form_param' => new Twig_SimpleFunction('form_param', array($this, 'formParamFunction')), - 'pages' => new Twig_SimpleFunction('pages', array($this, 'pagesFunction')) + 'url_param' => new \Twig\TwigFunction('url_param', array($this, 'urlParamFunction')), + 'form_param' => new \Twig\TwigFunction('form_param', array($this, 'formParamFunction')), + 'pages' => new \Twig\TwigFunction('pages', array($this, 'pagesFunction')) ); } @@ -136,12 +136,12 @@ public function markdownFilter($markdown, array $meta = array(), $singleLine = f * * @return array mapped values * - * @throws Twig_Error_Runtime + * @throws \Twig\Error\RuntimeError */ public function mapFilter($var, $mapKeyPath) { if (!is_array($var) && (!is_object($var) || !($var instanceof Traversable))) { - throw new Twig_Error_Runtime(sprintf( + throw new \Twig\Error\RuntimeError(sprintf( 'The map filter only works with arrays or "Traversable", got "%s"', is_object($var) ? get_class($var) : gettype($var) )); @@ -178,20 +178,20 @@ public function mapFilter($var, $mapKeyPath) * * @return array sorted array * - * @throws Twig_Error_Runtime + * @throws \Twig\Error\RuntimeError */ public function sortByFilter($var, $sortKeyPath, $fallback = 'bottom') { if (is_object($var) && ($var instanceof Traversable)) { $var = iterator_to_array($var, true); } elseif (!is_array($var)) { - throw new Twig_Error_Runtime(sprintf( + throw new \Twig\Error\RuntimeError(sprintf( 'The sort_by filter only works with arrays or "Traversable", got "%s"', is_object($var) ? get_class($var) : gettype($var) )); } if (($fallback !== 'top') && ($fallback !== 'bottom') && ($fallback !== 'keep') && ($fallback !== "remove")) { - throw new Twig_Error_Runtime( + throw new \Twig\Error\RuntimeError( 'The sort_by filter only supports the "top", "bottom", "keep" and "remove" fallbacks' ); } @@ -399,7 +399,7 @@ public function formParamFunction($name, $filter = '', $options = null, $flags = * returns Pico's full pages array. * * If `$depth` is negative after taking `$offset` into consideration, the - * function will throw a {@see Twig_Error_Runtime} exception, since this + * function will throw a {@see \Twig\Error\RuntimeError} exception, since this * would simply make no sense and is likely an error. Passing a negative * `$depthOffset` is equivalent to passing `$depthOffset = 0`. * @@ -421,7 +421,7 @@ public function formParamFunction($name, $filter = '', $options = null, $flags = * * @return array[] the data of the matched pages * - * @throws Twig_Error_Runtime + * @throws \Twig\Error\RuntimeError */ public function pagesFunction($start = '', $depth = 0, $depthOffset = 0, $offset = 1) { @@ -443,7 +443,7 @@ public function pagesFunction($start = '', $depth = 0, $depthOffset = 0, $offset $depthOffset = $depthOffset + $offset; if (($depth !== null) && ($depth < 0)) { - throw new Twig_Error_Runtime('The pages function doesn\'t support negative depths'); + throw new \Twig\Error\RuntimeError('The pages function doesn\'t support negative depths'); } $pageTree = $this->getPico()->getPageTree();