tl;dr Вкратце, в данной статье я создам трейт, позволяющий даже в версиях PHP младше 5.6 (до версии 5.4) добиться от компилятора поведения, подобного любому статическому языку программирования. Причём трейт будет валидировать не только входные, но и выходные парамеры тоже. Так сказать, полное погружение в тайп-хинтинг.
Данный трейт вы сможете без проблем подключить и использовать в своих веб-приложениях.


Тайп-хинтинг в 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.

Весь исходник трейта
З.Ы. Я специально написал валидацию каждого примитива по отдельности, чтобы в дальнейшем была возможность передать в трейт массив со своими дополнительными правилами валидации. Например для integer-а можно провалидировать maxValue, minValue, isNatural, для строк можно валидировать length вместо emptiness и так далее.

<?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 (простая чернильная ручка, инициализирующаяся с каким-то количеством чернил), выводящий сообщение на экран.

Класс 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)


  1. AlexLeonov
    24.05.2017 13:42
    +3

    Искренне не понял, причем тут слово «Полиморфизм». Возможно вы имели в виду что-то другое? Например «тайп-хинтинг»?

    P.S. За вот такое:

    include_once("{$dir}{$sep}traits{$sep}Validator.php");

    я обычно увольняю…
    Надеюсь, вы так не пишете в продакшн-коде? Советовал бы вам отредактировать, заменив на привычные пути, __DIR__, без скобок и двойных кавычек…


    1. the_kane_is_alive
      24.05.2017 16:35

      Искренне не понял, причем тут слово «Полиморфизм». Возможно вы имели в виду что-то другое? Например «тайп-хинтинг»?

      Да. Всё же я имел ввиду определение типов.
      А под словом «полиморфизм» здесь предполагалось то, что один и тот же трейт можно использовать во всех классах, к которому трейт присоединён.
      Поправлюсь в терминологии.

      P.S. За вот такое:
      include_once("{$dir}{$sep}traits{$sep}Validator.php");
      

      я обычно увольняю…

      Просто дурная привычка пилить всё на скорую руку.
      Добавил в статью автозагрузку классов вместо include-ов.


      1. AlexLeonov
        24.05.2017 17:05

        Даже на скорую руку такого нарочно не придумаешь. Как вы вообще приходите к таким «решениям»?
        И что изменилось в итоге? Так и остались dirname(__FILE__), дебильные двойные кавычки и скобки, а еще _once в автозагрузчике ))

        Я бы вам искренне советовал убрать статью в черновики и переделать.

        Очень много ляпов, которые мешают даже понять ее (статью) идею.


        1. the_kane_is_alive
          24.05.2017 17:15

          Ну раз уж очень хочется, то хорошо, можно и так написать:

          $separator = DIRECTORY_SEPARATOR;
          $basePath = __DIR__ . $sep;
          
          $traitsFolderName = 'traits';
          include_once($basePath . $traitsFolderName . $separator . 'Validator.php');
          
          $modelsFolderName = 'models';
          include_once($basePath . $modelsFolderName . $separator . 'Pen.php');
          include_once($basePath . $modelsFolderName . $separator . 'Notebook.php');
          

          Выглядит более элегантно по сравнению с предыдущим вариантом.


          1. AlexLeonov
            24.05.2017 17:16

            Это отвратительно выглядит. Вы знакомы со стандартами PSR?

            $basePath = __DIR__. $sep;

            С чего вы взяли вообще что конечный слэш является частью пути? Где, в какой спецификации, в какой файловой системе это так?

            Почему вдруг для трейтов нужна отдельная папка, а для неких «моделей» — другая? Откуда берется такая структура?

            И вопросов подобных миллион.


            1. the_kane_is_alive
              24.05.2017 17:59

              С чего вы взяли вообще что конечный слэш является частью пути? Где, в какой спецификации, в какой файловой системе это так?

              Это просто пример. Можно его сколь угодно доводить до идеального состояния, но конечной целью было не создать целое приложение, а протестировать работоспособность одного трейта на примере пары классов.
              Почему вдруг для трейтов нужна отдельная папка, а для неких «моделей» — другая? Откуда берется такая структура?

              Можно запихнуть всё и в одну папку models, нет никаких проблем)


              1. AlexLeonov
                24.05.2017 22:32

                Это просто пример.

                Это небрежность, идентичная непрофессионализму. Имхо.


          1. AlexLeonov
            24.05.2017 17:23

            Чтобы не быть голословным, приведу пример относительно (!) нормального автозагрузчика:

            const VENDOR = 'YourVendorName';
            const SRC_PATH = __DIR__ . '/src'; // к примеру
            
            spl_autoload_register(function ($class) {
                if (0 === strpos($class, VENDOR)) {
                    $file = SRC_PATH  
                      . str_replace('\\', '/', preg_replace('~^' . VENDOR . '~', '', $class)) 
                      . '.php';
                    if (is_readable($file)) {
                        require $file;
                    }
                }
            });
            


            Писал на калькуляторе в метро, так что могут быть ошибки ))


            1. Nahrimet
              25.05.2017 17:41
              +2

              Не поленился и почитал всю ветку переписки. Просто любопытно, зачем Вы так осаждаете человека?


  1. maxru
    24.05.2017 13:54
    +1

    Я долго писал обстоятельный комментарий, но потом всё стёр, потому что, мне кажется, тут можно обойтись одной строкой:


    "Вот так, с помощью нехитрых приспособлений, буханку белого (или чёрного) хлеба можно превратить в троллейбус. Но зачем?"


  1. maximw
    24.05.2017 15:19

    public function insertSort(ArrayForSorting &$sortArray)
    
    Объекты передаются по ссылке, амперсанд не нужен.

    // дабы не нарушать святы принципы полиморфизма,
    // будем возвращать пустой массив в случае ошибки валидации,
    // а не false или какой-нибудь -1.
    Вместо возврата корректного ответа на ошибочные аргументы, лучше кидать исключение. И при чем тут принципы полиморфизма?

    Ну и лучше все же на 7 переходить. А то тут просится картинка с буханкой-троллейбусом.


    1. the_kane_is_alive
      24.05.2017 17:05

      Вместо возврата корректного ответа на ошибочные аргументы, лучше кидать исключение.

      Это в трейте и реализовано.

      И при чем тут принципы полиморфизма?

      Да, про полиморфизм я не к месту написал, признаю.

      Ну и лучше все же на 7 переходить. А то тут просится картинка с буханкой-троллейбусом.

      Многие хостинг-провайдеры до сих пор остались в прошлом веке со старыми версиями.


      1. AlexLeonov
        24.05.2017 17:08

        Зачем вообще работать с хостинг-провайдерами? Вы к ним приколочены гвоздями? Простейший VPS, где можно поставить любую версию, стоит 150 рублей в месяц. Расскажите — чем лучше «хостинг»?


        1. the_kane_is_alive
          24.05.2017 17:19

          Расскажите — чем лучше «хостинг»?

          Согласен, ничем.
          (Если не считать того, что всё за тебя уже настроено заранее)


          1. Fortistello
            25.05.2017 07:00

            (Если не считать того, что всё за тебя уже настроено заранее)

            sudo apt install php php-common php-mysql mysql apache2
            Базовый веб-сервер, не особо заморачиваясь со всякими http/2, rabbitmq, zabbix и прочими фенечками, устанавливается на VPS минут за 15, 10 из которых вы будете пить чай.

            Я вижу шаред-хостинги только как площадку для какого-то самопального блога на вордпрессе от человека, который немножко перерос простые html странички, но не хочет изучать программирование.


            1. maxru
              25.05.2017 12:11

              Не говоря уже о том, что сейчас каждый первый VPS хостинг умеет сразу ставить шаблоны LAMP/LEMP



  1. smple
    25.05.2017 13:37
    +1

    tldr Давайте валидировать входные данные ручками все правила валидации сложим в трейт который будем подключать.


    Реальность такова что "строгую" типизацию ввели как раз из за того что люди очень много валидировали параметры ручками и это вынесли на уровень языка, значит это уже делали до вас и ничего нового тому кому нужна статическая типизация вы не сказали.


    Далее 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 надо будет его преобразовать, выглядит забавно чисто с точки зрения иследования, на продакшене я бы такое применять не стал


    1. AlexLeonov
      25.05.2017 13:40

      Согласен со всем, кроме

      далеко не лучшая практика потому что ошибка в данных это нормальная ситуация и она должна быть обработано и по возможности без исключений

      Ошибка валидации — это классическое исключение. Не фатальная ошибка, но нечто, что ждет нашей реакции и делает дальнейшее линейное выполнение программы невозможным или бессмысленным.

      Другое дело, что надо исключения в валидации немного не так топорно использовать, конечно же.


      1. smple
        25.05.2017 14:04

        давайте я немного поясню


        Валидация подразумевает два состояния, валидно и не валидно и если не валидно должен быть функционал получения текста что именно не валидно, это нужно для того чтобы выбросить соответствующий тип исключения с соответствующим текстом в том месте где вы вызывали валидацию, также вместо исключения может быть предусмотренно поведение в случае невалидных данных


        Например у вас спрашивают год рождения вы указываете 2222 с точки зрения валидации это не правильно год рождения не может быть больше текущего года и прекращать и падать всему приложению нет нужды, потому что после вашего кода может быть например логирование в приложение которое залогировало входящий запрос, а выходящий (ответ) не залогировало, вы можете сказать так сделайте логирование в обработке exception но это не правильно вы начинаете делать ветвление логики и писать ее в обработку исключения потом может появится еще что то.


        Вторая причина почему валидация не должна падать в корку это когда в форме спрашивают дату рождения и вы указываете 34.34.2222 тут сразу несколько ошибок валидации, неправильный номер месяца так как он больше 12, не правильный день и не правильный год, в случае валидации которая падает с exception она будет каждый этот exception выкидывать по одному, вы заметите что это должно проверятся на клиенте(js) и к вам уже приходить валидно и backend не обязан давать человеко читаемый текст, поэтому мы можем ронять приложение, но ронять резко приложение плохо смотри пункт один что могут быть события в конце обработки запроса.


        В противовес слову validation есть assert который как раз утверждение и если оно не истинно оно валится с исключением.


        tldr Я как потенциальный пользователь этого трейта видя validation ожидаю там валидацию, а не assertation, поэтому вам нужно или сделать там валидацию или переназвать метод на assertation, второе как мне кажется более правильно и менее трудо затратно, просто я имею ввиду что вы выбрали плохое имя для метода.


        Надеюсь я чуть прояснил ситуацию


        1. AlexLeonov
          25.05.2017 14:26

          Мне «прояснять» ничего не нужно. У вас фундаментальная ошибка в рассуждениях:

          прекращать и падать всему приложению нет нужды

          С чего вы взяли, что исключение — это «прекращать и падать»? Исключение — это инкапсулировать, передавать и управлять потоком.

          Избавьтесь уже от парадигмы «исключение — значит падение». Это просто объект, инкапсулирующий в себе информацию о случившемся. И не более того. Плюс способ передать этот объект вверх по стеку.

          Вот опять:
          assert который как раз утверждение и если оно не истинно оно валится с исключением

          Что за чушь? Почему «валится»? Выбрасывает исключение, уважаемый, выбрасывает. А вы — ловите. Если у вас что-то «валится» — вы хреновый программист и не умеете пользоваться исключениями. И assert() тоже.


          1. smple
            25.05.2017 14:58

            давайте сразу проясним


            Валится значит выполнение программы дальше бесмысленно, она останавливает обработку запроса корректно сообщая о том что завершена с ошибкой.


            Далее вы предлагаете вести разработку в стиле "Exception Drive Development (EDD)" и кидаться ими по стеку и заниматься рыбалкой (вылавливанием) я конечно понимаю что она имеет право на жизнь как например "GOTO Drive Development"(GTD) она даже может быть чуть похожа, но заниматься этим я этим все таки не буду, при этом я прекрасно знаю о важности обработки исключений и о том что есть RuntimeException и что в ходе обработки исключений программа может вернутся к нормальному исполнению (обработка с возвратом).


            EDD и GDD это естественно выдуманные слова.


            Для передачи сообщений вверх по стеку я предпочитаю использовать древнюю магию return и код который старается однозначна себя вести и использовать исключения по назначению, а не вместо return.


            Я на своем подходе не настаиваю я просто пояснил как бы я и некоторые другие люди могут воспринимать валидацию и если моя позиция не сходится с вашей здесь нет ничего страшного.


            Теперь по поводу кто какой программист это наверно не вам решать, о том навыках использования исключениями тоже, но как и все могу сказать что я далеко не идеальный и всегда бывают баги и если вы в своей жизни не допустили не одной баги в разработке, то либо очень мало разработали, либо еще не знаете о этих багах, ну или не считаете их за баги.


            Я же все таки советовал бы почитать http://bfy.tw/BzlD и что исключения это конечно инструмент разработчика, но наверно все таки не ведения разработки через исключения, потому что при большом проекте и таким гуляниям по стеку становится мучительно больно это поддерживать и чем больше проект тем больше это напоминает goto разработку.


            и дальнейшее обсуждение возможно будет не очень конструктивным, давайте каждый останется при своем мнение, потому то как использовать исключение это очень спорная тема и существуют разные подходы, о которых я знаю, в том числе и о том вы говорите, но я хочу сказать что есть и другие и иногда они бывают лучше а иногда хуже.


            1. AlexLeonov
              25.05.2017 15:02
              -1

              А зачем тогда вообще эта простыня малосвязного текста, переполненного ложными высказываниями?

              Далее вы предлагаете вести разработку в стиле «Exception Drive Development (EDD)»

              Я ничего вам не предлагал. Перестаньте так болезненно реагировать на существование внешнего мира не совсем совпадающего с вашими представлениями о нём.