只有关联数组是 PHP 中的结构体。
而且你不能让他们自己严格。
但是你可以用类和接口来伪造结构严格性,但要注意与结构不同,class instances are not passed in arguments, their identifiers are!
你可以通过一个接口(或至少接近它)定义一个结构
结构对对象强制执行某种结构。
PHP (
interface FooStruct
{
public function name() : string;
}
interface BarStruct
{
public function id() : int;
}
interface MyStruct
{
public function foo() : FooStruct;
public function bar() : BarStruct;
}
任何实现MyStruct 的类都是MyStruct。
它的构建方式不取决于结构,它只是确保返回的数据是正确的。
设置数据呢?
设置结构数据是有问题的,因为我们最终会使用 getter 和 setter,它接近于 anemic object 或 DTO 并且是 considered an anti-pattern by some people
错误示例:
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;
}
然后我们最终得到可能是可变的类实现,并且结构不能改变,这是为了使其成为“数据类型”,就像int,string。
然而,没有办法限制 PHP 中的接口,这意味着人们将能够在一个非结构的类中实现你的结构接口。
确保保留实例immutable
然后,结构可能会在没有正确数据的情况下被实例化,并在尝试访问数据时触发错误。
在 PHP 结构类中设置数据的一种简单可靠的方法是通过其构造函数
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;
}
}
使用接口的类型提示:(如果您的 IDE 支持)
如果您不介意不进行严格的类型检查,那么另一种方法是在 IDE 中使用带有 cmets 的接口或类。
/**
* 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
{
}
使用接口而不是类的原因与最初存在接口的原因相同,因为具有许多实现的许多类可以具有相同的结构,并且使用它的每个方法/函数都将支持具有此功能的每个类界面。
这取决于您的 IDE,因此您可能需要改用类或不使用它。
注意:
请记住,您必须在代码的其他地方验证/清理实例中的数据以匹配注释。