Là khai báo các trường trên các lớp thực sự có hại trong PHP?


12

Hãy xem xét đoạn mã sau, trong đó trình thiết lập bị phá vỡ có chủ ý do lỗi lập trình trần tục mà tôi đã thực hiện một vài lần trong quá khứ:

<?php

    class TestClass {

        private $testField;

        function setField($newVal) {
            $testField = $newVal;
            // deliberately broken; should be `$this->testField = $newVal`
        }

        function getField() {
            return $this->testField;
        }

    }

    $testInstance = new TestClass();
    $testInstance->setField("Hello world!");

    // Actually prints nothing; getField() returns null
    echo $testInstance->getField(); 

?>

Việc tôi tuyên bố $testFieldở đầu lớp giúp che giấu lỗi lập trình đó khỏi tôi. Nếu tôi không khai báo trường, thì tôi sẽ nhận được một cái gì đó tương tự như cảnh báo sau được in vào nhật ký lỗi của tôi khi gọi tập lệnh này, điều này có thể có giá trị để giúp tôi gỡ lỗi - đặc biệt là nếu tôi gặp lỗi như thế này một ứng dụng thực tế lớn và phức tạp:

Thông báo PHP: Thuộc tính không xác định: TestClass :: $ testField trong /var/www/test.php trên dòng 13

Với tuyên bố, không có cảnh báo.

Có lẽ tôi đang thiếu một cái gì đó, nhưng tôi chỉ biết hai lý do để khai báo các trường lớp trong PHP: thứ nhất, rằng các khai báo đóng vai trò là tài liệu, và thứ hai, rằng không có khai báo, người ta không thể sử dụng các công cụ sửa đổi truy cập privateprotectedtruy cập, đó là được cho là hữu ích. Vì đối số sau không áp dụng cho các trường công khai - gán cho trường không được khai báo của một đối tượng làm cho nó thành công khai - nên đối với tôi, ít nhất tôi phải bình luận tất cả các khai báo trường công khai của mình. Các ý kiến ​​sẽ cung cấp cùng một giá trị tài liệu, nhưng tôi sẽ được hưởng lợi từ các cảnh báo nếu tôi cố đọc một trường chưa được khởi tạo.

Tuy nhiên, về suy nghĩ xa hơn, dường như không có ý nghĩa gì để dừng lại ở đó. Vì theo kinh nghiệm của tôi, cố gắng đọc một trường chưa được khởi tạo là nguyên nhân gây ra lỗi phổ biến hơn nhiều so với việc cố đọc hoặc sửa đổi một trường riêng tư hoặc được bảo vệ một cách không chính xác (tôi đã từng làm việc trước đây trong sự nghiệp lập trình ngắn của mình, nhưng không bao giờ là sau ), có vẻ như tôi bình luận tất cả các tuyên bố trường - không chỉ công khai - sẽ là thực tiễn tốt nhất.

Điều khiến tôi do dự là tôi chưa bao giờ thấy ai khác làm điều đó trong mã của họ. Tại sao không? Có lợi ích gì khi khai báo các trường lớp mà tôi không biết? Hoặc tôi có thể sửa đổi cấu hình của PHP theo một cách nào đó để thay đổi hành vi của khai báo trường để tôi có thể sử dụng khai báo trường thực và vẫn được hưởng lợi từ các cảnh báo "Thuộc tính không xác định"? Hoặc có bất cứ điều gì khác mà tôi đã bỏ lỡ trong phân tích của tôi?


1
Có thể cho là hữu ích ? Những lợi ích bạn nêu là vô cùng quan trọng. Tôi có lẽ sẽ thẳng thừng từ chối làm việc với mã không có khai báo thuộc tính rõ ràng.
Michael

2
Thực sự, cách để bảo vệ bạn khỏi những lỗi này là kiểm tra lỗi cẩn thận và tốt hơn, thông qua kiểm tra đơn vị.
Michael

1
có báo cáo lỗi php được xây dựng (một mức thông báo) sẽ cho bạn biết nếu bạn sử dụng một biến mà không khai báo trước.
Crayon Bạo lực

3
@MarkAmery Tôi nghĩ rằng một công ty khởi nghiệp điên cuồng là một trong những nơi quan trọng nhất để áp dụng thử nghiệm đơn vị nghiêm ngặt - vì bạn luôn thay đổi mã, nên bạn có thể luôn phá vỡ nó. Đó thực sự là mục đích chính xác của kiểm tra nghiêm ngặt và TDD thẳng thừng. Vâng, tôi đoán rằng việc sử dụng dấu gạch dưới có thể giúp bạn nhớ bạn đang truy cập vào các mục riêng tư, nhưng đối với tôi, ý tưởng về việc áp dụng các tiêu chuẩn mã hóa cụ thể để bảo vệ tôi khỏi mắc lỗi mà tôi không nên mắc phải. Thích làm TRUE === $variableđể ngăn mình vô tình giao thay vì so sánh.
Michael

1
@MarkAmery Cũng lưu ý rằng các từ khóa hiển thị được phân tích cú pháp bởi các công cụ tài liệu tự động , vì vậy chúng cung cấp nhiều "giá trị tài liệu" hơn là một nhận xét bạn sẽ đưa vào một cách thủ công.
Michael

Câu trả lời:


14

Bạn nên luôn luôn khai báo các thuộc tính lớp của bạn trước thời hạn. Mặc dù PHP là một ngôn ngữ động và sẽ vui vẻ đi cùng với bạn tạo các thuộc tính của bạn khi chạy, có một số nhược điểm đối với đường dẫn này.

  • Trình biên dịch có thể tối ưu hóa các thuộc tính khai báo. Khi bạn tự động khai báo một thuộc tính, đây là một lần thực hiện vì trình biên dịch phải tạo một bảng băm động để lưu trữ các thuộc tính động của bạn.
  • Tôi không phải là 100% trong số này nhưng tôi tin rằng các trình tối ưu hóa mã byte như APC sẽ không hữu ích vì chúng sẽ không có hình ảnh đầy đủ về lớp học của bạn. (Và tối ưu hóa như APC là phải )
  • Làm cho mã của bạn khó đọc hơn 1000 lần. Mọi người sẽ ghét bạn.
  • Làm cho nó gấp 1000 lần khả năng bạn sẽ phạm sai lầm. (Là getId hay getID? Đợi đã, bạn đã sử dụng cả hai. Fail.)
  • Không có tự động hoàn thành IDE hoặc gợi ý gõ.
  • Trình tạo tài liệu sẽ không nhìn thấy tài sản của bạn.

Vấn đề bạn mô tả thực sự là một vấn đề nhỏ khi so sánh với vấn đề không khai báo các thuộc tính của bạn trong định nghĩa lớp. Đây là một giải pháp tốt.

Làm quen với việc khai báo mặc định cho các thuộc tính của bạn.

private $testField = null;
private $testField = '';
private $testField = 0;
private $testField = []; //array()
private $testField = false;

Ngoại trừ các thuộc tính sẽ lưu trữ các đối tượng, điều này sẽ bao gồm phần lớn các loại cơ sở mà bạn sẽ lưu trữ. Các thuộc tính sẽ lưu trữ các đối tượng có thể được đặt trong hàm tạo của bạn.

Một quy tắc tốt cho thiết kế lớp là sau khi đối tượng của bạn được tạo và hàm tạo của bạn đã chạy, bạn không nên có bất kỳ thuộc tính nào ngoài đó "không xác định".


Đáp lại 5 nhược điểm của bạn: 1 (Hiệu suất): Bạn có nguồn nào cho việc này không? 2 (Khả năng đọc): Không chắc tôi hiểu; đề xuất là để bình luận các tuyên bố, không chỉ loại bỏ chúng, vậy những gì dễ đọc bị mất? Tôi đoán là cú pháp ít thân thiện và khó phân biệt với các bình luận lân cận, tôi đoán vậy? 3: Cái này tôi không hiểu gì cả; bạn có thể làm rõ? 4 (IDE): Đủ công bằng - Tôi không có kinh nghiệm mã hóa PHP bằng IDE và không biết về gợi ý kiểu của Eclipse. 5 (doc tạo): Đủ công bằng - không bao giờ sử dụng một trong số này.
Đánh dấu Amery

Tôi không thấy dòng của bạn về nhận xét chúng ra. Bất kể, những điểm trên vẫn được áp dụng - làm thế nào để bạn biết những điểm nào đang hoạt động hoặc nhận xét hợp pháp ??
Jarrod Nettles

Đáp lại giải pháp đề xuất của bạn: đây chính xác là điều tôi muốn tránh - việc gán mặc định ngay cả khi chúng vô nghĩa và không bao giờ nên sử dụng các giá trị mặc định. Vấn đề với các mặc định này (và với khai báo không có gán, chỉ gán null) là nếu, do lỗi lập trình, tôi không gán giá trị cho thứ gì đó trong hàm tạo khi tôi nên, thay vì PHP đưa ra cảnh báo khi tôi cố gắng sử dụng giá trị đó sau - giúp tôi phát hiện ra lỗi của mình - mọi thứ dường như sẽ hoạt động tốt ngay cả khi lớp bị hỏng.
Đánh dấu Amery

2
@MarkAmery Lỗi lập trình của bạn, mặc dù, về cơ bản là một lỗi đánh máy. Tất cả chúng ta đều có chúng - nhưng bạn đang cố gắng kích nổ một quả bom để giết một con ruồi ở đây.
Jarrod Nettles

Bạn có thể đúng. Hôm nay tôi đã mất vài giờ để theo dõi một lỗi mà hóa ra là do lỗi chính tả đó và đã thất vọng sau khi nhận ra rằng nếu tôi không sử dụng khai báo trường, tôi sẽ nhận được thông báo và biết ngay lỗi của tôi là gì (điều mà tôi luôn nghĩ sẽ xảy ra trong những trường hợp này; dù sao tôi cũng không nhận ra các khai báo trường khởi tạo thành null). Khả năng tiếp cận ngay lập tức của trải nghiệm đó có lẽ làm sai lệch đánh giá của tôi về khả năng nó sẽ tái diễn hoặc đáng giá như thế nào để bảo vệ chống lại.
Đánh dấu Amery

2

Tôi đã làm việc trên một số mã sử dụng các thuộc tính đối tượng được tạo động. Tôi nghĩ rằng việc sử dụng các thuộc tính đối tượng được tạo động là khá tuyệt vời (đúng, theo ý kiến ​​của tôi). Tuy nhiên, chương trình của tôi mất 7 giây để chạy. Tôi đã loại bỏ các thuộc tính đối tượng động và thay thế chúng các thuộc tính đối tượng được khai báo như một phần của mỗi lớp (công khai trong trường hợp này). Thời gian CPU đi từ hơn 7 giây đến 0.177 giây. Điều đó khá đáng kể.

Có thể là tôi đã làm điều gì đó sai trong cách tôi đang sử dụng các thuộc tính đối tượng động. Cũng có thể cấu hình của tôi bị hỏng theo một cách nào đó. Tất nhiên, tôi nên nói rằng tôi có một cấu hình PHP vanilla rất đơn giản trên máy của mình.

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.