12/08/2018, 15:45

PHP - 10 sai lầm mà các lập trình viên PHP thường mắc phải (Phần cuối)

Index PHP - 10 lỗi phổ biến mà các lập trình viên PHP thường mắc phải Part 1 PHP - 10 lỗi phổ biến mà các lập trình viên PHP thường mắc phải Part 2 PHP - 10 lỗi phổ biến mà các lập trình viên PHP thường mắc phải Part 3 Sai thầm thứ 7: Cho rằng $_POST sẽ luôn ...

Index

  1. PHP - 10 lỗi phổ biến mà các lập trình viên PHP thường mắc phải Part 1
  2. PHP - 10 lỗi phổ biến mà các lập trình viên PHP thường mắc phải Part 2
  3. PHP - 10 lỗi phổ biến mà các lập trình viên PHP thường mắc phải Part 3

Sai thầm thứ 7: Cho rằng $_POST sẽ luôn luôn chứa dữ liệu POST của bạn

Mặc dù với tên của nó, nhưng mảng $_POST sẽ không bao giờ chứa dữ liệu POST của bạn và có thể dễ dàng là rỗng. Để hiểu điều này, chúng ta hãy xem xét một ví dụ. Giả sử chúng ta thực hiện một request tới servcer với một lời gọi jQuery.ajax () như sau:

// js
$.ajax({
    url: 'http://my.site/some/path',
    method: 'post',
    data: JSON.stringify({a: 'a', b: 'b'}),
    contentType: 'application/json'
});

(Chúng ta gửi dữ liệu dạng JSON, khá phổ biến với các API. Đó là mặc định, ví dụ POST ở trong[ AngularJS httpservice](https://docs.angularjs.org/api/ng/service/http service](https://docs.angularjs.org/api/ng/service/httpservice](https://docs.angularjs.org/api/ng/service/http)) Ở server trong ví dụ này, chúng ta chỉ cần lấy ra mảng $_POST.

// php
var_dump($_POST);

Đáng ngạc nhiên, kết quả sẽ là:

array(0) { }

Câu trả lời là PHP chỉ phân tích cú pháp POST một cách tự động khi nó có một application/x-www-form-urlencoded hoặc multipart / form-data (PHP only parses a POST payload automatically when it has a content type of application/x-www-form-urlencoded or multipart/form-data). Những lý do cho điều này là nội dung của hai loại này về cơ bản là những cái duy nhất được sử dụng cách đây nhiều năm trước khi $ _POST của PHP được sử dụng. Vì vậy, với bất kỳ loại nội dung khác (thậm chí cả những thứ được khá phổ biến ngày nay, như /json), PHP không tự động load gói POST. Vì $ _POST là biến toàn cục, nếu chúng ta ghi đè lên một lần (tốt nhất là trong ví dụ này của chúng ta), thì giá trị đã sửa đổi (tức là bao gồm cả gói POST) sau đó sẽ có thể tham chiếu trong xuyên suốt trong code của chúng ta. Điều này rất quan trọng vì$ _POST thường được sử dụng bởi các frameworks PHP và tùy chỉnh hầu hết các tập lệnh để trích xuất và chuyển đổi dữ liệu theo yêu cầu. Ví dụ: khi xử lý gói POST với nội dung có dạng application/json, chúng ta cần phải phân tích nội dung của request (tức là, giải mã dữ liệu JSON) và ghi đè biến $ _POST theo cách thủ công như sau:

// php
$_POST = json_decode(file_get_contents('php://input'), true);

Sau đó, khi chúng ta dump mảng $ _POST, chúng ta thấy rằng nó chính xác bao gồm gói POST. Ví dụ.: array(2) { ["a"]=> string(1) "a" ["b"]=> string(1) "b" }

Sai lầm thứ 8: Nghĩ rằng PHP hỗ trợ kiểu dữ liệu ký tự (char)

Thử nhìn vào đoạn mã sau và đoán xem nó sẽ in ra sao:

for ($c = 'a'; $c <= 'z'; $c++) {
    echo $c . "
";
}

Nếu bạn trả lời là từ 'a' đến 'z', bạn có thể ngạc nhiên khi biết rằng mình đã sai.

Đúng, nó sẽ in từ 'a' đến 'z', nhưng sau đó nó cũng sẽ in từ 'aa' đến 'yz' (aa ab ac...yx yy yz). Hãy xem tại sao.

Trong PHP không có kiểu dữ liệu char; thay vào đó là string. Với suy nghĩ đó, gia tăng chuỗi z trong PHP:

$c = 'z'; echo ++$c . "
";

Kết quả: aa Tuy nhiên, để làm phức tạp hơn các vấn đề, aa có thứ tự từ điển trước z: var_export((boolean)('aa' < 'z')) . " "; Kết quả: true Đó là lý do tại sao code mẫu được trình bày ở trên in các chữ từ a đến z, nhưng sau đó cũng in ra từ aa đến yz. Nó dừng lại khi nó đạt đến za, đó là giá trị đầu tiên mà nó gặp phải mà lớn hơn z:

var_export((boolean)('za' < 'z')) . "
";
// kq: false

Với trường hợp trên thì đây là một cách để có một vòng lặp đúng in ra các giá trị từ 'a' đến 'z' trong PHP:

for ($i = ord('a'); $i <= ord('z'); $i++) {
    echo chr($i) . "
";
}

Hoặc một cách khác:

$letters = range('a', 'z');

for ($i = 0; $i < count($letters); $i++) {
    echo $letters[$i] . "
";
}

Sai lầm thứ 9: Bỏ qua các tiêu chuẩn viết code

Mặc dù bỏ qua các tiêu chuẩn code không trực tiếp dẫn đến việc cần phải debug PHP code, nhưng vẫn có thể là một trong những điều quan trọng nhất để ta có thể thảo luận ở đây. Việc bỏ qua các tiêu chuẩn code có thể gây ra nhiều vấn đề cho dự án. Nhẹ nhất, nó dẫn đến code không tương thích, nhưng tệ nhất, nó tạo làm cho code không chạy hoặc có thể khó khăn (đôi khi hầu như không thể) để điều khiển, làm cho nó rất khó để debug, refactor, maintain. Và điều đó có nghĩa là giảm năng suất cho team của bạn, rất nhiều nỗ lực bị lãng phí (hoặc ít nhất là không cần thiết). May mắn cho các PHP developer, có một tiêu chuẩn viết code PHP Standards Recommendation (PSR), bao gồm năm tiêu chuẩn sau:

PSR-0: Autoloading Standard PSR-1: Basic Coding Standard PSR-2: Coding Style Guide PSR-3: Logger Interface PSR-4: Autoloader

PSR ban đầu được tạo ra dựa trên những input của những người bảo trì các nền tảng được công nhận nhất trên thị trường: Zend, Drupal, Symfony, Joomla và những người khác cũng đóng góp cho các tiêu chuẩn này, và hiện đang theo dõi chúng. Ngay cả PEAR, đã từng cố gắng để xây dựng được một tiêu chuẩn nhiều năm trước đó, hiện tại cũng đã tham gia vào PSR.

Theo một nghĩa nào đó, nó gần như không quan trọng tiêu chuẩn mã hóa của bạn là gì, miễn là bạn đồng ý về một tiêu chuẩn và flow theo nó, nhưng nói chung PSR là một ý tưởng tốt trừ khi bạn có một số lý do thuyết phục về dự án của bạn để làm khác. Ngày càng có nhiều team và dự án phù hợp với PSR. Nó được công nhận là một tiêu chuẩn bởi phần lớn các PHP developer, vì vậy việc sử dụng nó sẽ giúp đảm bảo rằng các developer mới quen thuộc và thoải mái với tiêu chuẩn mã hóa của bạn khi họ tham gia vào nhóm của bạn.

Sai lầm thứ 10: Lạm dụng empty()

Một số PHP developer sử dụng empty() để kiểm tra đúng sai cho tất cả mọi thứ. Tuy nhiên có trường hợp, nó có thể dẫn đến sự nhầm lẫn. Đầu tiên, chúng ta hãy trở lại với array và instances của ArrayObject (đối tượng mảng). Với sự tương đồng của chúng, rất dễ dàng để giả định rằng array và ArrayObject sẽ hoạt động giống hệt nhau. Tuy nhiên, đây là một giả định nguy hiểm. Ví dụ, trong PHP 5.0:

// PHP 5.0 or later:
$array = [];
var_dump(empty($array));        // outputs bool(true) 
$array = new ArrayObject();
var_dump(empty($array));        // outputs bool(false)
// tại sao kết quả lại khác nhau ?

Cách tiếp cận này không may là khá phổ biến. Ví dụ, đây là cách Zend Db TableGateway của Zend Framework 2 trả về dữ liệu khi gọi current () trong TableGateway :: select ()như kết quả của doc gợi ý. Developer có thể dễ dàng trở thành nạn nhân của lỗi này với các dữ liệu đó.

Để tránh những vấn đề này, cách tiếp cận tốt hơn để kiểm tra các cấu trúc mảng trống là sử dụng count ():

$array = [];
var_dump(count($array));        // outputs int(0)
$array = new ArrayObject();
var_dump(count($array));        // outputs int(0)

Và thật tình cờ, kể từ khi PHP tính 0 là fasle , count ()cũng có thể được sử dụng trong if () để kiểm tra mảng rỗng. Cũng cần lưu ý rằng, trong PHP, count () là độ phức tạp liên tục (độ phức tạp O (1)) trên các mảng, làm cho nó rõ ràng là một sự lựa chọn đúng. Một ví dụ khác khi empty () có thể gây nguy hiểm là khi kết hợp nó với magic function __get (). Hãy define hai lớp và test trong cả hai. Đầu tiên chúng ta hãy định nghĩa một class Regular mà bao gồm các test như một thuộc tính bình thường:

class Regular
{
	public $test = 'value';
}

Sau đó chúng ta hãy định nghĩa một Magic class sử dụng __get () để truy cập vào thuộc tính test của nó:

class Magic
{
	private $values = ['test' => 'value'];

	public function __get($key)
	{
		if (isset($this->values[$key])) {
			return $this->values[$key];
		}
	}
}

OK, bây giờ chúng ta hãy xem những gì xảy ra khi chúng ta cố gắng để truy cập vào test trong mỗi class:

$regular = new Regular();
var_dump($regular->test);    // outputs string(4) "value"
$magic = new Magic();
var_dump($magic->test);      // outputs string(4) "value"

Ok. Nhưng bây giờ chúng ta hãy xem những gì sẽ xảy ra khi chúng ta gọi empty () trên mỗi đoạn sau:

var_dump(empty($regular->test));    // outputs bool(false)
var_dump(empty($magic->test));      // outputs bool(true)

Vì vậy, nếu chúng ta dựa vào empty (), chúng ta có thể bị hiểu nhầm rằng test của $magic là empty, trong khi thực tế nó được đặt thành 'value'. Thật không may, nếu một class sử dụng hàm __get () để truy cập giá trị của thuộc tính, không có cách nào để kiểm tra xem giá trị thuộc tính đó có rỗng hay không. Bên ngoài phạm vi của class, bạn chỉ có thể kiểm tra nếu một giá trị null thì sẽ được trả về, và điều đó có nghĩa là không nhất thiết key tương ứng không được thiết lập, vì thực sự nó có thể đã được set thành null.

Ngược lại, nếu chúng ta cố gắng để tham khảo một non-existent property của class Regular thông thường, chúng ta sẽ nhận được một thông báo tương tự như sau:

Notice: Undefined property: Regular::$nonExistantTest in /path/to/test.php on line 10

Call Stack:
    0.0012     234704   1. {main}() /path/to/test.php:0

Vì vậy, điểm mấu chốt ở đây là nên cẩn thận khi sử dụng empty vì nó có thể gây nhầm lẫn - hoặc thậm chí có thể gây hiểu lầm - sai kết quả, nếu không cẩn thận.

Kết thúc chuỗi bài viết hy vọng các bạn có thể chú ý hơn đến những vấn đề trong PHP, để tránh những sai lầm không đáng có. Bài viết còn nhiều thiếu sót, mong các bạn ghóp ý

0