Have you ever written dozens of manual getters and setters in a PHP class? Or had to initialize a heavy object on every request even when it wasn't needed? PHP 8.4 solves exactly these problems with three features that change how you write clean, performant code: Property Hooks, Asymmetric Visibility, and Lazy Objects.
We, at Meteora Web, have been working with PHP for years — on WordPress, Laravel, custom platforms. We know how much time is wasted writing boilerplate or manually optimizing lazy loading. PHP 8.4 bakes these capabilities directly into the language. Let's see how they work and, more importantly, when to use them.
Property Hooks: Goodbye Manual Getters and Setters
If you've ever worked with classes that expose public properties but require validation or transformation logic, you know the classic solution was to write get/set methods. With PHP 8.4 you can define hooks directly on properties, just like in C# or Kotlin.
Before: The Old Way
class User {
private string $name;
public function getName(): string {
return ucfirst($this->name);
}
public function setName(string $value): void {
if (strlen($value) < 2) {
throw new \InvalidArgumentException('Name too short');
}
$this->name = trim($value);
}
}
After: Property Hooks
class User {
public string $name {
set => trim($value);
get => ucfirst($this->name);
}
// Validation in the hook itself
public string $email {
set {
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('Invalid email');
}
$this->email = strtolower($value);
}
}
}
The code is more compact, and the logic stays attached to the property. No more scattered methods. One caveat: hooks cannot be used on static or untyped properties. Also watch out for inheritance — hooks can be overridden but with strict rules (the property must maintain the same type).
Action Item
Pick one of your most-used classes (e.g., a Laravel model or a DTO) and replace classic getters with Property Hooks. You'll write less code and make the logic more readable.
Asymmetric Visibility: Read-Public, Write-Private Properties
How many times have you wanted a property readable from the outside but writable only from inside the class? Previously you had to use getter methods and private setters. With PHP 8.4 you can declare two levels of visibility on the same property.
class Product {
public private(set) string $sku;
public function __construct(string $sku) {
$this->sku = $sku; // only allowed here
}
}
$product = new Product('ABC-123');
echo $product->sku; // OK
$product->sku = 'XYZ'; // Error: write access is private
You can use public protected(set) or protected private(set) for different levels. This feature makes design intentions explicit without resorting to defensive patterns.
When to Use It
- Immutable IDs after creation (e.g., entity UUIDs)
- Computed values that should be readable but not modifiable from outside
- Configuration properties you want to expose as read-only
Lazy Objects: Heavy Objects Only When Needed
Lazy loading isn't new in PHP, but before PHP 8.4 you had to implement it manually or use libraries like Proxy Manager. Now the language itself provides Lazy Objects via the \LazyGhostTrait or the \lazy_object function.
The Concrete Problem
Imagine an object that loads data from a database or an API at construction time. If you don't always need it, you're wasting resources. With Lazy Objects, the instance is created only on the first access to a property or method.
class HeavyService {
public function __construct() {
// Simulate heavy loading
file_get_contents('https://api.example.com/large-data');
}
public function doWork(): string {
return 'Done';
}
}
// Create a Lazy Ghost without calling the constructor
$lazyService = \lazy_object(HeavyService::class);
// Constructor is executed only here
$result = $lazyService->doWork();
You can also define a custom initializer to avoid running the standard constructor:
$lazy = \lazy_object(HeavyService::class, function() {
// Custom initialization
$instance = new HeavyService('cache_value');
return $instance;
});
Ghost vs Virtual Proxy
PHP 8.4 offers two modes: Ghost (the object looks normal but is empty until touched) and Virtual Proxy (a wrapper that delegates to a real object when needed). For most cases, Ghost is simpler and more transparent.
Action Item
Identify a service in your application that is instantiated often but rarely used (e.g., an API client for a secondary endpoint). Wrap it in a Lazy Object and measure the reduction in bootstrap time.
Practical Considerations: When Not to Use Them
PHP 8.4 features aren't silver bullets. Property Hooks can obscure code if overused — stick to using them where real validation or transformation logic exists. Asymmetric Visibility is great for DTOs and Value Objects, but too many properties with different visibilities can hurt readability. Lazy Objects only make sense for truly expensive objects; don't apply it to everything.
We, at Meteora Web, started introducing them gradually in our Laravel projects. Our advice: upgrade to PHP 8.4, test in a staging environment, and start with a small class. The risk of regressions is minimal if your code is already well-typed.
In Summary — What To Do Now
- Upgrade PHP to at least 8.4 on your dev environment (see php.net for instructions).
- Replace a classic getter/setter with Property Hooks in a domain class.
- Make a property immutable using Asymmetric Visibility (
public private(set)). - Apply Lazy Object to a heavy service that loads external data.
- Monitor performance and readability gains with your usual profiling tools.
PHP 8.4 isn't just a minor update — these three features change how you design classes. Use them well, and your code will be cleaner, safer, and more performant.
Sponsored Protocol