core/base/Boot.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php

namespace luya\base;

use luya\console\Application as ConsoleApplication;
use luya\Exception;
use luya\helpers\ArrayHelper;
use luya\web\Application as WebApplication;
use ReflectionClass;
use Yii;

/**
 * LUYA Boot wrapper.
 *
 * Run a LUYA Application based on the current enviroment which is determined trough get_sapi_name(). To run an application
 * a config file with LUYA configuration must be provided via `$configFile` property. By default LUYA will try to find
 * the default config `../configs/env.php`.
 *
 * @author Basil Suter <basil@nadar.io>
 * @since 1.0.0
 */
abstract class Boot
{
    /**
     * @var string The current LUYA version (see: https://github.com/luyadev/luya/blob/master/core/CHANGELOG.md)
     */
    public const VERSION = '2.3.2';

    /**
     * @var string The path to the config file, which returns an array containing you configuration.
     */
    public $configFile = '../configs/env.php';

    /**
     * @var \luya\web\Application|\luya\console\Application The application object.
     */
    public $app;

    /**
     * @var bool When enabled the boot process will not return/echo something, but the variabled will contain the Application object.
     */
    public $mockOnly = false;

    /**
     * @var string Path to the Yii.php file.
     */
    private $_baseYiiFile;

    /**
     * Setter method for the base Yii file.
     *
     * Example path to the yii base file:
     *
     * ```php
     * $boot->setYiiPath(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');
     * ```
     *
     * @param string $baseYiiFile The path to the Yii.php file.
     */
    public function setBaseYiiFile($baseYiiFile)
    {
        $this->_baseYiiFile = $baseYiiFile;
    }

    /**
     * Getter method for Yii base file.
     *
     * @return string
     */
    public function getBaseYiiFile()
    {
        return $this->_baseYiiFile;
    }

    private $_isCli;

    /**
     * Getter method whether current request is cli or not.
     *
     * If not set via setIsCli() the value is determined trough php_sapi_name();
     *
     * @return boolean Whether current request is console env or not.
     * @since 1.0.12
     */
    public function getIsCli()
    {
        if ($this->_isCli === null) {
            $this->_isCli = $this->getSapiName() === 'cli';
        }

        return $this->_isCli;
    }

    /**
     * Setter method for isCli.
     *
     * @param boolean $isCli
     * @since 1.0.12
     */
    public function setIsCli($isCli)
    {
        $this->_isCli = $isCli;
    }

    /**
     * Returns the current sapi name in lower case.
     *
     * @return string e.g. cli or web
     */
    public function getSapiName()
    {
        return strtolower(php_sapi_name());
    }

    private $_configArray;

    /**
     * This method allows you to directly inject a configuration array instead of using the config file
     * method.
     *
     * This method is commonly used when running php unit tests which do not require an additional file.
     *
     * ```php
     * $app = new Boot();
     * $app->setConfigArray([
     *     // ...
     * ]);
     * ```
     *
     * @param array $config The configuration array for the application.
     */
    public function setConfigArray(array $config)
    {
        $this->_configArray = $config;
    }

    /**
     * The prependConfigArray will be merged into the config, this way you can prepand config values for a custom Boot class.
     *
     * > When using prependConfig inside a custom boot class, the custom boot class will not used in the vendor bin file `./vendor/bin/luya`,
     * > so make sure to generate your own bin file.
     *
     * @return array
     */
    public function prependConfigArray()
    {
        return [];
    }

    /**
     * Get the config array from the configFile path with the predefined values.
     *
     * @throws \luya\Exception Throws exception if the config file does not exists.
     * @return array The array which will be injected into the Application Constructor.
     */
    public function getConfigArray()
    {
        if ($this->_configArray === null) {
            if (!file_exists($this->configFile)) {
                if (!$this->getIsCli()) {
                    throw new Exception("Unable to load the config file '".$this->configFile."'.");
                }

                $config = ['id' => 'consoleapp', 'basePath' => dirname(__DIR__)];
            } else {
                $config = require $this->configFile;
            }

            if (!is_array($config)) {
                throw new Exception("config file '".$this->configFile."' found but no array returning.");
            }

            // preset the values from the defaultConfigArray
            if (!empty($this->prependConfigArray())) {
                $config = ArrayHelper::merge($config, $this->prependConfigArray());
            }

            $this->_configArray = $config;
        }

        return $this->_configArray;
    }

    /**
     * Run the application based on the Sapi Name.
     *
     * @return \luya\web\Application|\luya\console\Application Application objected based on the sapi name.
     */
    public function run()
    {
        if ($this->getIsCli()) {
            return $this->applicationConsole();
        }

        return $this->applicationWeb();
    }

    /**
     * Run Cli-Application based on the provided config file.
     */
    public function applicationConsole()
    {
        $this->setIsCli(true);
        $config = $this->getConfigArray();
        $config['defaultRoute'] = 'help';
        if (isset($config['components'])) {
            if (isset($config['components']['composition'])) {
                unset($config['components']['composition']);
            }
        }

        $this->includeYii();

        $baseUrl = null;
        if (isset($config['consoleBaseUrl'])) {
            $baseUrl = $config['consoleBaseUrl'];
        } elseif (isset($config['consoleHostInfo'])) {
            $baseUrl = '/';
        }

        $mergedConfig = ArrayHelper::merge($config, [
            'bootstrap' => ['luya\console\Bootstrap'],
            'components' => [
                'urlManager' => [
                    'class' => 'yii\web\UrlManager',
                    'enablePrettyUrl' => true,
                    'showScriptName' => false,
                    'baseUrl' => $baseUrl,
                    'hostInfo' => $config['consoleHostInfo'] ?? null,
                ],
            ],
        ]);
        $this->app = new ConsoleApplication($mergedConfig);

        if (!$this->mockOnly) {
            exit($this->app->run());
        }
    }

    /**
     * Run Web-Application based on the provided config file.
     */
    public function applicationWeb()
    {
        $config = $this->getConfigArray();
        $this->includeYii();
        $mergedConfig = ArrayHelper::merge($config, ['bootstrap' => ['luya\web\Bootstrap']]);
        $this->app = new WebApplication($mergedConfig);

        if (!$this->mockOnly) {
            return $this->app->run();
        }
    }

    /**
     * Returns the path to luya core files
     *
     * @return string The base path to the luya core folder.
     */
    public function getCoreBasePath()
    {
        $reflector = new ReflectionClass(get_class($this));
        return dirname($reflector->getFileName());
    }

    /**
     * Helper method to check whether the provided Yii Base file exists, if yes include and
     * return the file.
     *
     * @return bool Return value based on require_once command.
     * @throws Exception Throws Exception if the YiiBase file does not exists.
     */
    private function includeYii()
    {
        if (file_exists($this->_baseYiiFile)) {
            defined('LUYA_YII_VENDOR') ?: define('LUYA_YII_VENDOR', dirname($this->_baseYiiFile));

            $baseYiiFolder = LUYA_YII_VENDOR . DIRECTORY_SEPARATOR;
            $luyaYiiFile = $this->getCoreBasePath() . DIRECTORY_SEPARATOR .  'Yii.php';

            if (file_exists($luyaYiiFile)) {
                require_once($baseYiiFolder . 'BaseYii.php');
                require_once($luyaYiiFile);
            } else {
                require_once($baseYiiFolder . 'Yii.php');
            }

            Yii::setAlias('@luya', $this->getCoreBasePath());

            return true;
        }

        throw new Exception("YiiBase file does not exits '".$this->_baseYiiFile."'.");
    }
}