Данный трейт вы сможете без проблем подключить и использовать в своих веб-приложениях.
Тайп-хинтинг в PHP версии старше 7.0
PHP версии < 7 позволяет в определении метода описать, какие типы данных будут поступать в функцию, и выходной тип данных функции.
Здесь всё замечательно: что задал, то и пришло; что задал, то и вышло.
public function filterArray(array $arr, string $filterParameter, callable $filterCallback) : array
Надо нам определить своё правило фильтрации массива – взяли и создали лямбда-функцию, определили в ней своё правило фильтрации. А в filterArray() передали $arr, заранее зная, что это массив, а не integer какой-нибудь.
Если вдруг в качестве $filterParameter передадим не string, а object, нам PHP мигом выдаст ошибку парсинга. Мол, мы сиё не заказывали.
Тайп-хинтинг в PHP версии младше 5.6
А вот PHP версии < 5.6 не поддерживает явное указание выходных типов данных:
public function sortArray($arr, $filterParam) : array // <- ошибка парсинга
{
// ...
}
Также PHP < 5.6 не поддерживает примитивы в качестве входных типов данных, такие как integer, string, float.
Однако некоторые типы можно указать даже на старой версии языка. Например, можно указать, что в функцию будет передан параметр типа array, object, либо экземпляр класса:
/**
* Class ArrayForSorting
* Будем предполагать, что это какая-то структура с кучей параметров, которые нам сейчас не важны.
*/
class ArrayForSorting
{
/**
* Массив для сортировки.
*
* @var array
*/
public $arrayForSorting;
/**
* @construct
*/
public function __construct($arrayForSorting)
{
$this->arrayForSorting = $arrayForSorting;
}
}
/**
* Class UserSortArray
* Класс, сортирующий массивы с помощью раздичных методов: вставки, слияния, пузырька.
*/
class UserSortArray
{
/**
* Доступные методы сортировки.
*
* @var object
*/
public $availableSortingMethods;
/**
* Сортировка методом вставки.
*
* @param ArrayForSorting $sortArray массив для сортировки, передаётся по ссылке.
*
* @throws UserSortArrayException если метод сортировки не доступен в системе.
*/
public function insertSort(ArrayForSorting &$sortArray)
{
if (false === isset($availableSortMethods->insertMethod)) {
throw new UserSortArrayException('Insert method for user array sort is not available.');
}
return uasort($sortArray->arrayForSorting, $availableSortMethods->bubbleMethod);
}
}
Исходная проблема
Но, извольте. Что делать, если мне потребуется в функцию передавать не array, а, к примеру, double?
И программист может запросто передать в функцию хоть строку, хоть массив, хоть экземпляр любого класса.
Выход в данном случае простой: нужно просто каждый раз самостоятельно проверять входные и выходные параметры на валидность.
class ArraySorter
{
public function sortArray(array &$sortArray, $userCallback)
{
// дабы не нарушать святы принципы полиморфизма,
// будем возвращать пустой массив в случае ошибки валидации,
// а не false или какой-нибудь -1.
if (false === $this->validateArray($sortArray)) {
return [];
}
return uasort($sortArray, $userCallback);
}
private function validateArray($array)
{
if (!isset($array) || false === is_array($array)) {
return false;
}
return true;
}
}
Однако страшно даже подумать, сколько раз придётся писать один и тот же код, сводящийся к следующим строчкам:
if (null !== $param && '' !== $param) {
return false; // или [], или '', или что ещё надо возвратить в случае невалидных параметров
// либо
throw new Exception(__CLASS__ . __FUNCTION__ . ": Expected integer, got sting");
}
Очевидное решение проблемы – написание валидатора в трейте, которому в дальнейшем делегировать все проверки типов входных параметров. В случае, если параметр имеет не тот тип, который требовался, парсер тут же бросит исключение.
На выходе мы получаем следующее:
- Язык становится менее динамически типизированным. Зато принципы ООП также не посылаются куда подальше программистом;
- Дублирующийся код проверок типов данных выносится в отдельную… сущность, если трейт так можно назвать;
- Новые валидаторы можно добавлять, не затрагивая структуру других классов.
Трейты по сути своей похожи на protected-методы в плане того, что их можно вызвать из любого класса, в который он импортирован. Но, в отличие от наследования, мы можем подключать сколько угодно трейтов в класс и использовать все его свойства и методы.
Трейты доступны для использования в PHP, начиная с версии 5.4.0.
<?php
namespace traits;
/**
* Trait Validator
* Трейт валидации параметров.
*/
trait Validator
{
/**
* Валидация параметров.
*
* @param array $validationParams массив правил валидации.
* Формат : 'тип' => значение.
* Если после типа идёт слово 'not_empty' -- идёт проверка параметра на пустоту
* (т.е. массив, не содержащий элементов, или пустая строка).
* В массиве содержатся следующие значения:
* [
* 'integer' => 123,
* 'string not_empty' => 'hello world!',
* 'array' => [ ... ],
* ]
*
* @return bool true если валидация прошла успешно.
*
* @throws \Exception если метод валидации для типа данных не найден.
*/
public function validate($validationParams)
{
// Либо это массив, либо выбрасываем ошибку.
$this->validateArray($validationParams);
foreach ($validationParams as $type => $value) {
$methodName = 'validate' . ucfirst($type); // к примеру validateInteger
$isEmptinessValidation = false;
if ('not_empty' === substr($type, -9)) {
$methodName = 'validate' . ucfirst(substr($type, 0, -9));
$isEmptinessValidation = true;
}
if (false === method_exists($this, $methodName)) {
throw new \Exception("Trait 'Validator' does not have method '{$methodName}'.");
}
// Либо возвращает true, либо выбрасывает исключение, одно из двух.
$this->{$methodName}($value, $isEmptinessValidation);
}
return true;
}
/**
* Валидирует строку.
*
* @param string $string валидируемая строка.
* @param bool $isValidateForEmptiness нужно ли валидировать строку на пустоту.
*
* @return bool результат валидации.
*/
public function validateString($string, $isValidateForEmptiness)
{
$validationRules = is_string($string) && $this->validateForSetAndEmptiness($string, $isValidateForEmptiness);
if (false === $validationRules) {
$this->throwError('string', gettype($string));
}
return true;
}
/**
* Валидирует булевую переменную.
*
* @param boolean $bool булевая переменная.
*
* @return bool результат валидации.
*/
public function validateBoolean($boolean, $isValidateForEmptiness = false)
{
$validationRules = isset($boolean) && is_bool($boolean);
if (false === $validationRules) {
$this->throwError('boolean', gettype($boolean));
}
return true;
}
/**
* Валидирует массив.
*
* @param string $array валидируемый массив.
* @param bool $isValidateForEmptiness нужно ли валидировать массив на пустоту.
*
* @return bool результат валидации.
*/
public function validateArray($array, $isValidateForEmptiness)
{
$validationRules = is_array($array) && $this->validateForSetAndEmptiness($array, $isValidateForEmptiness);
if (false === $validationRules) {
$this->throwError('array', gettype($array));
}
return true;
}
/**
* Валидирует объект.
*
* @param string $object валидируемый объект.
* @param bool $isValidateForEmptiness нужно ли валидировать объект на пустоту.
*
* @return bool результат валидации.
*/
public function validateObject($object, $isValidateForEmptiness)
{
$validationRules = is_object($object) && $this->validateForSetAndEmptiness($object, $isValidateForEmptiness);
if (false === $validationRules) {
$this->throwError('object', gettype($object));
}
return true;
}
/**
* Валидирует целое число.
*
* @param string $integer валидируемое число.
* @param bool $isValidateForEmptiness нужно ли валидировать число на пустоту.
*
* @return bool результат валидации.
*/
public function validateInteger($integer, $isValidateForEmptiness)
{
$validationRules = is_int($integer) && $this->validateForSetAndEmptiness($integer, false);
if (false === $validationRules) {
$this->throwError('integer', gettype($integer));
}
return true;
}
/**
* Валидирует параметр на установленность и на то, пустой ли параметр.
*
* @param string $parameter валидируемый параметр.
* @param bool $isValidateForEmptiness нужно ли валидировать параметр (объект, массив, строку) на пустоту.
*
* @return bool результат валидации.
*/
private function validateForSetAndEmptiness($parameter, $isValidateForEmptiness)
{
$isNotEmpty = true;
if (true === $isValidateForEmptiness) {
$isNotEmpty = false === empty($parameter);
}
return isset($parameter) && true === $isNotEmpty;
}
/**
* Бросает исключение.
*
* @param string $expectedType
* @param string $gotType
*
* @throws \Exception в случае ошибки валидации входного параметра.
*/
private function throwError($expectedType, $gotType)
{
$validatorMethodName = ucfirst($expectedType) . 'Validator'; // integer -> IntegerValidator
throw new \Exception("Parse error: {$validatorMethodName} expected type {$expectedType}, got {$gotType}");
}
}
Используется трейт очень просто. В качестве примера реализуем класс Notebook, хранящий в себе методы генерации и получения уникального идентификатора для того, чтобы показать, как можно с помощью данного трейта проверять входные и выходные данные функции.
namespace models;
use traits;
/**
* Class Notebook
* Ноутбук с уникальным идентификатором.
*/
class Notebook
{
use \traits\Validator;
/**
* Уникальный ID ноутбука.
*
* @var string
*/
private $_uid;
/**
* @construct
*/
public function __construct()
{
$this->_uid = $this->generateUniqueIdentifier();
}
/**
* Возвращает уникальный ID ноутбука.
*
* @return string
*/
public function getNotebookUID()
{
// Метод validate() трейта принимает на вход массив с параметрами
// в стиле 'primitiveName' => $primitiveValue.
// При этом данный метод можно вызвать как в начале функции,
// так и в её конце.
$this->validate([
'string not_empty' => $this->_uid,
]);
return $this->_uid;
}
/**
* Генерирует уникальный ID ноутбука.
*
* @return string
*/
private function generateUniqueIdentifier()
{
$uniqueIdentifier = bin2hex(openssl_random_pseudo_bytes(40));
// А вот и пример валидации выходных параметров.
$this->validate([
'string not_empty' => $uniqueIdentifier,
]);
return $uniqueIdentifier;
}
}
Ещё один пример: класс Pen (простая чернильная ручка, инициализирующаяся с каким-то количеством чернил), выводящий сообщение на экран.
<?php
namespace models;
use traits;
/**
* Class Pen
* Обычная чернильная ручка.
*/
class Pen
{
use \traits\Validator;
/**
* Оставшееся количество чернил ручки.
*
* @var double
*/
private $remainingAmountOfInk;
/**
* @construct
*/
public function __construct()
{
$this->remainingAmountOfInk = 100;
}
/**
* Выводит сообщение на экран.
*
* @param string $message сообщение.
*
* @return void
*
* @throws ValidatorException в случае ошибки валидации входных параметров.
*/
public function drawMessage($message)
{
$this->validate([
'string' => $message,
]);
if (0 > $this->remainingAmountOfInk) {
echo 'Ink ended'; // кончились чернила
}
echo 'Pen writes message: ' . $message . '<br>' . PHP_EOL;
$this->remainingAmountOfInk -= 1;
}
/**
* Возвращает оставшееся количество чернил.
*
* @return integer
*/
public function getRemainingAmountOfInk()
{
$this->validate([
'double' => $this->remainingAmountOfInk,
]);
return $this->remainingAmountOfInk;
}
}
Ну а теперь давайте распишем нашу ручку на столе: «Hello World»!
// autoload.php - класс автолоадера.
/**
* Class Autoloader
* Класс для автозагрузки классов и трейтов.
*/
class AutoLoader
{
/**
* Данный метод подгружает классы.
*
* @param string $className путь к классу.
*/
public static function loadClasses($className)
{
$dir = dirname(__FILE__);
$sep = DIRECTORY_SEPARATOR;
require_once("{$dir}{$sep}{$className}.php");
}
}
// Подгружаем классы.
spl_autoload_register([
'AutoLoader',
'loadClasses'
]);
// ------------------------------------
// index.php
include_once("autoload.php");
use models as m;
$pen = new m\Pen();
$pen->drawMessage('hi habrahabr'); // Pen writes message: hi habrahabr
$message = [
'message' => 'hi im message inside array',
];
try {
$pen->drawMessage($message); // будет выброшено исключение ValidatorException
} catch (\Exception $e) {
echo 'exception was throwed during validation of message <br>' . PHP_EOL;
}
Вывод:
Pen writes message: hi habrahabr
exception was throwed during validation of message
Заключение
Вот с помощью такого вот простенького трейта можно
Я специально не стал прикручивать к методу validate() в примере выше особые параметры валидации, например такие, как минимальное/максимальное значение double-ов или строковых переменных, пользовательские колбэки на валидацию параметров, вывод своего сообщения при выбросе исключения и так далее.
Потому что основной целью статьи было рассказать о том, что технология, позволяющая добиться от языка статичности, есть и легко реализуема она даже на старой версии PHP.
Комментарии (23)
maxru
24.05.2017 13:54+1Я долго писал обстоятельный комментарий, но потом всё стёр, потому что, мне кажется, тут можно обойтись одной строкой:
"Вот так, с помощью нехитрых приспособлений, буханку белого (или чёрного) хлеба можно превратить в троллейбус. Но зачем?"
maximw
24.05.2017 15:19
Объекты передаются по ссылке, амперсанд не нужен.public function insertSort(ArrayForSorting &$sortArray)
// дабы не нарушать святы принципы полиморфизма,
Вместо возврата корректного ответа на ошибочные аргументы, лучше кидать исключение. И при чем тут принципы полиморфизма?
// будем возвращать пустой массив в случае ошибки валидации,
// а не false или какой-нибудь -1.
Ну и лучше все же на 7 переходить. А то тут просится картинка с буханкой-троллейбусом.the_kane_is_alive
24.05.2017 17:05Вместо возврата корректного ответа на ошибочные аргументы, лучше кидать исключение.
Это в трейте и реализовано.
И при чем тут принципы полиморфизма?
Да, про полиморфизм я не к месту написал, признаю.
Ну и лучше все же на 7 переходить. А то тут просится картинка с буханкой-троллейбусом.
Многие хостинг-провайдеры до сих пор остались в прошлом веке со старыми версиями.AlexLeonov
24.05.2017 17:08Зачем вообще работать с хостинг-провайдерами? Вы к ним приколочены гвоздями? Простейший VPS, где можно поставить любую версию, стоит 150 рублей в месяц. Расскажите — чем лучше «хостинг»?
the_kane_is_alive
24.05.2017 17:19Расскажите — чем лучше «хостинг»?
Согласен, ничем.
(Если не считать того, что всё за тебя уже настроено заранее)Fortistello
25.05.2017 07:00(Если не считать того, что всё за тебя уже настроено заранее)
sudo apt install php php-common php-mysql mysql apache2
Базовый веб-сервер, не особо заморачиваясь со всякими http/2, rabbitmq, zabbix и прочими фенечками, устанавливается на VPS минут за 15, 10 из которых вы будете пить чай.
Я вижу шаред-хостинги только как площадку для какого-то самопального блога на вордпрессе от человека, который немножко перерос простые html странички, но не хочет изучать программирование.maxru
25.05.2017 12:11Не говоря уже о том, что сейчас каждый первый VPS хостинг умеет сразу ставить шаблоны LAMP/LEMP
smple
25.05.2017 13:37+1tldr Давайте валидировать входные данные ручками все правила валидации сложим в трейт который будем подключать.
Реальность такова что "строгую" типизацию ввели как раз из за того что люди очень много валидировали параметры ручками и это вынесли на уровень языка, значит это уже делали до вас и ничего нового тому кому нужна статическая типизация вы не сказали.
Далее trait далеко не лучшая идея для хранения этих проверок, можно с тем же успехом сделать Util\Assert и кучу статических методов которые дерграть, это как пример, или сложить в функцию которую импортить.
Следующее аргумент функции валидации это массив где магическое имя ключа и значение которое он должен проверить, на самом деле лучше сделать что то типо assertString($arg...), assertInt($arg...), assertNotNull($arg ...)
Следующее ваш способ валидации предполагает выкидывания исключений, это сделано для того чтобы в коде меньше проверять ответы и в случае чего падать в корку, но это далеко не лучшая практика потому что ошибка в данных это нормальная ситуация и она должна быть обработано и по возможности без исключений, поэтому имя в виде валидации не очень здесь подходит.
tdlr С учетом всего выше сказанного вы просто написали еще одну библиотеку содержащую assert которых и так было достаточно и которые учитывают все мои замечания указанные выше.
Ну а теперь немножко позитива то что вы думаете над такими проблемами это хорошо и попытка сделать инструмент который можно было бы переиспользовать это тоже хорошо и попытка им поделится, вы как минимум получите замечания (рекомендации) по его улучшению и работая над его улучшением будете улучшать свои навыки и это лично для вас будет плюсом ну и попутно для других.
А сейчас посмотрите https://packagist.org/search/?q=assert количество библиотек и по смотрите как они решают подобную проблему может что полезное для себя узнаете.
Кстате еще один интересный подход http://php.net/manual/ru/book.stream.php можно с помощью эту функциональности сделать что то типо преобразования одного кода в другой и установить эти проверки на этапе "компиляции" и тогда ручные проверки делать не обязательно, можно например сразу код писать в стиле php 7 но для запуска его в php5 надо будет его преобразовать, выглядит забавно чисто с точки зрения иследования, на продакшене я бы такое применять не стал
AlexLeonov
25.05.2017 13:40Согласен со всем, кроме
далеко не лучшая практика потому что ошибка в данных это нормальная ситуация и она должна быть обработано и по возможности без исключений
Ошибка валидации — это классическое исключение. Не фатальная ошибка, но нечто, что ждет нашей реакции и делает дальнейшее линейное выполнение программы невозможным или бессмысленным.
Другое дело, что надо исключения в валидации немного не так топорно использовать, конечно же.smple
25.05.2017 14:04давайте я немного поясню
Валидация подразумевает два состояния, валидно и не валидно и если не валидно должен быть функционал получения текста что именно не валидно, это нужно для того чтобы выбросить соответствующий тип исключения с соответствующим текстом в том месте где вы вызывали валидацию, также вместо исключения может быть предусмотренно поведение в случае невалидных данных
Например у вас спрашивают год рождения вы указываете 2222 с точки зрения валидации это не правильно год рождения не может быть больше текущего года и прекращать и падать всему приложению нет нужды, потому что после вашего кода может быть например логирование в приложение которое залогировало входящий запрос, а выходящий (ответ) не залогировало, вы можете сказать так сделайте логирование в обработке exception но это не правильно вы начинаете делать ветвление логики и писать ее в обработку исключения потом может появится еще что то.
Вторая причина почему валидация не должна падать в корку это когда в форме спрашивают дату рождения и вы указываете 34.34.2222 тут сразу несколько ошибок валидации, неправильный номер месяца так как он больше 12, не правильный день и не правильный год, в случае валидации которая падает с exception она будет каждый этот exception выкидывать по одному, вы заметите что это должно проверятся на клиенте(js) и к вам уже приходить валидно и backend не обязан давать человеко читаемый текст, поэтому мы можем ронять приложение, но ронять резко приложение плохо смотри пункт один что могут быть события в конце обработки запроса.
В противовес слову validation есть assert который как раз утверждение и если оно не истинно оно валится с исключением.
tldr Я как потенциальный пользователь этого трейта видя validation ожидаю там валидацию, а не assertation, поэтому вам нужно или сделать там валидацию или переназвать метод на assertation, второе как мне кажется более правильно и менее трудо затратно, просто я имею ввиду что вы выбрали плохое имя для метода.
Надеюсь я чуть прояснил ситуацию
AlexLeonov
25.05.2017 14:26Мне «прояснять» ничего не нужно. У вас фундаментальная ошибка в рассуждениях:
прекращать и падать всему приложению нет нужды
С чего вы взяли, что исключение — это «прекращать и падать»? Исключение — это инкапсулировать, передавать и управлять потоком.
Избавьтесь уже от парадигмы «исключение — значит падение». Это просто объект, инкапсулирующий в себе информацию о случившемся. И не более того. Плюс способ передать этот объект вверх по стеку.
Вот опять:assert который как раз утверждение и если оно не истинно оно валится с исключением
Что за чушь? Почему «валится»? Выбрасывает исключение, уважаемый, выбрасывает. А вы — ловите. Если у вас что-то «валится» — вы хреновый программист и не умеете пользоваться исключениями. И assert() тоже.
smple
25.05.2017 14:58давайте сразу проясним
Валится значит выполнение программы дальше бесмысленно, она останавливает обработку запроса корректно сообщая о том что завершена с ошибкой.
Далее вы предлагаете вести разработку в стиле "Exception Drive Development (EDD)" и кидаться ими по стеку и заниматься рыбалкой (вылавливанием) я конечно понимаю что она имеет право на жизнь как например "GOTO Drive Development"(GTD) она даже может быть чуть похожа, но заниматься этим я этим все таки не буду, при этом я прекрасно знаю о важности обработки исключений и о том что есть RuntimeException и что в ходе обработки исключений программа может вернутся к нормальному исполнению (обработка с возвратом).
EDD и GDD это естественно выдуманные слова.
Для передачи сообщений вверх по стеку я предпочитаю использовать древнюю магию return и код который старается однозначна себя вести и использовать исключения по назначению, а не вместо return.
Я на своем подходе не настаиваю я просто пояснил как бы я и некоторые другие люди могут воспринимать валидацию и если моя позиция не сходится с вашей здесь нет ничего страшного.
Теперь по поводу кто какой программист это наверно не вам решать, о том навыках использования исключениями тоже, но как и все могу сказать что я далеко не идеальный и всегда бывают баги и если вы в своей жизни не допустили не одной баги в разработке, то либо очень мало разработали, либо еще не знаете о этих багах, ну или не считаете их за баги.
Я же все таки советовал бы почитать http://bfy.tw/BzlD и что исключения это конечно инструмент разработчика, но наверно все таки не ведения разработки через исключения, потому что при большом проекте и таким гуляниям по стеку становится мучительно больно это поддерживать и чем больше проект тем больше это напоминает goto разработку.
и дальнейшее обсуждение возможно будет не очень конструктивным, давайте каждый останется при своем мнение, потому то как использовать исключение это очень спорная тема и существуют разные подходы, о которых я знаю, в том числе и о том вы говорите, но я хочу сказать что есть и другие и иногда они бывают лучше а иногда хуже.
AlexLeonov
25.05.2017 15:02-1А зачем тогда вообще эта простыня малосвязного текста, переполненного ложными высказываниями?
Далее вы предлагаете вести разработку в стиле «Exception Drive Development (EDD)»
Я ничего вам не предлагал. Перестаньте так болезненно реагировать на существование внешнего мира не совсем совпадающего с вашими представлениями о нём.
AlexLeonov
Искренне не понял, причем тут слово «Полиморфизм». Возможно вы имели в виду что-то другое? Например «тайп-хинтинг»?
P.S. За вот такое:
я обычно увольняю…
Надеюсь, вы так не пишете в продакшн-коде? Советовал бы вам отредактировать, заменив на привычные пути, __DIR__, без скобок и двойных кавычек…
the_kane_is_alive
Да. Всё же я имел ввиду определение типов.
А под словом «полиморфизм» здесь предполагалось то, что один и тот же трейт можно использовать во всех классах, к которому трейт присоединён.
Поправлюсь в терминологии.
Просто дурная привычка пилить всё на скорую руку.
Добавил в статью автозагрузку классов вместо include-ов.
AlexLeonov
Даже на скорую руку такого нарочно не придумаешь. Как вы вообще приходите к таким «решениям»?
И что изменилось в итоге? Так и остались dirname(__FILE__), дебильные двойные кавычки и скобки, а еще _once в автозагрузчике ))
Я бы вам искренне советовал убрать статью в черновики и переделать.
Очень много ляпов, которые мешают даже понять ее (статью) идею.
the_kane_is_alive
Ну раз уж очень хочется, то хорошо, можно и так написать:
Выглядит более элегантно по сравнению с предыдущим вариантом.
AlexLeonov
Это отвратительно выглядит. Вы знакомы со стандартами PSR?
С чего вы взяли вообще что конечный слэш является частью пути? Где, в какой спецификации, в какой файловой системе это так?
Почему вдруг для трейтов нужна отдельная папка, а для неких «моделей» — другая? Откуда берется такая структура?
И вопросов подобных миллион.
the_kane_is_alive
Это просто пример. Можно его сколь угодно доводить до идеального состояния, но конечной целью было не создать целое приложение, а протестировать работоспособность одного трейта на примере пары классов.
Можно запихнуть всё и в одну папку models, нет никаких проблем)
AlexLeonov
Это небрежность, идентичная непрофессионализму. Имхо.
AlexLeonov
Чтобы не быть голословным, приведу пример относительно (!) нормального автозагрузчика:
Писал на калькуляторе в метро, так что могут быть ошибки ))
Nahrimet
Не поленился и почитал всю ветку переписки. Просто любопытно, зачем Вы так осаждаете человека?