Как проверить определенный тип объекта в PHP

У меня есть метод, который принимает объект PDO в качестве аргумента, чтобы позволить пользователю использовать существующее соединение, а не метод для открытия нового и сохранения ресурсов:

public static function databaseConnect($pdo = null) {

Я знаю is_object(), чтобы проверить, является ли аргумент объектом, но я хочу проверить, является ли $pdo объектом PDO, а не просто объектом.

Потому что пользователь может легко ввести (по ошибке?) объект другого типа, mysqli или что-то подобное, и весь скрипт развалится.

Вкратце: как я могу проверить переменную для определенного типа объекта?


person Madara's Ghost    schedule 11.11.2011    source источник


Ответы (4)


Вы можете использовать instanceof:

if ($pdo instanceof PDO) {
    // it's PDO
}

Имейте в виду, однако, что вы не можете отрицать, как !instanceof, поэтому вместо этого вы должны сделать:

if (!($pdo instanceof PDO)) {
    // it's not PDO
}

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

function connect(PDO $pdo = null)
{
    if (null !== $pdo) {
        // it's PDO since it can only be
        // NULL or a PDO object (or a sub-type of PDO)
    }
}

connect(new SomeClass()); // fatal error, if SomeClass doesn't extend PDO

Типизированные аргументы могут быть обязательными или необязательными:

// required, only PDO (and sub-types) are valid
function connect(PDO $pdo) { }

// optional, only PDO (and sub-types) and 
// NULL (can be omitted) are valid
function connect(PDO $pdo = null) { }

Нетипизированные аргументы обеспечивают гибкость за счет явных условий:

// accepts any argument, checks for PDO in body
function connect($pdo)
{
    if ($pdo instanceof PDO) {
        // ...
    }
}

// accepts any argument, checks for non-PDO in body
function connect($pdo)
{
    if (!($pdo instanceof PDO)) {
        // ...
    }
}

// accepts any argument, checks for method existance
function connect($pdo)
{
    if (method_exists($pdo, 'query')) {
        // ...
    }
}

Что касается последнего (с использованием method_exists), я немного смешано на мой взгляд. Людям, пришедшим из Ruby, будет знаком respond_to?, поскольку лучше или хуже. Я бы лично написал интерфейс и выполнил бы обычную подсказку типа:

interface QueryableInterface
{ 
    function query();
}

class MyPDO extends PDO implements QueryableInterface { }

function connect(QueryableInterface $queryable) { }

Однако это не всегда возможно; в этом примере объекты PDO не являются допустимыми параметрами, поскольку базовый тип не реализует QueryableInterface.

Также стоит отметить, что в PHP значения имеют типы, а не переменные. Это важно, потому что null не пройдет проверку instanceof.

$object = new Object();
$object = null;
if ($object instanceof Object) {
    // never run because $object is simply null
}

Значение теряет свой тип, когда становится null, отсутствием типа.

person Dan Lugg    schedule 11.11.2011
comment
Я думал, что подсказки не разрешены в PHP - person Madara's Ghost; 11.11.2011
comment
Это точно, но только для Array и типов классов объектов. Так что PDO и любые пользовательские классы хороши, как в моем примере. Однако вы не можете намекать на скаляры, такие как string. - person Dan Lugg; 11.11.2011
comment
Я вижу, хотя я не хочу фатальной ошибки, я хочу, чтобы было сгенерировано исключение. Хотя полезно знать :) - person Madara's Ghost; 11.11.2011
comment
@ Правда - я думаю, что это уловимая фатальная ошибка, вам нужно проверить. Если вы обработаете ошибку в ErrorException, я думаю, она будет преобразована. - person Dan Lugg; 11.11.2011
comment
Единственным недостатком подсказки типов в PHP является то, что она не генерирует исключений. Но вы можете зарегистрировать собственный обработчик ошибок, чтобы превратить все ошибки PHP в исключения. - person Oliver A.; 11.11.2011
comment
@Truth - Да, уловимый фатальный; используйте обработчик ошибок, чтобы поместить его в ErrorException - person Dan Lugg; 11.11.2011
comment
К сожалению, это не для модуляции. Это может помешать остальной части сценария (при условии, что этот класс был вставлен непосредственно в него). - person Madara's Ghost; 11.11.2011
comment
@Truth - Что ж, если вы не чувствуете, что подсказка типа принесет пользу, используйте оператор instanceof. Примечание: вы также можете использовать функцию method_exists(); это выгодно, если вы хотите передать несколько типов, которые могут не реализовывать один и тот же интерфейс, но неизбежно иметь один и тот же метод (ы). Таким образом, любой объект, например, с методом render(), может быть передан (хотя опять же, я бы отложил подсказку типа с интерфейсом) - person Dan Lugg; 11.11.2011
comment
Имейте в виду, что вы не можете отрицать, как !instanceof - я использую !instanceof без проблем и не могу найти ничего в документах, подтверждающих приведенное выше предположение. - person davidkonrad; 11.08.2017
comment
@davidkonrad Лексер/парсер для PHP значительно улучшился со времени этой публикации (PHP 5.3), поэтому, возможно, в него добавлена ​​поддержка прямого отрицания оператора. - person Dan Lugg; 11.08.2017
comment
@davidkonrad На самом деле это просто не работает ни в одной версии; вы не можете использовать синтаксис !instanceof согласно 3v4l.org/Tf1Tl - person Dan Lugg; 11.08.2017
comment
@ Дэн, хорошо. По крайней мере, в 5.5.9 localhost работает. Еще не загрузил на сервер, который, я думаю, использует 7. Какого черта, добавление дополнительных скобок все равно не имеет никакого значения :) - person davidkonrad; 11.08.2017

использовать

 bool is_a ( object $object , string $class_name )

Это будет работать и для дочерних классов.

см. http://php.net/is-a.

РЕДАКТИРОВАТЬ: Или вы можете использовать подсказку типа:

public static function databaseConnect(PDO $pdo = null) {...
person Oliver A.    schedule 11.11.2011
comment
В чем разница между этим и instanceof (обратите внимание, что я, вероятно, могу найти это в течение нескольких секунд в Google, я спрашиваю намеренно, чтобы будущие пользователи видели это) - person Madara's Ghost; 11.11.2011
comment
is_a принимает строку в качестве второго аргумента, поэтому теперь вы не должны использовать класс, который вы проверяете во время разработки. Я использую это для создания объектов из xml. Мой Unserializer анализирует xml, ищет ndoe, например ‹class›my\ns\user‹/class›, а затем создает новый экземпляр my\ns\user и внедряет все свойства и зависимости. если вы хотите, чтобы загрузчик объектов возвращал объект определенного типа, вы можете установить свойство $desiredClass, а затем использовать is_a($createdObject, $this-›desiredClass) - person Oliver A.; 11.11.2011
comment
@MadaraUchiha - stackoverflow.com/questions/3017684/ может быть полезным - person coatesap; 27.11.2013

Как указано в других ответах, instanceof, get_class и is_a, вероятно, то, что вы ищете.

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

Если вам действительно нужно, чтобы скрипт не развалился, когда программист неправильно использует ваш метод, оберните соответствующий раздел кода (в данном случае, возможно, внутреннюю часть databaseConnect) в try block, возможно, используйте set_error_handler, чтобы генерировать исключения при ошибках сценария, а затем настроить один или несколько блоков перехвата, указывающих, что делать при возникновении условия исключения.

person Weston C    schedule 11.11.2011

Я думаю, вы можете использовать instanceof что-то вроде:

if ($pdo instanceof YOUR_PDO_OBJECT_INSTANCE) {
   // it is...
}
person Sarfraz    schedule 11.11.2011