Можно ли указать более одной подсказки типа для параметра?

Есть ли способ добавить в метод более одного намека на тип? Например, foo(param) должен получить экземпляр строки ИЛИ bar ИЛИ baz.


person joao    schedule 01.10.2010    source источник
comment
Подсказка типа не поддерживает строковые скаляры; просто указал на это.   -  person Qix - MONICA WAS MISTREATED    schedule 14.10.2014
comment
@Qix Теперь мы должны упомянуть, что PHP поддерживает скалярный тип подсказка начиная с PHP 7.0.0.   -  person faintsignal    schedule 29.08.2017


Ответы (5)


Это невозможно применить (кроме как внутри метода). Вы можете указать только один тип подсказки и только для объектов/интерфейсов и массивов (начиная с PHP 5.1).

Однако вы можете/должны документировать это в своем методе, то есть:

/**
 * @param string|Bar|Baz $param1
 */
function foo($param1);
person PatrikAkerstrand    schedule 01.10.2010

Это одно из применений интерфейсов. Если вы хотите быть уверены, что у объекта есть метод ->foobar($baz), вы можете ожидать интерфейс:

interface iFooBar {
    public function foobar($baz);
}

class Foo implements iFooBar {
    public function foobar($baz) { echo $baz; }
}
class Bar implements iFooBar {
    public function foobar($baz) { print_r($baz); }
}

function doSomething(iFooBar $foo) {
    $foo->foobar('something');
}

Затем при вызове они будут работать:

doSomething(new Foo());
doSomething(new Bar());

Это не будет:

doSomething(new StdClass());
doSomething('testing');
person ircmaxell    schedule 01.10.2010
comment
Довольно круто! Также существуют типы SPL (экспериментальные): pl.php.net/manual /en/book.spl-types.php - person joao; 01.10.2010
comment
@joao: SPLTypes не будет работать на 5.3 (они используют устаревшие вызовы c). Кроме того, последнее обновление было в 2008 году, так что можно с уверенностью сказать, что оно не актуально... - person ircmaxell; 01.10.2010

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

Однако у меня есть в основном неполное предложение для типов объединений. Он нацелен на 7.NEXT (на момент написания этой статьи это 7.1) или 8 (в зависимости от того, что наступит раньше).

Вот простой пример того, что, как мне кажется, будет очень ценным: array | Traversable:

function map(callable $fn, array|Traversable $input) {
    foreach ($input as $key => $value) {
        yield $key => $fn($value);
    }
}

К сожалению, RFC не прошел; однако для конкретного типа array|Traversable теперь есть тип iterable, который является именно таким.

person Levi Morrison    schedule 12.05.2015
comment
Ваше предложение отклонено. Есть ли какие-либо планы на будущее для PHP8? - person Dharman; 25.02.2019
comment
Не то, что я знаю о. Я думаю, что это стоит того, но лично я буду работать над другими функциями. - person Levi Morrison; 25.02.2019

Подсказка типа допускает только одну подсказку для каждого параметра (а также подсказку должно быть array или имя класса, вы не можете указать string), но вы можете сделать это, проверив тип параметра в вашей функции, используя get_class:

function foo($param)
{
  if (!(is_string($param) || in_array(get_class($param), array("Bar", "Baz")))
  {
    // invalid type for $param!
  }
}

Вы даже можете использовать trigger_error, чтобы он не работал с ошибкой PHP. (как если бы подсказка типа не сработала), если хотите.

person Daniel Vandersluis    schedule 01.10.2010
comment
Я бы предложил выбросить InvalidArgumentException, чтобы вы могли хотя бы попытаться восстановиться после ошибки... - person ircmaxell; 01.10.2010
comment
@ircmaxell Я просто демонстрировал, как OP может воспроизвести работу подсказок типов. - person Daniel Vandersluis; 01.10.2010
comment
Я не говорю не trigger_error. Все зависит от вашего стиля (и нет ничего плохого в использовании trigger_error). Я предпочитаю исключения, поэтому использую InvalidArgumentException. Если ваша цель - имитировать подсказки типа, то во что бы то ни стало вызовите фатальную ошибку... - person ircmaxell; 01.10.2010
comment
@ircmaxell Согласен; Лично я бы тоже не стал использовать trigger_error для этого :) - person Daniel Vandersluis; 01.10.2010
comment
Обновление: поскольку строка PHP 7 (и bool, int, float) является допустимой подсказкой типа. - person chris342423; 19.09.2016
comment
Обновление: в PHP 7.1 добавлены типы подсказок, допускающие значение null, а в PHP 7.2 добавлены тип подсказки объекта. - person faintsignal; 24.01.2018

Фантастический вопрос. Это относится как к документации IDE, так и к подсказкам типов PHP 5. Вы должны помнить, что в ОО полиморфизм — ваш друг.

Если вы создадите базовый класс и расширите их, ваша подсказка типа будет базовым классом... все расширенные классы будут работать. См. пример ниже.

//
$test = new DUITest();

//  Calls below will work because of polymorphism
echo $test->test(new Molecule()) . '<br/>';
echo $test->test(new Vodka()) . '<br/>';
echo $test->test(new Driver()) . '<br/>';
echo $test->test(new Car()) . '<br/>';

//  Will not work because different data type
echo $test->test(new Pig()) . '<br/>';
echo $test->test(new Cop()) . '<br/>';
echo $test->test('test') . '<br/>';
echo $test->test(array()) . '<br/>';



/**
 * Class to test 
 */
class DUITest {

    public function __construct() {
        ;
    }

    /**
     * Using type-hinting
     * 
     * See below link for more information
     * @link http://www.php.net/manual/en/language.oop5.typehinting.php
     * 
     * @param Molecule|Car|Driver|Vodka $obj 
     */
    public function test(Molecule $obj) {
        echo $obj;
    }

}

/**
 * Base Class
 */
class Molecule {

    public function __construct() {}

    /**
     * Outputs name of class of current object
     * @return <type> 
     */
    public function __toString() {
        return get_class($this);
    }

}

class Car extends Molecule {}

class Driver extends Molecule {}

class Vodka extends Molecule {}

class Pig {}
class Cop extends Pig{}
person Alex    schedule 01.10.2010
comment
круто, а как насчет нуля? Обычной практикой является возврат чего-либо или null - person Victor Bredihin; 07.01.2019