updated core to 1.7.15

This commit is contained in:
2021-05-27 18:17:50 +02:00
parent dc1fdf21c9
commit 19ecb285ab
552 changed files with 80743 additions and 16675 deletions

View File

@@ -0,0 +1,392 @@
<?php
/**
* @package Grav\Installer
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Installer;
use Composer\Autoload\ClassLoader;
use Exception;
use Grav\Common\Cache;
use Grav\Common\GPM\Installer;
use Grav\Common\Grav;
use Grav\Common\Plugins;
use RuntimeException;
use function class_exists;
use function dirname;
use function function_exists;
use function is_string;
/**
* Grav installer.
*
* NOTE: This class can be initialized during upgrade from an older version of Grav. Make sure it runs there!
*/
final class Install
{
/** @var int Installer version. */
public $version = 1;
/** @var array */
public $requires = [
'php' => [
'name' => 'PHP',
'versions' => [
'7.4' => '7.4.0',
'7.3' => '7.3.6',
'' => '7.4.12'
]
],
'grav' => [
'name' => 'Grav',
'versions' => [
'1.6' => '1.6.0',
'' => '1.6.28'
]
],
'plugins' => [
'admin' => [
'name' => 'Admin',
'optional' => true,
'versions' => [
'1.9' => '1.9.0',
'' => '1.9.13'
]
],
'email' => [
'name' => 'Email',
'optional' => true,
'versions' => [
'3.0' => '3.0.0',
'' => '3.0.10'
]
],
'form' => [
'name' => 'Form',
'optional' => true,
'versions' => [
'4.1' => '4.1.0',
'4.0' => '4.0.0',
'3.0' => '3.0.0',
'' => '4.1.2'
]
],
'login' => [
'name' => 'Login',
'optional' => true,
'versions' => [
'3.3' => '3.3.0',
'3.0' => '3.0.0',
'' => '3.3.6'
]
],
]
];
/** @var array */
public $ignores = [
'backup',
'cache',
'images',
'logs',
'tmp',
'user',
'.htaccess',
'robots.txt'
];
/** @var array */
private $classMap = [
InstallException::class => __DIR__ . '/InstallException.php',
Versions::class => __DIR__ . '/Versions.php',
VersionUpdate::class => __DIR__ . '/VersionUpdate.php',
VersionUpdater::class => __DIR__ . '/VersionUpdater.php',
YamlUpdater::class => __DIR__ . '/YamlUpdater.php',
];
/** @var string|null */
private $zip;
/** @var string|null */
private $location;
/** @var VersionUpdater */
private $updater;
/** @var static */
private static $instance;
/**
* @return static
*/
public static function instance()
{
if (null === self::$instance) {
self::$instance = new static();
}
return self::$instance;
}
private function __construct()
{
}
/**
* @param string|null $zip
* @return $this
*/
public function setZip(?string $zip)
{
$this->zip = $zip;
return $this;
}
/**
* @param string|null $zip
* @return void
*/
public function __invoke(?string $zip)
{
$this->zip = $zip;
$failedRequirements = $this->checkRequirements();
if ($failedRequirements) {
$error = ['Following requirements have failed:'];
foreach ($failedRequirements as $name => $req) {
$error[] = "{$req['title']} >= <strong>v{$req['minimum']}</strong> required, you have <strong>v{$req['installed']}</strong>";
}
$errors = implode("<br />\n", $error);
if (\defined('GRAV_CLI') && GRAV_CLI) {
$errors = "\n\n" . strip_tags($errors) . "\n\n";
$errors .= <<<ERR
Please install Grav 1.6.31 first by running following commands:
wget -q https://getgrav.org/download/core/grav-update/1.6.31 -O tmp/grav-update-v1.6.31.zip
bin/gpm direct-install -y tmp/grav-update-v1.6.31.zip
rm tmp/grav-update.zip
ERR;
}
throw new RuntimeException($errors);
}
$this->prepare();
$this->install();
$this->finalize();
}
/**
* NOTE: This method can only be called after $grav['plugins']->init().
*
* @return array List of failed requirements. If the list is empty, installation can go on.
*/
public function checkRequirements(): array
{
$results = [];
$this->checkVersion($results, 'php', 'php', $this->requires['php'], PHP_VERSION);
$this->checkVersion($results, 'grav', 'grav', $this->requires['grav'], GRAV_VERSION);
$this->checkPlugins($results, $this->requires['plugins']);
return $results;
}
/**
* @return void
* @throws RuntimeException
*/
public function prepare(): void
{
// Locate the new Grav update and the target site from the filesystem.
$location = realpath(__DIR__);
$target = realpath(GRAV_ROOT . '/index.php');
if (!$location) {
throw new RuntimeException('Internal Error', 500);
}
if ($target && dirname($location, 4) === dirname($target)) {
// We cannot copy files into themselves, abort!
throw new RuntimeException('Grav has already been installed here!', 400);
}
// Load the installer classes.
foreach ($this->classMap as $class_name => $path) {
// Make sure that none of the Grav\Installer classes have been loaded, otherwise installation may fail!
if (class_exists($class_name, false)) {
throw new RuntimeException(sprintf('Cannot update Grav, class %s has already been loaded!', $class_name), 500);
}
require $path;
}
$this->legacySupport();
$this->location = dirname($location, 4);
$versions = Versions::instance(USER_DIR . 'config/versions.yaml');
$this->updater = new VersionUpdater('core/grav', __DIR__ . '/updates', $this->getVersion(), $versions);
$this->updater->preflight();
}
/**
* @return void
* @throws RuntimeException
*/
public function install(): void
{
if (!$this->location) {
throw new RuntimeException('Oops, installer was run without prepare()!', 500);
}
try {
// Update user/config/version.yaml before copying the files to avoid frontend from setting the version schema.
$this->updater->install();
Installer::install(
$this->zip ?? '',
GRAV_ROOT,
['sophisticated' => true, 'overwrite' => true, 'ignore_symlinks' => true, 'ignores' => $this->ignores],
$this->location,
!($this->zip && is_file($this->zip))
);
} catch (Exception $e) {
Installer::setError($e->getMessage());
}
$errorCode = Installer::lastErrorCode();
$success = !(is_string($errorCode) || ($errorCode & (Installer::ZIP_OPEN_ERROR | Installer::ZIP_EXTRACT_ERROR)));
if (!$success) {
throw new RuntimeException(Installer::lastErrorMsg());
}
}
/**
* @return void
* @throws RuntimeException
*/
public function finalize(): void
{
// Finalize can be run without installing Grav first.
if (!$this->updater) {
$versions = Versions::instance(USER_DIR . 'config/versions.yaml');
$this->updater = new VersionUpdater('core/grav', __DIR__ . '/updates', GRAV_VERSION, $versions);
$this->updater->install();
}
$this->updater->postflight();
Cache::clearCache('all');
clearstatcache();
if (function_exists('opcache_reset')) {
@opcache_reset();
}
}
/**
* @param array $results
* @param string $type
* @param string $name
* @param array $check
* @param string|null $version
* @return void
*/
protected function checkVersion(array &$results, $type, $name, array $check, $version): void
{
if (null === $version && !empty($check['optional'])) {
return;
}
$major = $minor = 0;
$versions = $check['versions'] ?? [];
foreach ($versions as $major => $minor) {
if (!$major || version_compare($version ?? '0', $major, '<')) {
continue;
}
if (version_compare($version ?? '0', $minor, '>=')) {
return;
}
break;
}
if (!$major) {
$minor = reset($versions);
}
$recommended = end($versions);
if (version_compare($recommended, $minor, '<=')) {
$recommended = null;
}
$results[$name] = [
'type' => $type,
'name' => $name,
'title' => $check['name'] ?? $name,
'installed' => $version,
'minimum' => $minor,
'recommended' => $recommended
];
}
/**
* @param array $results
* @param array $plugins
* @return void
*/
protected function checkPlugins(array &$results, array $plugins): void
{
if (!class_exists('Plugins')) {
return;
}
foreach ($plugins as $name => $check) {
$plugin = Plugins::get($name);
if (!$plugin) {
$this->checkVersion($results, 'plugin', $name, $check, null);
continue;
}
$blueprint = $plugin->blueprints();
$version = (string)$blueprint->get('version');
$check['name'] = ($blueprint->get('name') ?? $check['name'] ?? $name) . ' Plugin';
$this->checkVersion($results, 'plugin', $name, $check, $version);
}
}
/**
* @return string
*/
protected function getVersion(): string
{
$definesFile = "{$this->location}/system/defines.php";
$content = file_get_contents($definesFile);
if (false === $content) {
return '';
}
preg_match("/define\('GRAV_VERSION', '([^']+)'\);/mu", $content, $matches);
return $matches[1] ?? '';
}
protected function legacySupport(): void
{
// Support install for Grav 1.6.0 - 1.6.20 by loading the original class from the older version of Grav.
class_exists(\Grav\Console\Cli\CacheCommand::class, true);
}
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* @package Grav\Installer
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Installer;
use Throwable;
/**
* Class InstallException
* @package Grav\Installer
*/
class InstallException extends \RuntimeException
{
/**
* InstallException constructor.
* @param string $message
* @param Throwable $previous
*/
public function __construct(string $message, Throwable $previous)
{
parent::__construct($message, $previous->getCode(), $previous);
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace Grav\Installer;
use Closure;
/**
* Class VersionUpdate
* @package Grav\Installer
*/
final class VersionUpdate
{
/** @var string */
private $revision;
/** @var string */
private $version;
/** @var string */
private $date;
/** @var string */
private $patch;
/** @var VersionUpdater */
private $updater;
/** @var callable[] */
private $methods;
public function __construct(string $file, VersionUpdater $updater)
{
$name = basename($file, '.php');
$this->revision = $name;
[$this->version, $this->date, $this->patch] = explode('_', $name);
$this->updater = $updater;
$this->methods = require $file;
}
public function getRevision(): string
{
return $this->revision;
}
public function getVersion(): string
{
return $this->version;
}
public function getDate(): string
{
return $this->date;
}
public function getPatch(): string
{
return $this->date;
}
public function getUpdater(): VersionUpdater
{
return $this->updater;
}
/**
* Run right before installation.
*/
public function preflight(VersionUpdater $updater): void
{
$method = $this->methods['preflight'] ?? null;
if ($method instanceof Closure) {
$method->call($this);
}
}
/**
* Runs right after installation.
*/
public function postflight(VersionUpdater $updater): void
{
$method = $this->methods['postflight'] ?? null;
if ($method instanceof Closure) {
$method->call($this);
}
}
}

View File

@@ -0,0 +1,133 @@
<?php
namespace Grav\Installer;
use DirectoryIterator;
/**
* Class VersionUpdater
* @package Grav\Installer
*/
final class VersionUpdater
{
/** @var string */
private $name;
/** @var string */
private $path;
/** @var string */
private $version;
/** @var Versions */
private $versions;
/** @var VersionUpdate[] */
private $updates;
/**
* VersionUpdater constructor.
* @param string $name
* @param string $path
* @param string $version
* @param Versions $versions
*/
public function __construct(string $name, string $path, string $version, Versions $versions)
{
$this->name = $name;
$this->path = $path;
$this->version = $version;
$this->versions = $versions;
$this->loadUpdates();
}
/**
* Pre-installation method.
*/
public function preflight(): void
{
foreach ($this->updates as $revision => $update) {
$update->preflight($this);
}
}
/**
* Install method.
*/
public function install(): void
{
$versions = $this->getVersions();
$versions->updateVersion($this->name, $this->version);
$versions->save();
}
/**
* Post-installation method.
*/
public function postflight(): void
{
$versions = $this->getVersions();
foreach ($this->updates as $revision => $update) {
$update->postflight($this);
$versions->setSchema($this->name, $revision);
$versions->save();
}
}
/**
* @return Versions
*/
public function getVersions(): Versions
{
return $this->versions;
}
/**
* @param string|null $name
* @return string|null
*/
public function getExtensionVersion(string $name = null): ?string
{
return $this->versions->getVersion($name ?? $this->name);
}
/**
* @param string|null $name
* @return string|null
*/
public function getExtensionSchema(string $name = null): ?string
{
return $this->versions->getSchema($name ?? $this->name);
}
/**
* @param string|null $name
* @return array
*/
public function getExtensionHistory(string $name = null): array
{
return $this->versions->getHistory($name ?? $this->name);
}
protected function loadUpdates(): void
{
$this->updates = [];
$schema = $this->getExtensionSchema();
$iterator = new DirectoryIterator($this->path);
foreach ($iterator as $item) {
if (!$item->isFile() || $item->getExtension() !== 'php') {
continue;
}
$revision = $item->getBasename('.php');
if (!$schema || version_compare($revision, $schema, '>')) {
$realPath = $item->getRealPath();
if ($realPath) {
$this->updates[$revision] = new VersionUpdate($realPath, $this);
}
}
}
uksort($this->updates, 'version_compare');
}
}

View File

@@ -0,0 +1,329 @@
<?php
/**
* @package Grav\Installer
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Installer;
use Symfony\Component\Yaml\Yaml;
use function is_array;
use function is_string;
/**
* Grav Versions
*
* NOTE: This class can be initialized during upgrade from an older version of Grav. Make sure it runs there!
*/
final class Versions
{
/** @var string */
protected $filename;
/** @var array */
protected $items;
/** @var bool */
protected $updated = false;
/** @var self[] */
protected static $instance;
/**
* @param string|null $filename
* @return self
*/
public static function instance(string $filename = null): self
{
$filename = $filename ?? USER_DIR . 'config/versions.yaml';
if (!isset(self::$instance[$filename])) {
self::$instance[$filename] = new self($filename);
}
return self::$instance[$filename];
}
/**
* @return bool True if the file was updated.
*/
public function save(): bool
{
if (!$this->updated) {
return false;
}
file_put_contents($this->filename, Yaml::dump($this->items, 4, 2));
$this->updated = false;
return true;
}
/**
* @return array
*/
public function getAll(): array
{
return $this->items;
}
/**
* @return array|null
*/
public function getGrav(): ?array
{
return $this->get('core/grav');
}
/**
* @return array
*/
public function getPlugins(): array
{
return $this->get('plugins', []);
}
/**
* @param string $name
* @return array|null
*/
public function getPlugin(string $name): ?array
{
return $this->get("plugins/{$name}");
}
/**
* @return array
*/
public function getThemes(): array
{
return $this->get('themes', []);
}
/**
* @param string $name
* @return array|null
*/
public function getTheme(string $name): ?array
{
return $this->get("themes/{$name}");
}
/**
* @param string $extension
* @return array|null
*/
public function getExtension(string $extension): ?array
{
return $this->get($extension);
}
/**
* @param string $extension
* @param array|null $value
*/
public function setExtension(string $extension, ?array $value): void
{
if (null !== $value) {
$this->set($extension, $value);
} else {
$this->undef($extension);
}
}
/**
* @param string $extension
* @return string|null
*/
public function getVersion(string $extension): ?string
{
$version = $this->get("{$extension}/version", null);
return is_string($version) ? $version : null;
}
/**
* @param string $extension
* @param string|null $version
*/
public function setVersion(string $extension, ?string $version): void
{
$this->updateHistory($extension, $version);
}
/**
* NOTE: Updates also history.
*
* @param string $extension
* @param string|null $version
*/
public function updateVersion(string $extension, ?string $version): void
{
$this->set("{$extension}/version", $version);
$this->updateHistory($extension, $version);
}
/**
* @param string $extension
* @return string|null
*/
public function getSchema(string $extension): ?string
{
$version = $this->get("{$extension}/schema", null);
return is_string($version) ? $version : null;
}
/**
* @param string $extension
* @param string|null $schema
*/
public function setSchema(string $extension, ?string $schema): void
{
if (null !== $schema) {
$this->set("{$extension}/schema", $schema);
} else {
$this->undef("{$extension}/schema");
}
}
/**
* @param string $extension
* @return array
*/
public function getHistory(string $extension): array
{
$name = "{$extension}/history";
$history = $this->get($name, []);
// Fix for broken Grav 1.6 history
if ($extension === 'grav') {
$history = $this->fixHistory($history);
}
return $history;
}
/**
* @param string $extension
* @param string|null $version
*/
public function updateHistory(string $extension, ?string $version): void
{
$name = "{$extension}/history";
$history = $this->getHistory($extension);
$history[] = ['version' => $version, 'date' => gmdate('Y-m-d H:i:s')];
$this->set($name, $history);
}
/**
* Clears extension history. Useful when creating skeletons.
*
* @param string $extension
*/
public function removeHistory(string $extension): void
{
$this->undef("{$extension}/history");
}
/**
* @param array $history
* @return array
*/
private function fixHistory(array $history): array
{
if (isset($history['version'], $history['date'])) {
$fix = [['version' => $history['version'], 'date' => $history['date']]];
unset($history['version'], $history['date']);
$history = array_merge($fix, $history);
}
return $history;
}
/**
* Get value by using dot notation for nested arrays/objects.
*
* @param string $name Slash separated path to the requested value.
* @param mixed $default Default value (or null).
* @return mixed Value.
*/
private function get(string $name, $default = null)
{
$path = explode('/', $name);
$current = $this->items;
foreach ($path as $field) {
if (is_array($current) && isset($current[$field])) {
$current = $current[$field];
} else {
return $default;
}
}
return $current;
}
/**
* Set value by using dot notation for nested arrays/objects.
*
* @param string $name Slash separated path to the requested value.
* @param mixed $value New value.
*/
private function set(string $name, $value): void
{
$path = explode('/', $name);
$current = &$this->items;
foreach ($path as $field) {
// Handle arrays and scalars.
if (!is_array($current)) {
$current = [$field => []];
} elseif (!isset($current[$field])) {
$current[$field] = [];
}
$current = &$current[$field];
}
$current = $value;
$this->updated = true;
}
/**
* Unset value by using dot notation for nested arrays/objects.
*
* @param string $name Dot separated path to the requested value.
*/
private function undef(string $name): void
{
$path = $name !== '' ? explode('/', $name) : [];
if (!$path) {
return;
}
$var = array_pop($path);
$current = &$this->items;
foreach ($path as $field) {
if (!is_array($current) || !isset($current[$field])) {
return;
}
$current = &$current[$field];
}
unset($current[$var]);
$this->updated = true;
}
private function __construct(string $filename)
{
$this->filename = $filename;
$content = is_file($filename) ? file_get_contents($filename) : null;
if (false === $content) {
throw new \RuntimeException('Versions file cannot be read');
}
$this->items = $content ? Yaml::parse($content) : [];
}
}

View File

@@ -0,0 +1,430 @@
<?php
/**
* @package Grav\Installer
*
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Installer;
use Symfony\Component\Yaml\Yaml;
use function assert;
use function count;
use function is_array;
use function strlen;
/**
* Grav YAML updater.
*
* NOTE: This class can be initialized during upgrade from an older version of Grav. Make sure it runs there!
*/
final class YamlUpdater
{
/** @var string */
protected $filename;
/** @var string[] */
protected $lines;
/** @var array */
protected $comments;
/** @var array */
protected $items;
/** @var bool */
protected $updated = false;
/** @var self[] */
protected static $instance;
public static function instance(string $filename): self
{
if (!isset(self::$instance[$filename])) {
self::$instance[$filename] = new self($filename);
}
return self::$instance[$filename];
}
/**
* @return bool
*/
public function save(): bool
{
if (!$this->updated) {
return false;
}
try {
if (!$this->isHandWritten()) {
$yaml = Yaml::dump($this->items, 5, 2);
} else {
$yaml = implode("\n", $this->lines);
$items = Yaml::parse($yaml);
if ($items !== $this->items) {
throw new \RuntimeException('Failed saving the content');
}
}
file_put_contents($this->filename, $yaml);
} catch (\Exception $e) {
throw new \RuntimeException('Failed to update ' . basename($this->filename) . ': ' . $e->getMessage());
}
return true;
}
/**
* @return bool
*/
public function isHandWritten(): bool
{
return !empty($this->comments);
}
/**
* @return array
*/
public function getComments(): array
{
$comments = [];
foreach ($this->lines as $i => $line) {
if ($this->isLineEmpty($line)) {
$comments[$i+1] = $line;
} elseif ($comment = $this->getInlineComment($line)) {
$comments[$i+1] = $comment;
}
}
return $comments;
}
/**
* @param string $variable
* @param mixed $value
*/
public function define(string $variable, $value): void
{
// If variable has already value, we're good.
if ($this->get($variable) !== null) {
return;
}
// If one of the parents isn't array, we're good, too.
if (!$this->canDefine($variable)) {
return;
}
$this->set($variable, $value);
if (!$this->isHandWritten()) {
return;
}
$parts = explode('.', $variable);
$lineNos = $this->findPath($this->lines, $parts);
$count = count($lineNos);
$last = array_key_last($lineNos);
$value = explode("\n", trim(Yaml::dump([$last => $this->get(implode('.', array_keys($lineNos)))], max(0, 5-$count), 2)));
$currentLine = array_pop($lineNos) ?: 0;
$parentLine = array_pop($lineNos);
if ($parentLine !== null) {
$c = $this->getLineIndentation($this->lines[$parentLine] ?? '');
$n = $this->getLineIndentation($this->lines[$parentLine+1] ?? $this->lines[$parentLine] ?? '');
$indent = $n > $c ? $n : $c + 2;
} else {
$indent = 0;
array_unshift($value, '');
}
$spaces = str_repeat(' ', $indent);
foreach ($value as &$line) {
$line = $spaces . $line;
}
unset($line);
array_splice($this->lines, abs($currentLine)+1, 0, $value);
}
public function undefine(string $variable): void
{
// If variable does not have value, we're good.
if ($this->get($variable) === null) {
return;
}
// If one of the parents isn't array, we're good, too.
if (!$this->canDefine($variable)) {
return;
}
$this->undef($variable);
if (!$this->isHandWritten()) {
return;
}
// TODO: support also removing property from handwritten configuration file.
}
private function __construct(string $filename)
{
$content = is_file($filename) ? (string)file_get_contents($filename) : '';
$content = rtrim(str_replace(["\r\n", "\r"], "\n", $content));
$this->filename = $filename;
$this->lines = explode("\n", $content);
$this->comments = $this->getComments();
$this->items = $content ? Yaml::parse($content) : [];
}
/**
* Return array of offsets for the parent nodes. Negative value means position, but not found.
*
* @param array $lines
* @param array $parts
* @return int[]
*/
private function findPath(array $lines, array $parts)
{
$test = true;
$indent = -1;
$current = array_shift($parts);
$j = 1;
$found = [];
$space = '';
foreach ($lines as $i => $line) {
if ($this->isLineEmpty($line)) {
if ($this->isLineComment($line) && $this->getLineIndentation($line) > $indent) {
$j = $i;
}
continue;
}
if ($test === true) {
$test = false;
$spaces = strlen($line) - strlen(ltrim($line, ' '));
if ($spaces <= $indent) {
$found[$current] = -$j;
return $found;
}
$indent = $spaces;
$space = $indent ? str_repeat(' ', $indent) : '';
}
if (0 === \strncmp($line, $space, strlen($space))) {
$pattern = "/^{$space}(['\"]?){$current}\\1\:/";
if (preg_match($pattern, $line)) {
$found[$current] = $i;
$current = array_shift($parts);
if ($current === null) {
return $found;
}
$test = true;
}
} else {
$found[$current] = -$j;
return $found;
}
$j = $i;
}
$found[$current] = -$j;
return $found;
}
/**
* Returns true if the current line is blank or if it is a comment line.
*
* @param string $line Contents of the line
* @return bool Returns true if the current line is empty or if it is a comment line, false otherwise
*/
private function isLineEmpty(string $line): bool
{
return $this->isLineBlank($line) || $this->isLineComment($line);
}
/**
* Returns true if the current line is blank.
*
* @param string $line Contents of the line
* @return bool Returns true if the current line is blank, false otherwise
*/
private function isLineBlank(string $line): bool
{
return '' === trim($line, ' ');
}
/**
* Returns true if the current line is a comment line.
*
* @param string $line Contents of the line
* @return bool Returns true if the current line is a comment line, false otherwise
*/
private function isLineComment(string $line): bool
{
//checking explicitly the first char of the trim is faster than loops or strpos
$ltrimmedLine = ltrim($line, ' ');
return '' !== $ltrimmedLine && '#' === $ltrimmedLine[0];
}
/**
* @param string $line
* @return bool
*/
private function isInlineComment(string $line): bool
{
return $this->getInlineComment($line) !== null;
}
/**
* @param string $line
* @return string|null
*/
private function getInlineComment(string $line): ?string
{
$pos = strpos($line, ' #');
if (false === $pos) {
return null;
}
$parts = explode(' #', $line);
$part = '';
while ($part .= array_shift($parts)) {
// Remove quoted values.
$part = preg_replace('/(([\'"])[^\2]*\2)/', '', $part);
assert(null !== $part);
$part = preg_split('/[\'"]/', $part, 2);
assert(false !== $part);
if (!isset($part[1])) {
$part = $part[0];
array_unshift($parts, str_repeat(' ', strlen($part) - strlen(trim($part, ' '))));
break;
}
$part = $part[1];
}
return implode(' #', $parts);
}
/**
* Returns the current line indentation.
*
* @param string $line
* @return int The current line indentation
*/
private function getLineIndentation(string $line): int
{
return \strlen($line) - \strlen(ltrim($line, ' '));
}
/**
* Get value by using dot notation for nested arrays/objects.
*
* @param string $name Dot separated path to the requested value.
* @param mixed $default Default value (or null).
* @return mixed Value.
*/
private function get(string $name, $default = null)
{
$path = explode('.', $name);
$current = $this->items;
foreach ($path as $field) {
if (is_array($current) && isset($current[$field])) {
$current = $current[$field];
} else {
return $default;
}
}
return $current;
}
/**
* Set value by using dot notation for nested arrays/objects.
*
* @param string $name Dot separated path to the requested value.
* @param mixed $value New value.
*/
private function set(string $name, $value): void
{
$path = explode('.', $name);
$current = &$this->items;
foreach ($path as $field) {
// Handle arrays and scalars.
if (!is_array($current)) {
$current = [$field => []];
} elseif (!isset($current[$field])) {
$current[$field] = [];
}
$current = &$current[$field];
}
$current = $value;
$this->updated = true;
}
/**
* Unset value by using dot notation for nested arrays/objects.
*
* @param string $name Dot separated path to the requested value.
*/
private function undef(string $name): void
{
$path = $name !== '' ? explode('.', $name) : [];
if (!$path) {
return;
}
$var = array_pop($path);
$current = &$this->items;
foreach ($path as $field) {
if (!is_array($current) || !isset($current[$field])) {
return;
}
$current = &$current[$field];
}
unset($current[$var]);
$this->updated = true;
}
/**
* Get value by using dot notation for nested arrays/objects.
*
* @param string $name Dot separated path to the requested value.
* @return bool
*/
private function canDefine(string $name): bool
{
$path = explode('.', $name);
$current = $this->items;
foreach ($path as $field) {
if (is_array($current)) {
if (!isset($current[$field])) {
return true;
}
$current = $current[$field];
} else {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,24 @@
<?php
use Grav\Installer\InstallException;
use Grav\Installer\VersionUpdate;
use Grav\Installer\YamlUpdater;
return [
'preflight' => null,
'postflight' =>
function () {
/** @var VersionUpdate $this */
try {
// Keep old defaults for backwards compatibility.
$yaml = YamlUpdater::instance(GRAV_ROOT . '/user/config/system.yaml');
$yaml->define('twig.autoescape', false);
$yaml->define('strict_mode.yaml_compat', true);
$yaml->define('strict_mode.twig_compat', true);
$yaml->define('strict_mode.blueprint_compat', true);
$yaml->save();
} catch (\Exception $e) {
throw new InstallException('Could not update system configuration to maintain backwards compatibility', $e);
}
}
];