דילוג לתוכן
תצורה

חיפוש והחלפה של כתובת האתר במסד הנתונים של וורדפרס בלי להרוס נתונים מסודרתים

שינוי כתובת אתר וורדפרס במסד הנתונים בלי לרוקן וידג'טים, תפריטים או הגדרות תבנית. פקודת WP-CLI ששומרת על נתונים מסודרתים תקינים.

פורסם

אל תריץ UPDATE ... SET column = REPLACE(column, 'oldurl', 'newurl') פשוט על מסד נתונים של וורדפרס. הוא ישנה את הטקסט נכון ויהרוס את ההגדרות שלך באותו רגע, מפני שוורדפרס שומרת מערכים ואובייקטים כמחרוזות מסודרתות (serialized) של PHP, והפורמט הזה רושם את אורך הבתים של כל מחרוזת שהוא מכיל. החלפת SQL גולמית משכתבת את הטקסט אבל לעולם לא נוגעת באורך הרשום, והערך מפסיק להיות קריא.

השתמש במקום זה בכלי שמודע לסדרות. בשורת הפקודה זה wp search-replace. גבה קודם את מסד הנתונים, הרץ את זה כהרצת יבש, ורק אז הרץ באמת.

מה באמת נשבר

מחרוזת מסודרתת של PHP נראית כך:

s:18:"http://example.com"

ה-18 הוא אורך הבתים של הטקסט שבא אחריו. תספור — http://example.com הוא בדיוק 18 תווים.

עכשיו דמיין הגדרת תבנית שמורה:

a:1:{s:4:"logo";s:18:"http://example.com";}

הרץ החלפת SQL פשוטה של http:// ל-https:// ותקבל:

a:1:{s:4:"logo";s:19:"https://example.com";}

אלא שלא. מה שתקבל זה:

a:1:{s:4:"logo";s:18:"https://example.com";}

המחרוזת היא כעת באורך 19 בתים אבל עדיין מצהירה על 18. PHP קוראת את האורך המוצהר, מתקדמת 18 בתים קדימה, לא מוצאת את סימן הציטוט והנקודה-פסיק במקום שהיא מצפה להם, ומוותרת. כל המערך נכשל בפענוח.

למה זה נכשל בשקט

זה החלק שהופך את הבאג הזה ליקר כל כך. וורדפרס לא זורקת שגיאה ולא עוצרת. כשהיא קוראת אפשרות היא מעבירה את הערך דרך פונקציית הפענוח שלה, PHP מחזירה false עבור הנתונים הפגומים, ווורדפרס מוסרת לקוד שלך ערך שמתנהג כאילו האפשרות מעולם לא הוגדרה. התוסף או התבנית נופלים אז חזרה לברירת המחדל שלהם.

אז הסימפטום הוא לא עמוד שגיאה. הסימפטום הוא:

  • וידג’טים נעלמים מסרגלי הצד
  • הגדרות המתאים האישי מתאפסות לברירות המחדל של התבנית
  • נתוני הפריסה של בונה עמודים חוזרים ריקים והעמוד נטען ריק
  • עמודי ההגדרות של תוספים נראים כאילו זה עתה הותקנו
  • תפריטים מאבדים את המיקומים שהוקצו להם

שום דבר בממשק הניהול לא אומר שמשהו לא בסדר. השורה עדיין נמצאת במסד הנתונים, עדיין מלאה במה שנראה כמו טקסט נכון. רק קידומת האורך שגויה בבית או שניים. PHP כן פולטת הודעה כשהפענוח נכשל, אבל באתר בסביבת ייצור עם הצגת שגיאות כבויה, אף אחד לא רואה אותה. הפעלת WP_DEBUG יחד עם WP_DEBUG_LOG פעיל ו-WP_DEBUG_DISPLAY מוגדר כ-false תכניס את ההודעות האלה לקובץ לוג שבו תוכל לקרוא אותן.

הנזק גם לא מתפזר באופן אחיד. ערכים סקלריים פשוטים — siteurl,‏ home, תוכן פוסט רגיל, כתובת שיושבת בעמודה משלה — שורדים החלפה נאיבית בצורה מושלמת. רק ערכים מסודרתים נשברים. בגלל זה החלפה כושלת נראית לעיתים קרובות כאילו הצליחה במבט ראשון.

בדוק את wp-config.php לפני שאתה נוגע במשהו

אם wp-config.php מגדיר את WP_HOME או WP_SITEURL, הקבועים האלה גוברים על כל מה שנמצא במסד הנתונים. שינוי wp_options ייראה כאילו הוא לא עושה כלום. חפש שורות כמו אלה לפני שאתה מסיק שמסד הנתונים הוא הבעיה:

define( 'WP_HOME', 'http://example.com' );
define( 'WP_SITEURL', 'http://example.com' );

או שתעדכן אותן לכתובת החדשה, או שתסיר אותן ותיתן למסד הנתונים להוביל.

גבה קודם, ובאמת

שבירה של סדרות היא לא תמיד קלה להיפוך. ברגע שקידומת האורך והתוכן לא מסכימים, הערך המקורי אינו ניתן לשחזור מהערך השבור — אי אפשר לדעת מה האורך הנכון היה אמור להיות בלי לדעת את המחרוזת המקורית. גיבוי הוא הביטול האמיתי היחיד.

wp db export backup-before-replace.sql

או עם mysqldump אם WP-CLI לא זמין:

mysqldump -u USER -p DBNAME > backup-before-replace.sql

שמור אותו מחוץ לשרת אם אתה יכול. גיבוי שיושב בשורש האתר הוא גם נקודת שחזור וגם בעיית אבטחה.

הפקודה הבטוחה

ה-search-replace של WP-CLI מפענחת כל ערך, עוברת על המבנה המפוענח, מחליפה בתוכו, ומסדרתת מחדש עם אורכים נכונים. הרצת יבש קודם:

wp search-replace 'http://example.com' 'https://example.com' --dry-run --all-tables --skip-columns=guid

קרא את הדוח. הוא מספר לך כמה החלפות יתבצעו בכל טבלה ועמודה. אם רשימת הטבלאות נראית שגויה, או שלטבלה שאתה לא מזהה יש אלפי התאמות, עצור ותסתכל לפני שאתה מריץ באמת.

ואז הרץ:

wp search-replace 'http://example.com' 'https://example.com' --all-tables --skip-columns=guid --report-changed-only

הערות על הדגלים, בכנות:

  • --all-tables מכסה טבלאות שוורדפרס לא רושמת בעצמה, ושם לעיתים קרובות יושבים נתוני תוספים. בלעדיו, חלק מטבלאות התוספים מדולגות לגמרי.
  • --skip-columns=guid חשוב. ה-guid הוא מזהה קבוע עבור קוראי פיד, לא כתובת חיה. שכתוב שלו עלול לגרום למנויי הפיד לראות את כל הארכיון שלך כפוסטים חדשים.
  • חפש בלי הלוכסן הסופי ובלי גרסאות הפרוטוקול שאתה לא רוצה לגעת בהן. החלפה של example.com חשוף תפגע גם בכתובות דוא”ל ובכל אזכור בתוך טקסט הפוסטים.

אם אינך יכול להשתמש בשורת הפקודה, תוספי מיגרציה שמודעים לסדרות עושים את אותה עבודת פענוח דרך ממשק הניהול. ההתנהגות שאתה מחפש מתוארת במפורש כטיפול בנתונים מסודרתים; אם כלי לא אומר את זה, הנח שהוא לא עושה את זה.

אילו טבלאות ועמודות מעורבות

טבלהעמודהסיכון סדרות
wp_optionsoption_valueגבוה — וידג’טים, שינויי תבנית, טרנזיינטים, רוב הגדרות התוספים
wp_postmetameta_valueגבוה — פריסות של בוני עמודים, מערכים של שדות מותאמים
wp_usermetameta_valueגבוה — הרשאות, העדפות מסך ולוח בקרה
wp_termmetameta_valueבינוני — תלוי בתוספים
wp_postspost_content, guidנמוך לתוכן, אבל מאפייני קיצורי קוד וסימון בלוקים נושאים כתובות
wp_commentscomment_content, comment_author_urlנמוך
wp_linkslink_url, link_imageנמוך, ולרוב לא בשימוש

באתר רשת, הוסף את wp_blogs,‏ wp_site,‏ wp_sitemeta, ואת ה-wp_2_options,‏ wp_3_options וכן הלאה של כל אתר. שינויי כתובת באתר רשת כפופים לכללים נוספים מעבר לחיפוש והחלפה — התייחס לזה כמשימה נפרדת.

המקרים ש-search-replace לא פותרת במלואם

בכנות לגבי הגבולות:

JSON ששמור בתוך מסד הנתונים. חלק מבוני העמודים שומרים נתוני פריסה כ-JSON עם לוכסנים קדמיים מוברחים, כך שהכתובת שלך מופיעה כ-https:\/\/example.com ולא כ-https://example.com. חיפוש של הצורה הרגילה יפספס אותה. ייתכן שתצטרך מעבר שני על המחרוזת המוברחת. בדוק על עותק.

ערכים מקודדי Base64. אם תוסף מקודד הגדרות לפני שמירתן, שום החלפה מבוססת טקסט לא תוכל לראות את הכתובת בכלל. את ההגדרות האלה בדרך כלל צריך להזין מחדש דרך המסך של התוסף עצמו.

כתובות קשיחות בקבצים. כל מה שנכתב לתוך functions.php, תבנית של תבנית בת, או נתיב נכס קשיח — לא נמצא במסד הנתונים ולא ייגע בו. סרוק את ספריית התבנית בנפרד.

פלט שמור במטמון ופלט מיוצר. מטמון אובייקטים, מטמון עמודים ועותקי CDN ממשיכים להגיש את הכתובות הישנות גם אחרי החלפה נכונה. נקה אותם, ואז בדוק שוב לפני שאתה מניח שההחלפה נכשלה.

אם כבר הרצת את השאילתה הרעה

שחזר את הגיבוי. זו התשובה, ועדיף לומר אותה בפשטות מאשר להציע נוהל תיקון שברוב המקרים לא עובד.

אם אין גיבוי, המסלול הניתן לשחזור צר: ערכים שמעולם לא סודרתו תקינים, ואת המסודרתים השבורים צריך לבנות מחדש ידנית או לאפס על ידי שמירה מחדש של מסך ההגדרות הרלוונטי. שמירה מחדש של אזור וידג’טים, בחירה מחדש של מיקום תפריט, או הזנה מחדש של הגדרות תוסף כותבות ערך חדש ומסודרת נכון על גבי השבור. מייגע, אבל עובד, וזה עדיף על השארת שורות פגומות במקומן שיבלבלו את מי שיסתכל על האתר אחריך.

כדי לייצר את הפקודות הנכונות לפני שאתה מריץ משהו, בנה אותן עם כלי החיפוש וההחלפה ב-SQL של וורדפרס — הוא מראה אילו עמודות בטוחות לטיפול ב-SQL רגיל ואילו דורשות טיפול שמודע לסדרות, כך שתוכל לראות את החלוקה לפני שאתה מתחייב לפקודה.

FAQ

שאלות

למה REPLACE רגיל ב-SQL הורס אתר וורדפרס?

מפני שוורדפרס שומרת מערכים ואובייקטים כטקסט מסודרת (serialized) של PHP, והפורמט הזה רושם את אורך המחרוזת בבתים עבור כל ערך בתוכו. REPLACE גולמי משנה את הטקסט אבל משאיר את מספר האורך הישן במקומו. PHP מסרבת אז לפענח את הערך, וורדפרס נופלת חזרה לברירת מחדל, וההגדרות נראות ריקות במקום שגויות.

מהי הדרך הבטוחה ביותר לשנות כתובת אתר וורדפרס במסד הנתונים?

בצע גיבוי מלא של מסד הנתונים, ואז הרץ את פקודת WP-CLI‏ wp search-replace עם הרצת יבש (dry run) תחילה. WP-CLI מפענחת כל ערך, מחליפה בתוך המבנה המפוענח, ומסדרתת מחדש עם אורכים מתוקנים. תוספי מיגרציה שמודעים לסדרות עושים את אותה עבודה דרך ממשק הניהול אם אין גישה לשורת הפקודה.

אילו טבלאות מחזיקות את הכתובת הישנה?

בעיקר wp_options,‏ wp_posts,‏ wp_postmeta,‏ wp_usermeta,‏ wp_termmeta ו-wp_comments. הסכנה של נתונים מסודרתים נמצאת ב-wp_options ובכל עמודת meta_value, מפני שווידג'טים, שינויי תבנית, טרנזיינטים ונתוני בוני עמודים נשמרים כמערכים. באתר רשת (multisite) גם wp_blogs,‏ wp_site ו-wp_sitemeta מחזיקים את הדומיין.

האם שינוי מסד הנתונים מתקן את הכתובת אם wp-config.php מגדיר אותה?

לא. אם wp-config.php מגדיר את WP_HOME או WP_SITEURL, הקבועים האלה גוברים על כל מה ש-siteurl ו-home מחזיקים ב-wp_options. עריכת מסד הנתונים לא תשנה שום דבר גלוי עד שהקבועים יעודכנו או יוסרו. בדוק את wp-config.php ראשון, כי השורה הבודדת הזאת מסבירה הרבה מיגרציות שנכשלו.

האם כדאי להחליף גם את עמודת ה-guid?

לא. ה-guid הוא מזהה קבוע עבור קוראי פיד, לא כתובת עובדת, ושכתוב שלו עלול לגרום למנויי הפיד לראות שוב כל פוסט ישן כחדש. העבר skip-columns עם guid כשאתה מריץ את WP-CLI. החריג הנפוץ היחיד הוא אתר שמעולם לא היו לו פידים ציבוריים או מנויים.

איך אני יודע אם ערך נשבר אחרי החלפה כושלת?

ערכים מסודרתים שבורים נראים תקינים במסד הנתונים אבל חוזרים ריקים בוורדפרס. וידג'טים נעלמים, הגדרות המתאים האישי מתאפסות, ואפשרויות של תוספים חוזרות לברירת המחדל בלי שום הודעת שגיאה. הפעלת WP_DEBUG ו-WP_DEBUG_LOG חושפת את הודעת הפענוח. השוואה בין אורך המחרוזת המוצהר לספירת הבתים האמיתית מאשרת את זה ישירות.