6

次のようなメソッドでクラスを定義するとしましょう。

class Test {
    public function doStuff($a, $b, $c) {
    // -- Do stuff --
    }
}

このメソッドを使用することは可能ですが、次のように引数の順序が異なります:

$test = new Test();
$test->doStuff($b, $c, $a);

名前は同じですが、順序が異なるだけです。

Symfony2 はそのディスパッチャでそれを実行できるようです。引数は任意の順序で使用できます。リンク: Symfony2 コントローラーでできること

問題は、これをどのように機能させるかです。Symfony2 はどのようにして適切なアクション コントローラーを呼び出し、任意の順序で引数を受け取ることができるのでしょうか?

編集:配列を使用できません。また、php が名前付き引数を使用しないことを知っています。しかし、どういうわけか Symfony2 はそれを行うことができます。

4

5 に答える 5

8

Symfony が言っていることを誤解していると思います。コントローラー アクションに引数を任意の順序で渡すことはできません。特定の順序にする必要があります。彼らが行っていることは動的ですが、関数定義内でのルーティング パラメーターの順序を把握することです。

たとえば、ルートを定義します。

pattern:      /hello/{first_name}/{last_name}
defaults:     { _controller: AcmeHelloBundle:Hello:index, color: green }

ルートでは、パラメータの名前first_nameは 、last_name、およびcolorです。

彼らが言っているのは、アクションのパラメーターに使用する順序は問題ではないということです。

以下はそれぞれ同等です。

public function indexAction($first_name, $last_name, $color) {...}
public function indexAction($color, $last_name, $first_name) {...}
public function indexAction($last_name, $color, $first_name) {...}

引数はルート内のパラメーターと同じ名前になっているため、Symfonyは定義に基づいて引数の正しい順序を判断します。

次のアクションを手動で呼び出す場合:

public function indexAction($first_name, $last_name, $color)

その場合、引数は他の順序ではなく、そのまま渡す必要があります。$first_name, $last_name, $color異なる順序を使用すると、間違った値が引数に関連付けられます。symfony は、ルーティング パラメーターがメソッド引数と同じ名前でなければならないため、順序を決定するため、関数を定義する順序を気にしません。

うまくいけば、それで混乱が解消されます。

于 2012-10-19T20:16:35.157 に答える
4

セーブル・フォステのアイデアは私にインスピレーションを与えました。

別のパラメーターを使用して順序を指定してから、可変変数を使用できます。

function test($order, $_a, $_b, $_c){
  $order = explode(';', $order);
  ${$order[0]} = $_a;
  ${$order[1]} = $_b;
  ${$order[2]} = $_c;

  echo "a = $a; b = $b; c = $c<br>";
}


test('a;b;c', 'a', 'b', 'c');
test('b;a;c', 'b', 'a', 'c');

しかし、真剣に、配列を使用できないのはなぜですか? それが最善の方法です。


更新:これを書きました。私は本当に退屈だったに違いない。

今、私は汚いと感じています。

class FuncCaller{

    var $func;
    var $order_wanted;
    var $num_args_wanted;

    function FuncCaller( $func, $order ){
        $this->func = $func;
        if( is_set($order) ){
            // First version: receives string declaring parameters
            // We flip the order_wanted array so it maps name => index
            $this->order_wanted = array_flip( explode(';', $order) );  
            $this->num_args_wanted = count($this->order_wanted);
        } else {
            // Second version: we can do better, using reflection
            $func_reflection = new ReflectionFunction($this->func);
            $params = $func_reflection->getParameters();

            $this->num_args_wanted = func_reflection->getNumberOfParameters();
            $this->order_wanted = [];

            foreach( $params as $idx => $param_reflection ){
                $this->order_wanted[ $param_reflection->getName() ] = $idx;
            }
        }
    }


    function call(){
        if( func_num_args() <= 1 ){
            // Call without arguments
            return $this->func();
        }
        else if( func_num_args() == $this->num_args_wanted ){
            // order argument not present. Assume order is same as func signature
            $args = func_get_args();
            return call_user_func_array( $this->func, $args );
        }
        else {
            // @TODO: verify correct arguments were given
            $args_given = func_get_args();
            $order_given = explode( ';', array_shift($args_given) );
            $order_given = array_flip( $order_given );  // Map name to index
            $args_for_call = array();
            foreach( $this->order_wanted as $param_name => $idx ){
                $idx_given = $order_given[$param_name];
                $val = $args_given[ $idx_given ];
                $args_for_call[$idx] = $val;
            }
            return call_user_func_array( $this->func, $args_for_call );
        }
    }

    // This should allow calling the FuncCaller object as a function,        
    // but it wasn't working for me for some reason
    function __invoke(){
        $args = func_get_args();
        return call_user_func( $this->call, $args );
    }
}


// Let's create a function for testing:
function test( $first, $second, $third ){
    $first  = var_export($first , TRUE);
    $second = var_export($second, TRUE);
    $third  = var_export($third , TRUE);
    echo "Called test( $first, $second, $third );<br>";
}

// Now we test the first version: order of arguments is specified
$caller = new FuncCaller( test, '1st;2nd;3rd' );
$caller->call(1, 2, 3);
$caller->call('3rd;1st;2nd', 'c', 'a', 'b');
$caller->call('2nd;3rd;1st', 'Two', 'Three', 'One');
$caller->call('3rd;2nd;1st', 'Go!', 'Get Set...', 'Get Ready...');


echo "<br>";

// Now we test the second version: order of arguments is acquired by reflection
// Note we you won't be able to rename arguments this way, as we did above
$reflection_caller = new FuncCaller( test ); 
$reflection_caller->call(1, 2, 3);
$reflection_caller->call('third;first;second', 'c', 'a', 'b');
$reflection_caller->call('second;third;first', 'Two', 'Three', 'One');
$reflection_caller->call('third;second;first', 'Go!', 'Get Set...', 'Get Ready...');
于 2012-10-19T21:55:13.730 に答える
2

リフレクションを使用してコントローラーアクション関数の宣言を検査しているように感じます。次に、正しい順序で引数を使用して関数呼び出しを行います。

http://www.php.net/manual/en/class.reflectionparameter.phpをご覧ください。getName と getPosition があります。

于 2012-10-20T12:56:04.773 に答える
2

いいえ、ただし、異なる順序でメソッドをオーバーロードすることはできます。または、パラメーターについてのリフレクションまたはインテリジェントな推測でそれを行うこともできます。すべての機能で機能するエレガントなソリューションを思い付くことができるとは思えません。

于 2012-10-19T20:09:32.960 に答える
1

関数への受注を定義する 4 番目の変数 $d を追加しないでください。

class Test {
    public function doStuff($d, $a, $b, $c) {
      $v=explode(',', $d);
      foreach($v as $key => $value){
        switch ($value){
           case 'a':
             $aa = $v[a];
             break;
           case 'b':
             $bb = $v[b];
             break;
           case 'a':
             $cc = $v[c];
             break;
          }
         echo "a is $aa, b is $bb, c is $cc"; //output  
      }

// -- Do stuff --
}
}
$d= "b,c,a"; // the order of the variables
$test = new Test();
$test->doStuff($d, $b, $c, $a);
于 2012-10-19T20:23:54.693 に答える