Types7 min readExamples run on PHP 8.5.10

is_object()

Learn how PHP 8.4 is_object() tests for objects, which values count as objects, and the mistakes to avoid with class-name strings and arrays.

Signature

is_object(mixed $value): bool

Purpose

is_object() answers one question: does this value hold an object? It returns a boolean and never changes its argument. Use it when a value may come from loosely typed input, such as decoded JSON, a mixed-type array, or a legacy API, and your next step depends on it being an object.

Signature

Parameters and defaults

  • $value is the only parameter. It accepts any PHP value and has no default. You must pass exactly one argument.

Because the parameter is mixed, the function does not throw a TypeError for unexpected types. Passing null, an integer, an array or a string simply gives false.

Return value and failure cases

The function returns true when the value is an object and false for every other type. It has no error return, emits no warnings, and does not check what class the object belongs to. An instance of a built-in class, a user class, a closure, an enum case or a generator all count as objects.

The manual's changelog notes one historical detail: since PHP 7.2.0, an unserialized object whose class definition is missing (class __PHP_Incomplete_Class) gives true. Before that it gave false.

Example 1: Comparing common types

This loop checks several kinds of value. Only the stdClass instance and the arrow function are objects, because an arrow function is a Closure instance.

Example
<?php
$values = [
    'null' => null,
    'int' => 42,
    'array' => ['a' => 1],
    'string' => 'hello',
    'stdClass' => new stdClass(),
    'closure' => fn(int $x): int => $x + 1,
];
$result = [];
foreach ($values as $label => $value) {
    $result[$label] = is_object($value);
}
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
{"null":false,"int":false,"array":false,"string":false,"stdClass":true,"closure":true}

Example 2: Guarding property access

A common use is a guard before reading a property. The helper below returns -1 when it receives something other than an object, and otherwise counts the students on it.

Example
<?php
function studentCount(mixed $value): int
{
    if (!is_object($value)) {
        return -1;
    }
    return count($value->students ?? []);
}

$team = new stdClass();
$team->students = ['Kalle', 'Ross', 'Felipe'];

echo json_encode([studentCount(null), studentCount(['students' => [1]]), studentCount($team)]), PHP_EOL;
Output
[-1,-1,3]

Note that an array with a students key is rejected. Array keys and object properties are different things, even though they look similar in data.

Example 3: Edge cases

Some results surprise newcomers. Enum cases and generators are objects. JSON decoded without the associative flag yields an object, while the flag yields an array. A string that happens to be a class name is only a string. Casting changes the type, so (array) removes objectness and (object) adds it.

Example
<?php
enum Suit { case Hearts; }
function gen() { yield 1; }

$decoded = json_decode('{"name":"Ada"}');
$assoc = json_decode('{"name":"Ada"}', true);
$incomplete = unserialize('O:7:"Missing":0:{}');

$checks = [
    'enum case' => is_object(Suit::Hearts),
    'generator' => is_object(gen()),
    'json object' => is_object($decoded),
    'json assoc' => is_object($assoc),
    'class name string' => is_object('stdClass'),
    'cast array' => is_object((array) $decoded),
    'cast object' => is_object((object) ['a' => 1]),
    'incomplete' => is_object($incomplete),
];
echo json_encode($checks), PHP_EOL;
Output
{"enum case":true,"generator":true,"json object":true,"json assoc":false,"class name string":false,"cast array":false,"cast object":true,"incomplete":true}

The last entry shows the incomplete-class case: the class Missing is not defined, yet the value is still an object.

Edge cases to remember

  • is_object() says nothing about the class. If you need a specific type, use instanceof, which also covers parent classes and interfaces.
  • Closures, enum cases, generators and DateTime instances all return true.
  • A class name stored in a string returns false. Use class_exists() to ask about a class name, not an instance.
  • Objects that implement ArrayAccess or Traversable are still objects, not arrays, so is_array() returns false for them.

Common mistakes

  1. Treating a class name as an object. is_object('Foo') is false, because the value is a string.
  2. Assuming decoded JSON is always an object. The second argument of json_decode() controls whether you get arrays or objects. Check the shape you actually have.
  3. Using it instead of instanceof. A bare is_object() check does not prove that a method or property exists. It only proves some object is present.
  4. Forgetting that null is not an object. A missing optional dependency is null, so an is_object() check handles it, but a null property access without a check will raise a warning.
  5. Expecting casts to preserve type. After (array) $obj you have an array, so object checks fail.
  • is_array() tests for arrays.
  • is_string() tests for strings, which is what class names are.
  • is_bool(), is_int() and is_float() test the other scalar types.
  • get_debug_type() returns a readable type name when you need more than a yes or no answer.
  • instanceof checks a specific class or interface.

Recap

is_object(mixed $value): bool returns true for any object instance, including closures, enum cases, generators and incomplete classes, and false for everything else. It never throws, and it does not identify the class. Pair it with instanceof when you need a specific type, and remember that class-name strings and arrays are not objects.

Tested examples

Comparing common types
<?php
$values = [
    'null' => null,
    'int' => 42,
    'array' => ['a' => 1],
    'string' => 'hello',
    'stdClass' => new stdClass(),
    'closure' => fn(int $x): int => $x + 1,
];
$result = [];
foreach ($values as $label => $value) {
    $result[$label] = is_object($value);
}
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
{"null":false,"int":false,"array":false,"string":false,"stdClass":true,"closure":true}
Guarding property access
<?php
function studentCount(mixed $value): int
{
    if (!is_object($value)) {
        return -1;
    }
    return count($value->students ?? []);
}

$team = new stdClass();
$team->students = ['Kalle', 'Ross', 'Felipe'];

echo json_encode([studentCount(null), studentCount(['students' => [1]]), studentCount($team)]), PHP_EOL;
Output
[-1,-1,3]
Edge cases
<?php
enum Suit { case Hearts; }
function gen() { yield 1; }

$decoded = json_decode('{"name":"Ada"}');
$assoc = json_decode('{"name":"Ada"}', true);
$incomplete = unserialize('O:7:"Missing":0:{}');

$checks = [
    'enum case' => is_object(Suit::Hearts),
    'generator' => is_object(gen()),
    'json object' => is_object($decoded),
    'json assoc' => is_object($assoc),
    'class name string' => is_object('stdClass'),
    'cast array' => is_object((array) $decoded),
    'cast object' => is_object((object) ['a' => 1]),
    'incomplete' => is_object($incomplete),
];
echo json_encode($checks), PHP_EOL;
Output
{"enum case":true,"generator":true,"json object":true,"json assoc":false,"class name string":false,"cast array":false,"cast object":true,"incomplete":true}