web-dev-qa-db-ja.com

タイプヒント-オブジェクトの配列を指定する

引数の型を配列として指定するにはどうすればよいですか? 「Foo」という名前のクラスがあるとします。

class Foo {}

次に、そのクラス型を引数として受け入れる関数があります。

function getFoo(Foo $f) {}

「Foo」の配列を渡すと、次のエラーが表示されます。

キャッチ可能な致命的エラー:getFoo()に渡される引数1は、配列が指定されたFooのインスタンスでなければなりません

この問題を克服する方法はありますか?たぶん

function getFoo(Foo $f[]) {}
20
Yoav Kadosh

「Fooの配列」で作業していることを確認し、メソッドが「Fooの配列」を確実に受信するようにしたい場合は、次のことができます。

class ArrayOfFoo extends \ArrayObject {
    public function offsetSet($key, $val) {
        if ($val instanceof Foo) {
            return parent::offsetSet($key, $val);
        }
        throw new \InvalidArgumentException('Value must be a Foo');
    }
}

次に:

function workWithFoo(ArrayOfFoo $foos) {
    foreach ($foos as $foo) {
        // etc.
    }
}

$foos = new ArrayOfFoos();
$foos[] = new Foo();
workWithFoo($foos);

秘訣は、「fooの配列」の新しい「タイプ」を定義し、タイプヒント保護を使用してその「タイプ」を渡すことです。


Haldayneライブラリ は、独自のロールバックを希望しない場合のメンバーシップ要件チェックのボイラープレートを処理します。

class ArrayOfFoo extends \Haldayne\Boost\MapOfObjects {
    protected function allowed($value) { return $value instanceof Foo; }
}

(完全開示、私はハルダインの作者です。)


歴史的注記: Array Of RFC はこの機能を2014年に提案しました。RFCは4年と16年で拒否されました。コンセプトは最近 内部リストに再表示 ですが、苦情は元のRFCに対して課されたものとほとんど同じです。このチェックを追加すると パフォーマンスに大きく影響します

24
bishop

少なくともPHP7で、型付き配列のヒントを実現するために、古い投稿ですが、可変個の関数と配列のアンパックを(いくつかの制限付きで)使用できます。 (以前のバージョンではテストしていません)。

例:

class Foo {
  public function test(){
    echo "foo";
  }   
};  

class Bar extends Foo {
  //override parent method
  public function test(){
    echo "bar";
  }   
}          

function test(Foo ...$params){
  foreach($params as $param){
    $param->test();
  }   
}   

$f = new Foo();
$b = new Bar();

$arrayOfFoo = [$f,$b];

test(...$arrayOfFoo);
//will output "foobar"


制限:

  1. 型付き配列を実際に渡していないので、これは技術的には解決策ではありません。代わりに、配列の展開演算子を使用します1 (関数呼び出しの「...」)配列をパラメーターのリストに変換します。各パラメーターは、可変個宣言にヒントされた型でなければなりません。2 (これも省略記号を採用しています)。

  2. 関数呼び出しの「...」は絶対に必要です(上記の場合、これは当然のことです)。電話しようとしています

    test($arrayOfFoo)
    

    上記の例のコンテキストでは、コンパイラは配列ではなくfooのパラメータを想定しているため、型エラーが発生します。 some型ヒントを保持しながら、特定の型の配列を直接渡すためのハックな解決策については、以下を参照してください。

  3. 可変個引数関数は1個の可変個引数パラメーターのみを持つことができ、最後のパラメーターでなければなりません(そうでない場合、コンパイラーは可変個引数パラメーターの終了位置と次の開始位置をどのように決定するか)、つまり、関数を次のように宣言できません

    function test(Foo ...$foos, Bar ...$bars){ 
        //...
    }
    

    または

    function test(Foo ...$foos, Bar $bar){
        //...
    }
    


各要素の代わりに、わずかに優れているだけのチェック:

次の手順は、(1)関数本体で使用されるパラメーターが正しいタイプであることを保証する限り、各要素のタイプを単にチェックするよりも優れています型チェックで関数を乱雑にすることなく、(2)通常の型例外をスローします。

考慮してください:

function alt(Array $foos){
    return (function(Foo ...$fooParams){

        //treat as regular function body

        foreach($fooParams as $foo){
            $foo->test();
        }

    })(...$foos);
}

アイデアは、即座に呼び出されたクロージャの結果を定義して返すことであり、これはすべての可変/開梱ビジネスを処理します。 (原理をさらに拡張して、この構造の関数を生成する高次関数を定義し、ボイラープレートを減らすことができます)。上記の例では:

alt($arrayOfFoo) // also outputs "foobar"

このアプローチの問題は次のとおりです。

(1)特に経験の浅い開発者にとっては、不明確かもしれません。

(2)パフォーマンスのオーバーヘッドが発生する場合があります。

(3)配列要素を内部的にチェックするだけのように、特定の型の配列のみが有効なパラメーターであることを認識するために関数宣言を検査する(または型の例外を楽しむ)必要がある限り、型チェックを実装の詳細として扱います。インターフェイスまたは抽象関数では、完全な型のヒントをエンコードできませんでした。できることは、上記の種類(または類似の種類)の実装が期待されていることをコメントすることだけです。


メモ

[1]。簡単に言うと、配列の解凍は同等にレンダリングされます

example_function($a,$b,$c);

そして

example_function(...[$a,$b,$c]);


[2]。一言で言えば、フォームの可変関数

function example_function(Foo ...$bar){
    //...
}

次のいずれかの方法で有効に呼び出すことができます。

example_function();
example_function(new Foo());
example_function(new Foo(), new Foo());
example_function(new Foo(), new Foo(), new Foo());
//and so on
13
Evan

PHPは、このように機能しません。これは、クイックアンドイージープログラミング用に作成されているため、厳密な型指定に悩まされることはありません。そのため、ヘルプがなくても動的型地獄に陥ります。 (型推論コンパイラのように)PHPインタプリタは、配列に何を入れたかについて完全に無知なので、次のようなものを検証したい場合は、すべての配列エントリを反復処理する必要があります(もちろん、PHPでは動作しません):

function bar(Foo[] $myFoos)

これは、アレイが大きくなると、パフォーマンスに大きな影響を与えます。 PHPが型付き配列のヒントを提供しない理由はそれが理由だと思います。

ここでの他の答えは、強く型付けされた配列ラッパーを作成することを示唆しています。 JavaまたはC#のような一般的な型指定を持つコンパイラーがある場合、ラッパーは問題ありませんが、PHPの場合、私は同意しません。これらのラッパーは退屈なボイラープレートコードであり、型指定された配列ごとに1つ作成する必要があります。ライブラリの配列関数を使用する場合は、型チェックの委任関数を使用してラッパーを拡張し、コードを肥大化する必要があります。これは、アカデミックサンプルで行うことができますが、多くのクラスとコレクションを持つ本番システムでは、開発者は時間がかかり、WebクラスターのMIPSが不足していますか?私はそうは思いません。

したがって、PHP=関数のシグネチャで型の検証を行うために、強く型付けされた配列ラッパーを控えます。ROIが十分に得られるとは思いません。PHPDocの優れたサポートPHP IDEはさらに役立ちます。PHPDocタグでは、Foo []表記が機能します。

一方、ラッパーは、いくつかの優れたビジネスロジックをラッパーに集中させることができれば意味があります。

PHPが厳密に型指定された配列(またはより正確には、厳密に型指定された辞書)で拡張されるときが来るかもしれません。私はそれを望みます。そして、それらを使用して、それらはない署名のヒントを提供できますあなたを罰する。

6
Rolf
function getFoo()

一般的には、次にFooと入力するaddメソッドがあります。

function addFoo( Foo $f )

したがって、ゲッターはFooの配列を返し、addメソッドは配列にFooのみが存在することを確認できます。

[〜#〜] edit [〜#〜]ゲッターから引数を削除しました。何を考えていたのかわかりません。ゲッターで引数をとる必要はありません。

[〜#〜] edit [〜#〜]完全なクラスの例を表示するだけです:

class FooBar
{
    /**
     * @return array
     */
    private $foo;

    public function getFoo()
    {
        return $foo;
    }

    public function setFoo( array $f )
    {
        $this->foo = $f;

        return $this;
    }

    public function addFoo( Foo $f )
    {
        $this->foo[] = $f;

        return $this;
    }
}

$fooFooの配列であることを確認するためのaddメソッドがあるため、一般に、おそらくそうすべきではないでしょうが、クラスで何が行われているのかを示すのに役立ちます。

3
Hayden
class Foo {
/**
 * @param Foo[] $f
 */
    function getFoo($f) {

    }
}
2
Shami