Skip to content

Implement new PHP features up to 8.0 #119

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"codemix/yii2-localeurls": "^1.7",
"codeception/module-asserts": ">= 3.0",
"codeception/module-filesystem": "> 3.0",
"phpstan/phpstan": "^1.10"
"phpstan/phpstan": "^1.10",
"rector/rector": "^1.2"
},
"autoload":{
"classmap": ["src/"]
Expand Down
5 changes: 2 additions & 3 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ parameters:
paths:
- src
checkMaybeUndefinedVariables: true
checkGenericClassInNonGenericObjectType: 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/'
- message: '~^Variable \$_COOKIE in isset\(\) always exists and is not nullable.$~'
path: 'src/'
# If you want to ignore missing generics errors in the future, you can add:
# - identifier: missingType.generics
stubFiles:
- tests/Yii.stub
82 changes: 33 additions & 49 deletions src/Codeception/Lib/Connector/Yii2.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Codeception\Lib\Connector\Yii2\Logger;
use Codeception\Lib\Connector\Yii2\TestMailer;
use Codeception\Util\Debug;
use InvalidArgumentException;
use Symfony\Component\BrowserKit\AbstractBrowser as Client;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\BrowserKit\CookieJar;
Expand Down Expand Up @@ -114,7 +115,7 @@ public function getApplication(): \yii\base\Application
public function resetApplication(bool $closeSession = true): void
{
codecept_debug('Destroying application');
if (true === $closeSession) {
if ($closeSession) {
$this->closeSession();
}
Yii::$app = null;
Expand All @@ -141,13 +142,13 @@ public function findAndLoginUser(int|string|IdentityInterface $user): void
throw new ConfigurationException('The user component is not configured');
}

if ($user instanceof \yii\web\IdentityInterface) {
if ($user instanceof IdentityInterface) {
$identity = $user;
} else {
// class name implementing IdentityInterface
$identityClass = $userComponent->identityClass;
$identity = call_user_func([$identityClass, 'findIdentity'], $user);
if (!isset($identity)) {
$identity = $identityClass::findIdentity($user);
if ($identity === null) {
throw new \RuntimeException('User not found');
}
}
Expand Down Expand Up @@ -181,7 +182,7 @@ public function getInternalDomains(): array
if ($urlManager->enablePrettyUrl) {
foreach ($urlManager->rules as $rule) {
/** @var \yii\web\UrlRule $rule */
if (isset($rule->host)) {
if ($rule->host !== null) {
$domains[] = $this->getDomainRegex($rule->host);
}
}
Expand Down Expand Up @@ -228,12 +229,12 @@ private function getDomainRegex(string $template): string
$template = $matches[1];
}
$parameters = [];
if (strpos($template, '<') !== false) {
if (str_contains($template, '<')) {
$template = preg_replace_callback(
'/<(?:\w+):?([^>]+)?>/u',
function ($matches) use (&$parameters) {
function ($matches) use (&$parameters): string {
$key = '__' . count($parameters) . '__';
$parameters[$key] = isset($matches[1]) ? $matches[1] : '\w+';
$parameters[$key] = $matches[1] ?? '\w+';
return $key;
},
$template
Expand All @@ -258,24 +259,18 @@ public function startApp(?\yii\log\Logger $logger = null): void
codecept_debug('Starting application');
$config = require($this->configFile);
if (!isset($config['class'])) {
if (null !== $this->applicationClass) {
$config['class'] = $this->applicationClass;
} else {
$config['class'] = 'yii\web\Application';
}
$config['class'] = $this->applicationClass ?? \yii\web\Application::class;
}

if (isset($config['container']))
{
if (isset($config['container'])) {
Yii::configure(Yii::$container, $config['container']);
unset($config['container']);
}

$config = $this->mockMailer($config);
/** @var \yii\base\Application $app */
Yii::$app = Yii::createObject($config);

if ($logger !== null) {
if ($logger instanceof \yii\log\Logger) {
Yii::setLogger($logger);
} else {
Yii::setLogger(new Logger());
Expand Down Expand Up @@ -326,9 +321,6 @@ public function doRequest(object $request): \Symfony\Component\BrowserKit\Respon
$target->enabled = false;
}




$yiiRequest = $app->getRequest();
if ($request->getContent() !== null) {
$yiiRequest->setRawBody($request->getContent());
Expand Down Expand Up @@ -441,7 +433,7 @@ protected function mockMailer(array $config): array

$mailerConfig = [
'class' => TestMailer::class,
'callback' => function (MessageInterface $message) {
'callback' => function (MessageInterface $message): void {
$this->emails[] = $message;
}
];
Expand Down Expand Up @@ -474,7 +466,7 @@ public function getContext(): array
{
return [
'cookieJar' => $this->cookieJar,
'history' => $this->history,
'history' => $this->history,
];
}

Expand Down Expand Up @@ -509,11 +501,12 @@ protected function resetResponse(Application $app): void
{
$method = $this->responseCleanMethod;
// First check the current response object.
if (($app->response->hasEventHandlers(\yii\web\Response::EVENT_BEFORE_SEND)
|| $app->response->hasEventHandlers(\yii\web\Response::EVENT_AFTER_SEND)
|| $app->response->hasEventHandlers(\yii\web\Response::EVENT_AFTER_PREPARE)
|| count($app->response->getBehaviors()) > 0
) && $method === self::CLEAN_RECREATE
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(<<<TEXT
[WARNING] You are attaching event handlers or behaviors to the response object. But the Yii2 module is configured to recreate
Expand All @@ -525,17 +518,12 @@ protected function resetResponse(Application $app): void
$method = self::CLEAN_CLEAR;
}

switch ($method) {
case self::CLEAN_FORCE_RECREATE:
case self::CLEAN_RECREATE:
$app->set('response', $app->getComponents()['response']);
break;
case self::CLEAN_CLEAR:
$app->response->clear();
break;
case self::CLEAN_MANUAL:
break;
}
match ($method) {
self::CLEAN_FORCE_RECREATE, self::CLEAN_RECREATE => $app->set('response', $app->getComponents()['response']),
self::CLEAN_CLEAR => $app->response->clear(),
self::CLEAN_MANUAL => null,
default => throw new InvalidArgumentException("Unknown method: $method"),
};
}

protected function resetRequest(Application $app): void
Expand All @@ -555,12 +543,9 @@ protected function resetRequest(Application $app): void
$method = self::CLEAN_CLEAR;
}

switch ($method) {
case self::CLEAN_FORCE_RECREATE:
case self::CLEAN_RECREATE:
$app->set('request', $app->getComponents()['request']);
break;
case self::CLEAN_CLEAR:
match ($method) {
self::CLEAN_FORCE_RECREATE, self::CLEAN_RECREATE => $app->set('request', $app->getComponents()['request']),
self::CLEAN_CLEAR => (function () use ($request): void {
$request->getHeaders()->removeAll();
$request->setBaseUrl(null);
$request->setHostInfo(null);
Expand All @@ -572,11 +557,10 @@ protected function resetRequest(Application $app): void
$request->setSecurePort(0);
$request->setAcceptableContentTypes(null);
$request->setAcceptableLanguages(null);

break;
case self::CLEAN_MANUAL:
break;
}
})(),
self::CLEAN_MANUAL => null,
default => throw new InvalidArgumentException("Unknown method: $method"),
};
}

/**
Expand Down
13 changes: 8 additions & 5 deletions src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace Codeception\Lib\Connector\Yii2;

use Closure;
use JsonSerializable;
use ReflectionClass;
use yii\base\Event;
use yii\db\Connection;

Expand All @@ -14,14 +17,14 @@
*/
class ConnectionWatcher
{
private \Closure $handler;
private Closure $handler;

/** @var Connection[] */
private array $connections = [];

public function __construct()
{
$this->handler = function (Event $event) {
$this->handler = function (Event $event): void {
if ($event->sender instanceof Connection) {
$this->connectionOpened($event->sender);
}
Expand Down Expand Up @@ -55,10 +58,10 @@ public function closeAll(): void
}
}

protected function debug($message): void
protected function debug(string|array|JsonSerializable $message): void
{
$title = (new \ReflectionClass($this))->getShortName();
if (is_array($message) or is_object($message)) {
$title = (new ReflectionClass($this))->getShortName();
if (is_array($message) || is_object($message)) {
$message = stripslashes(json_encode($message));
}
codecept_debug("[$title] $message");
Expand Down
13 changes: 6 additions & 7 deletions src/Codeception/Lib/Connector/Yii2/FixturesStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,24 @@ class FixturesStore
{
use FixtureTrait;

protected $data;

/**
* Expects fixtures config
*
* FixturesStore constructor.
* @param $data
*/
public function __construct($data)
public function __construct(protected mixed $data)
{
$this->data = $data;
}

public function fixtures()
public function fixtures(): mixed
{
return $this->data;
}

public function globalFixtures()
/**
* @return array{initDbFixture: array{class: class-string}}
*/
public function globalFixtures(): array
{
return [
'initDbFixture' => [
Expand Down
35 changes: 14 additions & 21 deletions src/Codeception/Lib/Connector/Yii2/Logger.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
namespace Codeception\Lib\Connector\Yii2;

use Codeception\Util\Debug;
use yii\base\Exception as YiiException;
use yii\helpers\VarDumper;
use yii\log\Logger as YiiLogger;

class Logger extends \yii\log\Logger
class Logger extends YiiLogger
{
private \SplQueue $logQueue;

public function __construct(private int $maxLogItems = 5, $config = [])
public function __construct(private int $maxLogItems = 5, array $config = [])
{
parent::__construct($config);
$this->logQueue = new \SplQueue();
Expand All @@ -23,33 +25,28 @@ public function init(): void
}

/**
* @param string|array<mixed>|\yii\base\Exception $message
* @param $level
* @param $category
* @return void
* @param string|array|YiiException $message
* @param self::LEVEL_INFO|self::LEVEL_WARNING|self::LEVEL_ERROR $level
* @param string $category
*/
public function log($message, $level, $category = 'application'): void
{
if (!in_array($level, [
\yii\log\Logger::LEVEL_INFO,
\yii\log\Logger::LEVEL_WARNING,
\yii\log\Logger::LEVEL_ERROR,
])) {
self::LEVEL_INFO,
self::LEVEL_WARNING,
self::LEVEL_ERROR,
], true)) {
return;
}
if (str_starts_with($category, 'yii\db\Command')) {
return; // don't log queries
}

// https://github.com/Codeception/Codeception/issues/3696
if ($message instanceof \yii\base\Exception) {
if ($message instanceof YiiException) {
$message = $message->__toString();
}

$logMessage = "[$category] " . VarDumper::export($message);

Debug::debug($logMessage);

$this->logQueue->enqueue($logMessage);
if ($this->logQueue->count() > $this->maxLogItems) {
$this->logQueue->dequeue();
Expand All @@ -58,12 +55,8 @@ public function log($message, $level, $category = 'application'): void

public function getAndClearLog(): string
{
$completeStr = '';
foreach ($this->logQueue as $item) {
$completeStr .= $item . PHP_EOL;
}
$logs = iterator_to_array($this->logQueue);
$this->logQueue = new \SplQueue();

return $completeStr;
return implode(PHP_EOL, $logs) . PHP_EOL;
}
}
16 changes: 7 additions & 9 deletions src/Codeception/Lib/Connector/Yii2/TestMailer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,24 @@

namespace Codeception\Lib\Connector\Yii2;

use Closure;
use yii\mail\BaseMailer;

class TestMailer extends BaseMailer
{
public $messageClass = \yii\symfonymailer\Message::class;

/**
* @var \Closure
*/
public $callback;
public Closure $callback;

protected function sendMessage($message)
protected function sendMessage(mixed $message): bool
{
call_user_func($this->callback, $message);
($this->callback)($message);
return true;
}
protected function saveMessage($message)

protected function saveMessage(mixed $message): bool
{
call_user_func($this->callback, $message);
($this->callback)($message);
return true;
}
}
Loading