E_NOTICE - wyświetlać czy nie?

Zainspirowany ostatnimi wypowiedziami początkujących (i nie tylko) programistów php, postanowiłem skrobnąć małe co nie co na temat błędów E_NOTICE. Niektórzy mogą mi zarzucić, że E_NOTICE to nie błąd, lecz ja słowo "błąd" użyłem celowo i z pełną świadomością. Zapewne bardziej fachowo należałoby napisać "komunikaty" E_NOTICE, aczkolwiek słowo komunikat to przecież komunikat więc można olać, a błąd to już coś poważniejszego, więc może by warto na niego zwrócić uwagę. Także by nie usypiać uwagi początkujących programistów (jak i niestestety tych już uważających się za niepoczątkujących) będę używał słowa "błąd".

No ale zapewne zapytacie do czego tak w ogóle zmierzam. Już wyjaśniam: dosyć często spotykam się ze stwierdzeniem, że E_NOTICE to tylko komunikat, który można totalnie olać a najlepiej wyłączyć jego wyświetlanie (taki komunikat to tylko pierdułka, która powoduje masę śmieci na ekranie). Otóż nie moi drodzy, jesteście w błędzie wszyscy ci, którzy tak właśnie myślicie. Ta "pierdułka" może Wam zaoszczędzić kilku godzin w poszukiwaniu błędów.

Najczęstrzą przyczyną występowania błędów E_NOTICE jest niezadklarowany jakiś indeks w tablicy albo niezadeklarowana zmienna. Przykład:

//tutaj kod formularza
//....
if ($_POST['submit']){ 
//jeśli wyslalismy formularz to cos tam robimy  
//....
// tutaj coś robimy, np. wysyłamy dane z formularza do bazy
}

Taki kod, w przypadku gdy wejdziemy na stronę z formularzem, ale jeszcze formularza nie wysłaliśmy, wygeneruje nam E_NOTICE undefined index submit...... W tym akurat przypadku, E_NOTICE faktycznie jest zwykłym komunikatem, który informuje nas, że odwołujemy się do indeksu submit w tabeli $_POST, którego nie ma. A nie ma, bo nie wysłaliśmy jeszcze formularza. Tak czy siak warunek nie jest spełniony, a o to chodzi. Gdy formularz wyślemy, indeks będzie, warunek będzie spełniony i zostanie wykonane wszystko co ma być wykonane po wysłaniu formularza. Jaki więc wniosek wyciąga początkujący programista? E_NOTICE tu tylko bruździ więc przestanę go wyświetlać i po sprawie.

Wszystko fajnie pięknie, programista więc wyłączył E_NOTICE, nie robią one bałaganu na stronie, skrypt działa jak ta lala. No ale nasz początkujący programista pisze dalej. Rozpatrzmy kolejną sytuację:

//tutaj kod formularza
//formularz ten zawiera między innymi pole o nazwie np. length
//<input type="text" name="length" />
//....
if ($_POST['submit']){ 
//jeśli wyslalismy formularz to cos tam robimy  
//....
// tutaj coś robimy, np. wysyłamy dane z formularza do bazy
$length = $_POST['lenth']; //tę wartość programista wklada do bazy
}

W kodzie powyżej nasz dzielny programista pobiera wartości z formularza i wkłada je do bazy. Biedak ma jednak problem, bo jednej wartości nie może uzyskać. Chodzi oczywiście o wartość pola length - przy pobieraniu wartości tego pola popełnił literówkę. Siedzi więc bidulka i włosy z głowy wyrywa co jest nie tak. Patrzy na kod z lewej, z prawej, z góry - nic. Wkońcu zrozpaczony po "dwóch dniach" szukania błędu (to też jest dobre, jak ludzie lubią pisać na forum ile to godzin/dni spędzili na szukaniu błędu, a w rzeczywistości widać, że raczej zajmowali się czym innym) leci na forum i wali tekst: "Skrypt jest poprawny, ale nie wkłada mi wartości z formularza. Co jest nie tak? Pomóżcie, siedzę nad tym dwa dni". Oczywiście problem by był do zdiagnozowania w dwie minuty a nie w "dwa dni", gdyby nasz "chowacz noticów" jednak nie ukrywał błędów E_NOTICE a je wyświetlał. Gdyby tak zrobił, dostałby od razu komunikat undefined index lenth... i prawdopodobnie (piszę prawdopodobnie bo zdarzają się też "ślepcy") ujrzał by banalną literówkę, którą uczynił.

No dobrze, ale jak koleś włączy te E_NOTICE to nadal będzie dostawał ten zbędny komunikat, z pierwszego kodu. Co z nim uczynić? Odpowiedź jest banalnie prosta: zrobić tak, by ten komunikat się nie wyświetlał. I nie moi mili, nie chodzi mi tu o zastosowanie małpy (@), która wycisza komunikaty błędów. Chodzi mi o poprawne pisanie kodu. Jeśli więc chcemy sprawdzić, czy istnieje jakiś indeks, użyjmy do tego isset lub !empty

if (isset($_POST['submit'])){ 
}
if (!empty($_POST['submit'])){ 
}

To co użyjecie i jak, zależy w dużej mierze już od tego co chcecie osiągnąć. Nie będę tu się rozpisywał na temat metod, bo miejsca w bazie na ten artykuł mi zabraknie

Rozpatrzmy jeszcze jedną sytuację na koniec: nasz bohater jednak stwierdził, że na razie będzie lał na E_NOTICE - za "dużo" babrania w "ładne" pisanie. Tak więc pisze sobie swój własny system, ignoruje wszelkie błędy E_NOTICE, bo ich nie wyświetla. Tak sobie pisze przez kilka miesięcy. Natrafił jednak na problem/błąd, z którym nie mógł sobie poradzić. Z racji, że już trochę zmądrzał stwierdził, iż skorzysta jednak z E_NOTICE - może one pomogą mu w rozwiązaniu aktualnego problemu. Zadowolony, iż wpadł na tak genialny pomysł, włącza wyświetlanie wszystkich błędów - a tu ZONK. Z racji, że przez tyle czasu ignorował E_NOTICE, teraz nagle na ekranie pojawia mu się ich kilkadziesiąt - na ekranie ma normalnie czarno od błędów. Rzecz jasna, że przy takiej ilości "komunikatów" nie jest w stanie wychwycić tego, które może mu pomóc przy jego obecnym problemie...

No ale zapytacie: Czy na pewno zawsze trzeba pisać tak, by błąd się nie pojawiał? Czy naprawdę nie można go wyciszyć przy użyciu np. @? Odpowiedź: w bardzo ale to naprawdę w bardzo wyjątkwych sytuacjach można. Ale zapomnijcie o tych "wyjątkach" i nigdy nie używajcie @. Swego czasu w Zend Framework natrafiłem na kod, który używał @ do ewidentnego wyciszenia błędu rzucanego przez funkcję operującą na pliku. Kolesie z ZF użyli tego, gdyż było to szybsze niż wykonanie dodatkowej funkcji sprawdzającej stan pliku. Oczywiście błąd się generował, ale był wyciszany przez @ i nie był wyświetlany. Ja jednak użyłem własnej obsługi błędów i wyświetlałem nawet wyciszane błędy - w wyniku czego logi z błędami mi puchły - trochę napsioczyłem wówczas na kolesi z ZF

Na koniec chcę Wam przedstawić kod:

<?php
$end1 = microtime(true);
error_reporting(E_ALL ^ E_NOTICE); 
ini_set('display_errors','1');
	
$start1 = microtime(true);
for ($i=0;$i < 10000;$i++){
	if ($_POST['zm']){
	//rob cos
	}
}
$end1 = microtime(true);

echo 'Z wylaczeniem E_NOTICE: '.($end1 - $start1);

$end1 = microtime(true);
error_reporting(E_ALL); 
ini_set('display_errors','1');
	
$start1 = microtime(true);
for ($i=0;$i < 10000;$i++){
	if (isset($_POST['zm'])){
	//rob cos
	}
}
$end1 = microtime(true);

echo '<br />Z wlaczeniem E_NOTICE i ich niegenerowaniem: '.($end1 - $start1);
?>

Kod ten porównuje sytuację, gdy nie wyświetlamy błędów E_NOTICE, ale mamy taki kod, który je generuje. Drugą sytuacją jest gdy wyświetlamy wszystko jak popadnie, ale tak piszemy, by E_NOTICE nie były generowane. O to wyniki:

Z wylaczeniem E_NOTICE: 0.0129368305206
Z wlaczeniem E_NOTICE i ich niegenerowaniem: 0.00205683708191
0.0129368305206
0.00205683708191

Jak widać ładniejsze pisanie, mimo iż może jest dłuższe w kodzie (dłuższe "aż" o pare znaków) to jednak zdecydowanie szybciej się wykonuje - może ten argument Was przekona

Podsumowanie

W wielkim skrócie: zawsze, ale to zawsze analizujcie E_NOTICE, które dostajecie w wyniku działania skryptu. Nie wyciszajcie ich ani nie ignorujcie - te "komunikaty" naprawdę mogą Wam pomóc. Nawet jeśli teraz tego nie widzicie - w przyszłości wspomnicie moje słowa. A jak włączyć wyświetlanie wszystkich błędów? Tutaj macie napisane. Oczywiście takie wyświetlanie będziecie mieć na początku waszej przygody z php. Z czasem zapewne zaczniecie używać jakiegoś własnego systemu logowania błędów, np. przy użyciu set_error_handler().

Komentarze

 

2010-10-04 11:43 gość_Alan LBO Bem

E_NOTICE, owszem, ale tylko w środowisko developerskim - wiem, że o to Tobie chodziło, ale nigdzie tego jasno nie napisałeś.

2010-10-04 11:50 nospor

Teoretycznie... w produkcyjnym już tak kod powinien być cacy, że E_NOTICE nie powinny już się pojawiać Oczywiście to tylko teoria

Tak, masz rację, w developerskim. Ale to dotyczy wszystkiego rodzaju błędów a nie tylko E_NOTICE. Tak samo E_WARNING czy E_ERROR nie powinny być również wyświetlane w środowisku produkcyjnym. W produkcyjnym te błędy powinny być jak najbardziej przechwytywane, ale nie wyświetlane na ekran ale do logów

2010-10-05 09:29 gość_Tomasz Kowalczyk

W środowisku produkcyjnym powinno być wyłączone jakiekolwiek raportowanie błędów, to jasne, aczkolwiek w kodzie, który w takie środowisko puszczamy nie powinno być żadnych miejsc, gdzie takowy by występował. To wcale nie jest utopia - należy zwyczajnie przewidywać, gdzie i kiedy może się coś takiego pojawić i przy każdym niezerowym prawdopodobieństwie wystąpienia trzeba tak pokierować logiką skryptu, żeby nie było możliwości "nieistnienia indeksu" i podobnych sytuacji.

2010-10-05 09:39 nospor

Tomasz nie pisałem nic o utopii tylko o teorii
Zgadzam się z Tobą w 99.9999%. W praktyce jednak zdarzają się nawet noticy na wersjach produkcyjnych.

Mam do Ciebie pytanie pomocnicze:
Założmy, że masz formularz, w nim między innymi pola NAME oraz SURNAME. Możesz mi pokazać w jaki sposób odbierasz wartości tych pól z formularza?

2010-10-05 10:55 gość_matipl

Dla mnie to oczywistość. Tyle co powinniśmy je logować do pliku, wraz z błędami strict.

2010-10-05 20:52 vokiel

Jak już zauważono, w środowisku produkcyjnym błędy mogą być, ale logowane do pliku, nic się nie wyświetla użytkownikowi.

Odnośnie porównania czasu z wyłączonym/włączonym E_NOTICE to mnie zaskoczyłeś. Nie przyszło mi do głowy, żeby w ogóle mierzyć tu czas.

Na koniec jedna rzecz, ostatnio oglądałem wystąpienie Rasmusa Lerdorfa odnośnie optymalizacji skryptów napisanych w PHP. Był tam taki moment, w którym Rasmus zwrócił uwagę na taką banalną rzecz jak brak favicon.ico, które domyślnie jest poszukiwane podczas ładowania strony. Okazuje się, że niby nieważny błąd - brak pliku w lokalizacji głównej strony, znacznie wpływa na wydajność skryptu, gdyż serwer przeszukuje kolejno kilka lokalizacji w jego poszukiwaniu, a jak wiemy, operacje dyskowe nie należą do najszybszych. Zatem dodanie, nawet pustej ikonki wpływa na szybkość ładowania strony. Co chciałem powiedzieć tym przydługim akapitem, to to, że często wydawałoby się nieznaczny błąd może wpływać znacząco na wydajność, szybkość działania. Zatem przeglądanie wszystkich komunikatów błędów, pozbycie się ich, wraz z tymi E_NOTICE, zebrane w całość może się okazać zauważalnym skokiem wydajności.

2010-10-05 21:01 nospor

Odnośnie porównania czasu z wyłączonym/włączonym E_NOTICE to mnie zaskoczyłeś. Nie przyszło mi do głowy, żeby w ogóle mierzyć tu czas.
Wiesz, chciałem mieć jak najwięcej argumentów "za", więc szukałem co się tylko dało

2010-10-08 14:00 gość_zordon

Jako, że tekst adresowany raczej do początkujących - warto wspomnieć o najgorszej możliwej sytuacji z błędami - kiedy wszystko działa, lecz działa źle. Najprostszy możliwy przykład, który przychodzi mi do głowy to powiększenie jakiejś wartości o właśnie wartość elementu tablicy o nieistniejącym indeksie. Przy większej złożoności i wyłączonym wyświetlaniu notice'ów takie błędy baaardzo trudno wykryć. Kolega niedawno przy jakimś projekcie musiał połączyć się z systemem innej firmy i nie za fajnie się programuje jak z takiej aplikacji przy pierwszym kontakcie "wylatuje" kilkaset notice'ów. Sprawę pogarszał fakt, że "programista" po drugiej strony słuchawki jeszcze zaciekle bronił swoich praktyk, nawet wtedy, gdy aplikacja wywoływała błędy nie tylko "estetyczne". Dlatego warto uświadamiać kogo się da, jak wcześnie się da - nigdy nie wiadomo, kiedy się przyda

2010-10-21 14:20 gość_ktos

dlaczego strona nie jest zintegrowana z forum? nie lepiej by bylo :]

2010-10-21 14:26 nospor

Nie za bardzo rozumiem co masz na myśli... Jak strona zintegrowana z forum? Jaka strona i jakie forum?

2010-10-22 08:01 gość_ktos

Chodzi o to, że lepiej by było zrobić dział na forum (oczywiście mowa o Twojej stronie) o nazwie "Nowości", czy Bóg wie jak i klepać tam wszystkie nowości ze strony głównej. Fajnie by było ;o

2010-10-22 08:09 nospor

1) Nie wydaje mi się to dobrym pomysłem. Forum to forum, a devblog to devblog
2) To nie są "nowości" a artykuły
3) Forum pojawiło się niedawno na mojej stronie. Blog był duuuużo wcześniej
4) Tak poza tym ta dyskusja raczej nie powinna być prowadzona w tym arcie

2010-10-29 15:12 gość_Trexor

Chciałbym ustrzec wszystkich początkujących (i nie tylko), którzy to czytają, przed sprawdzaniem tego czy istnieje indeks tablicy za pomocą funkcji isset() i empty(). Do tego służy funkcja array_key_exists().

Funkcja isset() zwraca false między innymi wtedy, gdy zmienna ma wartość null. Spójrzmy na poniższy kod:

<?php

$foo['bar'] = null;

var_dump(isset($foo['bar']));
bool(false)

W wyniku isset() otrzymaliśmy false mimo tego, że indeks 'bar' tak naprawdę istnieje. Podobna sytuacja jest z funkcją empty().

Radzę dokładnie poczytać o działaniu tych funkcji w manualu PHP bo używanie isset() i empty() w celu sprawdzania indeksów również może przysporzyć trudnych do zdebudowania błędów.

2010-10-30 09:11 nospor

Zgadza się Trexor. Ale problem nie dotyczy tylko tablic ale również i zmiennych.
<?php
$zm = null;
isset($zm) //da false
?>

Tylko tutaj, nie mamy już funkcji zastępczej jak w przypadku tablic i mamy zonk.
Osobiście nie podoba mi się takie rozwiązanie dla isset.

2010-10-30 09:27 Michał

Dobrze dobrze, ale z ciekawości zrobiłem kiedyś testy pomiędzy isset a array_key_exists i różnica była ogromna. Więc jeżeli klucz jakiejś tablicy to null to tak, jakby go tam nie było. Nie miałem przypadku aby używanie isset było szkodliwe dla skryptu.

Pozdrawiam

2010-10-30 09:39 nospor

Ja w praktyce też nigdy nie używam array_key_exists tylko isset. Jedyne o czym muszę pamiętać to to, by nie wkładać do tablicy NULLi
Jednak widziałem już pare problemów u ludzi, gdy wkładali NULL a potem się głowili co im nie działa, więc informacja sama w sobie jest jak najbardziej potrzebna

ps: a ta ogromna różnica to na czyją korzyść była?

2010-10-30 11:49 Michał

array_key_exists(): 0.504 [82.895%] seconds
isset():            0.104 [17.105%] seconds

2010-10-30 12:33 nospor

Różnice faktycznie duże. Żeby nie powiedzieć wręcz strasznie duże.

2010-12-31 11:25 gość_Tomasz Kowalczyk

"Założmy, że masz formularz, w nim między innymi pola NAME oraz SURNAME. Możesz mi pokazać w jaki sposób odbierasz wartości tych pól z formularza? "

Formularz oczywiście wysyłany POSTem - przechodzi przez walidację, gdzie czeka na niego mechanizm "wiedzący", co ma przyjść (opis pól) i testujący każde pole na istnienie (tutaj nie bolą mnie mankamenty isset()'u - raczej nie akceptuję nulli jako wartości pola "name") oraz inne walidatory (email, nip - tego typu, w zależności od przypisania), jeśli cokolwiek jest źle, podpisuję pod pole komunikat i zwracam userowi do poprawki.

Czekam na wytknięcie błędów w rozumowaniu. ;]

2010-12-31 11:56 nospor

Nigdzie nie napisałem, że masz błedy w rozumowaniu
Napisałem jedynie, że nawet w prodykcyjnych kodach zdarzają się NOTICY

Jeśli ty sprawdzasz, czy istnieje każde pole, którego oczekujesz, to miło mi to słyszeć Właśnie o takiej sytuacji myślałem, że jak ktoś nie sprawdza, to wówczas może mieć NOTICY, nawet jeśli teoretycznie w jego formie wszystkie pola występują.

Dodaj komentarz

 

Dostępne bbcode: b, u, i, url, code, php, css, html, sql, js

Skrypty użytkowników

  1. Klasa obsługi szablo... Lirdoner
  2. Sekcje user76
  3. Klasa walidująca for... user76
  4. Licznik Gości online korey
  5. Form Builder Comandeer
  6. Dynamiczny licznik z... korey
  7. Captcha Comandeer