Search and replace pe baza de date WordPress fără să distrugi datele serializate
Schimbă adresa unui site WordPress direct în baza de date fără să golești widgeturile, meniurile sau setările temei. Comanda WP-CLI care păstrează datele serializate intacte.
Publicat
Nu rula un simplu UPDATE ... SET column = REPLACE(column, 'oldurl', 'newurl') pe o bază de date WordPress. Va schimba textul corect și îți va distruge setările în același timp, pentru că WordPress stochează tablourile și obiectele ca șiruri serializate de PHP, iar formatul acela notează lungimea în octeți a fiecărui șir pe care îl conține. Un replace brut în SQL rescrie textul, dar nu atinge niciodată lungimea înregistrată, iar valoarea devine imposibil de citit.
Folosește în schimb un instrument care înțelege serializarea. Pe linia de comandă acela este wp search-replace. Fă mai întâi o copie a bazei de date, rulează comanda ca dry run, apoi rulează-o pe bune.
Ce se strică, de fapt
Un șir serializat de PHP arată așa:
s:18:"http://example.com"
18 este lungimea în octeți a textului care urmează. Numără — http://example.com are exact 18 caractere.
Acum imaginează-ți o setare de temă salvată:
a:1:{s:4:"logo";s:18:"http://example.com";}
Rulează un replace simplu în SQL din http:// în https:// și obții:
a:1:{s:4:"logo";s:19:"https://example.com";}
Doar că nu. Obții asta:
a:1:{s:4:"logo";s:18:"https://example.com";}
Șirul are acum 19 octeți, dar susține în continuare că are 18. PHP citește lungimea declarată, avansează 18 octeți, nu găsește ghilimeaua de închidere și punctul și virgula acolo unde le așteaptă, și renunță. Tot tabloul eșuează la deserializare.
De ce eșuează în tăcere
Asta e partea care face buba atât de scumpă. WordPress nu aruncă o eroare și nu se oprește. Când citește o opțiune, trece valoarea prin funcția lui de deserializare, PHP returnează false pentru datele malformate, iar WordPress îi dă codului tău o valoare care se comportă ca și cum opțiunea nu ar fi fost setată niciodată. Pluginul sau tema cade apoi pe valoarea implicită.
Deci simptomul nu este o pagină de eroare. Simptomul este:
- Widgeturile dispar din bare laterale
- Setările din personalizator revin la valorile implicite ale temei
- Datele de aranjare ale unui constructor de pagini revin goale și pagina se randează goală
- Paginile de setări ale pluginurilor arată ca proaspăt instalate
- Meniurile își pierd locațiile atribuite
Nimic din interfața de administrare nu spune că e ceva în neregulă. Rândul e tot în baza de date, tot plin de ceva ce pare text corect. Doar prefixul de lungime e greșit cu un octet sau doi. PHP chiar emite o notificare când deserializarea eșuează, dar pe un site în producție, cu afișarea erorilor oprită, nu o vede nimeni. Activarea WP_DEBUG împreună cu WP_DEBUG_LOG pornit și WP_DEBUG_DISPLAY setat pe false pune notificările acelea într-un fișier de log unde le poți citi.
Nici pagubele nu sunt distribuite uniform. Valorile scalare simple — siteurl, home, conținutul obișnuit al articolelor, o adresă care stă în propria coloană — supraviețuiesc perfect unui replace naiv. Doar valorile serializate se strică. De asta un replace prost pare adesea, la prima vedere, că a funcționat.
Verifică wp-config.php înainte să atingi ceva
Dacă wp-config.php definește WP_HOME sau WP_SITEURL, constantele acelea bat orice se află în baza de date. Modificarea wp_options va părea că nu face absolut nimic. Caută linii ca acestea înainte să tragi concluzia că baza de date e problema:
define( 'WP_HOME', 'http://example.com' );
define( 'WP_SITEURL', 'http://example.com' );
Ori le actualizezi cu adresa nouă, ori le ștergi și lași baza de date să decidă.
Fă backup întâi, și chiar fă-l
O serializare stricată nu e mereu ușor de reparat. Odată ce prefixul de lungime și conținutul nu mai corespund, valoarea originală nu mai poate fi recuperată din cea stricată — nu ai cum să știi care trebuia să fie lungimea corectă fără să știi șirul original. O copie de siguranță este singurul „undo” real.
wp db export backup-before-replace.sql
Sau cu mysqldump, dacă WP-CLI nu e disponibil:
mysqldump -u USER -p DBNAME > backup-before-replace.sql
Ține copia în afara serverului, dacă poți. O copie de siguranță care stă în rădăcina web e în același timp un punct de restaurare și o problemă de securitate.
Comanda sigură
Comanda search-replace din WP-CLI deserializează fiecare valoare, parcurge structura decodată, înlocuiește în interiorul ei și reserializează cu lungimile corecte. Întâi dry run:
wp search-replace 'http://example.com' 'https://example.com' --dry-run --all-tables --skip-columns=guid
Citește raportul. Îți spune câte înlocuiri se vor face pe fiecare tabel și pe fiecare coloană. Dacă lista de tabele arată greșit sau un tabel pe care nu îl recunoști are mii de potriviri, oprește-te și uită-te înainte să rulezi pe bune.
Apoi rulează:
wp search-replace 'http://example.com' 'https://example.com' --all-tables --skip-columns=guid --report-changed-only
Câteva observații sincere despre parametri:
--all-tablesacoperă tabele pe care WordPress nu le înregistrează el însuși, adică exact acolo unde stau adesea datele pluginurilor. Fără el, unele tabele de plugin sunt sărite complet.--skip-columns=guidcontează. Câmpulguideste un identificator permanent pentru cititoarele de feed, nu o adresă vie. Rescrierea lui poate face ca abonații la feed să vadă toată arhiva ta ca articole noi.- Caută fără slash la final și fără variantele de protocol pe care nu vrei să le atingi. Înlocuirea lui
example.comsimplu va lovi și adresele de email și orice mențiune din textul articolelor.
Dacă nu poți folosi linia de comandă, pluginurile de migrare care înțeleg serializarea fac aceeași muncă de decodare din interfața de administrare. Comportamentul pe care îl cauți este descris explicit ca tratare a datelor serializate; dacă un instrument nu spune asta, presupune că nu o face.
Ce tabele și ce coloane sunt implicate
| Tabel | Coloană | Risc de serializare |
|---|---|---|
| wp_options | option_value | Mare — widgeturi, modificări de temă, tranzienți, majoritatea setărilor de plugin |
| wp_postmeta | meta_value | Mare — aranjamente de constructori de pagini, tablouri de câmpuri personalizate |
| wp_usermeta | meta_value | Mare — capabilități, preferințe de ecran și de tablou de bord |
| wp_termmeta | meta_value | Mediu — depinde de pluginuri |
| wp_posts | post_content, guid | Mic pentru conținut, dar atributele de shortcode și marcajul blocurilor conțin adrese |
| wp_comments | comment_content, comment_author_url | Mic |
| wp_links | link_url, link_image | Mic, și adesea nefolosit |
Pe multisite adaugă wp_blogs, wp_site, wp_sitemeta, plus tabelele per site wp_2_options, wp_3_options și așa mai departe. Schimbarea adreselor pe multisite are reguli suplimentare dincolo de un search and replace — tratează asta ca pe o treabă separată.
Cazurile pe care search-replace nu le rezolvă complet
Ca să fim sinceri cu limitele:
JSON stocat în baza de date. Unii constructori de pagini salvează datele de aranjare ca JSON cu slash-urile escapate, așa că adresa ta apare ca https:\/\/example.com, nu ca https://example.com. O căutare după forma normală o va rata. S-ar putea să ai nevoie de o a doua trecere pe forma escapată. Testează pe o copie.
Valori codate în Base64. Dacă un plugin codează setările înainte să le salveze, niciun replace bazat pe text nu poate vedea adresa. Setările acelea trebuie de obicei reintroduse din ecranul propriu al pluginului.
Adrese scrise direct în fișiere. Orice a fost scris în functions.php, într-un șablon de temă copil sau ca o cale fixă către un fișier nu se află în baza de date și nu va fi atins. Caută separat prin directorul temei.
Conținut din cache și generat. Cache-ul de obiecte, cache-ul de pagini și copiile din CDN continuă să servească adresele vechi și după un replace corect. Golește-le, apoi verifică din nou înainte să presupui că replace-ul a eșuat.
Dacă ai rulat deja interogarea proastă
Restaurează copia de siguranță. Ăsta e răspunsul și merită spus direct, în loc să ofer o procedură de reparare care în cea mai mare parte nu funcționează.
Dacă nu există copie de siguranță, drumul de recuperare e îngust: valorile care nu au fost niciodată serializate sunt în regulă, iar cele serializate stricate trebuie reconstruite manual sau resetate prin resalvarea ecranului de setări corespunzător. Resalvarea unei zone de widgeturi, reselectarea unei locații de meniu sau reintroducerea setărilor unui plugin scrie o valoare nouă, serializată corect, peste cea stricată. Plictisitor, dar funcționează, și e mai bine decât să lași rânduri corupte la locul lor, unde vor deruta următoarea persoană care se uită la site.
Ca să generezi instrucțiunile corecte înainte să rulezi ceva, construiește-le cu instrumentul de search and replace SQL pentru WordPress — îți arată ce coloane pot fi atinse în siguranță cu SQL simplu și care au nevoie de tratare conștientă de serializare, ca să vezi împărțirea înainte să te angajezi într-o comandă.
FAQ
Întrebări
De ce strică un REPLACE simplu în SQL un site WordPress?
Pentru că WordPress stochează tablourile și obiectele ca text serializat de PHP, iar formatul acela notează lungimea în octeți a fiecărui șir din interior. Un REPLACE brut schimbă textul, dar lasă numărul vechi de octeți la locul lui. PHP refuză apoi să deserializeze valoarea, WordPress cade pe o valoare implicită, iar setările apar goale, nu greșite.
Care este cel mai sigur mod de a schimba adresa unui site WordPress în baza de date?
Fă întâi o copie completă a bazei de date, apoi rulează comanda WP-CLI wp search-replace, prima dată ca dry run. WP-CLI deserializează fiecare valoare, înlocuiește în interiorul structurii decodate și reserializează cu lungimile corectate. Pluginurile de migrare care înțeleg serializarea fac aceeași treabă din interfața de administrare, dacă nu ai acces la linia de comandă.
În ce tabele se află adresa veche?
În principal wp_options, wp_posts, wp_postmeta, wp_usermeta, wp_termmeta și wp_comments. Pericolul serializării stă în wp_options și în orice coloană meta_value, pentru că widgeturile, modificările temei, tranzienții și datele constructorilor de pagini sunt salvate ca tablouri. Pe multisite, wp_blogs, wp_site și wp_sitemeta poartă și ele domeniul.
Rezolvă modificarea bazei de date problema dacă adresa e definită în wp-config.php?
Nu. Dacă wp-config.php definește WP_HOME sau WP_SITEURL, constantele acelea au prioritate față de ce conțin siteurl și home în wp_options. Editarea bazei de date nu schimbă nimic vizibil până când constantele nu sunt actualizate sau șterse. Verifică wp-config.php întâi, pentru că acea singură linie explică multe migrări eșuate.
Ar trebui să înlocuiesc și coloana guid?
Nu. Câmpul guid este un identificator permanent pentru cititoarele de feed, nu o adresă funcțională, iar rescrierea lui poate face ca abonații la feed să vadă din nou toate articolele vechi ca fiind noi. Folosește skip-columns cu guid când rulezi WP-CLI. Singura excepție obișnuită este un site care nu a avut niciodată feeduri publice sau abonați.
Cum îmi dau seama dacă o valoare s-a stricat după un replace prost?
Valorile serializate stricate arată normal în baza de date, dar revin goale în WordPress. Widgeturile dispar, setările din personalizator se resetează, iar opțiunile pluginurilor revin la valorile implicite fără niciun mesaj de eroare. Activarea WP_DEBUG și WP_DEBUG_LOG scoate la iveală notificarea de deserializare. Compararea lungimii declarate cu numărul real de octeți confirmă direct problema.