property_exists
Checks whether a named property is declared on a class or set on an object, even when its value is null.
property_exists(object|string $object_or_class, string $property): bool- Added in PHP 5.1.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$object_or_class | object|string | An object instance or a class name to inspect. |
$property | string | The property name, without the leading dollar sign. Matching is case-sensitive. |
Return value
booltrue if the property exists, false if it does not or if the class does not exist.
Errors
Throws a TypeError if the first argument is neither an object nor a string; an unknown class name returns false.
The reason property_exists() exists at all is that isset() lies about null. A declared property holding null makes isset($obj->name) return false, which is indistinguishable from the property not being there. property_exists() separates the two: it reports declaration, not value.
It sees public, protected, private and static properties, and it accepts either an instance or a class name. Dynamic properties assigned at runtime on an object also count — but only while they are set, since unset() removes a dynamic property entirely while a declared one keeps existing.
When to reach for it
- Hydrating an object from decoded JSON where a field may legitimately be
null. - Validating that a sort or filter column named in a request maps to a real property before you use it.
- Writing a generic mapper or DTO builder that must skip keys with no matching property.
What trips people up
It is case-sensitive, unlike method_exists() — PHP property names are case-sensitive, so 'Name' and 'name' are different properties.
It also cannot see anything served by __get(). Magic property bags and lazy-loading models will report false for names that read perfectly well at runtime; check __isset() or the object's own API there.
Example
When to use it
- Hydrating a DTO from json_decode output where a nullable field must be distinguished from an absent one.
- Whitelisting a sort column supplied by a query string against the model's real properties.
- Skipping unknown keys in a generic array-to-object mapper instead of creating dynamic properties.
Frequently asked questions
What is the difference between property_exists and isset?
property_exists() reports whether the property is declared; isset() reports whether it holds a non-null value. A property set to null gives true from property_exists() and false from isset().
Does property_exists work with __get?
No. Properties served only by the __get() magic method are not declared, so it returns false. There is no reliable built-in check for them beyond calling __isset().
Does property_exists see private properties?
Yes. It returns true for private, protected, public and static properties regardless of the calling scope, so it is not a visibility check.
More examples
Hydrating an object from JSON with null fields
The null email is assigned because property_exists() reports the property even with a null value, while "bogus" is skipped instead of becoming a dynamic property. An isset()-based guard would have skipped email too.
<?php
class User { public ?string $email = null; public int $age = 0; }
$data = json_decode('{"email":null,"age":31,"bogus":9}', true);
$u = new User();
foreach ($data as $key => $value) {
if (property_exists($u, $key)) {
$u->$key = $value;
} else {
echo "skipped unknown key: $key\n";
}
}
var_dump($u);Declared versus dynamic properties after unset
After unset() the declared property still exists as a declaration (true) while the dynamic one is gone for good (false). get_object_vars() shows an empty array because neither currently holds a value.
<?php
class Bag { public string $declared = 'x'; }
$b = new Bag();
$b->dynamic = 'y';
var_dump(property_exists($b, 'declared'), property_exists($b, 'dynamic'));
unset($b->declared, $b->dynamic);
var_dump(property_exists($b, 'declared'), property_exists($b, 'dynamic'));
print_r(get_object_vars($b));
Discussion