Creating Immutable Objects in PHP: A Look at Private Constructor and Public Readonly
Tags: php, architecture, cleancode, programming
Published: 2023-04-02 23:40:54

When it comes to object-oriented programming in PHP, two commonly used concepts for controlling object creation and initialization are private constructors and public readonly properties. PHP private constructor and public readonly are two different concepts that serve different purposes.

**Private constructor**
A private constructor is a constructor method that is declared as private, which means it can only be called from within the class itself. This is often used in the context of the Factory design pattern, where a class is responsible for creating instances of itself or other classes. By making the constructor private, the class ensures that instances can only be created through its own methods, which gives it more control over how instances are created and initialized.

Overall, private constructors can help reduce language ambiguity by providing a consistent and controlled way for objects to be created and initialized. This can help make code easier to maintain and update, and can improve the overall reliability and security of code that uses the language.

An example of this concept is this _User_ class.

```
class User {
  private string $email;
  private string $name;

  private function __construct(string $email, string $name) {
    $this->email = $email;
    $this->name = $name;
  }

  public static function create(string $email, string $name): User {
    return new self($email, $name);
  }

  public function email(): string {
    return $this->email;
  }

  public function name(): string {
    return $this->name;
  }
}
```
In the example above, the constructor is declared as private so that it cannot be called from outside the class. Instead, we provide a public static method _create()_ that creates a new instance of the _User_ class using the private constructor.

To create a new _User_ object, you would call the _create()_ method:

```
$user = User::create('johndoe@example.com', 'John Doe');
```
You can then access the email and name properties using the email() and name() methods, respectively:

```
echo $user->email(); // Outputs: johndoe@example.com
echo $user->name(); // Outputs: John Doe
```

**Readonly**
On the other hand, a public readonly property is a property that can be read from outside the class, but cannot be modified. This is achieved by declaring the property as public, but also using the readonly modifier in PHP 8.0 or from PHP 8.2 onwards declare the class as readonly. This ensures that the property’s value can only be set once, usually during object instantiation, and then remains unchanged throughout the object’s lifetime.

Following this concept our _User_ class will look like this:

```
class User {
  public readonly string $email;
  public readonly string $name;

  public function __construct(string $email, string $name) {
    $this->email = $email;
    $this->name = $name;
  }

  public function email(): string {
    return $this->email;
  }

  public function name(): string {
    return $this->name;
  }
}
```
the _$email_ and _$name_ properties are declared as public readonly, which means they can only be set once during object construction using the class constructor.

To create a new _User_ object, you would call the constructor and pass in the email and name values:

```
$user = new User('johndoe@example.com', 'John Doe');
You can then access the email and name properties using the email() and name() methods, respectively:

echo $user->email(); // Outputs: johndoe@example.com
echo $user->name(); // Outputs: John Doe
```
If you try to modify the email or name property after object construction, you will get a runtime error. For example, the following code will throw an error:

```
$user = new User('johndoe@example.com', 'John Doe');
$user->email = 'newemail@example.com'; // Throws a runtime error
```


**How truly immutable is a class defined as readonly or with readonly properties?**
A class with public readonly properties can be considered immutable only if the properties themselves are truly immutable. In other words, if the values of the properties cannot be changed after they are set, then the class can be considered immutable. However, if the properties can be changed after they are set, then the class cannot be considered truly immutable.

It’s important to note that in PHP, there is no native support for truly immutable objects or properties. Even if a property is declared as public readonly, it is still possible for the value of that property to be changed from outside the class using reflection or other means.

Furthermore, even if a property is immutable, the class as a whole may not be immutable if it has methods that modify the internal state of the object. In other words, immutability is not just about the properties themselves, but also about the behavior of the class and whether it allows its internal state to be changed after instantiation.

Therefore, while a class with public readonly properties can provide some level of immutability, it is not a foolproof way to ensure immutability. To truly create immutable objects, it may be necessary to use a combination of techniques such as private properties with only getter methods, final classes that help prevent the modification of object state after instantiation.