| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 | <?php/** * @package    Grav\Common\Config * * @copyright  Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license    MIT License; see LICENSE file for details. */namespace Grav\Common\Config;use BadMethodCallException;use Grav\Common\File\CompiledYamlFile;use Grav\Common\Data\Data;use Grav\Common\Utils;use InvalidArgumentException;use Pimple\Container;use Psr\Http\Message\ServerRequestInterface;use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;use RuntimeException;use function defined;use function is_array;/** * Class Setup * @package Grav\Common\Config */class Setup extends Data{    /**     * @var array Environment aliases normalized to lower case.     */    public static $environments = [        '' => 'unknown',        '127.0.0.1' => 'localhost',        '::1' => 'localhost'    ];    /**     * @var string|null Current environment normalized to lower case.     */    public static $environment;    /** @var string */    public static $securityFile = 'config://security.yaml';    /** @var array */    protected $streams = [        'user' => [            'type' => 'ReadOnlyStream',            'force' => true,            'prefixes' => [                '' => [] // Set in constructor            ]        ],        'cache' => [            'type' => 'Stream',            'force' => true,            'prefixes' => [                '' => [], // Set in constructor                'images' => ['images']            ]        ],        'log' => [            'type' => 'Stream',            'force' => true,            'prefixes' => [                '' => [] // Set in constructor            ]        ],        'tmp' => [            'type' => 'Stream',            'force' => true,            'prefixes' => [                '' => [] // Set in constructor            ]        ],        'backup' => [            'type' => 'Stream',            'force' => true,            'prefixes' => [                '' => [] // Set in constructor            ]        ],        'environment' => [            'type' => 'ReadOnlyStream'            // If not defined, environment will be set up in the constructor.        ],        'system' => [            'type' => 'ReadOnlyStream',            'prefixes' => [                '' => ['system'],            ]        ],        'asset' => [            'type' => 'Stream',            'prefixes' => [                '' => ['assets'],            ]        ],        'blueprints' => [            'type' => 'ReadOnlyStream',            'prefixes' => [                '' => ['environment://blueprints', 'user://blueprints', 'system://blueprints'],            ]        ],        'config' => [            'type' => 'ReadOnlyStream',            'prefixes' => [                '' => ['environment://config', 'user://config', 'system://config'],            ]        ],        'plugins' => [            'type' => 'ReadOnlyStream',            'prefixes' => [                '' => ['user://plugins'],             ]        ],        'plugin' => [            'type' => 'ReadOnlyStream',            'prefixes' => [                '' => ['user://plugins'],            ]        ],        'themes' => [            'type' => 'ReadOnlyStream',            'prefixes' => [                '' => ['user://themes'],            ]        ],        'languages' => [            'type' => 'ReadOnlyStream',            'prefixes' => [                '' => ['environment://languages', 'user://languages', 'system://languages'],            ]        ],        'image' => [            'type' => 'Stream',            'prefixes' => [                '' => ['user://images', 'system://images']            ]        ],        'page' => [            'type' => 'ReadOnlyStream',            'prefixes' => [                '' => ['user://pages']            ]        ],        'user-data' => [            'type' => 'Stream',            'force' => true,            'prefixes' => [                '' => ['user://data']            ]        ],        'account' => [            'type' => 'ReadOnlyStream',            'prefixes' => [                '' => ['user://accounts']            ]        ],    ];    /**     * @param Container|array $container     */    public function __construct($container)    {        // Configure main streams.        $abs = str_starts_with(GRAV_SYSTEM_PATH, '/');        $this->streams['system']['prefixes'][''] = $abs ? ['system', GRAV_SYSTEM_PATH] : ['system'];        $this->streams['user']['prefixes'][''] = [GRAV_USER_PATH];        $this->streams['cache']['prefixes'][''] = [GRAV_CACHE_PATH];        $this->streams['log']['prefixes'][''] = [GRAV_LOG_PATH];        $this->streams['tmp']['prefixes'][''] = [GRAV_TMP_PATH];        $this->streams['backup']['prefixes'][''] = [GRAV_BACKUP_PATH];        // If environment is not set, look for the environment variable and then the constant.        $environment = static::$environment ??            (defined('GRAV_ENVIRONMENT') ? GRAV_ENVIRONMENT : (getenv('GRAV_ENVIRONMENT') ?: null));        // If no environment is set, make sure we get one (CLI or hostname).        if (null === $environment) {            if (defined('GRAV_CLI')) {                $request = null;                $uri = null;                $environment = 'cli';            } else {                /** @var ServerRequestInterface $request */                $request = $container['request'];                $uri = $request->getUri();                $environment = $uri->getHost();            }        }        // Resolve server aliases to the proper environment.        static::$environment = static::$environments[$environment] ?? $environment;        // Pre-load setup.php which contains our initial configuration.        // Configuration may contain dynamic parts, which is why we need to always load it.        // If GRAV_SETUP_PATH has been defined, use it, otherwise use defaults.        $setupFile = defined('GRAV_SETUP_PATH') ? GRAV_SETUP_PATH : (getenv('GRAV_SETUP_PATH') ?: null);        if (null !== $setupFile) {            // Make sure that the custom setup file exists. Terminates the script if not.            if (!str_starts_with($setupFile, '/')) {                $setupFile = GRAV_WEBROOT . '/' . $setupFile;            }            if (!is_file($setupFile)) {                echo 'GRAV_SETUP_PATH is defined but does not point to existing setup file.';                exit(1);            }        } else {            $setupFile = GRAV_WEBROOT . '/setup.php';            if (!is_file($setupFile)) {                $setupFile = GRAV_WEBROOT . '/' . GRAV_USER_PATH . '/setup.php';            }            if (!is_file($setupFile)) {                $setupFile = null;            }        }        $setup = $setupFile ? (array) include $setupFile : [];        // Add default streams defined in beginning of the class.        if (!isset($setup['streams']['schemes'])) {            $setup['streams']['schemes'] = [];        }        $setup['streams']['schemes'] += $this->streams;        // Initialize class.        parent::__construct($setup);        $this->def('environment', static::$environment);        // Figure out path for the current environment.        $envPath = defined('GRAV_ENVIRONMENT_PATH') ? GRAV_ENVIRONMENT_PATH : (getenv('GRAV_ENVIRONMENT_PATH') ?: null);        if (null === $envPath) {            // Find common path for all environments and append current environment into it.            $envPath = defined('GRAV_ENVIRONMENTS_PATH') ? GRAV_ENVIRONMENTS_PATH : (getenv('GRAV_ENVIRONMENTS_PATH') ?: null);            if (null !== $envPath) {                $envPath .= '/';            } else {                // Use default location. Start with Grav 1.7 default.                $envPath = GRAV_WEBROOT. '/' . GRAV_USER_PATH . '/env';                if (is_dir($envPath)) {                    $envPath = 'user://env/';                } else {                    // Fallback to Grav 1.6 default.                    $envPath = 'user://';                }            }            $envPath .= $this->get('environment');        }        // Set up environment.        $this->def('environment', static::$environment);        $this->def('streams.schemes.environment.prefixes', ['' => [$envPath]]);    }    /**     * @return $this     * @throws RuntimeException     * @throws InvalidArgumentException     */    public function init()    {        $locator = new UniformResourceLocator(GRAV_WEBROOT);        $files = [];        $guard = 5;        do {            $check = $files;            $this->initializeLocator($locator);            $files = $locator->findResources('config://streams.yaml');            if ($check === $files) {                break;            }            // Update streams.            foreach (array_reverse($files) as $path) {                $file = CompiledYamlFile::instance($path);                $content = (array)$file->content();                if (!empty($content['schemes'])) {                    $this->items['streams']['schemes'] = $content['schemes'] + $this->items['streams']['schemes'];                }            }        } while (--$guard);        if (!$guard) {            throw new RuntimeException('Setup: Configuration reload loop detected!');        }        // Make sure we have valid setup.        $this->check($locator);        return $this;    }    /**     * Initialize resource locator by using the configuration.     *     * @param UniformResourceLocator $locator     * @return void     * @throws BadMethodCallException     */    public function initializeLocator(UniformResourceLocator $locator)    {        $locator->reset();        $schemes = (array) $this->get('streams.schemes', []);        foreach ($schemes as $scheme => $config) {            if (isset($config['paths'])) {                $locator->addPath($scheme, '', $config['paths']);            }            $override = $config['override'] ?? false;            $force = $config['force'] ?? false;            if (isset($config['prefixes'])) {                foreach ((array)$config['prefixes'] as $prefix => $paths) {                    $locator->addPath($scheme, $prefix, $paths, $override, $force);                }            }        }    }    /**     * Get available streams and their types from the configuration.     *     * @return array     */    public function getStreams()    {        $schemes = [];        foreach ((array) $this->get('streams.schemes') as $scheme => $config) {            $type = $config['type'] ?? 'ReadOnlyStream';            if ($type[0] !== '\\') {                $type = '\\RocketTheme\\Toolbox\\StreamWrapper\\' . $type;            }            $schemes[$scheme] = $type;        }        return $schemes;    }    /**     * @param UniformResourceLocator $locator     * @return void     * @throws InvalidArgumentException     * @throws BadMethodCallException     * @throws RuntimeException     */    protected function check(UniformResourceLocator $locator)    {        $streams = $this->items['streams']['schemes'] ?? null;        if (!is_array($streams)) {            throw new InvalidArgumentException('Configuration is missing streams.schemes!');        }        $diff = array_keys(array_diff_key($this->streams, $streams));        if ($diff) {            throw new InvalidArgumentException(                sprintf('Configuration is missing keys %s from streams.schemes!', implode(', ', $diff))            );        }        try {            // If environment is found, remove all missing override locations (B/C compatibility).            if ($locator->findResource('environment://', true)) {                $force = $this->get('streams.schemes.environment.force', false);                if (!$force) {                    $prefixes = $this->get('streams.schemes.environment.prefixes.');                    $update = false;                    foreach ($prefixes as $i => $prefix) {                        if ($locator->isStream($prefix)) {                            if ($locator->findResource($prefix, true)) {                                break;                            }                        } elseif (file_exists($prefix)) {                            break;                        }                        unset($prefixes[$i]);                        $update = true;                    }                    if ($update) {                        $this->set('streams.schemes.environment.prefixes', ['' => array_values($prefixes)]);                        $this->initializeLocator($locator);                    }                }            }            if (!$locator->findResource('environment://config', true)) {                // If environment does not have its own directory, remove it from the lookup.                $prefixes = $this->get('streams.schemes.environment.prefixes');                $prefixes['config'] = [];                $this->set('streams.schemes.environment.prefixes', $prefixes);                $this->initializeLocator($locator);            }            // Create security.yaml salt if it doesn't exist into existing configuration environment if possible.            $securityFile = Utils::basename(static::$securityFile);            $securityFolder = substr(static::$securityFile, 0, -\strlen($securityFile));            $securityFolder = $locator->findResource($securityFolder, true) ?: $locator->findResource($securityFolder, true, true);            $filename = "{$securityFolder}/{$securityFile}";            $security_file = CompiledYamlFile::instance($filename);            $security_content = (array)$security_file->content();            if (!isset($security_content['salt'])) {                $security_content = array_merge($security_content, ['salt' => Utils::generateRandomString(14)]);                $security_file->content($security_content);                $security_file->save();                $security_file->free();            }        } catch (RuntimeException $e) {            throw new RuntimeException(sprintf('Grav failed to initialize: %s', $e->getMessage()), 500, $e);        }    }}
 |