← Lekcje
Języki obok siebie · lekcja 05

Błędy: jak czytać komunikat w trzech językach

  1. Gdzie w ogóle zobaczycie komunikat o błędzie
  2. Co mówią cztery części komunikatu i od której linii czytać
  3. Trzy rodziny błędów: składnia, działanie, logika
  4. Ćwiczenie: trzy programy, trzy błędy do znalezienia
Rozgrzewka · 3 minuty

Z poprzedniej lekcji: dane od użytkownika

  1. Czym w każdym z tych trzech języków pobieramy dane od użytkownika?
  2. Dlaczego to, co wpisze użytkownik, trzeba najpierw zamienić na liczbę?
  3. Jak wstawiamy wartość zmiennej w środek napisu?
Część 1 · Gdzie szukać

Komunikat gdzieś jest. Trzeba wiedzieć gdzie

PHP strona w przeglądarce na górze, nad treścią albo log serwera gdy strona jest pusta Python okno wiersza poleceń to samo, w którym uruchomiliście program nic nie znika JavaScript konsola przeglądarki klawisz F12, zakładka Konsola strona wygląda zwykle
php.net: Language Reference › Errors › Basics · docs.python.org: tutorial 8.1 „Syntax Errors” · MDN: Learn_web_development/Core/Scripting/What_went_wrong (sekcja „Fixing errors reported in the console”)
Część 1 · Anatomia komunikatu

Cztery części jednego komunikatu

1 RODZAJ 2 TREŚĆ 3 PLIK 4 LINIA Parse error: syntax error, unexpected token "echo" in price.php on line 3 co to za błąd co dokładnie nie gra w którym pliku w której linii Trzy z czterech części mówią, gdzie stanąć. Czwarta mówi, czego szukać.

Kolejność pracy: otwórz podany plik, przejdź do podanej linii, a jeśli w niej nic nie widać, spójrz na linię wyżej. Tam najczęściej brakuje średnika, nawiasu albo dwukropka.

php.net: Language Reference › Errors › Basics · docs.python.org: tutorial 8.1 „Syntax Errors” · MDN: Web/JavaScript/Reference/Errors
Część 2 · Trzy rodziny

Trzy rodziny błędów obok siebie

składnia nie rusza wcale w trakcie staje w połowie logiczny dobiega do końca, ale wynik jest zły bez komunikatu

Błąd logiczny wygląda tak: program ma odjąć 10 % rabatu od ceny 120, a w kodzie stoi $total = $price * 0.1;. PHP wypisuje Razem: 12 i kończy pracę bez jednego ostrzeżenia. Powinno wyjść 108.

docs.python.org: tutorial 8.1 „Syntax Errors” i 8.2 „Exceptions” · php.net: Language Reference › Errors › Basics · MDN: Learn_web_development/Core/Scripting/What_went_wrong (sekcja „Types of error”)
Część 2 · Błąd składni

Program nie rusza: błąd składni

PHP · price.php

<?php
$price = 120
echo $price;
Parse error: syntax error,
unexpected token "echo"
in price.php on line 3

Python · price.py

price = 120
if price > 100
    print("Rabat")
  File "price.py", line 2
    if price > 100
                  ^
SyntaxError: expected ':'

JavaScript · script.js

let price = 120;
for (let i = 1; i <= 3; i++) {
  console.log(i);
script.js:4
Uncaught SyntaxError:
Unexpected end of input

Wszystkie trzy komunikaty wskazują linię po tej, w której coś zginęło: brak średnika, brak dwukropka, brak klamry zamykającej. Dlatego po wejściu we wskazaną linię zawsze sprawdzamy też linię wyżej.

php.net: Language Reference › Errors › Basics · docs.python.org: tutorial 8.1 „Syntax Errors” · MDN: Web/JavaScript/Reference/Global_Objects/SyntaxError
Część 2 · Błąd w trakcie działania

Nieznana zmienna: trzy reakcje

PHP · total.php

<?php
$price = 120;
echo "Razem: " . $total;
Warning: Undefined variable
$total in total.php on line 3
Razem:

Python · total.py

price = 120
print("Razem:", total)
Traceback (most recent call last):
  File "total.py", line 2
    print("Razem:", total)
                    ^^^^^
NameError: name 'total' is
not defined

JavaScript · script.js

let price = 120;
let discount = 10;
console.log("Razem: " + total);
Uncaught ReferenceError:
total is not defined
    at script.js:3:25

Dzielenie przez zero, ta sama linia 100 / 0: PHP przerywa program błędem DivisionByZeroError, Python błędem ZeroDivisionError, a JavaScript spokojnie wypisuje Infinity i pracuje dalej.

php.net: Language Reference › Variables › Basics · Predefined Exceptions › DivisionByZeroError · docs.python.org: Library › Built-in Exceptions (NameError, ZeroDivisionError) · MDN: Web/JavaScript/Reference/Errors/Not_defined · Global_Objects/Infinity
Część 3 · Obsługa wyjątku

try: spróbuj, a jak się nie uda, zrób to

PHP

$text = "abc";
try {
  $number = (int) $text;
  echo 100 / $number;
} catch (DivisionByZeroError $e) {
  echo "Brak wyniku: " . $e->getMessage();
}

Wypisuje: „Brak wyniku: Division by zero”

Python

text = "abc"
try:
    number = int(text)
    print(100 / number)
except ValueError:
    print("To nie jest liczba")
except ZeroDivisionError:
    print("Brak wyniku")

Wypisuje: „To nie jest liczba”

JavaScript

const text = "abc";
try {
  const number = Number(text);
  if (Number.isNaN(number))
    throw new Error("To nie jest liczba");
  console.log(100 / number);
} catch (e) {
  console.log("Brak wyniku: " + e.message);
}

Wypisuje: „Brak wyniku: To nie jest liczba”

Ten sam napis "abc", trzy różne momenty wykrycia: Python zgłasza błąd już przy zamianie na liczbę, PHP dopiero przy dzieleniu, a JavaScript nie zgłasza go wcale, więc programista sprawdza wynik sam i zgłasza błąd słowem throw.

php.net: Language Reference › Exceptions · docs.python.org: tutorial 8.3 „Handling Exceptions” · MDN: Web/JavaScript/Reference/Statements/try...catch · Global_Objects/Number/isNaN
Ściąga · do zeszytu

Jedna tabela na trzy języki

CoPHPPythonJavaScript
gdzie widać komunikatstrona albo log serweraokno wiersza poleceńkonsola, klawisz F12
błąd składniParse errorSyntaxErrorUncaught SyntaxError
nieznana zmiennaWarning: Undefined variableNameErrorUncaught ReferenceError
dzielenie przez zeroDivisionByZeroErrorZeroDivisionErrorInfinity, bez błędu
numer linii w komunikacieon line 3line 2script.js:3
od której linii czytaćod pierwszejod ostatniejod pierwszej
obsługa błędutry { } catch { }try: / except:try { } catch { }
php.net: Language Reference › Errors › Basics · Exceptions · docs.python.org: tutorial 8 „Errors and Exceptions” · MDN: Web/JavaScript/Reference/Errors
Ćwiczenie · na kartce · 12 minut

Trzy programy, trzy błędy

A · cart.php

<?php
$price = 40;
$count = 3
echo "Razem: " . $price * $count;
Parse error: syntax error,
unexpected token "echo"
in cart.php on line 4

B · discount.py

price = 120
discount = 10
final_price = price - discunt
print("Razem:", final_price)
Traceback (most recent call last):
  File "discount.py", line 3
    final_price = price - discunt
                          ^^^^^^^
NameError: name 'discunt' is not
defined. Did you mean: 'discount'?

C · script.js

let items = [3, 5, 2];
let total = 0;
for (let i = 0; i < items.length; i++) {
  total = total + items[i];
console.log("Razem: " + total);
script.js:6
Uncaught SyntaxError:
Unexpected end of input

Dla każdego programu zapisz trzy rzeczy: numer linii z prawdziwym błędem, rodzaj błędu (składnia albo w trakcie działania) i poprawkę.

  1. Program A w PHP
  2. Program B w Pythonie
  3. Program C w JavaScripcie
Sprawdzenie

Trzy pytania na koniec

  1. Gdzie zobaczycie błąd programu napisanego w JavaScripcie?
  2. Która rodzina błędów nie daje żadnego komunikatu?
  3. Od której linii czyta się komunikat w Pythonie?
Podsumowanie

A jak to wygląda w produkcji?

Na lekcjiW produkcjiStatus
Komunikat na stronieWyłączony dla gości, bo zdradza ścieżki plików. Trafia do dziennika serwerastandard
Szukanie błędu po komunikacieSystemy, które zbierają błędy z serwera i z przeglądarek wszystkich użytkownikówcodziennie
try i catchKażde sięgnięcie do bazy, pliku albo sieci ma obsługę błędu, bo sieć bywa zerwanacodziennie
Błąd logicznyŁapią go testy i przegląd kodu przez drugą osobę, bo komunikatu nie będziestandard

Ciekawostka: PHP ma funkcję fdiv(), która dzieli dokładnie tak jak JavaScript: zamiast przerwać program zwraca INF. Jeden język potrafi więc zachować się na oba sposoby, zależnie od tego, którego zapisu użyjecie.

ciekawostka: php.net: Function Reference › Math › fdiv · MDN: Web/JavaScript/Reference/Global_Objects/Infinity