web-dev-qa-db-ja.com

PHPDoc型はオブジェクトの配列をほのめかしていますか?

そのため、PHPDocでは、メンバー変数の宣言の上に@varを指定して、その型をヒントにすることができます。それから、例えばIDE。 PHPEdは、それがどのタイプのオブジェクトを扱っているかを知っていて、その変数についてのコードの洞察を提供することができます。

<?php
  class Test
  {
    /** @var SomeObj */
    private $someObjInstance;
  }
?>

これは、後でそれらのオブジェクトを反復処理するときに適切なヒントを得ることができるようにするために、オブジェクトの配列に対して同じことを行う必要があるまでは、うまく機能します。

それで、メンバー変数がSomeObjsの配列であることを指定するためにPHPDocタグを宣言する方法はありますか? @var配列は十分ではなく、例えば@var array(SomeObj)は有効ではないようです。

388

使用:

/* @var $objs Test[] */
foreach ($objs as $obj) {
    // Typehinting will occur after typing $obj->
}

インライン変数をタイプヒントするとき

class A {
    /** @var Test[] */
    private $items;
}

クラスプロパティ用。

PHPDoc(そしてZend StudioやNetbeansのようなIDE)がそのオプションを持っていなかったときの'09からの前回の回答:

あなたができる最善は言うことです、

foreach ($Objs as $Obj)
{
    /* @var $Obj Test */
    // You should be able to get hinting after the preceding line if you type $Obj->
}

私はZend Studioでそれをたくさんやります。他の編集者については知りませんが、うまくいくはずです。

299
Zahymaka

JetBrainsのPhpStorm IDEでは、/** @var SomeObj[] */を使用できます。

/**
 * @return SomeObj[]
 */
function getSomeObjects() {...}

phpdocドキュメント この方法をお勧めします。

単一の型を含むように指定された場合、Type定義は各配列要素の型を読者に知らせます。その場合、与えられた配列の要素として1つのTypeだけが期待されます。

例:@return int[]

878
Nishi

NetBeansのヒント:

$users[0]->のコード補完と、$this->のUserクラスの配列の補完ができます。

/**
 * @var User[]
 */
var $users = array();

$this->...の補完をするときに、クラスメンバーのリストで配列の型を見ることもできます。

57
user1491819

変数を指定することはオブジェクトの配列です:

$needles = getAllNeedles();
/* @var $needles Needle[] */
$needles[1]->...                        //codehinting works

これはNetbeans 7.2で動作します(私はそれを使っています)

以下とも動作します。

$needles = getAllNeedles();
/* @var $needles Needle[] */
foreach ($needles as $needle) {
    $needle->...                        //codehinting works
}

したがって、foreach内で宣言を使用する必要はありません。

29
Highmastdon

PSR-5:PHPDoc はGenericsスタイルの記法の形式を提案します。

構文

Type[]
Type<Type>
Type<Type[, Type]...>
Type<Type[|Type]...>

Collectionの中の値は、他の配列や他のCollectionでも構いません。

Type<Type<Type>>
Type<Type<Type[, Type]...>>
Type<Type<Type[|Type]...>>

<?php

$x = [new Name()];
/* @var $x Name[] */

$y = new Collection([new Name()]);
/* @var $y Collection<Name> */

$a = new Collection(); 
$a[] = new Model_User(); 
$a->resetChanges(); 
$a[0]->name = "George"; 
$a->echoChanges();
/* @var $a Collection<Model_User> */

注:IDEがコードアシストを行うことを期待している場合、それはIDEがPHPDocのGenericスタイルのコレクション表記をサポートしているかどうかについての別の質問です。

私の答えから この質問へ

19
Gerard

Robert C. Martinによる "Clean Code"で概説されているように、私はクリーンなコードを読み書きすることを好みます。彼の信条に従うとき、あなたは(あなたのAPIのユーザーとして)開発者にあなたの配列の(内部)構造を知ることを要求するべきではありません。

APIユーザーは尋ねるかもしれません:それは1次元だけの配列ですか?オブジェクトは多次元配列のすべてのレベルに広がっていますか?すべてのオブジェクトにアクセスするために必要なネストループ(foreachなど)はいくつありますか。どのような種類のオブジェクトがその配列に「格納」されていますか?

概説したように、その配列(オブジェクトを含む)を1次元配列として使用したいと思います。

Nishiによって概説されているように、あなたは使うことができます:

/**
 * @return SomeObj[]
 */

そのために。

ただし、注意してください。これは標準のdocblock表記ではありません。この表記法はIDEプロデューサーによって導入されました。

開発者として、 "[]"はPHPの配列に結び付けられていることを知っています。しかし、通常のPHPコンテキストで「何か[]」とはどういう意味ですか? "[]"は、 "something"内に新しい要素を作成することを意味します。新しい要素はすべてです。しかし、あなたが表現したいのは、同じ型を持つオブジェクトの配列であり、それが正確な型だということです。ご覧のとおり、IDEプロデューサーは新しいコンテキストを紹介します。あなたが学ばなければならなかった新しい文脈。他のPHP開発者が(あなたのdocblockを理解するために)学ばなければならなかった新しい状況。悪いスタイル(!).

配列には1つの次元があるので、その「オブジェクトの配列」を「リスト」と呼びたいと思うかもしれません。 "list"は他のプログラミング言語では非常に特別な意味を持つことに注意してください。たとえば「コレクション」と呼ぶ方が良いでしょう。

覚えておいてください:あなたはあなたにOOPのすべてのオプションを可能にするプログラミング言語を使います。配列ではなくクラスを使用し、クラスを配列のようにトラバース可能にします。例えば。:

class orderCollection implements ArrayIterator

あるいは、多次元配列/オブジェクト構造内の異なるレベルに内部オブジェクトを格納したい場合は、

class orderCollection implements RecursiveArrayIterator

この解決策はあなたの配列を "orderCollection"型のオブジェクトに置き換えますが、これまでのところあなたのIDE内でのコード補完を有効にしないでください。はい。次の一歩:

Docblockを使用してインターフェースによって導入されるメソッドを実装します - 特に:

/**
 * [...]
 * @return Order
 */
orderCollection::current()

/**
 * [...]
 * @return integer E.g. database identifier of the order
 */
orderCollection::key()

/**
 * [...]
 * @return Order
 */
orderCollection::offsetGet()

タイプヒントを使用することを忘れないでください。

orderCollection::append(Order $order)
orderCollection::offsetSet(Order $order)

この解決策はたくさんの導入をやめます。

/** @var $key ... */
/** @var $value ... */

zahymakaが彼女/彼の答えで確認したように、あなたのコードファイル全体(例えばループ内)。あなたのAPIユーザーは、コード補完をするためにそのdo​​cblockを導入することを強制されません。 @returnを1か所だけにすると、冗長性(@var)ができるだけ少なくなります。 "docBlocks with @var"を振りかけると、コードが読みにくくなります。

最後にこれで終わりです。達成するのは難しいですか?ハンマーでナッツを割るように見える?あなたはそのインターフェースとクリーンなコードに精通しているので、実際にはそうではありません。覚えておいてください:あなたのソースコードは一度だけ書かれている/たくさん読んでください。

IDEのコード補完がこの方法ではうまくいかない場合は、より良いもの(IntelliJ IDEA、PhpStorm、Netbeansなど)に切り替えるか、IDEのissue trackerに機能要求を提出してください。 _プロデューサー.

私のトレーナーであり、私にそのような素晴らしいものを教えてくれたChristian Weiss(ドイツ出身)に感謝します。シモンズ:XINGで私と彼に会いましょう。

12
DanielaWaranie

DanielaWaranieが彼女の答えで言及したように-$ collectionObjectの$ itemsを反復するときに$ itemのタイプを指定する方法があります:@return MyEntitiesClassNamecurrent()に追加し、残りのIteratorおよびArrayAccess--戻り値。

ブーム!foreachを介した/** @var SomeObj[] $collectionObj */では不要であり、コレクションオブジェクトで正しく動作します。@return SomeObj[]として説明されている特定のメソッドでコレクションを返す必要はありません。

すべてのIDEがサポートされているとは思わないが、PhpStormで完全に正常に動作するので、私は幸せだ。

例:

class MyCollection implements Countable, Iterator, ArrayAccess {

    /**
     * @return User
     */
    public function current() {
        return $this->items[$this->cursor];
    }

    //... implement rest of the required `interface` methods and your custom
}

この答えを投稿するのに役立つと思いました

私の場合、current()と残りのinterface--メソッドはAbstract-- collectionクラスに実装されており、最終的にどのようなエンティティがコレクションに格納されるかわかりません。

だからここにトリックがあります:抽象クラスで戻り値の型を指定せず、代わりに特定のコレクションクラスの説明でPhpDocインスツルメンテーション@methodを使用します。

例:

class User {

    function printLogin() {
        echo $this->login;
    }

}

abstract class MyCollection implements Countable, Iterator, ArrayAccess {

    protected $items = [];

    public function current() {
        return $this->items[$this->cursor];
    }

    //... implement rest of the required `interface` methods and your custom
    //... abstract methods which will be shared among child-classes
}

/**
 * @method User current()
 * ...rest of methods (for ArrayAccess) if needed
 */
class UserCollection extends MyCollection {

    function add(User $user) {
        $this->items[] = $user;
    }

    // User collection specific methods...

}

次に、クラスの使用法:

$collection = new UserCollection();
$collection->add(new User(1));
$collection->add(new User(2));
$collection->add(new User(3));

foreach ($collection as $user) {
    // IDE should `recognize` method `printLogin()` here!
    $user->printLogin();
}

もう一度:すべてのIDEがサポートしているとは思わないが、PhpStormはサポートしている。試してみて、結果をコメントで投稿してください!

5
Pavel

NetBeans 7.0(もっと低いかもしれません)では、戻り値の型 "Textオブジェクトを含む配列"を@return Textと同じように宣言でき、コードヒントが機能します。

編集: @Bob Fangerの提案で例を更新しました

/**
 * get all Tests
 *
 * @return Test|Array $tests
 */
public function getAllTexts(){
    return array(new Test(), new Test());
}

そしてそれを使うだけです:

$tests =  $controller->getAllTests();
//$tests->         //codehinting works!
//$tests[0]->      //codehinting works!

foreach($tests as $text){
    //$test->      //codehinting works!
}

それは完璧ではありませんが、それを単に "混合"のままにしておくのが良いでしょう。

CONSはあなたがエラーをスローするテキストオブジェクトとして配列を踏むことを許可されています。

5
d.raev

Zend Studioではarray[type]を使用してください。

Zend Studioでは、array[MyClass]array[int]、さらにはarray[array[MyClass]]でさえうまくいきます。

4
Erick Robertson

私はパーティーに遅れていることを知っていますが、私は最近この問題に取り組んでいます。正解であるが、受け入れられた答えがではないあなたがこれをすることができる最善の方法であるので、私は誰かがこれを見ることを望む。少なくともPHPStormには入っていませんが、NetBeansはテストしていません。

最善の方法は、ネイティブの配列型を使用するのではなく、ArrayIteratorクラスを拡張することです。これにより、インスタンスレベルではなくクラスレベルでヒントを入力できるようになります。つまり、コード全体ではなくPHPDocを1回だけ実行する必要があります(面倒でDRYに違反するだけでなく、問題となる場合もあります)。リファクタリング - PHPStormはリファクタリング時にPHPDocを見逃す習慣があります)

以下のコードを参照してください。

class MyObj
{
    private $val;
    public function __construct($val) { $this->val = $val; }
    public function getter() { return $this->val; }
}

/**
 * @method MyObj current()
 */
class MyObjCollection extends ArrayIterator
{
    public function __construct(Array $array = [])
    {
        foreach($array as $object)
        {
            if(!is_a($object, MyObj::class))
            {
                throw new Exception('Invalid object passed to ' . __METHOD__ . ', expected type ' . MyObj::class);
            }
        }
        parent::__construct($array);
    }

    public function echoContents()
    {
        foreach($this as $key => $myObj)
        {
            echo $key . ': ' . $myObj->getter() . '<br>';
        }
    }
}

$myObjCollection = new MyObjCollection([
    new MyObj(1),
    new MyObj('foo'),
    new MyObj('blah'),
    new MyObj(23),
    new MyObj(array())
]);

$myObjCollection->echoContents();

ここで重要なのは、ArrayIteratorから継承された戻り型(mixed)をオーバーライドするPHPDocの@method MyObj current()です。このPHPDocを含めることは、foreach($this as $myObj)を使用してクラスプロパティを反復処理するときに、変数$myObj->...を参照するときにコード補完を取得することを意味します。

私にとっては、これは(少なくともPHPが型付き配列を導入するまでは)これを達成するための最も良い方法です。クラスはコード全体に点在しています。

ここではArrayIteratorを拡張するための完全な解決策を示していません。そのため、この手法を使用する場合は、次のようにすることもできます。

  • offsetGet($index)next()などのメソッドには、必要に応じて他のクラスレベルのPHPDocを含めてください。
  • 健全性チェックis_a($object, MyObj::class)をコンストラクタからプライベートメソッドに移動します
  • offsetSet($index, $newval)append($value)のようなメソッドオーバーライドからこの(今は非公開の)健全性チェックを呼び出します
3
e_i_pi

問題は、@varが単一の型を表すことしかできないということです - 複雑な式は含みません。もしあなたが "array of Foo"のための構文を持っていたのに、なぜそこでやめて "array of array"のための構文を追加しないのですか?要素のリストはおそらくそれよりも一般的であることを私は理解していますが、それは滑りやすい斜面です。

個人的には、「Fooの配列」を表すために@var Foo[]を使用したことがありますが、IDEではサポートされていません。

2
troelskn
<?php foreach($this->models as /** @var Model_Object_WheelModel */ $model): ?>
    <?php
    // Type hinting now works:
    $model->getImage();
    ?>
<?php endforeach; ?>
1
Scott Hovestadt