PHP 7是否支持对类属性进行类型提示?
我的意思是,不仅仅是对于 setters/getters(设置器/获取器),而是针对属性本身。
类似于:
class Foo {
/**
*
* @var Bar
*/
public $bar : Bar;
}
$fooInstance = new Foo();
$fooInstance->bar = new NotBar(); //Error
PHP 7是否支持对类属性进行类型提示?
我的意思是,不仅仅是对于 setters/getters(设置器/获取器),而是针对属性本身。
类似于:
class Foo {
/**
*
* @var Bar
*/
public $bar : Bar;
}
$fooInstance = new Foo();
$fooInstance->bar = new NotBar(); //Error
PHP 7.4 将支持 类型属性,例如:
class Person
{
public string $name;
public DateTimeImmutable $dateOfBirth;
}
PHP 7.3及更早版本不支持此功能,但有一些替代方案。
您可以创建一个私有属性,只能通过具有类型声明的getter和setter访问:
class Person
{
private $name;
public function getName(): string {
return $this->name;
}
public function setName(string $newName) {
$this->name = $newName;
}
}
您还可以创建公共属性并使用文档块为阅读代码和使用IDE的人提供类型信息,但这不提供运行时类型检查:
class Person
{
/**
* @var string
*/
public $name;
}
实际上,您可以结合使用getter和setter以及文档块。
如果您更加冒险,您可以使用__get
、__set
、__isset
和__unset
魔术方法创建一个虚拟属性,并自行检查类型。虽然我不确定我是否推荐这样做。
array_push($this->foo, $bar)
或 sort($this->foobar)
这样的操作也将是一个大问题。 - Andrea(new Person())->dateOfBirth = '2001-01-01';
... 假设提供了 declare(strict_types=0);
。它会抛出异常还是使用 DateTimeImmutable
构造函数?如果是后者,那么如果字符串无效日期,将抛出哪种类型的错误?TypeError
? - lmerino7.4+:
好消息是,正如@Andrea指出的那样,它将在新版本中实现。 我会把这个解决方案留在这里,以防有人想在7.4之前使用它。
7.3或更低版本
根据我从这个主题仍然收到的通知,我相信许多人都遇到了和我一样的问题。我针对这种情况的解决方案是,在一个 trait 中组合 setters + __set
魔法方法,以模拟这种行为。下面是代码:
trait SettersTrait
{
/**
* @param $name
* @param $value
*/
public function __set($name, $value)
{
$setter = 'set'.$name;
if (method_exists($this, $setter)) {
$this->$setter($value);
} else {
$this->$name = $value;
}
}
}
这里是演示:
class Bar {}
class NotBar {}
class Foo
{
use SettersTrait; //It could be implemented within this class but I used it as a trait for more flexibility
/**
*
* @var Bar
*/
private $bar;
/**
* @param Bar $bar
*/
protected function setBar(Bar $bar)
{
//(optional) Protected so it wont be called directly by external 'entities'
$this->bar = $bar;
}
}
$foo = new Foo();
$foo->bar = new NotBar(); //Error
//$foo->bar = new Bar(); //Success
解释
首先,将bar
定义为私有属性,这样PHP就会自动地转换__set
。
__set
将检查当前对象中是否声明了某个setter(method_exists($this,$setter)
)。否则,它将像通常一样只设置其值。
声明一个setter方法(setBar),该方法接收带类型提示的参数(setBar(Bar $bar)
)。
只要PHP检测到传递给setter的不是Bar
实例,它就会自动触发致命错误:Uncaught TypeError: Argument 1 passed to Foo::setBar() must be an instance of Bar, instance of NotBar given
PHP 7.4的编辑:
自从PHP 7.4以来,您可以使用属性类型(文档 / 维基),这意味着您可以执行以下操作:
class Foo
{
protected ?Bar $bar;
public int $id;
...
}
class Foo
{
/**
* @var Bar
*/
protected $bar = null;
/**
* Foo constructor
* @param Bar $bar
**/
public function __construct(Bar $bar = null){
$this->bar = $bar;
}
/**
* @return Bar
*/
public function getBar() : ?Bar{
return $this->bar;
}
/**
* @param Bar $bar
*/
public function setBar(Bar $bar) {
$this->bar = $bar;
}
}
class Bar {
public $val;
}
class Foo {
/**
*
* @var Bar
*/
private $bar;
/**
* @return Bar
*/
public function getBar()
{
return $this->bar;
}
/**
* @param Bar $bar
*/
public function setBar(Bar $bar)
{
$this->bar = $bar;
}
}
$fooInstance = new Foo();
// $fooInstance->bar = new NotBar(); //Error
$fooInstance->setBar($fooInstance);
输出:
TypeError: Argument 1 passed to Foo::setBar() must be an instance of Bar, instance of Foo given, called in ...