Toán tử ternary PHP và toán tử hợp nhất null


341

Ai đó có thể giải thích sự khác biệt giữa toán tử tốc ký ternary ( ?:) và toán tử hợp nhất null ( ??) trong PHP không?

Khi nào họ cư xử khác nhau và khi nào theo cùng một cách (nếu điều đó thậm chí xảy ra)?

$a ?: $b

VS.

$a ?? $b

Câu trả lời:


343

Khi đối số đầu tiên của bạn là null, về cơ bản chúng giống nhau ngoại trừ việc kết hợp null sẽ không xuất ra E_NOTICEkhi bạn có một biến không xác định. Các tài liệu di chuyển PHP 7.0 có điều này để nói:

Toán tử hợp nhất null (??) đã được thêm vào dưới dạng đường cú pháp cho trường hợp phổ biến là cần sử dụng một ternary kết hợp với isset (). Nó trả về toán hạng đầu tiên của nó nếu nó tồn tại và không phải là NULL; nếu không, nó trả về toán hạng thứ hai của nó.

Dưới đây là một số mã ví dụ để chứng minh điều này:

<?php

$a = null;

print $a ?? 'b'; // b
print "\n";

print $a ?: 'b'; // b
print "\n";

print $c ?? 'a'; // a
print "\n";

print $c ?: 'a'; // Notice: Undefined variable: c in /in/apAIb on line 14
print "\n";

$b = array('a' => null);

print $b['a'] ?? 'd'; // d
print "\n";

print $b['a'] ?: 'd'; // d
print "\n";

print $b['c'] ?? 'e'; // e
print "\n";

print $b['c'] ?: 'e'; // Notice: Undefined index: c in /in/apAIb on line 33
print "\n";

Các dòng có thông báo là những dòng mà tôi đang sử dụng toán tử tốc độ nhanh trái ngược với toán tử liên kết null. Tuy nhiên, ngay cả với thông báo, PHP sẽ trả lời tương tự.

Thực thi mã: https://3v4l.org/McavC

Tất nhiên, điều này luôn luôn giả định là đối số đầu tiên null. Khi nó không còn là null nữa, thì cuối cùng bạn sẽ có sự khác biệt ở chỗ ??toán tử sẽ luôn trả về đối số đầu tiên trong khi tốc ?:ký sẽ chỉ khi đối số đầu tiên là trung thực và điều đó phụ thuộc vào cách PHP sẽ phân loại mọi thứ thành boolean .

Vì thế:

$a = false ?? 'f'; // false
$b = false ?: 'g'; // 'g'

sau đó sẽ có $abằng false$bbằng 'g'.


8
Mẹo: nếu bạn đã và đang sử dụng ?? thay vì ?: nhưng sau đó thấy mình cần phải làm cho mã của mình tương thích với các phiên bản PHP cũ hơn 7 (đối với một plugin cho ex), thì bạn có thể muốn trao đổi không ?? với isset ($ gì đó)? $ Something: $ Something_else ở mọi nơi trong mã của bạn. Bạn có thể dễ dàng thực hiện việc này với Notepad ++ hoặc nedit (và các trình soạn thảo khác) bằng cách sử dụng công cụ tìm / thay thế, chọn tùy chọn biểu thức chính quy và chèn vào trường tìm: "\ s * (\ S +) \ s * \? \?" và trong trường thay thế: "isset ($ 1)? $ 1:" không có dấu ngoặc kép (nedit sử dụng \ 1 thay vì $ 1). Sau đó thay thế tất cả.
Damian Green

14
Đây là câu trả lời đúng tuy nhiên kiểm tra tính trung thực là sự khác biệt lớn và cần được nhấn mạnh hơn.
mancze

2
@MasterOdin Không hài lòng với câu trả lời của bạn. Cả hai đều không giống nhau. Có kết quả khác nhau.
Tò mò

1
Đáng chú ý là bạn có thể sử dụng ?? với xích Ví dụ: $b = []; var_dump($b['a']['b']['c'] ?? 'default');hoặc với các đối tượng$b = new Foo; var_dump($b->a()->b()->c() ?? 'default');
Jack B

Xin lưu ý rằng hành vi cũng khác nhau $a = [];. Xem: 3v4l.org/iCCa0
Soullivaneuh

75

Chạy bên dưới trên chế độ tương tác php ( php -atrên thiết bị đầu cuối). Nhận xét trên mỗi dòng cho thấy kết quả.

var_dump (false ?? 'value2');   # bool(false)
var_dump (true  ?? 'value2');   # bool(true)
var_dump (null  ?? 'value2');   # string(6) "value2"
var_dump (''    ?? 'value2');   # string(0) ""
var_dump (0     ?? 'value2');   # int(0)

var_dump (false ?: 'value2');   # string(6) "value2"
var_dump (true  ?: 'value2');   # bool(true)
var_dump (null  ?: 'value2');   # string(6) "value2"
var_dump (''    ?: 'value2');   # string(6) "value2"
var_dump (0     ?: 'value2');   # string(6) "value2"

Vì vậy, đây là giải thích của tôi:

1. Toán tử hợp nhất Null - ??:

  • ??giống như một "cánh cổng" chỉ cho phép NULL đi qua .
  • Vì vậy, nó luôn trả về tham số đầu tiên , trừ khi tham số đầu tiên xảy raNULL .
  • Điều này có nghĩa ??là giống như( !isset() || is_null() )

2. Toán tử Ternary - ?:

  • ?:giống như một cánh cổng cho phép anything falsyđi qua - bao gồmNULL
  • 0, empty string, NULL, false, !isset(), empty().. bất cứ thứ gì có mùi falsy
  • Cũng giống như toán tử ternary cổ điển: echo ($x ? $x : false)
  • LƯU Ý: ?:sẽ đưa PHP NOTICEvào các biến không xác định ( unsethoặc !isset())

3. Vậy thưa bác sĩ, khi nào tôi dùng ???:..

  • Tôi chỉ nói đùa - Tôi không phải là bác sĩ và đây chỉ là một cách giải thích
  • Tôi sẽ sử dụng ?:khi
    • làm empty($x)kiểm tra
    • Hoạt động ternary cổ điển như !empty($x) ? $x : $ycó thể được rút ngắn để$x ?: $y
    • if(!$x) { fn($x); } else { fn($y); } có thể rút ngắn thành fn(($x ?: $y))
  • Tôi sẽ sử dụng ??khi
    • Tôi muốn !isset() || is_null()kiểm tra
    • ví dụ: kiểm tra nếu một đối tượng tồn tại - $object = $object ?? new objClassName();

4. Toán tử xếp chồng ...

  1. Toán tử Ternary có thể được xếp chồng lên nhau ...

    echo 0 ?: 1 ?: 2 ?: 3; //1
    echo 1 ?: 0 ?: 3 ?: 2; //1
    echo 2 ?: 1 ?: 0 ?: 3; //2
    echo 3 ?: 2 ?: 1 ?: 0; //3
    
    echo 0 ?: 1 ?: 2 ?: 3; //1
    echo 0 ?: 0 ?: 2 ?: 3; //2
    echo 0 ?: 0 ?: 0 ?: 3; //3

    Nguồn & tín dụng cho mã này

    Về cơ bản, đây là một chuỗi:

    if( truthy ) {}
    else if(truthy ) {}
    else if(truthy ) {}
    ..
    else {}
  2. Toán tử than Null có thể được xếp chồng lên nhau ...

    $v = $x ?? $y ?? $z; 

    Đây là một chuỗi:

    if(!isset($x) || is_null($x) ) {} 
    else if(!isset($y) || is_null($y) ) {}
    else {}
  3. Sử dụng xếp chồng, tôi có thể rút ngắn điều này:

    if(!isset($_GET['name'])){
       if($user_name){
          $name = $user_name;
       }else {
          $name = 'anonymous';
       }
    } else { 
       $name = $_GET['name'];
    }

    Về điều này:

    $name = $_GET['name'] ?? $user_name ?: 'anonymous';

    Thật tuyệt phải không? :-)


3
Cho đến nay câu trả lời hay nhất
Faizan Anwer Ali Rupani

69

Nếu bạn sử dụng toán tử ternary phím tắt như thế này, nó sẽ gây ra thông báo nếu $_GET['username']không được đặt:

$val = $_GET['username'] ?: 'default';

Vì vậy, thay vì bạn phải làm một cái gì đó như thế này:

$val = isset($_GET['username']) ? $_GET['username'] : 'default';

Các nhà khai thác vô coalescing tương đương với tuyên bố trên, và sẽ trở lại 'mặc định' nếu $_GET['username']không được thiết lập hoặc là null:

$val = $_GET['username'] ?? 'default';

Lưu ý rằng nó không kiểm tra tính trung thực . Nó chỉ kiểm tra nếu nó được đặt và không null.

Bạn cũng có thể làm điều này và giá trị được xác định (đặt và không null) đầu tiên sẽ được trả về:

$val = $input1 ?? $input2 ?? $input3 ?? 'default';

Bây giờ đó là một toán tử liên kết thích hợp.


42

Sự khác biệt chính là

  1. Ternary hành biểu hiện expr1 ?: expr3lợi nhuận expr1nếu expr1đánh giá lại để TRUEnhưng mặt khác Null coalescing hành biểu hiện (expr1) ?? (expr2) đánh giá lại để expr1nếu expr1không NULL

  2. Toán tử Ternary expr1 ?: expr3 phát ra một thông báo nếu giá trị bên trái (expr1) không tồn tại nhưng mặt khác, Toán tử hợp nhất Null (expr1) ?? (expr2) Đặc biệt, không phát ra thông báo nếu giá trị bên trái (expr1) không tồn tại, giống như isset().

  3. TernaryOperator là liên kết trái

    ((true ? 'true' : false) ? 't' : 'f');

    Toán tử hợp nhất Null là liên kết đúng

    ($a ?? ($b ?? $c));

Bây giờ hãy giải thích sự khác biệt giữa bằng ví dụ:

Toán tử Ternary (?:)

$x='';
$value=($x)?:'default';
var_dump($value);

// The above is identical to this if/else statement
if($x){
  $value=$x;
}
else{
  $value='default';
}
var_dump($value);

Toán tử hợp nhất Null (??)

$value=($x)??'default';
var_dump($value);

// The above is identical to this if/else statement
if(isset($x)){
  $value=$x;
}
else{
  $value='default';
}
var_dump($value);

Dưới đây là bảng giải thích sự khác biệt và tương đồng giữa '??'?:

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

Lưu ý đặc biệt: toán tử hợp nhất null và toán tử ternary là một biểu thức và nó không đánh giá một biến, nhưng là kết quả của một biểu thức. Điều này rất quan trọng để biết nếu bạn muốn trả về một biến bằng cách tham chiếu. Tuyên bố trả về $ foo ?? $ thanh; và trả lại $ var == 42? $ a: $ b; do đó, trong một hàm trả về tham chiếu sẽ không hoạt động và cảnh báo được đưa ra.


15

Cả hai đều hành xử khác nhau khi xử lý dữ liệu động.

Nếu biến trống (''), phép kết hợp null sẽ coi biến là đúng nhưng toán tử ternary viết tắt sẽ không. Và đó là điều cần phải có trong tâm trí.

$a = NULL;
$c = '';

print $a ?? '1b';
print "\n";

print $a ?: '2b';
print "\n";

print $c ?? '1d';
print "\n";

print $c ?: '2d';
print "\n";

print $e ?? '1f';
print "\n";

print $e ?: '2f';

Và đầu ra:

1b
2b

2d
1f

Notice: Undefined variable: e in /in/ZBAa1 on line 21
2f

Liên kết: https://3v4l.org/ZBAa1


Điều này rõ ràng là phản trực quan đối với PHP, trong đó một chuỗi rỗng thường được coi là sai. Tuy nhiên, nó được chỉ định rõ ràng trong các tài liệu cho ??: It returns its first operand if it exists and is not NULL; otherwise it returns its second operand.
Simon

12

Cả hai đều là tốc ký cho các biểu thức dài hơn.

?:là chữ viết tắt $a ? $a : $b. Biểu thức này sẽ ước tính thành $ a nếu $ a ước lượng thành TRUE .

??là chữ viết tắt isset($a) ? $a : $b. Biểu thức này sẽ ước tính thành $ a nếu $ a được đặt và không null.

Các trường hợp sử dụng của chúng trùng nhau khi $ a không xác định hoặc null. Khi $ a không được xác định ??sẽ không tạo ra E_NOTICE, nhưng kết quả là như nhau. Khi $ a là null thì kết quả là như nhau.


5

Dành cho người mới bắt đầu:

Toán tử hợp nhất Null (??)

Mọi thứ đều đúng ngoại trừ nullcác giá trị và không xác định (thuộc tính biến / chỉ mục / mảng)

Ví dụ:

$array = [];
$object = new stdClass();

var_export (false ?? 'second');                           # false
var_export (true  ?? 'second');                           # true
var_export (null  ?? 'second');                           # 'second'
var_export (''    ?? 'second');                           # ""
var_export ('some text'    ?? 'second');                  # "some text"
var_export (0     ?? 'second');                           # 0
var_export ($undefinedVarible ?? 'second');               # "second"
var_export ($array['undefined_index'] ?? 'second');       # "second"
var_export ($object->undefinedAttribute ?? 'second');     # "second"

về cơ bản, hãy kiểm tra biến (chỉ số mảng, thuộc tính đối tượng .. vv) có tồn tại hay không null. tương tự như issetchức năng

Viết tắt của toán tử ternary (? :)

mỗi điều sai ( false, null, 0, chuỗi rỗng) đều được đến như sai, nhưng nếu đó là không xác định nó cũng đi kèm như sai nhưng Noticesẽ ném

Ví dụ

$array = [];
$object = new stdClass();

var_export (false ?: 'second');                           # "second"
var_export (true  ?: 'second');                           # true
var_export (null  ?: 'second');                           # "second"
var_export (''    ?: 'second');                           # "second"
var_export ('some text'    ?? 'second');                  # "some text"
var_export (0     ?: 'second');                           # "second"
var_export ($undefinedVarible ?: 'second');               # "second" Notice: Undefined variable: ..
var_export ($array['undefined_index'] ?: 'second');       # "second" Notice: Undefined index: ..
var_export ($object->undefinedAttribute ?: 'second');     # "Notice: Undefined index: ..

Hi vọng điêu nay co ich


4

Cuộn xuống liên kết này và xem phần này, nó cung cấp cho bạn một ví dụ so sánh như được thấy dưới đây:

<?php
/** Fetches the value of $_GET['user'] and returns 'nobody' if it does not exist. **/
$username = $_GET['user'] ?? 'nobody';
/** This is equivalent to: **/
$username = isset($_GET['user']) ? $_GET['user'] : 'nobody';

/** Coalescing can be chained: this will return the first defined value out of $_GET['user'], $_POST['user'], and 'nobody'. **/
$username = $_GET['user'] ?? $_POST['user'] ?? 'nobody';
?>

Tuy nhiên, không nên xâu chuỗi các toán tử vì nó làm cho việc hiểu mã khó hơn khi đọc nó sau này.

Toán tử hợp nhất null (??) đã được thêm vào dưới dạng đường cú pháp cho trường hợp phổ biến là cần sử dụng một ternary kết hợp với isset (). Nó trả về toán hạng đầu tiên của nó nếu nó tồn tại và không phải là NULL; nếu không, nó trả về toán hạng thứ hai của nó.

Về cơ bản, sử dụng toán tử liên kết sẽ làm cho nó tự động kiểm tra null không giống như toán tử ternary.


1
Xin đừng xem xét việc xâu chuỗi ... thật khó để đọc / hiểu như những câu chuyện bị xiềng xích
Mark Baker

7
@MarkBaker Các ternary bị xiềng xích là khó hiểu vì PHP đã phá vỡ tính kết hợp ternary. Điều này không áp dụng cho toán tử kết hợp và imho xâu chuỗi là hoàn toàn dễ hiểu.
NikiC

7
Tôi không đồng ý. Xâu chuỗi null kết hợp là một tính năng tuyệt vời và nó không làm cho nó khó đọc nếu bạn hiểu toán tử. Nó thường được sử dụng trong javascript và một khi mọi người cảm thấy thoải mái với nó trong PHP thì cuộc gọi không sử dụng chuỗi này sẽ dừng lại. Ternaries chaining là rất khó để đọc, nhưng null hợp lại là dễ dàng. Khi bạn đọc từ trái sang phải, nó chỉ liệt kê giá trị nào sẽ được sử dụng tiếp theo.
Earl3s

2
Điều này trông rất giống với a || b || cmô hình phổ biến trong JS, ngoại trừ PHP có thể được sử dụng cho booleans ( false || 2trong JS là 2; false ?? 2trong PHP là sai)
fregante 6/12/2016

1
Tôi không đồng ý với bạn và những người khác về việc không sử dụng chuỗi. Nó giống như nói không bao giờ sử dụng cho các vòng lặp bởi vì có thể không hiểu chúng. Các nhà phát triển / lập trình viên hoàn toàn tự do sử dụng các tiêu chuẩn và thực hành mã hóa mà họ hiểu, ngay cả khi những người khác không làm như vậy. Cá nhân, tôi xem việc kết hợp chuỗi rất giống với các câu lệnh chuyển đổi. Nó trả về giá trị đầu tiên được tìm thấy (bộ) và giá trị cuối cùng nếu không tìm thấy gì.
Kurdtpage

3

Các câu trả lời khác đi sâu và đưa ra lời giải thích tuyệt vời. Đối với những người tìm kiếm câu trả lời nhanh chóng,

$a ?: 'fallback'$a ? $a : 'fallback'

trong khi

$a ?? 'fallback'$a = isset($a) ? $a : 'fallback'


Sự khác biệt chính sẽ là khi toán tử bên trái là:

  • Một giá trị falsy đó không phải là null ( 0, '', false, [], ...)
  • Một biến không xác định

Không nên có $a =sự mở rộng ở trên ??. $a ?? 'fallback' không đặt hoặc thay đổi giá trị của $ a. (Nó chỉ trả về một giá trị).
Doin

2

Dường như có những ưu và nhược điểm để sử dụng một trong hai ??hoặc ?:. Việc sử dụng chuyên nghiệp ?:là nó đánh giá sai và null và "" giống nhau. Con lừa là nó báo cáo E_NOTICE nếu đối số trước là null. Với ??pro là không có E_NOTICE, nhưng con lừa là nó không đánh giá sai và null giống nhau. Theo kinh nghiệm của tôi, tôi đã thấy mọi người bắt đầu sử dụng null và false thay thế cho nhau nhưng cuối cùng họ dùng đến việc sửa đổi mã của họ để phù hợp với việc sử dụng null hoặc false, nhưng không phải cả hai. Một cách khác là tạo ra một điều kiện ternary phức tạp hơn : (isset($something) or !$something) ? $something : $something_else.

Sau đây là một ví dụ về sự khác biệt của việc sử dụng ??toán tử bằng cả null và false:

$false = null;
$var = $false ?? "true";
echo $var . "---<br>";//returns: true---

$false = false;
$var = $false ?? "true";
echo $var . "---<br>"; //returns: ---

Tuy nhiên, bằng cách xây dựng toán tử ternary, chúng ta có thể tạo ra một chuỗi "" sai hoặc rỗng "hoạt động như thể nó là null mà không cần ném e_notice:

$false = null;
$var = (isset($false) or !$false) ? $false : "true";
echo $var . "---<br>";//returns: ---

$false = false;
$var = (isset($false) or !$false) ? $false : "true";
echo $var . "---<br>";//returns: ---

$false = "";
$var = (isset($false) or !$false) ? $false : "true";
echo $var . "---<br>";//returns: ---

$false = true;
$var = (isset($false) or !$false) ? $false : "true";
echo $var . "---<br>";//returns: 1---

Cá nhân, tôi nghĩ sẽ thật tuyệt nếu một phiên bản PHP trong tương lai có một toán tử mới khác: :?thay thế cú pháp trên. tức là: // $var = $false :? "true";Cú pháp đó sẽ đánh giá null, false và "" bằng nhau và không ném E_NOTICE ...


3
bạn có thể sử dụng $ var = $ false ?? null ?: "Chuỗi rỗng / false / null / không xác định";
RedSparr0w

Whoa ... ?? null ?:điều này là khá tuyệt vời, cảm ơn bạn. anh chàng thông minh.
Blaine Lafreniere

1
class a
{
    public $a = 'aaa';
}

$a = new a();

echo $a->a;  // Writes 'aaa'
echo $a->b;  // Notice: Undefined property: a::$b

echo $a->a ?? '$a->a does not exists';  // Writes 'aaa'

// Does not throw an error although $a->b does not exist.
echo $a->b ?? '$a->b does not exist.';  // Writes $a->b does not exist.

// Does not throw an error although $a->b and also $a->b->c does not exist.
echo $a->b->c ?? '$a->b->c does not exist.';  // Writes $a->b->c does not exist.

0

Null Coalescing operatorthực hiện chỉ hai nhiệm vụ: nó kiểm tra whether the variable is setwhether it is null. Hãy xem ví dụ sau:

<?php
# case 1:
$greeting = 'Hola';
echo $greeting ?? 'Hi There'; # outputs: 'Hola'

# case 2:
$greeting = null;
echo $greeting ?? 'Hi There'; # outputs: 'Hi There'

# case 3:
unset($greeting);
echo $greeting ?? 'Hi There'; # outputs: 'Hi There'

Ví dụ mã ở trên nói rằng Null Coalescing operatorxử lý một biến không tồn tại và một biến được đặt NULLtheo cùng một cách.

Null Coalescing operatorlà một cải tiến trên ternary operator. Hãy xem đoạn mã sau đây so sánh hai đoạn mã sau:

<?php /* example: checking for the $_POST field that goes by the name of 'fullname'*/
# in ternary operator
echo "Welcome ", (isset($_POST['fullname']) && !is_null($_POST['fullname']) ? $_POST['fullname'] : 'Mr. Whosoever.'); # outputs: Welcome Mr. Whosoever.
# in null coalecing operator
echo "Welcome ", ($_POST['fullname'] ?? 'Mr. Whosoever.'); # outputs: Welcome Mr. Whosoever.

Vì vậy, sự khác biệt giữa hai là Null Coalescing operatortoán tử được thiết kế để xử lý các biến không xác định tốt hơn so vớiternary operator . Trong khi đó, ternary operatorlà một tốc ký cho if-else.

Null Coalescing operatorkhông có nghĩa là để thay thế ternary operator, nhưng trong một số trường hợp sử dụng như trong ví dụ trên, nó cho phép bạn viết mã sạch với ít rắc rối hơn.

Tín dụng: http://dwellupper.io/post/6/php7-null-coalescing-operator-usage-and-examples


isset($_POST['fullname'])đã kiểm tra các NULLgiá trị - vì vậy && !is_null($_POST['fullname'])trong ví dụ đầu tiên là không cần thiết
Yaron U.

0

Khi sử dụng các siêu lớp như $ _GET hoặc $ _REQUEST, bạn nên biết rằng chúng có thể là một chuỗi rỗng. Trong trường hợp cụ thể này, ví dụ này

$username = $_GET['user'] ?? 'nobody';

sẽ thất bại vì giá trị của tên người dùng $ bây giờ là một chuỗi rỗng.

Vì vậy, khi sử dụng $ _GET hoặc thậm chí $ _REQUEST, bạn nên sử dụng toán tử ternary thay vì như thế này:

$username = (!empty($_GET['user'])?$_GET['user']:'nobody';

Bây giờ giá trị của tên người dùng $ là 'không ai' như mong đợi.


Nắm bắt tốt. Ngoài ra, toán tử kết hợp cũng sẽ thất bại trong trường hợp chuỗi rỗng.
Choxx
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.