/struct

Structs for PHP. Simple data transfer objects. Supports immutability.

Primary LanguagePHPMIT LicenseMIT

Structs

Structs for PHP. Simple data transfer objects.

Install

Via Composer:

composer require silvanus/structs

To use autoloading mechanism, you must include vendor/autoload.php file in your code.

Motivation

PHP does not have real structs. They are feature in many other languages like C++, Rust or Go. Typescripts interfaces also bear resemblence to structs. Structs would serve a purpose for simple data that has a shape, like data transfer objects.

In PHP you can't demand properties with Interfaces, only methods. Therefore interfaces for struct-like objects do not make much sense. But if you just use a class that has public properties, you'll still let clients just declare new properties on the fly. It would be preferable to keep the shape of data just the way the struct was defined.

This library aims to solve this by providing AbstractStruct class with sensible defaults to stop dynamically assigning new or incorrect class properties/members.

Usage

Declare new struct.

<?php

namespace YourNameSpace\Structs;

// Parent class.
use Silvanus\Structs\AbstractStruct;

class Employee extends AbstractStruct
{    
    public string $name;    
    public string $department;
    public int $salary;
}

Use the new struct

<?php

use YourNameSpace\Structs\Employee;

$employee = new Employee();

// Set up data by property.
$employee->name       = 'Eiríkr Blóðøx';
$employee->department = 'Norway';
$employee->salary     = 954;

echo $employee->name; // Eiríkr Blóðøx

/**
 * You can only use properties set in class / struct level.
 */
$employee->hobbies = "Raiding"; // Throws CanNotSetPropertyException.
echo $employee->hobbies // Throws CanNotGetPropertyException

You can also populate the struct straight in creation.

<?php

use YourNameSpace\Structs\Employee;

$data = array(
	'name' => 'Eiríkr Blóðøx',
	'department' => 'Norway',
	'salary' => 954,
	// Setting incorrect fields here will likewise throw exceptions.
);

$employee = new Employee($data);

Immutable version

You can also use a version of struct that does not allow modification of data once it is set.

Create immutable struct:

<?php

namespace YourNameSpace\Structs;

// Different parent class
use Silvanus\Structs\AbstractStructImmutable;

class EmployeeImmutable extends AbstractStructImmutable
{    
    public string $name;    
    public string $department;
    public int $salary;
}

Use immutable struct

<?php

use YourNameSpace\Structs\EmployeeImmutable;

$data = array(
	'name' => 'Eiríkr Blóðøx',
	'department' => 'Norway',
	'salary' => 954,
	// Setting incorrect fields here will likewise throw exceptions.
);

$employee = new EmployeeImmutable($data);

// You can access the properties.
echo $employee->name;
echo $employee->department;
echo $employee->salary;

// ... But you cannot edit them.
$employee->name = 'Eiríkr inn rauða'; // Throws CanNotSetPropertyException.

Whats the advantage over just using default class or an array

  • You lock creation of additional properties. That way your data keeps the shape you expect it to have.
  • You protect properties from typos. $employee->salary works, but $employee->salayr throws exception.
  • Typehinting against Employee $employee

How does it work?

Nothing fancy! Just writes final __set and final __get methods on abstract parent. They block getting & creating non-declared properties. Child classes can still add public properties, as calling a public property does not trigger __get or __set

The immutable version does things a bit differently. It first sets given properties in the public properties to trigger PHP type validation (if 7.4 or higher), but after that removes the values and only stores them in single array. __get automatically uses this $properties array to serve the values.