How to Check for a Specific Type of Object in PHP
Asked Answered
F

4

71

I have a method which accepts a PDO object as an argument, to allow the user to use an existing connection rather then the method to open a new one, and save resources:

public static function databaseConnect($pdo = null) {

I am aware of is_object() to check if the argument is an object, but I want to check if $pdo is a PDO object, and not just an object.

Because the user can easily enter (by mistake?) a different kind of object, a mysqli or such, and the entire script will break apart.

In short: How can I check a variable for a specific type of object?

Fluttery answered 11/11, 2011 at 7:57 Comment(0)
R
98

You can use instanceof:

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

Be aware though, you can't negate like !instanceof, so you'd instead do:

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

Also, looking over your question, you can use object type-hinting, which helps enforce requirements, as well as simplify your check logic:

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

Typed arguments can be required or optional:

// 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) { }

Untyped arguments allow for flexibility through explicit conditions:

// 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')) {
        // ...
    }
}

As for the latter (using method_exists), I'm a bit mixed in my opinion. People coming from Ruby would find it familiar to respond_to?, for better or for worse. I'd personally write an interface and perform a normal type-hint against that:

interface QueryableInterface
{ 
    function query();
}

class MyPDO extends PDO implements QueryableInterface { }

function connect(QueryableInterface $queryable) { }

However, that's not always feasible; in this example, PDO objects are not valid parameters as the base type doesn't implement QueryableInterface.

It's also worth mentioning that values have types, not variables, in PHP. This is important because null will fail an instanceof check.

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

The value loses it's type when it becomes null, a lack of type.

Recalcitrant answered 11/11, 2011 at 8:0 Comment(12)
I thought type-hinting was not allowed in PHPCyrilla
It sure is, but only for Array and object class types. So PDO and any user-land classes are good, as in my example. You can't hint scalars though, such as string.Recalcitrant
I see, though I don't want a fatal error, I want an exception thrown. Good to know though :)Cyrilla
@Truth - I think its a catchable fatal error, you'd have to check. If you error handle into ErrorException I think it'll get converted.Recalcitrant
The only drawback of type hinting in PHP is that it does not throw Exceptions. But you can register an custom error handler to turn all PHP errors into exceptions.Laparotomy
@Truth - Yea, catchable fatal; use the error handler to push it into an ErrorExceptionRecalcitrant
Sadly this is a no-no for modulation. It might interfere with the rest of the script (assuming this class was pasted directly on it).Cyrilla
@Truth - Well if you don't feel that type hinting will benefit, use the instanceof operator. A note, you can also use the method_exists() function; this is advantageous if you want to pass in multiple types that may not implement the same interface, but inevitably have the same method(s). That way any object with, for example, a "render()" method can be passed in (though again, I'd defer to type hinting with an interface)Recalcitrant
"Be aware though, you can't negate like !instanceof" - I use !instanceof with no problem, and cannot find anything in the docs that backup the above assumption.Engenia
@Engenia The lexer/parser for PHP has improved significantly since this post (PHP 5.3) so perhaps it has added support for directly negating the operator.Recalcitrant
@Engenia Actually, it just plain doesn't work on any version; you cannot use the !instanceof syntax, as per 3v4l.org/Tf1TlRecalcitrant
@Dan, OK. In 5.5.9 localhost it works, at least. Have not uploaded to server yet, which I think uses 7. What the heck, adding the extra paranthesis does not make any difference anyway :)Engenia
L
11

use

 bool is_a ( object $object , string $class_name )

This will work for child classes too.

see http://php.net/is-a

EDIT: Or you could use type hinting:

public static function databaseConnect(PDO $pdo = null) {...
Laparotomy answered 11/11, 2011 at 8:2 Comment(3)
What's the difference between this and instanceof (note that I can probably find it within seconds in google, I'm asking deliberately so that future users see it)Cyrilla
is_a takes a string as second argument so you must not now the class you check for at design time. I use this to create ojects from xml. My Unserializer parses the xml looks for a ndoe like <class>my\ns\user</class> and then creates an new instance of my\ns\user and injects all propertys and dependencies. if you want the object loader to return an certain type of object you can set a property $desiredClass and then use is_a($createdObject, $this->desiredClass)Laparotomy
@MadaraUchiha - #3018184 may be usefulMacrospore
S
3

As pointed out in other answers, instanceof, get_class, and is_a are probably what you're looking for.

However, rather than coding in a lot of guards that test for type, some developers find it more productive (and easier to read) to just let the runtime handle the enforcement, particularly when you're talking about calls other programmers will be making (when a programmer makes a mistake, app-breaking loud complaints are arguably a good thing).

If you really need to not have the script fall apart when a programmer uses your method incorrectly, wrap the relevant section of code (in this case, probably the inside of databaseConnect) in a try block, maybe use set_error_handler to throw exceptions on script errors, and then set up one or more catch blocks which indicated what to do when an exception condition happens.

Schulz answered 11/11, 2011 at 8:18 Comment(0)
G
2

I think you can use instanceof something like:

if ($pdo instanceof YOUR_PDO_OBJECT_INSTANCE) {
   // it is...
}
Godhead answered 11/11, 2011 at 8:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.