2012-10-19 74 views
6

可以說,我定義類的方法是這樣的:如何以任何順序使用PHP函數或方法參數?

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

是否有可能使用這種方法,但在不同的順序參數,就像這樣:

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

他們將具有相同的名稱,但順序不同。

我看到Symfony2可以用它的調度程序來完成它,你可以使用你想要的任何順序的參數。 鏈接:Symfony2 controller can do it

問題是,如何使這項工作? Symfony2如何調用適當的動作控制器,然後可以按照您喜歡的任何順序接受參數?

編輯: 我不能使用數組,我知道PHP不使用命名參數。但不知何故Symfony2能夠做到這一點。

+0

簡短的回答是否 – kalpaitch

+2

@kalpaitch - 好吧,symfony2做到了,所以有辦法。 – otporan

+1

看看這裏的建議:http://stackoverflow.com/questions/4697705/php-function-overloading –

回答

8

我想你是在誤解Symfony在說什麼。您不能以任何順序將參數傳遞給控制器​​操作,它必須按特定順序。然而,他們正在做的是動態的,它正在弄清楚你的路由參數在函數定義裏面的順序。

例如我們定義了一個路線:

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

在路線,參數被命名爲first_namelast_namecolor

他們在說什麼,它是沒有關係你使用的行動參數的順序。

下面的每一個是等價的:

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並不在乎你定義函數的順序,因爲它決定了順序,因爲你的路由參數必須和你的方法參數命名一樣。

希望能夠消除混淆。

+0

那麼他們如何動態地找出參數在函數定義中出現的順序呢?我不認爲這可以通過編程完成,除非文件以文本形式打開並解析。 – Mahn

+3

@Mahn他們的關鍵在於功能參數的命名必須與路由參數完全相同。然後,爲了獲得有關類,方法和參數的信息,他們可能會使用[ReflectionClass](http://php.net/reflectionclass),然後使用[ReflectionMethod](http://www.php.net/manual /en/class.reflectionmethod.php)來獲取參數的順序。 – drew010

+0

啊,這是正確的,它也可以用Reflection類完成。 – Mahn

2

不,但是您可以重載不同的方法。或者你可以嘗試用反射或智能猜測參數來做到這一點。我懷疑你可以想出一個適用於所有功能的優雅解決方案。

1

爲什麼不添加第四個變量$ 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); 
+0

有趣的想法。太糟糕了,你的代碼被破壞了。 – Zecc

+0

@Zecc,是的,對不起,我無法測試代碼,只是一個草圖。不過,我喜歡你在上面做的。 – Sablefoste

+0

謝謝。你激勵了我。我有一些空閒時間來完全調試它:) – Zecc

4

Sable Foste的想法激發了我的靈感。

您可以使用其他參數指定的順序,然後使用variable variables

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...'); 
相關問題