Why I Am Suddenly Getting a "Typed Property Must Not Be Accessed Before Initialization" Error When Introducing Properties Type Hints

Why I am suddenly getting a Typed property must not be accessed before initialization error when introducing properties type hints?

Since PHP 7.4 introduces type-hinting for properties, it is particularly important to provide valid values for all properties, so that all properties have values that match their declared types.

A property that has never been assigned doesn't have a null value, but it is on an undefined state, which will never match any declared type. undefined !== null.

For the code above, if you did:

$f = new Foo(1);
$f->getVal();

You would get:

Fatal error: Uncaught Error: Typed property Foo::$val must not be accessed before initialization

Since $val is neither string nor nullwhen accessing it.

The way to get around this is to assign values to all your properties that match the declared types. You can do this either as default values for the property or during construction, depending on your preference and the type of the property.

For example, for the above one could do:

class Foo {

private int $id;
private ?string $val = null; // <-- declaring default null value for the property
private Collection $collection;
private DateTimeInterface $createdAt;
private ?DateTimeInterface $updatedAt;

public function __construct(int $id) {
// and on the constructor we set the default values for all the other
// properties, so now the instance is on a valid state
$this->id = $id;
$this->createdAt = new DateTimeImmutable();
$this->updatedAt = new DateTimeImmutable();

$this->collection = new ArrayCollection();
}

Now all properties would have a valid value and the the instance would be on a valid state.

This can hit particularly often when you are relying on values that come from the DB for entity values. E.g. auto-generated IDs, or creation and/or updated values; which often are left as a DB concern.

For auto-generated IDs, the recommended way forward is to change the type declaration to:

private ?int $id = null

For all the rest, just choose an appropriate value for the property's type.

Typed property must not be accessed before initialization error in PHP 7.4+

I think there's a misunderstanding here. Uninitialized typed properties have no state, which means they have no initial NULL. If you want a property to be NULL you have to specify it explicitly.

private ?string $name = NULL;

So your attempt to avoid this exception without setting these properties is just not right and makes no sense!. The purpose of typed properties is to avoid implicit initialization and always provide an explicit value that is clear and makes sense. And please don't just define all properties as nullable to make this exception disappear!! as this will defeat the whole point of typed properties and PHP 7.4 .

Fatal error: Uncaught Error: Typed property must not be accessed before initialization

To access method getList() from class ProductRepository you need to create an object. Do this in the constructor of class products.

<?php
class products extends PageController
{
private ProductRepository $ProductRepository;

public function __construct()
{
$this->ProductRepository = new ProductRepository(); // you now have access to the public methods of ProductRepository
}

public function action(): void
{
$this->smarty->assign('headline', 'PRODUCTS');
$this->smarty->assign('info', 'Product Overview');
$this->smarty->assign('name', 'Every Product!');
$this->smarty->assign('LIST', $this->ProductRepository->getList());

try {
$this->smarty->display('products.tpl');
} catch (\SmartyException $e) {
} catch (\Exception $e) {
}
}
}

Finding out the type from an uninitialized typed property

I'm not sure what exactly you are attempting to do, and without knowing more about the whole thing I won't risk an opinion about the logic of it.

Putting that aside, using gettype() on an uninitialized typed property won't work, because the property has effectively no value at that point.

But, since the property is typed, you could get the property defined type via reflection:

class User {

public int $id = 0;
public string $code;
public string $name;

public function __construct() {

$reflect = new ReflectionClass($this);
$props = $reflect->getProperties();
foreach ($props as $prop) {
echo $prop->getType()->getName(), "\n";
}

}
}

new User();

The above would output:

int
string
string

Doing this on the fly seems like a lot of overhead, so please make sure that there is no better tool at your disposal. Doing this kind of thing on the __construct() method does not look very wise at first sight, but of course I do not know your specific constraints.

Getting error Typed property must not be accessed before initialization - Laravel Livewire

The other answers here both have some minor things to note about them. You don't have to check $invitation, because the typehinting Invitation makes Laravel use Model-Route-Binding, which fetches the corresponding record - or throws a HTTP 404 status code if not found.

Secondly, and this is the actual error you are currently seeing yourself, is that you don't have to do anything to the $this->invitation, since its not set. You should instead pass a parameter to the method.

When looping data in Livewire, it is always recommended to use wire:key, so that Livewire can keep track of each record in the loop.

So for the actual delete method, just call the delete method on the input-variable.

public function deleteInvitation(Invitation $invitation)
{
$invitation->delete();
// Emit an event to notify the user that the record was deleted
// Refresh the parent component to remove the invitation from the list

}

For your blade, add wire:key to the first element in the loop and pass the ID to the method.

(so wire:click="deleteInvitation({{ $invitation->id }})" instead of wire:click="deleteInvitation").

@forelse($invitations as $key => $invitation)
<li class="flex flex-row" wire:key="invitation_{{ $invitation->id }}">
<div class="flex flex-1 items-center px-8 py-4">
<div class="flex-1 mr-16">
<div class="text-sm dark:text-white">
{{ $invitation->email }}
</div>
</div>
<button wire:click="deleteInvitation({{ $invitation->id }})" class="text-right flex justify-end">
<x-icon.trash />
</button>
</div>
</li>
@empty
@endforelse

This in turn means that, since its never used, you can remove the declaration of the $invitation property of that class, the line just after public $showInvitationManagementModal = false;.

public Invitation $invitation;

Php, typed property and lazy loading? How can I check if it's initialized?

you can probably do

isset($this->test)

Can phpstan detect Typed property ... must not be accessed before initialization Errors?

It looks like the ability to scan for uninitialized property values was added in July of 2020

However, this feature is disabled by default. You'll need to be using a configuration file that sets the checkUninitializedProperties value

% cat phpstan.neon 
parameters:
checkUninitializedProperties: true

and then tell `phpstan` to use this configuration file.

% ./vendor/bin/phpstan analyse --level=8 --configuration=./phpstan.neon /tmp/test.php
1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

------ ------------------------------------------------------------------------------------------------------
Line /tmp/test.php
------ ------------------------------------------------------------------------------------------------------
6 Class Zip has an uninitialized property $zap. Give it default value or assign it in the constructor.
------ ------------------------------------------------------------------------------------------------------


[ERROR] Found 1 error

Also, at the risk of saying the obvious part loud, this check assumes a particular style of programming. For example, the following valid PHP program

<?php
class Zap {
}

class Zip {
public Zap $zap;
}
$object = new Zip;
$object->zap = new Zap;
var_dump(
$object->zap
);

Still fails the checkUninitializedProperties check.



Related Topics



Leave a reply



Submit