mirror of
				https://github.com/elyby/accounts.git
				synced 2025-05-31 14:11:46 +05:30 
			
		
		
		
	Upgrade project to PHP 8.3, add PHPStan, upgrade almost every dependency (#36)
* start updating to PHP 8.3 * taking off! Co-authored-by: ErickSkrauch <erickskrauch@yandex.ru> Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com> * dropped this Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com> * migrate to symfonymailer Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com> * this is so stupid 😭 Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com> * ah, free, at last. Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com> * oh, Gabriel. Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com> * now dawns thy reckoning. Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com> * and thy gore shall GLISTEN before the temples of man. Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com> * creature of steel. Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com> * my gratitude upon thee for my freedom. Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com> * but the crimes thy kind has committed against humanity Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com> * Upgrade PHP-CS-Fixer and do fix the codebase * First review round (maybe I have broken something) * are NOT forgotten. Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com> * Enable parallel PHP-CS-Fixer runner * PHPStan level 1 * PHPStan level 2 * PHPStan level 3 * PHPStan level 4 * PHPStan level 5 * Levels 6 and 7 takes too much effort. Generate a baseline and fix them eventually * Resolve TODO's related to the php-mock * Drastically reduce baseline size with the Rector * More code modernization with help of the Rector * Update GitLab CI --------- Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com> Co-authored-by: ErickSkrauch <erickskrauch@yandex.ru>
This commit is contained in:
		| @@ -54,7 +54,7 @@ class PrimaryKeyValueBehavior extends Behavior { | ||||
|         $owner = $this->owner; | ||||
|         $primaryKeys = $owner::primaryKey(); | ||||
|         if (!isset($primaryKeys[0])) { | ||||
|             throw new InvalidConfigException('"' . get_class($owner) . '" must have a primary key.'); | ||||
|             throw new InvalidConfigException('"' . $owner::class . '" must have a primary key.'); | ||||
|         } | ||||
|  | ||||
|         if (count($primaryKeys) > 1) { | ||||
|   | ||||
| @@ -3,7 +3,7 @@ actor_suffix: Tester | ||||
| bootstrap: _bootstrap.php | ||||
| paths: | ||||
|   tests: tests | ||||
|   log: tests/_output | ||||
|   output: tests/_output | ||||
|   data: tests/_data | ||||
|   helpers: tests/_support | ||||
| settings: | ||||
|   | ||||
| @@ -8,15 +8,11 @@ use GuzzleHttp\ClientInterface; | ||||
|  | ||||
| class Api { | ||||
|  | ||||
|     private $baseUrl; | ||||
|     private ?\GuzzleHttp\ClientInterface $client = null; | ||||
|  | ||||
|     /** | ||||
|      * @var ClientInterface | ||||
|      */ | ||||
|     private $client; | ||||
|  | ||||
|     public function __construct(string $baseUrl) { | ||||
|         $this->baseUrl = $baseUrl; | ||||
|     public function __construct( | ||||
|         private readonly string $baseUrl, | ||||
|     ) { | ||||
|     } | ||||
|  | ||||
|     public function setClient(ClientInterface $client): void { | ||||
|   | ||||
| @@ -26,10 +26,7 @@ class Component extends \yii\base\Component implements RendererInterface { | ||||
|      */ | ||||
|     public $basePath = ''; | ||||
|  | ||||
|     /** | ||||
|      * @var Api | ||||
|      */ | ||||
|     private $api; | ||||
|     private ?Api $api = null; | ||||
|  | ||||
|     public function init(): void { | ||||
|         parent::init(); | ||||
| @@ -47,11 +44,8 @@ class Component extends \yii\base\Component implements RendererInterface { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $templateName | ||||
|      * @param string $locale | ||||
|      * @param array $params | ||||
|      * @param array<string, mixed> $params | ||||
|      * | ||||
|      * @return string | ||||
|      * @throws \GuzzleHttp\Exception\GuzzleException | ||||
|      */ | ||||
|     public function render(string $templateName, string $locale, array $params = []): string { | ||||
|   | ||||
| @@ -5,25 +5,11 @@ namespace common\components\EmailsRenderer\Request; | ||||
|  | ||||
| class TemplateRequest { | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     private $name; | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     private $locale; | ||||
|  | ||||
|     /** | ||||
|      * @var array | ||||
|      */ | ||||
|     private $params; | ||||
|  | ||||
|     public function __construct(string $name, string $locale, array $params) { | ||||
|         $this->name = $name; | ||||
|         $this->locale = $locale; | ||||
|         $this->params = $params; | ||||
|     public function __construct( | ||||
|         private readonly string $name, | ||||
|         private readonly string $locale, | ||||
|         private readonly array $params, | ||||
|     ) { | ||||
|     } | ||||
|  | ||||
|     public function getName(): string { | ||||
|   | ||||
| @@ -12,15 +12,20 @@ use ReflectionClass; | ||||
|  | ||||
| class ElyDecorator implements DecoratorInterface { | ||||
|  | ||||
|     private const LOGO = __DIR__ . '/resources/logo.svg'; | ||||
|     private const string LOGO = __DIR__ . '/resources/logo.svg'; | ||||
|  | ||||
|     private const CORRECTION_MAP = [ | ||||
|     private const array CORRECTION_MAP = [ | ||||
|         ErrorCorrectionLevel::L => 7, | ||||
|         ErrorCorrectionLevel::M => 15, | ||||
|         ErrorCorrectionLevel::Q => 25, | ||||
|         ErrorCorrectionLevel::H => 30, | ||||
|     ]; | ||||
|  | ||||
|     /** | ||||
|      * @throws \ImagickException | ||||
|      * @throws \ImagickPixelException | ||||
|      * @throws \ImagickPixelIteratorException | ||||
|      */ | ||||
|     public function preProcess( | ||||
|         QrCode $qrCode, | ||||
|         RendererInterface $renderer, | ||||
| @@ -28,8 +33,8 @@ class ElyDecorator implements DecoratorInterface { | ||||
|         $outputHeight, | ||||
|         $leftPadding, | ||||
|         $topPadding, | ||||
|         $multiple | ||||
|     ) { | ||||
|         $multiple, | ||||
|     ): void { | ||||
|         if (!$renderer instanceof Svg) { | ||||
|             throw new InvalidArgumentException('$renderer must by instance of ' . Svg::class); | ||||
|         } | ||||
| @@ -38,11 +43,11 @@ class ElyDecorator implements DecoratorInterface { | ||||
|         $sizeMultiplier = $correctionLevel + floor($correctionLevel / 3); | ||||
|         $count = $qrCode->getMatrix()->getWidth(); | ||||
|  | ||||
|         $countToRemoveX = floor($count * $sizeMultiplier / 100); | ||||
|         $countToRemoveY = floor($count * $sizeMultiplier / 100); | ||||
|         $countToRemoveX = (int)floor($count * $sizeMultiplier / 100); | ||||
|         $countToRemoveY = (int)floor($count * $sizeMultiplier / 100); | ||||
|  | ||||
|         $startX = $leftPadding + round(($count - $countToRemoveX) / 2 * $multiple); | ||||
|         $startY = $topPadding + round(($count - $countToRemoveY) / 2 * $multiple); | ||||
|         $startX = (int)($leftPadding + round(($count - $countToRemoveX) / 2 * $multiple)); | ||||
|         $startY = (int)($topPadding + round(($count - $countToRemoveY) / 2 * $multiple)); | ||||
|         $width = $countToRemoveX * $multiple; | ||||
|         $height = $countToRemoveY * $multiple; | ||||
|  | ||||
| @@ -52,11 +57,12 @@ class ElyDecorator implements DecoratorInterface { | ||||
|         /** @var \SimpleXMLElement $svg */ | ||||
|         $svg = $property->getValue($renderer); | ||||
|  | ||||
|         /** @var \SimpleXMLElement $image */ | ||||
|         $image = $svg->addChild('image'); | ||||
|         $image->addAttribute('x', $startX); | ||||
|         $image->addAttribute('y', $startY); | ||||
|         $image->addAttribute('width', $width); | ||||
|         $image->addAttribute('height', $height); | ||||
|         $image->addAttribute('x', (string)$startX); | ||||
|         $image->addAttribute('y', (string)$startY); | ||||
|         $image->addAttribute('width', (string)$width); | ||||
|         $image->addAttribute('height', (string)$height); | ||||
|         $image->addAttribute('xlink:href', $this->encodeSvgToBase64(self::LOGO)); | ||||
|  | ||||
|         $logo = new Imagick(); | ||||
| @@ -89,8 +95,8 @@ class ElyDecorator implements DecoratorInterface { | ||||
|  | ||||
|             for ($i = $x - $padding; $i <= $x + $padding; $i++) { | ||||
|                 for ($j = $y - $padding; $j <= $y + $padding; $j++) { | ||||
|                     $matrixX = floor($i / $multiple); | ||||
|                     $matrixY = floor($j / $multiple); | ||||
|                     $matrixX = (int)floor($i / $multiple); | ||||
|                     $matrixY = (int)floor($j / $multiple); | ||||
|                     $qrCode->getMatrix()->set($matrixX, $matrixY, 0); | ||||
|                 } | ||||
|             } | ||||
| @@ -104,7 +110,7 @@ class ElyDecorator implements DecoratorInterface { | ||||
|         $outputHeight, | ||||
|         $leftPadding, | ||||
|         $topPadding, | ||||
|         $multiple | ||||
|         $multiple, | ||||
|     ) { | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -3,18 +3,20 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace common\components; | ||||
|  | ||||
| use mito\sentry\Component; | ||||
| use nohnaimer\sentry\Component; | ||||
| use Yii; | ||||
|  | ||||
| class Sentry extends Component { | ||||
|  | ||||
|     public function init() { | ||||
|     public bool $enabled = true; | ||||
|  | ||||
|     public function init(): void { | ||||
|         if (!$this->enabled) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (is_array($this->client) && !isset($this->client['release'])) { | ||||
|             $this->client['release'] = Yii::$app->version; | ||||
|         if (is_array($this->clientOptions) && !isset($this->clientOptions['release'])) { | ||||
|             $this->clientOptions['release'] = Yii::$app->version; | ||||
|         } | ||||
|  | ||||
|         parent::init(); | ||||
|   | ||||
| @@ -10,12 +10,9 @@ use Yii; | ||||
| // TODO: convert to complete Chrly client library | ||||
| class SkinsSystemApi { | ||||
|  | ||||
|     private string $baseDomain; | ||||
|  | ||||
|     private ?ClientInterface $client = null; | ||||
|  | ||||
|     public function __construct(string $baseDomain) { | ||||
|         $this->baseDomain = $baseDomain; | ||||
|     public function __construct(private readonly string $baseDomain) { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -9,22 +9,13 @@ use yii\base\Component; | ||||
|  | ||||
| class StatsD extends Component { | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     public $host; | ||||
|     public string $host; | ||||
|  | ||||
|     /** | ||||
|      * @var int | ||||
|      */ | ||||
|     public $port = 8125; | ||||
|     public int $port = 8125; | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     public $namespace = ''; | ||||
|     public string $namespace = ''; | ||||
|  | ||||
|     private $client; | ||||
|     private ?Client $client = null; | ||||
|  | ||||
|     public function inc(string $key): void { | ||||
|         $this->getClient()->increment($key); | ||||
|   | ||||
| @@ -5,7 +5,7 @@ namespace common\components; | ||||
|  | ||||
| class UserFriendlyRandomKey { | ||||
|  | ||||
|     public static function make(int $length = 18) { | ||||
|     public static function make(int $length = 18): string { | ||||
|         $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'; | ||||
|         $numChars = strlen($chars); | ||||
|         $key = ''; | ||||
|   | ||||
| @@ -8,8 +8,8 @@ namespace common\components; | ||||
|  */ | ||||
| class UserPass { | ||||
|  | ||||
|     public static function make($email, $pass) { | ||||
|         return md5($pass . md5(strtolower($email))); | ||||
|     public static function make($email, string $pass): string { | ||||
|         return md5($pass . md5(strtolower((string)$email))); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -5,14 +5,11 @@ namespace common\config; | ||||
|  | ||||
| use yii\helpers\ArrayHelper; | ||||
|  | ||||
| class ConfigLoader { | ||||
| final readonly class ConfigLoader { | ||||
|  | ||||
|     private const ROOT_PATH = __DIR__ . '/../..'; | ||||
|     private const string ROOT_PATH = __DIR__ . '/../..'; | ||||
|  | ||||
|     private $application; | ||||
|  | ||||
|     public function __construct(string $application) { | ||||
|         $this->application = $application; | ||||
|     public function __construct(private string $application) { | ||||
|     } | ||||
|  | ||||
|     public function getEnvironment(): string { | ||||
| @@ -54,11 +51,15 @@ class ConfigLoader { | ||||
|             $toMerge[] = require $path; | ||||
|         } | ||||
|  | ||||
|         // @phpstan-ignore arguments.count (Should be covered by Yii2 extension) | ||||
|         return ArrayHelper::merge(...$toMerge); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array<string, mixed> | ||||
|      */ | ||||
|     public static function load(string $application): array { | ||||
|         return (new static($application))->getConfig(); | ||||
|         return (new self($application))->getConfig(); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
							
								
								
									
										6
									
								
								common/config/config-phpstan.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								common/config/config-phpstan.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
|  | ||||
| defined('YII_ENV') ?: define('YII_ENV', 'test'); | ||||
|  | ||||
| return common\config\ConfigLoader::load('api'); | ||||
| @@ -1,4 +1,5 @@ | ||||
| <?php | ||||
|  | ||||
| return [ | ||||
|     'version' => '{{PLACE_VERSION_HERE}}', // This will be replaced by build tool | ||||
|     'vendorPath' => dirname(__DIR__, 2) . '/vendor', | ||||
| @@ -55,10 +56,10 @@ return [ | ||||
|             ], | ||||
|         ], | ||||
|         'mailer' => [ | ||||
|             'class' => yii\swiftmailer\Mailer::class, | ||||
|             'class' => yii\symfonymailer\Mailer::class, | ||||
|             'viewPath' => '@common/mail', | ||||
|             'transport' => [ | ||||
|                 'class' => Swift_SmtpTransport::class, | ||||
|                 'class' => Symfony\Component\Mailer\Transport\Smtp\SmtpTransport::class, | ||||
|                 'host' => getenv('SMTP_HOST'), | ||||
|                 'username' => getenv('SMTP_USER'), | ||||
|                 'password' => getenv('SMTP_PASS'), | ||||
| @@ -104,7 +105,7 @@ return [ | ||||
|         'statsd' => [ | ||||
|             'class' => common\components\StatsD::class, | ||||
|             'host' => getenv('STATSD_HOST'), | ||||
|             'port' => getenv('STATSD_PORT') ?: 8125, | ||||
|             'port' => (int)getenv('STATSD_PORT') ?: 8125, | ||||
|             'namespace' => getenv('STATSD_NAMESPACE') ?: 'ely.accounts.' . gethostname() . '.app', | ||||
|         ], | ||||
|         'queue' => [ | ||||
|   | ||||
| @@ -4,7 +4,7 @@ declare(strict_types=1); | ||||
| namespace common\db\mysql; | ||||
|  | ||||
| use SamIT\Yii2\MariaDb\QueryBuilder as MariaDbQueryBuilder; | ||||
| use yii\db\ExpressionInterface; | ||||
| use yii\db\Expression; | ||||
|  | ||||
| class QueryBuilder extends MariaDbQueryBuilder { | ||||
|  | ||||
| @@ -15,7 +15,7 @@ class QueryBuilder extends MariaDbQueryBuilder { | ||||
|  | ||||
|         $orders = []; | ||||
|         foreach ($columns as $name => $direction) { | ||||
|             if ($direction instanceof ExpressionInterface) { | ||||
|             if ($direction instanceof Expression) { | ||||
|                 $orders[] = $direction->expression; | ||||
|             } elseif (is_array($direction)) { | ||||
|                 // This condition branch is our custom solution | ||||
|   | ||||
| @@ -10,13 +10,9 @@ use yii\mail\MessageInterface; | ||||
|  | ||||
| abstract class Template { | ||||
|  | ||||
|     /** | ||||
|      * @var MailerInterface | ||||
|      */ | ||||
|     private $mailer; | ||||
|  | ||||
|     public function __construct(MailerInterface $mailer) { | ||||
|         $this->mailer = $mailer; | ||||
|     public function __construct( | ||||
|         private readonly MailerInterface $mailer, | ||||
|     ) { | ||||
|     } | ||||
|  | ||||
|     abstract public function getSubject(): string; | ||||
| @@ -25,7 +21,7 @@ abstract class Template { | ||||
|      * @return array|string | ||||
|      * @throws InvalidConfigException | ||||
|      */ | ||||
|     public function getFrom() { | ||||
|     public function getFrom(): array|string { | ||||
|         $fromEmail = Yii::$app->params['fromEmail'] ?? ''; | ||||
|         if (!$fromEmail) { | ||||
|             throw new InvalidConfigException('Please specify fromEmail app in app params'); | ||||
| @@ -34,25 +30,31 @@ abstract class Template { | ||||
|         return [$fromEmail => 'Ely.by Accounts']; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array<string, mixed> | ||||
|      */ | ||||
|     public function getParams(): array { | ||||
|         return []; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string|array $to see \yii\mail\MessageInterface::setTo to know the format. | ||||
|      * @param array|string $to see \yii\mail\MessageInterface::setTo to know the format. | ||||
|      * | ||||
|      * @throws \common\emails\exceptions\CannotSendEmailException | ||||
|      */ | ||||
|     public function send($to): void { | ||||
|     public function send(array|string $to): void { | ||||
|         if (!$this->createMessage($to)->send()) { | ||||
|             throw new exceptions\CannotSendEmailException(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string|array | ||||
|      * @return string|array{ | ||||
|      *     html?: string, | ||||
|      *     text?: string, | ||||
|      * } | ||||
|      */ | ||||
|     abstract protected function getView(); | ||||
|     abstract protected function getView(): string|array; | ||||
|  | ||||
|     final protected function getMailer(): MailerInterface { | ||||
|         return $this->mailer; | ||||
|   | ||||
| @@ -9,19 +9,16 @@ use yii\mail\MessageInterface; | ||||
|  | ||||
| abstract class TemplateWithRenderer extends Template { | ||||
|  | ||||
|     /** | ||||
|      * @var RendererInterface | ||||
|      */ | ||||
|     private $renderer; | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     private $locale = 'en'; | ||||
|     private string $locale = 'en'; | ||||
|  | ||||
|     public function __construct(MailerInterface $mailer, RendererInterface $renderer) { | ||||
|     public function __construct( | ||||
|         MailerInterface $mailer, | ||||
|         private readonly RendererInterface $renderer, | ||||
|     ) { | ||||
|         parent::__construct($mailer); | ||||
|         $this->renderer = $renderer; | ||||
|     } | ||||
|  | ||||
|     public function setLocale(string $locale): void { | ||||
| @@ -44,7 +41,7 @@ abstract class TemplateWithRenderer extends Template { | ||||
|         return $this->renderer; | ||||
|     } | ||||
|  | ||||
|     final protected function getView() { | ||||
|     final protected function getView(): string { | ||||
|         return $this->getTemplateName(); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -8,10 +8,7 @@ use yii\base\InvalidCallException; | ||||
|  | ||||
| class ChangeEmail extends Template { | ||||
|  | ||||
|     /** | ||||
|      * @var string|null | ||||
|      */ | ||||
|     private $key; | ||||
|     private ?string $key = null; | ||||
|  | ||||
|     public function setKey(string $key): void { | ||||
|         $this->key = $key; | ||||
| @@ -31,7 +28,7 @@ class ChangeEmail extends Template { | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     protected function getView() { | ||||
|     protected function getView(): array { | ||||
|         return [ | ||||
|             'html' => '@common/emails/views/current-email-confirmation-html', | ||||
|             'text' => '@common/emails/views/current-email-confirmation-text', | ||||
|   | ||||
| @@ -8,15 +8,9 @@ use yii\base\InvalidCallException; | ||||
|  | ||||
| class ConfirmNewEmail extends Template { | ||||
|  | ||||
|     /** | ||||
|      * @var string|null | ||||
|      */ | ||||
|     private $username; | ||||
|     private ?string $username = null; | ||||
|  | ||||
|     /** | ||||
|      * @var string|null | ||||
|      */ | ||||
|     private $key; | ||||
|     private ?string $key = null; | ||||
|  | ||||
|     public function setUsername(string $username): void { | ||||
|         $this->username = $username; | ||||
| @@ -41,7 +35,7 @@ class ConfirmNewEmail extends Template { | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     protected function getView() { | ||||
|     protected function getView(): array { | ||||
|         return [ | ||||
|             'html' => '@common/emails/views/new-email-confirmation-html', | ||||
|             'text' => '@common/emails/views/new-email-confirmation-text', | ||||
|   | ||||
| @@ -8,10 +8,7 @@ use yii\base\InvalidCallException; | ||||
|  | ||||
| class ForgotPasswordEmail extends TemplateWithRenderer { | ||||
|  | ||||
|     /** | ||||
|      * @var ForgotPasswordParams|null | ||||
|      */ | ||||
|     private $params; | ||||
|     private ?ForgotPasswordParams $params = null; | ||||
|  | ||||
|     public function getSubject(): string { | ||||
|         return 'Ely.by Account forgot password'; | ||||
| @@ -31,9 +28,9 @@ class ForgotPasswordEmail extends TemplateWithRenderer { | ||||
|         } | ||||
|  | ||||
|         return [ | ||||
|             'username' => $this->params->getUsername(), | ||||
|             'code' => $this->params->getCode(), | ||||
|             'link' => $this->params->getLink(), | ||||
|             'username' => $this->params->username, | ||||
|             'code' => $this->params->code, | ||||
|             'link' => $this->params->link, | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -3,30 +3,13 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace common\emails\templates; | ||||
|  | ||||
| class ForgotPasswordParams { | ||||
| final readonly class ForgotPasswordParams { | ||||
|  | ||||
|     private $username; | ||||
|  | ||||
|     private $code; | ||||
|  | ||||
|     private $link; | ||||
|  | ||||
|     public function __construct(string $username, string $code, string $link) { | ||||
|         $this->username = $username; | ||||
|         $this->code = $code; | ||||
|         $this->link = $link; | ||||
|     } | ||||
|  | ||||
|     public function getUsername(): string { | ||||
|         return $this->username; | ||||
|     } | ||||
|  | ||||
|     public function getCode(): string { | ||||
|         return $this->code; | ||||
|     } | ||||
|  | ||||
|     public function getLink(): string { | ||||
|         return $this->link; | ||||
|     public function __construct( | ||||
|         public string $username, | ||||
|         public string $code, | ||||
|         public string $link, | ||||
|     ) { | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -8,10 +8,7 @@ use yii\base\InvalidCallException; | ||||
|  | ||||
| class RegistrationEmail extends TemplateWithRenderer { | ||||
|  | ||||
|     /** | ||||
|      * @var RegistrationEmailParams|null | ||||
|      */ | ||||
|     private $params; | ||||
|     private ?RegistrationEmailParams $params = null; | ||||
|  | ||||
|     public function getSubject(): string { | ||||
|         return 'Ely.by Account registration'; | ||||
|   | ||||
| @@ -5,16 +5,11 @@ namespace common\emails\templates; | ||||
|  | ||||
| class RegistrationEmailParams { | ||||
|  | ||||
|     private $username; | ||||
|  | ||||
|     private $code; | ||||
|  | ||||
|     private $link; | ||||
|  | ||||
|     public function __construct(string $username, string $code, string $link) { | ||||
|         $this->username = $username; | ||||
|         $this->code = $code; | ||||
|         $this->link = $link; | ||||
|     public function __construct( | ||||
|         private readonly string $username, | ||||
|         private readonly string $code, | ||||
|         private readonly string $link, | ||||
|     ) { | ||||
|     } | ||||
|  | ||||
|     public function getUsername(): string { | ||||
|   | ||||
| @@ -1,8 +0,0 @@ | ||||
| <?php | ||||
| namespace common\helpers; | ||||
|  | ||||
| use common\components\RabbitMQ\Helper; | ||||
|  | ||||
| class Amqp extends Helper { | ||||
|  | ||||
| } | ||||
| @@ -3,71 +3,71 @@ namespace common\helpers; | ||||
|  | ||||
| final class Error { | ||||
|  | ||||
|     public const USERNAME_REQUIRED = 'error.username_required'; | ||||
|     public const USERNAME_TOO_SHORT = 'error.username_too_short'; | ||||
|     public const USERNAME_TOO_LONG = 'error.username_too_long'; | ||||
|     public const USERNAME_INVALID = 'error.username_invalid'; | ||||
|     public const USERNAME_NOT_AVAILABLE = 'error.username_not_available'; | ||||
|     public const string USERNAME_REQUIRED = 'error.username_required'; | ||||
|     public const string USERNAME_TOO_SHORT = 'error.username_too_short'; | ||||
|     public const string USERNAME_TOO_LONG = 'error.username_too_long'; | ||||
|     public const string USERNAME_INVALID = 'error.username_invalid'; | ||||
|     public const string USERNAME_NOT_AVAILABLE = 'error.username_not_available'; | ||||
|  | ||||
|     public const EMAIL_REQUIRED = 'error.email_required'; | ||||
|     public const EMAIL_TOO_LONG = 'error.email_too_long'; | ||||
|     public const EMAIL_INVALID = 'error.email_invalid'; | ||||
|     public const EMAIL_IS_TEMPMAIL = 'error.email_is_tempmail'; | ||||
|     public const EMAIL_HOST_IS_NOT_ALLOWED = 'error.email_host_is_not_allowed'; | ||||
|     public const EMAIL_NOT_AVAILABLE = 'error.email_not_available'; | ||||
|     public const EMAIL_NOT_FOUND = 'error.email_not_found'; | ||||
|     public const string EMAIL_REQUIRED = 'error.email_required'; | ||||
|     public const string EMAIL_TOO_LONG = 'error.email_too_long'; | ||||
|     public const string EMAIL_INVALID = 'error.email_invalid'; | ||||
|     public const string EMAIL_IS_TEMPMAIL = 'error.email_is_tempmail'; | ||||
|     public const string EMAIL_HOST_IS_NOT_ALLOWED = 'error.email_host_is_not_allowed'; | ||||
|     public const string EMAIL_NOT_AVAILABLE = 'error.email_not_available'; | ||||
|     public const string EMAIL_NOT_FOUND = 'error.email_not_found'; | ||||
|  | ||||
|     public const LOGIN_REQUIRED = 'error.login_required'; | ||||
|     public const LOGIN_NOT_EXIST = 'error.login_not_exist'; | ||||
|     public const string LOGIN_REQUIRED = 'error.login_required'; | ||||
|     public const string LOGIN_NOT_EXIST = 'error.login_not_exist'; | ||||
|  | ||||
|     public const PASSWORD_REQUIRED = 'error.password_required'; | ||||
|     public const PASSWORD_INCORRECT = 'error.password_incorrect'; | ||||
|     public const PASSWORD_TOO_SHORT = 'error.password_too_short'; | ||||
|     public const string PASSWORD_REQUIRED = 'error.password_required'; | ||||
|     public const string PASSWORD_INCORRECT = 'error.password_incorrect'; | ||||
|     public const string PASSWORD_TOO_SHORT = 'error.password_too_short'; | ||||
|  | ||||
|     public const KEY_REQUIRED = 'error.key_required'; | ||||
|     public const KEY_NOT_EXISTS = 'error.key_not_exists'; | ||||
|     public const KEY_EXPIRE = 'error.key_expire'; | ||||
|     public const string KEY_REQUIRED = 'error.key_required'; | ||||
|     public const string KEY_NOT_EXISTS = 'error.key_not_exists'; | ||||
|     public const string KEY_EXPIRE = 'error.key_expire'; | ||||
|  | ||||
|     public const ACCOUNT_BANNED = 'error.account_banned'; | ||||
|     public const ACCOUNT_NOT_ACTIVATED = 'error.account_not_activated'; | ||||
|     public const ACCOUNT_ALREADY_ACTIVATED = 'error.account_already_activated'; | ||||
|     public const ACCOUNT_CANNOT_RESEND_MESSAGE = 'error.account_cannot_resend_message'; | ||||
|     public const string ACCOUNT_BANNED = 'error.account_banned'; | ||||
|     public const string ACCOUNT_NOT_ACTIVATED = 'error.account_not_activated'; | ||||
|     public const string ACCOUNT_ALREADY_ACTIVATED = 'error.account_already_activated'; | ||||
|     public const string ACCOUNT_CANNOT_RESEND_MESSAGE = 'error.account_cannot_resend_message'; | ||||
|  | ||||
|     public const RECENTLY_SENT_MESSAGE = 'error.recently_sent_message'; | ||||
|     public const string RECENTLY_SENT_MESSAGE = 'error.recently_sent_message'; | ||||
|  | ||||
|     public const NEW_PASSWORD_REQUIRED = 'error.newPassword_required'; | ||||
|     public const NEW_RE_PASSWORD_REQUIRED = 'error.newRePassword_required'; | ||||
|     public const NEW_RE_PASSWORD_DOES_NOT_MATCH = self::RE_PASSWORD_DOES_NOT_MATCH; | ||||
|     public const string NEW_PASSWORD_REQUIRED = 'error.newPassword_required'; | ||||
|     public const string NEW_RE_PASSWORD_REQUIRED = 'error.newRePassword_required'; | ||||
|     public const string NEW_RE_PASSWORD_DOES_NOT_MATCH = self::RE_PASSWORD_DOES_NOT_MATCH; | ||||
|  | ||||
|     public const REFRESH_TOKEN_REQUIRED = 'error.refresh_token_required'; | ||||
|     public const REFRESH_TOKEN_NOT_EXISTS = 'error.refresh_token_not_exist'; | ||||
|     public const string REFRESH_TOKEN_REQUIRED = 'error.refresh_token_required'; | ||||
|     public const string REFRESH_TOKEN_NOT_EXISTS = 'error.refresh_token_not_exist'; | ||||
|  | ||||
|     public const CAPTCHA_REQUIRED = 'error.captcha_required'; | ||||
|     public const CAPTCHA_INVALID = 'error.captcha_invalid'; | ||||
|     public const string CAPTCHA_REQUIRED = 'error.captcha_required'; | ||||
|     public const string CAPTCHA_INVALID = 'error.captcha_invalid'; | ||||
|  | ||||
|     public const RULES_AGREEMENT_REQUIRED = 'error.rulesAgreement_required'; | ||||
|     public const string RULES_AGREEMENT_REQUIRED = 'error.rulesAgreement_required'; | ||||
|  | ||||
|     public const RE_PASSWORD_REQUIRED = 'error.rePassword_required'; | ||||
|     public const RE_PASSWORD_DOES_NOT_MATCH = 'error.rePassword_does_not_match'; | ||||
|     public const string RE_PASSWORD_REQUIRED = 'error.rePassword_required'; | ||||
|     public const string RE_PASSWORD_DOES_NOT_MATCH = 'error.rePassword_does_not_match'; | ||||
|  | ||||
|     public const UNSUPPORTED_LANGUAGE = 'error.unsupported_language'; | ||||
|     public const string UNSUPPORTED_LANGUAGE = 'error.unsupported_language'; | ||||
|  | ||||
|     public const SUBJECT_REQUIRED = 'error.subject_required'; | ||||
|     public const MESSAGE_REQUIRED = 'error.message_required'; | ||||
|     public const string SUBJECT_REQUIRED = 'error.subject_required'; | ||||
|     public const string MESSAGE_REQUIRED = 'error.message_required'; | ||||
|  | ||||
|     public const TOTP_REQUIRED = 'error.totp_required'; | ||||
|     public const TOTP_INCORRECT = 'error.totp_incorrect'; | ||||
|     public const string TOTP_REQUIRED = 'error.totp_required'; | ||||
|     public const string TOTP_INCORRECT = 'error.totp_incorrect'; | ||||
|  | ||||
|     public const OTP_ALREADY_ENABLED = 'error.otp_already_enabled'; | ||||
|     public const OTP_NOT_ENABLED = 'error.otp_not_enabled'; | ||||
|     public const string OTP_ALREADY_ENABLED = 'error.otp_already_enabled'; | ||||
|     public const string OTP_NOT_ENABLED = 'error.otp_not_enabled'; | ||||
|  | ||||
|     public const NAME_REQUIRED = 'error.name_required'; | ||||
|     public const string NAME_REQUIRED = 'error.name_required'; | ||||
|  | ||||
|     public const REDIRECT_URI_REQUIRED = 'error.redirectUri_required'; | ||||
|     public const REDIRECT_URI_INVALID = 'error.redirectUri_invalid'; | ||||
|     public const string REDIRECT_URI_REQUIRED = 'error.redirectUri_required'; | ||||
|     public const string REDIRECT_URI_INVALID = 'error.redirectUri_invalid'; | ||||
|  | ||||
|     public const WEBSITE_URL_INVALID = 'error.websiteUrl_invalid'; | ||||
|     public const string WEBSITE_URL_INVALID = 'error.websiteUrl_invalid'; | ||||
|  | ||||
|     public const MINECRAFT_SERVER_IP_INVALID = 'error.minecraftServerIp_invalid'; | ||||
|     public const string MINECRAFT_SERVER_IP_INVALID = 'error.minecraftServerIp_invalid'; | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -26,8 +26,9 @@ class StringHelper { | ||||
|  | ||||
|     public static function isUuid(string $uuid): bool { | ||||
|         try { | ||||
|             Uuid::fromString($uuid); | ||||
|         } catch (\InvalidArgumentException $e) { | ||||
|             /** @throws \InvalidArgumentException */ | ||||
|             Uuid::fromString($uuid); // @phpstan-ignore staticMethod.resultUnused (we don't care about the result, we need only an exception) | ||||
|         } catch (\InvalidArgumentException) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -1,9 +1,11 @@ | ||||
| <?php | ||||
| use yii\helpers\Html; | ||||
|  | ||||
| /* @var $this \yii\web\View view component instance */ | ||||
| /* @var $message \yii\mail\MessageInterface the message being composed */ | ||||
| /* @var $content string main view render result */ | ||||
| /** | ||||
|  * @var \yii\web\View $this view component instance | ||||
|  * @var \yii\mail\MessageInterface $message the message being composed | ||||
|  * @var string $content main view render result | ||||
|  */ | ||||
| ?> | ||||
| <?php $this->beginPage() ?> | ||||
| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| <?php | ||||
| /** | ||||
|  * @var $this \yii\web\View view component instance | ||||
|  * @var $message \yii\mail\MessageInterface the message being composed | ||||
|  * @var $content string main view render result | ||||
|  * @var \yii\web\View $this view component instance | ||||
|  * @var \yii\mail\MessageInterface $message the message being composed | ||||
|  * @var string $content main view render result | ||||
|  */ | ||||
|  | ||||
| ?> | ||||
|   | ||||
| @@ -30,7 +30,7 @@ use const common\LATEST_RULES_VERSION; | ||||
|  * @property int|null    $rules_agreement_version | ||||
|  * @property string|null $registration_ip | ||||
|  * @property string|null $otp_secret | ||||
|  * @property int         $is_otp_enabled | ||||
|  * @property bool        $is_otp_enabled | ||||
|  * @property int         $created_at | ||||
|  * @property int         $updated_at | ||||
|  * @property int         $password_changed_at | ||||
| @@ -81,16 +81,11 @@ class Account extends ActiveRecord { | ||||
|             $passwordHashStrategy = $this->password_hash_strategy; | ||||
|         } | ||||
|  | ||||
|         switch ($passwordHashStrategy) { | ||||
|             case self::PASS_HASH_STRATEGY_OLD_ELY: | ||||
|                 return UserPass::make($this->email, $password) === $this->password_hash; | ||||
|  | ||||
|             case self::PASS_HASH_STRATEGY_YII2: | ||||
|                 return Yii::$app->security->validatePassword($password, $this->password_hash); | ||||
|  | ||||
|             default: | ||||
|                 throw new InvalidConfigException('You must set valid password_hash_strategy before you can validate password'); | ||||
|         } | ||||
|         return match ($passwordHashStrategy) { | ||||
|             self::PASS_HASH_STRATEGY_OLD_ELY => UserPass::make($this->email, $password) === $this->password_hash, | ||||
|             self::PASS_HASH_STRATEGY_YII2 => Yii::$app->security->validatePassword($password, $this->password_hash), | ||||
|             default => throw new InvalidConfigException('You must set valid password_hash_strategy before you can validate password'), | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     public function setPassword(string $password): void { | ||||
| @@ -103,6 +98,9 @@ class Account extends ActiveRecord { | ||||
|         return $this->hasMany(EmailActivation::class, ['account_id' => 'id']); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return \yii\db\ActiveQuery<\common\models\OauthSession> | ||||
|      */ | ||||
|     public function getOauthSessions(): ActiveQuery { | ||||
|         return $this->hasMany(OauthSession::class, ['account_id' => 'id']); | ||||
|     } | ||||
| @@ -116,6 +114,9 @@ class Account extends ActiveRecord { | ||||
|         return $this->hasMany(UsernameHistory::class, ['account_id' => 'id']); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return \yii\db\ActiveQuery<\common\models\AccountSession> | ||||
|      */ | ||||
|     public function getSessions(): ActiveQuery { | ||||
|         return $this->hasMany(AccountSession::class, ['account_id' => 'id']); | ||||
|     } | ||||
|   | ||||
| @@ -8,12 +8,12 @@ use yii\db\ActiveRecord; | ||||
|  | ||||
| /** | ||||
|  * Fields: | ||||
|  * @property integer $id | ||||
|  * @property integer $account_id | ||||
|  * @property int     $id | ||||
|  * @property int     $account_id | ||||
|  * @property string  $refresh_token | ||||
|  * @property integer $last_used_ip | ||||
|  * @property integer $created_at | ||||
|  * @property integer $last_refreshed_at | ||||
|  * @property int     $last_used_ip | ||||
|  * @property int     $created_at | ||||
|  * @property int     $last_refreshed_at | ||||
|  * | ||||
|  * Relations: | ||||
|  * @property Account $account | ||||
|   | ||||
| @@ -37,6 +37,9 @@ class EmailActivation extends ActiveRecord { | ||||
|         return 'email_activations'; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array<self::TYPE_*, class-string<\common\models\EmailActivation>> | ||||
|      */ | ||||
|     public static function getClassMap(): array { | ||||
|         return [ | ||||
|             self::TYPE_REGISTRATION_EMAIL_CONFIRMATION => confirmations\RegistrationConfirmation::class, | ||||
| @@ -46,18 +49,16 @@ class EmailActivation extends ActiveRecord { | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public static function instantiate($row) { | ||||
|     public static function instantiate($row): static { | ||||
|         $type = ArrayHelper::getValue($row, 'type'); | ||||
|         if ($type === null) { | ||||
|             return parent::instantiate($row); | ||||
|         } | ||||
|  | ||||
|         $classMap = self::getClassMap(); | ||||
|         if (!isset($classMap[$type])) { | ||||
|             throw new InvalidConfigException('Unexpected type'); | ||||
|         } | ||||
|         $className = self::getClassMap()[$type] ?? throw new InvalidConfigException('Unexpected type'); | ||||
|  | ||||
|         return new $classMap[$type](); | ||||
|         // @phpstan-ignore return.type (the type is correct, but it seems like it must be fixed within Yii2-extension) | ||||
|         return new $className(); | ||||
|     } | ||||
|  | ||||
|     public static function find(): EmailActivationQuery { | ||||
| @@ -72,9 +73,7 @@ class EmailActivation extends ActiveRecord { | ||||
|             ], | ||||
|             [ | ||||
|                 'class' => PrimaryKeyValueBehavior::class, | ||||
|                 'value' => function(): string { | ||||
|                     return UserFriendlyRandomKey::make(); | ||||
|                 }, | ||||
|                 'value' => fn(): string => UserFriendlyRandomKey::make(), | ||||
|             ], | ||||
|         ]; | ||||
|     } | ||||
|   | ||||
| @@ -6,9 +6,10 @@ namespace common\models; | ||||
| use yii\db\ActiveQuery; | ||||
|  | ||||
| /** | ||||
|  * @extends \yii\db\ActiveQuery<\common\models\EmailActivation> | ||||
|  * @see EmailActivation | ||||
|  */ | ||||
| class EmailActivationQuery extends ActiveQuery { | ||||
| final class EmailActivationQuery extends ActiveQuery { | ||||
|  | ||||
|     public function withType(int ...$typeId): self { | ||||
|         return $this->andWhere(['type' => $typeId]); | ||||
|   | ||||
| @@ -11,7 +11,7 @@ use yii\db\Command; | ||||
|  */ | ||||
| class OauthClientQuery extends ActiveQuery { | ||||
|  | ||||
|     private $showDeleted = false; | ||||
|     private bool $showDeleted = false; | ||||
|  | ||||
|     public function includeDeleted(): self { | ||||
|         $this->showDeleted = true; | ||||
|   | ||||
| @@ -10,12 +10,9 @@ use Yii; | ||||
|  | ||||
| class Textures { | ||||
|  | ||||
|     private const MAX_RETRIES = 3; | ||||
|     private const int MAX_RETRIES = 3; | ||||
|  | ||||
|     protected Account $account; | ||||
|  | ||||
|     public function __construct(Account $account) { | ||||
|         $this->account = $account; | ||||
|     public function __construct(protected Account $account) { | ||||
|     } | ||||
|  | ||||
|     public function getMinecraftResponse(bool $signed = false): array { | ||||
|   | ||||
| @@ -6,7 +6,7 @@ namespace common\notifications; | ||||
| use common\models\Account; | ||||
| use Webmozart\Assert\Assert; | ||||
|  | ||||
| final class AccountDeletedNotification implements NotificationInterface { | ||||
| final readonly class AccountDeletedNotification implements NotificationInterface { | ||||
|  | ||||
|     private Account $account; | ||||
|  | ||||
|   | ||||
| @@ -5,15 +5,12 @@ namespace common\notifications; | ||||
|  | ||||
| use common\models\Account; | ||||
|  | ||||
| final class AccountEditNotification implements NotificationInterface { | ||||
| final readonly class AccountEditNotification implements NotificationInterface { | ||||
|  | ||||
|     private Account $account; | ||||
|  | ||||
|     private array $changedAttributes; | ||||
|  | ||||
|     public function __construct(Account $account, array $changedAttributes) { | ||||
|         $this->account = $account; | ||||
|         $this->changedAttributes = $changedAttributes; | ||||
|     public function __construct( | ||||
|         private Account $account, | ||||
|         private array $changedAttributes, | ||||
|     ) { | ||||
|     } | ||||
|  | ||||
|     public static function getType(): string { | ||||
|   | ||||
| @@ -6,7 +6,7 @@ namespace common\notifications; | ||||
| use common\models\OauthSession; | ||||
| use Webmozart\Assert\Assert; | ||||
|  | ||||
| final class OAuthSessionRevokedNotification implements NotificationInterface { | ||||
| final readonly class OAuthSessionRevokedNotification implements NotificationInterface { | ||||
|  | ||||
|     private OauthSession $oauthSession; | ||||
|  | ||||
|   | ||||
| @@ -7,12 +7,9 @@ use common\models\Account; | ||||
| use Yii; | ||||
| use yii\queue\RetryableJobInterface; | ||||
|  | ||||
| final class ClearAccountSessions implements RetryableJobInterface { | ||||
| final readonly class ClearAccountSessions implements RetryableJobInterface { | ||||
|  | ||||
|     private int $accountId; | ||||
|  | ||||
|     public function __construct(int $accountId) { | ||||
|         $this->accountId = $accountId; | ||||
|     public function __construct(private int $accountId) { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -9,16 +9,13 @@ use yii\queue\RetryableJobInterface; | ||||
|  | ||||
| final class ClearOauthSessions implements RetryableJobInterface { | ||||
|  | ||||
|     public string $clientId; | ||||
|  | ||||
|     /** | ||||
|      * @var int|null unix timestamp, that allows to limit this task to clear only some old sessions | ||||
|      */ | ||||
|     public ?int $notSince; | ||||
|  | ||||
|     public function __construct(string $clientId, int $notSince = null) { | ||||
|         $this->clientId = $clientId; | ||||
|         $this->notSince = $notSince; | ||||
|     public function __construct( | ||||
|         public string $clientId, | ||||
|         /** | ||||
|          * @var int|null unix timestamp, that allows to limit this task to clear only some old sessions | ||||
|          */ | ||||
|         public ?int $notSince = null, | ||||
|     ) { | ||||
|     } | ||||
|  | ||||
|     public static function createFromOauthClient(OauthClient $client, int $notSince = null): self { | ||||
| @@ -26,7 +23,7 @@ final class ClearOauthSessions implements RetryableJobInterface { | ||||
|     } | ||||
|  | ||||
|     public function getTtr(): int { | ||||
|         return 60/*sec*/ * 5/*min*/; | ||||
|         return 60/* sec */ * 5/* min */; | ||||
|     } | ||||
|  | ||||
|     public function canRetry($attempt, $error): bool { | ||||
|   | ||||
| @@ -8,12 +8,9 @@ use common\notifications\NotificationInterface; | ||||
| use yii\db\Expression; | ||||
| use yii\queue\RetryableJobInterface; | ||||
|  | ||||
| final class CreateWebHooksDeliveries implements RetryableJobInterface { | ||||
| final readonly class CreateWebHooksDeliveries implements RetryableJobInterface { | ||||
|  | ||||
|     private NotificationInterface $notification; | ||||
|  | ||||
|     public function __construct(NotificationInterface $notification) { | ||||
|         $this->notification = $notification; | ||||
|     public function __construct(private NotificationInterface $notification) { | ||||
|     } | ||||
|  | ||||
|     public function getTtr(): int { | ||||
|   | ||||
| @@ -7,12 +7,9 @@ use common\models\Account; | ||||
| use Yii; | ||||
| use yii\queue\RetryableJobInterface; | ||||
|  | ||||
| final class DeleteAccount implements RetryableJobInterface { | ||||
| final readonly class DeleteAccount implements RetryableJobInterface { | ||||
|  | ||||
|     private int $accountId; | ||||
|  | ||||
|     public function __construct(int $accountId) { | ||||
|         $this->accountId = $accountId; | ||||
|     public function __construct(private int $accountId) { | ||||
|     } | ||||
|  | ||||
|     public function getTtr(): int { | ||||
|   | ||||
| @@ -20,7 +20,7 @@ class DeliveryWebHook implements RetryableJobInterface { | ||||
|  | ||||
|     public string $url; | ||||
|  | ||||
|     public ?string $secret; | ||||
|     public ?string $secret = null; | ||||
|  | ||||
|     public array $payloads; | ||||
|  | ||||
|   | ||||
| @@ -13,12 +13,12 @@ use Webmozart\Assert\Assert; | ||||
| use Yii; | ||||
| use yii\queue\JobInterface; | ||||
|  | ||||
| class PullMojangUsername implements JobInterface { | ||||
| final class PullMojangUsername implements JobInterface { | ||||
|  | ||||
|     public $username; | ||||
|  | ||||
|     public static function createFromAccount(Account $account): self { | ||||
|         $result = new static(); | ||||
|         $result = new self(); | ||||
|         $result->username = $account->username; | ||||
|  | ||||
|         return $result; | ||||
| @@ -29,17 +29,17 @@ class PullMojangUsername implements JobInterface { | ||||
|      * | ||||
|      * @throws \Exception | ||||
|      */ | ||||
|     public function execute($queue) { | ||||
|     public function execute($queue): void { | ||||
|         Yii::$app->statsd->inc('queue.pullMojangUsername.attempt'); | ||||
|         /** @var MojangApi $mojangApi */ | ||||
|         $mojangApi = Yii::$container->get(MojangApi::class); | ||||
|         try { | ||||
|             $response = $mojangApi->usernameToUUID($this->username); | ||||
|             Yii::$app->statsd->inc('queue.pullMojangUsername.found'); | ||||
|         } catch (NoContentException $e) { | ||||
|         } catch (NoContentException) { | ||||
|             $response = false; | ||||
|             Yii::$app->statsd->inc('queue.pullMojangUsername.not_found'); | ||||
|         } catch (GuzzleException | MojangApiException $e) { | ||||
|         } catch (GuzzleException|MojangApiException) { | ||||
|             Yii::$app->statsd->inc('queue.pullMojangUsername.error'); | ||||
|             return; | ||||
|         } | ||||
|   | ||||
| @@ -7,18 +7,22 @@ use common\emails\EmailHelper; | ||||
| use common\emails\templates\ChangeEmail; | ||||
| use common\models\confirmations\CurrentEmailConfirmation; | ||||
| use Yii; | ||||
| use yii\mail\MailerInterface; | ||||
| use yii\queue\RetryableJobInterface; | ||||
|  | ||||
| class SendCurrentEmailConfirmation implements RetryableJobInterface { | ||||
|  | ||||
|     public $email; | ||||
|     public mixed $email = null; | ||||
|  | ||||
|     public $username; | ||||
|     public mixed $username = null; | ||||
|  | ||||
|     public $code; | ||||
|     public mixed $code = null; | ||||
|  | ||||
|     public function __construct(public MailerInterface $mailer) { | ||||
|     } | ||||
|  | ||||
|     public static function createFromConfirmation(CurrentEmailConfirmation $confirmation): self { | ||||
|         $result = new self(); | ||||
|         $result = new self(Yii::$app->mailer); | ||||
|         $result->email = $confirmation->account->email; | ||||
|         $result->username = $confirmation->account->username; | ||||
|         $result->code = $confirmation->key; | ||||
| @@ -38,9 +42,9 @@ class SendCurrentEmailConfirmation implements RetryableJobInterface { | ||||
|      * @param \yii\queue\Queue $queue | ||||
|      * @throws \common\emails\exceptions\CannotSendEmailException | ||||
|      */ | ||||
|     public function execute($queue) { | ||||
|     public function execute($queue): void { | ||||
|         Yii::$app->statsd->inc('queue.sendCurrentEmailConfirmation.attempt'); | ||||
|         $template = new ChangeEmail(Yii::$app->mailer); | ||||
|         $template = new ChangeEmail($this->mailer); | ||||
|         $template->setKey($this->code); | ||||
|         $template->send(EmailHelper::buildTo($this->username, $this->email)); | ||||
|     } | ||||
|   | ||||
| @@ -38,7 +38,7 @@ class SendNewEmailConfirmation implements RetryableJobInterface { | ||||
|      * @param \yii\queue\Queue $queue | ||||
|      * @throws \common\emails\exceptions\CannotSendEmailException | ||||
|      */ | ||||
|     public function execute($queue) { | ||||
|     public function execute($queue): void { | ||||
|         Yii::$app->statsd->inc('queue.sendNewEmailConfirmation.attempt'); | ||||
|         $template = new ConfirmNewEmail(Yii::$app->mailer); | ||||
|         $template->setKey($this->code); | ||||
|   | ||||
| @@ -47,7 +47,7 @@ class SendPasswordRecoveryEmail implements RetryableJobInterface { | ||||
|      * @param \yii\queue\Queue $queue | ||||
|      * @throws \common\emails\exceptions\CannotSendEmailException | ||||
|      */ | ||||
|     public function execute($queue) { | ||||
|     public function execute($queue): void { | ||||
|         Yii::$app->statsd->inc('queue.sendPasswordRecovery.attempt'); | ||||
|         $template = new ForgotPasswordEmail(Yii::$app->mailer, Yii::$app->emailsRenderer); | ||||
|         $template->setLocale($this->locale); | ||||
|   | ||||
| @@ -47,7 +47,7 @@ class SendRegistrationEmail implements RetryableJobInterface { | ||||
|      * @param \yii\queue\Queue $queue | ||||
|      * @throws \common\emails\exceptions\CannotSendEmailException | ||||
|      */ | ||||
|     public function execute($queue) { | ||||
|     public function execute($queue): void { | ||||
|         Yii::$app->statsd->inc('queue.sendRegistrationEmail.attempt'); | ||||
|         $template = new RegistrationEmail(Yii::$app->mailer, Yii::$app->emailsRenderer); | ||||
|         $template->setLocale($this->locale); | ||||
|   | ||||
| @@ -9,7 +9,7 @@ use Yii; | ||||
|  | ||||
| class ApplicationRedisBridge extends Module { | ||||
|  | ||||
|     protected $config = [ | ||||
|     protected array $config = [ | ||||
|         'module' => 'Redis', | ||||
|     ]; | ||||
|  | ||||
|   | ||||
| @@ -16,7 +16,6 @@ use yii\test\InitDbFixture; | ||||
|  * TODO: try to remove | ||||
|  */ | ||||
| class FixtureHelper extends Module { | ||||
|  | ||||
|     /** | ||||
|      * Redeclare visibility because codeception includes all public methods that do not start with "_" | ||||
|      * and are not excluded by module settings, in actor class. | ||||
| @@ -30,21 +29,21 @@ class FixtureHelper extends Module { | ||||
|         getFixture as protected; | ||||
|     } | ||||
|  | ||||
|     public function _before(TestInterface $test) { | ||||
|     public function _before(TestInterface $test): void { | ||||
|         $this->loadFixtures(); | ||||
|     } | ||||
|  | ||||
|     public function _after(TestInterface $test) { | ||||
|     public function _after(TestInterface $test): void { | ||||
|         $this->unloadFixtures(); | ||||
|     } | ||||
|  | ||||
|     public function globalFixtures() { | ||||
|     public function globalFixtures(): array { | ||||
|         return [ | ||||
|             InitDbFixture::class, | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function fixtures() { | ||||
|     public function fixtures(): array { | ||||
|         return [ | ||||
|             'accounts' => fixtures\AccountFixture::class, | ||||
|             'accountSessions' => fixtures\AccountSessionFixture::class, | ||||
|   | ||||
| @@ -16,10 +16,7 @@ class Fixture extends BaseFixture { | ||||
|     use ArrayAccessTrait; | ||||
|     use FileFixtureTrait; | ||||
|  | ||||
|     /** | ||||
|      * @var Connection | ||||
|      */ | ||||
|     public $redis = 'redis'; | ||||
|     public string|Connection $redis = 'redis'; | ||||
|  | ||||
|     public $keysPrefix = ''; | ||||
|  | ||||
| @@ -27,12 +24,12 @@ class Fixture extends BaseFixture { | ||||
|  | ||||
|     public $data = []; | ||||
|  | ||||
|     public function init() { | ||||
|     public function init(): void { | ||||
|         parent::init(); | ||||
|         $this->redis = Instance::ensure($this->redis, Connection::class); | ||||
|     } | ||||
|  | ||||
|     public function load() { | ||||
|     public function load(): void { | ||||
|         $this->data = []; | ||||
|         foreach ($this->getData() as $key => $data) { | ||||
|             $key = $this->buildKey($key); | ||||
| @@ -47,7 +44,7 @@ class Fixture extends BaseFixture { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function unload() { | ||||
|     public function unload(): void { | ||||
|         $this->redis->flushdb(); | ||||
|     } | ||||
|  | ||||
| @@ -75,7 +72,7 @@ class Fixture extends BaseFixture { | ||||
|         throw new InvalidArgumentException('Unsupported input type'); | ||||
|     } | ||||
|  | ||||
|     protected function buildKey($key): string { | ||||
|     protected function buildKey(string|int $key): string { | ||||
|         return $this->keysPrefix . $key . $this->keysPostfix; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -4,15 +4,14 @@ namespace common\tests\_support\queue; | ||||
| use Codeception\Exception\ModuleException; | ||||
| use Codeception\Module; | ||||
| use Codeception\Module\Yii2; | ||||
| use yii\queue\JobInterface; | ||||
|  | ||||
| class CodeceptionQueueHelper extends Module { | ||||
|  | ||||
|     /** | ||||
|      * Returns last sent message | ||||
|      * | ||||
|      * @return \yii\queue\JobInterface|null | ||||
|      */ | ||||
|     public function grabLastQueuedJob() { | ||||
|     public function grabLastQueuedJob(): ?JobInterface { | ||||
|         $messages = $this->grabQueueJobs(); | ||||
|         $last = end($messages); | ||||
|         if ($last === false) { | ||||
| @@ -27,11 +26,10 @@ class CodeceptionQueueHelper extends Module { | ||||
|      * Each message is `\PhpAmqpLib\Message\AMQPMessage` instance. | ||||
|      * Useful to perform additional checks using `Asserts` module. | ||||
|      * | ||||
|      * @param string|null $exchange | ||||
|      * @return \yii\queue\JobInterface[] | ||||
|      * @throws ModuleException | ||||
|      */ | ||||
|     public function grabQueueJobs() { | ||||
|     public function grabQueueJobs(): array { | ||||
|         $amqp = $this->grabComponent('queue'); | ||||
|         if (!$amqp instanceof Queue) { | ||||
|             throw new ModuleException($this, 'AMQP module is not mocked, can\'t test messages'); | ||||
|   | ||||
| @@ -6,15 +6,15 @@ use yii\queue\Queue as BaseQueue; | ||||
|  | ||||
| class Queue extends BaseQueue { | ||||
|  | ||||
|     private $messages = []; | ||||
|     private array $messages = []; | ||||
|  | ||||
|     public function __set($name, $value) { | ||||
|         // Yii2 components may contains some configuration | ||||
|         // But we just ignore it for this mock component | ||||
|     } | ||||
|  | ||||
|     public function push($job) { | ||||
|         $this->messages[] = $job; | ||||
|     public function push($job): ?string { | ||||
|         return (string)array_push($this->messages, $job); | ||||
|     } | ||||
|  | ||||
|     public function status($id) { | ||||
| @@ -25,8 +25,8 @@ class Queue extends BaseQueue { | ||||
|         return $this->messages; | ||||
|     } | ||||
|  | ||||
|     protected function pushMessage($message, $ttr, $delay, $priority) { | ||||
|         // This function is abstract, but will be not called | ||||
|     protected function pushMessage($message, $ttr, $delay, $priority): string { | ||||
|         return ''; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -4,5 +4,4 @@ use common\config\ConfigLoader; | ||||
| use yii\helpers\ArrayHelper; | ||||
|  | ||||
| return ArrayHelper::merge(ConfigLoader::load('common'), [ | ||||
|  | ||||
| ]); | ||||
|   | ||||
| @@ -3,8 +3,9 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace common\tests\helpers; | ||||
|  | ||||
| use phpmock\Deactivatable; | ||||
| use phpmock\phpunit\MockObjectProxy; | ||||
| use phpmock\phpunit\PHPMock; | ||||
| use PHPUnit\Framework\MockObject\MockObject; | ||||
| use ReflectionClass; | ||||
|  | ||||
| trait ExtendedPHPMock { | ||||
| @@ -13,12 +14,35 @@ trait ExtendedPHPMock { | ||||
|         defineFunctionMock as private defineOriginalFunctionMock; | ||||
|     } | ||||
|  | ||||
|     public function getFunctionMock($namespace, $name): MockObject { | ||||
|         return $this->getOriginalFunctionMock(static::getClassNamespace($namespace), $name); | ||||
|     /** | ||||
|      * @var Deactivatable[] | ||||
|      */ | ||||
|     private array $deactivatables = []; | ||||
|  | ||||
|     public function getFunctionMock($namespace, $name): MockObjectProxy { | ||||
|         // @phpstan-ignore return.type | ||||
|         return $this->getOriginalFunctionMock(self::getClassNamespace($namespace), $name); | ||||
|     } | ||||
|  | ||||
|     public static function defineFunctionMock($namespace, $name) { | ||||
|         static::defineOriginalFunctionMock(static::getClassNamespace($namespace), $name); | ||||
|     public static function defineFunctionMock($namespace, $name): void { | ||||
|         self::defineOriginalFunctionMock(self::getClassNamespace($namespace), $name); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Override this method since original implementation relies on the PHPUnit's state, | ||||
|      * but we're dealing with the Codeception, which uses different event system | ||||
|      */ | ||||
|     public function registerForTearDown(Deactivatable $deactivatable): void { | ||||
|         $this->deactivatables[] = $deactivatable; | ||||
|     } | ||||
|  | ||||
|     protected function _after(): void { | ||||
|         parent::_after(); | ||||
|         foreach ($this->deactivatables as $deactivatable) { | ||||
|             $deactivatable->disable(); | ||||
|         } | ||||
|  | ||||
|         $this->deactivatables = []; | ||||
|     } | ||||
|  | ||||
|     private static function getClassNamespace(string $className): string { | ||||
|   | ||||
| @@ -1,29 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace common\tests\helpers; | ||||
|  | ||||
| use phpmock\mockery\PHPMockery; | ||||
| use ReflectionClass; | ||||
|  | ||||
| class Mock { | ||||
|  | ||||
|     /** | ||||
|      * @param string $className | ||||
|      * @param string $function | ||||
|      * | ||||
|      * @return \Mockery\Expectation | ||||
|      */ | ||||
|     public static function func(string $className, string $function) { | ||||
|         return PHPMockery::mock(self::getClassNamespace($className), $function); | ||||
|     } | ||||
|  | ||||
|     public static function define(string $className, string $function): void { | ||||
|         PHPMockery::define(self::getClassNamespace($className), $function); | ||||
|     } | ||||
|  | ||||
|     private static function getClassNamespace(string $className): string { | ||||
|         return (new ReflectionClass($className))->getNamespaceName(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -9,20 +9,18 @@ use yii\db\ActiveRecord; | ||||
|  | ||||
| class PrimaryKeyValueBehaviorTest extends TestCase { | ||||
|  | ||||
|     public function testGenerateValueForThePrimaryKey() { | ||||
|     public function testGenerateValueForThePrimaryKey(): void { | ||||
|         $model = $this->createDummyModel(); | ||||
|         $behavior = $this->createPartialMock(PrimaryKeyValueBehavior::class, ['isValueExists']); | ||||
|         $behavior->method('isValueExists')->willReturn(false); | ||||
|         $behavior->value = function() { | ||||
|             return 'mock'; | ||||
|         }; | ||||
|         $behavior->value = fn(): string => 'mock'; | ||||
|  | ||||
|         $model->attachBehavior('primary-key-value-behavior', $behavior); | ||||
|         $behavior->setPrimaryKeyValue(); | ||||
|         $this->assertSame('mock', $model->id); | ||||
|     } | ||||
|  | ||||
|     public function testShouldRegenerateValueWhenGeneratedAlreadyExists() { | ||||
|     public function testShouldRegenerateValueWhenGeneratedAlreadyExists(): void { | ||||
|         $model = $this->createDummyModel(); | ||||
|         $behavior = $this->createPartialMock(PrimaryKeyValueBehavior::class, ['isValueExists', 'generateValue']); | ||||
|         $behavior->expects($this->exactly(3))->method('generateValue')->willReturnOnConsecutiveCalls('1', '2', '3'); | ||||
| @@ -33,11 +31,14 @@ class PrimaryKeyValueBehaviorTest extends TestCase { | ||||
|         $this->assertSame('3', $model->id); | ||||
|     } | ||||
|  | ||||
|     private function createDummyModel() { | ||||
|     /** | ||||
|      * @return \yii\db\ActiveRecord&object{ id: string } | ||||
|      */ | ||||
|     private function createDummyModel(): ActiveRecord { | ||||
|         return new class extends ActiveRecord { | ||||
|             public $id; | ||||
|             public string $id; | ||||
|  | ||||
|             public static function primaryKey() { | ||||
|             public static function primaryKey(): array { | ||||
|                 return ['id']; | ||||
|             } | ||||
|         }; | ||||
|   | ||||
| @@ -14,20 +14,19 @@ use GuzzleHttp\Psr7\Response; | ||||
|  | ||||
| class ApiTest extends TestCase { | ||||
|  | ||||
|     /** | ||||
|      * @var Api | ||||
|      */ | ||||
|     private $api; | ||||
|     private Api $api; | ||||
|  | ||||
|     private MockHandler $mockHandler; | ||||
|  | ||||
|     /** | ||||
|      * @var MockHandler | ||||
|      * @var array<array{ | ||||
|      *     request: \Psr\Http\Message\RequestInterface, | ||||
|      *     response: \Psr\Http\Message\ResponseInterface, | ||||
|      *     error: string|null, | ||||
|      *     options: array<mixed>, | ||||
|      * }> | ||||
|      */ | ||||
|     private $mockHandler; | ||||
|  | ||||
|     /** | ||||
|      * @var \Psr\Http\Message\RequestInterface[] | ||||
|      */ | ||||
|     private $history; | ||||
|     private array $history = []; | ||||
|  | ||||
|     protected function setUp(): void { | ||||
|         parent::setUp(); | ||||
| @@ -44,7 +43,7 @@ class ApiTest extends TestCase { | ||||
|         $this->api->setClient($client); | ||||
|     } | ||||
|  | ||||
|     public function testGetTemplate() { | ||||
|     public function testGetTemplate(): void { | ||||
|         $this->mockHandler->append(new Response(200, [], 'mock-response')); | ||||
|  | ||||
|         $request = new TemplateRequest('mock-name', 'mock-locale', ['find-me' => 'please']); | ||||
|   | ||||
| @@ -7,18 +7,13 @@ use common\components\EmailsRenderer\Api; | ||||
| use common\components\EmailsRenderer\Component; | ||||
| use common\components\EmailsRenderer\Request\TemplateRequest; | ||||
| use common\tests\unit\TestCase; | ||||
| use PHPUnit\Framework\MockObject\MockObject; | ||||
|  | ||||
| class ComponentTest extends TestCase { | ||||
|  | ||||
|     /** | ||||
|      * @var Api|\PHPUnit\Framework\MockObject\MockObject | ||||
|      */ | ||||
|     private $api; | ||||
|     private Api&MockObject $api; | ||||
|  | ||||
|     /** | ||||
|      * @var Component | ||||
|      */ | ||||
|     private $component; | ||||
|     private Component $component; | ||||
|  | ||||
|     protected function setUp(): void { | ||||
|         parent::setUp(); | ||||
| @@ -30,7 +25,7 @@ class ComponentTest extends TestCase { | ||||
|             'basePath' => '/images/emails-templates', | ||||
|         ]; | ||||
|         $this->component = new class($componentParams) extends Component { | ||||
|             public $api; | ||||
|             public Api $api; | ||||
|  | ||||
|             protected function getApi(): Api { | ||||
|                 return $this->api; | ||||
| @@ -38,7 +33,7 @@ class ComponentTest extends TestCase { | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     public function testRender() { | ||||
|     public function testRender(): void { | ||||
|         $expectedRequest = new TemplateRequest('mock-name', 'mock-locale', [ | ||||
|             'find-me' => 'please', | ||||
|             'assetsHost' => 'http://localhost/images/emails-templates', | ||||
|   | ||||
| @@ -7,7 +7,7 @@ use Yii; | ||||
|  | ||||
| class QueryBuilderTest extends TestCase { | ||||
|  | ||||
|     public function testBuildOrderByField() { | ||||
|     public function testBuildOrderByField(): void { | ||||
|         $queryBuilder = new QueryBuilder(Yii::$app->db); | ||||
|         $result = $queryBuilder->buildOrderBy(['dummy' => ['first', 'second']]); | ||||
|         $this->assertSame("ORDER BY FIELD(`dummy`,'first','second')", $result); | ||||
|   | ||||
| @@ -6,7 +6,7 @@ use common\tests\unit\TestCase; | ||||
|  | ||||
| class EmailHelperTest extends TestCase { | ||||
|  | ||||
|     public function testBuildTo() { | ||||
|     public function testBuildTo(): void { | ||||
|         $this->assertSame(['mock@ely.by' => 'username'], EmailHelper::buildTo('username', 'mock@ely.by')); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -6,37 +6,29 @@ namespace common\tests\unit\emails; | ||||
| use common\emails\exceptions\CannotSendEmailException; | ||||
| use common\emails\Template; | ||||
| use common\tests\unit\TestCase; | ||||
| use PHPUnit\Framework\MockObject\MockObject; | ||||
| use Yii; | ||||
| use yii\mail\MailerInterface; | ||||
| use yii\mail\MessageInterface; | ||||
|  | ||||
| class TemplateTest extends TestCase { | ||||
|  | ||||
|     /** | ||||
|      * @var Template|\PHPUnit\Framework\MockObject\MockObject $template | ||||
|      */ | ||||
|     private $template; | ||||
|     private Template&MockObject $template; | ||||
|  | ||||
|     /** | ||||
|      * @var MailerInterface|\PHPUnit\Framework\MockObject\MockObject | ||||
|      */ | ||||
|     private $mailer; | ||||
|     private MailerInterface&MockObject $mailer; | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     private $initialFromEmail; | ||||
|     private string $initialFromEmail; | ||||
|  | ||||
|     public function testGetters() { | ||||
|     public function testGetters(): void { | ||||
|         $this->assertSame(['find-me' => 'Ely.by Accounts'], $this->template->getFrom()); | ||||
|         $this->assertSame([], $this->template->getParams()); | ||||
|     } | ||||
|  | ||||
|     public function testSend() { | ||||
|     public function testSend(): void { | ||||
|         $this->runTestForSend(true); | ||||
|     } | ||||
|  | ||||
|     public function testNotSend() { | ||||
|     public function testNotSend(): void { | ||||
|         $this->expectException(CannotSendEmailException::class); | ||||
|         $this->runTestForSend(false); | ||||
|     } | ||||
| @@ -49,16 +41,15 @@ class TemplateTest extends TestCase { | ||||
|         Yii::$app->params['fromEmail'] = 'find-me'; | ||||
|     } | ||||
|  | ||||
|     protected function _after() { | ||||
|     protected function _after(): void { | ||||
|         parent::_after(); | ||||
|         Yii::$app->params['fromEmail'] = $this->initialFromEmail; | ||||
|     } | ||||
|  | ||||
|     private function runTestForSend(bool $sendResult) { | ||||
|     private function runTestForSend(bool $sendResult): void { | ||||
|         $this->template->expects($this->once())->method('getSubject')->willReturn('mock-subject'); | ||||
|         $this->template->expects($this->once())->method('getView')->willReturn('mock-view'); | ||||
|  | ||||
|         /** @var MailerInterface|\PHPUnit\Framework\MockObject\MockObject $message */ | ||||
|         $message = $this->createMock(MessageInterface::class); | ||||
|         $message->expects($this->once())->method('setTo')->with(['to@ely.by' => 'To'])->willReturnSelf(); | ||||
|         $message->expects($this->once())->method('setFrom')->with(['find-me' => 'Ely.by Accounts'])->willReturnSelf(); | ||||
|   | ||||
| @@ -8,43 +8,32 @@ use common\emails\RendererInterface; | ||||
| use common\emails\TemplateWithRenderer; | ||||
| use common\tests\unit\TestCase; | ||||
| use Exception; | ||||
| use PHPUnit\Framework\MockObject\MockObject; | ||||
| use Yii; | ||||
| use yii\mail\MailerInterface; | ||||
| use yii\mail\MessageInterface; | ||||
|  | ||||
| class TemplateWithRendererTest extends TestCase { | ||||
|  | ||||
|     /** | ||||
|      * @var TemplateWithRenderer|\PHPUnit\Framework\MockObject\MockObject $template | ||||
|      */ | ||||
|     private $template; | ||||
|     private TemplateWithRenderer&MockObject $template; | ||||
|  | ||||
|     /** | ||||
|      * @var MailerInterface|\PHPUnit\Framework\MockObject\MockObject | ||||
|      */ | ||||
|     private $mailer; | ||||
|     private MailerInterface&MockObject $mailer; | ||||
|  | ||||
|     /** | ||||
|      * @var RendererInterface|\PHPUnit\Framework\MockObject\MockObject | ||||
|      */ | ||||
|     private $renderer; | ||||
|     private RendererInterface&MockObject $renderer; | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     private $initialFromEmail; | ||||
|     private string $initialFromEmail; | ||||
|  | ||||
|     public function testGetLocale() { | ||||
|     public function testGetLocale(): void { | ||||
|         $this->assertSame('en', $this->template->getLocale()); | ||||
|         $this->template->setLocale('find me'); | ||||
|         $this->assertSame('find me', $this->template->getLocale()); | ||||
|     } | ||||
|  | ||||
|     public function testSend() { | ||||
|     public function testSend(): void { | ||||
|         $this->runTestForSend(); | ||||
|     } | ||||
|  | ||||
|     public function testSendWithRenderError() { | ||||
|     public function testSendWithRenderError(): void { | ||||
|         $renderException = new Exception('find me'); | ||||
|         try { | ||||
|             $this->runTestForSend($renderException); | ||||
| @@ -56,10 +45,10 @@ class TemplateWithRendererTest extends TestCase { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $this->assertFalse(true, 'no exception was thrown'); | ||||
|         $this->fail('no exception was thrown'); | ||||
|     } | ||||
|  | ||||
|     protected function _before() { | ||||
|     protected function _before(): void { | ||||
|         parent::_before(); | ||||
|         $this->mailer = $this->createMock(MailerInterface::class); | ||||
|         $this->renderer = $this->createMock(RendererInterface::class); | ||||
| @@ -68,12 +57,15 @@ class TemplateWithRendererTest extends TestCase { | ||||
|         Yii::$app->params['fromEmail'] = 'find-me'; | ||||
|     } | ||||
|  | ||||
|     protected function _after() { | ||||
|     protected function _after(): void { | ||||
|         parent::_after(); | ||||
|         Yii::$app->params['fromEmail'] = $this->initialFromEmail; | ||||
|     } | ||||
|  | ||||
|     private function runTestForSend($renderException = null) { | ||||
|     /** | ||||
|      * @throws \common\emails\exceptions\CannotRenderEmailException | ||||
|      */ | ||||
|     private function runTestForSend($renderException = null): void { | ||||
|         $renderMethodExpectation = $this->renderer->expects($this->once())->method('render')->with('mock-template', 'mock-locale', []); | ||||
|         if ($renderException === null) { | ||||
|             $renderMethodExpectation->willReturn('mock-template-contents'); | ||||
| @@ -86,7 +78,6 @@ class TemplateWithRendererTest extends TestCase { | ||||
|         $this->template->expects($times())->method('getSubject')->willReturn('mock-subject'); | ||||
|         $this->template->expects($times())->method('getTemplateName')->willReturn('mock-template'); | ||||
|  | ||||
|         /** @var MailerInterface|\PHPUnit\Framework\MockObject\MockObject $message */ | ||||
|         $message = $this->createMock(MessageInterface::class); | ||||
|         $message->expects($times())->method('setTo')->with(['to@ely.by' => 'To'])->willReturnSelf(); | ||||
|         $message->expects($times())->method('setHtmlBody')->with('mock-template-contents')->willReturnSelf(); | ||||
|   | ||||
| @@ -8,25 +8,22 @@ use common\tests\unit\TestCase; | ||||
| use yii\base\InvalidCallException; | ||||
| use yii\mail\MailerInterface; | ||||
|  | ||||
| class ChangeEmailTest extends TestCase { | ||||
| final class ChangeEmailTest extends TestCase { | ||||
|  | ||||
|     /** | ||||
|      * @var ChangeEmail()|\PHPUnit\Framework\MockObject\MockObject | ||||
|      */ | ||||
|     private $template; | ||||
|     private ChangeEmail $template; | ||||
|  | ||||
|     public function testParams() { | ||||
|     public function testParams(): void { | ||||
|         $this->template->setKey('mock-key'); | ||||
|         $params = $this->template->getParams(); | ||||
|         $this->assertSame('mock-key', $params['key']); | ||||
|     } | ||||
|  | ||||
|     public function testInvalidCallOfParams() { | ||||
|     public function testInvalidCallOfParams(): void { | ||||
|         $this->expectException(InvalidCallException::class); | ||||
|         $this->template->getParams(); | ||||
|     } | ||||
|  | ||||
|     protected function _before() { | ||||
|     protected function _before(): void { | ||||
|         parent::_before(); | ||||
|         /** @var MailerInterface|\PHPUnit\Framework\MockObject\MockObject $mailer */ | ||||
|         $mailer = $this->createMock(MailerInterface::class); | ||||
|   | ||||
| @@ -10,12 +10,9 @@ use yii\mail\MailerInterface; | ||||
|  | ||||
| class ConfirmNewEmailTest extends TestCase { | ||||
|  | ||||
|     /** | ||||
|      * @var ConfirmNewEmail|\PHPUnit\Framework\MockObject\MockObject | ||||
|      */ | ||||
|     private $template; | ||||
|     private ConfirmNewEmail $template; | ||||
|  | ||||
|     public function testParams() { | ||||
|     public function testParams(): void { | ||||
|         $this->template->setUsername('mock-username'); | ||||
|         $this->template->setKey('mock-key'); | ||||
|         $params = $this->template->getParams(); | ||||
| @@ -26,22 +23,21 @@ class ConfirmNewEmailTest extends TestCase { | ||||
|     /** | ||||
|      * @dataProvider getInvalidCallsCases | ||||
|      */ | ||||
|     public function testInvalidCallOfParams(?string $username, ?string $key) { | ||||
|     public function testInvalidCallOfParams(?string $username, ?string $key): void { | ||||
|         $this->expectException(InvalidCallException::class); | ||||
|         $username !== null && $this->template->setUsername($username); | ||||
|         $key !== null && $this->template->setKey($key); | ||||
|         $this->template->getParams(); | ||||
|     } | ||||
|  | ||||
|     public function getInvalidCallsCases() { | ||||
|     public function getInvalidCallsCases(): iterable { | ||||
|         yield [null, null]; | ||||
|         yield ['value', null]; | ||||
|         yield [null, 'value']; | ||||
|     } | ||||
|  | ||||
|     protected function _before() { | ||||
|     protected function _before(): void { | ||||
|         parent::_before(); | ||||
|         /** @var MailerInterface|\PHPUnit\Framework\MockObject\MockObject $mailer */ | ||||
|         $mailer = $this->createMock(MailerInterface::class); | ||||
|         $this->template = new ConfirmNewEmail($mailer); | ||||
|     } | ||||
|   | ||||
| @@ -12,12 +12,9 @@ use yii\mail\MailerInterface; | ||||
|  | ||||
| class ForgotPasswordEmailTest extends TestCase { | ||||
|  | ||||
|     /** | ||||
|      * @var ForgotPasswordEmail|\PHPUnit\Framework\MockObject\MockObject | ||||
|      */ | ||||
|     private $template; | ||||
|     private ForgotPasswordEmail $template; | ||||
|  | ||||
|     public function testParams() { | ||||
|     public function testParams(): void { | ||||
|         $this->template->setParams(new ForgotPasswordParams('mock-username', 'mock-code', 'mock-link')); | ||||
|         $params = $this->template->getParams(); | ||||
|         $this->assertSame('mock-username', $params['username']); | ||||
| @@ -25,7 +22,7 @@ class ForgotPasswordEmailTest extends TestCase { | ||||
|         $this->assertSame('mock-link', $params['link']); | ||||
|     } | ||||
|  | ||||
|     public function testInvalidCallOfParams() { | ||||
|     public function testInvalidCallOfParams(): void { | ||||
|         $this->expectException(InvalidCallException::class); | ||||
|         $this->template->getParams(); | ||||
|     } | ||||
|   | ||||
| @@ -12,12 +12,9 @@ use yii\mail\MailerInterface; | ||||
|  | ||||
| class RegistrationEmailTest extends TestCase { | ||||
|  | ||||
|     /** | ||||
|      * @var RegistrationEmail()|\PHPUnit\Framework\MockObject\MockObject | ||||
|      */ | ||||
|     private $template; | ||||
|     private RegistrationEmail $template; | ||||
|  | ||||
|     public function testParams() { | ||||
|     public function testParams(): void { | ||||
|         $this->template->setParams(new RegistrationEmailParams('mock-username', 'mock-code', 'mock-link')); | ||||
|         $params = $this->template->getParams(); | ||||
|         $this->assertSame('mock-username', $params['username']); | ||||
| @@ -25,16 +22,14 @@ class RegistrationEmailTest extends TestCase { | ||||
|         $this->assertSame('mock-link', $params['link']); | ||||
|     } | ||||
|  | ||||
|     public function testInvalidCallOfParams() { | ||||
|     public function testInvalidCallOfParams(): void { | ||||
|         $this->expectException(InvalidCallException::class); | ||||
|         $this->template->getParams(); | ||||
|     } | ||||
|  | ||||
|     protected function _before() { | ||||
|     protected function _before(): void { | ||||
|         parent::_before(); | ||||
|         /** @var MailerInterface|\PHPUnit\Framework\MockObject\MockObject $mailer */ | ||||
|         $mailer = $this->createMock(MailerInterface::class); | ||||
|         /** @var RendererInterface|\PHPUnit\Framework\MockObject\MockObject $renderer */ | ||||
|         $renderer = $this->createMock(RendererInterface::class); | ||||
|         $this->template = new RegistrationEmail($mailer, $renderer); | ||||
|     } | ||||
|   | ||||
| @@ -6,7 +6,7 @@ use common\tests\unit\TestCase; | ||||
|  | ||||
| class StringHelperTest extends TestCase { | ||||
|  | ||||
|     public function testGetEmailMask() { | ||||
|     public function testGetEmailMask(): void { | ||||
|         $this->assertSame('**@ely.by', StringHelper::getEmailMask('e@ely.by')); | ||||
|         $this->assertSame('e**@ely.by', StringHelper::getEmailMask('es@ely.by')); | ||||
|         $this->assertSame('e**i@ely.by', StringHelper::getEmailMask('eri@ely.by')); | ||||
| @@ -14,16 +14,16 @@ class StringHelperTest extends TestCase { | ||||
|         $this->assertSame('эр**уч@елу.бел', StringHelper::getEmailMask('эрикскрауч@елу.бел')); | ||||
|     } | ||||
|  | ||||
|     public function testIsUuid() { | ||||
|     public function testIsUuid(): void { | ||||
|         $this->assertTrue(StringHelper::isUuid('a80b4487-a5c6-45a5-9829-373b4a494135')); | ||||
|         $this->assertTrue(StringHelper::isUuid('a80b4487a5c645a59829373b4a494135')); | ||||
|         $this->assertFalse(StringHelper::isUuid('12345678')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @dataProvider trimProvider() | ||||
|      * @dataProvider trimProvider | ||||
|      */ | ||||
|     public function testTrim($expected, $string) { | ||||
|     public function testTrim(string $expected, string $string): void { | ||||
|         $result = StringHelper::trim($string); | ||||
|         $this->assertSame($expected, $result); | ||||
|     } | ||||
| @@ -34,7 +34,7 @@ class StringHelperTest extends TestCase { | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function trimProvider() { | ||||
|     public static function trimProvider(): array { | ||||
|         return [ | ||||
|             ['foo   bar', '  foo   bar  '], // Simple spaces | ||||
|             ['foo bar', ' foo bar'], // Only left side space | ||||
|   | ||||
| @@ -6,19 +6,19 @@ use common\tests\unit\TestCase; | ||||
|  | ||||
| class AccountSessionTest extends TestCase { | ||||
|  | ||||
|     public function testGenerateRefreshToken() { | ||||
|     public function testGenerateRefreshToken(): void { | ||||
|         $model = new AccountSession(); | ||||
|         $model->generateRefreshToken(); | ||||
|         $this->assertNotNull($model->refresh_token, 'method call will set refresh_token value'); | ||||
|     } | ||||
|  | ||||
|     public function testSetIp() { | ||||
|     public function testSetIp(): void { | ||||
|         $model = new AccountSession(); | ||||
|         $model->setIp('127.0.0.1'); | ||||
|         $this->assertSame(2130706433, $model->last_used_ip, 'method should convert passed ip string to long'); | ||||
|     } | ||||
|  | ||||
|     public function testGetReadableIp() { | ||||
|     public function testGetReadableIp(): void { | ||||
|         $model = new AccountSession(); | ||||
|         $model->last_used_ip = 2130706433; | ||||
|         $this->assertSame('127.0.0.1', $model->getReadableIp(), 'method should convert stored long into readable ip'); | ||||
|   | ||||
| @@ -17,7 +17,7 @@ use const common\LATEST_RULES_VERSION; | ||||
|  */ | ||||
| class AccountTest extends TestCase { | ||||
|  | ||||
|     public function testSetPassword() { | ||||
|     public function testSetPassword(): void { | ||||
|         $model = new Account(); | ||||
|         $model->setPassword('12345678'); | ||||
|         $this->assertNotEmpty($model->password_hash, 'hash should be set'); | ||||
| @@ -25,19 +25,17 @@ class AccountTest extends TestCase { | ||||
|         $this->assertSame(Account::PASS_HASH_STRATEGY_YII2, $model->password_hash_strategy, 'latest password hash should be used'); | ||||
|     } | ||||
|  | ||||
|     public function testValidatePassword() { | ||||
|     public function testValidatePassword(): void { | ||||
|         // Use old hashing algorithm | ||||
|         $model = new Account(); | ||||
|         $model->email = 'erick@skrauch.net'; | ||||
|         $model->password_hash = '2cfdb29eb354af970865a923335d17d9'; // 12345678 | ||||
|         $model->password_hash_strategy = null; // To be sure it's not set | ||||
|         $this->assertTrue($model->validatePassword('12345678', Account::PASS_HASH_STRATEGY_OLD_ELY), 'valid password should pass'); | ||||
|         $this->assertFalse($model->validatePassword('87654321', Account::PASS_HASH_STRATEGY_OLD_ELY), 'invalid password should fail'); | ||||
|  | ||||
|         // Modern hash algorithm should also work | ||||
|         $model = new Account(); | ||||
|         $model->password_hash = '$2y$04$N0q8DaHzlYILCnLYrpZfEeWKEqkPZzbawiS07GbSr/.xbRNweSLU6'; // 12345678 | ||||
|         $model->password_hash_strategy = null; // To be sure it's not set | ||||
|         $this->assertTrue($model->validatePassword('12345678', Account::PASS_HASH_STRATEGY_YII2), 'valid password should pass'); | ||||
|         $this->assertFalse($model->validatePassword('87654321', Account::PASS_HASH_STRATEGY_YII2), 'invalid password should fail'); | ||||
|  | ||||
| @@ -57,7 +55,7 @@ class AccountTest extends TestCase { | ||||
|         $this->assertFalse($model->validatePassword('87654321'), 'invalid password should fail'); | ||||
|     } | ||||
|  | ||||
|     public function testHasMojangUsernameCollision() { | ||||
|     public function testHasMojangUsernameCollision(): void { | ||||
|         $model = new Account(); | ||||
|         $model->username = 'ErickSkrauch'; | ||||
|         $this->assertFalse($model->hasMojangUsernameCollision()); | ||||
| @@ -69,13 +67,13 @@ class AccountTest extends TestCase { | ||||
|         $this->assertTrue($model->hasMojangUsernameCollision()); | ||||
|     } | ||||
|  | ||||
|     public function testGetProfileLink() { | ||||
|     public function testGetProfileLink(): void { | ||||
|         $model = new Account(); | ||||
|         $model->id = '123'; | ||||
|         $model->id = 123; | ||||
|         $this->assertSame('http://ely.by/u123', $model->getProfileLink()); | ||||
|     } | ||||
|  | ||||
|     public function testIsAgreedWithActualRules() { | ||||
|     public function testIsAgreedWithActualRules(): void { | ||||
|         $model = new Account(); | ||||
|         $this->assertFalse($model->isAgreedWithActualRules(), 'field is null'); | ||||
|  | ||||
| @@ -86,7 +84,7 @@ class AccountTest extends TestCase { | ||||
|         $this->assertTrue($model->isAgreedWithActualRules()); | ||||
|     } | ||||
|  | ||||
|     public function testSetRegistrationIp() { | ||||
|     public function testSetRegistrationIp(): void { | ||||
|         $account = new Account(); | ||||
|         $account->setRegistrationIp('42.72.205.204'); | ||||
|         $this->assertSame('42.72.205.204', inet_ntop($account->registration_ip)); | ||||
| @@ -96,7 +94,7 @@ class AccountTest extends TestCase { | ||||
|         $this->assertNull($account->registration_ip); | ||||
|     } | ||||
|  | ||||
|     public function testGetRegistrationIp() { | ||||
|     public function testGetRegistrationIp(): void { | ||||
|         $account = new Account(); | ||||
|         $account->setRegistrationIp('42.72.205.204'); | ||||
|         $this->assertSame('42.72.205.204', $account->getRegistrationIp()); | ||||
| @@ -106,7 +104,7 @@ class AccountTest extends TestCase { | ||||
|         $this->assertNull($account->getRegistrationIp()); | ||||
|     } | ||||
|  | ||||
|     public function testAfterSaveInsertEvent() { | ||||
|     public function testAfterSaveInsertEvent(): void { | ||||
|         $account = new Account(); | ||||
|         $account->afterSave(true, [ | ||||
|             'username' => 'old-username', | ||||
| @@ -114,7 +112,7 @@ class AccountTest extends TestCase { | ||||
|         $this->assertNull($this->tester->grabLastQueuedJob()); | ||||
|     } | ||||
|  | ||||
|     public function testAfterSaveNotMeaningfulAttributes() { | ||||
|     public function testAfterSaveNotMeaningfulAttributes(): void { | ||||
|         $account = new Account(); | ||||
|         $account->afterSave(false, [ | ||||
|             'updatedAt' => time(), | ||||
| @@ -122,7 +120,7 @@ class AccountTest extends TestCase { | ||||
|         $this->assertNull($this->tester->grabLastQueuedJob()); | ||||
|     } | ||||
|  | ||||
|     public function testAfterSavePushEvent() { | ||||
|     public function testAfterSavePushEvent(): void { | ||||
|         $changedAttributes = [ | ||||
|             'username' => 'old-username', | ||||
|             'email' => 'old-email@ely.by', | ||||
| @@ -144,7 +142,7 @@ class AccountTest extends TestCase { | ||||
|         $this->assertSame($changedAttributes, $notification->getPayloads()['changedAttributes']); | ||||
|     } | ||||
|  | ||||
|     public function testAfterDeletePushEvent() { | ||||
|     public function testAfterDeletePushEvent(): void { | ||||
|         $account = new Account(); | ||||
|         $account->id = 1; | ||||
|         $account->status = Account::STATUS_REGISTERED; | ||||
|   | ||||
| @@ -21,18 +21,18 @@ class EmailActivationTest extends TestCase { | ||||
|     /** | ||||
|      * @dataProvider getInstantiateTestCases | ||||
|      */ | ||||
|     public function testInstantiate(int $type, string $expectedClassType) { | ||||
|     public function testInstantiate(int $type, string $expectedClassType): void { | ||||
|         $this->assertInstanceOf($expectedClassType, EmailActivation::findOne(['type' => $type])); | ||||
|     } | ||||
|  | ||||
|     public function getInstantiateTestCases() { | ||||
|     public function getInstantiateTestCases(): iterable { | ||||
|         yield [EmailActivation::TYPE_REGISTRATION_EMAIL_CONFIRMATION, confirmations\RegistrationConfirmation::class]; | ||||
|         yield [EmailActivation::TYPE_FORGOT_PASSWORD_KEY, confirmations\ForgotPassword::class]; | ||||
|         yield [EmailActivation::TYPE_CURRENT_EMAIL_CONFIRMATION, confirmations\CurrentEmailConfirmation::class]; | ||||
|         yield [EmailActivation::TYPE_NEW_EMAIL_CONFIRMATION, confirmations\NewEmailConfirmation::class]; | ||||
|     } | ||||
|  | ||||
|     public function testCanResend() { | ||||
|     public function testCanResend(): void { | ||||
|         $model = $this->createPartialMock(EmailActivation::class, ['getResendTimeout']); | ||||
|         $model->method('getResendTimeout')->willReturn(new DateInterval('PT10M')); | ||||
|  | ||||
| @@ -45,7 +45,7 @@ class EmailActivationTest extends TestCase { | ||||
|         $this->assertEqualsWithDelta(Carbon::now()->subSecond(), $model->canResendAt(), 3); | ||||
|     } | ||||
|  | ||||
|     public function testCanResendWithNullTimeout() { | ||||
|     public function testCanResendWithNullTimeout(): void { | ||||
|         $model = $this->createPartialMock(EmailActivation::class, ['getResendTimeout']); | ||||
|         $model->method('getResendTimeout')->willReturn(null); | ||||
|  | ||||
| @@ -54,7 +54,7 @@ class EmailActivationTest extends TestCase { | ||||
|         $this->assertEqualsWithDelta(Carbon::now(), $model->canResendAt(), 3); | ||||
|     } | ||||
|  | ||||
|     public function testIsStale() { | ||||
|     public function testIsStale(): void { | ||||
|         $model = $this->createPartialMock(EmailActivation::class, ['getExpireDuration']); | ||||
|         $model->method('getExpireDuration')->willReturn(new DateInterval('PT10M')); | ||||
|  | ||||
| @@ -65,7 +65,7 @@ class EmailActivationTest extends TestCase { | ||||
|         $this->assertTrue($model->isStale()); | ||||
|     } | ||||
|  | ||||
|     public function testIsStaleWithNullDuration() { | ||||
|     public function testIsStaleWithNullDuration(): void { | ||||
|         $model = $this->createPartialMock(EmailActivation::class, ['getExpireDuration']); | ||||
|         $model->method('getExpireDuration')->willReturn(null); | ||||
|  | ||||
|   | ||||
| @@ -13,30 +13,24 @@ class OauthClientQueryTest extends TestCase { | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function testDefaultHideDeletedEntries() { | ||||
|     public function testDefaultHideDeletedEntries(): void { | ||||
|         /** @var OauthClient[] $clients */ | ||||
|         $clients = OauthClient::find()->all(); | ||||
|         $this->assertEmpty(array_filter($clients, function(OauthClient $client) { | ||||
|             return (bool)$client->is_deleted === true; | ||||
|         })); | ||||
|         $this->assertEmpty(array_filter($clients, fn(OauthClient $client): bool => (bool)$client->is_deleted === true)); | ||||
|         $this->assertNull(OauthClient::findOne('deleted-oauth-client')); | ||||
|     } | ||||
|  | ||||
|     public function testAllowFindDeletedEntries() { | ||||
|     public function testAllowFindDeletedEntries(): void { | ||||
|         /** @var OauthClient[] $clients */ | ||||
|         $clients = OauthClient::find()->includeDeleted()->all(); | ||||
|         $this->assertNotEmpty(array_filter($clients, function(OauthClient $client) { | ||||
|             return (bool)$client->is_deleted === true; | ||||
|         })); | ||||
|         $this->assertNotEmpty(array_filter($clients, fn(OauthClient $client): bool => (bool)$client->is_deleted === true)); | ||||
|         $client = OauthClient::find() | ||||
|             ->includeDeleted() | ||||
|             ->andWhere(['id' => 'deleted-oauth-client']) | ||||
|             ->one(); | ||||
|         $this->assertInstanceOf(OauthClient::class, $client); | ||||
|         $deletedClients = OauthClient::find()->onlyDeleted()->all(); | ||||
|         $this->assertEmpty(array_filter($deletedClients, function(OauthClient $client) { | ||||
|             return (bool)$client->is_deleted === false; | ||||
|         })); | ||||
|         $this->assertEmpty(array_filter($deletedClients, fn(OauthClient $client): bool => (bool)$client->is_deleted === false)); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -21,7 +21,7 @@ class ClearAccountSessionsTest extends TestCase { | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function testExecute() { | ||||
|     public function testExecute(): void { | ||||
|         /** @var \common\models\Account $bannedAccount */ | ||||
|         $bannedAccount = $this->tester->grabFixture('accounts', 'banned-account'); | ||||
|         $task = new ClearAccountSessions($bannedAccount->id); | ||||
|   | ||||
| @@ -19,7 +19,7 @@ class ClearOauthSessionsTest extends TestCase { | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function testCreateFromClient() { | ||||
|     public function testCreateFromClient(): void { | ||||
|         $client = new OauthClient(); | ||||
|         $client->id = 'mocked-id'; | ||||
|  | ||||
| @@ -34,7 +34,7 @@ class ClearOauthSessionsTest extends TestCase { | ||||
|         $this->assertEqualsWithDelta(time(), $result->notSince, 1); | ||||
|     } | ||||
|  | ||||
|     public function testExecute() { | ||||
|     public function testExecute(): void { | ||||
|         $task = new ClearOauthSessions('deleted-oauth-client-with-sessions', 1519510065); | ||||
|         $task->execute($this->createMock(Queue::class)); | ||||
|  | ||||
|   | ||||
| @@ -3,6 +3,7 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace common\tests\unit\tasks; | ||||
|  | ||||
| use Carbon\Exceptions\UnreachableException; | ||||
| use common\notifications\NotificationInterface; | ||||
| use common\tasks\CreateWebHooksDeliveries; | ||||
| use common\tasks\DeliveryWebHook; | ||||
| @@ -21,7 +22,7 @@ class CreateWebHooksDeliveriesTest extends TestCase { | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function testExecute() { | ||||
|     public function testExecute(): void { | ||||
|         $notification = new class implements NotificationInterface { | ||||
|             public static function getType(): string { | ||||
|                 return 'account.edit'; | ||||
| @@ -33,24 +34,28 @@ class CreateWebHooksDeliveriesTest extends TestCase { | ||||
|         }; | ||||
|  | ||||
|         $queue = $this->createMock(Queue::class); | ||||
|         $queue->expects($this->exactly(2))->method('push')->withConsecutive( | ||||
|             [$this->callback(function(DeliveryWebHook $task): bool { | ||||
|         $invocationCount = $this->exactly(2); | ||||
|         $queue->expects($invocationCount)->method('push')->willReturnCallback(function(DeliveryWebHook $task) use ($invocationCount): bool { | ||||
|             if ($invocationCount->numberOfInvocations() === 1) { | ||||
|                 $this->assertSame('account.edit', $task->type); | ||||
|                 $this->assertSame(['key' => 'value'], $task->payloads); | ||||
|                 $this->assertSame('http://localhost:80/webhooks/ely', $task->url); | ||||
|                 $this->assertSame('my-secret', $task->secret); | ||||
|  | ||||
|                 return true; | ||||
|             })], | ||||
|             [$this->callback(function(DeliveryWebHook $task): bool { | ||||
|             } | ||||
|  | ||||
|             if ($invocationCount->numberOfInvocations() === 2) { | ||||
|                 $this->assertSame('account.edit', $task->type); | ||||
|                 $this->assertSame(['key' => 'value'], $task->payloads); | ||||
|                 $this->assertSame('http://localhost:81/webhooks/ely', $task->url); | ||||
|                 $this->assertNull($task->secret); | ||||
|  | ||||
|                 return true; | ||||
|             })], | ||||
|         ); | ||||
|             } | ||||
|  | ||||
|             throw new UnreachableException(); | ||||
|         }); | ||||
|  | ||||
|         $task = new CreateWebHooksDeliveries($notification); | ||||
|         $task->execute($queue); | ||||
|   | ||||
| @@ -25,7 +25,7 @@ class DeleteAccountTest extends TestCase { | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function testExecute() { | ||||
|     public function testExecute(): void { | ||||
|         /** @var Account $account */ | ||||
|         $account = $this->tester->grabFixture('accounts', 'admin'); | ||||
|         $account->status = Account::STATUS_DELETED; | ||||
| @@ -46,7 +46,7 @@ class DeleteAccountTest extends TestCase { | ||||
|      * When a user restores his account back, the task doesn't removed | ||||
|      * @throws \Throwable | ||||
|      */ | ||||
|     public function testExecuteOnNotDeletedAccount() { | ||||
|     public function testExecuteOnNotDeletedAccount(): void { | ||||
|         /** @var Account $account */ | ||||
|         $account = $this->tester->grabFixture('accounts', 'admin'); | ||||
|         // By default, this account is active | ||||
| @@ -66,7 +66,7 @@ class DeleteAccountTest extends TestCase { | ||||
|      * For each deletion the job will be created, so assert, that job for restored deleting will not work | ||||
|      * @throws \Throwable | ||||
|      */ | ||||
|     public function testExecuteOnDeletedAccountWhichWasRestoredAndThenDeletedAgain() { | ||||
|     public function testExecuteOnDeletedAccountWhichWasRestoredAndThenDeletedAgain(): void { | ||||
|         /** @var Account $account */ | ||||
|         $account = $this->tester->grabFixture('accounts', 'admin'); | ||||
|         $account->status = Account::STATUS_DELETED; | ||||
|   | ||||
| @@ -20,14 +20,14 @@ use yii\queue\Queue; | ||||
|  */ | ||||
| class DeliveryWebHookTest extends TestCase { | ||||
|  | ||||
|     private $historyContainer = []; | ||||
|     private array $historyContainer = []; | ||||
|  | ||||
|     /** | ||||
|      * @var Response|\GuzzleHttp\Exception\GuzzleException | ||||
|      */ | ||||
|     private $response; | ||||
|  | ||||
|     public function testCanRetry() { | ||||
|     public function testCanRetry(): void { | ||||
|         $task = new DeliveryWebHook(); | ||||
|         $this->assertFalse($task->canRetry(1, new \Exception())); | ||||
|         $request = $this->createMock(RequestInterface::class); | ||||
| @@ -38,7 +38,7 @@ class DeliveryWebHookTest extends TestCase { | ||||
|         $this->assertFalse($task->canRetry(5, new ServerException('', $request, $response))); | ||||
|     } | ||||
|  | ||||
|     public function testExecuteSuccessDelivery() { | ||||
|     public function testExecuteSuccessDelivery(): void { | ||||
|         $this->response = new Response(); | ||||
|         $task = $this->createMockedTask(); | ||||
|         $task->type = 'account.edit'; | ||||
| @@ -58,7 +58,7 @@ class DeliveryWebHookTest extends TestCase { | ||||
|         $this->assertSame('key=value&another=value', (string)$request->getBody()); | ||||
|     } | ||||
|  | ||||
|     public function testExecuteSuccessDeliveryWithSignature() { | ||||
|     public function testExecuteSuccessDeliveryWithSignature(): void { | ||||
|         $this->response = new Response(); | ||||
|         $task = $this->createMockedTask(); | ||||
|         $task->type = 'account.edit'; | ||||
| @@ -79,7 +79,7 @@ class DeliveryWebHookTest extends TestCase { | ||||
|         $this->assertSame('key=value&another=value', (string)$request->getBody()); | ||||
|     } | ||||
|  | ||||
|     public function testExecuteHandleClientException() { | ||||
|     public function testExecuteHandleClientException(): void { | ||||
|         $this->response = new Response(403); | ||||
|         $task = $this->createMockedTask(); | ||||
|         $task->type = 'account.edit'; | ||||
| @@ -92,7 +92,7 @@ class DeliveryWebHookTest extends TestCase { | ||||
|         $task->execute($this->createMock(Queue::class)); | ||||
|     } | ||||
|  | ||||
|     public function testExecuteUnhandledException() { | ||||
|     public function testExecuteUnhandledException(): void { | ||||
|         $this->expectException(ServerException::class); | ||||
|  | ||||
|         $this->response = new Response(502); | ||||
| @@ -114,11 +114,11 @@ class DeliveryWebHookTest extends TestCase { | ||||
|         return new class($container, $response) extends DeliveryWebHook { | ||||
|             private $historyContainer; | ||||
|  | ||||
|             private $response; | ||||
|  | ||||
|             public function __construct(array &$historyContainer, $response) { | ||||
|             public function __construct( | ||||
|                 array & $historyContainer, | ||||
|                 private $response, | ||||
|             ) { | ||||
|                 $this->historyContainer = &$historyContainer; | ||||
|                 $this->response = $response; | ||||
|             } | ||||
|  | ||||
|             protected function createStack(): HandlerStack { | ||||
|   | ||||
| @@ -30,7 +30,7 @@ class PullMojangUsernameTest extends TestCase { | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function _before() { | ||||
|     public function _before(): void { | ||||
|         parent::_before(); | ||||
|  | ||||
|         /** @var \PHPUnit\Framework\MockObject\MockObject|MojangApi $mockApi */ | ||||
| @@ -40,14 +40,14 @@ class PullMojangUsernameTest extends TestCase { | ||||
|         Yii::$container->set(MojangApi::class, $mockApi); | ||||
|     } | ||||
|  | ||||
|     public function testCreateFromAccount() { | ||||
|     public function testCreateFromAccount(): void { | ||||
|         $account = new Account(); | ||||
|         $account->username = 'find-me'; | ||||
|         $result = PullMojangUsername::createFromAccount($account); | ||||
|         $this->assertSame('find-me', $result->username); | ||||
|     } | ||||
|  | ||||
|     public function testExecuteUsernameExists() { | ||||
|     public function testExecuteUsernameExists(): void { | ||||
|         $this->mockedMethod->willReturn(new ProfileInfo('069a79f444e94726a5befca90e38aaf5', 'Notch')); | ||||
|  | ||||
|         /** @var MojangUsername $mojangUsernameFixture */ | ||||
| @@ -62,7 +62,7 @@ class PullMojangUsernameTest extends TestCase { | ||||
|         $this->assertLessThanOrEqual(time(), $mojangUsername->last_pulled_at); | ||||
|     } | ||||
|  | ||||
|     public function testExecuteChangedUsernameExists() { | ||||
|     public function testExecuteChangedUsernameExists(): void { | ||||
|         $this->mockedMethod->willReturn(new ProfileInfo('069a79f444e94726a5befca90e38aaf5', 'Notch')); | ||||
|  | ||||
|         /** @var MojangUsername $mojangUsernameFixture */ | ||||
| @@ -77,7 +77,7 @@ class PullMojangUsernameTest extends TestCase { | ||||
|         $this->assertLessThanOrEqual(time(), $mojangUsername->last_pulled_at); | ||||
|     } | ||||
|  | ||||
|     public function testExecuteChangedUsernameNotExists() { | ||||
|     public function testExecuteChangedUsernameNotExists(): void { | ||||
|         $this->mockedMethod->willReturn(new ProfileInfo('607153852b8c4909811f507ed8ee737f', 'Chest')); | ||||
|  | ||||
|         $task = new PullMojangUsername(); | ||||
| @@ -88,7 +88,7 @@ class PullMojangUsernameTest extends TestCase { | ||||
|         $this->assertInstanceOf(MojangUsername::class, $mojangUsername); | ||||
|     } | ||||
|  | ||||
|     public function testExecuteRemoveIfExistsNoMore() { | ||||
|     public function testExecuteRemoveIfExistsNoMore(): void { | ||||
|         $this->mockedMethod->willThrowException(new NoContentException(new Request('GET', ''), new Response())); | ||||
|  | ||||
|         $username = $this->tester->grabFixture('mojangUsernames', 'not-exists')['username']; | ||||
| @@ -100,7 +100,7 @@ class PullMojangUsernameTest extends TestCase { | ||||
|         $this->assertNull($mojangUsername); | ||||
|     } | ||||
|  | ||||
|     public function testExecuteUuidUpdated() { | ||||
|     public function testExecuteUuidUpdated(): void { | ||||
|         $this->mockedMethod->willReturn(new ProfileInfo('f498513ce8c84773be26ecfc7ed5185d', 'jeb')); | ||||
|  | ||||
|         /** @var MojangUsername $mojangInfo */ | ||||
|   | ||||
| @@ -8,11 +8,13 @@ use common\models\AccountQuery; | ||||
| use common\models\confirmations\CurrentEmailConfirmation; | ||||
| use common\tasks\SendCurrentEmailConfirmation; | ||||
| use common\tests\unit\TestCase; | ||||
| use Yii; | ||||
| use yii\queue\Queue; | ||||
| use yii\symfonymailer\Message; | ||||
|  | ||||
| class SendCurrentEmailConfirmationTest extends TestCase { | ||||
|  | ||||
|     public function testCreateFromConfirmation() { | ||||
|     public function testCreateFromConfirmation(): void { | ||||
|         $account = new Account(); | ||||
|         $account->username = 'mock-username'; | ||||
|         $account->email = 'mock@ely.by'; | ||||
| @@ -31,8 +33,8 @@ class SendCurrentEmailConfirmationTest extends TestCase { | ||||
|         $this->assertSame('ABCDEFG', $result->code); | ||||
|     } | ||||
|  | ||||
|     public function testExecute() { | ||||
|         $task = new SendCurrentEmailConfirmation(); | ||||
|     public function testExecute(): void { | ||||
|         $task = new SendCurrentEmailConfirmation(Yii::$app->mailer); | ||||
|         $task->username = 'mock-username'; | ||||
|         $task->email = 'mock@ely.by'; | ||||
|         $task->code = 'GFEDCBA'; | ||||
| @@ -40,12 +42,11 @@ class SendCurrentEmailConfirmationTest extends TestCase { | ||||
|         $task->execute($this->createMock(Queue::class)); | ||||
|  | ||||
|         $this->tester->canSeeEmailIsSent(1); | ||||
|         /** @var \yii\swiftmailer\Message $email */ | ||||
|         /** @var Message $email */ | ||||
|         $email = $this->tester->grabSentEmails()[0]; | ||||
|         $this->assertSame(['mock@ely.by' => 'mock-username'], $email->getTo()); | ||||
|         $this->assertSame('Ely.by Account change E-mail confirmation', $email->getSubject()); | ||||
|         $children = $email->getSwiftMessage()->getChildren()[0]; | ||||
|         $this->assertStringContainsString('GFEDCBA', $children->getBody()); | ||||
|         $this->assertStringContainsString('GFEDCBA', $email->getSymfonyEmail()->getTextBody()); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -12,7 +12,7 @@ use yii\queue\Queue; | ||||
|  | ||||
| class SendNewEmailConfirmationTest extends TestCase { | ||||
|  | ||||
|     public function testCreateFromConfirmation() { | ||||
|     public function testCreateFromConfirmation(): void { | ||||
|         $account = new Account(); | ||||
|         $account->username = 'mock-username'; | ||||
|         $account->lang = 'id'; | ||||
| @@ -31,7 +31,7 @@ class SendNewEmailConfirmationTest extends TestCase { | ||||
|         $this->assertSame('ABCDEFG', $result->code); | ||||
|     } | ||||
|  | ||||
|     public function testExecute() { | ||||
|     public function testExecute(): void { | ||||
|         $task = new SendNewEmailConfirmation(); | ||||
|         $task->username = 'mock-username'; | ||||
|         $task->email = 'mock@ely.by'; | ||||
| @@ -40,12 +40,11 @@ class SendNewEmailConfirmationTest extends TestCase { | ||||
|         $task->execute($this->createMock(Queue::class)); | ||||
|  | ||||
|         $this->tester->canSeeEmailIsSent(1); | ||||
|         /** @var \yii\swiftmailer\Message $email */ | ||||
|         /** @var \yii\symfonymailer\Message $email */ | ||||
|         $email = $this->tester->grabSentEmails()[0]; | ||||
|         $this->assertSame(['mock@ely.by' => 'mock-username'], $email->getTo()); | ||||
|         $this->assertSame('Ely.by Account new E-mail confirmation', $email->getSubject()); | ||||
|         $children = $email->getSwiftMessage()->getChildren()[0]; | ||||
|         $this->assertStringContainsString('GFEDCBA', $children->getBody()); | ||||
|         $this->assertStringContainsString('GFEDCBA', $email->getSymfonyEmail()->getTextBody()); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -9,17 +9,15 @@ use common\models\AccountQuery; | ||||
| use common\models\confirmations\ForgotPassword; | ||||
| use common\tasks\SendPasswordRecoveryEmail; | ||||
| use common\tests\unit\TestCase; | ||||
| use PHPUnit\Framework\MockObject\MockObject; | ||||
| use Yii; | ||||
| use yii\queue\Queue; | ||||
|  | ||||
| class SendPasswordRecoveryEmailTest extends TestCase { | ||||
|  | ||||
|     /** | ||||
|      * @var RendererInterface|\PHPUnit\Framework\MockObject\MockObject | ||||
|      */ | ||||
|     private $renderer; | ||||
|     private RendererInterface&MockObject $renderer; | ||||
|  | ||||
|     public function testCreateFromConfirmation() { | ||||
|     public function testCreateFromConfirmation(): void { | ||||
|         $account = new Account(); | ||||
|         $account->username = 'mock-username'; | ||||
|         $account->email = 'mock@ely.by'; | ||||
| @@ -40,7 +38,7 @@ class SendPasswordRecoveryEmailTest extends TestCase { | ||||
|         $this->assertSame('id', $result->locale); | ||||
|     } | ||||
|  | ||||
|     public function testExecute() { | ||||
|     public function testExecute(): void { | ||||
|         $task = new SendPasswordRecoveryEmail(); | ||||
|         $task->username = 'mock-username'; | ||||
|         $task->email = 'mock@ely.by'; | ||||
| @@ -57,14 +55,14 @@ class SendPasswordRecoveryEmailTest extends TestCase { | ||||
|         $task->execute($this->createMock(Queue::class)); | ||||
|  | ||||
|         $this->tester->canSeeEmailIsSent(1); | ||||
|         /** @var \yii\swiftmailer\Message $email */ | ||||
|         /** @var \yii\symfonymailer\Message $email */ | ||||
|         $email = $this->tester->grabSentEmails()[0]; | ||||
|         $this->assertSame(['mock@ely.by' => 'mock-username'], $email->getTo()); | ||||
|         $this->assertSame('Ely.by Account forgot password', $email->getSubject()); | ||||
|         $this->assertSame('mock-template', $email->getSwiftMessage()->getBody()); | ||||
|         $this->assertSame('mock-template', $email->getSymfonyEmail()->getHtmlBody()); | ||||
|     } | ||||
|  | ||||
|     protected function _before() { | ||||
|     protected function _before(): void { | ||||
|         parent::_before(); | ||||
|  | ||||
|         $this->renderer = $this->createMock(RendererInterface::class); | ||||
|   | ||||
| @@ -9,17 +9,15 @@ use common\models\AccountQuery; | ||||
| use common\models\confirmations\RegistrationConfirmation; | ||||
| use common\tasks\SendRegistrationEmail; | ||||
| use common\tests\unit\TestCase; | ||||
| use PHPUnit\Framework\MockObject\MockObject; | ||||
| use Yii; | ||||
| use yii\queue\Queue; | ||||
|  | ||||
| class SendRegistrationEmailTest extends TestCase { | ||||
|  | ||||
|     /** | ||||
|      * @var RendererInterface|\PHPUnit\Framework\MockObject\MockObject | ||||
|      */ | ||||
|     private $renderer; | ||||
|     private RendererInterface&MockObject $renderer; | ||||
|  | ||||
|     public function testCreateFromConfirmation() { | ||||
|     public function testCreateFromConfirmation(): void { | ||||
|         $account = new Account(); | ||||
|         $account->username = 'mock-username'; | ||||
|         $account->email = 'mock@ely.by'; | ||||
| @@ -40,7 +38,7 @@ class SendRegistrationEmailTest extends TestCase { | ||||
|         $this->assertSame('ru', $result->locale); | ||||
|     } | ||||
|  | ||||
|     public function testExecute() { | ||||
|     public function testExecute(): void { | ||||
|         $task = new SendRegistrationEmail(); | ||||
|         $task->username = 'mock-username'; | ||||
|         $task->email = 'mock@ely.by'; | ||||
| @@ -57,11 +55,11 @@ class SendRegistrationEmailTest extends TestCase { | ||||
|         $task->execute($this->createMock(Queue::class)); | ||||
|  | ||||
|         $this->tester->canSeeEmailIsSent(1); | ||||
|         /** @var \yii\swiftmailer\Message $email */ | ||||
|         /** @var \yii\symfonymailer\Message $email */ | ||||
|         $email = $this->tester->grabSentEmails()[0]; | ||||
|         $this->assertSame(['mock@ely.by' => 'mock-username'], $email->getTo()); | ||||
|         $this->assertSame('Ely.by Account registration', $email->getSubject()); | ||||
|         $this->assertSame('mock-template', $email->getSwiftMessage()->getBody()); | ||||
|         $this->assertSame('mock-template', $email->getSymfonyEmail()->getHtmlBody()); | ||||
|     } | ||||
|  | ||||
|     protected function _before() { | ||||
|   | ||||
| @@ -3,9 +3,11 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace common\tests\unit\validators; | ||||
|  | ||||
| use common\models\Account; | ||||
| use common\tests\fixtures\AccountFixture; | ||||
| use common\tests\unit\TestCase; | ||||
| use common\validators\EmailValidator; | ||||
| use Generator; | ||||
| use yii\base\Model; | ||||
| use yii\validators\EmailValidator as YiiEmailValidator; | ||||
|  | ||||
| @@ -16,7 +18,7 @@ final class EmailValidatorTest extends TestCase { | ||||
|  | ||||
|     private EmailValidator $validator; | ||||
|  | ||||
|     public function _before() { | ||||
|     public function _before(): void { | ||||
|         parent::_before(); | ||||
|  | ||||
|         self::defineFunctionMock(YiiEmailValidator::class, 'checkdnsrr'); | ||||
| @@ -25,7 +27,7 @@ final class EmailValidatorTest extends TestCase { | ||||
|         $this->validator = new EmailValidator(); | ||||
|     } | ||||
|  | ||||
|     public function testValidateTrimming() { | ||||
|     public function testValidateTrimming(): void { | ||||
|         // Prevent it to access to db | ||||
|         $this->getFunctionMock(YiiEmailValidator::class, 'checkdnsrr')->expects($this->any())->willReturn(false); | ||||
|  | ||||
| @@ -35,7 +37,7 @@ final class EmailValidatorTest extends TestCase { | ||||
|         $this->assertSame('testemail@ely.by', $model->field); | ||||
|     } | ||||
|  | ||||
|     public function testValidateAttributeRequired() { | ||||
|     public function testValidateAttributeRequired(): void { | ||||
|         $model = $this->createModel(''); | ||||
|         $this->validator->validateAttribute($model, 'field'); | ||||
|         $this->assertSame(['error.email_required'], $model->getErrors('field')); | ||||
| @@ -45,14 +47,14 @@ final class EmailValidatorTest extends TestCase { | ||||
|         $this->assertNotSame(['error.email_required'], $model->getErrors('field')); | ||||
|     } | ||||
|  | ||||
|     public function testValidateAttributeLength() { | ||||
|     public function testValidateAttributeLength(): void { | ||||
|         $this->getFunctionMock(YiiEmailValidator::class, 'checkdnsrr')->expects($this->any())->willReturn(false); | ||||
|  | ||||
|         $model = $this->createModel( | ||||
|             'emailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemail' . | ||||
|             'emailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemail' . | ||||
|             'emailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemail' . | ||||
|             '@gmail.com' // = 256 symbols | ||||
|             'emailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemail' | ||||
|             . 'emailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemail' | ||||
|             . 'emailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemail' | ||||
|             . '@gmail.com', // = 256 symbols | ||||
|         ); | ||||
|         $this->validator->validateAttribute($model, 'field'); | ||||
|         $this->assertSame(['error.email_too_long'], $model->getErrors('field')); | ||||
| @@ -62,7 +64,7 @@ final class EmailValidatorTest extends TestCase { | ||||
|         $this->assertNotSame(['error.email_too_long'], $model->getErrors('field')); | ||||
|     } | ||||
|  | ||||
|     public function testValidateAttributeEmailCaseNotExistsDomain() { | ||||
|     public function testValidateAttributeEmailCaseNotExistsDomain(): void { | ||||
|         $this->getFunctionMock(YiiEmailValidator::class, 'checkdnsrr')->expects($this->any())->willReturn(false); | ||||
|         $this->getFunctionMock(YiiEmailValidator::class, 'dns_get_record')->expects($this->never()); | ||||
|  | ||||
| @@ -71,7 +73,7 @@ final class EmailValidatorTest extends TestCase { | ||||
|         $this->assertSame(['error.email_invalid'], $model->getErrors('field')); | ||||
|     } | ||||
|  | ||||
|     public function testValidateAttributeEmailCaseExistsDomainButWithoutMXRecord() { | ||||
|     public function testValidateAttributeEmailCaseExistsDomainButWithoutMXRecord(): void { | ||||
|         $this->getFunctionMock(YiiEmailValidator::class, 'checkdnsrr')->expects($this->exactly(2))->willReturnOnConsecutiveCalls(false, true); | ||||
|         $this->getFunctionMock(YiiEmailValidator::class, 'dns_get_record')->expects($this->any())->willReturn(['127.0.0.1']); | ||||
|  | ||||
| @@ -80,7 +82,7 @@ final class EmailValidatorTest extends TestCase { | ||||
|         $this->assertNotSame(['error.email_invalid'], $model->getErrors('field')); | ||||
|     } | ||||
|  | ||||
|     public function testValidateAttributeEmailCaseExistsDomainWithMXRecord() { | ||||
|     public function testValidateAttributeEmailCaseExistsDomainWithMXRecord(): void { | ||||
|         $this->getFunctionMock(YiiEmailValidator::class, 'checkdnsrr')->expects($this->any())->willReturn(true); | ||||
|         $this->getFunctionMock(YiiEmailValidator::class, 'dns_get_record')->expects($this->any())->willReturn(['mx.google.com']); | ||||
|  | ||||
| @@ -98,7 +100,7 @@ final class EmailValidatorTest extends TestCase { | ||||
|         $this->assertSame(['error.email_invalid'], $model->getErrors('field')); | ||||
|     } | ||||
|  | ||||
|     public function testValidateAttributeTempmail() { | ||||
|     public function testValidateAttributeTempmail(): void { | ||||
|         $this->getFunctionMock(YiiEmailValidator::class, 'checkdnsrr')->expects($this->any())->willReturn(true); | ||||
|         $this->getFunctionMock(YiiEmailValidator::class, 'dns_get_record')->expects($this->any())->willReturn(['127.0.0.1']); | ||||
|  | ||||
| @@ -114,7 +116,7 @@ final class EmailValidatorTest extends TestCase { | ||||
|     /** | ||||
|      * @dataProvider getValidateAttributeBlacklistedHostTestCases | ||||
|      */ | ||||
|     public function testValidateAttributeBlacklistedHost(string $email, bool $expectValid) { | ||||
|     public function testValidateAttributeBlacklistedHost(string $email, bool $expectValid): void { | ||||
|         $this->getFunctionMock(YiiEmailValidator::class, 'checkdnsrr')->expects($this->any())->willReturn(true); | ||||
|         $this->getFunctionMock(YiiEmailValidator::class, 'dns_get_record')->expects($this->any())->willReturn(['127.0.0.1']); | ||||
|  | ||||
| @@ -128,7 +130,7 @@ final class EmailValidatorTest extends TestCase { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function getValidateAttributeBlacklistedHostTestCases() { | ||||
|     public static function getValidateAttributeBlacklistedHostTestCases(): Generator { | ||||
|         yield 'seznam.cz' => ['user@seznam.cz', false]; | ||||
|         yield 'valid' => ['valid@google.com', true]; | ||||
|     } | ||||
| @@ -136,7 +138,7 @@ final class EmailValidatorTest extends TestCase { | ||||
|     /** | ||||
|      * @dataProvider getValidateAttributeIdnaTestCases | ||||
|      */ | ||||
|     public function testValidateAttributeIdna(string $input, string $expectedOutput) { | ||||
|     public function testValidateAttributeIdna(string $input, string $expectedOutput): void { | ||||
|         $this->getFunctionMock(YiiEmailValidator::class, 'checkdnsrr')->expects($this->any())->willReturn(true); | ||||
|         $this->getFunctionMock(YiiEmailValidator::class, 'dns_get_record')->expects($this->any())->willReturn(['127.0.0.1']); | ||||
|  | ||||
| @@ -145,13 +147,13 @@ final class EmailValidatorTest extends TestCase { | ||||
|         $this->assertSame($expectedOutput, $model->field); | ||||
|     } | ||||
|  | ||||
|     public function getValidateAttributeIdnaTestCases() { | ||||
|     public static function getValidateAttributeIdnaTestCases(): Generator { | ||||
|         yield ['qdushyantasunassm@❕.gq', 'qdushyantasunassm@xn--bei.gq']; | ||||
|         yield ['Rafaelaabraão@gmail.com', 'xn--rafaelaabrao-dcb@gmail.com']; | ||||
|         yield ['valid-email@gmail.com', 'valid-email@gmail.com']; | ||||
|     } | ||||
|  | ||||
|     public function testValidateAttributeUnique() { | ||||
|     public function testValidateAttributeUnique(): void { | ||||
|         $this->getFunctionMock(YiiEmailValidator::class, 'checkdnsrr')->expects($this->any())->willReturn(true); | ||||
|         $this->getFunctionMock(YiiEmailValidator::class, 'dns_get_record')->expects($this->any())->willReturn(['127.0.0.1']); | ||||
|  | ||||
| @@ -159,7 +161,7 @@ final class EmailValidatorTest extends TestCase { | ||||
|             'accounts' => AccountFixture::class, | ||||
|         ]); | ||||
|  | ||||
|         /** @var \common\models\Account $accountFixture */ | ||||
|         /** @var Account $accountFixture */ | ||||
|         $accountFixture = $this->tester->grabFixture('accounts', 'admin'); | ||||
|  | ||||
|         $model = $this->createModel($accountFixture->email); | ||||
| @@ -167,9 +169,7 @@ final class EmailValidatorTest extends TestCase { | ||||
|         $this->assertSame(['error.email_not_available'], $model->getErrors('field')); | ||||
|  | ||||
|         $model = $this->createModel($accountFixture->email); | ||||
|         $this->validator->accountCallback = function() use ($accountFixture) { | ||||
|             return $accountFixture->id; | ||||
|         }; | ||||
|         $this->validator->accountCallback = fn() => $accountFixture->id; | ||||
|         $this->validator->validateAttribute($model, 'field'); | ||||
|         $this->assertNotSame(['error.email_not_available'], $model->getErrors('field')); | ||||
|         $this->validator->accountCallback = null; | ||||
| @@ -180,12 +180,11 @@ final class EmailValidatorTest extends TestCase { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $fieldValue | ||||
|      * @return Model | ||||
|      * @return Model&object{ field: mixed } | ||||
|      */ | ||||
|     private function createModel(string $fieldValue): Model { | ||||
|         $class = new class extends Model { | ||||
|             public $field; | ||||
|             public string $field; | ||||
|         }; | ||||
|  | ||||
|         $class->field = $fieldValue; | ||||
|   | ||||
| @@ -9,7 +9,7 @@ class MinecraftServerAddressValidatorTest extends TestCase { | ||||
|     /** | ||||
|      * @dataProvider domainNames | ||||
|      */ | ||||
|     public function testValidate($address, $shouldBeValid) { | ||||
|     public function testValidate(string $address, bool $shouldBeValid): void { | ||||
|         $validator = new MinecraftServerAddressValidator(); | ||||
|         $validator->message = 'mock message'; | ||||
|         $validator->validate($address, $errors); | ||||
| @@ -20,7 +20,7 @@ class MinecraftServerAddressValidatorTest extends TestCase { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function domainNames() { | ||||
|     public function domainNames(): array { | ||||
|         return [ | ||||
|             ['localhost',            true], | ||||
|             ['localhost:25565',      true], | ||||
|   | ||||
| @@ -8,24 +8,21 @@ use yii\base\Model; | ||||
|  | ||||
| class UsernameValidatorTest extends TestCase { | ||||
|  | ||||
|     /** | ||||
|      * @var UsernameValidator | ||||
|      */ | ||||
|     private $validator; | ||||
|     private UsernameValidator $validator; | ||||
|  | ||||
|     public function _before() { | ||||
|     public function _before(): void { | ||||
|         parent::_before(); | ||||
|         $this->validator = new UsernameValidator(); | ||||
|     } | ||||
|  | ||||
|     public function testValidateTrimming() { | ||||
|     public function testValidateTrimming(): void { | ||||
|         $model = $this->createModel("HereIsJohnny#\u{feff}"); // Zero width no-break space (U+FEFF) | ||||
|         $this->validator->validateAttribute($model, 'field'); | ||||
|         $this->assertSame(['error.username_invalid'], $model->getErrors('field')); | ||||
|         $this->assertSame('HereIsJohnny#', $model->field); | ||||
|     } | ||||
|  | ||||
|     public function testValidateAttributeRequired() { | ||||
|     public function testValidateAttributeRequired(): void { | ||||
|         $model = $this->createModel(''); | ||||
|         $this->validator->validateAttribute($model, 'field'); | ||||
|         $this->assertSame(['error.username_required'], $model->getErrors('field')); | ||||
| @@ -35,7 +32,7 @@ class UsernameValidatorTest extends TestCase { | ||||
|         $this->assertNotSame(['error.username_required'], $model->getErrors('field')); | ||||
|     } | ||||
|  | ||||
|     public function testValidateAttributeLength() { | ||||
|     public function testValidateAttributeLength(): void { | ||||
|         $model = $this->createModel('at'); | ||||
|         $this->validator->validateAttribute($model, 'field'); | ||||
|         $this->assertSame(['error.username_too_short'], $model->getErrors('field')); | ||||
| @@ -51,7 +48,7 @@ class UsernameValidatorTest extends TestCase { | ||||
|     } | ||||
|  | ||||
|     // TODO: rewrite this test with @provider usage | ||||
|     public function testValidateAttributePattern() { | ||||
|     public function testValidateAttributePattern(): void { | ||||
|         $shouldBeValid = [ | ||||
|             'русский_ник', 'русский_ник_на_грани!', 'numbers1132', '*__*-Stars-*__*', '1-_.!$%^&*()[]', | ||||
|             '[ESP]Эрик', 'Свят_помидор;', 'зроблена_ў_беларусі:)', | ||||
| @@ -72,7 +69,7 @@ class UsernameValidatorTest extends TestCase { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function testValidateAttributeUnique() { | ||||
|     public function testValidateAttributeUnique(): void { | ||||
|         $this->tester->haveFixtures([ | ||||
|             'accounts' => AccountFixture::class, | ||||
|         ]); | ||||
| @@ -85,9 +82,7 @@ class UsernameValidatorTest extends TestCase { | ||||
|         $this->assertSame(['error.username_not_available'], $model->getErrors('field')); | ||||
|  | ||||
|         $model = $this->createModel($accountFixture->username); | ||||
|         $this->validator->accountCallback = function() use ($accountFixture) { | ||||
|             return $accountFixture->id; | ||||
|         }; | ||||
|         $this->validator->accountCallback = fn() => $accountFixture->id; | ||||
|         $this->validator->validateAttribute($model, 'field'); | ||||
|         $this->assertNotSame(['error.username_not_available'], $model->getErrors('field')); | ||||
|         $this->validator->accountCallback = null; | ||||
| @@ -99,11 +94,11 @@ class UsernameValidatorTest extends TestCase { | ||||
|  | ||||
|     /** | ||||
|      * @param string $fieldValue | ||||
|      * @return Model | ||||
|      * @return Model&object{ field: string } | ||||
|      */ | ||||
|     private function createModel(string $fieldValue): Model { | ||||
|         $class = new class extends Model { | ||||
|             public $field; | ||||
|             public string $field; | ||||
|         }; | ||||
|  | ||||
|         $class->field = $fieldValue; | ||||
|   | ||||
| @@ -15,15 +15,15 @@ use yii\validators\Validator; | ||||
| final class EmailValidator extends Validator { | ||||
|  | ||||
|     /** | ||||
|      * @var callable(): int the function must return the account id for which the current validation is being performed. | ||||
|      * @phpstan-var \Closure(): int the function must return the account id for which the current validation is being performed. | ||||
|      * Allows you to skip the email uniqueness check for the current account. | ||||
|      */ | ||||
|     public $accountCallback; | ||||
|     public ?\Closure $accountCallback = null; | ||||
|  | ||||
|     public $skipOnEmpty = false; | ||||
|  | ||||
|     public function validateAttribute($model, $attribute): void { | ||||
|         $trim = new validators\FilterValidator(['filter' => [StringHelper::class, 'trim']]); | ||||
|         $trim = new validators\FilterValidator(['filter' => StringHelper::trim(...)]); | ||||
|  | ||||
|         $required = new validators\RequiredValidator(); | ||||
|         $required->message = E::EMAIL_REQUIRED; | ||||
| @@ -40,7 +40,7 @@ final class EmailValidator extends Validator { | ||||
|         $additionalEmail = new class extends Validator { | ||||
|             protected function validateValue($value): ?array { | ||||
|                 // Disallow emails starting with slash since Postfix (or someone before?) can't correctly handle it | ||||
|                 if (str_starts_with($value, '/')) { | ||||
|                 if (str_starts_with((string)$value, '/')) { | ||||
|                     return [E::EMAIL_INVALID, []]; | ||||
|                 } | ||||
|  | ||||
| @@ -57,7 +57,7 @@ final class EmailValidator extends Validator { | ||||
|             ]; | ||||
|  | ||||
|             protected function validateValue($value): ?array { | ||||
|                 $host = explode('@', $value)[1]; | ||||
|                 $host = explode('@', (string)$value)[1]; | ||||
|                 if (in_array($host, $this->hosts, true)) { | ||||
|                     return [E::EMAIL_HOST_IS_NOT_ALLOWED, []]; | ||||
|                 } | ||||
| @@ -81,15 +81,15 @@ final class EmailValidator extends Validator { | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         $this->executeValidation($trim, $model, $attribute) && | ||||
|         $this->executeValidation($required, $model, $attribute) && | ||||
|         $this->executeValidation($length, $model, $attribute) && | ||||
|         $this->executeValidation($email, $model, $attribute) && | ||||
|         $this->executeValidation($additionalEmail, $model, $attribute) && | ||||
|         $this->executeValidation($tempmail, $model, $attribute) && | ||||
|         $this->executeValidation($blacklist, $model, $attribute) && | ||||
|         $this->executeValidation($idnaDomain, $model, $attribute) && | ||||
|         $this->executeValidation($unique, $model, $attribute); | ||||
|         $this->executeValidation($trim, $model, $attribute) | ||||
|         && $this->executeValidation($required, $model, $attribute) | ||||
|         && $this->executeValidation($length, $model, $attribute) | ||||
|         && $this->executeValidation($email, $model, $attribute) | ||||
|         && $this->executeValidation($additionalEmail, $model, $attribute) | ||||
|         && $this->executeValidation($tempmail, $model, $attribute) | ||||
|         && $this->executeValidation($blacklist, $model, $attribute) | ||||
|         && $this->executeValidation($idnaDomain, $model, $attribute) | ||||
|         && $this->executeValidation($unique, $model, $attribute); | ||||
|     } | ||||
|  | ||||
|     private function executeValidation(Validator $validator, Model $model, string $attribute): bool { | ||||
|   | ||||
| @@ -27,7 +27,7 @@ class LanguageValidator extends Validator { | ||||
|         $primary = Locale::getPrimaryLanguage($value); | ||||
|         $region = Locale::getRegion($value); | ||||
|         $locales = ResourceBundle::getLocales(''); // http://php.net/manual/ru/resourcebundle.locales.php#115965 | ||||
|         if (($region !== '' && strtolower($primary) !== strtolower($region)) && !in_array($value, $locales)) { | ||||
|         if (($region !== '' && strtolower((string)$primary) !== strtolower((string)$region)) && !in_array($value, $locales)) { | ||||
|             return [$this->message, []]; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,7 @@ use yii\validators\Validator; | ||||
|  | ||||
| class MinecraftServerAddressValidator extends Validator { | ||||
|  | ||||
|     protected function validateValue($value) { | ||||
|     protected function validateValue($value): ?array { | ||||
|         // we will add minecraft protocol to help parse_url understand all another parts | ||||
|         $urlParts = parse_url('minecraft://' . $value); | ||||
|         $cnt = count($urlParts); | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| <?php | ||||
| namespace common\validators; | ||||
|  | ||||
| use Closure; | ||||
| use common\helpers\Error as E; | ||||
| use common\helpers\StringHelper; | ||||
| use common\models\Account; | ||||
| @@ -12,15 +13,15 @@ use yii\validators\Validator; | ||||
| class UsernameValidator extends Validator { | ||||
|  | ||||
|     /** | ||||
|      * @var \Closure the function must return the account id for which the current validation is being performed. | ||||
|      * @phpstan-var \Closure(): int the function must return the account id for which the current validation is being performed. | ||||
|      * Allows you to skip the username check for the current account. | ||||
|      */ | ||||
|     public $accountCallback; | ||||
|     public ?Closure $accountCallback = null; | ||||
|  | ||||
|     public $skipOnEmpty = false; | ||||
|  | ||||
|     public function validateAttribute($model, $attribute) { | ||||
|         $filter = new validators\FilterValidator(['filter' => [StringHelper::class, 'trim']]); | ||||
|     public function validateAttribute($model, $attribute): ?array { | ||||
|         $filter = new validators\FilterValidator(['filter' => StringHelper::trim(...)]); | ||||
|  | ||||
|         $required = new validators\RequiredValidator(); | ||||
|         $required->message = E::USERNAME_REQUIRED; | ||||
| @@ -39,16 +40,18 @@ class UsernameValidator extends Validator { | ||||
|         $unique->targetClass = Account::class; | ||||
|         $unique->targetAttribute = 'username'; | ||||
|         if ($this->accountCallback !== null) { | ||||
|             $unique->filter = function(QueryInterface $query) { | ||||
|             $unique->filter = function(QueryInterface $query): void { | ||||
|                 $query->andWhere(['NOT', ['id' => ($this->accountCallback)()]]); | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         $this->executeValidation($filter, $model, $attribute) && | ||||
|         $this->executeValidation($required, $model, $attribute) && | ||||
|         $this->executeValidation($length, $model, $attribute) && | ||||
|         $this->executeValidation($pattern, $model, $attribute) && | ||||
|         $this->executeValidation($unique, $model, $attribute); | ||||
|         $this->executeValidation($filter, $model, $attribute) | ||||
|         && $this->executeValidation($required, $model, $attribute) | ||||
|         && $this->executeValidation($length, $model, $attribute) | ||||
|         && $this->executeValidation($pattern, $model, $attribute) | ||||
|         && $this->executeValidation($unique, $model, $attribute); | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     protected function executeValidation(Validator $validator, Model $model, string $attribute) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user