Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
66.67% covered (warning)
66.67%
90 / 135
58.33% covered (warning)
58.33%
28 / 48
CRAP
0.00% covered (danger)
0.00%
0 / 1
LogicBase
66.67% covered (warning)
66.67%
90 / 135
58.33% covered (warning)
58.33%
28 / 48
257.70
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 getRequest
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 getFile
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getRequestContent
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRequestJson
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setHttpStatus
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getHttpStatus
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCookie
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setCookie
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
12
 removeCookie
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 peekTemporary
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 popTemporary
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 pushTemporary
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 removeTemporary
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 existsSession
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSession
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 requireSession
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 setSession
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 removeSession
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 cancelSession
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 restartSession
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 shutdownSession
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addResponseHeader
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 registerParameterKeys
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
4.03
 setValue
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
3.14
 hasError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 clearErrors
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 removeError
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 addCommonError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addError
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
3.14
 validation
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 validateImpl
n/a
0 / 0
n/a
0 / 0
0
 executeImpl
n/a
0 / 0
n/a
0 / 0
0
 startup
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 cleanup
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 validate
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 execute
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 run
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
3
 getResponseHeaders
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getViewData
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 setTextContent
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setJsonContent
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setContent
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setFileContent
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 setDownloadContent
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getContent
60.00% covered (warning)
60.00%
3 / 5
0.00% covered (danger)
0.00%
0 / 1
3.58
 tryGetResult
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 equalsResult
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 receiveErrorMessage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 receiveErrorKind
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace PeServer\Core\Mvc;
6
7use DateInterval;
8use DateTimeImmutable;
9use PeServer\Core\Binary;
10use PeServer\Core\Collection\Arr;
11use PeServer\Core\Environment;
12use PeServer\Core\Http\HttpRequest;
13use PeServer\Core\Http\HttpStatus;
14use PeServer\Core\I18n;
15use PeServer\Core\IO\File;
16use PeServer\Core\IO\IOUtility;
17use PeServer\Core\Log\ILogger;
18use PeServer\Core\Mime;
19use PeServer\Core\Mvc\DataContent;
20use PeServer\Core\Mvc\IValidationReceiver;
21use PeServer\Core\Mvc\LogicCallMode;
22use PeServer\Core\Mvc\LogicParameter;
23use PeServer\Core\Mvc\Template\TemplateParameter;
24use PeServer\Core\Mvc\UploadedFile;
25use PeServer\Core\Mvc\Validator;
26use PeServer\Core\Store\CookieOptions;
27use PeServer\Core\Store\CookieStore;
28use PeServer\Core\Store\SessionStore;
29use PeServer\Core\Store\SpecialStore;
30use PeServer\Core\Store\Stores;
31use PeServer\Core\Store\Template\TemporaryStore;
32use PeServer\Core\Text;
33use PeServer\Core\Throws\ArgumentException;
34use PeServer\Core\Throws\InvalidOperationException;
35use PeServer\Core\Throws\KeyNotFoundException;
36use PeServer\Core\Utc;
37
38/**
39 * コントローラから呼び出されるロジック基底処理。
40 */
41abstract class LogicBase implements IValidationReceiver
42{
43    #region define
44
45    protected const SESSION_ALL_CLEAR = Text::EMPTY;
46
47    #endregion
48
49    #region variable
50
51    /**
52     * ロジック開始日時。
53     */
54    protected readonly DateTimeImmutable $beginTimestamp;
55
56    /**
57     * ロガー。
58     */
59    protected readonly ILogger $logger;
60
61    /**
62     * リクエストデータ。
63     */
64    private readonly HttpRequest $request;
65
66    /**
67     * HTTPステータスコード。
68     */
69    private HttpStatus $httpResponseStatus;
70    /**
71     * 検証エラー。
72     *
73     * @var array<string,string[]>
74     */
75    private array $errors = [];
76    /**
77     * 応答データ。
78     *
79     * @var array<non-empty-string,mixed>
80     */
81    private array $values = [];
82
83    /**
84     * 要素設定がなされている場合に応答データのキーをこの項目に固定。
85     *
86     * @var non-empty-string[]
87     */
88    private array $keys = [];
89
90    /**
91     * コントローラ内結果データ。
92     *
93     * @var array<string,mixed>
94     */
95    protected array $result = [];
96
97    /**
98     * 検証処理。
99     */
100    protected Validator $validator;
101
102    /**
103     * 応答データ。
104     */
105    private ?DataContent $content = null;
106
107    /**
108     */
109    protected readonly Stores $stores;
110
111    protected readonly Environment $environment;
112
113    /**
114     * 応答ヘッダ。
115     *
116     * @var array<non-empty-string,string[]>
117     */
118    private array $responseHeaders = [];
119
120    #endregion
121
122    /**
123     * 生成。
124     *
125     * @param LogicParameter $parameter ロジック用パラメータ。
126     */
127    protected function __construct(LogicParameter $parameter)
128    {
129        $this->beginTimestamp = Utc::create();
130        $this->httpResponseStatus = HttpStatus::OK;
131        $this->request = $parameter->request;
132        $this->stores = $parameter->stores;
133        $this->environment = $parameter->environment;
134        $this->logger = $parameter->logger;
135
136        $this->validator = new Validator($this);
137    }
138
139    #region function
140
141    /**
142     * 要求データの取得。
143     *
144     * @param string $key 要求キー。
145     * @param string $fallbackValue 要求キーに存在しない場合の戻り値。
146     * @param bool $trim 取得データをトリムするか。
147     * @return string 要求データ。
148     */
149    protected function getRequest(string $key, string $fallbackValue = Text::EMPTY, bool $trim = true): string
150    {
151        if (!$this->request->exists($key)->exists) {
152            return $fallbackValue;
153        }
154
155        $value = $this->request->getValue($key);
156
157        if ($trim) {
158            return Text::trim($value);
159        }
160
161        return $value;
162    }
163
164    protected function getFile(string $key): UploadedFile
165    {
166        if (!$this->request->exists($key, true)->exists) {
167            throw new KeyNotFoundException('$key: ' . $key);
168        }
169
170        $file = $this->request->getFile($key);
171        return $file;
172    }
173
174    /**
175     * 要求本文の生データを取得。
176     *
177     * @return Binary
178     */
179    protected function getRequestContent(): Binary
180    {
181        return $this->request->specialStore->getRequestContent();
182    }
183
184    /**
185     * 要求本文から JSON を取得。
186     *
187     * @return array<mixed>
188     */
189    protected function getRequestJson(): array
190    {
191        return $this->request->specialStore->getRequestJson();
192    }
193
194    /**
195     * HTTP応答ステータスコードの設定。
196     *
197     * @param HttpStatus $httpStatus HTTPステータスコード。
198     * @return void
199     */
200    protected function setHttpStatus(HttpStatus $httpStatus): void
201    {
202        $this->httpResponseStatus = $httpStatus;
203    }
204
205    /**
206     * HTTP応答ステータスコードの取得。
207     *
208     * @return HttpStatus
209     */
210    public function getHttpStatus(): HttpStatus
211    {
212        return $this->httpResponseStatus;
213    }
214
215    /**
216     * Cookie を取得。
217     *
218     * @param string $key キー。
219     * @param string $fallbackValue 取得失敗時の値。
220     * @return string
221     */
222    protected function getCookie(string $key, string $fallbackValue = Text::EMPTY): string
223    {
224        return $this->stores->cookie->getOr($key, $fallbackValue);
225    }
226
227    /**
228     * Cookie を設定。
229     *
230     * @param string $key キー。
231     * @param string $value 設定値。
232     * @param CookieOptions|array{path:?string,span:?DateInterval,secure:?bool,httpOnly:?bool,sameSite:?string}|null $options オプション。
233     * @return void
234     */
235    protected function setCookie(string $key, string $value, CookieOptions|array|null $options = null): void
236    {
237        /** @var CookieOptions|null */
238        $cookieOptions = null;
239
240        if ($options !== null) {
241            if ($options instanceof CookieOptions) {
242                $cookieOptions = $options;
243            } else {
244                /** @var string */
245                $path = $options['path'] ?? $this->stores->cookie->options->path;
246                /** @var \DateInterval|null */
247                $span = $options['span'] ?? $this->stores->cookie->options->span;
248                /** @var bool */
249                $secure = $options['secure'] ?? $this->stores->cookie->options->secure;
250                /** @var bool */
251                $httpOnly = $options['httpOnly'] ?? $this->stores->cookie->options->httpOnly;
252                /**
253                 * @var string
254                 * @phpstan-var CookieSameSiteAlias
255                 */
256                $sameSite = $options['sameSite'] ?? $this->stores->cookie->options->sameSite;
257
258                $cookieOptions = new CookieOptions(
259                    $path,
260                    $span,
261                    $secure,
262                    $httpOnly,
263                    $sameSite
264                );
265            }
266        }
267
268        $this->stores->cookie->set($key, $value, $cookieOptions);
269    }
270
271    /**
272     * Cookie を削除。
273     *
274     * @param string $key キー。
275     * @return void
276     */
277    protected function removeCookie(string $key): void
278    {
279        $this->stores->cookie->remove($key);
280    }
281
282    protected function peekTemporary(string $key, mixed $fallbackValue = null): mixed
283    {
284        $value = $this->stores->temporary->peek($key);
285        if ($value === null) {
286            return $fallbackValue;
287        }
288
289        return $value;
290    }
291
292    protected function popTemporary(string $key, mixed $fallbackValue = null): mixed
293    {
294        $value = $this->stores->temporary->pop($key);
295        if ($value === null) {
296            return $fallbackValue;
297        }
298
299        return $value;
300    }
301
302    protected function pushTemporary(string $key, mixed $value): void
303    {
304        $this->stores->temporary->push($key, $value);
305    }
306    protected function removeTemporary(string $key): void
307    {
308        $this->stores->temporary->remove($key);
309    }
310
311    protected function existsSession(string $key): bool
312    {
313        return $this->stores->session->tryGet($key, $unused);
314    }
315
316    protected function getSession(string $key, mixed $fallbackValue = null): mixed
317    {
318        return $this->stores->session->getOr($key, $fallbackValue);
319    }
320
321    protected function requireSession(string $key): mixed
322    {
323        if ($this->stores->session->tryGet($key, $result)) {
324            return $result;
325        }
326
327        throw new KeyNotFoundException($key);
328    }
329
330
331    protected function setSession(string $key, mixed $value): void
332    {
333        $this->stores->session->set($key, $value);
334    }
335    protected function removeSession(string $key): void
336    {
337        $this->stores->session->remove($key);
338    }
339    protected function cancelSession(): void
340    {
341        $this->stores->session->setApplyState(SessionStore::APPLY_CANCEL);
342    }
343    protected function restartSession(): void
344    {
345        $this->stores->session->setApplyState(SessionStore::APPLY_RESTART);
346    }
347    protected function shutdownSession(): void
348    {
349        $this->stores->session->setApplyState(SessionStore::APPLY_SHUTDOWN);
350    }
351
352    /**
353     * 応答HTTPヘッダ追加。
354     *
355     * @param non-empty-string $name
356     * @param string $value
357     * @return void
358     */
359    public function addResponseHeader(string $name, string $value): void
360    {
361        if (isset($this->responseHeaders[$name])) {
362            $this->responseHeaders[$name][] = $value;
363        } else {
364            $this->responseHeaders[$name] = [$value];
365        }
366    }
367
368    /**
369     * パラメータキーの設定。
370     *
371     * @param non-empty-string[] $keys
372     * @param bool $overwrite キー項目を要求データで上書きするか
373     * @param bool $initialize キー情報を初期化するか
374     * @return void
375     */
376    protected function registerParameterKeys(array $keys, bool $overwrite, bool $initialize = true): void
377    {
378        if ($initialize) {
379            $this->keys = $keys;
380        } else {
381            $this->keys += $keys;
382        }
383
384        foreach ($this->keys as $key) {
385            if ($overwrite) {
386                $value = $this->getRequest($key, Text::EMPTY);
387                $this->values[$key] = $value;
388            } else {
389                $this->values[$key] = Text::EMPTY;
390            }
391        }
392    }
393
394    /**
395     * 応答データとして設定。
396     *
397     * @param non-empty-string $key
398     * @param mixed $value
399     * @return void
400     * @throws ArgumentException 入力データとして未登録の場合に投げられる。
401     */
402    protected function setValue(string $key, $value): void
403    {
404        if (Arr::getCount($this->keys)) {
405            if (array_search($key, $this->keys) === false) {
406                throw new ArgumentException("未登録 key -> $key");
407            }
408        }
409
410        $this->values[$key] = $value;
411    }
412
413    /**
414     * 検証エラーが存在するか。
415     *
416     * @return boolean
417     */
418    protected function hasError(): bool
419    {
420        return 0 < count($this->errors);
421    }
422
423    protected function clearErrors(): void
424    {
425        $this->errors = [];
426    }
427
428    protected function removeError(string $key): void
429    {
430        if (isset($this->errors[$key])) {
431            unset($this->errors[$key]);
432        }
433    }
434
435    protected function addCommonError(string $message): void
436    {
437        $this->addError(Validator::COMMON, $message);
438    }
439
440    protected function addError(string $key, string $message): void
441    {
442        if (isset($this->errors[$key])) {
443            if (array_search($message, $this->errors[$key]) === false) {
444                $this->errors[$key][] = $message;
445            }
446        } else {
447            $this->errors[$key] = [$message];
448        }
449    }
450
451    /**
452     * キーに対する一括検証処理。
453     *
454     * @param string $key
455     * @param callable(string,string):void $callback
456     * @param array{default?:string,trim?:bool}|null $options オプション
457     *   * default: 取得失敗時の値。
458     *   * trim: 値をトリムするか。
459     * @return void
460     */
461    protected function validation(string $key, callable $callback, ?array $options = null): void
462    {
463        /** @var string */
464        $default = $options['default'] ?? Text::EMPTY;
465        /** @var bool */
466        $trim = $options['trim'] ?? true;
467
468        $value = $this->getRequest($key, $default, $trim);
469        $callback($key, $value);
470    }
471
472    /**
473     * 検証ロジック実装。
474     *
475     * @param LogicCallMode $callMode 呼び出し。
476     * @return void
477     */
478    abstract protected function validateImpl(LogicCallMode $callMode): void;
479
480    /**
481     * 実行ロジック実装。
482     *
483     * @param LogicCallMode $callMode 呼び出し。
484     * @return void
485     */
486    abstract protected function executeImpl(LogicCallMode $callMode): void;
487
488    protected function startup(LogicCallMode $callMode): void
489    {
490        //NOP
491    }
492
493    protected function cleanup(LogicCallMode $callMode): void
494    {
495        //NOP
496    }
497
498    /**
499     * 検証ロジック実装。
500     *
501     * @param LogicCallMode $callMode 呼び出し。
502     * @return void
503     */
504    private function validate(LogicCallMode $callMode): void
505    {
506        $this->validateImpl($callMode);
507    }
508
509    /**
510     * 実行ロジック。
511     *
512     * @param LogicCallMode $callMode 呼び出し。
513     * @return void
514     */
515    private function execute(LogicCallMode $callMode): void
516    {
517        $this->executeImpl($callMode);
518    }
519
520    /**
521     * ロジック処理。
522     *
523     * @param LogicCallMode $callMode 呼び出し。
524     * @return boolean
525     */
526    public function run(LogicCallMode $callMode): bool
527    {
528        try {
529            $this->startup($callMode);
530
531            $this->validate($callMode);
532            if ($this->hasError()) {
533                return false;
534            }
535
536            $this->execute($callMode);
537
538            if ($this->hasError()) {
539                return false;
540            }
541
542            return true;
543        } finally {
544            $this->cleanup($callMode);
545        }
546    }
547
548    /**
549     * 応答ヘッダの取得。
550     *
551     * @return array<non-empty-string,string[]>
552     */
553    public function getResponseHeaders(): array
554    {
555        return $this->responseHeaders;
556    }
557
558    /**
559     * View表示用データの取得。
560     *
561     * @return TemplateParameter
562     */
563    public function getViewData(): TemplateParameter
564    {
565        return new TemplateParameter(
566            $this->httpResponseStatus,
567            $this->values,
568            $this->errors
569        );
570    }
571
572    final protected function setTextContent(string $data): void
573    {
574        $this->setContent(Mime::TEXT, $data);
575    }
576
577    /**
578     * JSON応答データ設定。
579     *
580     * @param array<mixed> $data
581     * @return void
582     */
583    final protected function setJsonContent(array $data): void
584    {
585        $this->setContent(Mime::JSON, $data);
586    }
587
588    /**
589     * 応答データ設定。
590     *
591     * @param non-empty-string $mime
592     * @phpstan-param non-empty-string|\PeServer\Core\Mime::* $mime
593     * @param string|array<mixed>|Binary $data
594     * @return void
595     */
596    protected function setContent(string $mime, $data): void
597    {
598        $this->content = new DataContent(HttpStatus::None, $mime, $data);
599    }
600
601    /**
602     * ファイルを応答として設定。
603     *
604     * @param string|null $mime
605     * @phpstan-param Mime::*|null $mime
606     * @param string $path
607     */
608    protected function setFileContent(?string $mime, string $path): void
609    {
610        if (Text::isNullOrWhiteSpace($mime)) {
611            $mime = Mime::fromFileName($path);
612        }
613
614        $content = File::readContent($path);
615
616        $this->content = new DataContent(HttpStatus::None, $mime, $content->raw);
617    }
618
619    /**
620     * ダウンロードデータ応答。
621     *
622     * @param non-empty-string $mime
623     * @phpstan-param non-empty-string|\PeServer\Core\Mime::* $mime
624     * @param string $fileName
625     * @param Binary $data
626     * @return void
627     */
628    final protected function setDownloadContent(string $mime, string $fileName, Binary $data): void
629    {
630        $this->content = new DownloadDataContent($mime, $fileName, $data);
631    }
632
633    /**
634     * 応答データ取得。
635     *
636     * @return DataContent
637     * @throws InvalidOperationException 応答データ未設定
638     */
639    public function getContent(): DataContent
640    {
641        if ($this->content === null) {
642            throw new InvalidOperationException();
643        }
644
645        if ($this->content instanceof DownloadDataContent) {
646            return $this->content;
647        }
648
649        return new DataContent($this->httpResponseStatus, $this->content->mime, $this->content->data);
650    }
651
652    /**
653     * ロジック結果に指定キー項目が存在するか。
654     *
655     * @template TValue
656     * @param string $key
657     * @param mixed $result
658     * @phpstan-param TValue $result
659     * @return boolean
660     */
661    public function tryGetResult(string $key, &$result): bool
662    {
663        return Arr::tryGet($this->result, $key, $result);
664    }
665
666    /**
667     * ロジック結果の指定キー項目が指定値に一致するか。
668     *
669     * @template TValue
670     * @param string $key
671     * @param mixed $value
672     * @phpstan-param TValue $value
673     * @return boolean
674     */
675    public function equalsResult(string $key, $value): bool
676    {
677        if ($this->tryGetResult($key, $result)) {
678            return $result === $value;
679        }
680
681        return false;
682    }
683
684    #endregion
685
686    #region IValidationReceiver
687
688    public function receiveErrorMessage(string $key, string $message): void
689    {
690        $this->addError($key, $message);
691    }
692
693    public function receiveErrorKind(string $key, int $kind, array $parameters): void
694    {
695        $map = [
696            Validator::KIND_EMPTY => I18n::ERROR_EMPTY,
697            Validator::KIND_WHITE_SPACE => I18n::ERROR_WHITE_SPACE,
698            Validator::KIND_LENGTH => I18n::ERROR_LENGTH,
699            Validator::KIND_RANGE => I18n::ERROR_RANGE,
700            Validator::KIND_MATCH => I18n::ERROR_MATCH,
701            Validator::KIND_EMAIL => I18n::ERROR_EMAIL,
702            Validator::KIND_WEBSITE => I18n::ERROR_WEBSITE,
703        ];
704
705        $this->receiveErrorMessage($key, I18n::message($map[$kind], $parameters));
706    }
707
708    #endregion
709}