Menggunakan Argumen Default dalam suatu Fungsi

177

Saya bingung tentang nilai default untuk fungsi PHP. Katakanlah saya memiliki fungsi seperti ini:

function foo($blah, $x = "some value", $y = "some other value") {
    // code here!
}

Bagaimana jika saya ingin menggunakan argumen default untuk $ x dan menetapkan argumen lain untuk $ y?

Saya telah bereksperimen dengan berbagai cara dan saya semakin bingung. Misalnya, saya mencoba keduanya:

foo("blah", null, "test");
foo("blah", "", "test");

Tetapi keduanya tidak menghasilkan argumen default yang tepat untuk $ x. Saya juga mencoba mengaturnya dengan nama variabel.

foo("blah", $x, $y = "test");   

Saya sepenuhnya berharap sesuatu seperti ini bekerja. Tetapi itu tidak bekerja seperti yang saya harapkan sama sekali. Sepertinya apa pun yang saya lakukan, saya harus tetap mengetikkan argumen default, setiap kali saya memanggil fungsi. Dan saya pasti kehilangan sesuatu yang jelas.

renosis
sumber
68
Anda mungkin tidak melewatkan apa pun, pengembang PHP mungkin melewatkan ini.
Matti Virkkunen
Itu pertanyaan yang menarik. Saya tidak pernah benar-benar perlu melakukan ini seperti yang telah Anda nyatakan karena saya hanya menggunakan args default untuk memperluas fungsi yang diwarisi oleh programmer lain ketika saya tidak yakin apa yang tergantung pada aslinya. Saya ingin tahu tentang jawaban.
Kai Qing
3
Salah satu pertanyaan terbaik yang pernah saya lihat! Sudahkah Anda mencoba melewati apa pun? foo("blah", , "test");?
Hantu Madara
2
Aturannya adalah bahwa argumen default hanya dapat mengikuti argumen. Itu adalah Anda bisa memiliki foo ("bla") dan x, y adalah default, atau foo ("bla", "xyz") dan y adalah default.
Mouse Food
1
Perilaku yang diharapkan. Lihat bagian "Nilai argumen default" di: php.net/manual/en/functions.arguments.php - nilai nol dan string kosong diterima sebagai argumen aktual untuk fungsi tersebut
jmdavalos

Jawaban:

164

Saya akan mengusulkan mengubah deklarasi fungsi sebagai berikut sehingga Anda dapat melakukan apa yang Anda inginkan:

function foo($blah, $x = null, $y = null) {
    if (null === $x) {
        $x = "some value";
    }

    if (null === $y) {
        $y = "some other value";
    }

    code here!

}

Dengan cara ini, Anda dapat membuat panggilan seperti foo('blah', null, 'non-default y value');dan membuatnya berfungsi sesuai keinginan, di mana parameter kedua $xmasih mendapatkan nilai default.

Dengan metode ini, meneruskan nilai nol berarti Anda menginginkan nilai default untuk satu parameter ketika Anda ingin mengganti nilai default untuk parameter yang datang setelahnya.

Sebagaimana dinyatakan dalam jawaban lain,

parameter default hanya berfungsi sebagai argumen terakhir ke fungsi. Jika Anda ingin mendeklarasikan nilai default dalam definisi fungsi, tidak ada cara untuk menghilangkan satu parameter dan menimpa yang mengikutinya.

Jika saya memiliki metode yang dapat menerima berbagai jumlah parameter, dan parameter dari berbagai jenis, saya sering menyatakan fungsi yang mirip dengan jawaban yang ditunjukkan oleh Ryan P.

Ini adalah contoh lain (ini tidak menjawab pertanyaan Anda, tetapi semoga informatif:

public function __construct($params = null)
{
    if ($params instanceof SOMETHING) {
        // single parameter, of object type SOMETHING
    } else if (is_string($params)) {
        // single argument given as string
    } else if (is_array($params)) {
        // params could be an array of properties like array('x' => 'x1', 'y' => 'y1')
    } else if (func_num_args() == 3) {
        $args = func_get_args();

        // 3 parameters passed
    } else if (func_num_args() == 5) {
        $args = func_get_args();
        // 5 parameters passed
    } else {
        throw new InvalidArgumentException("Could not figure out parameters!");
    }
}
drew010
sumber
MENGAPA tidak $ x = menerbitkan ($ x)? $ x: 'nilai default'; ?
zloctb
@ zloctb ini adalah cek nol yang bersih dan dapat dibaca. Sepertinya itu adalah preferensi pengembang. Entah $ x = isset ($ x)? $ x: 'nilai default'; atau $ x = is_null ($ x)? 'nilai default': $ x; akan bekerja sebaik solusi ini. Ini adalah pilihan preferensi tentang keterbacaan dan pemeliharaan.
DeveloperWeeks
9
Hanya untuk memperjelas pembaca masa depan. Solusi pertama jelas membuat tidak mungkin untuk menetapkan nullnilai nyata ke parameter, karena setiap kali akan menetapkan nilai default. Bahkan jika Anda memiliki nilai khusus lain ketika Anda benar-benar menginginkan nol (mis. Ketika $ x == "use_null" menghasilkan $ x = null), maka Anda tidak akan dapat menetapkan nilai khusus seperti nilai literal dari parameter (dalam hal ini, string "use_null"). Jadi pastikan untuk menggunakan "kunci" unik, yang tidak pernah diinginkan untuk saat Anda ingin menggunakan nilai default (dan Anda ingin nullmenjadi opsi yang valid)
DiegoDD
37

Argumen opsional hanya berfungsi di akhir panggilan fungsi. Tidak ada cara untuk menentukan nilai untuk $ y dalam fungsi Anda tanpa juga menentukan $ x. Beberapa bahasa mendukung ini melalui parameter bernama (VB / C # misalnya), tetapi bukan PHP.

Anda bisa meniru ini jika Anda menggunakan array asosiatif untuk parameter alih-alih argumen - yaitu

function foo(array $args = array()) {
    $x = !isset($args['x']) ? 'default x value' : $args['x'];
    $y = !isset($args['y']) ? 'default y value' : $args['y'];

    ...
}

Kemudian panggil fungsi seperti ini:

foo(array('y' => 'my value'));
Ryan P
sumber
1
Kondisi Anda harus benar-benar isset($args['x']), karena saat ini akan menggantikan string kosong, array kosong, atau falsedengan nilai default.
Tim Cooper
@TimCooper lol Saya berubah berdasarkan komentar pertama Anda bahwa itu seharusnya '=== null', pergi dan ubah jawaban saya untuk menggunakan isset, kemudian kembali untuk melihat Anda mengubah komentar Anda juga. : D
Ryan P
Ah, sial! Saya tidak suka sama sekali ... Oh well, Anda tidak dapat memiliki segalanya. Saya kira saya harus lebih memikirkan urutan argumen default saya. Terima kasih!
renosis
Sayangnya, Anda tidak akan dapat menetapkan nol dengan jawaban ini. Ini adalah pilihan yang lebih baik: $x = !array_key_exists('x',$args) ? 'default x value' : $args['x'];
Frank Forte
20

Itu sebenarnya mungkin:

foo( 'blah', (new ReflectionFunction('foo'))->getParameters()[1]->getDefaultValue(), 'test');

Apakah Anda ingin melakukannya adalah cerita lain :)


MEMPERBARUI:

Alasan untuk menghindari solusi ini adalah:

  • itu (bisa dibilang) jelek
  • ini memiliki overhead yang jelas.
  • sebagai bukti jawaban lain, ada alternatif

Tapi itu sebenarnya bisa berguna dalam situasi di mana:

  • Anda tidak ingin / tidak dapat mengubah fungsi aslinya.

  • Anda bisa mengubah fungsinya tetapi:

    • menggunakan null(atau setara) bukanlah suatu opsi (lihat komentar DiegoDD )
    • Anda tidak ingin pergi dengan asosiatif atau dengan func_num_args()
    • hidup Anda tergantung pada penghematan beberapa LOC

Tentang kinerja, tes yang sangat sederhana menunjukkan bahwa menggunakan API Refleksi untuk mendapatkan parameter default membuat panggilan fungsi 25 kali lebih lambat , sementara itu masih membutuhkan waktu kurang dari satu mikrodetik . Anda harus tahu apakah Anda bisa hidup dengan itu.

Tentu saja, jika Anda bermaksud menggunakannya dalam satu lingkaran, Anda harus mendapatkan nilai default terlebih dahulu.

David
sumber
1
Apa alasan tidak ingin melakukan ini? Tampaknya cukup berguna.
Bryan
10
function image(array $img)
{
    $defaults = array(
        'src'    => 'cow.png',
        'alt'    => 'milk factory',
        'height' => 100,
        'width'  => 50
    );

    $img = array_merge($defaults, $img);
    /* ... */
}
zloctb
sumber
2
Apakah saya melewatkan sesuatu? Ini sepertinya tidak menjawab pertanyaan sama sekali, tetapi memiliki 7 upvotes. Mungkin menambahkan sedikit penjelasan akan membantu?
Sean the Bean
3
@ SeantheBean: benar, penjelasan akan lebih baik, tetapi maksudnya adalah: karena PHP tidak memiliki nama argumen, gunakan array asosiatif sebagai parameter tunggal.
MestreLion
1
Lebih bermanfaat menggunakan contoh yang diberikan dalam pertanyaan, misalnya$defaults = [ 'x' => 'some value', 'y' => 'some other value'];
Frank Forte
4

Satu-satunya cara saya tahu melakukannya adalah dengan menghilangkan parameter. Satu-satunya cara untuk menghilangkan parameter adalah dengan mengatur ulang daftar parameter sehingga yang ingin Anda hapus adalah setelah parameter yang Anda HARUS atur. Sebagai contoh:

function foo($blah, $y = "some other value", $x = "some value")

Maka Anda dapat memanggil foo seperti:

foo("blah", "test");

Ini akan menghasilkan:

$blah = "blah";
$y = "test";
$x = "some value";
Wes Crow
sumber
3

Baru-baru ini saya mengalami masalah ini dan menemukan pertanyaan dan jawaban ini. Sementara pertanyaan di atas berfungsi, masalahnya adalah mereka tidak menunjukkan nilai default ke IDE yang mendukungnya (seperti PHPStorm).

masukkan deskripsi gambar di sini

jika Anda menggunakan nullAnda tidak akan tahu apa nilainya jika Anda membiarkannya kosong.

Solusi yang saya sukai adalah dengan meletakkan nilai default dalam definisi fungsi juga:

protected function baseItemQuery(BoolQuery $boolQuery, $limit=1000, $sort = [], $offset = 0, $remove_dead=true)
{
    if ($limit===null) $limit =1000;
    if ($sort===null) $sort = [];
    if ($offset===null) $offset = 0;
    ...

Satu-satunya perbedaan adalah bahwa saya perlu memastikan mereka sama - tetapi saya pikir itu harga kecil untuk membayar kejelasan tambahan.

Yehosef
sumber
Penggunaan internal ReflectionFunction dengan sendirinya akan menghemat harga!
SubjectDelta
2

Anda tidak dapat melakukan ini secara langsung, tetapi sedikit kode mengutak-atik memungkinkan untuk ditiru.

function foo($blah, $x = false, $y = false) {
  if (!$x) $x = "some value";
  if (!$y) $y = "some other value";

  // code
}
kba
sumber
6
Itu akan berhasil, tetapi saya menemukan menggunakan nulldalam hal ini secara konseptual lebih rapi daripada false.
TRiG
Menambahkan ke komentar TriG, falseadalah pilihan yang lebih berisiko daripada null, karena php adalah bahasa yang tidak ketat. !$xakan berlaku untuk beberapa input berbeda, yang mungkin mengejutkan programmer: 0, ''. Sedangkan dengan null, Anda bisa menggunakan !issettes yang lebih ketat.
ToolmakerSteve
1
<?php
function info($name="George",$age=18) {
echo "$name is $age years old.<br>";
}
info();     // prints default values(number of values = 2)
info("Nick");   // changes first default argument from George to Nick
info("Mark",17);    // changes both default arguments' values

?>
Odin
sumber
1

2 sen saya dengan operator penggabungan nol ?? (sejak PHP 7)

function foo($blah, $x = null, $y = null) {
    $varX = $x ?? 'Default value X';
    $varY = $y ?? 'Default value Y';
    // ...
}

Anda dapat memeriksa lebih banyak contoh di repl.it saya

SandroMarques
sumber
0

Ini terjadi, ketika objek lebih baik - karena Anda dapat mengatur objek Anda untuk menahan x dan y, mengatur default dll.

Pendekatan dengan array dekat untuk membuat objek (Faktanya, objek adalah sekelompok parameter dan fungsi yang akan bekerja di atas objek, dan array yang mengambil fungsi akan bekerja pada beberapa parameter sekelompok ov)

Tentunya Anda selalu dapat melakukan beberapa trik untuk menetapkan null atau sesuatu seperti ini sebagai default

Sersan
sumber
0

Anda juga dapat memeriksa apakah Anda memiliki string kosong sebagai argumen sehingga Anda dapat memanggil seperti:

foo('blah', "", 'non-default y value', null);

Di bawah fungsi:

function foo($blah, $x = null, $y = null, $z = null) {
    if (null === $x || "" === $x) {
        $x = "some value";
    }

    if (null === $y || "" === $y) {
        $y = "some other value";
    }

    if (null === $z || "" === $z) {
        $z = "some other value";
    }

    code here!

}

Tidak masalah jika Anda mengisi nullatau "", Anda masih akan mendapatkan hasil yang sama.

Mustafa Ekici
sumber
0

Lewatkan array ke fungsi, alih-alih parameter individual dan gunakan operator penggabungan nol (PHP 7+).

Di bawah, saya melewati array dengan 2 item. Di dalam fungsi, saya memeriksa apakah nilai untuk item1 diatur, jika tidak ditetapkan sebagai default vault.

$args = ['item2' => 'item2',
        'item3' => 'value3'];

    function function_name ($args) {
        isset($args['item1']) ? $args['item1'] : 'default value';
    }
Roger
sumber
Anda bisa menggunakan $args['item1'] = $args['item1']??'default value'; PHP 7+. jika $ args ['item1'] adalah null maka default valuestring akan menetapkan ke $ args ['item1']
Sadee