PHP Records vs Structs: Simplifying Immutability in PHP

As a long-time PHP enthusiast and contributor, I’ve always been fascinated by how PHP can evolve to improve developer experience while maintaining its simplicity. Over the years, I’ve explored various ways to enhance the language, including my previously published Object Initializer RFC and a blog post discussing its potential impact. Additionally, I drafted an early RFC on Structs, which reflected my interest in improving PHP’s handling of immutable data.

The PHP Internals community is discussing two exciting proposals: Records, championed by Rob, and Structs, proposed by Ilija Tovilo. Both aim to bring value semantics into PHP, but with distinct approaches and goals. Ilija’s proposal has even progressed into a Work-in-Progress PR on GitHub, showing active development. Let’s dive into these proposals, share my perspective, and highlight what they could mean for PHP.


What Are Records and Structs?

At their core, both Records and Structs aim to simplify the creation of value objects in PHP. However, they differ in their goals and implementations.


Syntax Comparison

The syntax of Records is concise and minimal:

record User(string $name, int $age);

In contrast, Structs use a class-like structure:

struct User {
    public string $name;
    public int $age;
}

While both approaches define value objects, Records clearly reduce verbosity, making them ideal for simple use cases. Structs, on the other hand, offer more explicitness, which might be preferable for complex scenarios.


Key Features of Records

1. Value Semantics

Records implement value-based semantics by default. This means two instances with the same properties are considered equal:

$user1 = new User("Alice", 30);
$user2 = new User("Alice", 30);
var_dump($user1 === $user2); // true

This simplicity eliminates the need for manual equality methods.

2. Extensibility with Attributes

Attributes provide a flexible way to enhance functionality without adding complexity. For example:

#[JsonSerializable]
record User(string $name, int $age);

This aligns well with modern PHP trends like enums and attributes.

3. Syntax Simplicity

The single-line definition of Records makes them perfect for small types like configuration objects or DTOs.


Key Features of Structs

1. Copy-on-Write (CoW) Semantics

Structs optimize value semantics by sharing data until a modification is required. When a change occurs, only the modified data is copied:

struct Line {
    public Point $start;
    public Point $end;
}

$line1 = new Line(start: new Point(0, 0), end: new Point(1, 1));
$line2 = $line1->with(end: new Point(2, 2));

This approach ensures performance efficiency for large or nested structures.

2. Explicit Equality

Unlike Records, Structs require developers to define their own equality logic, offering flexibility but at the cost of verbosity.


Strengths and Weaknesses

FeatureRecordsStructs
SyntaxConcise and minimalVerbose but familiar
EqualityAutomatic by valueRequires manual logic
PerformanceStandard value semanticsOptimized with CoW
ExtensibilityAttributesTraditional methods
Ideal Use CaseSmall, lightweight typesLarge, nested structures

Community Opinions

The PHP community has provided valuable feedback on these proposals:

For more details, you can follow the discussions on Records and Structs.


My Preferences

As someone deeply invested in PHP’s evolution, I find myself leaning towards Rob’s Records proposal for its simplicity, automatic equality, and alignment with modern PHP trends. However, I also recognize the value of Ilija’s Copy-on-Write semantics for performance-critical applications. A hybrid approach combining these strengths could offer the best of both worlds.


Future Directions

The discussion around Records and Structs opens the door to exciting possibilities:

  1. Composition Support: Enabling Records or Structs to support composition could allow for more reusable and complex data structures.
  2. Standard Attributes: Attributes like #[ValueObject] or #[Immutable] could formalize and enhance the functionality of value types.
  3. Performance Optimizations: Integrating CoW into Records for larger structures might strike a balance between simplicity and efficiency.

What’s Next?

The PHP internals team and the community must weigh the trade-offs between these proposals. A community vote could help gauge overall preferences and provide clarity on the direction PHP should take.

As these discussions continue, it’s clear that both Records and Structs have the potential to significantly improve PHP’s handling of value data. The question now is not whether, but how, they will shape the future of the language.


What are your thoughts?
Would you prefer the simplicity of Records or the performance benefits of Structs? Let me know in the comments or join the discussion on the PHP internals mailing list!

comments powered by Disqus