PHP 7のプロパティのタイプヒント?


80

php 7はクラスプロパティの型ヒントをサポートしていますか?

つまり、セッター/ゲッターだけでなく、プロパティ自体にも当てはまります。

何かのようなもの:

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

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

1
私が知っていることではありません。しかし、一般的に任意のプロパティの値に制約することは、とにかくセッターを介して行われるべきです。セッターは「value」引数のタイプヒントを簡単に持つことができるので、準備は完了です。
Niet the Dark Absol 2016年

そこにある多くのフレームワークは、保護された属性を利用してます(主にコントローラー用)。特にそれらの場合には、それは非常に役に立ちます。
CarlosCarucce 2016年

回答:


134

PHP 7.4、次のような型付きプロパティをサポートします

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

PHP 7.3以前はこれをサポートしていませんが、いくつかの代替手段があります。

型宣言を持つゲッターとセッターを介してのみアクセス可能なプライベートプロパティを作成できます。

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

パブリックプロパティを作成し、docblockを使用して、コードを読んでIDEを使用しているユーザーに型情報を提供することもできますが、これでは実行時型チェックは提供されません。

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

そして実際、ゲッターとセッター、およびdocblockを組み合わせることができます。

あなたはもっと冒険している場合は、あなたが偽のプロパティを作ることができる__get__set__isset__unset魔法の方法、および種類を自分で確認してください。でも、お勧めするかどうかはわかりません。


本当にいいですね。次のリリースで何が来るのか楽しみです!
CarlosCarucce 2016年

もう1つの重要な問題は、参照の処理です。これは、型宣言と実際にはうまく相互作用せず、そのようなプロパティでは無効にする必要がある場合があります。パフォーマンスの問題がなくても、実行できない、言うことができない、array_push($this->foo, $bar)またはsort($this->foobar)大したことになるでしょう。
アンドレア

タイプの強制はどのように機能しますか?例:(new Person())->dateOfBirth = '2001-01-01';...提供さdeclare(strict_types=0);れます。DateTimeImmutableコンストラクターをスローまたは使用しますか?その場合、文字列が無効な日付であると、どのようなエラーがスローされますか?TypeError
lmerino

@Imerino DateTime(Immutable)への暗黙の変換はなく、かつてない
Andrea

12

7.4以降:

@Andreaが指摘したように、新しいリリースで実装されることは朗報です。誰かが7.4より前にそれを使用したい場合に備えて、このソリューションをここに残しておきます


7.3以下

私がまだこのスレッドから受け取っている通知に基づいて、そこにいる多くの人々が私と同じ問題を抱えていた/抱えていると思います。この場合の私のソリューションを組み合わせたセッター+__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

説明

まず、barPHPが__set 自動的にキャストするようにプライベートプロパティとして定義します。

__set現在のオブジェクト(method_exists($this, $setter))で宣言されているセッターがあるかどうかを確認します。それ以外の場合は、通常どおりに値を設定するだけです。

タイプヒント付き引数(setBar(Bar $bar))を受け取るセッターメソッド(setBar)を宣言します。

PHPがBarインスタンスではないものがセッターに渡されていることを検出する限り、致命的なエラーを自動的にトリガーします:Uncaught TypeError:Foo :: setBar()に渡される引数1はBarのインスタンスである必要があり、NotBarのインスタンスが指定されている必要があります


4

PHP 7.4用に編集:

PHP 7.4以降、属性(ドキュメント/ Wiki)を入力できます。つまり、次のことができます。

    class Foo
{
    protected ?Bar $bar;
    public int $id;
    ...
}

ウィキによると、すべての許容値は次のとおりです。

  • bool、int、float、string、array、object
  • 反復可能
  • 自己、親
  • 任意のクラスまたはインターフェイス名
  • ?type //ここで、「type」は上記のいずれかです。

PHP <7.4

それは実際には不可能であり、実際にそれをシミュレートする方法は4つしかありません。

  • デフォルト値
  • コメントブロックのデコレータ
  • コンストラクターのデフォルト値
  • ゲッターとセッター

ここでそれらすべてを組み合わせました

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

null(php7.0では使用できない)である可能性があるため、php 7.1(null可能)以降、実際にはリターンを?Barとして入力できることに注意してください。

php7.1以降、returnをvoidとして入力することもできます


1

セッターが使えます

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 ...
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.