diff --git a/.github/workflows/main.yml b/.github/workflows/prs.yml similarity index 91% rename from .github/workflows/main.yml rename to .github/workflows/prs.yml index 21a02f9..0264bab 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/prs.yml @@ -1,15 +1,12 @@ name: CI - -on: [push, pull_request] - +on: + pull_request: jobs: tests: runs-on: ubuntu-latest - strategy: matrix: php: [8.3, 8.4] - steps: - name: Checkout code uses: actions/checkout@v4 @@ -20,12 +17,12 @@ jobs: php-version: ${{ matrix.php }} extensions: pdo, sqlite, imagick coverage: none - - name: Validate composer.json and composer.lock run: composer validate - - name: Install dependencies run: composer install --prefer-dist --no-progress --no-interaction --no-suggest + - name: Run ECS + run: php vendor/bin/ecs - name: Run test suite run: | php vendor/bin/codecept build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..7439044 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,71 @@ +name: Automated release +on: + push: + branches: + - master +jobs: + static_analysis: + name: Static Analysis + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup PHP with PECL extension + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + - uses: ramsey/composer-install@v3 + - name: Initialize cache + uses: actions/cache@v4 + with: + key: phpstan + path: .phpstan-cache + - name: Run PHPStan + run: vendor/bin/phpstan + - name: Run ECS + run: php vendor/bin/ecs + tests: + runs-on: ubuntu-latest + strategy: + matrix: + php: [8.3, 8.4] + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: pdo, sqlite, imagick + coverage: none + - uses: ramsey/composer-install@v3 + - name: Run test suite + run: | + php vendor/bin/codecept build + php vendor/bin/codecept run + release: + name: Automated release + needs: + - static_analysis + - tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: actions/setup-node@v4 + with: + node-version: 22 + - run: > + npx + -p "@semantic-release/commit-analyzer" + -p "@semantic-release/release-notes-generator" + -p conventional-changelog-conventionalcommits + -p semantic-release + -- semantic-release --dry-run + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +permissions: + packages: write + contents: write + pull-requests: write diff --git a/.gitignore b/.gitignore index 31a345f..ff434cd 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ tests/_support tests/_output tests/cases/yii2-app-advanced/_data/db.sqlite +.php-cs-fixer.cache +.ecs-cache diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 0000000..a5c5e67 --- /dev/null +++ b/.releaserc.json @@ -0,0 +1,10 @@ +{ + "branches": ["master"], + "plugins": [ + ["@semantic-release/commit-analyzer", { + "preset": "conventionalcommits", + "presetConfig": {} + }], + "@semantic-release/github", + "@semantic-release/release-notes-generator"] +} diff --git a/composer.json b/composer.json index f93903b..998784c 100644 --- a/composer.json +++ b/composer.json @@ -26,11 +26,10 @@ "yiisoft/yii2": "dev-master", "yiisoft/yii2-app-advanced": "dev-master", "codeception/verify": "^3.0", - "codemix/yii2-localeurls": "^1.7", "codeception/module-asserts": ">= 3.0", "codeception/module-filesystem": "> 3.0", "phpstan/phpstan": "^2", - "rector/rector": "^2" + "symplify/easy-coding-standard": "^12.5" }, "autoload":{ "classmap": ["src/"] diff --git a/ecs.php b/ecs.php new file mode 100644 index 0000000..0a1526b --- /dev/null +++ b/ecs.php @@ -0,0 +1,69 @@ +parallel(); + + $ecsConfig->cacheDirectory('.ecs-cache'); + // Paths + $ecsConfig->paths([ + __DIR__ . '/src', __DIR__ . '/tests', __DIR__ . '/ecs.php' + ]); + + // A. full sets + $ecsConfig->sets([SetList::PSR_12, SetList::SPACES, SetList::STRICT, SetList::DOCBLOCK]); + + $ecsConfig->rule(NotOperatorWithSuccessorSpaceFixer::class); + $ecsConfig->rule(ArraySyntaxFixer::class); + $ecsConfig->ruleWithConfiguration(GeneralPhpdocAnnotationRemoveFixer::class, [ + 'annotations' => ['author', 'inheritdoc', 'package'] + ]); + $ecsConfig->rule(NoBlankLinesAfterPhpdocFixer::class); + $ecsConfig->ruleWithConfiguration(NoSuperfluousPhpdocTagsFixer::class, [ + 'allow_mixed' => true + ]); + $ecsConfig->rule(NoEmptyPhpdocFixer::class); + $ecsConfig->rule(NoUnusedImportsFixer::class); + $ecsConfig->ruleWithConfiguration(FinalInternalClassFixer::class, [ + 'annotation_exclude' => ['@not-fix', '@internal'], + 'annotation_include' => [], + 'consider_absent_docblock_as_internal_class' => true + ]); + $ecsConfig->ruleWithConfiguration(ForbiddenFunctionsSniff::class, [ + 'forbiddenFunctions' => [ + 'passthru' => null, + 'var_dump' => null, + ] + ]); + $ecsConfig->rule(PhpdocIndentFixer::class); + $ecsConfig->rule(\PhpCsFixer\Fixer\Phpdoc\AlignMultilineCommentFixer::class); + + $ecsConfig->skip([ + ForbiddenFunctionsSniff::class => [ + 'tests/**', + 'console/**' + ] + ]); + + // $ecsConfig->skip([ + // FinalClassFixer::class => [ + // 'tests/**' + // ] + // ]); +}; diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 4417730..ce4877d 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1704,84 +1704,6 @@ parameters: count: 1 path: tests/cases/events/functional/ResponseCest.php - - - message: '#^Cannot call method getComponents\(\) on object\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/cases/locale-urls/config.php - - - - message: '#^Cannot call method has\(\) on object\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/cases/locale-urls/config.php - - - - message: '#^Cannot call method set\(\) on object\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/cases/locale-urls/config.php - - - - message: '#^Access to an undefined property yii\\console\\Request\|yii\\web\\Request\:\:\$bodyParams\.$#' - identifier: property.notFound - count: 1 - path: tests/cases/locale-urls/controllers/SiteController.php - - - - message: '#^Access to an undefined property yii\\console\\Response\|yii\\web\\Response\:\:\$statusCode\.$#' - identifier: property.notFound - count: 1 - path: tests/cases/locale-urls/controllers/SiteController.php - - - - message: '#^Cannot access property \$request on yii\\base\\Application\|null\.$#' - identifier: property.nonObject - count: 1 - path: tests/cases/locale-urls/controllers/SiteController.php - - - - message: '#^Cannot access property \$response on yii\\base\\Application\|null\.$#' - identifier: property.nonObject - count: 1 - path: tests/cases/locale-urls/controllers/SiteController.php - - - - message: '#^Method app\\localeurls\\controllers\\SiteController\:\:actionForm\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/cases/locale-urls/controllers/SiteController.php - - - - message: '#^Method app\\localeurls\\controllers\\SiteController\:\:actionPost\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/cases/locale-urls/controllers/SiteController.php - - - - message: '#^Method tests\\LocaleUrlCest\:\:testFormSubmit\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/cases/locale-urls/functional/LocaleUrlCest.php - - - - message: '#^Method tests\\LocaleUrlCest\:\:testFormSubmit2\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/cases/locale-urls/functional/LocaleUrlCest.php - - - - message: '#^Method tests\\LocaleUrlCest\:\:testInstantiation\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/cases/locale-urls/functional/LocaleUrlCest.php - - - - message: '#^Method tests\\LocaleUrlCest\:\:testMultipleGet\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/cases/locale-urls/functional/LocaleUrlCest.php - - message: '#^Cannot access property \$mailer on yii\\base\\Application\|null\.$#' identifier: property.nonObject @@ -2034,12 +1956,6 @@ parameters: count: 1 path: tests/cases/simple/helpers/EmptyString.php - - - message: '#^Parameter \#1 \$directory of function tempnam expects string, null given\.$#' - identifier: argument.type - count: 3 - path: tests/cases/sqlite/config.php - - message: '#^Cannot call method get\(\) on yii\\base\\Application\|null\.$#' identifier: method.nonObject diff --git a/phpstan.neon b/phpstan.neon index 4b26724..fb2d525 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,12 @@ includes: - phpstan-baseline.neon parameters: + exceptions: + check: + missingCheckedExceptionInThrows: false + reportUncheckedExceptionDeadCatch: false reportUnmatchedIgnoredErrors: true + editorUrl: "phpstorm://open?file=%%file%%&line=%%line%%" dynamicConstantNames: - CONSOLE - YII_DEBUG @@ -10,10 +15,11 @@ parameters: - src - tests checkMaybeUndefinedVariables: true + treatPhpDocTypesAsCertain: false ignoreErrors: - # All Yii setters accept `null` but their phpdoc is incorrect. - - message: '~^Parameter #1 \$(.+) of method yii\\web\\Request::set(.+)\(\) expects (.+), null given.$~' - path: 'src/' + - identifier: return.type + path: tests/_support/_generated/FunctionalTesterActions.php + message: "# but returns mixed.$#" # If you want to ignore missing generics errors in the future, you can add: # - identifier: missingType.generics stubFiles: diff --git a/src/Codeception/Lib/Connector/Yii2.php b/src/Codeception/Lib/Connector/Yii2.php index d6f89cf..bd4b180 100644 --- a/src/Codeception/Lib/Connector/Yii2.php +++ b/src/Codeception/Lib/Connector/Yii2.php @@ -15,59 +15,58 @@ use Symfony\Component\BrowserKit\CookieJar; use Symfony\Component\BrowserKit\History; use Symfony\Component\BrowserKit\Request as BrowserkitRequest; -use yii\web\Request as YiiRequest; use Symfony\Component\BrowserKit\Response; use Yii; -use yii\base\Component; use yii\base\Event; use yii\base\ExitException; use yii\base\Security; use yii\base\UserException; use yii\mail\BaseMessage; -use yii\mail\MessageInterface; use yii\web\Application; -use yii\web\ErrorHandler; use yii\web\IdentityInterface; -use yii\web\Request; +use yii\web\Request as YiiRequest; use yii\web\Response as YiiResponse; use yii\web\User; - /** * @extends Client + * @internal This class is not part of the public API */ -class Yii2 extends Client +final class Yii2 extends Client { use Shared\PhpSuperGlobalsConverter; - const CLEAN_METHODS = [ + public const CLEAN_METHODS = [ self::CLEAN_RECREATE, self::CLEAN_CLEAR, self::CLEAN_FORCE_RECREATE, - self::CLEAN_MANUAL + self::CLEAN_MANUAL, ]; + /** * Clean the response object by recreating it. * This might lose behaviors / event handlers / other changes that are done in the application bootstrap phase. */ - const CLEAN_RECREATE = 'recreate'; + public const CLEAN_RECREATE = 'recreate'; + /** * Same as recreate but will not warn when behaviors / event handlers are lost. */ - const CLEAN_FORCE_RECREATE = 'force_recreate'; + public const CLEAN_FORCE_RECREATE = 'force_recreate'; + /** * Clean the response object by resetting specific properties via its' `clear()` method. * This will keep behaviors / event handlers, but could inadvertently leave some changes intact. + * * @see \yii\web\Response::clear() */ - const CLEAN_CLEAR = 'clear'; + public const CLEAN_CLEAR = 'clear'; /** * Do not clean the response, instead the test writer will be responsible for manually resetting the response in * between requests during one test */ - const CLEAN_MANUAL = 'manual'; - + public const CLEAN_MANUAL = 'manual'; /** * @var string application config file @@ -92,6 +91,7 @@ class Yii2 extends Client /** * This option is there primarily for backwards compatibility. * It means you cannot make any modification to application state inside your app, since they will get discarded. + * * @var bool whether to recreate the whole application before each request */ public $recreateApplication = false; @@ -107,7 +107,6 @@ class Yii2 extends Client */ public string|null $applicationClass = null; - /** * @var list */ @@ -118,7 +117,7 @@ class Yii2 extends Client */ protected function getApplication(): \yii\base\Application { - if (!isset(Yii::$app)) { + if (! isset(Yii::$app)) { $this->startApp(); } return Yii::$app ?? throw new \RuntimeException('Failed to create Yii2 application'); @@ -127,7 +126,7 @@ protected function getApplication(): \yii\base\Application private function getWebRequest(): YiiRequest { $request = $this->getApplication()->request; - if (!$request instanceof YiiRequest) { + if (! $request instanceof YiiRequest) { throw new \RuntimeException('Request component is not of type ' . YiiRequest::class); } return $request; @@ -149,15 +148,16 @@ public function resetApplication(bool $closeSession = true): void /** * Finds and logs in a user + * * @internal - * @throws ConfigurationException - * @throws \RuntimeException + * @throws ConfigurationException + * @throws \RuntimeException */ public function findAndLoginUser(int|string|IdentityInterface $user): void { $app = $this->getApplication(); $userComponent = $app->get('user'); - if (!$userComponent instanceof User) { + if (! $userComponent instanceof User) { throw new ConfigurationException('The user component is not configured'); } @@ -176,14 +176,14 @@ public function findAndLoginUser(int|string|IdentityInterface $user): void /** * @internal - * @param string $name The name of the cookie - * @param string $value The value of the cookie - * @return string The value to send to the browser + * @param string $name The name of the cookie + * @param string $value The value of the cookie + * @return string The value to send to the browser */ public function hashCookieData(string $name, string $value): string { $request = $this->getWebRequest(); - if (!$request->enableCookieValidation) { + if (! $request->enableCookieValidation) { return $value; } return $this->getApplication()->security->hashData(serialize([$name, $value]), $request->cookieValidationKey); @@ -191,7 +191,7 @@ public function hashCookieData(string $name, string $value): string /** * @internal - * @return non-empty-list List of regex patterns for recognized domain names + * @return non-empty-list List of regex patterns for recognized domain names */ public function getInternalDomains(): array { @@ -200,7 +200,9 @@ public function getInternalDomains(): array $domains = [$this->getDomainRegex($urlManager->hostInfo)]; if ($urlManager->enablePrettyUrl) { foreach ($urlManager->rules as $rule) { - /** @var \yii\web\UrlRule $rule */ + /** + * @var \yii\web\UrlRule $rule + */ if ($rule->host !== null) { $domains[] = $this->getDomainRegex($rule->host); } @@ -211,7 +213,7 @@ public function getInternalDomains(): array /** * @internal - * @return list List of sent emails + * @return list List of sent emails */ public function getEmails(): array { @@ -220,6 +222,7 @@ public function getEmails(): array /** * Deletes all stored emails. + * * @internal */ public function clearEmails(): void @@ -227,19 +230,6 @@ public function clearEmails(): void $this->emails = []; } - /** - * @internal - */ - public function getComponent(string $name): object|null - { - $app = $this->getApplication(); - $result = $app->get($name, false); - if (!isset($result)) { - throw new ConfigurationException("Component $name is not available in current application"); - } - return $result; - } - /** * Getting domain regex from rule host template */ @@ -257,7 +247,7 @@ function ($matches) use (&$parameters): string { $parameters[$key] = $matches[1] ?? '\w+'; return $key; }, - $template + $template, ); } if ($template === null) { @@ -270,6 +260,7 @@ function ($matches) use (&$parameters): string { /** * Gets the name of the CSRF param. + * * @internal */ public function getCsrfParamName(): string @@ -280,8 +271,8 @@ public function getCsrfParamName(): string public function startApp(?\yii\log\Logger $logger = null): void { codecept_debug('Starting application'); - $config = require($this->configFile); - if (!isset($config['class'])) { + $config = include $this->configFile; + if (! isset($config['class'])) { $config['class'] = $this->applicationClass ?? \yii\web\Application::class; } @@ -292,7 +283,7 @@ public function startApp(?\yii\log\Logger $logger = null): void $config = $this->mockMailer($config); $app = Yii::createObject($config); - if (!$app instanceof \yii\base\Application) { + if (! $app instanceof \yii\base\Application) { throw new ModuleConfigException($this, "Failed to initialize Yii2 app"); } \Yii::$app = $app; @@ -327,7 +318,7 @@ public function doRequest(object $request): Response $queryString = parse_url($uri, PHP_URL_QUERY); $_SERVER['REQUEST_URI'] = $queryString === null ? $pathString : $pathString . '?' . $queryString; $_SERVER['REQUEST_METHOD'] = strtoupper($request->getMethod()); - $_SERVER['QUERY_STRING'] = (string)$queryString; + $_SERVER['QUERY_STRING'] = (string) $queryString; parse_str($queryString ?: '', $params); foreach ($params as $k => $v) { @@ -339,7 +330,7 @@ public function doRequest(object $request): Response $this->beforeRequest(); $app = $this->getApplication(); - if (!$app instanceof Application) { + if (! $app instanceof Application) { throw new ConfigurationException("Application is not a web application"); } @@ -373,7 +364,7 @@ public function doRequest(object $request): Response // to expect error response codes in tests. $app->errorHandler->discardExistingOutput = false; $app->errorHandler->handleException($e); - } elseif (!$e instanceof ExitException) { + } elseif (! $e instanceof ExitException) { // for exceptions not related to Http, we pass them to Codeception throw $e; } @@ -387,7 +378,7 @@ public function doRequest(object $request): Response } $content = ob_get_clean(); - if (empty($content) && !empty($yiiResponse->content) && !isset($yiiResponse->stream)) { + if (empty($content) && ! empty($yiiResponse->content) && ! isset($yiiResponse->stream)) { throw new \RuntimeException('No content was sent from Yii application'); } elseif ($content === false) { throw new \RuntimeException('Failed to get output buffer'); @@ -398,19 +389,22 @@ public function doRequest(object $request): Response /** * Encodes the cookies and adds them to the headers. + * * @throws \yii\base\InvalidConfigException */ protected function encodeCookies( YiiResponse $response, YiiRequest $request, - Security $security + Security $security, ): void { if ($request->enableCookieValidation) { $validationKey = $request->cookieValidationKey; } foreach ($response->getCookies() as $cookie) { - /** @var \yii\web\Cookie $cookie */ + /** + * @var \yii\web\Cookie $cookie + */ $value = $cookie->value; // Expire = 1 means we're removing the cookie if ($cookie->expire !== 1 && isset($validationKey)) { @@ -419,7 +413,7 @@ protected function encodeCookies( : $cookie->value; $value = $security->hashData(serialize($data), $validationKey); } - $expires = is_int($cookie->expire) ? (string)$cookie->expire : null; + $expires = is_int($cookie->expire) ? (string) $cookie->expire : null; $c = new Cookie( $cookie->name, $value, @@ -427,7 +421,7 @@ protected function encodeCookies( $cookie->path, $cookie->domain, $cookie->secure, - $cookie->httpOnly + $cookie->httpOnly, ); $this->getCookieJar()->set($c); } @@ -435,7 +429,8 @@ protected function encodeCookies( /** * Replace mailer with in memory mailer - * @param array $config Original configuration + * + * @param array $config Original configuration * @return array New configuration */ protected function mockMailer(array $config): array @@ -457,13 +452,15 @@ protected function mockMailer(array $config): array 'class' => TestMailer::class, 'callback' => function (BaseMessage $message): void { $this->emails[] = $message; - } + }, ]; if (isset($config['components'])) { - if (!is_array($config['components'])) { - throw new ModuleConfigException($this, - "Yii2 config does not contain components key is not of type array"); + if (! is_array($config['components'])) { + throw new ModuleConfigException( + $this, + "Yii2 config does not contain components key is not of type array", + ); } } else { $config['components'] = []; @@ -490,13 +487,13 @@ public function restart(): void * Return an assoc array with the client context: cookieJar, history. * * @internal - * @return array{ cookieJar: CookieJar, history: History } + * @return array{ cookieJar: CookieJar, history: History } */ public function getContext(): array { return [ 'cookieJar' => $this->cookieJar, - 'history' => $this->history, + 'history' => $this->history, ]; } @@ -513,6 +510,7 @@ public function setContext(array $context): void /** * This functions closes the session of the application, if the application exists and has a session. + * * @internal */ public function closeSession(): void @@ -531,19 +529,19 @@ protected function resetResponse(Application $app): void { $method = $this->responseCleanMethod; // First check the current response object. - if ( - ($app->response->hasEventHandlers(YiiResponse::EVENT_BEFORE_SEND) - || $app->response->hasEventHandlers(YiiResponse::EVENT_AFTER_SEND) - || $app->response->hasEventHandlers(YiiResponse::EVENT_AFTER_PREPARE) - || count($app->response->getBehaviors()) > 0) + if (($app->response->hasEventHandlers(YiiResponse::EVENT_BEFORE_SEND) + || $app->response->hasEventHandlers(YiiResponse::EVENT_AFTER_SEND) + || $app->response->hasEventHandlers(YiiResponse::EVENT_AFTER_PREPARE) + || count($app->response->getBehaviors()) > 0) && $method === self::CLEAN_RECREATE ) { - Debug::debug(<<getBehaviors()) > 0 && $method === self::CLEAN_RECREATE) { - Debug::debug(<<getApplication(); - if (!$application instanceof Application) { + if (! $application instanceof Application) { throw new ConfigurationException('Application must be an instance of web application when doing requests'); } $this->resetResponse($application); diff --git a/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php b/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php index 7f20d5c..e3be083 100644 --- a/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php +++ b/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php @@ -11,15 +11,16 @@ use yii\db\Connection; /** - * Class ConnectionWatcher * This class will watch for new database connection and store a reference to the connection object. - * @package Codeception\Lib\Connector\Yii2 + * @internal */ class ConnectionWatcher { - private Closure $handler; + private readonly Closure $handler; - /** @var Connection[] */ + /** + * @var list + */ private array $connections = []; public function __construct() @@ -59,8 +60,7 @@ public function closeAll(): void } /** - * @param string|array|JsonSerializable $message - * @return void + * @param string|array|JsonSerializable $message */ protected function debug(string|array|JsonSerializable $message): void { diff --git a/src/Codeception/Lib/Connector/Yii2/FixturesStore.php b/src/Codeception/Lib/Connector/Yii2/FixturesStore.php index cb46a77..d985bd3 100644 --- a/src/Codeception/Lib/Connector/Yii2/FixturesStore.php +++ b/src/Codeception/Lib/Connector/Yii2/FixturesStore.php @@ -7,17 +7,16 @@ use yii\test\FixtureTrait; use yii\test\InitDbFixture; -class FixturesStore +final class FixturesStore { use FixtureTrait; /** * Expects fixtures config - * - * FixturesStore constructor. */ - public function __construct(protected mixed $data) - { + public function __construct( + protected mixed $data + ) { } public function fixtures(): mixed diff --git a/src/Codeception/Lib/Connector/Yii2/Logger.php b/src/Codeception/Lib/Connector/Yii2/Logger.php index cae874f..69ebd4e 100644 --- a/src/Codeception/Lib/Connector/Yii2/Logger.php +++ b/src/Codeception/Lib/Connector/Yii2/Logger.php @@ -9,7 +9,7 @@ use yii\helpers\VarDumper; use yii\log\Logger as YiiLogger; -class Logger extends YiiLogger +final class Logger extends YiiLogger { /** * @var \SplQueue @@ -17,11 +17,12 @@ class Logger extends YiiLogger private \SplQueue $logQueue; /** - * @param int $maxLogItems * @param array $config */ - public function __construct(private readonly int $maxLogItems = 5, array $config = []) - { + public function __construct( + private readonly int $maxLogItems = 5, + array $config = [] + ) { parent::__construct($config); $this->logQueue = new \SplQueue(); } @@ -32,17 +33,22 @@ public function init(): void } /** - * @param string|array|YiiException $message + * @param string|array|YiiException $message * @param self::LEVEL_INFO|self::LEVEL_WARNING|self::LEVEL_ERROR $level - * @param string $category + * @param string $category */ public function log($message, $level, $category = 'application'): void { - if (!in_array($level, [ - self::LEVEL_INFO, - self::LEVEL_WARNING, - self::LEVEL_ERROR, - ], true)) { + if (! in_array( + $level, + [ + self::LEVEL_INFO, + self::LEVEL_WARNING, + self::LEVEL_ERROR, + ], + true + ) + ) { return; } if (str_starts_with($category, 'yii\db\Command')) { diff --git a/src/Codeception/Lib/Connector/Yii2/TestMailer.php b/src/Codeception/Lib/Connector/Yii2/TestMailer.php index 230e15a..89b9bca 100644 --- a/src/Codeception/Lib/Connector/Yii2/TestMailer.php +++ b/src/Codeception/Lib/Connector/Yii2/TestMailer.php @@ -7,7 +7,7 @@ use Closure; use yii\mail\BaseMailer; -class TestMailer extends BaseMailer +final class TestMailer extends BaseMailer { public $messageClass = \yii\symfonymailer\Message::class; diff --git a/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php b/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php index ae9f19f..e93ac44 100644 --- a/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php +++ b/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php @@ -9,27 +9,28 @@ use yii\db\Transaction; /** - * Class TransactionForcer * This class adds support for forcing transactions as well as reusing PDO objects. - * @package Codeception\Lib\Connector\Yii2 */ -class TransactionForcer extends ConnectionWatcher +final class TransactionForcer extends ConnectionWatcher { /** * @var array */ private array $pdoCache = []; + /** * @var array */ private array $dsnCache = []; + /** * @var array */ private array $transactions = []; - public function __construct(private bool $ignoreCollidingDSN) - { + public function __construct( + private bool $ignoreCollidingDSN + ) { parent::__construct(); } @@ -40,34 +41,39 @@ protected function connectionOpened(Connection $connection): void * We should check if the known PDO objects are the same, in which case we should reuse the PDO * object so only 1 transaction is started and multiple connections to the same database see the * same data (due to writes inside a transaction not being visible from the outside). - * */ - $key = md5(json_encode([ - 'dsn' => $connection->dsn, - 'user' => $connection->username, - 'pass' => $connection->password, - 'attributes' => $connection->attributes, - 'emulatePrepare' => $connection->emulatePrepare, - 'charset' => $connection->charset, - ], JSON_THROW_ON_ERROR)); + $key = md5( + json_encode( + [ + 'dsn' => $connection->dsn, + 'user' => $connection->username, + 'pass' => $connection->password, + 'attributes' => $connection->attributes, + 'emulatePrepare' => $connection->emulatePrepare, + 'charset' => $connection->charset, + ], + JSON_THROW_ON_ERROR + ) + ); /* * If keys match we assume connections are "similar enough". */ if (isset($this->pdoCache[$key])) { $connection->pdo = $this->pdoCache[$key]; - } elseif(isset($connection->pdo)) { + } elseif (isset($connection->pdo)) { $this->pdoCache[$key] = $connection->pdo; } if (isset($this->dsnCache[$connection->dsn]) && $this->dsnCache[$connection->dsn] !== $key - && !$this->ignoreCollidingDSN + && ! $this->ignoreCollidingDSN ) { - $this->debug(<<debug( + <<dsn}) with different configuration. These connections will not see the same database state since we cannot share a transaction between different PDO instances. You can remove this message by adding 'ignoreCollidingDSN = true' in the module configuration. -TEXT +TEXT, ); Debug::pause(); } @@ -81,7 +87,9 @@ protected function connectionOpened(Connection $connection): void public function rollbackAll(): void { - /** @var Transaction $transaction */ + /** + * @var Transaction $transaction + */ foreach ($this->transactions as $transaction) { if ($transaction->db->isActive) { $transaction->rollBack(); diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index cfa9eee..768c69a 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -16,20 +16,20 @@ use Codeception\Lib\Interfaces\MultiSession; use Codeception\Lib\Interfaces\PartedModule; use Codeception\TestInterface; +use PHPUnit\Framework\Assert; use ReflectionClass; use RuntimeException; use Symfony\Component\BrowserKit\CookieJar; use Symfony\Component\BrowserKit\History; use Yii; use yii\base\Security; -use yii\web\Application as WebApplication; use yii\db\ActiveQueryInterface; use yii\db\ActiveRecordInterface; use yii\helpers\Url; use yii\mail\BaseMessage; use yii\mail\MessageInterface; use yii\test\Fixture; -use yii\web\Application; +use yii\web\Application as WebApplication; use yii\web\IdentityInterface; /** @@ -218,7 +218,7 @@ * applicationClass: class-string<\yii\base\Application>|null * } */ -class Yii2 extends Framework implements ActiveRecord, MultiSession, PartedModule +final class Yii2 extends Framework implements ActiveRecord, MultiSession, PartedModule { /** * @var list @@ -227,16 +227,17 @@ class Yii2 extends Framework implements ActiveRecord, MultiSession, PartedModule /** * Application config file must be set. + * * @var ModuleConfig */ protected array $config = [ 'fixturesMethod' => '_fixtures', - 'cleanup' => true, + 'cleanup' => true, 'ignoreCollidingDSN' => false, 'transaction' => true, 'configFile' => null, 'entryScript' => '', - 'entryUrl' => 'http://localhost/index-test.php', + 'entryUrl' => 'http://localhost/index-test.php', 'responseCleanMethod' => Yii2Connector::CLEAN_CLEAR, 'requestCleanMethod' => Yii2Connector::CLEAN_RECREATE, 'recreateComponents' => [], @@ -256,7 +257,7 @@ class Yii2 extends Framework implements ActiveRecord, MultiSession, PartedModule private TransactionForcer $transactionForcer; /** - * @var array The contents of $_SERVER upon initialization of this object. + * @var array The contents of upon initialization of this object. * This is only used to restore it upon object destruction. * It MUST not be used anywhere else. */ @@ -266,10 +267,10 @@ class Yii2 extends Framework implements ActiveRecord, MultiSession, PartedModule private function getClient(): Yii2Connector { - if (!isset($this->client)) { + if (! isset($this->client)) { throw new RuntimeException('Browser not initialized'); } - if (!$this->client instanceof Yii2Connector) { + if (! $this->client instanceof Yii2Connector) { throw new RuntimeException('The Yii2 module must be used with the Yii2 browser client'); } return $this->client; @@ -311,13 +312,16 @@ private function initServerGlobal(): void $parsedUrl = parse_url($entryUrl); $entryFile = $this->config['entryScript'] ?: basename($entryUrl); $entryScript = $this->config['entryScript'] ?: ($parsedUrl['path'] ?? ''); - $_SERVER = array_merge($_SERVER, [ + $_SERVER = array_merge( + $_SERVER, + [ 'SCRIPT_FILENAME' => $entryFile, 'SCRIPT_NAME' => $entryScript, 'SERVER_NAME' => $parsedUrl['host'] ?? '', 'SERVER_PORT' => $parsedUrl['port'] ?? '80', - 'HTTPS' => isset($parsedUrl['scheme']) && $parsedUrl['scheme'] === 'https' - ]); + 'HTTPS' => isset($parsedUrl['scheme']) && $parsedUrl['scheme'] === 'https', + ] + ); } /** @@ -326,35 +330,34 @@ private function initServerGlobal(): void protected function validateConfig(): void { parent::validateConfig(); - if (!isset($this->config['configFile'])) { + if (! isset($this->config['configFile'])) { throw new ModuleConfigException( self::class, - "The application config file was not configured" + "The application config file was not configured", ); } $pathToConfig = codecept_absolute_path($this->config['configFile']); - if (!is_file($pathToConfig)) { + if (! is_file($pathToConfig)) { throw new ModuleConfigException( self::class, - "The application config file does not exist: " . $pathToConfig + "The application config file does not exist: " . $pathToConfig, ); } $validMethods = implode(", ", Yii2Connector::CLEAN_METHODS); - if (!in_array($this->config['responseCleanMethod'], Yii2Connector::CLEAN_METHODS, true)) { + if (! in_array($this->config['responseCleanMethod'], Yii2Connector::CLEAN_METHODS, true)) { throw new ModuleConfigException( self::class, - "The response clean method must be one of: " . $validMethods + "The response clean method must be one of: " . $validMethods, ); } - if (!in_array($this->config['requestCleanMethod'], Yii2Connector::CLEAN_METHODS, true)) { + if (! in_array($this->config['requestCleanMethod'], Yii2Connector::CLEAN_METHODS, true)) { throw new ModuleConfigException( self::class, - "The request clean method must be one of: " . $validMethods + "The request clean method must be one of: " . $validMethods, ); } } - /** * @param ClientConfig $settings */ @@ -379,13 +382,15 @@ protected function recreateClient(): void $parsedUrl = parse_url($entryUrl); $entryFile = $this->config['entryScript'] ?: basename($entryUrl); $entryScript = $this->config['entryScript'] ?: ($parsedUrl['path'] ?? ''); - $this->client = new Yii2Connector([ + $this->client = new Yii2Connector( + [ 'SCRIPT_FILENAME' => $entryFile, 'SCRIPT_NAME' => $entryScript, 'SERVER_NAME' => $parsedUrl['host'] ?? '', 'SERVER_PORT' => $parsedUrl['port'] ?? '80', - 'HTTPS' => isset($parsedUrl['scheme']) && $parsedUrl['scheme'] === 'https' - ]); + 'HTTPS' => isset($parsedUrl['scheme']) && $parsedUrl['scheme'] === 'https', + ] + ); $this->validateConfig(); $this->configureClient($this->config); } @@ -517,7 +522,7 @@ public function amLoggedInAs(int|string|IdentityInterface $user): void { try { $this->getClient()->findAndLoginUser($user); - } catch (ConfigurationException|RuntimeException $e) { + } catch (ConfigurationException | RuntimeException $e) { throw new ModuleException($this, $e->getMessage()); } } @@ -555,7 +560,7 @@ public function amLoggedInAs(int|string|IdentityInterface $user): void * ``` * instead of calling `haveFixtures` in Cest `_before` * - * @part fixtures + * @part fixtures * @param array $fixtures */ public function haveFixtures(array $fixtures): void @@ -573,14 +578,14 @@ public function haveFixtures(array $fixtures): void * Returns all loaded fixtures. * Array of fixture instances * - * @part fixtures + * @part fixtures * @return array */ public function grabFixtures(): array { $result = []; - foreach($this->loadedFixtures as $store) { - foreach($store->getFixtures() as $name => $fixture) { + foreach ($this->loadedFixtures as $store) { + foreach ($store->getFixtures() as $name => $fixture) { $result[$name] = $fixture; } } @@ -604,19 +609,19 @@ public function grabFixtures(): array * ``` * * @throws \Codeception\Exception\ModuleException if the fixture is not found - * @part fixtures + * @part fixtures */ public function grabFixture(string $name, null|string $index = null): Fixture|\yii\db\ActiveRecord|null { $fixtures = $this->grabFixtures(); - if (!isset($fixtures[$name])) { + if (! isset($fixtures[$name])) { throw new ModuleException($this, "Fixture $name is not loaded"); } $fixture = $fixtures[$name]; return match (true) { $index === null => $fixture, $fixture instanceof \yii\test\BaseActiveFixture => $fixture->getModel($index), - default => throw new ModuleException($this, "Fixture $name is not an instance of ActiveFixture and can't be loaded with second parameter") + default => throw new ModuleException($this, "Fixture $name is not an instance of ActiveFixture and can't be loaded with second parameter"), }; } @@ -625,42 +630,45 @@ public function grabFixture(string $name, null|string $index = null): Fixture|\y * * ``` php * haveRecord('app\models\User', array('name' => 'Davert')); + * $user_id = $I->haveRecord(model: User::class, attributes: ['name' => 'Davert']); * ?> * ``` + * * @template T of \yii\db\ActiveRecord - * @param class-string $model - * @param array $attributes - * @part orm + * @param class-string $model + * @param array $attributes + * @part orm + * @return int|string|array The primary key */ - public function haveRecord(string $model, $attributes = []): mixed + public function haveRecord(string $model, $attributes = []): int|string|array { - /** @var T $record */ + /** + * @var T $record + */ $record = \Yii::createObject($model); $record->setAttributes($attributes, false); - $res = $record->save(false); - if (!$res) { - $this->fail("Record $model was not saved: " . \yii\helpers\Json::encode($record->errors)); + if (! $record->save(false)) { + Assert::fail("Record $model was not saved: " . \yii\helpers\Json::encode($record->errors)); } - return $record->primaryKey; + return $record->getPrimaryKey(); } /** * Checks that a record exists in the database. * * ```php - * $I->seeRecord('app\models\User', array('name' => 'davert')); + * $I->seeRecord(model: User::class, attributes: ['name' => 'davert']); * ``` * * @param class-string<\yii\db\ActiveRecord> $model - * @param array $attributes - * @part orm + * @param array $attributes + * @part orm */ public function seeRecord(string $model, array $attributes = []): void { $record = $this->findRecord($model, $attributes); - if (!$record) { - $this->fail("Couldn't find $model with " . json_encode($attributes)); + if (! $record) { + Assert::fail("Couldn't find $model with " . json_encode($attributes)); } $this->debugSection($model, json_encode($record)); } @@ -669,19 +677,19 @@ public function seeRecord(string $model, array $attributes = []): void * Checks that a record does not exist in the database. * * ```php - * $I->dontSeeRecord('app\models\User', array('name' => 'davert')); + * $I->dontSeeRecord(User::class, attributes: ['name' => 'davert']); * ``` * * @param class-string<\yii\db\ActiveRecord> $model - * @param array $attributes - * @part orm + * @param array $attributes + * @part orm */ public function dontSeeRecord(string $model, array $attributes = []): void { $record = $this->findRecord($model, $attributes); $this->debugSection($model, json_encode($record)); if ($record) { - $this->fail("Unexpectedly managed to find $model with " . json_encode($attributes)); + Assert::fail("Unexpectedly managed to find $model with " . json_encode($attributes)); } } @@ -689,13 +697,13 @@ public function dontSeeRecord(string $model, array $attributes = []): void * Retrieves a record from the database * * ```php - * $category = $I->grabRecord('app\models\User', array('name' => 'davert')); + * $category = $I->grabRecord(User::class, attributes: ['name' => 'davert']); * ``` * - * @param class-string<\yii\db\ActiveRecord> $model - * @param array $attributes + * @param class-string<\yii\db\ActiveRecord> $model + * @param array $attributes * @return ActiveRecordInterface|null|array - * @part orm + * @part orm */ public function grabRecord(string $model, array $attributes = []): ActiveRecordInterface|null|array { @@ -703,18 +711,17 @@ public function grabRecord(string $model, array $attributes = []): ActiveRecordI } /** - * @param class-string<\yii\db\ActiveRecord> $model Class name - * @param array $attributes + * @param class-string<\yii\db\ActiveRecord> $model Class name + * @param array $attributes * @return ActiveRecordInterface|null|array */ protected function findRecord(string $model, array $attributes = []): ActiveRecordInterface|null|array { - if (!class_exists($model)) { + if (! class_exists($model)) { throw new RuntimeException("Class $model does not exist"); } $rc = new ReflectionClass($model); if ($rc->hasMethod('find') - /** @phpstan-ignore-next-line */ && ($findMethod = $rc->getMethod('find')) && $findMethod->isStatic() && $findMethod->isPublic() @@ -736,9 +743,9 @@ protected function findRecord(string $model, array $attributes = []): ActiveReco * $I->amOnRoute('site/view', ['page' => 'about']); * ``` * - * @param string $route A route + * @param string $route A route * @param array $params Additional route parameters - * @part route + * @part route */ public function amOnRoute(string $route, array $params = []): void { @@ -763,7 +770,7 @@ public function amOnRoute(string $route, array $params = []): void * ``` * * @throws \Codeception\Exception\ModuleException - * @part email + * @part email */ public function seeEmailIsSent(?int $num = null): void { @@ -796,7 +803,7 @@ public function dontSeeEmailIsSent(): void * $I->assertSame('admin@site,com', $messages[0]->getTo()); * ``` * - * @part email + * @part email * @return list List of sent emails * @throws \Codeception\Exception\ModuleException */ @@ -818,6 +825,7 @@ public function grabSentEmails(): array * $message = $I->grabLastSentEmail(); * $I->assertSame('admin@site,com', $message->getTo()); * ``` + * * @part email */ public function grabLastSentEmail(): BaseMessage|null @@ -830,6 +838,7 @@ public function grabLastSentEmail(): BaseMessage|null /** * Returns a list of regex patterns for recognized domain names + * * @return non-empty-list */ public function getInternalDomains(): array @@ -846,8 +855,9 @@ private function defineConstants(): void /** * Sets a cookie and, if validation is enabled, signs it. - * @param string $name The name of the cookie - * @param string $val The value of the cookie + * + * @param string $name The name of the cookie + * @param string $val The value of the cookie * @param array{domain?: string, path?: string, expires?: int, secure?:bool} $params Additional cookie params like `domain`, `path`, `expires` and `secure`. */ public function setCookie($name, $val, $params = []): void @@ -857,7 +867,8 @@ public function setCookie($name, $val, $params = []): void /** * Creates the CSRF Cookie. - * @param string $val The value of the CSRF token + * + * @param string $val The value of the CSRF token * @return string[] Returns an array containing the name of the CSRF param and the masked CSRF token. */ public function createAndSetCsrfCookie(string $val): array @@ -889,6 +900,7 @@ public function _initializeSession(): void /** * Return the session content for future restoring. Implements MultiSession. + * * @return SessionBackup */ public function _backupSession(): array @@ -907,6 +919,7 @@ public function _backupSession(): array /** * Restore a session. Implements MultiSession. + * * @param SessionBackup $session output of _backupSession() */ public function _loadSession($session): void @@ -924,11 +937,12 @@ public function _loadSession($session): void /** * Close and dump a session. Implements MultiSession. + * * @param mixed $session */ public function _closeSession($session = null): void { - if (!$session) { + if (! $session) { $this->_initializeSession(); } } diff --git a/tests/Yii.stub b/tests/Yii.stub index 2a715d2..bbfd44a 100644 --- a/tests/Yii.stub +++ b/tests/Yii.stub @@ -17,6 +17,27 @@ namespace yii { namespace yii\base { class Application {} + + class Model {} +} + +namespace yii\db { + class BaseActiveRecord extends \yii\base\Model { + /** + * @param bool $asArray + * @return int|string|array|null + */ + public function getPrimaryKey($asArray = false) {} + + /** + * @return bool + * @param bool $runValidation + * @param null|list $attributeNames + * @phpstan-assert-if-true (int|string|array) $this->getPrimaryKey() + */ + public function save($runValidation = true, $attributeNames = null) {} + + } } namespace yii\web { @@ -33,6 +54,45 @@ namespace yii\web { * @return void */ public function setBaseUrl($url) {} + + /** + * @param array|object|null $params + */ + public function setBodyParams($params): void {} + + /** + * @param list|null $params + */ + public function setAcceptableContentTypes($params): void {} + /** + * @param list|null $params + */ + public function setAcceptableLanguages($params): void {} + + /** + * @param string|null $body + */ + public function setRawBody($body): void {} + + /** + * @param string|null $path + */ + public function setPathInfo($path): void {} + + /** + * @param string|null $path + */ + public function setScriptUrl($path): void {} + + /** + * @param string|null $path + */ + public function setUrl($path): void {} + + /** + * @param string|null $path + */ + public function setScriptFile($path): void {} } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 5ce6bee..9dcd5fa 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,4 +1,7 @@ 'Simple', 'basePath' => __DIR__, @@ -9,4 +12,4 @@ 'dsn' => 'sqlite:' . \tests\helpers\SqlliteHelper::getTmpFile(), ], ], -]; \ No newline at end of file +]; diff --git a/tests/cases/closeConnections/fixtures/EmptyFixture.php b/tests/cases/closeConnections/fixtures/EmptyFixture.php index 9006bb0..688e5a5 100644 --- a/tests/cases/closeConnections/fixtures/EmptyFixture.php +++ b/tests/cases/closeConnections/fixtures/EmptyFixture.php @@ -6,7 +6,7 @@ use yii\test\DbFixture; -class EmptyFixture extends DbFixture +final class EmptyFixture extends DbFixture { public function load() { @@ -15,4 +15,4 @@ public function load() public function unload() { } -} \ No newline at end of file +} diff --git a/tests/cases/closeConnections/functional/FixturesCest.php b/tests/cases/closeConnections/functional/FixturesCest.php index 1c034cf..b8e7b82 100644 --- a/tests/cases/closeConnections/functional/FixturesCest.php +++ b/tests/cases/closeConnections/functional/FixturesCest.php @@ -9,7 +9,7 @@ use tests\FunctionalTester; use tests\helpers\SqlliteHelper; -class FixturesCest +final class FixturesCest { public function _fixtures() { @@ -26,12 +26,10 @@ protected function numberProvider() } /** - * @param FunctionalTester $I * @dataProvider numberProvider */ public function NoConnections(FunctionalTester $I, Example $example) { $I->assertSame(SqlliteHelper::connectionCount(), $example['count']); } - -} \ No newline at end of file +} diff --git a/tests/cases/closeConnections/functional/FixturesInBeforeCest.php b/tests/cases/closeConnections/functional/FixturesInBeforeCest.php index 711d587..72d33e1 100644 --- a/tests/cases/closeConnections/functional/FixturesInBeforeCest.php +++ b/tests/cases/closeConnections/functional/FixturesInBeforeCest.php @@ -9,7 +9,7 @@ use tests\FunctionalTester; use tests\helpers\SqlliteHelper; -class FixturesInBeforeCest +final class FixturesInBeforeCest { public function _before(FunctionalTester $I) { @@ -26,12 +26,10 @@ protected function numberProvider() } /** - * @param FunctionalTester $I * @dataProvider numberProvider */ public function NoConnections(FunctionalTester $I, Example $example) { $I->assertSame(SqlliteHelper::connectionCount(), $example['count']); } - -} \ No newline at end of file +} diff --git a/tests/cases/closeConnections/functional/NoFixturesCest.php b/tests/cases/closeConnections/functional/NoFixturesCest.php index 2ff5b40..ba2e9bf 100644 --- a/tests/cases/closeConnections/functional/NoFixturesCest.php +++ b/tests/cases/closeConnections/functional/NoFixturesCest.php @@ -8,20 +8,18 @@ use tests\FunctionalTester; use tests\helpers\SqlliteHelper; -class NoFixturesCest +final class NoFixturesCest { - protected function numberProvider() { return array_pad([], 5, ['count' => 0]); } /** - * @param FunctionalTester $I * @dataProvider numberProvider */ public function NoConnections(FunctionalTester $I, Example $example) { $I->assertSame(SqlliteHelper::connectionCount(), $example['count']); } -} \ No newline at end of file +} diff --git a/tests/cases/closeConnections/helpers/SqlliteHelper.php b/tests/cases/closeConnections/helpers/SqlliteHelper.php index 15f263a..b7c4887 100644 --- a/tests/cases/closeConnections/helpers/SqlliteHelper.php +++ b/tests/cases/closeConnections/helpers/SqlliteHelper.php @@ -4,7 +4,7 @@ namespace tests\helpers; -class SqlliteHelper +final class SqlliteHelper { protected static $temp_name; @@ -20,7 +20,7 @@ public static function connectionCount() { $path = self::$temp_name; $count = shell_exec("lsof -w {$path} | grep {$path} | wc -l"); - return (int)$count; + return (int) $count; } public static function debug() @@ -30,5 +30,4 @@ public static function debug() codecept_debug("Executing : $cmd"); codecept_debug(shell_exec($cmd)); } - -} \ No newline at end of file +} diff --git a/tests/cases/closeConnectionsNoCleanup/config.php b/tests/cases/closeConnectionsNoCleanup/config.php index 99e2245..7c85f5f 100644 --- a/tests/cases/closeConnectionsNoCleanup/config.php +++ b/tests/cases/closeConnectionsNoCleanup/config.php @@ -1,4 +1,7 @@ 'Simple', 'basePath' => __DIR__, @@ -9,4 +12,4 @@ 'dsn' => 'sqlite:' . \tests\helpers\SqlliteHelper::getTmpFile(), ], ], -]; \ No newline at end of file +]; diff --git a/tests/cases/closeConnectionsNoCleanup/functional/FixturesCest.php b/tests/cases/closeConnectionsNoCleanup/functional/FixturesCest.php index 92a5016..f9d9676 100644 --- a/tests/cases/closeConnectionsNoCleanup/functional/FixturesCest.php +++ b/tests/cases/closeConnectionsNoCleanup/functional/FixturesCest.php @@ -4,11 +4,11 @@ namespace tests\closeConnectionsNoCleanup; -use tests\FunctionalTester; use tests\fixtures\EmptyFixture; +use tests\FunctionalTester; use tests\helpers\SqlliteHelper; -class FixturesCest +final class FixturesCest { public function _fixtures() { @@ -36,5 +36,4 @@ public function NoConnections3(FunctionalTester $I) $count = SqlliteHelper::connectionCount(); $I->assertSame(0, $count); } - -} \ No newline at end of file +} diff --git a/tests/cases/closeConnectionsNoCleanup/functional/FixturesInBeforeCest.php b/tests/cases/closeConnectionsNoCleanup/functional/FixturesInBeforeCest.php index bdf0241..0329642 100644 --- a/tests/cases/closeConnectionsNoCleanup/functional/FixturesInBeforeCest.php +++ b/tests/cases/closeConnectionsNoCleanup/functional/FixturesInBeforeCest.php @@ -4,11 +4,11 @@ namespace tests\closeConnectionsNoCleanup; -use tests\FunctionalTester; use tests\fixtures\EmptyFixture; +use tests\FunctionalTester; use tests\helpers\SqlliteHelper; -class FixturesInBeforeCest +final class FixturesInBeforeCest { public function _before(FunctionalTester $I) { @@ -36,4 +36,4 @@ public function OnlyOneConnection3(FunctionalTester $I) $count = SqlliteHelper::connectionCount(); $I->assertSame(1, $count); } -} \ No newline at end of file +} diff --git a/tests/cases/closeConnectionsNoCleanup/functional/ThirdCest.php b/tests/cases/closeConnectionsNoCleanup/functional/ThirdCest.php index 1a9caa2..1df7ee1 100644 --- a/tests/cases/closeConnectionsNoCleanup/functional/ThirdCest.php +++ b/tests/cases/closeConnectionsNoCleanup/functional/ThirdCest.php @@ -4,11 +4,11 @@ namespace tests\closeConnectionsNoCleanup; -use tests\FunctionalTester; use tests\fixtures\EmptyFixture; +use tests\FunctionalTester; use tests\helpers\SqlliteHelper; -class ThirdCest +final class ThirdCest { public function NoConnections1(FunctionalTester $I) { @@ -39,4 +39,4 @@ public function OnlyOneConnection3(FunctionalTester $I) $count = SqlliteHelper::connectionCount(); $I->assertSame(1, $count); } -} \ No newline at end of file +} diff --git a/tests/cases/events/config.php b/tests/cases/events/config.php index 09869fd..31dce8c 100644 --- a/tests/cases/events/config.php +++ b/tests/cases/events/config.php @@ -1,22 +1,25 @@ 'Response Events', 'basePath' => __DIR__, 'bootstrap' => [ - function(\yii\web\Application $application) { - $application->response->on(\yii\web\Response::EVENT_BEFORE_SEND, function($event) use ($application) { + function (\yii\web\Application $application) { + $application->response->on(\yii\web\Response::EVENT_BEFORE_SEND, function ($event) use ($application) { $application->trigger('responseBeforeSendBootstrap'); }); - } + }, ], 'components' => [ 'request' => [ - 'cookieValidationKey' => 'secret' + 'cookieValidationKey' => 'secret', ], 'response' => [ - 'on beforeSend' => function() { + 'on beforeSend' => function () { \Yii::$app->trigger('responseBeforeSendConfig'); - } - ] - ] -]; \ No newline at end of file + }, + ], + ], +]; diff --git a/tests/cases/events/controllers/SiteController.php b/tests/cases/events/controllers/SiteController.php index 9c640e7..02ef1a7 100644 --- a/tests/cases/events/controllers/SiteController.php +++ b/tests/cases/events/controllers/SiteController.php @@ -3,13 +3,12 @@ declare(strict_types=1); namespace app\controllers; + use yii\web\Controller; -class SiteController extends Controller +final class SiteController extends Controller { - public function actionIndex() { - } -} \ No newline at end of file +} diff --git a/tests/cases/events/functional/ResponseCest.php b/tests/cases/events/functional/ResponseCest.php index 360b888..37176f9 100644 --- a/tests/cases/events/functional/ResponseCest.php +++ b/tests/cases/events/functional/ResponseCest.php @@ -7,16 +7,15 @@ use Codeception\Lib\Connector\Yii2; use yii\base\Event; -class ResponseCest +final class ResponseCest { - public function testAfterSend(FunctionalTester $I) { $sources = []; - \Yii::$app->on('responseBeforeSendConfig', function(Event $event) use (&$sources) { + \Yii::$app->on('responseBeforeSendConfig', function (Event $event) use (&$sources) { $sources[] = 'config'; }); - \Yii::$app->on('responseBeforeSendBootstrap', function(Event $event) use (&$sources) { + \Yii::$app->on('responseBeforeSendBootstrap', function (Event $event) use (&$sources) { $sources[] = 'bootstrap'; }); $I->assertEmpty($sources); @@ -32,14 +31,14 @@ public function testAfterSend(FunctionalTester $I) public function testAfterSendWithRecreate(FunctionalTester $I, \Codeception\Module\Yii2 $module) { $module->_reconfigure([ - 'responseCleanMethod' => Yii2::CLEAN_RECREATE + 'responseCleanMethod' => Yii2::CLEAN_RECREATE, ]); $module->client->startApp(); $sources = []; - \Yii::$app->on('responseBeforeSendConfig', function(Event $event) use (&$sources) { + \Yii::$app->on('responseBeforeSendConfig', function (Event $event) use (&$sources) { $sources[] = 'config'; }); - \Yii::$app->on('responseBeforeSendBootstrap', function(Event $event) use (&$sources) { + \Yii::$app->on('responseBeforeSendBootstrap', function (Event $event) use (&$sources) { $sources[] = 'bootstrap'; }); $I->assertEmpty($sources); @@ -57,14 +56,14 @@ public function testAfterSendWithRecreate(FunctionalTester $I, \Codeception\Modu public function testAfterSendWithForcedRecreate(FunctionalTester $I, \Codeception\Module\Yii2 $module) { $module->_reconfigure([ - 'responseCleanMethod' => Yii2::CLEAN_FORCE_RECREATE + 'responseCleanMethod' => Yii2::CLEAN_FORCE_RECREATE, ]); $module->client->startApp(); $sources = []; - \Yii::$app->on('responseBeforeSendConfig', function(Event $event) use (&$sources) { + \Yii::$app->on('responseBeforeSendConfig', function (Event $event) use (&$sources) { $sources[] = 'config'; }); - \Yii::$app->on('responseBeforeSendBootstrap', function(Event $event) use (&$sources) { + \Yii::$app->on('responseBeforeSendBootstrap', function (Event $event) use (&$sources) { $sources[] = 'bootstrap'; }); @@ -83,4 +82,4 @@ public function testAfterSendWithForcedRecreate(FunctionalTester $I, \Codeceptio $I->assertSame(['config'], $sources); } -} \ No newline at end of file +} diff --git a/tests/cases/locale-urls/codeception.yml b/tests/cases/locale-urls/codeception.yml deleted file mode 100644 index 6c75eef..0000000 --- a/tests/cases/locale-urls/codeception.yml +++ /dev/null @@ -1,15 +0,0 @@ -paths: - tests: . - output: ../../_output - data: _data - support: ../../_support -namespace: tests\ -extensions: - enabled: - - Codeception\Extension\RunFailed -modules: - config: - Yii2: - configFile: config.php - transaction: true - cleanup: true \ No newline at end of file diff --git a/tests/cases/locale-urls/config.php b/tests/cases/locale-urls/config.php deleted file mode 100644 index 101c377..0000000 --- a/tests/cases/locale-urls/config.php +++ /dev/null @@ -1,31 +0,0 @@ - 'Simple', - 'basePath' => __DIR__, - 'controllerNamespace' => 'app\localeurls\controllers', - 'components' => [ - 'request' => [ - 'enableCsrfValidation' => false, - 'cookieValidationKey' => 'test' - ], - 'urlManager' => [ - 'class' => UrlManager::class, - 'enablePrettyUrl' => true, - 'showScriptName' => false, - 'enableLanguagePersistence' => false, - 'enableLocaleUrls' => true, - 'languages' => ['nl', 'en'] - ] - ], - 'on beforeRequest' => function(\yii\base\Event $event) { - $app = $event->sender; - if ($app->has('urlManager', true)) { - $config = $app->getComponents()['urlManager']; - \Codeception\Util\Debug::debug('Resetting url manager: ' . print_r($config, true)); - $app->set('urlManager', $config); - } - } -]; \ No newline at end of file diff --git a/tests/cases/locale-urls/controllers/SiteController.php b/tests/cases/locale-urls/controllers/SiteController.php deleted file mode 100644 index c26c1bf..0000000 --- a/tests/cases/locale-urls/controllers/SiteController.php +++ /dev/null @@ -1,35 +0,0 @@ - - -
- -Submit -
- - - - -HTML; - - } - - public function actionPost() - { - \Yii::$app->response->statusCode = 201; - return print_r(\Yii::$app->request->bodyParams, true); - } -} \ No newline at end of file diff --git a/tests/cases/locale-urls/functional.suite.yml b/tests/cases/locale-urls/functional.suite.yml deleted file mode 100644 index 754807a..0000000 --- a/tests/cases/locale-urls/functional.suite.yml +++ /dev/null @@ -1,13 +0,0 @@ -# Codeception Test Suite Configuration -# -# Suite for functional tests -# Emulate web requests and make application process them -# Include one of framework modules (Symfony2, Yii2, Laravel5) to use it -# Remove this suite if you don't use frameworks -actor: FunctionalTester -modules: - enabled: - - Yii2 - - Asserts -steps: - - Codeception\Step\ConditionalAssertion diff --git a/tests/cases/locale-urls/functional/LocaleUrlCest.php b/tests/cases/locale-urls/functional/LocaleUrlCest.php deleted file mode 100644 index 96c431a..0000000 --- a/tests/cases/locale-urls/functional/LocaleUrlCest.php +++ /dev/null @@ -1,43 +0,0 @@ -assertInstanceOf(Application::class, \Yii::$app); - } - - public function testMultipleGet(FunctionalTester $I) - { - $I->amOnRoute('/en/site/form'); - $I->amOnRoute('/en/site/form'); - } - public function testFormSubmit(FunctionalTester $I) - { - $I->amOnRoute('site/form'); - $I->seeResponseCodeIs(200); - - $I->fillField('#test', 'test'); - $I->click('#submit'); - $I->canSeeResponseCodeIs(201); - } - - public function testFormSubmit2(FunctionalTester $I) - { - $I->amOnRoute('/en/site/form'); - $I->seeResponseCodeIs(200); - $I->submitForm('form', [ - 'login-form[login]' => 'user', - 'login-form[password]' => 'test', - ]); - $I->canSeeResponseCodeIs(201); - } - -} diff --git a/tests/cases/mock-mailer/config.php b/tests/cases/mock-mailer/config.php index 5b33dcc..a883b5f 100644 --- a/tests/cases/mock-mailer/config.php +++ b/tests/cases/mock-mailer/config.php @@ -1,4 +1,7 @@ 'Simple', 'basePath' => __DIR__, @@ -6,7 +9,7 @@ 'components' => [ 'request' => [ 'enableCsrfValidation' => false, - 'cookieValidationKey' => 'test' + 'cookieValidationKey' => 'test', ], 'mailer' => [ 'class' => 'yii\symfonymailer\Mailer', diff --git a/tests/cases/pageCacheHeaderAlreadySent/config.php b/tests/cases/pageCacheHeaderAlreadySent/config.php index b65fa02..830ecd1 100644 --- a/tests/cases/pageCacheHeaderAlreadySent/config.php +++ b/tests/cases/pageCacheHeaderAlreadySent/config.php @@ -1,5 +1,7 @@ 'PageCache', 'basePath' => __DIR__, diff --git a/tests/cases/pageCacheHeaderAlreadySent/controllers/UserController.php b/tests/cases/pageCacheHeaderAlreadySent/controllers/UserController.php index 35195eb..a9ea0f6 100644 --- a/tests/cases/pageCacheHeaderAlreadySent/controllers/UserController.php +++ b/tests/cases/pageCacheHeaderAlreadySent/controllers/UserController.php @@ -6,14 +6,14 @@ use yii\filters\PageCache; -class UserController extends \yii\web\Controller +final class UserController extends \yii\web\Controller { public function behaviors() { return [ 'cache' => [ - 'class' => PageCache::class - ] + 'class' => PageCache::class, + ], ]; } diff --git a/tests/cases/pageCacheHeaderAlreadySent/functional/PageCest.php b/tests/cases/pageCacheHeaderAlreadySent/functional/PageCest.php index dda305f..4fb68b6 100644 --- a/tests/cases/pageCacheHeaderAlreadySent/functional/PageCest.php +++ b/tests/cases/pageCacheHeaderAlreadySent/functional/PageCest.php @@ -2,8 +2,8 @@ declare(strict_types=1); -class PageCest { - +final class PageCest +{ public function testCache(\tests\FunctionalTester $I) { $I->amOnRoute('user/index'); @@ -12,4 +12,4 @@ public function testCache(\tests\FunctionalTester $I) $I->amOnRoute('user/index'); $I->canSeeResponseCodeIs(200); } -} \ No newline at end of file +} diff --git a/tests/cases/simple/config.php b/tests/cases/simple/config.php index 2e11ee3..1c293dd 100644 --- a/tests/cases/simple/config.php +++ b/tests/cases/simple/config.php @@ -1,5 +1,7 @@ [ 'request' => [ 'enableCsrfValidation' => false, - 'cookieValidationKey' => 'test' + 'cookieValidationKey' => 'test', ], 'user' => [ - 'identityClass' => DummyUser::class - ] + 'identityClass' => DummyUser::class, + ], ], - 'on beforeRequest' => function() { + 'on beforeRequest' => function () { if (isset(\Yii::$app->params['throw'])) { throw \Yii::$app->params['throw']; } - } -]; \ No newline at end of file + }, +]; diff --git a/tests/cases/simple/controllers/SiteController.php b/tests/cases/simple/controllers/SiteController.php index c8c02ef..cca2620 100644 --- a/tests/cases/simple/controllers/SiteController.php +++ b/tests/cases/simple/controllers/SiteController.php @@ -3,14 +3,14 @@ declare(strict_types=1); namespace app\simple\controllers; + use app\simple\helpers\EmptyString; use yii\base\Action; use yii\helpers\Url; use yii\web\Controller; -class SiteController extends Controller +final class SiteController extends Controller { - public function actionForm() { $action = Url::to(['site/post']); @@ -46,13 +46,12 @@ public function actionEnd() \Yii::$app->end(); } - /** * @param Action $action * @return bool * @throws \yii\web\BadRequestHttpException */ - public function beforeAction($action) + public function beforeAction($action) { if ($action->id === 'empty-response') { \Yii::$app->response->stream = fopen('php://memory', 'r+'); @@ -71,4 +70,4 @@ public function actionIndex() { return ''; } -} \ No newline at end of file +} diff --git a/tests/cases/simple/functional/SimpleCest.php b/tests/cases/simple/functional/SimpleCest.php index 7306fd7..16e78fd 100644 --- a/tests/cases/simple/functional/SimpleCest.php +++ b/tests/cases/simple/functional/SimpleCest.php @@ -5,12 +5,10 @@ namespace tests; use Codeception\Exception\ModuleException; -use yii\base\ExitException; use yii\web\Application; -class SimpleCest +final class SimpleCest { - public function testInstantiation(FunctionalTester $I) { $I->assertInstanceOf(Application::class, \Yii::$app); @@ -38,7 +36,7 @@ public function testFormSubmit2(FunctionalTester $I) public function testException(FunctionalTester $I) { - $I->expectThrowable(new \Exception('This is not an HttpException'), function() use ($I) { + $I->expectThrowable(new \Exception('This is not an HttpException'), function () use ($I) { $I->amOnRoute('/site/exception'); }); $I->assertInstanceOf(Application::class, \Yii::$app); @@ -48,7 +46,7 @@ public function testExceptionInBeforeRequest(FunctionalTester $I) { $e = new \Exception('This is not an HttpException'); \Yii::$app->params['throw'] = $e; - $I->expectThrowable($e, function() use ($I) { + $I->expectThrowable($e, function () use ($I) { $I->amOnRoute('/site/exception'); }); } @@ -67,7 +65,7 @@ public function testEmptyResponse(FunctionalTester $I) public function testMissingUser(FunctionalTester $I) { - $I->expectThrowable(ModuleException::class, function() use ($I) { + $I->expectThrowable(ModuleException::class, function () use ($I) { $I->amLoggedInAs('nobody'); }); $I->amOnRoute('/site/index'); diff --git a/tests/cases/simple/helpers/DummyUser.php b/tests/cases/simple/helpers/DummyUser.php index 8b00bd1..c7ec457 100644 --- a/tests/cases/simple/helpers/DummyUser.php +++ b/tests/cases/simple/helpers/DummyUser.php @@ -4,12 +4,10 @@ namespace app\simple\helpers; - use yii\web\IdentityInterface; -class DummyUser implements IdentityInterface +final class DummyUser implements IdentityInterface { - /** * Finds an identity by the given ID. * @param string|int $id the ID to be looked for @@ -20,7 +18,7 @@ class DummyUser implements IdentityInterface public static function findIdentity($id) { return null; - // TODO: Implement findIdentity() method. + } /** @@ -34,7 +32,7 @@ public static function findIdentity($id) */ public static function findIdentityByAccessToken($token, $type = null) { - // TODO: Implement findIdentityByAccessToken() method. + } /** @@ -43,7 +41,7 @@ public static function findIdentityByAccessToken($token, $type = null) */ public function getId() { - // TODO: Implement getId() method. + } /** @@ -60,7 +58,7 @@ public function getId() */ public function getAuthKey() { - // TODO: Implement getAuthKey() method. + } /** @@ -73,6 +71,6 @@ public function getAuthKey() */ public function validateAuthKey($authKey) { - // TODO: Implement validateAuthKey() method. + } -} \ No newline at end of file +} diff --git a/tests/cases/simple/helpers/EmptyString.php b/tests/cases/simple/helpers/EmptyString.php index feeb241..c5792e8 100644 --- a/tests/cases/simple/helpers/EmptyString.php +++ b/tests/cases/simple/helpers/EmptyString.php @@ -7,7 +7,7 @@ /** * Class that is empty when converted to string */ -class EmptyString +final class EmptyString { private $value; @@ -25,4 +25,4 @@ public function __toString() { return ''; } -} \ No newline at end of file +} diff --git a/tests/cases/sqlite/config.php b/tests/cases/sqlite/config.php index e20ed92..0fc2b1b 100644 --- a/tests/cases/sqlite/config.php +++ b/tests/cases/sqlite/config.php @@ -1,4 +1,7 @@ 'Simple', 'basePath' => __DIR__, @@ -6,19 +9,19 @@ 'components' => [ 'db' => [ 'class' => yii\db\Connection::class, - 'dsn' => 'sqlite:' . tempnam(null, '/file0') + 'dsn' => 'sqlite:' . tempnam('', '/file0'), ], 'db1' => [ 'class' => yii\db\Connection::class, - 'dsn' => 'sqlite:' . tempnam(null, '/file1') + 'dsn' => 'sqlite:' . tempnam('', '/file1'), ], 'db21' => [ 'class' => yii\db\Connection::class, - 'dsn' => 'sqlite:' . ($name = tempnam(null, '/file2')) + 'dsn' => 'sqlite:' . ($name = tempnam('', '/file2')), ], 'db22' => [ 'class' => yii\db\Connection::class, - 'dsn' => 'sqlite:' . $name - ] - ] -]; \ No newline at end of file + 'dsn' => 'sqlite:' . $name, + ], + ], +]; diff --git a/tests/cases/sqlite/fixtures/TestFixture.php b/tests/cases/sqlite/fixtures/TestFixture.php index 04c66a9..dd32324 100644 --- a/tests/cases/sqlite/fixtures/TestFixture.php +++ b/tests/cases/sqlite/fixtures/TestFixture.php @@ -4,22 +4,22 @@ namespace tests\fixtures; - use yii\db\Connection; -use yii\db\Exception; use yii\test\DbFixture; -class TestFixture extends DbFixture +final class TestFixture extends DbFixture { public $tableName = 'test'; + public $tableConfig = [ - 'id' => 'int' + 'id' => 'int', ]; public $dbComponents = []; - public function load() { - foreach($this->dbComponents as $name) { + public function load() + { + foreach ($this->dbComponents as $name) { /** @var Connection $connection */ $connection = \Yii::$app->get($name); $connection->createCommand()->createTable($this->tableName, $this->tableConfig)->execute(); @@ -28,12 +28,12 @@ public function load() { public function unload() { - foreach($this->dbComponents as $name) { + foreach ($this->dbComponents as $name) { /** @var Connection $connection */ $connection = \Yii::$app->get($name); - if (in_array($this->tableName, $connection->getSchema()->getTableNames('', true))) { + if (in_array($this->tableName, $connection->getSchema()->getTableNames('', true), true)) { $connection->createCommand()->dropTable($this->tableName)->execute(); } } } -} \ No newline at end of file +} diff --git a/tests/cases/sqlite/functional/SqLiteCest.php b/tests/cases/sqlite/functional/SqLiteCest.php index ea3b4c7..05fa0f7 100644 --- a/tests/cases/sqlite/functional/SqLiteCest.php +++ b/tests/cases/sqlite/functional/SqLiteCest.php @@ -7,7 +7,7 @@ use tests\fixtures\TestFixture; use yii\db\Connection; -class SqLiteCest +final class SqLiteCest { /** * This is called before the database transaction is started. @@ -17,7 +17,7 @@ public function _fixtures() return [ [ 'class' => TestFixture::class, - 'dbComponents' => ['db1', 'db21'] + 'dbComponents' => ['db1', 'db21'], ], ]; } diff --git a/tests/cases/yii2-app-advanced/config.php b/tests/cases/yii2-app-advanced/config.php index 82c61da..72ebe3e 100644 --- a/tests/cases/yii2-app-advanced/config.php +++ b/tests/cases/yii2-app-advanced/config.php @@ -1,5 +1,7 @@ [ 'db' => [ 'class' => yii\db\Connection::class, - 'dsn' => 'sqlite:' . codecept_output_dir('/db.sqlite') + 'dsn' => 'sqlite:' . codecept_output_dir('/db.sqlite'), ], 'request' => [ - 'cookieValidationKey' => 'test' + 'cookieValidationKey' => 'test', ], 'assetManager' => [ - 'basePath' => sys_get_temp_dir() + 'basePath' => sys_get_temp_dir(), ], 'mailer' => [ 'viewPath' => '@common/mail', @@ -24,9 +26,9 @@ ], 'aliases' => [ '@bower' => '@vendor/bower-asset', - '@npm' => '@vendor/npm-asset' + '@npm' => '@vendor/npm-asset', ], - 'vendorPath' => __DIR__ . '/../../../vendor' + 'vendorPath' => __DIR__ . '/../../../vendor', ]); -return $config; \ No newline at end of file +return $config; diff --git a/tests/cases/yii2-app-advanced/migrate.php b/tests/cases/yii2-app-advanced/migrate.php index a910536..964b72f 100644 --- a/tests/cases/yii2-app-advanced/migrate.php +++ b/tests/cases/yii2-app-advanced/migrate.php @@ -1,5 +1,7 @@ [ 'db' => [ 'class' => yii\db\Connection::class, - 'dsn' => 'sqlite:' . __DIR__ . '/_data/db.sqlite' - ], + 'dsn' => 'sqlite:' . __DIR__ . '/_data/db.sqlite', + ], ], - 'vendorPath' => __DIR__ . '/../../../vendor' + 'vendorPath' => __DIR__ . '/../../../vendor', ]); return $config;