Kontakt z pomocą

Odpowiadamy e-mailem, zwykle w ciągu dwóch dni.

Google reCAPTCHA sprawdza to zgłoszenie pod kątem nadużyć; dane trafiają do Google. Skrypt ładuje się dopiero po otwarciu formularza.

← Wszystkie wpisy

Sprawdzenie, które zawsze jest prawdziwe

W tej usłudze stoją 23 instrukcje UPDATE. Dokładnie jedna z nich sprawdza, czy cokolwiek zmieniła. To sprawdzenie brzmi:

'success' => $n->rowCount() >= 0

rowCount() zwraca 0, gdy żaden wiersz nie pasował. Zero jest większe lub równe zeru. Sprawdzenie przechodzi, cokolwiek się stanie.

23 instrukcje UPDATE, jedna patrzy na wynik rowCount() >= 0 — przechodzi, nawet gdy nic nie pasowało rowCount() > 0 w całym kodzie: 0 razy liczniki z wierszem w tabeli, którą to zmienia: 4 z 2 218 dziś nic nie jest zepsute — z powodu, którym nie jest ten wiersz

Do czego ta instrukcja służy

Zapisuje jeden ptaszek: czy właściciel licznika chce tygodniowego podsumowania e-mailem. Adres leży w osobnej tabeli, a wiersz powstaje tam dopiero wtedy, gdy ktoś kliknie odnośnik potwierdzający w liście, o który sam poprosił. Cztery liczniki mają taki wiersz. Czynnych jest 2 218.

Dla 99,8 % liczników UPDATE nie trafia więc w nic. Punkt końcowy odpowiada success: true, a ustawienie nie zostaje zapisane, bo nie ma w czym go zapisać.

Komentarz nad nim jest słuszny

Trzy wiersze wyżej, w tej samej funkcji:

„Tylko tam, gdzie stoi potwierdzona droga powrotna. Bez niej nie ma adresu, na który list mógłby pójść — a ptaszek obiecałby coś, co się nie dzieje."

To dokładnie tak. Ktoś pomyślał o tym przypadku, zrozumiał go i zapisał rozumowanie. Potem wiersz poniżej użył >= tam, gdzie rozumowanie wymagało >.

Warto na to spojrzeć prosto, bo zwykłe wyjaśnienie — nikt o tym nie pomyślał — leży pod ręką i jest fałszywe. Myśl stoi w pliku. Zawiódł jeden znak, w miejscu, w którym wersja zła i dobra wyglądają na pierwszy rzut oka tak samo i zachowują się tak samo w każdym teście, który ma wiersz do zaktualizowania.

Nikt nie jest okłamywany

Teraz część, którą łatwo byłoby pominąć, a której pominięcie uczyniłoby ten wpis bardziej dramatycznym i mniej prawdziwym.

Strona w ogóle nie rysuje tego ptaszka, gdy wiersza brak. Ekran dalej, w kodzie budującym skrzynkę właściciela, ta sama tabela jest pytana najpierw, a cały blok zostaje pominięty, gdy zapytanie wraca puste. Właściciel bez potwierdzonego adresu nigdy nie widzi tego przycisku, nigdy go nie klika i nigdy nie dostaje fałszywego potwierdzenia.

Zepsute sprawdzenie jest osiągalne tylko przez bezpośrednie wywołanie punktu końcowego z ważnym tokenem. Kto to zrobi, dostanie success: true i żadnego zapisanego ustawienia. To prawdziwa wada, i wąska.

Dlaczego mimo to warto to zapisać

Funkcja jest bezpieczna dzięki zabezpieczeniu, którego nikt nie zapisał jako zabezpieczenia. Wiersz, który ma czynić ją bezpieczną, nie czyni jej bezpieczną. Robi to warunek przy rysowaniu w innej funkcji, którego komentarz wyjaśnia, dlaczego ptaszek jest ukryty — a nie to, że cokolwiek zależy od tego, by pozostał ukryty.

Usuń albo przebuduj ten warunek — rozsądna rzecz przy przerabianiu panelu ustawień — a wada staje się widoczna natychmiast, i nic nigdzie nie łączy tych dwóch zmian. Bezpieczeństwo jest prawdziwe, jest przypadkowe, a przypadkowe bezpieczeństwo to takie, które znika przy niezwiązanej pracy.

Ten sam kod robi to poprawnie plik dalej. Odpowiednia funkcja webringu pyta, czy wiersz istnieje, zwraca fałsz, gdy nie, i w ogóle nie wysyła aktualizacji. Ta tabela ma obecnie zero wierszy, więc funkcja zwraca fałsz przy każdym wywołaniu — i to jest właściwa odpowiedź.

Kształt ogólny

UPDATE, który nie trafia w żaden wiersz, nie jest błędem w żadnej bazie. To udana instrukcja, która nic nie zrobiła, a każda warstwa powyżej zgłosi sukces, dopóki ktoś nie zapyta. Dwadzieścia dwie instrukcje tutaj nie pytają, i przy większości jest to w porządku: aktualizują wiersz, którego istnienie żądanie już udowodniło.

Ta jedna, która musiała zapytać, zapytała w sposób, który nie może wypaść źle. rowCount() >= 0 to nie słabe sprawdzenie, to brak sprawdzenia w kostiumie sprawdzenia — i jest gorsze niż żadne, bo następny, kto przeczyta tę funkcję, widzi badany wynik i przestaje patrzeć.

Zwrot rowCount() > 0 nie występuje w tym kodzie ani razu. To ta liczba robi z literówki coś wartego wpisu: nie to, że raz napisano źle, tylko to, że nigdzie nie było poprawnego wystąpienia, z którym można by to porównać.

Naprawione 28 sierpnia 2026. Punkt końcowy pyta teraz przed zapisem, czy wiersz istnieje — tak jak funkcja webringu plik dalej robiła od zawsze — a w przeciwnym razie odpowiada fałszem. Oczywista poprawka na jeden znak, czyli sprawdzanie „większe od zera", została wcześniej zmierzona i odrzucona: sterownik zgłasza liczbę zmienionych wierszy, nie trafionych. Kto ustawia ptaszek na wartość, którą już ma, nic nie zmienia — a ta wersja zgłosiłaby porażkę dla czynności, która była w porządku. Obie wersje są błędne, tylko w przeciwnych kierunkach.

Reklama