Czy deklarowanie pól w klasach jest rzeczywiście szkodliwe w PHP?

13

Rozważ następujący kod, w którym seter jest celowo uszkodzony z powodu przyziemnego błędu programistycznego, który popełniłem naprawdę kilka razy w przeszłości:

<?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(); 

?>

Fakt, że zadeklarowałem $testFieldna szczycie klasy, pomaga mi ukryć ten błąd programowy. Gdybym nie zadeklarował tego pola, dostałbym coś podobnego do następującego ostrzeżenia wydrukowanego w moim dzienniku błędów podczas wywoływania tego skryptu, co potencjalnie byłoby cenne dla mojej pomocy w debugowaniu - szczególnie gdybym popełnił taki błąd w duża i skomplikowana aplikacja w świecie rzeczywistym:

Wskazówka PHP: Niezdefiniowana właściwość: TestClass :: $ testField w /var/www/test.php w linii 13

W deklaracji nie ma ostrzeżenia.

Być może coś mi brakuje, ale zdaję sobie sprawę z tylko dwóch powodów, aby zadeklarować pola klas w PHP: po pierwsze, że deklaracje działają jako dokumentacja, a po drugie, że bez deklaracji nie można używać modyfikatorów privatei protecteddostępu, które są prawdopodobnie użyteczne. Ponieważ ten ostatni argument nie dotyczy pól publicznych - przypisanie do niezadeklarowanego pola obiektu czyni go publicznym - wydaje mi się, że powinienem przynajmniej skomentować wszystkie moje deklaracje pól publicznych. Komentarze podadzą dokładnie tę samą wartość dokumentacji, ale skorzystam z ostrzeżeń, jeśli spróbuję odczytać niezainicjowane pole.

Jednak po dalszych przemyśleniach nie ma sensu się na tym zatrzymywać. Ponieważ z mojego doświadczenia wynika, że ​​próba odczytania niezainicjowanego pola jest o wiele bardziej powszechną przyczyną błędu niż próba niewłaściwego odczytania lub modyfikacji pola prywatnego lub chronionego (to samo zrobiłem kilka razy w mojej krótkiej karierze programistycznej, ale nigdy nie drugie ), wydaje mi się, że najlepszą praktyką byłoby komentowanie wszystkich deklaracji terenowych - nie tylko publicznych.

Waham się, że nigdy nie widziałem, aby ktokolwiek inny robił to w swoim kodzie. Dlaczego nie? Czy deklarowanie pól klasowych jest zaletą, o której nie wiem? Czy też mogę w jakiś sposób zmodyfikować konfigurację PHP, aby zmienić zachowanie deklaracji pól, aby móc używać rzeczywistych deklaracji pól i nadal korzystać z ostrzeżeń „Niezdefiniowana właściwość”? Czy jest coś jeszcze, czego nie zauważyłem w mojej analizie?

Mark Amery
źródło
1
Prawdopodobnie przydatne ? Wymienione korzyści są niezwykle ważne. Prawdopodobnie stanowczo odmówiłbym pracy nad kodem, który nie ma wyraźnych deklaracji własności.
Michael
2
Naprawdę, sposobem na ochronę się przed tymi błędami jest staranne sprawdzanie błędów, a jeszcze lepiej dzięki testom jednostkowym.
Michael
1
wbudowane jest raportowanie błędów php (poziom powiadomienia), które powie ci, czy używasz zmiennej bez uprzedniego jej zadeklarowania.
Kredka Gwałtowna
3
@ MarkAmery Myślę, że gorączkowy start-up jest jednym z najważniejszych miejsc do przyjęcia ścisłych testów jednostkowych - ponieważ zawsze zmieniasz kod, prawdopodobnie zawsze go łamiesz . To jest właśnie dokładny cel rygorystycznych testów i pełnego TDD. Tak, chyba użycie podkreślników może pomóc ci pamiętać, że masz dostęp do prywatnych rzeczy, ale dla mnie pomysł przyjęcia określonych standardów kodowania, aby uchronić mnie przed błędami, których nie powinienem popełniać, jest niepokojący. Jak robienie tego, TRUE === $variableaby uniknąć przypadkowego przypisania zamiast porównywania.
Michael
1
@ MarkAmery Należy również pamiętać, że słowa kluczowe widoczności są analizowane przez zautomatyzowane narzędzia dokumentacyjne , dzięki czemu dają więcej „wartości dokumentacji” niż tylko komentarz, który należy wprowadzić ręcznie.
Michael

Odpowiedzi:

15

Zawsze należy wcześniej zadeklarować właściwości klasy. Podczas gdy PHP jest językiem dynamicznym i chętnie będzie towarzyszył Ci podczas tworzenia właściwości w czasie wykonywania, istnieje kilka wad tej ścieżki.

  • Kompilator może zoptymalizować zadeklarowane właściwości. Gdy dynamicznie deklarujesz właściwość, jest to uderzenie wydajności, ponieważ kompilator musi utworzyć dynamiczną tablicę skrótów, aby przechowywać twoje właściwości dynamiczne.
  • Nie jestem w 100% na tym, ale wierzę, że optymalizatory kodu bajtowego, takie jak APC, nie będą tak przydatne, ponieważ nie będą miały pełnego obrazu twojej klasy. (A optymalizatory takie jak APC są koniecznością )
  • Sprawia, że ​​kod 1000x jest trudniejszy do odczytania. Ludzie będą cię nienawidzić.
  • Sprawia, że ​​1000 razy bardziej prawdopodobne jest, że popełnisz błąd. (Czy to getId czy getID? Poczekaj, użyłeś obu. Nie.)
  • Brak autouzupełniania IDE lub podpowiedzi typu.
  • Generatory dokumentacji nie zobaczą twojej nieruchomości.

Opisany przez ciebie problem jest w rzeczywistości niewielki w porównaniu z problemem braku deklarowania twoich właściwości w definicji klasy. Oto dobre rozwiązanie.

Przyzwyczaj się do deklarowania wartości domyślnych swoich właściwości.

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

Z wyjątkiem właściwości, które będą przechowywać obiekty, obejmie to większość typów bazowych, które będziesz przechowywać. Właściwości, które będą przechowywać obiekty, można ustawić w konstruktorze.

Dobrą zasadą przy projektowaniu klas jest to, że po utworzeniu obiektu i uruchomieniu konstruktora nie powinno być żadnych „niezdefiniowanych” właściwości.

Jarrod Pokrzywy
źródło
W odpowiedzi na twoje 5 wad: 1 (Wydajność): Czy masz na to źródło? 2 (Czytelność): Nie jestem pewien, czy rozumiem; propozycja polegała na komentowaniu deklaracji, a nie tylko usuwaniu ich, więc jaka utrata czytelności? Chyba nieco mniej przyjazne podświetlanie składni i potencjalne trudności w odróżnieniu od sąsiednich komentarzy, tak myślę? 3: Tego w ogóle nie rozumiem; możesz wyjaśnić? 4 (IDE): W porządku - nie mam doświadczenia w kodowaniu PHP za pomocą IDE i nie wiedziałem o podpowiedziach typu Eclipse. 5 (generatory dokumentów): W porządku - nigdy nie korzystałem z żadnego z nich.
Mark Amery
Nie widziałem twojego zdania na temat ich komentowania. Niezależnie od tego, powyższe punkty nadal obowiązują - skąd wiesz, które z nich są aktywne lub słusznie skomentowane?
Jarrod Nettles
W odpowiedzi na proponowane rozwiązanie: właśnie tego chcę uniknąć - przypisywania wartości domyślnych, nawet gdy są one pozbawione znaczenia i nigdy nie należy używać wartości domyślnych. Problem z tymi wartościami domyślnymi (oraz z deklaracją bez przypisania, która po prostu przypisuje null) polega na tym, że jeśli z powodu błędu programowego nie przypisuję wartości do konstruktora, kiedy powinienem, to zamiast PHP daje ostrzeżenie, gdy próbuję użyć tej wartości później - pomagając mi dostrzec mój błąd - wszystko wydaje się działać dobrze, nawet jeśli klasa jest zepsuta.
Mark Amery
2
@MarkAmery Twój błąd programowy w istocie polega na literówce. Wszyscy je mieliśmy - ale próbujesz zdetonować bombę, aby zabić muchę tutaj.
Jarrod Nettles
Możesz mieć rację. Spędziłem dziś kilka godzin na wyszukiwaniu błędu, który okazał się być całkowicie spowodowany taką literówką, a potem byłem sfrustrowany nagłym uświadomieniem sobie, że gdybym tylko nie użył deklaracji terenowych, dostałbym powiadomienie i od razu wiedziałem, gdzie był mój błąd (co i tak zawsze myślałem, że w takich okolicznościach się zdarzy; nie zdawałem sobie sprawy, że deklaracje zainicjowały pola na zero). Natychmiastowa dostępność tego doświadczenia prawdopodobnie wypacza moją ocenę tego, jak prawdopodobne jest, że się powtórzy, lub jak warto się bronić.
Mark Amery
2

Pracowałem nad kodem, który wykorzystywał dynamicznie tworzone właściwości obiektu. Myślałem, że używanie dynamicznie tworzonych właściwości obiektów jest całkiem fajne (prawda, moim zdaniem). Jednak uruchomienie mojego programu zajęło 7 sekund. Usunąłem właściwości obiektu dynamicznego i zastąpiłem je właściwościami obiektu zadeklarowanymi jako część każdej klasy (w tym przypadku publicznym). Czas pracy procesora wzrósł z ponad 7 sekund do 0,177 sekund. To całkiem spore.

Możliwe, że robiłem coś złego w sposobie używania dynamicznych właściwości obiektu. Możliwe jest również, że moja konfiguracja jest w jakiś sposób zepsuta. Oczywiście powinienem powiedzieć, że mam bardzo prostą waniliową konfigurację PHP na moim komputerze.

użytkownik328304
źródło