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

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

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

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

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

<?php
// Тестовый класс
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';
}
?>

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

<?php
// Экземпляры каждого класса
$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);
?>

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

<?php
// Пример класса
class MyClass {
    public 
$var 'Hello World';
}

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

// Это работает
$myclass = new MyClass;
myFunction($myclass);
?>

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

<?php

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

}

test(NULL);
test(new stdClass);

?>

Коментарии

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.
2005-07-06 06:54:33
http://php5.kiev.ua/manual/ru/language.oop5.typehinting.html
The manual's sample code says:
<?php
//...
// Fatal Error: Argument 1 must not be null
$myclass->test(null);
//...
?>

And this is true, unless a default value of NULL is given; in fact, this is the only way to give a default value for object arguments (as a default value must be a constant expression):
<?php
$mine 
= new MyClass();
$mine->test(NULL);
class 
MyClass{
    public function 
__construct(OtherClass $arg NULL){
        if(
is_null($arg)){
           
//Apply default value here.
       
}
    }
    public function 
test(array $arr NULL){
       
print_r($arr);
    }
}
class 
OtherClass{
   
}
?>
2007-11-06 21:50:53
http://php5.kiev.ua/manual/ru/language.oop5.typehinting.html
TYPE-HINTING and VISIBILITY

Type-hinting is just one more small piece of PHP that protects our objects when visibility cannot.

<?php

class Point {
  public 
$x$y;

  public function 
__construct($xVal 0$yVal 0) {
   
$this->$xVal;
   
$this->$yVal;
  }
}

class 
Polyline {
  protected 
$points = array();

  public function 
addPoint(Point $p) {  // the line we're interested in...
   
$this->points[] = $p;
  }
}

$point1 = new Point(1512);
$polyline = new Polyline();
$polyline->addPoint($point1);
$polyline->addPoint(new Point(5522));
$polyline->addPoint(new Point(3331));

$polyline->addPoint(new stdClass());    // PHP will throw an error for us! 

?>

Since our Polyline::addPoint() function has to be public, any outside code can try to pass anything. But, when type-hinting is declared, PHP throws an error when phoney data tries to sneak by.
2008-10-01 21:20:52
http://php5.kiev.ua/manual/ru/language.oop5.typehinting.html
One useful thing with Type Hinting that I could not find in the documentation (but tested) is that you can also use an Interface in the hint (versus a Class).  This is a very useful tool if you are trying to code to Interfaces rather than Classes (which is common in Test Driven Development and Dependency Injection paradigms).  It means your external class can present itself into the method as long as it implements the nominated Interface (obviously).
2008-10-24 23:26:35
http://php5.kiev.ua/manual/ru/language.oop5.typehinting.html
I have made a little bench between three method of type hinting for native type (string, integer, ...).

First method : by test type in function like :
<?php
function testTest($arg) {
    if (!
is_string($arg)) {
       
trigger_error('Argument $arg passed to test must be an instance of string, other given');
    }
    return 
$arg;
}
?>

Second method : by object representing native type :
<?php
function testObject(StringObj $arg) {
    return 
$arg;
}
?>

Third method : by class TypeHint proposed by Daniel :
<?php
function testHint(string $arg) {
    return 
$arg;
}
?>

the results are here :
bench for 100000 iterations,  in seconds
        avg                    min                    max                    total
test    5.3275489807129E-6    2.8610229492188E-6    0.0033020973205566    0.53275895118713
object    4.9089097976685E-6    3.814697265625E-6    0.0025870800018311    0.49089503288269
hint    3.2338891029358E-5    2.9802322387695E-5    0.0025920867919922    3.2338931560516

As you can see, the method by object is the best
now you know...
2010-10-22 09:02:49
http://php5.kiev.ua/manual/ru/language.oop5.typehinting.html
i use eclipse ganymede as an IDE and it offers "intellisense" where it can, i.e. when variables are "declared" via type hinting or a "new"-statement . i found using the following pattern helps eclipse along as well:

<?php
class MyClass{

  public static function 
Cast(MyClass &$object=NULL){
       return 
$object;
  }

  public 
method CallMe(){
  }
}

$x=unserialize($someContent);
$x=MyObject::Cast($x);
$x->CallMe();
?>

after calling Cast(), due to the type hinting, eclipse offers me the "CallMe" function in a dropdown when i type "$x->" in the code afterwards.

i found this very practical und included the Cast() function in my code template for new classes. i've been wondering, if there is a drawback i oversaw, but i haven't noticed any negative effects so far... maybe some of you will find this just as handy as i do ;o)

p.s. do note: when used in inherited classes a STRICT-notice comes up, because the function definition of the inherited class doesn't match the parent's definition (different type hinting) - but it works great!
2011-04-29 21:33:51
http://php5.kiev.ua/manual/ru/language.oop5.typehinting.html
I've implemented a basic function to ensure argument's type.

<?php
/**
 * 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::BOOLEANType::INTType::STRING);
}

$object = new ReflectionClass('ReflectionClass');

dummyFunction(1$object'Hello there');
2013-04-25 06:05:28
http://php5.kiev.ua/manual/ru/language.oop5.typehinting.html
I've done some tests of the overhead that class Typehint  gives us.
At my PC it goes as follows:
teststringNormal took: 0.041965961456299
teststringOverhead took: 0.48374915122986
It's like 10x longer time (not mention about memory usage), it's just because exception is thrown EVERY SINGLE TIME, along with expensive preg_match() and debug_backtrace() calls.
I think that using class in bigger applications will increase overhead like 100% or more.
<?php

function teststringOverhead(string $string) { 
    return 
$string;
}
function 
teststringNormal($string){
    if(!
is_string($string)){
        return;
    }
    return 
$string
}
$loopTimes 20000;

/////////// test of overhead implementation vs normal
$t1 microtime(true);
for(
$i 0$i <= $loopTimes$i++)  teststringNormal("xxx");
echo 
"<br>teststringNormal took: " . (microtime(true) - $t1);

$t2 microtime(true);
for(
$i 0$i <= $loopTimes$i++)  teststringOverhead("xxx");
echo 
"<br>teststringOverhead took: " . (microtime(true) - $t2);
?>
2014-03-06 11:16:03
http://php5.kiev.ua/manual/ru/language.oop5.typehinting.html
Автор:
i think this is pretty close.

$lines=hhb_mustbe('array',file("foofile"));
//either file succeeds, and returns an array, or file returns FALSE which is not an array, which throws an unexpectedValueException.

$socket=hhb_mustbe('Socket',socket_create(AF_INET,SOCK_STREAM,getprotobyname('tcp')));
//either socket_create succeeds, and returns a Socket, or socket_create returns False, which is not a resource of type Socket, and you get an UnexpectedValueException

$size=hhb_mustbe('int',filesize(somefile));
//either filesize() returns an integer, or returns FALSE wich is not an int, and you'll get UnexpectedValueException.

    function hhb_mustbe(/*string*/$type,/*mixed*/$variable){
        //should it be UnexpectedValueException or InvalidArgumentException?
        //going with UnexpectedValueException for now...
        $actual_type=gettype($variable);
        if($actual_type==='unknown type'){
            //i dont know how this can happen, but it is documented as a possible return value of gettype...
            throw new Exception('could not determine the type of the variable!');
        }
        if($actual_type==='object'){
            if(!is_a($variable,$type)){
                $dbg=get_class($variable);
                throw new UnexpectedValueException('variable is an object which does NOT implement class: '.$type.'. it is of class: '.$dbg);
            }
            return $variable;
        }
        if($actual_type==='resource'){
            $dbg=get_resource_type($variable);
            if($dbg!==$type){
                throw new UnexpectedValueException('variable is a resource, which is NOT of type: '.$type.'. it is of type: '.$dbg);
            }
            return $variable;
        }
        //now a few special cases
        if($type==='bool'){
            $parsed_type='boolean';   
            } else if($type==='int'){
            $parsed_type='integer';
            } else if($type==='float'){
            $parsed_type='double';
            } else if($type==='null'){
            $parsed_type='NULL';
            } else{
            $parsed_type=$type;
        }
        if($parsed_type!==$actual_type && $type!==$actual_type){
            throw new UnexpectedValueException('variable is NOT of type: '.$type.'. it is of type: '.$actual_type);
        }
        //ok, variable passed all tests.
        return $variable;
    }
2015-08-30 16:52:14
http://php5.kiev.ua/manual/ru/language.oop5.typehinting.html
In regards to language.oop5.typehinting#103729 , when using PHP in Eclipse, the best way to typehint is already available in Eclipse natively. 

The format is /* @var $variable ObjectName */

The single /* is important. You can also use namespaces, IE 

/* @var $variable \Some\Namespace */

In short, there is no reason to create functions that return itself.
2016-04-19 16:31:40
http://php5.kiev.ua/manual/ru/language.oop5.typehinting.html
Автор:
Please note, that with PHP 7, type hinting is faster than normal implementation.
This has to do with type hinting before PHP 7 allowing only objects and arrays and anything else (scalars) would throw an error.

I did some using code "doom at doom dot pl" posted 2 years ago 

Here are results from couple of test runs using PHP 7 (200.000 loops)
- teststringNormal took: 0.01799488067627
- teststringOverhead took: 0.012195825576782

- teststringNormal took: 0.027030944824219
- teststringOverhead took: 0.012197017669678

- teststringNormal took: 0.017856121063232
- teststringOverhead took: 0.012274980545044

As you can see, the overhead is faster and much more consistent.

Here is one run with the is_string check removed from Normal:
- teststringNormal took: 0.010342836380005
- teststringOverhead took: 0.012849092483521
2017-01-10 19:23:26
http://php5.kiev.ua/manual/ru/language.oop5.typehinting.html
the following code demonstrate php7 response to various combination of inputs and type hinting in a simple matrix:
<?php
$array 
= [1010.12true'10USD''USD'];
function 
getValue($x$y)
{
   
$typeMap = [
       
'integer' => 'int',
       
'int' => 'int',
       
'float' => 'float',
       
'double' => 'float',
       
'real' => 'float',
       
'string' => 'string',
       
'boolean' => 'bool',
       
'bool' => 'bool',
    ];
   
$yType $typeMap[gettype($y)];
   
$call 'test($x)';
    if (
$yType == 'string')
       
$call 'test(\'$x\')';
   
$template = <<<TEMPLATE_FUNCTION
  if (!function_exists('test')) {function test($yType \$arg){}}
try{
 $call;
} catch (Error \$e) {
    return \$e->getMessage();
}
return 'Pass';
TEMPLATE_FUNCTION;
    return eval(
$template);
}

?>
<table border="1" width="50%">
    <thead>

    <tr><td></td><td align="center" colspan="<?= count($array?>"><strong>Input value</strong></td></tr>
    <tr>
        <td></td>
        <?php foreach ($array as $header): ?>
            <td><?= gettype($header), ' 'var_export($header?></td>
        <?php endforeach; ?>
    </tr>
    </thead>
    <tbody>
    <tr></tr>
    <?php foreach ($array as $y): ?>
        <tr>
            <td><?= gettype($y), ' 'var_export($y?></td>
            <?php foreach ($array as $x): ?>
                <td><?= getValue($x$y); ?></td>
            <?php endforeach; ?>
        </tr>
    <?php endforeach; ?>
    </tbody>
</table>
2018-04-05 13:46:26
http://php5.kiev.ua/manual/ru/language.oop5.typehinting.html
This matrix demonstrate passing different variable types with different type hints:
<?php

$array 
= [1010.12true'10USD''USD'];
function 
getValue($x$y)
{
   
$typeMap = [
       
'integer' => 'int',
       
'int' => 'int',
       
'float' => 'float',
       
'double' => 'float',
       
'real' => 'float',
       
'string' => 'string',
       
'boolean' => 'bool',
       
'bool' => 'bool',
    ];
   
$yType $typeMap[gettype($y)];
   
$xType $typeMap[gettype($x)];

    if (
$xType == 'string')
       
$x "'$x'";
    elseif (
$xType == 'bool')
       
$x $x 'true' 'false';
    try {
        eval(
"if (!function_exists('test$yType')) {function test$yType($yType \$arg){}} test$yType($x);");
    } catch (
Error $e) {
        return 
get_class($e).': '.$e->getMessage();
    }
    return 
'Pass';
}

?>
<table border="1" width="50%">
    <thead>

    <tr>
        <td></td>
        <td align="center" colspan="<?= count($array?>"><strong>Input value</strong></td>
    </tr>
    <tr>
        <td></td>
        <?php foreach ($array as $header): ?>
            <td><?= gettype($header), ' 'var_export($header?></td>
        <?php endforeach; ?>
    </tr>
    </thead>
    <tbody>
    <tr></tr>
    <?php foreach ($array as $y): ?>
        <tr>
            <td><?= gettype($y), ' 'var_export($y?></td>
            <?php foreach ($array as $x): ?>
                <td><?= getValue($x$y); ?></td>
            <?php endforeach; ?>
        </tr>
    <?php endforeach; ?>
    </tbody>
</table>
2018-04-05 15:33:58
http://php5.kiev.ua/manual/ru/language.oop5.typehinting.html
Sometimes you want to work with classes (not their instances), but Typehinting is only permitted for an object (instance).

Here is sample code that tests whether the parameter is a valid type (the class/subclass/instance of an expected class):

<?php
declare(strict_types=1);

class 
MyClass {};

function 
myfunc$class ) {
   
// Validate classname.
   
if ( $class instanceof MyClass ) {
       
$class get_class$class );
    } elseif ( !
class_exists$class ) or !is_a$classMyClass::class, true ) ) {
        throw new 
TypeError"$class is not of type " MyClass::class );   
    }
   
// Proceed with processing of MyClass subclass ...
}
 
$myclass = new MyClass;
myfunc$myclass );                // valid
myfuncMyClass::class );        // valid
myfuncString::class );          // not of type MyClass.
2019-07-16 02:20:24
http://php5.kiev.ua/manual/ru/language.oop5.typehinting.html

    Поддержать сайт на родительском проекте КГБ