Sử dụng đối số mặc định trong hàm


177

Tôi bối rối về các giá trị mặc định cho các hàm PHP. Nói rằng tôi có một chức năng như thế này:

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

Điều gì xảy ra nếu tôi muốn sử dụng đối số mặc định cho $ x và đặt đối số khác cho $ y?

Tôi đã thử nghiệm nhiều cách khác nhau và tôi càng bối rối hơn. Ví dụ, tôi đã thử hai:

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

Nhưng cả hai đều không dẫn đến một đối số mặc định phù hợp cho $ x. Tôi cũng đã cố gắng đặt nó theo tên biến.

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

Tôi hoàn toàn mong đợi một cái gì đó như thế này để làm việc. Nhưng nó không hoạt động như tôi mong đợi. Dường như bất kể tôi làm gì, tôi sẽ phải kết thúc bằng cách gõ các đối số mặc định, mỗi khi tôi gọi hàm. Và tôi phải thiếu một cái gì đó rõ ràng.


68
Có lẽ bạn không thiếu thứ gì, các nhà phát triển PHP có thể đã bỏ lỡ điều này.
Matti Virkkunen

Đó là một câu hỏi thú vị. Tôi chưa bao giờ thực sự cần phải làm điều này như bạn đã nói vì tôi chỉ sử dụng các đối số mặc định để mở rộng các chức năng được các lập trình viên khác thừa kế khi tôi không chắc điều gì phụ thuộc vào bản gốc. Tôi tò mò về một câu trả lời.
Kai Qing

3
Một trong những câu hỏi hay nhất tôi từng thấy trong một thời gian! Bạn đã cố gắng vượt qua không có gì? foo("blah", , "test");?
Madara's Ghost

2
Quy tắc là các đối số mặc định chỉ có thể theo các đối số. Đó là bạn có thể có foo ("blah") và x, y là mặc định hoặc foo ("Blah", "xyz") và y là mặc định.
Thức ăn cho chuột

1
Hành vi dự kiến. Xem phần "Giá trị đối số mặc định" trong: php.net/manual/en/fiances.argument.php - giá trị null và chuỗi rỗng được chấp nhận làm đối số thực tế cho hàm
jmdavalos

Câu trả lời:


164

Tôi sẽ đề xuất thay đổi khai báo hàm như sau để bạn có thể làm những gì bạn muốn:

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

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

    code here!

}

Bằng cách này, bạn có thể thực hiện một cuộc gọi như thế foo('blah', null, 'non-default y value');và để nó hoạt động như bạn muốn, trong đó tham số thứ hai $xvẫn nhận được giá trị mặc định.

Với phương thức này, truyền giá trị null có nghĩa là bạn muốn giá trị mặc định cho một tham số khi bạn muốn ghi đè giá trị mặc định cho tham số đi sau nó.

Như đã nêu trong các câu trả lời khác,

tham số mặc định chỉ hoạt động như các đối số cuối cùng cho hàm. Nếu bạn muốn khai báo các giá trị mặc định trong định nghĩa hàm, không có cách nào để bỏ qua một tham số và ghi đè lên một tham số theo sau nó.

Nếu tôi có một phương thức có thể chấp nhận số lượng tham số khác nhau và tham số của các loại khác nhau, tôi thường khai báo hàm tương tự như câu trả lời được hiển thị bởi Ryan P.

Đây là một ví dụ khác (điều này không trả lời câu hỏi của bạn, nhưng hy vọng có nhiều thông tin:

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!");
    }
}

Tại sao không $ x = isset ($ x)? $ x: 'giá trị mặc định'; ?
zloctb

@zloctb đây là một kiểm tra null sạch và dễ đọc. Có vẻ như đó là sở thích của nhà phát triển. Hoặc $ x = isset ($ x)? $ x: 'giá trị mặc định'; hoặc $ x = is_null ($ x)? 'giá trị mặc định': $ x; sẽ làm việc cũng như giải pháp này. Đó là một lựa chọn ưu tiên về khả năng đọc và bảo trì.
Nhà phát

9
Chỉ để làm rõ cho độc giả tương lai. Giải pháp đầu tiên rõ ràng làm cho không thể gán nullgiá trị thực cho tham số, vì mỗi lần nó sẽ gán giá trị mặc định. Ngay cả trong trường hợp bạn có một giá trị đặc biệt khác khi bạn thực sự muốn null (ví dụ: khi $ x == "use_null" tạo $ x = null), thì bạn sẽ không thể gán giá trị đặc biệt đó như giá trị bằng chữ của tham số (trong trường hợp này là chuỗi "use_null"). Vì vậy, chỉ cần đảm bảo sử dụng một "khóa" duy nhất, không bao giờ mong muốn khi bạn muốn sử dụng giá trị mặc định (và bạn muốn nulltrở thành một tùy chọn hợp lệ)
DiegoDD

37

Đối số tùy chọn chỉ hoạt động khi kết thúc cuộc gọi hàm. Không có cách nào để chỉ định giá trị cho $ y trong hàm của bạn mà không chỉ định $ x. Một số ngôn ngữ hỗ trợ điều này thông qua các tham số được đặt tên (ví dụ VB / C #), nhưng không phải PHP.

Bạn có thể mô phỏng điều này nếu bạn sử dụng một mảng kết hợp cho các tham số thay vì đối số - tức là

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

    ...
}

Sau đó gọi hàm như vậy:

foo(array('y' => 'my value'));

1
Điều kiện của bạn phải thực sự là isset($args['x']), vì hiện tại nó sẽ thay thế một chuỗi trống, mảng trống hoặc falsebằng giá trị mặc định.
Tim Cooper

@TimCooper lol Tôi đã thay đổi dựa trên nhận xét đầu tiên của bạn rằng đó phải là '=== null', đã thay đổi câu trả lời của tôi để sử dụng ngay lập tức, sau đó quay lại để thấy bạn cũng thay đổi nhận xét của mình. : D
Ryan P

À, chết tiệt! Tôi hoàn toàn không thích điều đó ... Ồ, bạn không thể có mọi thứ. Tôi cho rằng tôi sẽ phải suy nghĩ nhiều hơn vào thứ tự các đối số mặc định của mình sau đó. Cảm ơn!
đổi mới

Thật không may, bạn sẽ không thể gán null với câu trả lời này. Đây là một lựa chọn tốt hơn: $x = !array_key_exists('x',$args) ? 'default x value' : $args['x'];
Frank Forte

20

Nó thực sự có thể:

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

Cho dù bạn muốn làm như vậy là một câu chuyện khác :)


CẬP NHẬT:

Những lý do để tránh giải pháp này là:

  • nó (được cho là) ​​xấu xí
  • nó có một chi phí rõ ràng
  • như bằng chứng câu trả lời khác, có những lựa chọn thay thế

Nhưng nó thực sự có thể hữu ích trong các tình huống:

  • bạn không muốn / không thể thay đổi chức năng ban đầu.

  • bạn có thể thay đổi chức năng nhưng:

    • sử dụng null(hoặc tương đương) không phải là một tùy chọn (xem bình luận của DiegoDD )
    • bạn không muốn đi với một hiệp hội hoặc với func_num_args()
    • cuộc sống của bạn phụ thuộc vào việc cứu một vài LỘC

Về hiệu suất, một thử nghiệm rất đơn giản cho thấy rằng việc sử dụng API Reflection để lấy các tham số mặc định làm cho hàm gọi chậm hơn 25 lần , trong khi vẫn mất ít hơn một micro giây . Bạn nên biết nếu bạn có thể sống với điều đó.

Tất nhiên, nếu bạn muốn sử dụng nó trong một vòng lặp, bạn sẽ nhận được giá trị mặc định trước.


1
Lý do cho việc không muốn làm điều này là gì? Có vẻ khá tiện dụng.
Bryan

10
function image(array $img)
{
    $defaults = array(
        'src'    => 'cow.png',
        'alt'    => 'milk factory',
        'height' => 100,
        'width'  => 50
    );

    $img = array_merge($defaults, $img);
    /* ... */
}

2
Tui bỏ lỡ điều gì vậy? Điều này dường như không trả lời câu hỏi nào cả, nhưng nó có 7 upvote. Có lẽ thêm một chút giải thích sẽ giúp?
Sean the Bean

3
@SeantheBean: đúng, một lời giải thích sẽ tốt hơn, nhưng quan điểm của anh ấy là: vì PHP không có đối số được đặt tên, nên sử dụng một mảng kết hợp làm tham số duy nhất.
MestreLion

1
Đó là hữu ích hơn để sử dụng các ví dụ được đưa ra trong câu hỏi, ví dụ$defaults = [ 'x' => 'some value', 'y' => 'some other value'];
Frank Forte

4

Cách duy nhất tôi biết để làm điều đó là bỏ qua tham số. Cách duy nhất để bỏ qua tham số là sắp xếp lại danh sách tham số sao cho danh sách bạn muốn bỏ qua là sau các tham số mà bạn để đặt. Ví dụ:

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

Sau đó, bạn có thể gọi foo như:

foo("blah", "test");

Điều này sẽ dẫn đến:

$blah = "blah";
$y = "test";
$x = "some value";

3

Gần đây tôi có vấn đề này và tìm thấy câu hỏi và câu trả lời này. Mặc dù các câu hỏi trên hoạt động, vấn đề là chúng không hiển thị các giá trị mặc định cho các IDE hỗ trợ nó (như PHPStorm).

nhập mô tả hình ảnh ở đây

nếu bạn sử dụng, nullbạn sẽ không biết giá trị sẽ là gì nếu bạn để trống.

Giải pháp tôi thích là đặt giá trị mặc định trong định nghĩa hàm:

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

Sự khác biệt duy nhất là tôi cần đảm bảo chúng giống nhau - nhưng tôi nghĩ đó là một cái giá nhỏ để trả cho sự rõ ràng bổ sung.


Việc sử dụng nội bộ ReflectionFunction trên chính nó sẽ tiết kiệm giá!
MônDelta

2

Bạn không thể thực hiện điều này một cách trực tiếp, nhưng một ít mã sai khiến nó có thể mô phỏng.

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

  // code
}

6
Điều đó sẽ làm việc, nhưng tôi thấy việc sử dụng nulltrong trường hợp này là gọn gàng hơn về mặt khái niệm false.
TRiG

Thêm vào nhận xét của TriG, falselà một lựa chọn rủi ro hơn null, bởi vì php là một ngôn ngữ không nghiêm ngặt. !$xsẽ đúng đối với nguyên liệu đầu vào khác nhau, mà có thể gây ngạc nhiên cho lập trình viên: 0, ''. Trong khi đó null, bạn có thể sử dụng !issetnhư một bài kiểm tra chặt chẽ hơn.
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

?>

1

2 xu của tôi với toán tử hợp nhất null ?? (kể từ PHP 7)

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

Bạn có thể kiểm tra thêm ví dụ trên repl.it của tôi


0

Đây là trường hợp, khi đối tượng tốt hơn - bởi vì bạn có thể thiết lập đối tượng của mình để giữ x và y, thiết lập mặc định, v.v.

Cách tiếp cận với mảng gần để tạo đối tượng (Trong thực tế, đối tượng là bó các tham số và hàm sẽ hoạt động trên đối tượng và mảng lấy hàm sẽ hoạt động trên một số tham số ov ov)

Thực sự bạn luôn có thể thực hiện một số thủ thuật để đặt null hoặc một cái gì đó như thế này làm mặc định


0

Bạn cũng có thể kiểm tra xem bạn có một chuỗi rỗng làm đối số để bạn có thể gọi như:

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

Bên dưới chức năng:

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!

}

Không thành vấn đề nếu bạn điền nullhoặc "", bạn vẫn sẽ nhận được kết quả tương tự.


0

Truyền một mảng cho hàm, thay vì các tham số riêng lẻ và sử dụng toán tử hợp nhất null (PHP 7+).

Dưới đây, tôi đang vượt qua một mảng với 2 mục. Bên trong hàm, tôi đang kiểm tra xem giá trị cho item1 có được đặt hay không, nếu không được gán vault mặc định.

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

    function function_name ($args) {
        isset($args['item1']) ? $args['item1'] : 'default value';
    }

Bạn có thể sử dụng $args['item1'] = $args['item1']??'default value'; trong PHP 7+. nếu $ args ['item1'] là null thì default valuechuỗi sẽ gán cho $ args ['item1']
Sadee
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.