Does PHP have a struct data type?

Php

Php Problem Overview


Is there something like struct data type in PHP? Can anyone give me example for struct data type to understand this better?

If there's no such data type, how can I get a data type that behaves like a struct?

Php Solutions


Solution 1 - Php

Closest you'd get to a struct is an object with all members public.

class MyStruct {
    public $foo;
    public $bar;
}

$obj = new MyStruct();
$obj->foo = 'Hello';
$obj->bar = 'World';

I'd say looking at the PHP Class Documentation would be worth it. If you need a one-off struct, use the StdObject as mentioned in alex's answer.

Solution 2 - Php

You can use an array

$something = array(
   'key' => 'value',
   'key2' => 'value2'
);

or with standard object.

$something = new StdClass();

$something->key = 'value';
$something->key2 = 'value2';

Solution 3 - Php

I recommend 2 things. First is associative array.

$person = Array();
$person['name'] = "Joe";
$person['age'] = 22;

Second is classes.

Detailed documentation here: http://php.net/manual/en/language.oop5.php

Solution 4 - Php

This is quite high up on Google so I figured I'd share my implementation of a pseudo-struct using PHP8 syntax. The toArray() method does depend on Illuminate\Support\Str to transform keys to snake case (useful for mass assignment against Laravel models) however just remove it if it doesn't fit your use-case.

The base class:

<?php

namespace App\Infrastructure\Structs;

use App\Infrastructure\Exceptions\CannotMutateStructException;
use App\Infrastructure\Exceptions\ClassPropertyNotFoundException;
use Illuminate\Support\Str;
use ReflectionClass;

abstract class Struct
{
    /**
     * @param string $name
     * @param mixed $value
     * @throws CannotMutateStructException
     */
    public function __set(string $name, mixed $value): void
    {
        throw new CannotMutateStructException(
            'Structs are immutable. If you need mutable data then use a class instead.'
        );
    }

    public function all(): array
    {
        $reflector = new ReflectionClass(static::class);
        $response = [];

        foreach ($reflector->getProperties() as $property) {
            $response[$property->name] = $this->{$property->name};
        }

        return $response;
    }

    public function toArray(bool $snakeCase = false): array
    {
        $all = self::all();

        if ($snakeCase === false) {
            return $all;
        }

        $snakeCaseAll = [];

        foreach ($all as $key => $value) {
            $snakeCaseAll[Str::snake($key)] =  $value;
        }

        return $snakeCaseAll;
    }
}

How to use:

<?php

namespace App\Infrastructure\Structs;

class Person extends Struct
{
    public function __construct(
        public string $name,
        public int $age,
        public int $heightInCentimetres,
    ) {}
}

How to interact with it:

>>> $t = new \App\Infrastructure\Structs\Person('Max', 26, 182);

>>> $t->age
=> 26

>>> $t->age = 40
App\Infrastructure\Exceptions\CannotMutateStructException with message 'Structs are immutable. If you need mutable data then use a class instead.'

>>> $t->toArray(true)
=> [
     "name" => "Max",
     "age" => 26,
     "height_in_centimetres" => 182,
   ]

Hopefully this helps someone.

Edit: with PHP8.1 we now have readonly properties which can make this even more concise.

Solution 5 - Php

I cobbled together a 'dynamic' struct class today, had a look tonight and someone has written something similar with better handling of constructor parameters, it might be worth a look:

http://code.activestate.com/recipes/577160-php-struct-port/

One of the comments on this page mentions an interesting thing in PHP - apparently you're able to cast an array as an object, which lets you refer to array elements using the arrow notation, as you would with a Struct pointer in C. The comment's example was as follows:

$z = array('foo' => 1, 'bar' => true, 'baz' => array(1,2,3));
//accessing values as properties
$y = (object)$z;
echo $y->foo;

I haven't tried this myself yet, but it may be that you could get the desired notation by just casting - if that's all you're after. These are of course 'dynamic' data structures, just syntactic sugar for accessing key/value pairs in a hash.

If you're actually looking for something more statically typed, then ASpencer's answer is the droid you're looking for (as Obi-Wan might say.)

Solution 6 - Php

It seems that the struct datatype is commonly used in SOAP:

var_dump($client->__getTypes());

array(52) {
  [0] =>
  string(43) "struct Bank {\n string Code;\n string Name;\n}"
}

This is not a native PHP datatype!

It seems that the properties of the struct type referred to in SOAP can be accessed as a simple PHP stdClass object:

$some_struct = $client->SomeMethod();
echo 'Name: ' . $some_struct->Name;

Solution 7 - Php

Only associative arrays are structs in PHP.

And you can't make them strict on their own.

But you can sort of fake structure strictness with classes and interfaces, but beware that unlike structures, class instances are not passed in arguments, their identifiers are!


You can define a struct through an interface (or at least close to it)

Structs enforce a certain structure on an object.

PHP (<= 7.3) does not have native structs, but you can get around it with interfaces and type hinting:

interface FooStruct 
{
    public function name() : string;
}
interface BarStruct 
{
    public function id() : int;
}
interface MyStruct 
{
   public function foo() : FooStruct;
   public function bar() : BarStruct;
}

Any class implementing MyStruct will be a MyStruct.

The way it's build up is not up to the struct, it just ensures that the data returned is correct.


What about setting data?

Setting struct data is problematic as we end up with getters and setters and it's something that is close to an anemic object or a DTO and is considered an anti-pattern by some people

Wrong example:

interface FooStruct 
{
    public function getName() : string;
    public function setName(string $value) : FooStruct;
}
interface BarStruct 
{
    public function getId() : int;
    public function setId(int $value) : BarStruct;
}
interface MyStruct 
{
   public function getFoo() : FooStruct;
   public function setFoo(FooStruct $value) : MyStruct;
   public function getBar() : BarStruct;
   public function setBar(BarStruct $value) : MyStruct;
}

Then we end up with class implementations that might be mutable, and a struct must not mutate, this is to make it a "data type", just like int, string. Yet there's no way to restrict that with interfaces in PHP, meaning people will be able to implement your struct interface in a class that is not a struct. Make sure to keep the instance immutable

Also a struct may then be instantiated without the correct data and trigger errors when trying to access the data.

An easy and reliable way to set data in a PHP struct class is through its constructor

interface FooStruct 
{
    public function name() : string;
}
interface BarStruct 
{
    public function id() : int;
}
interface MyStruct 
{
   public function foo() : FooStruct;
   public function bar() : BarStruct;
}

class Foo implements FooStruct 
{
   protected $name;
   public function __construct(string $name)
   {
       $this->name = $name;
   }
   public function name() : string
   {
       return $this->name;
   }
}

class Bar implements BarStruct 
{
   protected $id;
   public function __construct(string $id)
   {
       $this->id = $id;
   }
   public function id() : int
   {
       return $this->id;
   }
}

class My implements MyStruct 
{
   protected $foo, $bar;
   public function __construct(FooStruct $foo, BarStruct $bar)
   {
       $this->foo = $foo;
       $this->bar = $bar;
   }
   public function foo() : FooStruct
   {
       return $this->foo;
   }
   public function bar() : BarStruct
   {
       return $this->bar;
   }
}

Type hinting using interfaces: (if your IDE supports it)

If you don't mind not having the strict type checking, then another way would be using interfaces or classes with comments for the IDE.

/**
 * Interface My
 * @property Foo $foo
 * @property Bar $bar
 */
interface My 
{

}

/**
 * Interface Foo
 * @property string|integer $id
 * @property string $name
 */
interface Foo 
{

}

/**
 * Interface Bar
 * @property integer $id
 */
interface Bar
{

}

The reason to use interfaces instead of classes is for the same reason why interfaces exist in the first place, because then many classes with many implementations can have this same structure and each method/function that uses it will support every class with this interface.

This depends on your IDE, so you might need to use classes instead or just live without it.

Note: Remember that you have to validate/sanitize the data in the instance elsewhere in the code to match the comment.

Solution 8 - Php

A public class is one option, if you want something more encapsulated you can use an abstract/anonymous class combination. My favorite part is that autocomplete still works (for PhpStorm) for this but I don't have a public class sitting around.

<?php

final class MyParentClass
{
    /**
     * @return MyStruct[]
     */
    public function getData(): array
    {
        return array(
        	$this->createMyObject("One", 1.0, new DateTime("now")),
        	$this->createMyObject("Two", 2.0, new DateTime("tommorow"))
    	);
    }
    
    private function createMyObject(string $description, float $magnitude, DateTime $timeStamp): MyStruct
    {
        return new class(func_get_args()) extends MyStruct {
            protected function __construct(array $args)
            {
                $this->description = $args[0];
                $this->magnitude = $args[1];
                $this->timeStamp = $args[2];
            }
        };
    }
}

abstract class MyStruct
{
    public string $description;
    public float $magnitude;
    public DateTime $timeStamp;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionktmView Question on Stackoverflow
Solution 1 - PhpASpencerView Answer on Stackoverflow
Solution 2 - PhpalexView Answer on Stackoverflow
Solution 3 - PhpRuelView Answer on Stackoverflow
Solution 4 - PhpMaxView Answer on Stackoverflow
Solution 5 - PhppikuseruView Answer on Stackoverflow
Solution 6 - PhpdotancohenView Answer on Stackoverflow
Solution 7 - PhpTimo HuovinenView Answer on Stackoverflow
Solution 8 - PhpDavid RyanView Answer on Stackoverflow