<?php
declare(strict_types=1);
namespace Dshafik;
class Struct {
// Because gettype() and ReflectionType->__toString() are inconsistent
/**
* @var array Type mappings
*/
private $__typeMap = [
'int' => 'integer',
'bool' => 'boolean',
'float' => 'double',
];
/**
* @var array Reflection data
*/
private $__reflection = [];
/**
* @var array The struct values
*/
protected $__values = [];
public function __construct()
{
$this->introspect();
$this->hoist(func_get_args());
}
public function __get($what)
{
return $this->__values[$what];
}
public function __set($what, $value)
{
if (($type = gettype($value)) === $this->__reflection[$what]) {
return $this->__values[$what] = $value;
} else {
throw new \TypeError("Key '$what' must be of type {$this->__reflection[$what]}, $type given.");
}
}
final protected function introspect()
{
if ($this->__reflection) {
return;
}
$class = new \ReflectionObject($this);
$constructor = $class->getConstructor();
foreach ($constructor->getParameters() as $arg) {
$type = (string) $arg->getType();
$this->__reflection[$arg->name] = $this->__typeMap[$type] ?? $type;
}
}
final protected function hoist($args)
{
$this->introspect();
$i = 0;
foreach ($this->__reflection as $key => $type) {
$this->__set($key, $args[$i]);
$i++;
}
}
}
// With strict types, will throw a TypeError, or coerce otherwise on mismatch
$struct = new class(1, "Hello") extends Struct {
public function __construct(int $foo, string $bar) {
$this->hoist(func_get_args());
}
};
var_dump($struct);
// Works
$struct->foo = 2;
var_dump($struct);
// Will throw a TypeError
$struct->foo = 'World';
preferences:
16.48 ms | 402 KiB | 5 Q