v0.25.0 - Odinstalowanie bez sladu, pilnowane testem
v0.25.0 — Odinstalowanie bez sladu, pilnowane testem
Wtyczka sprzatala po sobie od Kroku 18, ale lista kluczy w uninstall.php byla
pisana recznie i po cichu zostawala w tyle za kodem. Wydanie 0.24.0 dolozylo dwie
opcje, ktorych nikt do niej nie dopisal — i zaden test tego nie zauwazyl. To
wydanie zamyka luke w bazie klienta i stawia straznika, ktory nie pozwoli jej
otworzyc ponownie.
Czego brakowalo (uninstall.php)
Dopisane wpisy, kazdy zostawal dotad w bazie po usunieciu wtyczki:
aifaq_site_profile— temat witryny wyprowadzony z bazy wiedzy (Seo\SiteProfile)aifaq_public_faq— pary Q&A opublikowane na podstronie (Faq\PublicFaq)aifaq_editor_hint_done— wyciszenie komunikatu edytora; siedzi w metadanych
UZYTKOWNIKA, nie w opcjach, wiec kasuje jedelete_metadata( 'user', 0, …, true )- transienty o stalej nazwie:
aifaq_indexing_lock,aifaq_cache_flush_lock - transienty o zmiennym sufiksie —
aifaq_rl_<ip_hash>,aifaq_cooldown_generate_<model>,
aifaq_cooldown_embed_<model>,aifaq_no_thinking_<model>
Transientow ze zmiennym sufiksem nie da sie wymienic po jednym, wiec kasuje je
jeden DELETE po LIKE na wp_options, dla obu prefiksow (_transient_aifaq_%
i _transient_timeout_aifaq_%), z esc_like(). Jeden wzorzec zamiast trzech —
obejmuje takze kazdy przyszly transient wtyczki i nie dotyka niczego cudzego.
Ograniczenie zapisane wprost w pliku: przy zewnetrznym trwalym object cache
(Redis, Memcached) transienty nie trafiaja do wp_options i ta sciezka ich nie
obejmuje. Wygasaja same po TTL; najdluzszy to godzina.
Multisite
Dotad sprzatany byl wylacznie biezacy blog, a wtyczka trzyma osobny komplet tabel
i opcji w kazdym blogu sieci. Cale sprzatanie siedzi teraz w jednej funkcji
aifaq_uninstall_cleanup_site() (prefiks + oslona function_exists(), bo plik
laduje sie do przestrzeni globalnej obok cudzych uninstall.php), a przy
is_multisite() wykonuje sie w petli switch_to_blog() / restore_current_blog()
po get_sites( array( 'number' => 10000, 'fields' => 'ids' ) ) — limit jawny, bo
domyslne 100 witryn byloby cicha strata.
Nazwy tabel powstaja wewnatrz funkcji: switch_to_blog() podmienia
$wpdb->prefix, wiec policzone raz na starcie wskazywalyby caly czas na blog 1.
Zasada bez zmian: wylacznie literaly stringow, zadnych stalych klas
(PageGuard::OPTION bez autoloadera to Fatal error), kazda funkcja WP spoza
gwarantowanego rdzenia za function_exists().
Podstrona „Generator FAQ" nadal nie jest kasowana — to tresc w witrynie klienta.
Straznik kompletnosci (tests/uninstall-guard-test.php)
Nowy zestaw skanuje src/** + ai-faq-generator.php czysto statycznie
(token_get_all, zero WordPressa, zero wywolan API), wyciaga klucze przekazywane
do zapisu trwalego (opcje, transienty, user meta, post meta), rozwiazuje stale klas
(self::OPTION → 'aifaq_site_profile') i klucze sklejane ('aifaq_rl_' . $x)
po prefiksie, po czym wymaga pokrycia dla kazdego klucza aifaq*.
Pokrycie liczy sie dwiema drogami: doslowny literal w uninstall.php albo wzorzec
SQL zadeklarowany znacznikiem GUARD-PATTERN: — ten drugi wylacznie dla
transientow, bo opcje kasuje delete_option() po nazwie. Straznik sprawdza tez,
ze znacznik ma pokrycie w realnym kodzie (DELETE … LIKE, oba prefiksy,
esc_like), ze sprzatanie obejmuje siec i ze plik nie siega po stale klas.
Lista wyjatkow (klucze budowane ze zmiennej, statycznie nierozwiazywalne) jest
zamknieta i uzasadniona po jednej pozycji: nowe wystapienie czerwieni test,
zamiast przejsc niezauwazone.
Falszywa zielen w samym strazniku — znaleziona i naprawiona przy odbiorze
Asercja multisite sprawdzala poczatkowo strpos( $un_src, 'switch_to_blog' ).
Mutacja pokazala, ze jest slepa: po wycieciu realnego przelaczania blogow nazwa
zostaje w pliku wewnatrz function_exists( 'switch_to_blog' ), wiec test dalej
przechodzil. Stawka byla konkretna — petla przeleciałaby N razy po biezacym blogu,
a pozostale witryny sieci zostalyby zasmiecone, i zaden test by tego nie zglosil.
Asercja liczy teraz REALNE WYWOLANIA na tokenach (literal w function_exists() to
T_CONSTANT_ENCAPSED_STRING i sie nie liczy; wywolanie to T_STRING + (), plus
doszla kontrola symetrii switch_to_blog() / restore_current_blog() — bez niej
uninstall konczylby sie na przelaczonym blogu i psul kontekst kolejnym wtyczkom.
Weryfikacja
- runner: 41 zestawow, 0 niezaliczonych (nowy segment
uninstall-guard-test.php,
24 asercje) — zmierzone PO naprawie asercji multisite php -l: 4 zmienione pliki PHP, zero bledow skladni- mutacje straznika: 8/8 czerwienia (7 od razu, 1 dopiero po naprawie asercji)
M1usunieciedelete_option( 'aifaq_public_faq' )→FAIL KAZDY klucz aifaq* ma pokrycie
(czerwieni takzekrok19-migracja-test.php, bo licznik27przestaje sie zgadzac)M2usunieciedelete_metadata()user meta → kotwicaaifaq_editor_hint_doneM3usuniecie znacznikaGUARD-PATTERN:→ brak zadeklarowanego wzorcaM4rozbicie prefiksu_transient_timeout_aifaq_→ wzorzec bez pokrycia w kodzieM5usuniecieswitch_to_blog()→ PRZESZLA NA ZIELONO przed naprawa asercji
(patrz „Falszywa zielen" wyzej); po naprawie czerwieniM6dorzucenieupdate_option( 'aifaq_nowa_sierota', 1 )dosrc/→
BRAK: aifaq_nowa_sierota [option] ← SiteProfile.php.
Pierwsze podejscie do tej mutacji dalo parse error w mutowanym pliku, wiec
czerwien nie dowodzila niczego; powtorzone na pliku z czystymphp -lM7usuniecierestore_current_blog()→ zlamana symetria switch/restoreM8pusta petla (wycieteaifaq_uninstall_cleanup_site()) → brak wywolania sprzatania
- galaz multisite wykonana na atrapie
$wpdb(3 blogi): 18DROP TABLEz trzema
ROZNYMI prefiksami (wp_,wp_2_,wp_7_) — dowod, ze nazwy tabel powstaja po
switch_to_blog(); 81delete_option(3 x 27 unikalnych);LIKEpoprawnie
zescapowany:'\_transient\_aifaq\_%' - zamrozone kontrakty bez zmian:
krok7-rest15 tras,krok20-capy15 tras,
krok18-faqtoolpanel138 asercji bez regresji - podniesiona zamrozona liczba w
krok19-migracja-test.php(C24):25→27
delete_option— test WYKONUJEuninstall.php, wiec dwie nowe opcje musialy
zmienic licznik - zero wywolan API — caly zestaw jest statyczny
AIFAQ_DB_VERSIONzostaje4(schemat bazy bez zmian)