├── tests ├── Support │ ├── Data │ │ └── .gitkeep │ ├── _generated │ │ └── .gitignore │ ├── WebTester.php │ ├── UnitTester.php │ ├── ConsoleTester.php │ └── FunctionalTester.php ├── .gitignore ├── Functional.suite.yml ├── Console.suite.yml ├── Unit.suite.yml ├── bootstrap.php ├── Web.suite.yml ├── Console │ ├── YiiCest.php │ └── HelloCest.php ├── Web │ ├── HomePageCest.php │ └── NotFoundHandlerCest.php ├── Unit │ └── EnvironmentTest.php └── Functional │ └── HomePageCest.php ├── config ├── .gitignore ├── web │ ├── params.php │ └── di │ │ ├── psr17.php │ │ └── application.php ├── environments │ ├── prod │ │ └── params.php │ ├── test │ │ └── params.php │ └── dev │ │ └── params.php ├── console │ ├── commands.php │ └── params.php ├── common │ ├── application.php │ ├── bootstrap.php │ ├── routes.php │ ├── aliases.php │ ├── di │ │ ├── application.php │ │ ├── logger.php │ │ ├── router.php │ │ └── error-handler.php │ └── params.php └── configuration.php ├── docker ├── dev │ ├── .gitignore │ ├── override.env.example │ ├── .env │ └── compose.yml ├── prod │ ├── .gitignore │ ├── .env │ └── compose.yml ├── test │ ├── .gitignore │ ├── .env │ └── compose.yml ├── compose.yml ├── .env └── Dockerfile ├── runtime └── .gitignore ├── public ├── assets │ └── .gitignore ├── robots.txt ├── favicon.ico ├── favicon.svg └── index.php ├── screenshot.png ├── yii.bat ├── .dockerignore ├── src ├── autoload.php ├── Shared │ └── ApplicationParams.php ├── Web │ ├── Shared │ │ └── Layout │ │ │ └── Main │ │ │ ├── MainAsset.php │ │ │ └── layout.php │ ├── HomePage │ │ ├── Action.php │ │ └── template.php │ └── NotFound │ │ ├── template.php │ │ └── NotFoundHandler.php ├── Console │ └── HelloCommand.php └── Environment.php ├── .editorconfig ├── .gitignore ├── yii ├── codeception.yml ├── rector.php ├── .php-cs-fixer.php ├── psalm.xml ├── composer-dependency-analyser.php ├── assets └── main │ └── site.css ├── composer.json └── Makefile /tests/Support/Data/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | /_output 2 | -------------------------------------------------------------------------------- /config/.gitignore: -------------------------------------------------------------------------------- 1 | .merge-plan.php 2 | -------------------------------------------------------------------------------- /docker/dev/.gitignore: -------------------------------------------------------------------------------- 1 | /override.env 2 | -------------------------------------------------------------------------------- /docker/prod/.gitignore: -------------------------------------------------------------------------------- 1 | /override.env 2 | -------------------------------------------------------------------------------- /docker/test/.gitignore: -------------------------------------------------------------------------------- 1 | /override.env 2 | -------------------------------------------------------------------------------- /runtime/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /public/assets/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /tests/Support/_generated/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /tests/Functional.suite.yml: -------------------------------------------------------------------------------- 1 | actor: FunctionalTester 2 | -------------------------------------------------------------------------------- /docker/dev/override.env.example: -------------------------------------------------------------------------------- 1 | APP_HOST_PATH=/projects/yiisoft/app 2 | -------------------------------------------------------------------------------- /docker/prod/.env: -------------------------------------------------------------------------------- 1 | APP_ENV=prod 2 | APP_DEBUG=false 3 | SERVER_NAME=:80 4 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yiisoft/app/HEAD/screenshot.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yiisoft/app/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /config/web/params.php: -------------------------------------------------------------------------------- 1 | 'phpstorm://open?url=file://{file}&line={line}', 7 | ]; 8 | -------------------------------------------------------------------------------- /config/console/commands.php: -------------------------------------------------------------------------------- 1 | Console\HelloCommand::class, 9 | ]; 10 | -------------------------------------------------------------------------------- /docker/compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | app: &appconfig 3 | extra_hosts: 4 | - "host.docker.internal:host-gateway" 5 | 6 | volumes: 7 | caddy_data: 8 | caddy_config: 9 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Ignore everything 2 | * 3 | 4 | # except: 5 | 6 | !/config 7 | !/public 8 | !/src 9 | !autoload.php 10 | !yii 11 | !composer.json 12 | !composer.lock 13 | !.env* 14 | -------------------------------------------------------------------------------- /config/common/application.php: -------------------------------------------------------------------------------- 1 | 'UTF-8', 7 | 'locale' => 'en', 8 | 'name' => 'My Project', 9 | ]; 10 | -------------------------------------------------------------------------------- /src/autoload.php: -------------------------------------------------------------------------------- 1 | [ 7 | 'commands' => require __DIR__ . '/commands.php', 8 | ], 9 | ]; 10 | -------------------------------------------------------------------------------- /config/common/bootstrap.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | return []; 11 | -------------------------------------------------------------------------------- /docker/.env: -------------------------------------------------------------------------------- 1 | STACK_NAME=app 2 | 3 | # 4 | # Development 5 | # 6 | 7 | DEV_PORT=80 8 | 9 | # 10 | # Production 11 | # 12 | 13 | PROD_HOST=app.example.com 14 | PROD_SSH="ssh://docker-web" 15 | 16 | IMAGE=app 17 | IMAGE_TAG=latest 18 | -------------------------------------------------------------------------------- /tests/Web.suite.yml: -------------------------------------------------------------------------------- 1 | actor: WebTester 2 | extensions: 3 | enabled: 4 | - Codeception\Extension\RunProcess: 5 | 0: composer serve 6 | sleep: 3 7 | modules: 8 | enabled: 9 | - PhpBrowser: 10 | url: http://127.0.0.1:8080 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | indent_style = space 10 | indent_size = 4 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | 16 | [*.yml] 17 | indent_size = 2 18 | -------------------------------------------------------------------------------- /src/Shared/ApplicationParams.php: -------------------------------------------------------------------------------- 1 | routes( 12 | Route::get('/') 13 | ->action(Web\HomePage\Action::class) 14 | ->name('home'), 15 | ), 16 | ]; 17 | -------------------------------------------------------------------------------- /tests/Console/YiiCest.php: -------------------------------------------------------------------------------- 1 | runApp(); 14 | $I->canSeeResultCodeIs(0); 15 | $I->seeInShellOutput('Yii Console'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/Console/HelloCest.php: -------------------------------------------------------------------------------- 1 | runApp('hello'); 14 | $I->canSeeResultCodeIs(0); 15 | $I->seeInShellOutput('Hello!'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/Web/HomePageCest.php: -------------------------------------------------------------------------------- 1 | wantTo('home page works.'); 14 | $I->amOnPage('/'); 15 | $I->expectTo('see page home.'); 16 | $I->see('Hello!'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config/common/aliases.php: -------------------------------------------------------------------------------- 1 | dirname(__DIR__, 2), 7 | '@src' => '@root/src', 8 | '@assets' => '@root/public/assets', 9 | '@assetsUrl' => '@baseUrl/assets', 10 | '@assetsSource' => '@root/assets', 11 | '@baseUrl' => '/', 12 | '@public' => '@root/public', 13 | '@runtime' => '@root/runtime', 14 | '@vendor' => '@root/vendor', 15 | ]; 16 | -------------------------------------------------------------------------------- /config/common/di/application.php: -------------------------------------------------------------------------------- 1 | [ 11 | '__construct()' => [ 12 | 'name' => $params['application']['name'], 13 | 'charset' => $params['application']['charset'], 14 | 'locale' => $params['application']['locale'], 15 | ], 16 | ], 17 | ]; 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # phpstorm project files 2 | .idea 3 | 4 | # netbeans project files 5 | nbproject 6 | 7 | # zend studio for eclipse project files 8 | .buildpath 9 | .project 10 | .settings 11 | 12 | # sublime text project / workspace files 13 | *.sublime-project 14 | *.sublime-workspace 15 | 16 | # windows thumbnail cache 17 | Thumbs.db 18 | 19 | # Mac DS_Store Files 20 | .DS_Store 21 | 22 | # composer vendor dir 23 | /vendor 24 | 25 | # Codeception C3 26 | c3.php 27 | -------------------------------------------------------------------------------- /src/Web/Shared/Layout/Main/MainAsset.php: -------------------------------------------------------------------------------- 1 | run(); 19 | -------------------------------------------------------------------------------- /src/Web/HomePage/Action.php: -------------------------------------------------------------------------------- 1 | viewRenderer->render(__DIR__ . '/template'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/Unit/EnvironmentTest.php: -------------------------------------------------------------------------------- 1 | [ 15 | 'class' => Logger::class, 16 | '__construct()' => [ 17 | 'targets' => ReferencesArray::from([ 18 | FileTarget::class, 19 | StreamTarget::class, 20 | ]), 21 | ], 22 | ], 23 | ]; 24 | -------------------------------------------------------------------------------- /config/common/di/router.php: -------------------------------------------------------------------------------- 1 | [ 15 | 'class' => RouteCollection::class, 16 | '__construct()' => [ 17 | 'collector' => DynamicReference::to( 18 | static fn() => (new RouteCollector())->addRoute(...$config->get('routes')), 19 | ), 20 | ], 21 | ], 22 | ]; 23 | -------------------------------------------------------------------------------- /src/Web/HomePage/template.php: -------------------------------------------------------------------------------- 1 | setTitle($applicationParams->name); 14 | ?> 15 | 16 |
17 |

Hello!

18 | 19 |

Let's start something great with Yii3!

20 | 21 |

22 | 23 | Don't forget to check the guide. 24 | 25 |

26 |
27 | -------------------------------------------------------------------------------- /rector.php: -------------------------------------------------------------------------------- 1 | withPaths([ 12 | __DIR__ . '/src', 13 | __DIR__ . '/tests', 14 | ]) 15 | ->withPhpSets(php82: true) 16 | ->withRules([ 17 | InlineConstructorDefaultToPropertyRector::class, 18 | ]) 19 | ->withSkip([ 20 | ClosureToArrowFunctionRector::class, 21 | ReadOnlyPropertyRector::class, 22 | ]); 23 | -------------------------------------------------------------------------------- /src/Console/HelloCommand.php: -------------------------------------------------------------------------------- 1 | writeln('Hello!'); 22 | return ExitCode::OK; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Support/WebTester.php: -------------------------------------------------------------------------------- 1 | in([ 14 | $root . '/config', 15 | $root . '/src', 16 | $root . '/tests', 17 | ]) 18 | ->append([ 19 | $root . '/public/index.php', 20 | ]); 21 | 22 | return (new Config()) 23 | ->setCacheFile(__DIR__ . '/runtime/cache/.php-cs-fixer.cache') 24 | ->setParallelConfig(ParallelConfigFactory::detect()) 25 | ->setRules([ 26 | '@PER-CS2.0' => true, 27 | 'no_unused_imports' => true, 28 | ]) 29 | ->setFinder($finder); 30 | -------------------------------------------------------------------------------- /tests/Support/UnitTester.php: -------------------------------------------------------------------------------- 1 | sendRequest( 18 | new ServerRequest(uri: '/'), 19 | ); 20 | 21 | assertSame(200, $response->getStatusCode()); 22 | assertStringContainsString( 23 | 'Don\'t forget to check the guide', 24 | $response->getBody()->getContents(), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Web/NotFound/template.php: -------------------------------------------------------------------------------- 1 | setTitle('404'); 15 | ?> 16 | 17 |
18 |

19 | 404 20 |

21 | 22 |

23 | The page 24 | getUri()?->getPath() ?? 'unknown') ?> 25 | not found. 26 |

27 | 28 |

29 | The above error occurred while the Web server was processing your request.
30 | Please contact us if you think this is a server error. Thank you. 31 |

32 | 33 |

34 | Go Back Home 35 |

36 |
37 | -------------------------------------------------------------------------------- /docker/prod/compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | app: 3 | image: ${IMAGE}:${IMAGE_TAG} 4 | networks: 5 | - caddy_public 6 | volumes: 7 | - runtime:/app/runtime 8 | - caddy_data:/data 9 | - caddy_config:/config 10 | env_file: 11 | - path: ./prod/.env 12 | - path: ./prod/override.env 13 | required: false 14 | deploy: 15 | replicas: 2 16 | update_config: 17 | delay: 10s 18 | parallelism: 1 19 | order: start-first 20 | failure_action: rollback 21 | monitor: 10s 22 | rollback_config: 23 | parallelism: 0 24 | order: stop-first 25 | restart_policy: 26 | condition: on-failure 27 | delay: 5s 28 | max_attempts: 3 29 | window: 120s 30 | labels: 31 | caddy: ${PROD_HOST:-app.example.com} 32 | caddy.reverse_proxy: "{{upstreams 80}}" 33 | 34 | volumes: 35 | runtime: 36 | 37 | networks: 38 | caddy_public: 39 | external: true 40 | -------------------------------------------------------------------------------- /composer-dependency-analyser.php: -------------------------------------------------------------------------------- 1 | disableComposerAutoloadPathScan() 11 | ->setFileExtensions(['php']) 12 | ->addPathToScan($root . '/src', isDev: false) 13 | ->addPathToScan($root . '/config', isDev: false) 14 | ->addPathToScan($root . '/public/index.php', isDev: false) 15 | ->addPathToScan($root . '/yii', isDev: false) 16 | ->addPathToScan($root . '/tests', isDev: true) 17 | ->ignoreErrorsOnPackage('psr/container', [ErrorType::UNUSED_DEPENDENCY]) 18 | ->ignoreErrorsOnPackage('yiisoft/config', [ErrorType::UNUSED_DEPENDENCY]) 19 | ->ignoreErrorsOnPackage('yiisoft/di', [ErrorType::UNUSED_DEPENDENCY]) 20 | ->ignoreErrorsOnPackage('yiisoft/view', [ErrorType::UNUSED_DEPENDENCY]) 21 | ->ignoreErrorsOnPackage('yiisoft/router-fastroute', [ErrorType::UNUSED_DEPENDENCY]); 22 | -------------------------------------------------------------------------------- /tests/Web/NotFoundHandlerCest.php: -------------------------------------------------------------------------------- 1 | wantTo('see 404 page.'); 14 | $I->amOnPage('/non-existent-page'); 15 | $I->canSeeResponseCodeIs(404); 16 | $I->see('404'); 17 | $I->see('The page /non-existent-page not found.'); 18 | $I->see('The above error occurred while the Web server was processing your request.'); 19 | $I->see('Please contact us if you think this is a server error. Thank you.'); 20 | } 21 | 22 | public function returnHome(WebTester $I): void 23 | { 24 | $I->wantTo('check "Go Back Home" link.'); 25 | $I->amOnPage('/non-existent-page'); 26 | $I->canSeeResponseCodeIs(404); 27 | $I->click('Go Back Home'); 28 | $I->expectTo('see page home.'); 29 | $I->see('Hello!'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Web/NotFound/NotFoundHandler.php: -------------------------------------------------------------------------------- 1 | viewRenderer 26 | ->render(__DIR__ . '/template', ['urlGenerator' => $this->urlGenerator, 'currentRoute' => $this->currentRoute]) 27 | ->withStatus(Status::NOT_FOUND); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Support/ConsoleTester.php: -------------------------------------------------------------------------------- 1 | runShellCommand( 35 | dirname(__DIR__, 2) . '/yii' . ($parameters === null ? '' : (' ' . $parameters)), 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /config/web/di/psr17.php: -------------------------------------------------------------------------------- 1 | RequestFactory::class, 20 | ServerRequestFactoryInterface::class => ServerRequestFactory::class, 21 | ResponseFactoryInterface::class => ResponseFactory::class, 22 | StreamFactoryInterface::class => StreamFactory::class, 23 | UriFactoryInterface::class => UriFactory::class, 24 | UploadedFileFactoryInterface::class => UploadedFileFactory::class, 25 | ]; 26 | -------------------------------------------------------------------------------- /config/common/di/error-handler.php: -------------------------------------------------------------------------------- 1 | [ 14 | '__construct()' => [ 15 | 'traceLink' => static function (string $file, int|null $line) use ($params): string|null { 16 | if (!isset($params['traceLink'])) { 17 | return null; 18 | } 19 | 20 | try { 21 | $hostPath = Environment::appHostPath(); 22 | if ($hostPath !== null) { 23 | /** @var string $file */ 24 | $file = preg_replace('~^(/app/)~', rtrim($hostPath, '\\/') . '/', $file); 25 | } 26 | return str_replace( 27 | ['{file}', '{line}'], 28 | [$file, (string) $line], 29 | $params['traceLink'], 30 | ); 31 | } catch (Throwable) { 32 | return null; 33 | } 34 | }, 35 | ], 36 | ], 37 | ]; 38 | -------------------------------------------------------------------------------- /config/common/params.php: -------------------------------------------------------------------------------- 1 | require __DIR__ . '/application.php', 15 | 16 | 'yiisoft/aliases' => [ 17 | 'aliases' => require __DIR__ . '/aliases.php', 18 | ], 19 | 20 | 'yiisoft/view' => [ 21 | 'basePath' => null, 22 | 'parameters' => [ 23 | 'assetManager' => Reference::to(AssetManager::class), 24 | 'applicationParams' => Reference::to(ApplicationParams::class), 25 | 'aliases' => Reference::to(Aliases::class), 26 | 'urlGenerator' => Reference::to(UrlGeneratorInterface::class), 27 | 'currentRoute' => Reference::to(CurrentRoute::class), 28 | ], 29 | ], 30 | 31 | 'yiisoft/yii-view-renderer' => [ 32 | 'viewPath' => null, 33 | 'layout' => '@src/Web/Shared/Layout/Main/layout.php', 34 | 'injections' => [ 35 | Reference::to(CsrfViewInjection::class), 36 | ], 37 | ], 38 | ]; 39 | -------------------------------------------------------------------------------- /tests/Support/FunctionalTester.php: -------------------------------------------------------------------------------- 1 | runAndGetResponse($request); 46 | 47 | $body = $response->getBody(); 48 | if ($body->isSeekable()) { 49 | $body->rewind(); 50 | } 51 | 52 | return $response; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/main/site.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | html { 6 | height: 100%; 7 | } 8 | body { 9 | display: flex; 10 | flex-direction: column; 11 | height: 100%; 12 | font: 16px/24px "Trebuchet MS", Helvetica, sans-serif; 13 | background: #f8f8f8; 14 | color: #4b4b4b; 15 | } 16 | 17 | .header { 18 | margin: 24px; 19 | padding: 12px; 20 | border-radius: 12px; 21 | background: #fff; 22 | color: #888; 23 | box-shadow: 0 0 24px #f4f4f4; 24 | text-align: center; 25 | } 26 | .header svg { 27 | height: 48px; 28 | } 29 | 30 | .content { 31 | flex-grow: 1; 32 | display: flex; 33 | align-items: center; 34 | } 35 | .content_i { 36 | flex-grow: 1; 37 | padding: 24px 5%; 38 | } 39 | .content a { 40 | color: #00617b; 41 | } 42 | .content a:hover { 43 | color: #1191b3; 44 | } 45 | 46 | .footer { 47 | border-top: 1px solid #e6e8ec; 48 | margin: 24px 24px 0; 49 | padding: 24px; 50 | text-align: center; 51 | } 52 | 53 | .footer_copyright a { 54 | color: #838b99; 55 | text-decoration: none; 56 | } 57 | 58 | .footer_copyright a:hover { 59 | color: #0b7794; 60 | text-decoration: underline; 61 | } 62 | 63 | .footer_icons { 64 | padding-top: 16px; 65 | display: flex; 66 | justify-content: center; 67 | } 68 | 69 | .footer_icons a { 70 | margin: 0 12px; 71 | text-decoration: none; 72 | } 73 | 74 | .footer_icons svg { 75 | height: 32px; 76 | fill: #838b99; 77 | } 78 | 79 | .footer_icons a:hover svg { 80 | fill: #0b7794; 81 | } 82 | 83 | .text-center { 84 | text-align: center; 85 | } 86 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM composer/composer:2-bin AS composer 2 | 3 | FROM dunglas/frankenphp:1-php8.2-bookworm AS base 4 | 5 | RUN apt update && apt -y install \ 6 | unzip 7 | 8 | RUN install-php-extensions \ 9 | opcache \ 10 | mbstring \ 11 | intl \ 12 | dom \ 13 | ctype \ 14 | curl \ 15 | phar \ 16 | openssl \ 17 | xml \ 18 | xmlwriter \ 19 | simplexml \ 20 | pdo 21 | 22 | # 23 | # Development 24 | # 25 | 26 | FROM base AS dev 27 | ARG USER_ID=10001 28 | ARG GROUP_ID=10001 29 | ARG USER_NAME=appuser 30 | ARG GROUP_NAME=appuser 31 | 32 | RUN install-php-extensions \ 33 | xdebug 34 | 35 | COPY --from=composer /composer /usr/bin/composer 36 | 37 | # Based on https://frankenphp.dev/docs/docker/#running-as-a-non-root-user 38 | RUN \ 39 | groupadd --gid ${GROUP_ID} ${GROUP_NAME}; \ 40 | useradd --gid ${GROUP_ID} --uid ${USER_ID} ${GROUP_NAME}; \ 41 | # Add additional capability to bind to port 80 and 443 \ 42 | setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \ 43 | # Give write access to /data/caddy and /config/caddy \ 44 | chown -R ${USER_NAME}:${GROUP_NAME} /data/caddy && chown -R ${USER_NAME}:${GROUP_NAME} /config/caddy 45 | USER ${USER_NAME} 46 | 47 | # 48 | # Production 49 | # 50 | 51 | FROM base AS prod-builder 52 | COPY --from=composer /composer /usr/bin/composer 53 | COPY .. /app 54 | RUN --mount=type=cache,target=/tmp/cache \ 55 | composer install --no-dev --no-progress --no-interaction --classmap-authoritative && \ 56 | rm composer.lock composer.json 57 | 58 | FROM base AS prod 59 | ENV APP_ENV=prod 60 | COPY --from=prod-builder --chown=www-data:www-data /app /app 61 | USER www-data 62 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | setLevels([ 50 | LogLevel::EMERGENCY, 51 | LogLevel::ERROR, 52 | LogLevel::WARNING, 53 | ]), 54 | ], 55 | ), 56 | new HtmlRenderer(), 57 | ), 58 | ); 59 | $runner->run(); 60 | -------------------------------------------------------------------------------- /config/configuration.php: -------------------------------------------------------------------------------- 1 | [ 10 | 'params' => 'common/params.php', 11 | 'params-web' => [ 12 | '$params', 13 | 'web/params.php', 14 | ], 15 | 'params-console' => [ 16 | '$params', 17 | 'console/params.php', 18 | ], 19 | 'di' => 'common/di/*.php', 20 | 'di-web' => [ 21 | '$di', 22 | 'web/di/*.php', 23 | ], 24 | 'di-console' => '$di', 25 | 'di-delegates' => [], 26 | 'di-delegates-console' => '$di-delegates', 27 | 'di-delegates-web' => '$di-delegates', 28 | 'di-providers' => [], 29 | 'di-providers-web' => [ 30 | '$di-providers', 31 | ], 32 | 'di-providers-console' => [ 33 | '$di-providers', 34 | ], 35 | 'events' => [], 36 | 'events-web' => ['$events'], 37 | 'events-console' => ['$events'], 38 | 'routes' => 'common/routes.php', 39 | 'bootstrap' => 'common/bootstrap.php', 40 | 'bootstrap-web' => '$bootstrap', 41 | 'bootstrap-console' => '$bootstrap', 42 | ], 43 | 'config-plugin-environments' => [ 44 | Environment::DEV => [ 45 | 'params' => [ 46 | 'environments/dev/params.php', 47 | ], 48 | ], 49 | Environment::TEST => [ 50 | 'params' => [ 51 | 'environments/test/params.php', 52 | ], 53 | ], 54 | Environment::PROD => [ 55 | 'params' => [ 56 | 'environments/prod/params.php', 57 | ], 58 | ], 59 | ], 60 | 'config-plugin-options' => [ 61 | 'source-directory' => 'config', 62 | ], 63 | ]; 64 | -------------------------------------------------------------------------------- /config/web/di/application.php: -------------------------------------------------------------------------------- 1 | [ 25 | '__construct()' => [ 26 | 'dispatcher' => DynamicReference::to([ 27 | 'class' => MiddlewareDispatcher::class, 28 | 'withMiddlewares()' => [ 29 | [ 30 | ErrorCatcher::class, 31 | SessionMiddleware::class, 32 | CsrfTokenMiddleware::class, 33 | FormatDataResponse::class, 34 | RequestCatcherMiddleware::class, 35 | Router::class, 36 | ], 37 | ], 38 | ]), 39 | 'fallbackHandler' => Reference::to(NotFoundHandler::class), 40 | ], 41 | ], 42 | 43 | ParametersResolverInterface::class => [ 44 | 'class' => CompositeParametersResolver::class, 45 | '__construct()' => [ 46 | Reference::to(HydratorAttributeParametersResolver::class), 47 | Reference::to(RequestInputParametersResolver::class), 48 | ], 49 | ], 50 | ]; 51 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yiisoft/app", 3 | "type": "project", 4 | "description": "Yii3 web application template", 5 | "keywords": [ 6 | "yii3", 7 | "app" 8 | ], 9 | "license": "BSD-3-Clause", 10 | "support": { 11 | "issues": "https://github.com/yiisoft/app/issues?state=open", 12 | "source": "https://github.com/yiisoft/app", 13 | "forum": "https://www.yiiframework.com/forum/", 14 | "wiki": "https://www.yiiframework.com/wiki/", 15 | "irc": "ircs://irc.libera.chat:6697/yii", 16 | "chat": "https://t.me/yii3en" 17 | }, 18 | "funding": [ 19 | { 20 | "type": "opencollective", 21 | "url": "https://opencollective.com/yiisoft" 22 | }, 23 | { 24 | "type": "github", 25 | "url": "https://github.com/sponsors/yiisoft" 26 | } 27 | ], 28 | "scripts": { 29 | "serve": [ 30 | "Composer\\Config::disableProcessTimeout", 31 | "@php ./yii serve" 32 | ], 33 | "test": "codecept run" 34 | }, 35 | "require": { 36 | "php": "8.2 - 8.5", 37 | "ext-filter": "*", 38 | "httpsoft/http-message": "^1.1.6", 39 | "psr/container": "^2.0.2", 40 | "psr/http-factory": "^1.1", 41 | "psr/http-message": "^2.0", 42 | "psr/http-server-handler": "^1.0.2", 43 | "psr/log": "^3.0.2", 44 | "symfony/console": "^7.4.1", 45 | "yiisoft/aliases": "^3.1.1", 46 | "yiisoft/assets": "^5.1.2", 47 | "yiisoft/config": "^1.6.2", 48 | "yiisoft/csrf": "^2.2.3", 49 | "yiisoft/data-response": "^2.1.2", 50 | "yiisoft/definitions": "^3.4.1", 51 | "yiisoft/di": "^1.4.1", 52 | "yiisoft/error-handler": "^4.3.1", 53 | "yiisoft/html": "^3.12", 54 | "yiisoft/http": "^1.3", 55 | "yiisoft/input-http": "^1.0.1", 56 | "yiisoft/log": "^2.2.0", 57 | "yiisoft/log-target-file": "^3.1", 58 | "yiisoft/middleware-dispatcher": "^5.4", 59 | "yiisoft/request-provider": "^1.2", 60 | "yiisoft/router": "^4.0.2", 61 | "yiisoft/router-fastroute": "^4.0.3", 62 | "yiisoft/session": "^3.0.1", 63 | "yiisoft/view": "^12.2.2", 64 | "yiisoft/yii-console": "^2.4.2", 65 | "yiisoft/yii-http": "^1.1.1", 66 | "yiisoft/yii-runner-console": "^2.2.1", 67 | "yiisoft/yii-runner-http": "^3.2.1", 68 | "yiisoft/yii-view-renderer": "^7.3.1" 69 | }, 70 | "require-dev": { 71 | "codeception/c3": "^2.9", 72 | "codeception/codeception": "^5.3.3", 73 | "codeception/module-asserts": "^3.2.1", 74 | "codeception/module-cli": "^2.0.1", 75 | "codeception/module-phpbrowser": "^3.0.2", 76 | "friendsofphp/php-cs-fixer": "^3.92.3", 77 | "phpunit/phpunit": "^11.5.46", 78 | "rector/rector": "^2.2.14", 79 | "roave/infection-static-analysis-plugin": "^1.40", 80 | "roave/security-advisories": "dev-latest", 81 | "shipmonk/composer-dependency-analyser": "^1.8.4", 82 | "vimeo/psalm": "^6.14.2" 83 | }, 84 | "autoload": { 85 | "psr-4": { 86 | "App\\": "src" 87 | } 88 | }, 89 | "autoload-dev": { 90 | "psr-4": { 91 | "App\\Tests\\": "tests" 92 | } 93 | }, 94 | "extra": { 95 | "config-plugin-file": "config/configuration.php" 96 | }, 97 | "config": { 98 | "sort-packages": true, 99 | "bump-after-update": true, 100 | "allow-plugins": { 101 | "yiisoft/config": true, 102 | "infection/extension-installer": true, 103 | "codeception/c3": true, 104 | "composer/installers": true 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Environment.php: -------------------------------------------------------------------------------- 1 | register(MainAsset::class); 20 | 21 | $this->addCssFiles($assetManager->getCssFiles()); 22 | $this->addCssStrings($assetManager->getCssStrings()); 23 | $this->addJsFiles($assetManager->getJsFiles()); 24 | $this->addJsStrings($assetManager->getJsStrings()); 25 | $this->addJsVars($assetManager->getJsVars()); 26 | 27 | $this->beginPage() 28 | ?> 29 | 30 | 31 | 32 | 33 | 34 | 35 | <?= Html::encode($this->getTitle()) ?> 36 | head() ?> 37 | 38 | 39 | beginBody() ?> 40 | 41 |
42 | 43 | 44 | 47 | 48 | 50 | 53 | 56 | 59 | 62 | 65 | 66 | 67 | 68 |
69 | 70 |
71 |
72 | 73 |
74 |
75 | 76 | 124 | 125 | endBody() ?> 126 | 127 | 128 | endPage() ?> 129 | --------------------------------------------------------------------------------