オブジェクトの配列に対するPHPDocタイプのヒント?


417

そのため、PHPDocでは、@varメンバー変数宣言の上にそのタイプを示すために指定できます。次に、IDEなど。PHPEdは、どのタイプのオブジェクトを使用しているかを認識し、その変数のコードに関する洞察を提供できます。

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

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

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


2
IDEは現在、アレイメンバーの種類を推定するスマート十分であることを、このNetbeansの6.8のdevのブログでいくつかの参照があります:blogs.sun.com/netbeansphp/entry/php_templates_improved
ジョン・カーター

3
@therefromhere:リンクが壊れています。新しいURLは次のとおり
netbeansphp

私のような人は、通り過ぎて答えを検索します。PHPStormを使用している場合、最も投票された答えを見てください。具体的なヒントがあります。stackoverflow.com/a/1763425/1356098(これは、たとえば、PHPEdを要求しているため、OPに対する回答であることを意味するものではありません)
Erenor Paz

回答:


337

使用する:

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

インライン変数をタイプヒンティングするとき、および

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

クラスプロパティ用。

PHPDoc(およびZend StudioやNetbeansなどのIDE)にそのオプションがなかった2009年の以前の回答:

あなたができる最善のことは、

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

私はZend Studioでそれをよく行います。他の編集者については知らないが、動作するはずだ。


3
これは理にかなっていますが、PHPEd 5.2では機能しませんでした。私がうまくいったことを思いついた唯一のことはforeach($ Objs as / ** @var Test * / $ Obj)で、これはひどく醜いです。:(
Artem Russakovskii

14
Netbeans 7では、アスタリスクが1つしかないことが重要であるように見えます— /** @var $Obj Test */動作しません。
contrebis

3
@contrebis:「@var」は有効なdocblockタグです。そのため、IDEがdocblock "/ ** ... /"内でサポートせず、 "/ ... * /"のみで "@var"をサポートする場合でも、正しいdocblockを変更しないでください。IDEのバグトラッカーに問題を提出して、IDEを標準に準拠させます。あなたの開発チーム/外部開発者/コミュニティが異なるIDEを使用していると想像してください。そのままにして、将来に備えましょう。
DanielaWaranie 2012年

181
以下を確認してください! 私はほとんどスクロールダウンしませんでした-大きな間違いだったでしょう!!! 多くのIDEはより良い構文をサポートします!(ヒント:@var Object[] $objects「$ objects」はObjectのインスタンスの配列であると言います。)
Thom Porter

10
/** @var TYPE $variable_name */正しい構文です。タイプと変数名の順序を逆にしないでください(コメントで前に提案したように)。これは、すべてのケースで機能しないためです。
srcspider 2013年

893

JetBrainsのPhpStorm IDEでは、次のように使用できます/** @var SomeObj[] */

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

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

単一の型を含むように指定されている場合、Type定義は各配列要素の型をリーダーに通知します。その場合、特定の配列の要素として期待されるのは1つのタイプのみです。

例: @return int[]


10
先週ダウンロードしたばかりで、phpstormを使用しています。Aptana(無料であることには最適です)を一気に打ち負かします。これはまさに私が探していたものです。実際、これはJavaScriptで行う方法と同じです。推測したはずです
Juan Mendes、

3
ありがとう!これはまさに私が探していたものです。PHPStormは素晴らしいです。
Erik Schierboom、2011年

5
これはNetbeansでは機能しません。がっかりしました。Jetbrainsは、いくつかの非常に優れたツールになります。
Keyo 2011

10
@fishbone @Keyoこれは現在Netbeansで動作します(少なくとも7.1ナイトリービルドでは、おそらくそれ以前)。一時変数を使用する必要があるようです(バグ?)。のヒントはforeach(getSomeObjects() as $obj)機能しませんが、機能します$objs = getSomeObjects(); foreach($objs as $obj)
John Carter

2
@var Obj[string]連想配列を持っているといいでしょう。
donquixote 2013

59

Netbeansのヒント:

ユーザークラスの配列上$users[0]->および$this->配列のコード補完を取得します。

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

また、完了時にクラスメンバーのリストで配列の型を確認できます $this->...


4
PhpStorm 9 EAPでも機能します:/ ** * @var UserInterface [] * / var $ users = []; //インターフェイスを実装するObjsの配列
Ronan

私は、NetBeans IDE 8.0.2でそれを試してみたが、それは私が現在てるクラスからの提案を得ることができます。
ウォシエッチ・ジャジンスキー

Eclipse 4.6.3でも機能します(導入されたバージョンのサポートは
IDK

残念ながら、これarray_pop()は何らかの理由でや類似の関数を使用した後は機能しません。Netbeansはこれらの関数が入力配列の単一の要素を返すことを認識していないようです。
ウィリアムW

29

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

$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必要はありません。


2
この解決策は、私の見解で受け入れられている回答よりもきれいです。これは、foreachを複数回使用でき、型ヒントが/* @var $Obj Test */毎回新しい注釈なしで機能し続けるためです。
ヘンリー

私はここで二つの問題を参照してください:1.との適切phpdocのが始まり/** 2.正しい形式は@var <data-type> <variable-name>
クリスチャン

@クリスチャン1:主な質問はphpdocではなくタイプヒンティング2:他の回答によると、正しい形式はあなたの言うとおりではありません。実際、あなたのコメントには2つの問題があり、なぜ正しい形式で自分の答えを出すことができないのか疑問に思います
Highmastdon

1.タイプヒンティングはphpdocで動作します... docblockを使用しない場合、IDEはランダムなコメントで書き込んだ内容を推測しようとしません。2.他のいくつかの回答でも述べたように、正しい形式は、上記で指定したものです。変数名の前のデータ型3.質問には別の回答は必要なく、コードを編集するだけではないので、別の回答は書きませんでした。
クリスチャン

24

PSR-5:PHPDocは、ジェネリック形式の表記法を提案しています。

構文

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

コレクションの値は、別の配列や別のコレクションであってもかまいません。

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ジェネリックスタイルのコレクション表記をサポートしているかどうかについての別の質問です。

この質問への私の答えから。



11

私は、クリーンなコードを読み書きすることを好みます。彼の信任に従うとき、開発者に(APIのユーザーとして)配列の(内部)構造を知っている必要はありません。

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

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

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

/**
 * @return SomeObj[]
 */

そのため。

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

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

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

注意: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ユーザーは、コード補完を行うために、そのdocblockを導入する必要はありません。@returnを1か所だけに置くと、冗長性(@var)ができるだけ減ります。"docBlocks with @var"を振りかけると、コードが最も読みやすくなります。

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

IDEのコード補完がこのアプローチで機能しない場合は、より優れた方法(IntelliJ IDEA、PhpStorm、Netbeansなど)に切り替えるか、IDEプロデューサーの課題追跡に機能リクエストを提出してください。

私のトレーナーであり、私にそのような素晴らしいことを教えてくれたクリスチャン・ワイス(ドイツから)に感謝します。PS:XINGで私と彼に会ってください。


これは「正しい」方法のように見えますが、Netbeansで動作させることはできません。少し例を挙げます:imgur.com/fJ9Qsro
fehrlich 2013

2
多分2012年にはこれは「標準ではありません」でしたが、現在はphpDocの組み込み機能として説明されています。
Wirone 14

@Wironeは、phpDocumentorがideプロデューサーへの反応としてこれをマニュアルに追加しているように見えます。幅広いツールをサポートしていても、それがベストプラクティスであるとは限りません。それは、数年前にrequire、require_once、include、include_onceが行われたのと同様に、SomeObj []がますます多くのプロジェクトに広がるようになりました。オートロードにより、そのステートメントの外観は5%未満に低下します。うまくいけば、SomeObj []は、上記のアプローチを支持して、今後2年間で同じレートに落ち込みます。
DanielaWaranie 14

1
なぜか分かりませんか?これは非常にシンプルで明確な表記法です。SomeObj[]あなたがそれを見るときSomeObj、それはインスタンスの2次元配列であることがわかり、それからそれをどうするかがわかります。「クリーンなコード」の信条に従わないと私は思います。
Wirone 14

これが答えになるはずです。ただし、すべてのIDEが@return <className>for current()やすべての人とのアプローチをサポートしているわけではありません。PhpStormはサポートしているので、とても役に立ちました。相棒、ありがとな!
Pavel、

5

array[type]Zend Studioで使用します。

Zendのメーカーでは、array[MyClass]またはarray[int]、あるいはarray[array[MyClass]]仕事の素晴らしいです。


5

NetBeans 7.0(これよりも低い可能性があります)では、戻り値の型「テキストオブジェクトを含む配列」を宣言することができ@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!
}

完璧ではありませんが、単に「混合」したままにしておいた方がいいので、価値はありません。

短所は、エラーをスローするテキストオブジェクトとして配列を処理できることです。


1
「@return array | Test Some description」を使用します。同じ動作をトリガーしますが、もう少し説明します。
Bob Fanger 2013年

1
これは回避策であり、解決ではありません。ここであなたが言っているのは、「この関数はタイプが「テスト」のオブジェクト、または配列を返す可能性がある」ということです。ただし、技術的にはアレイ内の内容については何も通知しません。
Byson、2015

5

DanielaWaranieが彼女の答えで述べたように-あなたは$ collectionObjectで$の項目を反復処理$アイテムの種類を指定する方法があります:追加@return MyEntitiesClassNameするcurrent()と、残りのIteratorArrayAccess-methodsその戻り値。

ブーム!/** @var SomeObj[] $collectionObj */overの必要はなくforeach、コレクションオブジェクトで正しく機能します@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クラスに実装されており、最終的にコレクションに格納されるエンティティの種類はわかりません。

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

例:

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はサポートしています。試してみて、結果をコメントで投稿してください!


それまでそれをプッシュするためのバウチャーですが、残念ながら私はコレクションを専門化して古き良きJavaジェネリック型を置き換えるように自分自身を解決することができます... yuck '
Sebas

ありがとうございました。静的メソッドをタイプヒントするにはどうすればよいですか?
Yevgeniy Afanasyev 2018

3

私はパーティーに遅れていることを知っていますが、最近この問題に取り組んでいます。受け入れられた答えは正しいですが、これを行うための最良の方法ではないため、誰かがこれを見てほしいと思います。少なくとも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();

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

私には、これが(コード化されたクラスのインスタンスではなく)反復可能クラスで反復子型を宣言しているため、これを実現する最も簡単な方法です(少なくともPHPが型付き配列を導入するまで)。

ここではArrayIteratorを拡張するための完全なソリューションを示していません。そのため、この手法を使用する場合は、次のことも実行できます。

  • offsetGet($index)およびなどのメソッドについて、必要に応じて他のクラスレベルのPHPDocを含めますnext()
  • サニティチェックis_a($object, MyObj::class)をコンストラクターからプライベートメソッドに移動する
  • offsetSet($index, $newval)and などのメソッドのオーバーライドから、この(現在はプライベート)健全性チェックを呼び出します。append($value)

とても素晴らしくてきれいな解決法!:)
MarkoŠutija19年

2

問題は@var、単一の型を表すことができるということです-複雑な式を含まない。「Fooの配列」の構文がある場合は、そこで停止して「2つのFooと3つのバーを含む配列の配列」の構文を追加しないのはなぜですか?要素のリストはおそらくそれより一般的であると理解していますが、それは滑りやすい斜面です。

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


5
C / C ++について私が気に入っている点の1つは、実際に型をこのレベルまで追跡していることです。それは滑り落ちるのにとても心地よい坂道でしょう。
ブリリアント、

2
Netbeans 7.2(少なくとも私が使用しているバージョン)でサポートされていますが、次のように少し調整されています/* @var $foo Foo[] */。それについて以下に答えを書いてください。これはforeach(){}ループ内でも使用できます
Highmastdon

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

5
どのIDEがこれをサポートしていますか?
philfreo 2010年

21
これは非常に醜いです。このようなプログラミングを開始するときに、コードをクリーンアップするために別れを告げます。
halfpastfour.am 2012

むしろ、配列の内容を定義することで私の答えを見てください:stackoverflow.com/a/14110784/431967
Highmastdon

-5

私は働いている何かを見つけました、それは命を救うことができます!

private $userList = array();
$userList = User::fetchAll(); // now $userList is an array of User objects
foreach ($userList as $user) {
   $user instanceof User;
   echo $user->getName();
}

11
唯一の問題は、実行する追加のコードが導入されることです。これは、IDEでのみ使用されます。代わりに、コメント内で型ヒントを定義する方がはるかに優れています。
ベン・ロウ、2010年

1
うわーこれは素晴らしい作品です。コードを追加することになりますが、害はないようです。私はこれから始めます:$ x instanceof Y; // typehint
Igor

3
docblockまたはインスペクションに基づいてコード補完を提供するIDEに切り替えます。IDEファイルを切り替えたくない場合は、IDEの課題追跡で機能リクエストを送信してください。
DanielaWaranie 2012年

1
型が正しくない場合に例外がスローされると、実行時の型チェックに役立ちます。もし...
リルバーディー2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.