it-swarm-vi.com

Gõ các biến đúc trong PHP, lý do thực tế để làm điều này là gì?

PHP, như hầu hết chúng ta đều biết, có gõ yế . Đối với những người không, PHP.net nói:

PHP không yêu cầu (hoặc hỗ trợ) định nghĩa kiểu rõ ràng trong khai báo biến; một loại của biến được xác định bởi bối cảnh sử dụng biến đó.

Yêu hay ghét nó, PHP tái tạo các biến khi đang di chuyển. Vì vậy, đoạn mã sau là hợp lệ:

$var = "10";
$value = 10 + $var;
var_dump($value); // int(20)

PHP cũng cho phép bạn truyền một cách rõ ràng một biến, như vậy:

$var = "10";
$value = 10 + $var;
$value = (string)$value;
var_dump($value); // string(2) "20"

Điều đó thật tuyệt ... nhưng, đối với cuộc sống của tôi, tôi không thể hình dung ra một lý do thực tế để làm điều này.

Tôi không gặp vấn đề với việc gõ mạnh vào các ngôn ngữ hỗ trợ nó, như Java. Điều đó tốt, và tôi hoàn toàn hiểu nó. Ngoài ra, tôi biết - và hiểu đầy đủ về tính hữu ích của - gợi ý loại trong các tham số chức năng.

Vấn đề tôi có với kiểu đúc được giải thích bằng trích dẫn ở trên. Nếu PHP có thể hoán đổi các loại theo ý muốn , nó có thể làm như vậy ngay cả sau khi bạn buộc cast một loại; và nó có thể làm như vậy khi đang bay khi bạn cần một loại nhất định trong một hoạt động. Điều đó làm cho các giá trị sau:

$var = "10";
$value = (int)$var;
$value = $value . ' TaDa!';
var_dump($value); // string(8) "10 TaDa!"

Vậy quan điểm là gì?


Lấy ví dụ lý thuyết này về một thế giới nơi việc truyền kiểu do người dùng định nghĩa có ý nghĩa trong PHP :

  1. Bạn buộc ép biến $foo như int(int)$foo.
  2. Bạn cố lưu trữ một giá trị chuỗi trong biến $foo.
  3. PHP ném một ngoại lệ !! ← Điều đó sẽ có ý nghĩa. Đột nhiên, lý do cho loại người dùng xác định đúc tồn tại!

Thực tế là PHP sẽ chuyển đổi mọi thứ xung quanh khi cần thiết khiến cho việc xác định kiểu người dùng xác định kiểu mơ hồ. Ví dụ, hai mẫu mã sau đây là tương đương:

// example 1
$foo = 0;
$foo = (string)$foo;
$foo = '# of Reasons for the programmer to type cast $foo as a string: ' . $foo;

// example 2
$foo = 0;
$foo = (int)$foo;
$foo = '# of Reasons for the programmer to type cast $foo as a string: ' . $foo;

Một năm sau khi ban đầu đặt câu hỏi này, hãy đoán xem ai đã tìm thấy chính mình bằng cách sử dụng typecasting trong một môi trường thực tế? Bạn thật sự.

Yêu cầu là hiển thị giá trị tiền trên một trang web cho menu nhà hàng. Thiết kế của trang web yêu cầu các số 0 ở cuối được cắt bớt, để màn hình hiển thị trông giống như sau:

Menu Item 1 .............. $ 4
Menu Item 2 .............. $ 7.5
Menu Item 3 .............. $ 3

Cách tốt nhất tôi tìm thấy để làm lãng phí đó để biến biến thành một dấu phẩy:

$price = '7.50'; // a string from the database layer.
echo 'Menu Item 2 .............. $ ' . (float)$price;

PHP cắt các số 0 ở cuối của float, và sau đó lấy lại float như một chuỗi để nối.

45
Stephen

Trong một ngôn ngữ được gõ yếu, việc tạo kiểu tồn tại để loại bỏ sự mơ hồ trong các hoạt động được gõ, khi không thì trình biên dịch/trình thông dịch sẽ sử dụng thứ tự hoặc các quy tắc khác để đưa ra giả định sử dụng thao tác nào.

Thông thường tôi sẽ nói PHP theo mô hình này, nhưng trong số các trường hợp tôi đã kiểm tra, PHP đã hành xử ngược lại với nhau theo trực giác.

Dưới đây là những trường hợp, sử dụng JavaScript làm ngôn ngữ so sánh.

Chuỗi liên kết

Rõ ràng đây không phải là vấn đề trong PHP vì có riêng biệt nối chuỗi (.) và bổ sung (+) toán tử.

JavaScript
var a = 5;
var b = "10"
var incorrect = a + b; // "510"
var correct = a + Number(b); // 15

So sánh chuỗi

Thông thường trong các hệ thống máy tính "5" lớn hơn "10" vì nó không hiểu nó là một số. Không phải như vậy trong PHP, mà ngay cả khi cả hai là các chuỗi, nhận ra chúng là các số và loại bỏ sự cần thiết phải truyền):

JavaScript
console.log("5" > "10" ? "true" : "false"); // true
PHP
echo "5" > "10" ? "true" : "false";  // false!

Chức năng gõ chữ ký

PHP thực hiện kiểm tra kiểu xương trần trên chữ ký hàm, nhưng thật không may, nó rất thiếu sót nên có lẽ hiếm khi sử dụng được.

Tôi nghĩ rằng tôi có thể đã làm gì đó sai, nhưng một nhận xét về các tài liệ xác nhận rằng các loại tích hợp ngoài mảng không thể được sử dụng trong PHP - mặc dù thông báo lỗi là sai lệch.

PHP
function testprint(string $a) {
    echo $a;
}

$test = 5;
testprint((string)5); // "Catchable fatal error: Argument 1 passed to testprint()
                      //  must be an instance of string, string given" WTF?

Và không giống như bất kỳ ngôn ngữ nào khác mà tôi biết, ngay cả khi bạn sử dụng một loại ngôn ngữ mà nó hiểu, null không còn có thể được chuyển cho đối số đó (must be an instance of array, null given). Thật ngu ngốc.

Giải thích Boolean

[Chỉnh sửa]: Cái này mới. Tôi đã nghĩ đến một trường hợp khác và một lần nữa logic được đảo ngược từ JavaScript.

JavaScript
console.log("0" ? "true" : "false"); // True, as expected. Non-empty string.
PHP
echo "0" ? "true" : "false"; // False! This one probably causes a lot of bugs.

Vì vậy, kết luận, trường hợp hữu ích duy nhất tôi có thể nghĩ đến là ... (trống)

Kiểu cắt

Nói cách khác, khi bạn có một giá trị của một loại (nói chuỗi) và bạn muốn diễn giải nó thành một loại (int) khác và bạn muốn buộc nó trở thành một trong những giá trị hợp lệ trong loại đó:

$val = "test";
$val2 = "10";
$intval = (int)$val; // 0
$intval2 = (int)$val2; // 10
$boolval = (bool)$intval // false
$boolval2 = (bool)$intval2 // true
$props = (array)$myobject // associative array of $myobject's properties

Tôi không thể thấy những gì u ám (đến một loại bao gồm nhiều giá trị hơn) sẽ thực sự mang lại cho bạn.

Vì vậy, trong khi tôi không đồng ý với cách sử dụng gõ được đề xuất của bạn (về cơ bản bạn đang đề xuất gõ tĩnh , nhưng với sự mơ hồ chỉ khi đó là ép buộc thành một loại sẽ nó gây ra lỗi - điều này sẽ gây nhầm lẫn), tôi nghĩ đó là một câu hỏi hay, bởi vì rõ ràng việc truyền có rất ​​mục đích nhỏ trong PHP.

32
Nicole

Bạn đang trộn các khái niệm loại yếu/mạnh và động/tĩnh.

PHP yếu và năng động, nhưng vấn đề của bạn là về khái niệm kiểu động. Điều đó có nghĩa là, các biến không có loại, giá trị nào.

'Đúc kiểu' là một biểu thức tạo ra giá trị mới của một loại khác của bản gốc; nó không làm gì với biến (nếu có liên quan).

Một tình huống trong đó tôi thường xuyên nhập các giá trị truyền là trên các tham số SQL số. Bạn phải vệ sinh/thoát khỏi mọi giá trị đầu vào mà bạn chèn vào các câu lệnh SQL hoặc (tốt hơn nhiều) sử dụng các truy vấn được tham số hóa. Nhưng, nếu bạn muốn một số giá trị PHẢI là một số nguyên, việc bỏ nó sẽ dễ dàng hơn nhiều.

Xem xét:

function get_by_id ($id) {
   $id = (int)$id;
   $q = "SELECT * FROM table WHERE id=$id LIMIT 1";
   ........
}

nếu tôi rời khỏi dòng đầu tiên, $id sẽ là một vectơ dễ dàng cho SQL tiêm. Các diễn viên đảm bảo rằng đó là một số nguyên vô hại; mọi nỗ lực để chèn một số SQL sẽ chỉ dẫn đến một truy vấn cho id=0

15
Javier

Một cách sử dụng để truyền kiểu trong PHP mà tôi đã tìm thấy:

[.__.] Tôi đang phát triển một ứng dụng Android, tạo các yêu cầu http đến PHP tập lệnh trên máy chủ để truy xuất dữ liệu từ cơ sở dữ liệu. Tập lệnh lưu trữ tập lệnh dữ liệu ở dạng a PHP (hoặc mảng kết hợp) và được trả về dưới dạng đối tượng JSON cho ứng dụng. Nếu không có kiểu truyền, tôi sẽ nhận được một cái gì đó như thế này:

{ "user" : { "id" : "1", "name" : "Bob" } }

Nhưng, sử dụng PHP kiểu truyền (int) trên id của người dùng khi lưu trữ đối tượng PHP, thay vào đó, tôi nhận được điều này được trả lại cho ứng dụng:

{ "user" : { "id" : 1, "name" : "Bob" } }

Sau đó, khi đối tượng JSON được phân tích cú pháp trong ứng dụng, nó sẽ giúp tôi không phải phân tích cú pháp id thành Integer!

Xem, rất hữu ích.

4
Ryan

Một ví dụ là các đối tượng có phương thức __toString: $str = $obj->__toString(); vs $str = (string) $obj;. Việc gõ thứ hai ít hơn nhiều và phần bổ sung là dấu chấm câu, việc này sẽ mất nhiều thời gian hơn để gõ. Tôi cũng nghĩ rằng nó dễ đọc hơn, mặc dù những người khác có thể không đồng ý.

Một cái khác đang tạo một mảng phần tử đơn: array($item); vs (array) $item;. Điều này sẽ đặt bất kỳ loại vô hướng (số nguyên, tài nguyên, vv) trong một mảng.
[.__.] Một cách nóng bỏng, nếu $item là một đối tượng, các thuộc tính của nó sẽ trở thành khóa cho các giá trị của chúng. Tuy nhiên, tôi nghĩ rằng đối tượng-> chuyển đổi mảng hơi lạ: các thuộc tính riêng tư và được bảo vệ là một phần của mảng và được đổi tên. Để trích dẫn tài liệu PHP : các biến riêng có tên lớp được đặt trước tên biến; các biến được bảo vệ có '*' được đặt trước tên biến.

Một cách sử dụng khác là chuyển đổi dữ liệu GET/POST thành các loại thích hợp cho cơ sở dữ liệu. MySQL có thể tự xử lý việc này nhưng tôi nghĩ rằng các máy chủ tuân thủ ANSI hơn có thể từ chối dữ liệu. Lý do tôi chỉ đề cập đến cơ sở dữ liệu là trong hầu hết các trường hợp khác, dữ liệu sẽ có một thao tác được thực hiện trên đó theo loại của nó tại một số điểm (ví dụ: int/float thường sẽ có các phép tính được thực hiện trên chúng, v.v.).

3
Alan Pearce

Nó cũng có thể được sử dụng như một phương pháp nhanh và bẩn để đảm bảo dữ liệu không đáng tin cậy sẽ không phá vỡ thứ gì đó, ví dụ như nếu sử dụng dịch vụ từ xa có xác thực tào lao và chỉ phải chấp nhận số.

$amount = (float) $_POST['amount'];

if( $amount > 0 ){
    $remoteService->doacalculationwithanumber( $amount );    
}

Rõ ràng điều này là thiếu sót và cũng được xử lý ngầm bởi toán tử so sánh trong câu lệnh if, nhưng rất hữu ích trong việc đảm bảo bạn biết chính xác mã của bạn đang làm gì.

0
Gruffputs

Kịch bản này:

$tags = _GET['tags'];
foreach ($tags as $tag) {
    echo 'tag: ', $tag;
}

sẽ chạy tốt cho script.php?tags[]=one nhưng sẽ thất bại cho script.php?tags=one, bởi vì _GET['tags'] trả về một mảng trong trường hợp đầu tiên nhưng không trả về mảng thứ hai. Vì tập lệnh được viết để mong đợi một mảng (và bạn có ít quyền kiểm soát hơn chuỗi truy vấn được gửi đến tập lệnh), nên vấn đề có thể được giải quyết bằng cách truyền kết quả một cách thích hợp từ _GET:

$tags = (array) _GET['tags'];
foreach ($tags as $tag) {
    echo 'tag: ', $tag;
}
0
beldaz