Como posso pular argumentos opcionais em uma chamada de função?

94

OK, eu esqueci totalmente como pular argumentos no PHP.

Digamos que tenho:

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

Como eu chamaria essa função para que o parâmetro do meio receba o valor padrão (ou seja, '50')?

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

O acima está correto? Não consigo fazer isso funcionar.

Chuck Le Butt
fonte
1
@Chuck, o que você está procurando exatamente? Como uma solução alternativa ou uma classe para implementar isso ou ...?
Rizier123
@ Rizier123 Estou perguntando se a versão atual do PHP suporta pular argumentos (como outras linguagens fazem). As respostas atuais podem estar desatualizadas. Da descrição do bounty: "As respostas atuais têm cinco anos. A versão atual do PHP muda as coisas?"
Chuck Le Butt
1
@Chuck Então a resposta provavelmente será: que nada mudou; sem uma solução alternativa / código você não obterá sua funcionalidade.
Rizier123
Todas as respostas aqui parecem assumir que você tem controle sobre a função que está sendo chamada. Se essa função for parte de uma biblioteca ou estrutura, você não tem opção a não ser especificar algo para o argumento 2 se precisar do argumento 3. A única opção é procurar a função na fonte e replicar o valor padrão.
Snapey
Não é possível pular, mas você pode passar o argumento padrão usando ReflectionFunction. Eu postei essa resposta em uma pergunta semelhante.
David

Respostas:

55

Sua postagem está correta.

Infelizmente, se você precisar usar um parâmetro opcional no final da lista de parâmetros, você deve especificar tudo até o último parâmetro. Geralmente, se você deseja misturar e combinar, você dá a eles os valores padrão de ''ou nulle não os usa dentro da função se eles forem esse valor padrão.

zumbat
fonte
1
você pode dar um exemplo?
eu sou eu em
2
@iamme fazer $limit = is_numeric($limit) ? $limit : '50';no início do getData função
Kamafeather
40

Não há maneira de "pular" um argumento a não ser para especificar um padrão como falseou null.

Uma vez que o PHP carece de algum açúcar sintático quando se trata disso, você frequentemente verá algo assim:

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

Que, como eloqüentemente disse nos comentários, é usar matrizes para emular argumentos nomeados.

Isso oferece flexibilidade máxima, mas pode não ser necessário em alguns casos. No mínimo, você pode mover tudo o que acha que não é esperado na maioria das vezes para o final da lista de argumentos.

Paolo Bergantino
fonte
2
Eu identificaria 'algo assim' como 'usando matrizes para emular argumentos nomeados', FWIW. :)
caos
3
Apesar de ser mais flexível, você deve se lembrar dos parâmetros que deve / pode definir (já que eles não estão na assinatura). Portanto, no final, pode não ser tão útil quanto parece, mas é claro que isso depende do contexto.
Felix Kling
Então, quais seriam as melhores práticas para tal cenário?
Adam Grant
39

Não, não é possível ignorar argumentos dessa maneira. Você pode omitir a passagem de argumentos apenas se eles estiverem no final da lista de parâmetros.

Havia uma proposta oficial para isso: https://wiki.php.net/rfc/skipparams , que foi recusada. A página da proposta tem links para outras perguntas do OE sobre este tópico.

Cristik
fonte
Então, como as funções do PHP têm parâmetros opcionais de mais / menos?
eu sou eu em
2
PHP tem suporte para valores de parâmetro padrão. Mas esses parâmetros devem estar no final da lista.
Cristik
1
Você pode encontrar exemplos na documentação
Cristik
2
Idiotas. Me deixa tão bravo. Se você não gosta de um recurso solicitado por outra pessoa, simplesmente não o use. A internet é o pior às vezes.
Lee Saxon
@LeeSaxon, o problema é a legibilidade e a portabilidade. 1) Se alguém decidir pular parâmetros, e alguém que está depurando o código não perceber esse fato, ocorrerá uma grande confusão. 2) o novo código não poderia funcionar em versões mais antigas sem uma grande revisão e as chances de erros seriam muito altas.
Mark Walsh
6

Nada mudou em relação à capacidade de ignorar argumentos opcionais, no entanto, para a sintaxe correta e para poder especificar NULL para argumentos que desejo ignorar, é assim que eu faria:

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;
    ...
}

Isso pode ser chamado da seguinte maneira: getData('some name',NULL,'23');e qualquer pessoa que chamar a função no futuro não precisará se lembrar dos padrões todas as vezes ou da constante declarada para eles.

Ibrahim Lawal
fonte
1
Você provavelmente quer uma comparação estrita ($ limit === null)
roelleor
4

A resposta simples é não . Mas por que pular quando reorganizar os argumentos consegue isso?

O seu é um " Uso incorreto de argumentos de função padrão " e não funcionará como você espera.

Uma observação lateral da documentação do PHP:

Ao usar argumentos padrão, quaisquer padrões devem estar no lado direito de quaisquer argumentos não padrão; caso contrário, as coisas não funcionarão como esperado.

Considere o seguinte:

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

O resultado será:

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

O uso correto dos argumentos da função padrão deve ser assim:

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

O resultado será:

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

Colocar o padrão à sua direita após os não padrões garante que sempre executará novamente o valor padrão para aquela variável se não estiver definido / fornecido. Aqui está um link para referência e de onde esses exemplos vieram.

Editar: configurá-lo nullcomo os outros estão sugerindo pode funcionar e é outra alternativa, mas pode não ser o que você deseja. Ele sempre definirá o padrão como nulo se não estiver definido.

Nelson Owalo
fonte
Esta é a melhor resposta, pois fornece explicações sintáticas claramente ilustradas! Obrigado!
Christian
2

Para qualquer parâmetro ignorado (você tem que) ir com o parâmetro padrão, para estar no lado seguro.

(Se decidir por nulo, onde o parâmetro padrão é '' ou semelhante ou vice-versa, você terá problemas ...)

Frank Nocke
fonte
2

Conforme mencionado acima, você não poderá ignorar os parâmetros. Escrevi esta resposta para fornecer algum adendo, que era muito grande para incluir em um comentário.

@Frank Nocke propõe chamar a função com seus parâmetros padrão, por exemplo, tendo

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

você deveria usar

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

que funcionalmente será o mesmo que omitir os dois primeiros parâmetros ( $be $c).

Não está claro quais são os padrões (é 0digitado para fornecer o valor padrão ou é importante?).

Também existe o perigo de que o problema dos valores padrão esteja conectado a uma função externa (ou embutida), quando os valores padrão podem ser alterados pelo autor da função (ou método). Portanto, se você não mudasse sua chamada no programa, poderia mudar acidentalmente seu comportamento.

Uma solução alternativa poderia ser definir algumas constantes globais, como DEFAULT_A_Bqual seria "valor padrão do parâmetro B da função A" e "omitir" os parâmetros desta forma:

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

Para classes é mais fácil e elegante se você definir constantes de classe, porque elas fazem parte do escopo global, por exemplo.

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.

Observe que essa constante padrão é definida exatamente uma vez em todo o código (não há valor mesmo na declaração do método), portanto, no caso de algumas alterações inesperadas, você sempre fornecerá a função / método com o valor padrão correto.

A clareza desta solução é obviamente melhor do que fornecer valores padrão brutos (como NULL, 0etc.) que não dizem nada ao leitor.

(Concordo que ligar como $var = a(,,'ddd');seria a melhor opção)

Voitcus
fonte
2

Você não pode pular argumentos, mas pode usar parâmetros de array e precisa definir apenas 1 parâmetro, que é um array de parâmetros.

function myfunction($array_param)
{
    echo $array_param['name'];
    echo $array_param['age'];
    .............
}

E você pode adicionar quantos parâmetros você precisar, você não precisa defini-los. Ao chamar a função, você coloca seus parâmetros como este:

myfunction(array("name" => "Bob","age" => "18", .........));
Vlad Isoc
fonte
0

Bem, como todo mundo já disse, o que você quer não será possível em PHP sem adicionar linhas de código na função.

Mas você pode colocar este pedaço de código no topo de uma função para obter sua funcionalidade:

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

Então, basicamente debug_backtrace(), consigo o nome da função na qual este código é colocado, para então criar um novoReflectionFunction objeto e fazer um loop por todos os argumentos da função.

No loop, eu simplesmente verifico se o argumento da função é empty()AND se o argumento é "opcional" (significa que tem um valor padrão). Se sim, simplesmente atribuo o valor padrão ao argumento.

Demo

Rizier123
fonte
0

Defina o limite como nulo

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

e chamar essa função

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

se você quiser definir o limite, você pode passar como um argumento

getData('some name', 50, '23');
Hari Kumar
fonte
0

Conforme recomendado anteriormente , nada mudou. Cuidado, porém, muitos parâmetros (especialmente os opcionais) são um forte indicador do cheiro do código.

Talvez sua função esteja fazendo muito:

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

Alguns parâmetros podem ser agrupados logicamente:

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

Em último recurso, você sempre pode introduzir um objeto de parâmetro ad-hoc :

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

(que é apenas uma versão glorificada da técnica de array de parâmetros menos formal )

Às vezes, o próprio motivo para tornar um parâmetro opcional está errado. Neste exemplo, é $pagerealmente opcional? Salvar alguns personagens realmente faz diferença?

// 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 */);
RandomSeed
fonte
0

Este trecho:

    function getData ($ name, $ options) {
       $ default = array (
            'limite' => 50,
            'page' => 2,
        );
        $ args = array_merge ($ default, $ options);
        print_r ($ args);
    }

    getData ('foo', array ());
    getData ('foo', array ('limit' => 2));
    getData ('foo', array ('limit' => 10, 'page' => 10));

A resposta é :

     Array
    (
        [limite] => 50
        [página] => 2
    )
    Array
    (
        [limite] => 2
        [página] => 2
    )
    Array
    (
        [limite] => 10
        [página] => 10
    )

ninguém; dia
fonte
2
Agradável. Mas uma pequena explicação seria útil.
showdev
Boa resposta, mas se você quiser omitir o parâmetro opcional, faça desta forma: function getData ($ name, $ args = array ()) {$ defaults = array ('limit': => 50, 'page' => 2) ; $ args = array_merge ($ defaults, $ args); } Agora é possível chamar esta função apenas com o primeiro parâmetro como este: getData ('foo');
EchoSin
0

Isso é o que eu faria:

<?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 */

?>

Espero que isso ajude alguém

Michael Eugene Yuen
fonte
0

Essa é uma pergunta meio antiga com várias respostas tecnicamente competentes, mas clama por um dos padrões de design modernos em PHP: Programação Orientada a Objetos. Em vez de injetar uma coleção de tipos de dados escalares primitivos, considere o uso de um "objeto injetado" que contém todos os dados necessários para a função. http://php.net/manual/en/language.types.intro.php

O objeto injetado pode ter rotinas de validação de propriedade, etc. Se a instanciação e injeção de dados no objeto injetado não for capaz de passar toda a validação, o código pode lançar uma exceção imediatamente e o aplicativo pode evitar o processo estranho de lidar com dados potencialmente incompletos.

Podemos sugerir o tipo de objeto injetado para detectar erros antes da implantação. Algumas das idéias são resumidas neste artigo de alguns anos atrás.

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

Ray Paseur
fonte
0

Tive que fazer uma fábrica com parâmetros opcionais, minha solução alternativa foi usar o operador de coalescência nulo:

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 ?? '[email protected]';
    $subject    = $subject ?? 'Some subject';
    $message    = $message ?? 'Some message';
}

Uso:

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

Não é a coisa mais bonita, mas pode ser um bom padrão para usar em fábricas para testes.

Lucas Bustamante
fonte
-1

Experimente isso.

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

getData('some name', '', '23');
Dev Danidhariya
fonte
-2

Você não pode pular o parâmetro do meio em sua chamada de função. Mas você pode contornar isso:

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

Função:

function function_call($a, $b='50', $c){
    if(isset($b)){
        echo $b;
    }
    else{
        echo '50';
    }
}
Ronak Patel
fonte
Não sei por que você tem votos negativos, exceto $ c que também precisa de um valor padrão (você não pode ter nenhum argumento obrigatório após um argumento opcional).
Frank Forte
-2

Como @IbrahimLawal apontou. É uma prática recomendada apenas defini-los com nullvalores. Apenas verifique se o valor passado é aquele nullem que você usa seus padrões definidos.

<?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;
    ...
}
?>

Espero que isto ajude.

asp.patrickg
fonte
Você simplesmente duplicou a resposta. Em vez disso, você poderia ter votado a favor ou comentado sobre isso.
Ibrahim Lawal
-6
getData('some name');

apenas não os passe e o valor padrão será aceito

Shal
fonte