Why Your Code is Crashing: The "Uninitialized" State
PHP 7.4 brought us typed properties, but PHP 8 really turned up the heat on how we handle them. Standard untyped properties always default to null. Typed properties, however, exist in a "zombie" state called uninitialized until you explicitly give them a value. If you try to read one before that happens, PHP won't just give you a warning—it will stop your script cold with a fatal error.
Fatal error: Uncaught Error: Typed property Foo::$bar must not be accessed before initialization
This usually bites developers during PHP 8 migrations or when they assume a nullable property (like ?string) defaults to null automatically. It doesn't. You have to be intentional about your data.
Example of the failing code
<?php
class UserProfile {
public string $username;
public int $age;
}
$profile = new UserProfile();
echo $profile->username; // ❌ Boom: Fatal Error triggers here
?>
How to Fix the Error
You can tackle this in a few different ways, depending on how your class is structured.
1. The Cleanest Fix: Constructor Property Promotion
Modern PHP makes it easy to define and initialize properties in one go. Since PHP 8.0, you can use constructor property promotion to ensure every object starts its life with valid data. It’s concise and eliminates boilerplate.
<?php
class UserProfile {
// Define and assign in one line
public function __construct(
public string $username = "Guest",
public int $age = 0
) {}
}
$profile = new UserProfile();
echo $profile->username; // Works: outputs "Guest"
?>
2. The Fast Path: Inline Default Values
If you don't need to pass values through the constructor, simply assign a default directly in the class definition. This immediately moves the property out of that dangerous "uninitialized" state.
<?php
class AppConfig {
public string $theme = 'light';
public bool $enabled = false;
}
$config = new AppConfig();
echo $config->theme; // Works: outputs "light"
?>
3. The Nullable Gotcha
Adding a question mark to a type (like ?string) makes it nullable, but it still won't default to null on its own. You must explicitly set it. This is a common pitfall when refactoring legacy 7.x code.
<?php
class Document {
// ❌ Still crashes if accessed immediately
// public ?string $title;
// ✅ Safe: Initialized with null
public ?string $title = null;
}
$doc = new Document();
var_dump($doc->title); // Works: outputs NULL
?>
4. The "Just-in-Time" Check with isset()
Sometimes you can't provide a value right away—maybe it needs to be fetched from a database later. In these cases, use isset(). In PHP, isset() returns false for uninitialized properties, allowing you to load data only when needed.
<?php
class LazyLoader {
public string $data;
public function getData(): string {
if (!isset($this->data)) {
$this->data = $this->fetchFromApi(); // Specific example of lazy loading
}
return $this->data;
}
private function fetchFromApi(): string {
return "Critical Data Loaded";
}
}
?>
Testing the Solution
Run a quick check in your terminal to make sure the crash is gone. If the script finishes without an Uncaught Error, you've successfully managed the property state.
# Run your script
php your_script.php
# Success looks like this:
string(5) "Guest"
Pro Tips for Better PHP Code
- **Be Strict with Nulls:** Only make a property nullable if `null` actually means something in your business logic. If a user *must* have a name, force it through the constructor.
- **Automate Your Checks:** Use PHPStan at level 6 or higher. It will scan your entire project and point out exactly where you’ve forgotten to initialize a property before you even hit save.
- **Watch Your Mappers:** If you use an ORM like Doctrine, ensure your database schema matches your class types. An empty database column trying to fill a non-nullable property will trigger this same error.

