PHP 8: Streamlining Object Initialization with Constructor Promotion

In Object-Oriented Programming (OOP), a constructor is a specialized method used to initialize objects. It is called automatically the moment an object is instantiated. Its primary purpose is to ensure a newly created object starts in a valid and consistent state, preventing the use of uninitialized "garbage" data that can lead to unpredictable behavior and bugs.

The Evolution of OOP in PHP

While PHP has supported OOP since version 3, the release of PHP 5 in 2004 marked a pivotal move toward modern OOP principles, making the language a viable choice for complex, scalable applications. Like many C-style languages, PHP uses a constructor method within a class definition.

Traditionally, defining and initializing object properties in PHP required a repetitive, three-step process:

  • Declare the property.
  • Define a parameter in the constructor.
  • Manually assign that parameter to the property using $this.

The Traditional Approach

Even in a simple class, this redundancy can be cumbersome and prone to typos:

class Polygon {

public $numberSides;

public $fillColor;

function __construct($sides, $color) {

$this->numberSides = $sides;

$this->fillColor = $color;

}

function get_details() {

echo "The polygon has " .

$this->numberSides .

" sides, and it is " .

$this->fillColor .

".<br>";

}

}

$triangle = new Polygon(3, 'Red');

$triangle->get_details();

// Output: The polygon has 3 sides, and it is Red.

$hexagon = new Polygon(6, 'Blue');

$hexagon->get_details();

// Output: The polygon has 6 sides, and it is Blue.

?>

Modernizing with Constructor Property Promotion

Introduced in PHP 8, Constructor Property Promotion eliminates this boilerplate. It provides a shorthand that allows constructor parameters to be transformed into properties automatically.

By adding a visibility modifier (public, protected, or private) directly to a constructor argument, PHP interprets it as both an object property and an argument. The engine then assigns the value to the property without any additional code.

class Polygon {

function __construct(public $numberSides, public $fillColor) {

}

function get_details() {

echo "The polygon has " .

$this->numberSides .

" sides, and it is " .

$this->fillColor .

".<br>";

}

}

$triangle = new Polygon(3, 'Red');

$triangle->get_details();

// Output: The polygon has 3 sides, and it is Red.

$hexagon = new Polygon(6, 'Blue');

$hexagon->get_details();

// Output: The polygon has 6 sides, and it is Blue.

?>

The constructor body can remain empty or contain additional logic. If extra statements are included, they are executed after the promoted values have been assigned, ensuring the properties are available for use immediately.

Mixing Promoted and Non-Promoted Arguments

Constructor promotion is flexible; you can mix promoted and traditional arguments in any order. In the following example, we use a non-promoted $radius to calculate the area, while promoting the other properties:

class Polygon {

public $area;

function __construct($radius, public $numberSides, public $fillColor = "Blue") {

$angle = 360 / $this->numberSides;

$this->area = 0.5 * $this->numberSides * pow($radius, 2) * sin(deg2rad($angle));

}

function get_details() {

echo "The polygon has " .

$this->numberSides .

" sides with an area of " .

number_format($this->area, 4) .

" square units, and it is " .

$this->fillColor .

".<br>";

}

}

$triangle = new Polygon(2, 3, 'Red');

$triangle->get_details();

// Output: The polygon has 3 sides with an area of 5.1962 square units, and it is Red.

$hexagon = new Polygon(2, 6);

$hexagon->get_details();

// Output: The polygon has 6 sides with an area of 10.3923 square units, and it is Blue.

?>

Critical Considerations for Implementation

To use this feature effectively, keep these methodical rules in mind:

  • Modifiers are Mandatory: An argument must have a modifier (public, protected, private, or even readonly) to trigger promotion.
  • Type Hinting: Promoted arguments can be typed (e.g., public int $sides). However, they cannot be typed as callable due to engine ambiguity.
  • Attribute Replication: If you use PHP Attributes on a promoted argument, PHP is smart enough to replicate that metadata onto both the parameter and the property.
  • Default Values: While default values on promoted arguments work as expected, they are only replicated to the argument in the generated code. The constructor remains the "single source of truth" for initialization.

Under the Hood: How the Engine Sees It

When you write a promoted property with a default value or a PHP Attribute, the PHP engine effectively "expands" your code during execution:

What the developer enterspublic function __construct(#[MyAttribute] public string $username, public string $role = 'guest') {}
What the PHP engine executesclass User {
  #[MyAttribute] public string $username; // Replicated here
  public string $role; // No default value here!

  public function __construct(#[MyAttribute] string $username, string $role = 'guest') {
    $this->role = $role;
  }
}

Conclusion

Constructor Property Promotion allows you to combine property declaration, constructor definition, and variable assignment into a single, elegant syntax. For the methodical developer, this is more than just a time-saver; it is a way to reduce the surface area for bugs and create cleaner, more readable object-oriented code.

This feature brings PHP in line with similar modern syntaxes found in TypeScript (Parameter Properties) and Kotlin, making it easier for full-stack developers to switch between languages without losing efficiency.