כיצד להתגבר על בעיית הקידוד השגוי בעת העתקת בסיס נתונים של אתר דרופל לשרת חדש?

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

מה הבעיה?
למה זה קורה לי?
איך פותרים את הבעיה?
מוסר השכל

מה הבעיה?

רבים נתקלים בבעיה מאוד מטרידה, שלאחר שדרוג/גיבוי ואחזור/העתקה של אתר דרופל, העברית שבו נעלמת, ובמקומה מופיעים סימנים ידועים לשמצה, שאומרים: "ù$"'^ù**ùù*'ù"ç_é_'émùém'"", פחות או יותר. האבחנה הראשונה היא שמדובר בבעית קידוד: מישהו, דהיינו המחשב שלנו, האתר שלנו, או השרת שלנו, לא קוראים נכון את הנתונים שנשמרו בבסיס הנתונים, או שהם אינם יודעים להציג אותם, וכך נוצר הבלבול.
בקצרה מאוד על קידוד: קידוד הוא אוסף הכללים שעל פיהם מתבצע תרגום של מחרוזת מסויימת, בשפה שהמחשב מבין, לתו מסויים, בשפה שאנחנו מבינים. כלומר - את המחרוזת האקראית &x087b; (שאין לה משמעות ידועה לי) ניתן היה לפרש כאות עברית, על פי מערכת כללים אחת, או כאות הינדית, על פי מערכת כללים אחרת. הדבר דומה למילים משפות שונות, שנשמעות דומה, כמו המילה "לחם" בעברית, שמשמעותה בערבית - "בשר". אותה מילה לכאורה, אבל בכל שפה (= בכל קידוד) פירושה אחר.
דרופל מציגה, מעבירה ושומרת נתונים בקידוד utf-8, שהוא קידוד הכולל את רוב שפות העולם (ועל כן מאפשר להציג מספר שפות בדף בודד, מבלי לשנות את מערכת הכללים לקריאה). מסד הנתונים Mysql, מאפשר שמירה והעברה של נתונים בקידודים שונים, אך ברוב השרתים ברירת המחדל היא latin-1. פירושו של דבר הוא שדרופל מדברת שפה אחת ומסד הנתונים מבין שפה אחרת. בכך בעצם מתמצה הבעיה, ומיד נראה כיצד לפתור אותה.

למה זה קורה לי?

כי מגיע לך! בחרת בדרופל! יכולת לבחור במשהו אחר, אבל התעקשת!
בעצם אנחנו צריכים לשאול את עצמנו את השאלה ההפוכה - איך זה שעד היום הכל עבד כמו שצריך? ובכן, ככה זה עובד (עם קצת ריכוז וקריאה שניה ושלישית, זה נהיה מובן יותר):
ל-mysql יש מספר משתנים האומרים לו באיזה קידוד הוא משוחח עם הלקוח (=דרופל), וברירת המחדל איננה utf-8, אלא latin-1. תוכלו לראות זאת כאשר תריצו את השאילתא show variables. כלומר, mysql מקבל מחרוזות utf-8 מדרופל ומניח שהן מקודדות ב-latin-1. לכן המידע מאוכסן בצורה מחורבשת בבסיס הנתונים, לא באמת ב-utf-8. כש-mysql שולח את המידע בחזרה לדרופל, מתבצע חירבוש הפוך, ולכן, לכאורה, הכל תקין.

עיון ב-database.mysql.inc של דרופל 4.7 ומעלה תגלה את הפקודה הבאה:

mysql_query('SET NAMES "utf8"');

זוהי הפקודה האומרת ל-mysql שאנחנו משוחחים איתו ב-utf8. זה חיוני. עד דרופל 4.6 הפקודה לא נשלחה, ולכן mysql חשב שאנו משוחחים איתו בקידוד ברירת המחדל - latin-1, ולכן העניינים מתחרבשים.

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

איך פותרים את הבעיה?

יש לייצא את בסיס הנתונים "כהלכה" (לקובץ sql) ולייבא אותו לבסיס נתונים חדש: השיטה הפשוטה ביותר היא להשתמש ב-mysqldump, שזו תוכנית שמגיעה ביחד עם שרת ה-mysql, ולומר לה לשוחח עם mysql בקידוד latin-1:

mysqldump --default-character-set=latin1 db_name > db_name.sql

מתבצע כאן "החירבוש ההפוך" המפורסם, ולכן מתקבל קובץ utf8 (הפתעה!).
אח"כ יש לערוך את קובץ ה-sql המתקבל ולשנות את פקודת SET NAMES latin1 ל-SET NAMES utf8

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

מוסר השכל

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

תגובות

הסבה ל-4.7: איך התגברתי על הג'יבריש

לשלמות הדיון, אני מצרף קישור לפוסט שכתבתי לפני כשלושה חודשים, על איך טיפלתי בבעייה דומה: http://www.drupal.org.il/mysql-gibbrish

יש למעלה שגיאה קטנה

נא לתקן - mysqldump במקום myqldump :)