Extbase entities and lazy loaded typed properties
Tags: typo3, extbase, lazyloading, php74
Published: 2022-09-18 16:33:59

---
title: Extbase entities and lazy loaded typed properties
published: true
description: A shiort article that explains how to handle property types of lazy loaded extbase entity properties with php 7.4+
tags: typo3, extbase, lazyloading, #php74
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
---

Hi folks,

today I want to write about property types for lazy loaded entity properties in extbase:

```php
use TYPO3\CMS\Extbase\Annotation as Extbase;

class Foo
{
    /**
     * @Extbase\ORM\Lazy
     * @var Bar
     */
    private $bar;
}
```

That's how things looked until PHP 7.3, but how do we deal with it with it with PHP 7.4+ (property types) and PHP 8.0+ (union types)?

Well, let's start with 7.4 and also take `\TYPO3\CMS\Extbase\Persistence\Generic\LazyObjectStorage` into account.

Something we cannot do with PHP 7.4 is to use union types. The following example look neat but is impossible to implement.

```php
use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;

class Foo
{
    /**
     * @Extbase\ORM\Lazy
     */
    private Bar|LazyLoadingProxy|null $bar = null;
}
```

But missing union types in PHP 7.4 aren't the only limitation we are facing here. There is a limitation/bug in extbase's reflection framework which lets extbase only detect a single type even in phpdoc blocks. So annotating `@var Bar|LazyLoadingProxy` already leads to an error as extbase does skip the whole property processing.

So the last example isn't working due to a missing language feature (in PHP 7.4), but the following example isn't working because of a limitation/bug in extbase:

```php
use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;

class Foo
{
    /**
     * @Extbase\ORM\Lazy
     * @var Bar|LazyLoadingProxy
     */
    private $bar;
}
```

Given the circumstances, what's the best approach here to be as strict and meaningful as possible? Here's my current best practice:


```php
use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;

class Foo
{
    /**
     * @Extbase\ORM\Lazy
     * @var Bar|null
     * @phpstan-var Bar|LazyLoadingProxy|null
     */
    private ?object $bar = null;
}
```

As phpdoc take precedence in extbase, the `?object` property type is not relevant for extbase, but it already narrows down the allowed types. Depending on your IDE and it's support for custom annotations for static code analysis tools like phpstan, it will or will not know that the property can be a `LazyLoadingProxy`, but it will at least know the type is `null` or `Bar`. PHPStan, in this case, will know the whole truth and to me that's more important than full IDE support.

Speaking if `LazyLoadingProxy`...
`\TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy::_loadRealInstance()` is our public api to resolve the actual value of the proxy but unfortunately, the return type annotation of that method is just wrong. It says `object` which is neither specific nor correct. The actual return type is hard to tell because it is actually dynamic, in theory at least. It's very unlikely that the internal logic may fail but from a static code analysis standpoint, it's possible. If the actual value of the proxy cannot be resolved, `_loadRealInstance()` returns the actual value of the property, which in most cases is `null`. This means, the following code can break:

```php
use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;

class Foo
{
    /**
     * @Extbase\ORM\Lazy
     * @var Bar|null
     * @phpstan-var Bar|LazyLoadingProxy|null
     */
    private ?object $bar = null;

    public function getBar(): ?Bar
    {
        return $this->bar instanceof LazyLoadingProxy
            ? $this->bar->_loadRealInstance()
            : $this->bar;
    }
}
```

It's really just a theoretical case but my best practice for those getters looks like this:


```php
use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;

class Foo
{
    /**
     * @Extbase\ORM\Lazy
     * @var Bar|null
     * @phpstan-var Bar|LazyLoadingProxy|null
     */
    private ?object $bar = null;

    public function getBar(): ?Bar
    {
        if ($this->bar instanceof LazyLoadingProxy) {
            /** @var Bar|null $resolvedValue */
            $resolvedValue = $this->bar->_loadRealInstance();
            return $this->bar = $resolvedValue instanceof Bar
                ? $resolvedValue
                : null;
        }

        return $this->bar;
    }
}
```


## What about LazyObjectStorage?

Right, there is `LazyObjectStorage` as well. How to deal with that? Well, this is a lot easier because `LazyObjectStorage` is a sub class of `ObjectStorage` which enables us to write this code:

```php
use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;

class Foo
{
    /**
     * @Extbase\ORM\Lazy
     */
    private ObjectStorage $bar;

    public function __construct() {
        $this->initializeObject();
    }

    public function initializeObject() {
        $this->bar = new ObjectStorage();
    }

    public function getBar(): ObjectStorage
    {
        return $this->bar;
    }
}
```

We completely omit the `LazyObjectStorage` type here because we really don't need it. There is no benefit for PHPStan and the like to know, neither for your IDE or you personally because the api of `LazyObjectStorage` is the same as for `ObjectStorage` and the underlying code can work the same both. Easy, right?

That's it for today.
Have a nice one!