Type hinting for properties in PHP 7?

梦想与她 提交于 2019-11-30 22:18:00

问题


Does php 7 support type hinting for class properties?

I mean, not just for setters/getters but for the property itself.

Something like:

class Foo {
    /**
     *
     * @var Bar
     */
    public $bar : Bar;
}

$fooInstance = new Foo();
$fooInstance->bar = new NotBar(); //Error

回答1:


PHP 7.4 will support typed properties like so:

class Person
{
    public string $name;
    public DateTimeImmutable $dateOfBirth;
}

PHP 7.3 and earlier do not support this, but there are some alternatives.

You can make a private property which is accessible only through getters and setters which have type declarations:

class Person
{
    private $name;
    public function getName(): string {
        return $this->name;
    }
    public function setName(string $newName) {
        $this->name = $newName;
    }
}

You can also make a public property and use a docblock to provide type information to people reading the code and using an IDE, but this provides no runtime type-checking:

class Person
{
    /**
      * @var string
      */
    public $name;
}

And indeed, you can combine getters and setters and a docblock.

If you're more adventurous, you could make a fake property with the __get, __set, __isset and __unset magic methods, and check the types yourself. I'm not sure if I'd recommend it, though.




回答2:


7.4+:

Good news that it will be implemented in the new releases, as @Andrea pointed out. I will just leave this solution here in case someone wants to use it prior to 7.4


7.3 or less

Based on the notifications I still receive from this thread, I believe that many people out there had/is having the same issue that I had. My solution for this case was combining setters + __set magic method inside a trait in order to simulate this behaviour. Here it is:

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;
        }
    }
}

And here is the demonstration:

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

Explanation

First of all, define bar as a private property so PHP will cast __set automagically.

__set will check if there is some setter declared in the current object (method_exists($this, $setter)). Otherwise it will only set its value as it normally would.

Declare a setter method (setBar) that receives a type-hinted argument (setBar(Bar $bar)).

As long as PHP detects that something that is not Bar instance is being passed to the setter, it will automaticaly trigger a Fatal Error: Uncaught TypeError: Argument 1 passed to Foo::setBar() must be an instance of Bar, instance of NotBar given




回答3:


It is actually not possible and you only have 4 ways to actually simulate it :

  • Default values
  • Decorators in comment blocks
  • Default values in constructor
  • Getters and setters

I combined all of them here

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;
    }
}

Note that you actually can type the return as ?Bar since php 7.1 (nullable) because it could be null (not available in php7.0.)

You also can type the return as void since php7.1




回答4:


You can use setter

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);

Output:

TypeError: Argument 1 passed to Foo::setBar() must be an instance of Bar, instance of Foo given, called in ...


来源:https://stackoverflow.com/questions/37254695/type-hinting-for-properties-in-php-7

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!