Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > PHP: Extensions > Обработчик для проверки типов аргументов функций


Автор: ShamanVoodoo 26.1.2013, 15:47
Пользовательские функции не проверяют типы принимаемых аргументов,  для того чтобы это обойти я использую обработчик. Проблема в том, что в случае ошибки проверки возникает ошибка E_ERROR и ниже идуший не выполняется, приходится оборачивать каждую фунцию проверяющую типы аргументов в try catch , а мне хотелось бы чтобы выводилась ошибка E_NOTICE и код продолжал свое выполнение.

Посоветуйте что делать? Использовать set_exception_handler('exception_handler')? 
Может у кого-то есть в наработки в данном направлении? 
Буду благодарен за любую помощь!

Код

// Обработчик типов аргументов функций

final class TypeHint
{
    
    static private $TypeHints = array(
        'boolean'    => 'is_bool',
        'bool'        => 'is_bool',
        'integer'    => 'self::is_int',
        'int'        => 'self::is_int',
        'float'        => 'self::is_float',
        'double'    => 'self::is_float',
        'string'    => 'is_string',
        'resource'    => 'is_resource',
        'strict'    => 'self::is_strict',        // Имена классов\функций\аргуметов
        'url_param'    => 'self::is_url_param',    // параметр ЧПУ ссылки
        'file_name'    => 'self::is_file_name',    // Имя файла
    );
    
    // Проверка на соотвествие типу integer \ int
    static private function is_int( $value )
    {
        if( ( is_numeric($value) && is_string($value) && preg_match( '/^[-]{0,1}[0-9]+$/u', $value ) ) || is_int($value) )
        {
            return TRUE;
        }
        else 
        {
            return FALSE;
        };
    }
    
    // Проверка на соотвествие типу float \ double
    static private function is_float( $value )
    {
        if( ( is_numeric($value) && is_string($value) && preg_match( '/^[-]{0,1}[0-9]+\.[0-9]+$/u', $value ) ) || is_float($value) )
        {
            return TRUE;
        }
        else 
        {
            return FALSE;
        };
    }
    
    // Проверка на соотвествие типу strict
    static private function is_strict( $value )
    {
        if( is_string($value) && preg_match( '/^[a-z_\x7f-\xff]{2,}[a-z0-9_\x7f-\xff]*$/iu', $value ) )
        {
            return TRUE;
        }
        else 
        {
            return FALSE;
        };
    }
    
    // Проверка на соотвествие типу url_param
    static private function is_url_param( $value )
    {
        if( ( is_string($value) || is_int($value) ) && preg_match( '/^[a-z0-9_\x7f-\xff]+$/iu', $value ) )
        {
            return TRUE;
        }
        else 
        {
            return FALSE;
        };
    }
    
    // Проверка на соотвествие типу file_name
    static private function is_file_name( $value )
    {
        if( is_string($value) && preg_match( '/^[a-z0-9_\x7f-\xff]+\.[a-z0-9_\x7f-\xff]+$/iu', $value ) )
        {
            return TRUE;
        }
        else 
        {
            return FALSE;
        };
    }
    
    // Создание объекта возможно только через метод initializeHandler
    private function __construct() {}
    
    // Инициализация обработчика
    static public function initializeHandler()
    {
        set_error_handler( 'TypeHint::handleTypeHint' );
        return TRUE;
    }
    
    // Получение аргументов функции
    static private function getTypeHintedArgument( $ThBackTrace, $ThFunction, $ThArgIndex, &$ThArgValue )
    {
        foreach( $ThBackTrace as $ThTrace )
        {
            if( isset($ThTrace['function']) && $ThTrace['function'] == $ThFunction )
            {
                $ThArgValue = $ThTrace['args'][$ThArgIndex - 1];
                return TRUE;
            };
        };
        return FALSE;
    }
    
    // Обработчик
    static public function handleTypeHint( $ErrLevel, $ErrMessage, $ErrFile, $ErrLine  )
    {
        if( $ErrLevel == E_RECOVERABLE_ERROR )
        {
            if( preg_match( '/^Argument (\d)+ passed to (?:(\w+)::)?(\w+)\(\) must be an instance of (\w+), (\w+) given/u', $ErrMessage, $ErrMatches ) )
            {
                list( $ErrMatch, $ThArgIndex, $ThClass, $ThFunction, $ThHint, $ThType ) = $ErrMatches;
                if( isset( self::$TypeHints[$ThHint] ) )
                {
                    $ThBacktrace = debug_backtrace();
                    $ThArgValue  = NULL;
                    if( self::getTypeHintedArgument( $ThBacktrace, $ThFunction, $ThArgIndex, $ThArgValue ) )
                    {
                        if( call_user_func( self::$TypeHints[$ThHint], $ThArgValue ) )
                        {
                            return TRUE;
                        }
                        else
                        {
                            throw new ErrorException( $ErrMessage, 0, $ErrLevel, $ErrFile, $ErrLine );
                        };
                    };
                };
            };
        };
        
        return FALSE;
    }
    
}

// Инициализируем обработчик
TypeHint::initializeHandler();

// Тест
function test( strict $arg1, file_name $arg2, $arg3 = 1 ) { echo "<br>{$arg1} | {$arg2} | {$arg3}"; };

try{ test( 'func_2', array(1,2) ); } catch( ErrorException $e ) { echo "<br>".$e->getMessage(); };
try{ test( 10.1, 'str' ); } catch( ErrorException $e ) { echo "<br>".$e->getMessage(); };
try{ test( 'class_name', '02.jpg' ); } catch( ErrorException $e ) { echo "<br>".$e->getMessage(); };

Автор: Arantir 26.1.2013, 16:47
Замените 
Код

throw new ErrorException( $ErrMessage, 0, $ErrLevel, $ErrFile, $ErrLine );
 на 
Код

trigger_error($ErrMessage."; Level: ".$ErrLevel."; File: ".$ErrFile."; Line: ".$ErrLine);
trigger_error по умолчанию возвращает ошибку уровня E_USER_NOTICE, т.е. код будет обрабатываться дальше.

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

Автор: ShamanVoodoo 26.1.2013, 17:45
Arantir > не работает

Выдает
Notice: Argument 1 passed to test() must be an instance of strict, double given, called in ...\www\index.php on line 94 and defined; Level: 4096; File: ...\www\index.php; Line: 92 in ...\www\core\class.TypeHint.php on line 132
Catchable fatal error: Argument 1 passed to test() must be an instance of strict, double given, called in ...\www\index.php on line 94 and defined in ...\www\index.php on line 92

и дальше не выполняется

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

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)