如何在函数调用中跳过可选参数?

Posted

技术标签:

【中文标题】如何在函数调用中跳过可选参数?【英文标题】:How would I skip optional arguments in a function call? 【发布时间】:2010-11-07 04:29:55 【问题描述】:

好的,我完全忘记了如何在 php 中跳过参数。

假设我有:

function getData($name, $limit = '50', $page = '1') 
    ...

我该如何调用这个函数,以便中间参数采用默认值(即'50')?

getData('some name', '', '23');

以上是正确的吗?我似乎无法让它工作。

【问题讨论】:

@Chuck 你到底在找什么?像一个解决方法或一个类来实现这个或......? @Rizier123 我想看看当前版本的 PHP 是否支持跳过参数(就像其他语言一样)。当前的答案可能已经过时。来自赏金描述:“当前答案是五年前的。当前版本的 PHP 会改变吗?” @Chuck 那么答案可能是:什么都没有改变;没有解决方法/代码,您将无法获得您的功能。 这里的所有答案似乎都假设您可以控制被调用的函数。如果该函数是库或框架的一部分,则您别无选择,只能在需要参数 3 时为参数 2 指定某些内容。唯一的选择是查看函数的源代码并复制默认值。 无法跳过,但您可以使用ReflectionFunction 传递默认参数。我在类似的问题中发布了 answer。 【参考方案1】:

你的帖子是正确的。

不幸的是,如果您需要在参数列表的最后使用可选参数,则必须指定最后一个参数之前的所有内容。一般来说,如果你想混搭,你给它们默认值''null,如果它们是默认值,就不要在函数中使用它们。

【讨论】:

你能举个例子吗? @iamme do $limit = is_numeric($limit) ? $limit : '50';getData函数的开头【参考方案2】:

除了指定falsenull 之类的默认值外,没有其他方法可以“跳过”参数。

由于 PHP 在这方面缺乏一些语法糖,你会经常看到这样的东西:

checkbox_field(array(
    'name' => 'some name',
    ....
));

正如 cmets 中雄辩的所说,它使用数组来模拟命名参数。

这提供了最大的灵活性,但在某些情况下可能不需要。至少你可以把你认为大多数时候不期望的东西移到参数列表的末尾。

【讨论】:

我会将“类似这样的东西”标识为“使用数组来模拟命名参数”,FWIW。 :) 尽管更加灵活,但您必须记住您应该/可以设置的参数(因为它们不在签名中)。所以最终它可能没有看起来那么有用,但这当然取决于上下文。 那么对于这种情况,最佳实践是什么?【参考方案3】:

不,不能以这种方式跳过参数。如果参数位于参数列表的末尾,您可以省略传递参数。

对此有一个官方提议:https://wiki.php.net/rfc/skipparams,但被拒绝了。提案页面链接到有关此主题的其他 SO 问题。

【讨论】:

那么PHP函数如何有more/less参数可选? PHP 支持默认参数值。但是这些参数必须在列表的末尾。 您可以在PHP documentation中找到示例 混蛋。让我很生气。如果您不喜欢其他人要求的功能,请不要使用它。互联网有时是最糟糕的。 @LeeSaxon 问题在于可读性和可移植性。 1)如果有人决定跳过参数,而有人调试代码错过了这个事实,就会发生很大的混乱。 2) 如果不进行大修,新代码无法在旧版本上运行,并且出错的可能性非常高。【参考方案4】:

关于能够跳过可选参数没有任何改变,但是为了正确的语法和能够为我想跳过的参数指定 NULL,我会这样做:

define('DEFAULT_DATA_LIMIT', '50');
define('DEFAULT_DATA_PAGE', '1');

/**
 * getData
 * get a page of data 
 *
 * Parameters:
 *     name - (required) the name of data to obtain
 *     limit - (optional) send NULL to get the default limit: 50
 *     page - (optional) send NULL to get the default page: 1
 * Returns:
 *     a page of data as an array
 */

function getData($name, $limit = NULL, $page = NULL) 
    $limit = ($limit===NULL) ? DEFAULT_DATA_LIMIT : $limit;
    $page = ($page===NULL) ? DEFAULT_DATA_PAGE : $page;
    ...

这可以这样调用:getData('some name',NULL,'23'); 并且将来调用该函数的任何人都不需要每次都记住默认值或为它们声明的常量。

【讨论】:

您可能需要严格比较 ($limit===null)【参考方案5】:

简单的答案是。但是为什么要在重新排列参数实现这一点时跳过呢?

您的问题是“默认函数参数的使用不正确”,不会像您期望的那样工作。

PHP 文档的附注:

使用默认参数时,任何默认值都应位于任何非默认参数的右侧;否则,事情将无法按预期进行。

考虑以下几点:

function getData($name, $limit = '50', $page = '1') 
    return "Select * FROM books WHERE name = $name AND page = $page limit $limit";


echo getData('some name', '', '23');   // won't work as expected

输出将是:

"Select * FROM books WHERE name = some name AND page = 23 limit"

默认函数参数的正确用法应该是这样的:

function getData($name, $page = '1', $limit = '50') 
    return "Select * FROM books WHERE name = $name AND page = $page limit $limit";


echo getData('some name', '23');  // works as expected

输出将是:

"Select * FROM books WHERE name = some name AND page = 23 limit 50"

在非默认值之后将默认值放在您的右侧,以确保如果该变量未定义/未给定,它将始终重新调整该变量的默认值 这是一个link 供参考以及这些示例的来源。

编辑:将其设置为null,正如其他人所建议的那样可能会起作用,并且是另一种选择,但可能不适合您想要的。如果未定义,它将始终将默认值设置为 null。

【讨论】:

这是最好的答案,因为它提供了清晰的句法解释!谢谢!【参考方案6】:

为了安全起见,对于跳过的任何参数(您必须)使用默认参数。

(在默认参数为 '' 或类似参数的情况下设置为 null,反之亦然,这会让您陷入困境......)

【讨论】:

【参考方案7】:

如上所述,您将无法跳过参数。我写这个答案是为了提供一些附录,它太大而无法放在评论中。

@Frank Nocke proposes 使用其默认参数调用函数,例如具有

function a($b=0, $c=NULL, $d='') //...

你应该使用

$var = a(0, NULL, 'ddd'); 

这在功能上与省略前两个($b$c)参数相同。

不清楚哪些是默认值(0 是输入以提供默认值,还是很重要?)。

默认值问题也存在与外部(或内置)函数有关的危险,此时函数(或方法)作者可以更改默认值。因此,如果您不更改程序中的调用,您可能会无意中更改其行为。

一些解决方法可能是定义一些全局常量,例如DEFAULT_A_B,这将是“函数 A 的 B 参数的默认值”和“省略”参数:

$var = a(DEFAULT_A_B, DEFAULT_A_C, 'ddd');

对于类来说,如果定义类常量会更简单、更优雅,因为它们是全局范围的一部分,例如。

class MyObjectClass 
  const DEFAULT_A_B = 0;

  function a($b = self::DEFAULT_A_B)
    // method body
  
 
$obj = new MyObjectClass();
$var = $obj->a(MyObjectClass::DEFAULT_A_B); //etc.

请注意,此默认常量在整个代码中只定义一次(即使在方法声明中也没有值),因此如果发生一些意外更改,您将始终为函数/方法提供正确的默认值。

这个解决方案的清晰性当然比提供对读者没有任何意义的原始默认值(如NULL0 等)要好。

(我同意像$var = a(,,'ddd'); 这样打电话是最好的选择)

【讨论】:

【参考方案8】:

你不能跳过参数,但你可以使用数组参数,你只需要定义一个参数,这是一个参数数组。

function myfunction($array_param)

    echo $array_param['name'];
    echo $array_param['age'];
    .............

您可以添加任意数量的参数,无需定义它们。当你调用函数时,你把你的参数像这样:

myfunction(array("name" => "Bob","age" => "18", .........));

【讨论】:

【参考方案9】:

此功能在 PHP 8.0 中实现

PHP 8 引入了命名参数

其中:

允许任意跳过默认值

documentation 供参考

无需更改即可使用此功能:

让我们使用 OPs 函数function getData($name, $limit = '50', $page = '1')

用法

getData(name: 'some name', page: '23');

原生函数也会使用这个特性

htmlspecialchars($string, double_encode: false);
// Same as
htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);

支持 Netbeans IDE 12.3 功能

支持此功能,命名参数的代码完成除外,看起来更好;)

【讨论】:

【参考方案10】:

这是一个老问题,有许多技术上能胜任的答案,但它呼唤 PHP 中的一种现代设计模式:面向对象编程。与其注入原始标量数据类型的集合,不如考虑使用包含函数所需的所有数据的“注入对象”。 http://php.net/manual/en/language.types.intro.php

注入对象可能有属性验证例程等。如果向注入对象实例化和注入数据无法通过所有验证,代码可以立即抛出异常,应用程序可以避免尴尬处理可能不完整的数据的过程。

我们可以在部署前对注入的对象进行类型提示以捕获错误。这篇文章总结了几年前的一些想法。

https://www.experts-exchange.com/articles/18409/Using-Named-Parameters-in-PHP-Function-Calls.html

【讨论】:

【参考方案11】:

我必须创建一个带有可选参数的工厂,我的解决方法是使用空合并运算符:

public static function make(
    string $first_name = null,
    string $last_name = null,
    string $email = null,
    string $subject = null,
    string $message = null
) 
    $first_name = $first_name ?? 'First';
    $last_name  = $last_name ?? 'Last';
    $email      = $email ?? 'foo@bar.com';
    $subject    = $subject ?? 'Some subject';
    $message    = $message ?? 'Some message';

用法:

$factory1 = Factory::make('First Name Override');
$factory2 = Factory::make(null, 'Last Name Override');
$factory3 = Factory::make(null, null, null, null 'Message Override');

不是最漂亮的东西,但可能是在工厂中用于测试的好模式。

【讨论】:

【参考方案12】:

正如其他人已经说过的那样,如果不在函数中添加任何代码行,您想要的在 PHP 中将无法实现。

但是您可以将这段代码放在函数的顶部以获取您的功能:

foreach((new ReflectionFunction(debug_backtrace()[0]["function"]))->getParameters() as $param) 
    if(empty($$param->getName()) && $param->isOptional())
        $$param->getName() = $param->getDefaultValue();

所以基本上使用debug_backtrace(),我得到了放置此代码的函数名称,然后创建一个新的ReflectionFunction 对象并循环遍历所有函数参数。

在循环中,我只检查函数参数是否为empty() 并且参数是否为“可选”(意味着它具有默认值)。如果是,我只需将默认值分配给参数。

Demo

【讨论】:

【参考方案13】:

将限制设置为空

function getData($name, $limit = null, $page = '1') 
    ...

并调用该函数

getData('some name', null, '23');

如果你想设置限制,你可以作为参数传递

getData('some name', 50, '23');

【讨论】:

【参考方案14】:

As advised earlier,没有任何改变。 但请注意,过多的参数(尤其是可选参数)是代码异味的强烈指标。

也许你的函数做的太多了:

// first build context
$dataFetcher->setPage(1);
// $dataFetcher->setPageSize(50); // not used here
// then do the job
$dataFetcher->getData('some name');

一些参数可以按逻辑分组:

$pagination = new Pagination(1 /*, 50*/);
getData('some name', $pagination);
// Java coders will probably be familiar with this form:
getData('some name', new Pagination(1));

万不得已,您总是可以引入一个临时的parameter object:

$param = new GetDataParameter();
$param->setPage(1);
// $param->setPageSize(50); // not used here
getData($param);

(这只是不太正式的parameter array technique的美化版本)

有时,将参数设置为可选的原因是错误的。在这个例子中,$page 真的是可选的吗?保存几个字符真的有区别吗?

// dubious
// it is not obvious at first sight that a parameterless call to "getData()"
// returns only one page of data
function getData($page = 1);

// this makes more sense
function log($message, $timestamp = null /* current time by default */);

【讨论】:

【参考方案15】:

这个sn-p:

函数 getData($name, $options) $默认 = 数组( '限制' => 50, '页面' => 2, ); $args = array_merge($default, $options); print_r($args); getData('foo', 数组()); getData('foo', array('limit'=>2)); getData('foo', array('limit'=>10, 'page'=>10));

答案是:

大批 ( [限制] => 50 [页面] => 2 ) 大批 ( [限制] => 2 [页面] => 2 ) 大批 ( [限制] => 10 [页] => 10 )

【讨论】:

不错。但稍微解释一下会有所帮助。 好答案,但如果你想省略可选参数,请这样做: function getData($name, $args = array()) $defaults = array('limit':=>50 ,'页面'=>2); $args=array_merge($defaults,$args);现在可以只用这样的第一个参数调用这个函数:getData('foo');【参考方案16】:

这就是我要做的:

<?php

    function getData($name, $limit = '', $page = '1') 
            $limit = (EMPTY($limit)) ? 50 : $limit;
            $output = "name=$name&limit=$limit&page=$page";
            return $output;
    

     echo getData('table');

    /* output name=table&limit=50&page=1 */

     echo getData('table',20);

    /* name=table&limit=20&page=1 */

    echo getData('table','',5);

    /* output name=table&limit=50&page=5 */

    function getData2($name, $limit = NULL, $page = '1') 
            $limit = (ISSET($limit)) ? $limit : 50;
            $output = "name=$name&limit=$limit&page=$page";
            return $output;
    

    echo getData2('table');

    // /* output name=table&limit=50&page=1 */

    echo getData2('table',20);

    /* output name=table&limit=20&page=1 */

    echo getData2('table',NULL,3);

    /* output name=table&limit=50&page=3 */

?>

希望这会对某人有所帮助

【讨论】:

【参考方案17】:

试试这个。

function getData($name, $limit = NULL, $page = '1') 
               if (!$limit)
                 $limit = 50;
               


getData('some name', '', '23');

【讨论】:

【参考方案18】:

您不能在函数调用中跳过中间参数。但是,您可以解决这个问题:

function_call('1', '2', '3'); // Pass with parameter.
function_call('1', null, '3'); // Pass without parameter.

功能:

function function_call($a, $b='50', $c)
    if(isset($b))
        echo $b;
    
    else
        echo '50';
    

【讨论】:

我不知道你为什么投反对票,除了 $c 还需要一个默认值(在可选参数之后你不能有任何必需的参数)。【参考方案19】:

正如@IbrahimLawal 指出的那样。最好将它们设置为 null 值。只需检查传递的值是否为null,您在其中使用定义的默认值。

<?php
define('DEFAULT_LIMIT', 50);
define('DEFAULT_PAGE', 1);

function getData($name, $limit = null, $page = null) 
    $limit = is_null($limit) ? DEFAULT_LIMIT : $limit;
    $page = is_null($page) ? DEFAULT_PAGE : $page;
    ...

?>

希望这会有所帮助。

【讨论】:

您只是复制了响应。相反,您可以对其进行投票或评论。【参考方案20】:
getData('some name');

只是不传递它们,默认值将被接受

【讨论】:

以上是关于如何在函数调用中跳过可选参数?的主要内容,如果未能解决你的问题,请参考以下文章

Python调用某函数时如果想跳过某个可选参数怎么办,以OpenCV的函数resize()为例

Angular如何传递跳过一些[重复]的可选构造函数参数

C#基础 可选参数调用params无参静态构造函数

如何使用可选参数创建端点? (节点、快递、MongoDB)

在 ColdFusion 中调用函数时如何仅指定一些可选参数?

如何在打字稿中使用可选参数调用类构造函数[重复]