Menu

Solving PHPStan error “Access to an undefined property”

March 31, 2023 · 8 min read

“Access to an undefined property Foo::$x” is one of the most common errors you will encounter when running PHPStan. Here are all the ways you can solve it.

Fix the name #

The easiest situation you can find yourself in is that there’s a typo in the source code, and the property access needs to be corrected to the right name:

 	public function getName(): string
 	{
-		return $this->nama;
+		return $this->name;
 	}

Narrow the type the property is accessed on #

If you encounter errors like:

Access to an undefined property object::$a. Cannot access property $a on mixed.

The problem isn’t the name of the property but the type we’re accessing the property on:

// $object is object
echo $object->a;

We can’t be sure the property exists on this type because it can be an object of any class. The solution is to narrow the type first and only then access the property.

Declare the property #

In older versions of PHP you weren’t required to declare the property, and the code might have actually run fine. But for obvious type safety reasons it’s better to declare the property if it’s missing:

 class HelloWorld
 {

+	private string $name;
+
 	public function setName(string $name): void
 	{
 		$this->name = $name;

PHP 8.2: Add #[AllowDynamicProperties] #

If the class is meant to have dynamic properties (when they vary from object to object), it needs to meet one of the following conditions:

  • Have __get and/or __set magic methods for handling dynamic properties
  • Be stdClass or extend stdClass
  • Have #[AllowDynamicProperties] class attribute

This is because dynamic properties have been deprecated in PHP 8.2+.

Addressing these requirements will not make the PHPStan error go away, but it will allow the following solutions to work if you use PHP 8.2 or later.

Universal object crates #

Classes without predefined structure are common in PHP applications. They are used as universal holders of data - any property can be set and read on them. Notable examples include stdClass, SimpleXMLElement (these are enabled by default), objects with results of database queries etc. Use universalObjectCratesClasses key to let PHPStan know which classes with these characteristics are used in your codebase by setting them in your configuration file:

parameters:
	universalObjectCratesClasses:
		- Dibi\Row
		- Ratchet\ConnectionInterface

See also object shape PHPDoc type for a better alternative that lets you describe types of properties of such objects.

Add @property PHPDoc #

If the class features magic properties but these properties are always the same, you can declare them with @property PHPDocs:

/**
 * @property int $foo
 * @property-read string $bar
 * @property-write \stdClass $baz
 */
class Foo { ... }

Making @property PHPDoc above interfaces work on PHP 8.2+ #

You might find that the following code is not sufficient to declare a property on an interface when you analyse code in PHP 8.2 and newer:

/**
 * @property string $bar
 */
interface Foo
{
}

function (Foo $foo): void {
    // Error: Access to an undefined property Foo::$bar.
    echo $foo->bar;
};

That’s because PHP 8.2 requires extra steps to allow dynamic properties. And there’s actually no way in the language to make these extra steps work on interfaces.

Fortunately there’s a PHPDoc feature that helps you get rid of this error. It’s @phpstan-require-extends. Class implementing an interface that uses this PHPDoc tag is required to extend a parent referenced in this tag. If this parent class allows dynamic properties, it makes this error go away. Additionally, it makes all properties and methods from this parent class also available when working with the interface.

/**
 * @property string $bar
 * @phpstan-require-extends Model
 */
interface Foo
{
}

class Model
{
    public function __get(string $name): mixed
    {
        // some magic logic for dynamic properties
    }
}

function (Foo $foo): void {
    // OK - No error
    echo $foo->bar;
};

Learn more about @phpstan-require-extends and @phpstan-require-implements »

Mixin #

When a class delegates unknown method calls and property accesses to a different class using __call and __get/__set, we can describe the relationship using @mixin PHPDoc tag:

class A
{
	public string $name = 'Class A';
}

/**
 * @mixin A
 */
class B
{
	public function __get(string $name): mixed
	{
		return (new A())->$name;
	}
}

$b = new B();
echo $b->name; // No error

If you use a popular framework like Laravel, where model properties are often not declared on class, you can use framework-specific extensions to make PHPStan understand your code properly.

Check out the extension library and search for PHPStan extensions on Packagist.

Write your own extension #

You can write your own class reflection extension to let PHPStan know which properties exist on your classes.

This solution works best if there’s custom logic in your __get() and __set() magic methods. For example if you can say “Property $name on class Foo exists if there’s a getter called getName()”, writing a custom class reflection extension to describe this lets you solve this PHPStan error in a nice and easy way.


Do you like PHPStan and use it every day? Consider supporting further development of PHPStan on GitHub Sponsors. I’d really appreciate it!

Theme
A
© 2016–2024 Ondřej Mirtes