Контроль типа

PHP 5 предоставляет возможность использовать контроль типов. На данный момент функции имеют возможность заставлять параметры быть либо объектами (путем указания имени класса в прототипе функции), либо интерфейсами, либо массивами (начиная с PHP 5.1), или колбеком с типом callable (начиная с PHP 5.4). Однако, если NULL использовался как значение параметра по умолчанию, то это будет также допустимо в качестве аргумента для последующего вызова.

Если класс или интерфейс указан для контроля типа, то все его потомки или реализации также допустимы.

Контроль типа не может быть использован со скалярными типами, такими как int или string. Трейты также недопустимы.

Пример #1 Пример контроля типов


Наша группа в телеграмм для обмена идеями, проектами, мыслями, людьми в сфере ИТ г.Ростова-на-Дону: @it_rostov
// Тестовый класс
class MyClass
{
    /**
     * Тестовая функция
     *
     * Первый параметр должен быть объектом типа OtherClass
     */
    public function test(OtherClass $otherclass) {
        echo $otherclass->var;
    }
    /**
     * Другая тестовая функция
     *
     * Первый параметр должен быть массивом
     */
    public function test_array(array $input_array) {
        print_r($input_array);
    }
    
    /**
     * Первый параметр должен быть итератором
     */
    public function test_interface(Traversable $iterator) {
        echo get_class($iterator);
    }
    
    /**
     * Первый параметр должен быть типа callable
     */
    public function test_callable(callable $callback, $data) {
        call_user_func($callback, $data);
    }
}
// Другой тестовый класс
class OtherClass {
    public $var = 'Hello World';
}

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


// Экземпляры каждого класса
$myclass = new MyClass;
$otherclass = new OtherClass;
// Ошибка: Аргумент 1 должен быть экземпляром класса OtherClass
$myclass->test('hello');
// Ошибка: Аргумент 1 должен быть экземпляром класса OtherClass
$foo = new stdClass;
$myclass->test($foo);
// Ошибка: Аргумент 1 не должен быть null
$myclass->test(null);
// Работает: Выводит Hello World
$myclass->test($otherclass);
// Ошибка: Аргумент 1 должен быть массив
$myclass->test_array('a string');
// Работает: Выводит массив
$myclass->test_array(array('a', 'b', 'c'));
// Работает: Выводит ArrayObject
$myclass->test_interface(new ArrayObject(array()));
// Работает: Выводит int(1)
$myclass->test_callable('var_dump', 1);

Также, контроль типов работает и с функциями:


// Пример класса
class MyClass {
    public $var = 'Hello World';
}
/**
 * Тестовая функция
 *
 * Первый параметр должен быть объект класса MyClass
 */
function myFunction(MyClass $foo) {
    echo $foo->var;
}
// Это работает
$myclass = new MyClass;
myFunction($myclass);

Контроль типов допускает значения NULL:


/* Прием значения NULL */
function test(stdClass $obj = NULL) {
}
test(NULL);
test(new stdClass);

User Contributed Notes 28 notes



10
Daniel dot L dot Wood at Gmail dot Com5 years ago
define('TYPEHINT_PCRE'              ,'/^Argument (\d)+ passed to (?:(\w+)::)?(\w+)\(\) must be an instance of (\w+), (\w+) given/');
class Typehint
{
    private static $Typehints = array(
        'boolean'   => 'is_bool',
        'integer'   => 'is_int',
        'float'     => 'is_float',
        'string'    => 'is_string',
        'resrouce'  => 'is_resource'
    );
    private function __Constrct() {}
    public static function initializeHandler()
    {
        set_error_handler('Typehint::handleTypehint');
        return TRUE;
    }
    private static function getTypehintedArgument($ThBackTrace, $ThFunction, $ThArgIndex, &$ThArgValue)
    {
        foreach ($ThBackTrace as $ThTrace)
        {
            // Match the function; Note we could do more defensive error checking.
            if (isset($ThTrace['function']) && $ThTrace['function'] == $ThFunction)
            {
                $ThArgValue = $ThTrace['args'][$ThArgIndex - 1];
                return TRUE;
            }
        }
        return FALSE;
    }
    public static function handleTypehint($ErrLevel, $ErrMessage)
    {
        if ($ErrLevel == E_RECOVERABLE_ERROR)
        {
            if (preg_match(TYPEHINT_PCRE, $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;
                        }
                    }
                }
            }
        }
        return FALSE;
    }
}
Typehint::initializeHandler();


2
sorin dot badea91 at gmail dot com7 months ago
/**
 * Primary types
 */
class Type
{
    const SKIP     = 0;
    const INT      = 1;
    const STRING   = 2;
    const BOOLEAN  = 3;
    const CALLBACK = 4;
    const FLOAT    = 5;
    const RESOURCE = 6;
}
/**
 * @throws InvalidArgumentException
 */
function ensureType()
{
    $debugStack = debug_backtrace();
    $argv       = $debugStack[1]['args'];
    $types      = func_get_args();
    foreach ($argv as $key => $value) {
        $message = null;
        if (is_null($value)) {
            continue;
        }
        switch ($types[$key]) {
            case Type::INT:
                if (!is_int($value)) {
                    $message = 'Argument ' . $key . ' passed to ' . $debugStack[1]['function'] . '() must be of type int';
                }
                break;
            case Type::STRING:
                if (!is_string($value)) {
                    $message = 'Argument ' . $key . ' passed to ' . $debugStack[1]['function'] . '() must be of type string';
                }
                break;
            case Type::BOOLEAN:
                if (!is_bool($value)) {
                    $message = 'Argument ' . $key . ' passed to ' . $debugStack[1]['function'] . '() must be of type boolean';
                }
                break;
            case Type::CALLBACK:
                if (!is_callable($value)) {
                    $message = 'Argument ' . $key . ' passed to ' . $debugStack[1]['function'] . '() must be a valid callback';
                }
                break;
            case Type::FLOAT:
                if (!is_float($value)) {
                    $message = 'Argument ' . $key . ' passed to ' . $debugStack[1]['function'] . '() must be of type float';
                }
                break;
            case Type::RESOURCE:
                if (!is_resource($value)) {
                    $message = 'Argument ' . $key . ' passed to ' . $debugStack[1]['function'] . '() must be of type resource';
                }
                break;
        }
        if (!is_null($message)) {
            if (is_object($value)) {
                $message .= ', instance of ' . get_class($value) . ' given';
            } else {
                $message .= ', ' . gettype($value) . ' given';
            }
            throw new InvalidArgumentException($message);
        }
    }
}
function dummyFunction($var1, $var2, $var3)
{
    ensureType(Type::BOOLEAN, Type::INT, Type::STRING);
}
$object = new ReflectionClass('ReflectionClass');
dummyFunction(1, $object, 'Hello there'); 
 
2 
 michaelrfairhurst at gmail dot com ¶9 months ago
 The scalar type hinting solutions are all overthinking it. I provided the optimized regex version, as well as the fastest implementation I've come up with, which just uses strpos. Then I benchmark both against the TypeHint class.
<?php
function optimized_strpos($ErrLevel, $ErrMessage) {
        if ($ErrLevel == E_RECOVERABLE_ERROR
            // order this according to what your app uses most
            return strpos($ErrMessage, 'must be an instance of string, string')
                || strpos($ErrMessage, 'must be an instance of integer, integer')
                || strpos($ErrMessage, 'must be an instance of float, double')
                || strpos($ErrMessage, 'must be an instance of boolean, boolean')
                || strpos($ErrMessage, 'must be an instance of resource, resource');
}
function optimized_regex($ErrLevel, $ErrMessage) {
        if ($ErrLevel == E_RECOVERABLE_ERROR) {
            if(preg_match('/^Argument \d+ passed to (?:\w+::)?\w+\(\) must be an instance of (\w+), (\w+) given/', $ErrMessage, $matches))
                return $matches[1] == ($matches[2] == 'double' ? 'float' : $matches[2]);
        }
}


2
john at cast dot com2 years ago
class MyClass{
  public static function Cast(MyClass &$object=NULL){
       return $object;
  }
  public method CallMe(){
  }
}
$x=unserialize($someContent);
$x=MyObject::Cast($x);
$x->CallMe();


2
bantam at banime dot com4 years ago
        public static function typehint($level, $message)
        {
            if($level == E_RECOVERABLE_ERROR)
            {
                if(preg_match('/^Argument (\d)+ passed to (?:(\w+)::)?(\w+)\(\) must be an instance of (\w+), (\w+) given/', $message, $match))
                {
                    if($match[4] == $match[5])
                        return true;
                }
            }
            
            return false;
        }


0
Anonymous2 years ago
class String extends AutoBoxedObject
{
    public $value;
 
    public function __construct($value) {
        $this->value = $value;
    }
 
    public function __toString() {
        return "$this->value";
    }
 
    public function toUpperCase() {
        return strtoupper($this->value);
    }
}
 
function & string($value = null) {
    $x = & VariablesManager::getNewPointer(new String($value));
    return $x;
}


0
macobex dot exe at gmail dot com2 years ago
function testPrimitive($str) {
   if ( is_string($str) )
      $x = $str . ' world';   // using the argument's value
   else
      trigger_error('Argument passed must be of type string.');
   }


0
alejosimon at gmail dot com3 years ago
function phpErrorHandler( $code, $message, $file, $line ) {
    if ( error_reporting() & $code ) {
        if ( $code == E_RECOVERABLE_ERROR ) { // Scalar Type-Hinting patch.
            $regexp = '/^Argument (\d)+ passed to (.+) must be an instance of (?<hint>.+), (?<given>.+) given/i' ;
            if ( preg_match( $regexp, $message, $match ) ) {
                $given = $match[ 'given' ] ;
                $hint  = end( explode( '\\', $match[ 'hint' ] ) ) ; // namespaces support.
                if ( $hint == $given ) return true ;
            }
        }
        return false ;
    }
}
set_error_handler( 'phpErrorHandler' ) ;
/************************************/
function typeHintTest( integer $arg1 ) {
    print_r( $arg1 ) ;
}
typeHintTest( true ) ; // Error throw because not integer type.


0
gdecad at NOSPAM dot example dot com3 years ago
function testTest($arg) {
    if (!is_string($arg)) {
        trigger_error('Argument $arg passed to test must be an instance of string, other given');
    }
    return $arg;
}


0
bogdan dot wrobel at gmail dot com3 years ago
namespace a {
    interface A {
        public function execute(string $sth);
    }
}
namespace a\b {
    class B implements a\A {
        public function execute(string $sth){} // Wrong
        public function execute(a\string $sth){} // Correct
    }
} // Causes a fatal error on this line. 'string' is considered an object from the namespace 'a'


0
dpariyskiy at netatwork dot com4 years ago
public static function initializeHandler() 
{
    
    set_error_handler(array('Typehint','handleTypehint')); 
    
    return TRUE; 
}


0
wickedmuso at gmail dot com5 years ago
abstract class DataType
{
    protected $length;
    protected $precision;
    protected $number;
    
  public function __construct()
  {
    $this->number = false;
    $this->precision = null;
  }
    
    public function GetLength()
    {
        return $this->length;
    }
    
    public function IsNumber()
    {
        return $this->number;
    }
    public function GetPrecision()
    {
        return $this->precision;
    }
}
class Integer extends DataType
{
    public function __construct($length = 12)
    {
        parent::__construct();
        $this->number = true;
        $this->length = $length;
        $this->precision = 0;
    }
}
class Float extends DataType
{
  public function __construct($length = 12, $precision = 2)
  {
    parent::__construct();
    $this->number = true;
    $this->length = $length;
    $this->precision = $precision;
  }
}
class String extends DataType
{
    public function __construct($length = 255)
    {
        parent::__constructor();
        $this->length = $length;
    }
}
//etc etc through the types...


0
wbcarts at juno dot com5 years ago
class Point {
  public $x, $y;
  public function __construct($xVal = 0, $yVal = 0) {
    $this->x = $xVal;
    $this->y = $yVal;
  }
}
class Polyline {
  protected $points = array();
  public function addPoint(Point $p) {  // the line we're interested in...
    $this->points[] = $p;
  }
}
$point1 = new Point(15, 12);
$polyline = new Polyline();
$polyline->addPoint($point1);
$polyline->addPoint(new Point(55, 22));
$polyline->addPoint(new Point(33, 31));
$polyline->addPoint(new stdClass());    // PHP will throw an error for us! 


0
DanielLWood [at] Gmail [dot] Com5 years ago
function notypehinting($x)
{
    is_string($x);   //checking the type manually instead
}
function typehinting(string $x)
{
}
$test=new timer;
for($i=0;$i<10000;$i++)
{
try{
    notypehinting('test');
}
catch(Exception $e){}
}
echo $test.'<br />';
$test2=new timer;
for($i=0;$i<10000;$i++)
{
try{
    typehinting('test');
}
catch(Exception $e){}
}
echo $test2.'<br />';


0
comments at ignorethis netweblogic com5 years ago
//Wont work
function test(ObjName $obj = ''){
   //.....
}
//Will work
function test(Array $obj = array()){
   //.....
}


0
marcus at ignorethis netweblogic dot com5 years ago
public static function handleTypehint($ErrLevel, $ErrMessage) {
        if ($ErrLevel == E_RECOVERABLE_ERROR) {
            if (preg_match ( TYPEHINT_PCRE, $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;
                        }
                    }
                }
                throw new Exception($ErrMessage);
            }
        }
        return FALSE;
    }


0
madness5 years ago
    public static function handleError($errno, $errstr, $errfile, $errline){
        // Implements just-in-time classes for broad type hinting
        if (TypeHint::handleTypehint($errno, $errstr)){
            return true;
        }
        
        // do your usual stuff here
        /*
         * ...
         */
    }


0
jesdisciple @t gmail -dot- com6 years ago
//...
// Fatal Error: Argument 1 must not be null
$myclass->test(null);
//...


0
Jazz6 years ago
abstract class Object
{
    public abstract function toString( );
    public abstract function equals( Object &$o );
}
class Chair extends Object
{
    public function toString( )
    {
        return 'This is a chair.';
    }
    
    public function equals( Chair &$o )
    {
        return TRUE;
    }
}
class Table extends Object
{
    public function toString( )
    {
        return 'This is a table.';
    }
    
    public function equals( Table &$o )
    {
        return TRUE;
    }
}
$chair = new Chair();
$table = new Table();
echo $chair->equals( $table );


0
Anonymous7 years ago
interface fooface
{
    public function foo ();
}
class fooclass implements fooface
{
    public function foo ()
    {
        echo ('foo<br />');
    }
}
class barclass implements fooface
{
    public function foo ()
    {
        echo ('bar<br />');
    }
}
class bazclass implements fooface
{
    public function foo ()
    {
        echo ('baz<br />');
    }
}
class quuxclass
{
    public function foo ()
    {
        echo ('quux<br />');
    }
}
function callfoo (fooface $myClass)
{
    $myClass -> foo ();
}
$myfoo = new fooclass;
$mybar = new barclass;
$mybaz = new bazclass;
$myquux = new quuxclass;
callfoo ($myfoo);
callfoo ($mybar);
callfoo ($mybaz);
callfoo ($myquux); // Fails because the quuxclass doesn't implement the fooface interface


0
mlovett at morpace dot com8 years ago
Type hinting works with interfaces too. In other words, you can specify the name of an interface for a function parameter, and the object passed in must implement that interface, or else type hinting throws an exception.




Смотрите также:
Описание на ru2.php.net
Описание на php.ru

.

Популярное:


Содержание: