Implemented PHP-CS-Fixer support

This commit is contained in:
ErickSkrauch
2018-04-17 23:47:25 +03:00
parent bfdcaf2233
commit 02ea7346a8
115 changed files with 883 additions and 363 deletions

View File

@@ -5,6 +5,7 @@ use yii\base\Behavior;
use yii\helpers\ArrayHelper;
class DataBehavior extends Behavior {
/**
* @var string имя атрибута, к которому будет применяться поведение
*/

View File

@@ -30,7 +30,7 @@ class EmailActivationExpirationBehavior extends Behavior {
* @see EmailActivation::compareTime()
* @return bool
*/
public function canRepeat() : bool {
public function canRepeat(): bool {
return $this->compareTime($this->repeatTimeout);
}
@@ -44,7 +44,7 @@ class EmailActivationExpirationBehavior extends Behavior {
* @see EmailActivation::compareTime()
* @return bool
*/
public function isExpired() : bool {
public function isExpired(): bool {
return $this->compareTime($this->expirationTimeout);
}
@@ -53,7 +53,7 @@ class EmailActivationExpirationBehavior extends Behavior {
*
* @return int
*/
public function canRepeatIn() : int {
public function canRepeatIn(): int {
return $this->calculateTime($this->repeatTimeout);
}
@@ -62,11 +62,11 @@ class EmailActivationExpirationBehavior extends Behavior {
*
* @return int
*/
public function expireIn() : int {
public function expireIn(): int {
return $this->calculateTime($this->expirationTimeout);
}
protected function compareTime(int $value) : bool {
protected function compareTime(int $value): bool {
if ($value < 0) {
return false;
}
@@ -78,7 +78,7 @@ class EmailActivationExpirationBehavior extends Behavior {
return time() > $this->calculateTime($value);
}
protected function calculateTime(int $value) : int {
protected function calculateTime(int $value): int {
return $this->owner->created_at + $value;
}

View File

@@ -24,7 +24,7 @@ class PrimaryKeyValueBehavior extends Behavior {
];
}
public function setPrimaryKeyValue() : bool {
public function setPrimaryKeyValue(): bool {
if ($this->owner->getPrimaryKey() === null) {
$this->refreshPrimaryKeyValue();
}
@@ -40,15 +40,15 @@ class PrimaryKeyValueBehavior extends Behavior {
$this->owner->{$this->getPrimaryKeyName()} = $key;
}
protected function generateValue() : string {
protected function generateValue(): string {
return (string)call_user_func($this->value);
}
protected function isValueExists(string $key) : bool {
protected function isValueExists(string $key): bool {
return $this->owner->find()->andWhere([$this->getPrimaryKeyName() => $key])->exists();
}
protected function getPrimaryKeyName() : string {
protected function getPrimaryKeyName(): string {
$owner = $this->owner;
$primaryKeys = $owner->primaryKey();
if (!isset($primaryKeys[0])) {

View File

@@ -43,7 +43,7 @@ class EmailRenderer extends Component {
$this->renderer->setBaseDomain($this->buildBasePath());
}
public function getBaseDomain() : string {
public function getBaseDomain(): string {
return $this->_baseDomain;
}

View File

@@ -106,7 +106,6 @@ class ElyDecorator implements DecoratorInterface {
$topPadding,
$multiple
) {
}
private function encodeSvgToBase64(string $filePath): string {

View File

@@ -1,11 +1,11 @@
<?php
namespace common\components\RabbitMQ;
use yii\base\Exception;
use yii\helpers\Json;
use PhpAmqpLib\Channel\AMQPChannel;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use yii\base\Exception;
use yii\helpers\Json;
/**
* Не гибкий компонент для работы с RabbitMQ, заточенный под нужны текущего проекта
@@ -17,20 +17,10 @@ use PhpAmqpLib\Message\AMQPMessage;
*/
class Component extends \yii\base\Component {
const TYPE_TOPIC = 'topic';
const TYPE_DIRECT = 'direct';
const TYPE_HEADERS = 'headers';
const TYPE_FANOUT = 'fanout';
/**
* @var AMQPStreamConnection
*/
protected $amqpConnection;
/**
* @var AMQPChannel[]
*/
protected $channels = [];
public const TYPE_TOPIC = 'topic';
public const TYPE_DIRECT = 'direct';
public const TYPE_HEADERS = 'headers';
public const TYPE_FANOUT = 'fanout';
/**
* @var string
@@ -57,6 +47,16 @@ class Component extends \yii\base\Component {
*/
public $vhost = '/';
/**
* @var AMQPStreamConnection
*/
protected $amqpConnection;
/**
* @var AMQPChannel[]
*/
protected $channels = [];
/**
* @inheritdoc
*/
@@ -115,6 +115,30 @@ class Component extends \yii\base\Component {
$channel->basic_publish(...$this->preparePublishArgs($message, $exchangeName, $routingKey, $publishArgs));
}
/**
* Returns prepaired AMQP message.
*
* @param string|array|object $message
* @param array $properties
* @return AMQPMessage
* @throws Exception If message is empty.
*/
public function prepareMessage($message, $properties = null) {
if ($message instanceof AMQPMessage) {
return $message;
}
if (empty($message)) {
throw new Exception('AMQP message can not be empty');
}
if (is_array($message) || is_object($message)) {
$message = Json::encode($message);
}
return new AMQPMessage($message, $properties);
}
/**
* Объединяет переданный набор аргументов с поведением по умолчанию
*
@@ -150,28 +174,4 @@ class Component extends \yii\base\Component {
], $args);
}
/**
* Returns prepaired AMQP message.
*
* @param string|array|object $message
* @param array $properties
* @return AMQPMessage
* @throws Exception If message is empty.
*/
public function prepareMessage($message, $properties = null) {
if ($message instanceof AMQPMessage) {
return $message;
}
if (empty($message)) {
throw new Exception('AMQP message can not be empty');
}
if (is_array($message) || is_object($message)) {
$message = Json::encode($message);
}
return new AMQPMessage($message, $properties);
}
}

View File

@@ -157,7 +157,7 @@ class Connection extends Component implements ConnectionInterface {
/**
* @var array List of available redis commands http://redis.io/commands
*/
const REDIS_COMMANDS = [
public const REDIS_COMMANDS = [
'BLPOP', // key [key ...] timeout Remove and get the first element in a list, or block until one is available
'BRPOP', // key [key ...] timeout Remove and get the last element in a list, or block until one is available
'BRPOPLPUSH', // source destination timeout Pop a value from a list, push it to another list and return it; or block until one is available
@@ -368,14 +368,6 @@ class Connection extends Component implements ConnectionInterface {
*/
private $_client;
public function getConnection() : ClientInterface {
if ($this->_client === null) {
$this->_client = new Client($this->prepareParams(), $this->options);
}
return $this->_client;
}
public function __call($name, $params) {
$redisCommand = mb_strtoupper($name);
if (in_array($redisCommand, self::REDIS_COMMANDS)) {
@@ -385,6 +377,14 @@ class Connection extends Component implements ConnectionInterface {
return parent::__call($name, $params);
}
public function getConnection(): ClientInterface {
if ($this->_client === null) {
$this->_client = new Client($this->prepareParams(), $this->options);
}
return $this->_client;
}
public function executeCommand(string $name, array $params = []) {
return $this->getConnection()->$name(...$params);
}

View File

@@ -30,11 +30,13 @@ class Key {
public function setValue($value): self {
$this->getRedis()->set($this->key, $value);
return $this;
}
public function delete(): self {
$this->getRedis()->del([$this->getKey()]);
return $this;
}
@@ -44,17 +46,19 @@ class Key {
public function expire(int $ttl): self {
$this->getRedis()->expire($this->key, $ttl);
return $this;
}
public function expireAt(int $unixTimestamp): self {
$this->getRedis()->expireat($this->key, $unixTimestamp);
return $this;
}
private function buildKey(array $parts): string {
$keyParts = [];
foreach($parts as $part) {
foreach ($parts as $part) {
$keyParts[] = str_replace('_', ':', $part);
}

View File

@@ -8,11 +8,13 @@ class Set extends Key implements IteratorAggregate {
public function add($value): self {
$this->getRedis()->sadd($this->getKey(), $value);
return $this;
}
public function remove($value): self {
$this->getRedis()->srem($this->getKey(), $value);
return $this;
}

View File

@@ -6,7 +6,7 @@ use Yii;
class Api {
const BASE_DOMAIN = 'http://skinsystem.ely.by';
private const BASE_DOMAIN = 'http://skinsystem.ely.by';
/**
* @param string $username

View File

@@ -1,10 +1,11 @@
<?php
namespace common\components;
declare(strict_types=1);
namespace common\components;
class UserFriendlyRandomKey {
public static function make ($length = 18) {
public static function make(int $length = 18) {
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
$numChars = strlen($chars);
$key = '';

View File

@@ -1,7 +1,6 @@
<?php
namespace common\components;
/**
* Этот класс был использован для изначальной генерации паролей на Ely.by и сейчас должен быть планомерно выпилен
* с проекта с целью заменить этот алгоритм каким-нибудь посерьёзнее.

View File

@@ -13,11 +13,11 @@ class ConfigLoader {
$this->application = $application;
}
public function getEnvironment() : string {
public function getEnvironment(): string {
return YII_ENV;
}
public function getConfig() : array {
public function getConfig(): array {
$toMerge = [
require __DIR__ . '/config.php',
];
@@ -55,7 +55,7 @@ class ConfigLoader {
return ArrayHelper::merge(...$toMerge);
}
public static function load(string $application) : array {
public static function load(string $application): array {
return (new static($application))->getConfig();
}

View File

@@ -115,6 +115,6 @@ return [
],
'aliases' => [
'@bower' => '@vendor/bower-asset',
'@npm' => '@vendor/npm-asset',
'@npm' => '@vendor/npm-asset',
],
];

View File

@@ -1,7 +1,7 @@
<?php
namespace common\db\mysql;
use yii\db\Expression;
use yii\db\ExpressionInterface;
use yii\db\mysql\QueryBuilder as MysqlQueryBuilder;
class QueryBuilder extends MysqlQueryBuilder {
@@ -12,22 +12,21 @@ class QueryBuilder extends MysqlQueryBuilder {
}
$orders = [];
foreach($columns as $name => $direction) {
if ($direction instanceof Expression) {
foreach ($columns as $name => $direction) {
if ($direction instanceof ExpressionInterface) {
$orders[] = $direction->expression;
} elseif (is_array($direction)) {
// This is new feature
// This condition branch is our custom solution
if (empty($direction)) {
continue;
}
$fieldValues = [];
foreach($direction as $fieldValue) {
foreach ($direction as $fieldValue) {
$fieldValues[] = $this->db->quoteValue($fieldValue);
}
$orders[] = 'FIELD(' . $this->db->quoteColumnName($name) . ',' . implode(',', $fieldValues) . ')';
// End of new feature
} else {
$orders[] = $this->db->quoteColumnName($name) . ($direction === SORT_DESC ? ' DESC' : '');
}

View File

@@ -42,7 +42,7 @@ abstract class TemplateWithRenderer extends Template {
*/
abstract public function getTemplateName(): string;
protected final function getView() {
final protected function getView() {
return $this->getTemplateName();
}

View File

@@ -16,6 +16,12 @@ class ChangeEmailConfirmCurrentEmail extends Template {
return 'Ely.by Account change E-mail confirmation';
}
public function getParams(): array {
return [
'key' => $this->key,
];
}
/**
* @return string|array
*/
@@ -26,10 +32,4 @@ class ChangeEmailConfirmCurrentEmail extends Template {
];
}
public function getParams(): array {
return [
'key' => $this->key,
];
}
}

View File

@@ -19,6 +19,13 @@ class ChangeEmailConfirmNewEmail extends Template {
return 'Ely.by Account new E-mail confirmation';
}
public function getParams(): array {
return [
'key' => $this->key,
'username' => $this->username,
];
}
/**
* @return string|array
*/
@@ -29,11 +36,4 @@ class ChangeEmailConfirmNewEmail extends Template {
];
}
public function getParams(): array {
return [
'key' => $this->key,
'username' => $this->username,
];
}
}

View File

@@ -3,70 +3,70 @@ namespace common\helpers;
final class Error {
const USERNAME_REQUIRED = 'error.username_required';
const USERNAME_TOO_SHORT = 'error.username_too_short';
const USERNAME_TOO_LONG = 'error.username_too_long';
const USERNAME_INVALID = 'error.username_invalid';
const USERNAME_NOT_AVAILABLE = 'error.username_not_available';
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';
const EMAIL_REQUIRED = 'error.email_required';
const EMAIL_TOO_LONG = 'error.email_too_long';
const EMAIL_INVALID = 'error.email_invalid';
const EMAIL_IS_TEMPMAIL = 'error.email_is_tempmail';
const EMAIL_NOT_AVAILABLE = 'error.email_not_available';
const EMAIL_NOT_FOUND = 'error.email_not_found';
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_NOT_AVAILABLE = 'error.email_not_available';
public const EMAIL_NOT_FOUND = 'error.email_not_found';
const LOGIN_REQUIRED = 'error.login_required';
const LOGIN_NOT_EXIST = 'error.login_not_exist';
public const LOGIN_REQUIRED = 'error.login_required';
public const LOGIN_NOT_EXIST = 'error.login_not_exist';
const PASSWORD_REQUIRED = 'error.password_required';
const PASSWORD_INCORRECT = 'error.password_incorrect';
const PASSWORD_TOO_SHORT = 'error.password_too_short';
public const PASSWORD_REQUIRED = 'error.password_required';
public const PASSWORD_INCORRECT = 'error.password_incorrect';
public const PASSWORD_TOO_SHORT = 'error.password_too_short';
const KEY_REQUIRED = 'error.key_required';
const KEY_NOT_EXISTS = 'error.key_not_exists';
const KEY_EXPIRE = 'error.key_expire';
public const KEY_REQUIRED = 'error.key_required';
public const KEY_NOT_EXISTS = 'error.key_not_exists';
public const KEY_EXPIRE = 'error.key_expire';
const ACCOUNT_BANNED = 'error.account_banned';
const ACCOUNT_NOT_ACTIVATED = 'error.account_not_activated';
const ACCOUNT_ALREADY_ACTIVATED = 'error.account_already_activated';
const ACCOUNT_CANNOT_RESEND_MESSAGE = 'error.account_cannot_resend_message';
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';
const RECENTLY_SENT_MESSAGE = 'error.recently_sent_message';
public const RECENTLY_SENT_MESSAGE = 'error.recently_sent_message';
const NEW_PASSWORD_REQUIRED = 'error.newPassword_required';
const NEW_RE_PASSWORD_REQUIRED = 'error.newRePassword_required';
const NEW_RE_PASSWORD_DOES_NOT_MATCH = self::RE_PASSWORD_DOES_NOT_MATCH;
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;
const REFRESH_TOKEN_REQUIRED = 'error.refresh_token_required';
const REFRESH_TOKEN_NOT_EXISTS = 'error.refresh_token_not_exist';
public const REFRESH_TOKEN_REQUIRED = 'error.refresh_token_required';
public const REFRESH_TOKEN_NOT_EXISTS = 'error.refresh_token_not_exist';
const CAPTCHA_REQUIRED = 'error.captcha_required';
const CAPTCHA_INVALID = 'error.captcha_invalid';
public const CAPTCHA_REQUIRED = 'error.captcha_required';
public const CAPTCHA_INVALID = 'error.captcha_invalid';
const RULES_AGREEMENT_REQUIRED = 'error.rulesAgreement_required';
public const RULES_AGREEMENT_REQUIRED = 'error.rulesAgreement_required';
const RE_PASSWORD_REQUIRED = 'error.rePassword_required';
const RE_PASSWORD_DOES_NOT_MATCH = 'error.rePassword_does_not_match';
public const RE_PASSWORD_REQUIRED = 'error.rePassword_required';
public const RE_PASSWORD_DOES_NOT_MATCH = 'error.rePassword_does_not_match';
const UNSUPPORTED_LANGUAGE = 'error.unsupported_language';
public const UNSUPPORTED_LANGUAGE = 'error.unsupported_language';
const SUBJECT_REQUIRED = 'error.subject_required';
const MESSAGE_REQUIRED = 'error.message_required';
public const SUBJECT_REQUIRED = 'error.subject_required';
public const MESSAGE_REQUIRED = 'error.message_required';
const TOTP_REQUIRED = 'error.totp_required';
const TOTP_INCORRECT = 'error.totp_incorrect';
public const TOTP_REQUIRED = 'error.totp_required';
public const TOTP_INCORRECT = 'error.totp_incorrect';
const OTP_ALREADY_ENABLED = 'error.otp_already_enabled';
const OTP_NOT_ENABLED = 'error.otp_not_enabled';
public const OTP_ALREADY_ENABLED = 'error.otp_already_enabled';
public const OTP_NOT_ENABLED = 'error.otp_not_enabled';
const NAME_REQUIRED = 'error.name_required';
public const NAME_REQUIRED = 'error.name_required';
const REDIRECT_URI_REQUIRED = 'error.redirectUri_required';
const REDIRECT_URI_INVALID = 'error.redirectUri_invalid';
public const REDIRECT_URI_REQUIRED = 'error.redirectUri_required';
public const REDIRECT_URI_INVALID = 'error.redirectUri_invalid';
const WEBSITE_URL_INVALID = 'error.websiteUrl_invalid';
public const WEBSITE_URL_INVALID = 'error.websiteUrl_invalid';
const MINECRAFT_SERVER_IP_INVALID = 'error.minecraftServerIp_invalid';
public const MINECRAFT_SERVER_IP_INVALID = 'error.minecraftServerIp_invalid';
}

View File

@@ -13,9 +13,9 @@ class StringHelper {
if ($usernameLength === 1) {
$mask = $maskChars;
} elseif($usernameLength === 2) {
} elseif ($usernameLength === 2) {
$mask = mb_substr($username, 0, 1) . $maskChars;
} elseif($usernameLength === 3) {
} elseif ($usernameLength === 3) {
$mask = mb_substr($username, 0, 1) . $maskChars . mb_substr($username, 2, 1);
} else {
$mask = mb_substr($username, 0, 2) . $maskChars . mb_substr($username, -2, 2);

View File

@@ -44,13 +44,13 @@ use const common\LATEST_RULES_VERSION;
*/
class Account extends ActiveRecord {
const STATUS_DELETED = -10;
const STATUS_BANNED = -1;
const STATUS_REGISTERED = 0;
const STATUS_ACTIVE = 10;
public const STATUS_DELETED = -10;
public const STATUS_BANNED = -1;
public const STATUS_REGISTERED = 0;
public const STATUS_ACTIVE = 10;
const PASS_HASH_STRATEGY_OLD_ELY = 0;
const PASS_HASH_STRATEGY_YII2 = 1;
public const PASS_HASH_STRATEGY_OLD_ELY = 0;
public const PASS_HASH_STRATEGY_YII2 = 1;
public static function tableName(): string {
return '{{%accounts}}';
@@ -67,10 +67,9 @@ class Account extends ActiveRecord {
$passwordHashStrategy = $this->password_hash_strategy;
}
switch($passwordHashStrategy) {
switch ($passwordHashStrategy) {
case self::PASS_HASH_STRATEGY_OLD_ELY:
$hashedPass = UserPass::make($this->email, $password);
return $hashedPass === $this->password_hash;
return UserPass::make($this->email, $password) === $this->password_hash;
case self::PASS_HASH_STRATEGY_YII2:
return Yii::$app->security->validatePassword($password, $this->password_hash);

View File

@@ -28,10 +28,10 @@ use yii\helpers\ArrayHelper;
*/
class EmailActivation extends ActiveRecord {
const TYPE_REGISTRATION_EMAIL_CONFIRMATION = 0;
const TYPE_FORGOT_PASSWORD_KEY = 1;
const TYPE_CURRENT_EMAIL_CONFIRMATION = 2;
const TYPE_NEW_EMAIL_CONFIRMATION = 3;
public const TYPE_REGISTRATION_EMAIL_CONFIRMATION = 0;
public const TYPE_FORGOT_PASSWORD_KEY = 1;
public const TYPE_CURRENT_EMAIL_CONFIRMATION = 2;
public const TYPE_NEW_EMAIL_CONFIRMATION = 3;
public static function tableName() {
return '{{%email_activations}}';
@@ -79,15 +79,15 @@ class EmailActivation extends ActiveRecord {
throw new InvalidConfigException('Unexpected type');
}
return new $classMap[$type];
return new $classMap[$type]();
}
public static function getClassMap() {
return [
self::TYPE_REGISTRATION_EMAIL_CONFIRMATION => confirmations\RegistrationConfirmation::class,
self::TYPE_FORGOT_PASSWORD_KEY => confirmations\ForgotPassword::class,
self::TYPE_CURRENT_EMAIL_CONFIRMATION => confirmations\CurrentEmailConfirmation::class,
self::TYPE_NEW_EMAIL_CONFIRMATION => confirmations\NewEmailConfirmation::class,
self::TYPE_FORGOT_PASSWORD_KEY => confirmations\ForgotPassword::class,
self::TYPE_CURRENT_EMAIL_CONFIRMATION => confirmations\CurrentEmailConfirmation::class,
self::TYPE_NEW_EMAIL_CONFIRMATION => confirmations\NewEmailConfirmation::class,
];
}

View File

@@ -29,7 +29,7 @@ use yii\db\ActiveRecord;
*/
class MinecraftAccessKey extends ActiveRecord {
const LIFETIME = 172800; // Ключ актуален в течение 2 дней
public const LIFETIME = 172800; // Ключ актуален в течение 2 дней
public static function tableName(): string {
return '{{%minecraft_access_keys}}';

View File

@@ -11,6 +11,10 @@ class OauthScopeQuery {
private $owner;
public function __construct(array $scopes) {
$this->scopes = $scopes;
}
public function onlyPublic(): self {
$this->internal = false;
return $this;
@@ -43,8 +47,4 @@ class OauthScopeQuery {
}), 'value');
}
public function __construct(array $scopes) {
$this->scopes = $scopes;
}
}

View File

@@ -21,7 +21,7 @@ class Textures {
}
public function getMinecraftResponse() {
$response = [
$response = [
'name' => $this->account->username,
'id' => str_replace('-', '', $this->account->uuid),
'properties' => [
@@ -54,9 +54,9 @@ class Textures {
if (!$encrypted) {
return $array;
} else {
return static::encrypt($array);
}
return static::encrypt($array);
}
public function getTextures(): array {

View File

@@ -45,7 +45,7 @@ class UsernameHistory extends ActiveRecord {
* @param int $afterTime
* @return UsernameHistory|null
*/
public function findNext(int $afterTime = null) /*: ?UsernameHistory*/ {
public function findNext(int $afterTime = null): ?UsernameHistory {
return self::find()
->andWhere(['account_id' => $this->account_id])
->andWhere(['>', 'applied_in', $afterTime ?: $this->applied_in])

View File

@@ -13,7 +13,7 @@ class NewEmailConfirmation extends EmailActivation {
public function behaviors() {
return ArrayHelper::merge(parent::behaviors(), [
'expirationBehavior' => [
'repeatTimeout' => 5 * 60,
'repeatTimeout' => 5 * 60,
],
'dataBehavior' => [
'class' => NewEmailConfirmationBehavior::class,

View File

@@ -8,11 +8,11 @@ use common\behaviors\DataBehavior;
*/
class NewEmailConfirmationBehavior extends DataBehavior {
public function getNewEmail() : string {
public function getNewEmail(): string {
return $this->getKey('newEmail');
}
public function setNewEmail(string $newEmail) {
public function setNewEmail(string $newEmail): void {
$this->setKey('newEmail', $newEmail);
}