├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── psalm80.xml ├── rector.php └── src ├── ApplicationRunner.php ├── BootstrapRunner.php └── RunnerInterface.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Yii Application Runner Change Log 2 | 3 | ## 2.2.1 under development 4 | 5 | - Chg #68: Change PHP constraint in `composer.json` to `8.0 - 8.4` (@vjik) 6 | 7 | ## 2.2.0 March 09, 2024 8 | 9 | - New #58: Add ability to set custom config merge plan file path, config and vendor directories (@vjik) 10 | 11 | ## 2.1.0 December 06, 2023 12 | 13 | - New #45: Add ability to set custom config modifiers (@vjik) 14 | 15 | ## 2.0.0 February 19, 2023 16 | 17 | - New #38, #39: Add ability to configure all config group names (@vjik) 18 | - New #39: Add parameter `$checkEvents` to `ApplicationRunner` constructor (@vjik) 19 | - Chg #39: Adapt to Yii configuration groups names convention (@vjik) 20 | - Chg #39: Remove `ConfigFactory`, instead it move code to `ApplicationRunner::createDefaultConfig()` method (@vjik) 21 | - Chg #39: Remove methods `withBootstrap()`, `withoutBootstrap()`, `withCheckingEvents()`, `withoutCheckingEvents()` 22 | from `ApplicationRunner` (@vjik) 23 | - Chg #41: Raise `yiisoft/yii-event` required version to `^2.0` (@vjik) 24 | - Сhg #37: Remove parameters in `ApplicationRunner` methods `runBootstrap()` and `checkEvents()`, instead are used 25 | internal container and config instances (@vjik) 26 | - Enh #39: Make methods `ApplicationRunner::getConfig()` and `ApplicationRunner::getContainer()` public (@vjik) 27 | 28 | ## 1.2.1 November 07, 2022 29 | 30 | - Enh #26: Add support for `yiisoft/definitions` version `^3.0` (@vjik) 31 | 32 | ## 1.2.0 July 29, 2022 33 | 34 | - Chg #21: Store config inside DI container (@xepozz) 35 | 36 | ## 1.1.2 June 27, 2022 37 | 38 | - Enh #19: Add support for `psr/container` version `^2.0` (@vjik) 39 | 40 | ## 1.1.1 June 17, 2022 41 | 42 | - Enh #15: Add support for `yiisoft/definitions` version `^2.0` (@vjik) 43 | 44 | ## 1.1.0 April 18, 2022 45 | 46 | - New #10: Add container's tags support (@xepozz) 47 | 48 | ## 1.0.0 January 17, 2022 49 | 50 | - Initial release. 51 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright © 2008 by Yii Software () 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the 13 | distribution. 14 | * Neither the name of Yii Software nor the names of its 15 | contributors may be used to endorse or promote products derived 16 | from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 21 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 22 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 28 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Yii 4 | 5 |

Yii Application Runner

6 |
7 |

8 | 9 | [![Latest Stable Version](https://poser.pugx.org/yiisoft/yii-runner/v/stable.png)](https://packagist.org/packages/yiisoft/yii-runner) 10 | [![Total Downloads](https://poser.pugx.org/yiisoft/yii-runner/downloads.png)](https://packagist.org/packages/yiisoft/yii-runner) 11 | [![Build status](https://github.com/yiisoft/yii-runner/workflows/build/badge.svg)](https://github.com/yiisoft/yii-runner/actions?query=workflow%3Abuild) 12 | [![Code Coverage](https://codecov.io/gh/yiisoft/yii-runner/branch/master/graph/badge.svg)](https://codecov.io/gh/yiisoft/yii-runner) 13 | [![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fyiisoft%2Fyii-runner%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/yiisoft/yii-runner/master) 14 | [![static analysis](https://github.com/yiisoft/yii-runner/workflows/static%20analysis/badge.svg)](https://github.com/yiisoft/yii-runner/actions?query=workflow%3A%22static+analysis%22) 15 | [![type-coverage](https://shepherd.dev/github/yiisoft/yii-runner/coverage.svg)](https://shepherd.dev/github/yiisoft/yii-runner) 16 | 17 | The package defines Yii Application Runner. A runner hides application initialization details making configuration 18 | process easier. 19 | 20 | ## Requirements 21 | 22 | - PHP 8.0 or higher. 23 | 24 | ## Installation 25 | 26 | The package could be installed with [Composer](https://getcomposer.org): 27 | 28 | ```shell 29 | composer require yiisoft/yii-runner 30 | ``` 31 | 32 | ## General usage 33 | 34 | Install one of the adapters: 35 | 36 | - [Console](https://github.com/yiisoft/yii-runner-console) 37 | - [HTTP](https://github.com/yiisoft/yii-runner-http) 38 | - [RoadRunner](https://github.com/yiisoft/yii-runner-roadrunner) 39 | 40 | Instantiate and run it in an entry script: 41 | 42 | ```php 43 | use Yiisoft\Yii\Runner\Http\HttpApplicationRunner; 44 | 45 | require_once __DIR__ . '/autoload.php'; 46 | 47 | (new HttpApplicationRunner( 48 | rootPath: __DIR__, 49 | debug: $_ENV['YII_DEBUG'], 50 | environment: $_ENV['YII_ENV'] 51 | ))->run(); 52 | ``` 53 | 54 | ## Documentation 55 | 56 | - [Internals](docs/internals.md) 57 | 58 | If you need help or have a question, the [Yii Forum](https://forum.yiiframework.com/c/yii-3-0/63) is a good place for that. 59 | You may also check out other [Yii Community Resources](https://www.yiiframework.com/community). 60 | 61 | ## License 62 | 63 | The Yii Application Runner is free software. It is released under the terms of the BSD License. 64 | Please see [`LICENSE`](./LICENSE.md) for more information. 65 | 66 | Maintained by [Yii Software](https://www.yiiframework.com/). 67 | 68 | ## Support the project 69 | 70 | [![Open Collective](https://img.shields.io/badge/Open%20Collective-sponsor-7eadf1?logo=open%20collective&logoColor=7eadf1&labelColor=555555)](https://opencollective.com/yiisoft) 71 | 72 | ## Follow updates 73 | 74 | [![Official website](https://img.shields.io/badge/Powered_by-Yii_Framework-green.svg?style=flat)](https://www.yiiframework.com/) 75 | [![Twitter](https://img.shields.io/badge/twitter-follow-1DA1F2?logo=twitter&logoColor=1DA1F2&labelColor=555555?style=flat)](https://twitter.com/yiiframework) 76 | [![Telegram](https://img.shields.io/badge/telegram-join-1DA1F2?style=flat&logo=telegram)](https://t.me/yii3en) 77 | [![Facebook](https://img.shields.io/badge/facebook-join-1DA1F2?style=flat&logo=facebook&logoColor=ffffff)](https://www.facebook.com/groups/yiitalk) 78 | [![Slack](https://img.shields.io/badge/slack-join-1DA1F2?style=flat&logo=slack)](https://yiiframework.com/go/slack) 79 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yiisoft/yii-runner", 3 | "type": "library", 4 | "description": "Yii application runner", 5 | "keywords": [ 6 | "yii3", 7 | "runner" 8 | ], 9 | "homepage": "https://www.yiiframework.com/", 10 | "license": "BSD-3-Clause", 11 | "support": { 12 | "issues": "https://github.com/yiisoft/yii-runner/issues?state=open", 13 | "source": "https://github.com/yiisoft/yii-runner", 14 | "forum": "https://www.yiiframework.com/forum/", 15 | "wiki": "https://www.yiiframework.com/wiki/", 16 | "irc": "ircs://irc.libera.chat:6697/yii", 17 | "chat": "https://t.me/yii3en" 18 | }, 19 | "funding": [ 20 | { 21 | "type": "opencollective", 22 | "url": "https://opencollective.com/yiisoft" 23 | }, 24 | { 25 | "type": "github", 26 | "url": "https://github.com/sponsors/yiisoft" 27 | } 28 | ], 29 | "require": { 30 | "php": "8.0 - 8.4", 31 | "psr/container": "^1.0 || ^2.0", 32 | "yiisoft/config": "^1.1", 33 | "yiisoft/definitions": "^1.0 || ^2.0 || ^3.0", 34 | "yiisoft/di": "^1.0", 35 | "yiisoft/yii-event": "^2.0" 36 | }, 37 | "require-dev": { 38 | "maglnet/composer-require-checker": "^4.4", 39 | "phpunit/phpunit": "^9.6.22", 40 | "rector/rector": "^2.0.11", 41 | "roave/infection-static-analysis-plugin": "^1.25", 42 | "spatie/phpunit-watcher": "^1.23.6", 43 | "vimeo/psalm": "^4.30 || ^5.26.1 || ^6.10", 44 | "yiisoft/test-support": "^3.0.2" 45 | }, 46 | "suggest": { 47 | "yiisoft/yii-runner-console": "Allows to run console application", 48 | "yiisoft/yii-runner-http": "Allows to run HTTP application", 49 | "yiisoft/yii-runner-roadrunner": "Allows to run HTTP application for RoadRunner" 50 | }, 51 | "autoload": { 52 | "psr-4": { 53 | "Yiisoft\\Yii\\Runner\\": "src" 54 | } 55 | }, 56 | "autoload-dev": { 57 | "psr-4": { 58 | "Yiisoft\\Yii\\Runner\\Tests\\": "tests" 59 | } 60 | }, 61 | "extra": { 62 | "config-plugin-options": { 63 | "build-merge-plan": false 64 | } 65 | }, 66 | "config": { 67 | "sort-packages": true, 68 | "allow-plugins": { 69 | "infection/extension-installer": true, 70 | "composer/package-versions-deprecated": true, 71 | "yiisoft/config": true 72 | } 73 | }, 74 | "scripts": { 75 | "test": "phpunit --testdox --no-interaction", 76 | "test-watch": "phpunit-watcher watch" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /psalm80.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /rector.php: -------------------------------------------------------------------------------- 1 | paths([ 12 | __DIR__ . '/src', 13 | __DIR__ . '/tests', 14 | ]); 15 | 16 | // register a single rule 17 | $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class); 18 | 19 | // define sets of rules 20 | $rectorConfig->sets([ 21 | LevelSetList::UP_TO_PHP_80, 22 | ]); 23 | 24 | $rectorConfig->skip([ 25 | ClosureToArrowFunctionRector::class, 26 | ]); 27 | }; 28 | -------------------------------------------------------------------------------- /src/ApplicationRunner.php: -------------------------------------------------------------------------------- 1 | $nestedParamsGroups 52 | * @psalm-param list $nestedEventsGroups 53 | * @psalm-param list $configModifiers 54 | */ 55 | public function __construct( 56 | protected string $rootPath, 57 | protected bool $debug, 58 | protected bool $checkEvents, 59 | protected ?string $environment, 60 | protected string $bootstrapGroup, 61 | protected string $eventsGroup, 62 | protected string $diGroup, 63 | protected string $diProvidersGroup, 64 | protected string $diDelegatesGroup, 65 | protected string $diTagsGroup, 66 | protected string $paramsGroup, 67 | protected array $nestedParamsGroups, 68 | protected array $nestedEventsGroups, 69 | protected array $configModifiers = [], 70 | protected string $configDirectory = 'config', 71 | protected string $vendorDirectory = 'vendor', 72 | protected string $configMergePlanFile = '.merge-plan.php', 73 | ) { 74 | } 75 | 76 | abstract public function run(): void; 77 | 78 | /** 79 | * Returns a new instance with the specified config instance {@see ConfigInterface}. 80 | * 81 | * @param ConfigInterface $config The config instance. 82 | */ 83 | final public function withConfig(ConfigInterface $config): static 84 | { 85 | $new = clone $this; 86 | $new->config = $config; 87 | return $new; 88 | } 89 | 90 | /** 91 | * Returns a new instance with the specified container instance {@see ContainerInterface}. 92 | * 93 | * @param ContainerInterface $container The container instance. 94 | */ 95 | final public function withContainer(ContainerInterface $container): static 96 | { 97 | $new = clone $this; 98 | $new->container = $container; 99 | return $new; 100 | } 101 | 102 | /** 103 | * @throws ErrorException|RuntimeException 104 | */ 105 | final protected function runBootstrap(): void 106 | { 107 | $bootstrapList = $this->getConfiguration($this->bootstrapGroup); 108 | if (empty($bootstrapList)) { 109 | return; 110 | } 111 | 112 | (new BootstrapRunner($this->getContainer(), $bootstrapList))->run(); 113 | } 114 | 115 | /** 116 | * @throws ContainerExceptionInterface|ErrorException|NotFoundExceptionInterface 117 | */ 118 | final protected function checkEvents(): void 119 | { 120 | if ( 121 | $this->checkEvents 122 | && null !== $configuration = $this->getConfiguration($this->eventsGroup) 123 | ) { 124 | /** @psalm-suppress MixedMethodCall */ 125 | $this->getContainer() 126 | ->get(ListenerConfigurationChecker::class) 127 | ->check($configuration); 128 | } 129 | } 130 | 131 | /** 132 | * @throws ErrorException 133 | */ 134 | final public function getConfig(): ConfigInterface 135 | { 136 | return $this->config ??= $this->createDefaultConfig(); 137 | } 138 | 139 | /** 140 | * @throws ErrorException|InvalidConfigException 141 | */ 142 | final public function getContainer(): ContainerInterface 143 | { 144 | $this->container ??= $this->createDefaultContainer(); 145 | 146 | if ($this->container instanceof Container) { 147 | return $this->container->get(ContainerInterface::class); 148 | } 149 | 150 | return $this->container; 151 | } 152 | 153 | final protected function getConfiguration(string $name): ?array 154 | { 155 | $config = $this->getConfig(); 156 | return $config->has($name) ? $config->get($name) : null; 157 | } 158 | 159 | /** 160 | * @throws ErrorException 161 | */ 162 | private function createDefaultConfig(): Config 163 | { 164 | $paramsGroups = [$this->paramsGroup, ...$this->nestedParamsGroups]; 165 | $eventsGroups = [$this->eventsGroup, ...$this->nestedEventsGroups]; 166 | 167 | return new Config( 168 | new ConfigPaths($this->rootPath, $this->configDirectory, $this->vendorDirectory), 169 | $this->environment, 170 | [ 171 | ReverseMerge::groups(...$eventsGroups), 172 | RecursiveMerge::groups(...$paramsGroups, ...$eventsGroups), 173 | ...$this->configModifiers, 174 | ], 175 | $this->paramsGroup, 176 | $this->configMergePlanFile, 177 | ); 178 | } 179 | 180 | /** 181 | * @throws ErrorException|InvalidConfigException 182 | */ 183 | private function createDefaultContainer(): Container 184 | { 185 | $containerConfig = ContainerConfig::create()->withValidate($this->debug); 186 | 187 | $config = $this->getConfig(); 188 | 189 | if (null !== $definitions = $this->getConfiguration($this->diGroup)) { 190 | $containerConfig = $containerConfig->withDefinitions($definitions); 191 | } 192 | 193 | if (null !== $providers = $this->getConfiguration($this->diProvidersGroup)) { 194 | $containerConfig = $containerConfig->withProviders($providers); 195 | } 196 | 197 | if (null !== $delegates = $this->getConfiguration($this->diDelegatesGroup)) { 198 | $containerConfig = $containerConfig->withDelegates($delegates); 199 | } 200 | 201 | if (null !== $tags = $this->getConfiguration($this->diTagsGroup)) { 202 | $containerConfig = $containerConfig->withTags($tags); 203 | } 204 | 205 | $containerConfig = $containerConfig->withDefinitions( 206 | array_merge($containerConfig->getDefinitions(), [ConfigInterface::class => $config]) 207 | ); 208 | 209 | return new Container($containerConfig); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/BootstrapRunner.php: -------------------------------------------------------------------------------- 1 | bootstrapList as $callback) { 31 | if (!is_callable($callback)) { 32 | throw new RuntimeException( 33 | sprintf( 34 | 'Bootstrap callback must be callable, "%s" given.', 35 | get_debug_type($callback), 36 | ) 37 | ); 38 | } 39 | 40 | $callback($this->container); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/RunnerInterface.php: -------------------------------------------------------------------------------- 1 |