EST. 2026 — บันทึกงานเซิร์ฟเวอร์จริง SYSADMIN · SECURITY · AI · DIRECTADMIN เล่มที่ 01
THE SYSADMIN ALMANAC
หน้าหลัก / Web / รัน mysql_* บน PHP 8.4 ด้วย shim ไฟล์เดียวผ่าน auto_prepend_file
Web

รัน mysql_* บน PHP 8.4 ด้วย shim ไฟล์เดียวผ่าน auto_prepend_file

โดย Ton-Or ·9 พ.ย. 2026·อ่าน 11 นาที
รัน mysql_* บน PHP 8.4 ด้วย shim ไฟล์เดียวผ่าน auto_prepend_file

shim ไม่ได้ทำให้โค้ดเก่าปลอดภัยขึ้น มันแค่ซื้อเวลาให้หน้าที่ตายกลับมาเปิดได้ระหว่างรอเขียนใหม่ และต้องจำกัดไว้ทีละเว็บ ไม่ใช่เปิดทั้งเครื่อง

สรุปใน 1 นาที

อาการ/ปัญหา
เว็บ PHP เก่าเรียก mysql_* ที่ถูกถอดตั้งแต่ PHP 7.0 หน้าเว็บเลยตาย
ใครควรอ่าน
คนดูแลเว็บ PHP เก่าบน PHP 8.4 ที่ยังไม่มีเวลาเขียนใหม่
ทางแก้แบบสั้น
ไฟล์เดียวนิยามฟังก์ชัน mysql_* บน mysqli โหลดผ่าน .user.ini ทีละเว็บ มีตัวย้อนกลับอัตโนมัติ เป็นการซื้อเวลา ไม่ได้ทำให้ปลอดภัยขึ้น
ข้ามไปขั้นตอน

พอดีโฮสต์รวมที่ผมดูแลต้องถอด PHP รุ่นที่หมดอายุออกไปทั้งหมด ทั้ง 5.6 และ 7.4 เพราะมีช่องโหว่ที่เจาะได้จากภายนอกตามที่เล่าไว้ใน ข่าว php2shell พอย้ายเว็บที่เหลือขึ้น 8.4 ปรากฏว่าเว็บเก่าที่ลูกค้าหลายรายเขียนไว้นานแล้วไม่กี่เว็บ มีบางหน้าตายสนิท สาเหตุไม่ซับซ้อน คือโค้ดเรียก mysql_connect() ซึ่งส่วนขยาย mysql_* ถูกถอดไปตั้งแต่ PHP 7.0 เจ้าของเว็บยังไม่มีงบเขียนใหม่ ส่วนจะนั่งแก้ทีละไฟล์ก็ใหญ่เกินกว่าจะทำวันเดียว ผมเลยหาวิธีที่ทำให้หน้าเดิมกลับมาเปิดได้ก่อนโดยไม่แตะโค้ดของลูกค้า

อาการ ฟังก์ชันที่โค้ดเรียกไม่มีอยู่ใน 8.4

หน้าที่ตายขึ้นข้อความเดียวกันทุกหน้า และ PHP ตอบ 500

PHP Fatal error:  Uncaught Error: Call to undefined function mysql_connect()

ทางลัดที่ใครก็นึกถึงคือใช้ sed แทนชื่อฟังก์ชันจาก mysql_ เป็น mysqli_ ทั้งต้นไม้ ผมตัดทางนี้ทิ้งทันที เพราะลำดับอาร์กิวเมนต์ไม่เหมือนกัน mysql_query($sql, $link) ต้องกลับเป็น mysqli_query($link, $sql) และโค้ดเก่าเรียกโดยไม่ใส่ link เยอะมาก การแทนที่ด้วยข้อความจะได้โค้ดที่คอมไพล์ผ่านแต่พังตอนรัน ซ้ำยังต้องเขียนทับไฟล์ของลูกค้าทุกไฟล์ ผมไม่อยากให้ใครต้องรับผิดชอบการแก้แบบนั้น

ก่อนเลือกทางแก้ ผมอยากรู้ขนาดของงานก่อนว่าฟังก์ชันที่ตายกระจายอยู่ตรงไหนบ้าง ผมใช้ grep อ่านอย่างเดียวไล่ทั้งโฟลเดอร์ของเว็บแล้วนับจำนวนไฟล์ต่อชนิดของฟังก์ชัน ผลที่ผมต้องการไม่ใช่รายการสวย ๆ แต่คือคำตอบว่าชุดฟังก์ชันที่ shim ต้องครอบมีขอบเขตแค่ไหน

# นับไฟล์ที่เรียกฟังก์ชันที่ถูกถอด แยกตามชื่อ (อ่านอย่างเดียว)
cd /home/webuser/domains/example.com/public_html
for fn in mysql_connect mysql_query mysql_fetch_array mysql_fetch_assoc \
          mysql_num_rows mysql_real_escape_string ereg split each mcrypt_encrypt; do
  printf '%-26s %s\n' "$fn" "$(grep -rlE "\b${fn}[[:space:]]*\(" --include='*.php' . | wc -l)"
done

ถ้านับแล้วส่วนใหญ่เป็นกลุ่ม mysql_* ที่มีคู่เทียบตรง ๆ บน mysqli แปลว่า shim คุ้มที่จะทำ ส่วนของที่ไม่มีคู่เทียบอย่าง mcrypt_* จะโผล่แยกให้เห็นชัด ซึ่งเป็นตัวบอกว่าตรงไหน shim ช่วยไม่ได้ ผมใช้ตัวเลขนี้คุยกับเจ้าของเว็บก่อนลงมือว่าจะกู้ได้กี่ส่วน

หลักคิด ประกาศฟังก์ชันที่หายไปแทนที่จะแก้ผู้เรียก

แนวที่ผมใช้คือกลับด้าน ไม่แก้โค้ดที่เรียก แต่ทำให้ฟังก์ชันที่ถูกเรียกกลับมามีตัวตน เขียนไฟล์ PHP หนึ่งไฟล์ที่นิยาม mysql_connect, mysql_query และพวกเดียวกันไว้บนฐาน mysqli แล้วให้ PHP โหลดไฟล์นี้ก่อนโค้ดของเว็บทุกครั้งด้วย auto_prepend_file

สำหรับ PHP-FPM ตั้งค่านี้ต่อเว็บได้ผ่านไฟล์ .user.ini ในโฟลเดอร์ราก ไม่ต้องแตะ config ของ pool หรือของเครื่อง

; .user.ini ในโฟลเดอร์รากของเว็บที่ต้องการ
auto_prepend_file = /usr/local/php84/lib/legacy-shim/legacy_shim.php

FPM แคช .user.ini ไว้ 300 วินาทีเป็นค่าเริ่มต้น ถ้าไม่อยากรอก็ reload php-fpm แล้วมีผลทันที

browsernginxphp-fpm poolPHP เริ่มทำงาน.user.ini ของเว็บauto_prepend_file=legacy_shim.phpshim ประกาศฟังก์ชันที่ยังไม่มีแต่ละตัวเรียก mysqli_*หน้าเก่าเรียก mysql_connect()PHP 8 ไม่มีฟังก์ชัน = fatal 500หน้าเดิมรันต่อโดยไม่แก้โค้ดผ่าน shim -> mysqli -> 200ถ้าไม่มี shimมี shim
shim ถูกโหลดก่อนโค้ดของเว็บทุกครั้ง โค้ดเดิมเรียกฟังก์ชันชื่อเดิม แต่ข้างในคือ mysqli

ข้อสำคัญของตำแหน่งไฟล์ คือต้องวาง shim ไว้ในโฟลเดอร์ที่อยู่ใน open_basedir ของทุก pool อยู่แล้ว ไม่งั้น PHP จะปฏิเสธการโหลดและทุกหน้าที่ตั้งค่าไว้ก็พังกว่าเดิม ผมเลยวางไว้ใต้ไดเรกทอรี lib ของ PHP 8.4 ในโฟลเดอร์ย่อยที่มีไฟล์นี้ไฟล์เดียว

ตารางข้างล่างคือความต่างของลำดับอาร์กิวเมนต์ที่ทำให้ sed แทนชื่อตรง ๆ ไม่ได้ผล และเป็นสิ่งที่ shim ต้องรับภาระแทน

ของเก่า ของ mysqli ความต่างที่ shim ซ่อนให้
mysql_query($sql, $link) mysqli_query($link, $sql) ลำดับอาร์กิวเมนต์สลับ และ link เป็นตัวเลือกได้
mysql_select_db($db, $link) mysqli_select_db($link, $db) สลับเหมือนกัน
mysql_real_escape_string($s) mysqli_real_escape_string($link, $s) ของเก่าไม่ต้องส่ง link
mysql_result($r, $row, $field) ไม่มีคู่ตรง ต้องเลื่อนแถวแล้วอ่านฟิลด์เอง
mysql_error() mysqli_error($link) ต้องรู้ว่า link ไหนคือล่าสุด

shim ที่ผมเขียน และการตัดสินใจสามข้อในนั้น

ตัวไฟล์ครอบทุกฟังก์ชันด้วย function_exists เพื่อไม่ชนกับของจริงถ้าวันหนึ่งมีส่วนขยายกลับมา ด้านล่างนี้ย่อมาให้ดูแนว ตัวจริงยาวกว่านี้เพราะมีฟังก์ชัน fetch, num_rows, result, escape และอีกหลายตัว

<?php
// legacy_shim.php (ย่อ) ประกาศเฉพาะสิ่งที่ยังไม่มี
if (!function_exists('mysql_connect') && extension_loaded('mysqli')) {
    $GLOBALS['__legacy_mysql_link'] = null;

    function __legacy_link($link = null) {
        if ($link instanceof mysqli) return $link;
        return $GLOBALS['__legacy_mysql_link'] instanceof mysqli ? $GLOBALS['__legacy_mysql_link'] : null;
    }
    function mysql_connect($server = null, $user = null, $pass = null) {
        mysqli_report(MYSQLI_REPORT_OFF);          // ของเก่าคืน false ไม่โยน exception
        $l = @mysqli_connect($server ?: 'localhost', $user, $pass);
        if (!$l) return false;
        mysqli_set_charset($l, 'latin1');          // เหมือนส่วนขยายเก่า
        $GLOBALS['__legacy_mysql_link'] = $l;
        return $l;
    }
    function mysql_query($sql, $link = null) {
        $l = __legacy_link($link);
        return $l ? mysqli_query($l, $sql) : false;
    }
}

สามข้อที่ผมตัดสินใจจงใจ และเหตุผล

  1. ปิด mysqli_report ตอน connect ตั้งแต่ PHP 8.1 mysqli โยน exception เป็นค่าเริ่มต้น แต่โค้ดเก่าเขียนบนสมมติฐานว่าฟังก์ชันคืน false เมื่อพลาด ถ้าไม่ปิด จะไปชนเรื่องเดียวกับที่เล่าใน บทความเรื่อง mysqli โยน exception
  2. charset เริ่มต้นเป็น latin1 เหมือนส่วนขยายเก่า ข้อมูลภาษาไทยที่เก็บเป็นไบต์ดิบในฐานแบบ latin1 จะยังแสดงถูก ถ้าหน้าไหนสั่ง SET NAMES เองก็ยังทับค่านี้ได้ตามเดิม
  3. เก็บ link ล่าสุดไว้ใน global เพราะโค้ดเก่าเรียก mysql_query โดยไม่ส่ง link เยอะมาก ฟังก์ชันต้องหา connection ล่าสุดเองให้เหมือนเดิม

นอกจาก mysql_* ผมครอบกลุ่มที่ PHP 7 และ 8 ถอดไปพร้อมกันด้วย คือ ereg, eregi, split ที่ map ไปเป็น preg_*, get_magic_quotes_gpc ที่คืน false เสมอ และ each() ทั้งหมดนี้ประกาศเฉพาะตอนที่ฟังก์ชันนั้นยังไม่มี

หัวแปลงปลั๊กทองเหลืองแบบเก่าที่เสียบคั่นระหว่างเครื่องใช้ไฟฟ้าสองขาในอดีตกับเต้าเสียบผนังรุ่นใหม่ มีป้ายเขียนว่าชั่วคราว
shim ทำหน้าที่เหมือนหัวแปลงปลั๊ก เครื่องเก่ายังใช้ได้โดยไม่ต้องเปลี่ยนที่ตัวเครื่อง แต่เป็นของชั่วคราวเสมอ

ทำไมเลือก auto_prepend_file ต่อเว็บ ไม่ใช่วิธีอื่น

ผมชั่งน้ำหนักอยู่สี่ทางก่อนเลือก ตารางนี้คือเหตุผลที่ตัดสินใจ

ทาง ต้องแตะไฟล์ลูกค้าไหม ขอบเขตผลกระทบ ย้อนกลับ
sed แทนชื่อฟังก์ชัน ต้อง ทุกไฟล์ เว็บนั้น ยาก ต้องมีสำรองเต็ม
เพิ่ม include ท้ายทุกไฟล์ ต้อง ทุกไฟล์ เว็บนั้น ต้องลบทีละไฟล์
auto_prepend_file ต่อเว็บ ไม่ต้อง แตะแค่ .user.ini เว็บนั้นเท่านั้น ลบบรรทัดเดียว
auto_prepend_file ใน php.ini ของ pool ไม่ต้อง ทุกเว็บใน pool ลบแล้ว reload

ทางสุดท้ายดูง่ายสุด แต่มันบังคับให้ทุกเว็บใน pool ใช้ shim รวมถึงเว็บที่เขียนถูกต้องและไม่ต้องการ ผมไม่อยากให้โค้ดที่ปลอดภัยดีอยู่แล้วต้องมาเดินผ่านชั้นจำลองของเก่า ทางต่อเว็บเลยเป็นทางเดียวที่แตะเฉพาะเว็บที่ป่วย และถอดออกได้โดยไม่กระทบเว็บอื่นเลย

เปิดทีละเว็บ พร้อมตัวย้อนกลับ

การเปิดบนโค้ดของคนอื่นต้องมีตัวกันพลาด ผมเขียนสคริปต์ที่ทำเป็นลำดับตามผัง เริ่มจากวัดสภาพก่อนแตะ แล้วเปิดแล้ววัดซ้ำ ถ้าหน้าไหนแย่ลงถอดเฉพาะเว็บนั้นออกเอง

1 probe ก่อน3-4 หน้าต่อเว็บcurl --resolve2 เพิ่มบรรทัดใน .user.iniสำรองของเดิม3 reloadphp-fpm4 probe หลังเก็บ after.txt5 เทียบผล<500 -> >=500ถอดเองshim ต้องอยู่ในโฟลเดอร์ที่open_basedir ของ pool อนุญาต/usr/local/php84/lib/legacy-shim/หน้าไหนแย่ลง ถอดบรรทัด shimเฉพาะเว็บนั้นออกเองเว็บอื่นที่ผ่านยังอยู่ต่อ
ซ้ายคือเงื่อนไขของตำแหน่งไฟล์ ขวาคือกติกาย้อนกลับที่ทำให้เปิดทีละเว็บได้โดยไม่ต้องลุ้น

probe ของผมดึงหน้าตัวอย่าง 3 ถึง 4 หน้าต่อเว็บผ่านต้นทางโดยตรงด้วย curl --resolve บันทึกสามอย่างคือ สถานะ HTTP ขนาดตัวหน้า และธงว่าเนื้อหามีข้อความ Fatal error หรือ Call to undefined function หรือไม่

probe() {   # host path -> "code size errflag"
  f=$(mktemp)
  c=$(curl -sk -o "$f" -w '%{http_code}' --max-time 40 \
        --resolve "$1:443:203.0.113.10" "https://$1$2")
  e=$(grep -ciE 'Fatal error|Call to undefined function|mysql_connect' "$f")
  echo "$c $(stat -c %s "$f") $e"; rm -f "$f"
}

# worse = หน้าที่เคยต่ำกว่า 500 แล้วกลายเป็น 500 ขึ้นไป -> ถอดบรรทัด shim ของเว็บนั้น
sed -i '/legacy_shim.php/d' "$DOCROOT/.user.ini"

สิทธิ์ของไฟล์เป็นเรื่องเล็กที่พลาดง่าย .user.ini ต้องเป็นของผู้ใช้เจ้าของเว็บและอ่านได้โดย pool ที่รันเว็บนั้น ผมตั้งเป็นเจ้าของเดียวกับโฟลเดอร์ราก และสิทธิ์ 644 ถ้าสร้างด้วย root แล้วลืมเปลี่ยนเจ้าของ บางเครื่องจะไม่ยอมอ่านหรือผู้ใช้เจ้าของเว็บแก้เองไม่ได้ ซึ่งเป็นปัญหาตอนต้องถอด

หลังเปิดแล้วนอกจากสถานะ HTTP ผมดู error log ของ pool ด้วยว่ามีบรรทัดใหม่ที่เกี่ยวกับ shim หรือ mysqli ไหม เพราะหน้าที่คืน 200 แต่ข้อมูลบนหน้าผิดจะไม่โผล่ในตัวเลขสถานะ

# ดูข้อความผิดพลาดใหม่ของ pool หลังเปิด shim (ปรับพาธ log ตามเครื่อง)
tail -n 200 /var/log/php-fpm84/error.log | grep -Ei 'mysqli|mysql_|legacy_shim' | tail -n 20

ก่อนเพิ่มบรรทัดผมสำรอง .user.ini เดิมของเว็บไว้ ถ้ามี และเก็บคำสั่งถอดไว้ในไฟล์ RESTORE ข้างกัน

ไม่กี่เว็บเก่าที่เปิด shim
5หน้าที่กลับมา 200
300 วิแคช .user.ini
0ไฟล์ลูกค้าที่แก้

ผลหลังเปิดคือหน้าที่ตายห้าหน้ากลับมาเป็น 200 ครบทุกเว็บ และมีหนึ่งจุดที่ต้องแก้เพิ่ม เป็นบรรทัดเปรียบเทียบที่เขียนว่า == NA โดยไม่ใส่เครื่องหมายคำพูด ใน PHP 8 ตัวอักษรเปล่าแบบนี้คือ constant ที่ไม่มีอยู่ และเป็น fatal ต้องแก้เป็น 'NA' ซึ่งเป็นการแก้โค้ดของเว็บจริงหนึ่งบรรทัด ไม่เกี่ยวกับ shim

<?php
// ก่อนแก้: PHP 7 เตือนแล้วตีความ NA เป็นสตริง แต่ PHP 8 โยน Error "Undefined constant"
if ($row['grade'] == NA) { show_none(); }

// หลังแก้: ใส่เครื่องหมายคำพูดให้เป็นสตริงตรง ๆ
if ($row['grade'] == 'NA') { show_none(); }

เรื่องนี้เป็นตัวอย่างว่า shim ไม่ใช่ยาครอบจักรวาล มันกู้เฉพาะฟังก์ชันที่ถูกถอด ส่วนพฤติกรรมอื่นของภาษาที่เข้มขึ้นใน PHP 8 ยังต้องไล่ดูเอง ผมเลยรัน probe ก่อนและหลังเสมอ เพราะบางหน้าที่ shim แก้ได้แล้วอาจยังมีปัญหาอีกชั้นรออยู่ข้างหลัง และตัวเลข 500 ที่เหลือหลังเปิด shim คือรายการงานที่ต้องตามต่อ

ภาพก่อนและหลัง ฝั่งซ้ายหน้าเว็บองค์กรเล็กเป็นจอขาวแสดงข้อผิดพลาด ฝั่งขวาหน้าเดียวกันแสดงรายการข่าวพร้อมป้ายเล็กว่ากำลังรันผ่านตัวแปลง
จอขาวกลายเป็นหน้ารายการข่าวโดยไม่แตะโค้ดของหน้า แต่ป้ายเล็ก ๆ ที่แปะไว้ต้องอยู่ต่อจนกว่าจะเขียนใหม่

ตั้งแต่แรกต้องวางแผนว่าจะเลิกใช้ยังไง

shim ที่ไม่มีวันหมดอายุจะกลายเป็นของถาวรโดยไม่มีใครตั้งใจ ผมเลยตั้งหลักไว้ตั้งแต่ตอนเปิดว่ามันเป็นรายการหนี้ที่ต้องปิด วิธีที่ผมวางไว้คือเขียนใหม่ทีละหน้า เริ่มจากหน้าที่มีฟอร์มหรือรับข้อมูลจากผู้ใช้ก่อน เพราะเป็นจุดที่ SQL injection เจาะได้ง่ายที่สุด ส่วนหน้าที่แสดงข้อมูลอย่างเดียวค่อยตามมา

พอทุกไฟล์ของเว็บหนึ่งเลิกเรียก mysql_* แล้ว ก็ทดสอบด้วยการลบบรรทัด auto_prepend_file ออกจาก .user.ini ของเว็บนั้น แล้วรันชุด probe เดิมซ้ำ ถ้าทุกหน้ายังเป็น 200 ก็ลบไฟล์ .user.ini ทิ้งได้ ใช้ชุด probe ตัวเดียวกันทั้งตอนเปิดและตอนเลิก ผมถือว่าเป็นข้อดีของการวัดด้วยสคริปต์ตั้งแต่ต้น เพราะมันตอบคำถามว่า พร้อมเลิกหรือยัง ได้ด้วยเกณฑ์เดียวกับตอนเปิด

  1. เขียนหน้าที่รับข้อมูลจากผู้ใช้ใหม่ด้วย mysqli และ prepared statement ก่อน
  2. เขียนหน้าที่แสดงผลอย่างเดียวตามมา
  3. ลบบรรทัด auto_prepend_file ของเว็บนั้น แล้วรัน probe เดิม
  4. ทุกหน้ายังต่ำกว่า 500 ให้ลบ .user.ini และบันทึกว่าเว็บนี้เลิกใช้ shim แล้ว

อีกเรื่องที่ผมทำทุกเว็บคือยืนยันว่า PHP อ่าน .user.ini นั้นจริง ไม่ใช่เดาจากว่าหน้าเว็บกลับมา ผมวางไฟล์ PHP ชั่วคราวชื่อสุ่มในโฟลเดอร์รากของเว็บ เรียกผ่าน HTTPS หนึ่งครั้งแล้วลบทันที เหมือนวิธีพิสูจน์ที่ผมใช้กับค่า PHP อื่นบนเครื่องนี้ เพราะการตรวจใน CLI ไม่ได้อ่านไฟล์เดียวกับ pool ของ FPM

<?php
// ลบทิ้งทันทีหลังเรียกหนึ่งครั้ง
echo json_encode([
    'prepend' => ini_get('auto_prepend_file'),
    'shim'    => function_exists('mysql_connect'),
]);

ถ้า prepend ว่างเปล่า แปลว่า .user.ini ยังไม่ถูกอ่าน อาจเพราะเจ้าของไฟล์ผิด ชื่อไฟล์ผิด หรือยังติดแคช 300 วินาที ส่วน shim เป็น true เลยยืนยันว่าฟังก์ชันถูกประกาศแล้วจริง

ข้อจำกัดที่ต้องพูดให้ชัด

นี่คือทางผ่านชั่วคราว ไม่ใช่คำแนะนำ

shim จำลองความหมายของ API เก่าไว้ครบ รวมส่วนที่ไม่ปลอดภัยด้วย โค้ดเก่าที่ต่อสตริง SQL เอง ไม่ใช้ prepared statement ยังมีช่อง SQL injection อยู่เท่าเดิมทุกจุด เพียงแต่ตอนนี้มันรันได้บน 8.4 อีกครั้ง ผมไม่แนะนำให้ใช้กับโค้ดใหม่ และไม่แนะนำให้เปิดเป็นค่าของทั้งเครื่อง ต้องเปิดทีละเว็บ และมีแผนเขียนใหม่ควบคู่เสมอ

ข้อจำกัดอีกสองข้อ ข้อแรก mcrypt_* ไม่มี shim เพราะไม่มีทางแทนที่ตรง ๆ เว็บไหนที่ยังใช้มันอยู่ หน้าที่เรียกมันจะยังพังต่อไป ส่วนนั้นต้องเขียนใหม่จริง ข้อสองคือ .user.ini ใช้ได้กับ FPM หรือ CGI เท่านั้น ถ้าเว็บรันบน mod_php วิธีนี้ไม่มีผล

shim ต่อเว็บ

ซื้อเวลา เสี่ยงเท่าเดิม

หน้าที่ตายกลับมาเปิดได้ภายในไม่กี่นาที โดยไม่แตะไฟล์ลูกค้า ย้อนกลับได้ด้วยการลบบรรทัดเดียว แต่ช่องโหว่ในโค้ดเก่ายังอยู่ครบ

เขียนใหม่ด้วย mysqli

แก้ที่ต้นเหตุ ใช้เวลาและเงิน

ต้องไล่ทีละไฟล์ แก้ลำดับอาร์กิวเมนต์ เปลี่ยนมาใช้ prepared statement ปิดช่อง injection ได้จริง แต่เจ้าของเว็บต้องจ่ายและรอ

เรื่องความสัมพันธ์กับการย้ายเครื่องที่ฐานข้อมูลเข้มขึ้น ผมเขียนแยกไว้ใน บทความเรื่อง strict mode ของ MariaDB ซึ่งเป็นอีกชั้นหนึ่งที่เว็บเก่ามักสะดุดหลังย้าย

สรุปคือ shim ไฟล์เดียวพอจะกู้หน้าที่ตายเพราะ mysql_* กลับมาได้ ถ้าเปิดทีละเว็บ วัดก่อนหลัง และมีตัวถอดอัตโนมัติ ส่วนจะเขียนใหม่เมื่อไรขึ้นกับงบและเวลาของแต่ละเจ้าของเว็บ ผมเลือกใช้เฉพาะเว็บเหล่านี้ และจะลบบรรทัดออกทีละเว็บเมื่อเขียนใหม่เสร็จ เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ

อยากให้เราทำให้ไหม?

ให้เราช่วยติดตั้ง ตั้งค่า ย้ายระบบ หรือแก้ปัญหาเซิร์ฟเวอร์ เพื่อให้คุณมีเวลาไปทำงานที่สำคัญกว่า

คุยเรื่องงานกับเรา

เคยเจองานแบบนี้ไหม? ติดตรงไหน?

มาแลกวิธีกันต่อในบอร์ด LinuxThai

→ คุยต่อในบอร์ด

ความคิดเห็น 0

ยังไม่มีความคิดเห็น — เป็นคนแรกเลย