ข้ามไปที่เนื้อหาหลัก

เรื่องสยองของ Bug ใน PHP 7.4.18



เมื่อจู่ๆ วันหนึ่ง คุณก็ไม่สามารถที่จะ insert ข้อมูลใหม่ได้เพราะเจอ error ฟ้องกลับมาว่า .

operator does not exist: boolean = integer

หรือ

SQLSTATE[42804]: Datatype mismatch: 7 ERROR: column “column_boolean” is of type boolean but the expression is of type integer

เรื่องสยองหลายบรรทัดมันเกิดขึ้นในตอนที่ผม deploy PHP application ขึ้นไปยัง staging environment error ต่างๆ ก็ไหลมารวมกัน เนื่องจากใน code นั้นประกอบไปด้วย eloquent ที่เกี่ยวข้องกับ boolean เช่น

$this->model->where('is_active', true)->first();

หรือจะเป็นเรื่องของการ insert/update

// Insert
$this->model->update([
'email' => 'thanos@rabbit.co.th',
'is_active' => true
]);
// Update
$this->model->update(['is_active' => true]);

ซึ่งสาวก Laravel หรือ Lumen ที่กำลังใช้ Postgres น่าจะเจอกันอยู่นะ 5555+

Error รัวๆ แบบไม่เกรงใจพื้นที่ Log 5555+

แล้วทำไมถึงเกิดปัญหานี้ขึ้น? ทำไม production หรือ local ไม่เจอปัญหานี้ล่ะ?

ตอนแรกก็แอบสงสัย Eloquent ว่าได้มีแอบการแปลงค่าของ boolean กลายเป็น integer ซึ่งผมก็เข้าใจว่าปัญหาน่าจะเกิดจากทาง Illuminate เค้ามีการแก้ไขอะไรบางอย่าง …

FYI, ต้องบอกก่อนว่าตัว Application นั้นใช้ database ยี่ห้อ PostgreSQL ซึ่งทางคุณพี่เค้าก็ค่อนข้างที่จะ strict นิดหนึง ในเรื่องของ Datatype แล้วสาเหตุของปัญหาก็คือ เกิดการแปลงค่าจาก Boolean ไปเป็น integer ในตอนที่กำลังสร้าง query นั่นเอง

อ่านมาถึงตรงนี้ คงกำลังสงสัยว่าแล้วไหนล่ะ วิธีแก้ บวกกับยังไม่เข้าใจเรื่องสยองนี้ ว่าที่แท้จริงแล้วเกิดอะไรขึ้น? ไช่มั้ยครับ?

ไช่ ลีลา จริงๆ 555+

หลังจากหาข้อมูลอยู่เกือบครึ่งวัน จนทีมเริ่มสงสัยว่าจะมีใครเจอปัญหานี้เหมือนกันกับเราหรือเปล่า? และแล้วเราก็เจอต้นตอของปัญหา

ใน PHP เวอร์ชัน 7.4.18 และ 8.0.5 มีการเปลี่ยนกลไกลบางอย่างขึ้นส่งผลให้มีการ convert ค่า Boolean เป็น Integer ซึ่งนี่เองที่ทำให้เราถึงบางอ้อ และทางทีม PHP ก็ได้ออก Patch อัพเดทมาแก้ไขในเวอร์ชันถัดไปนั่นก็คือ 7.4.19, 8.0.6

แน่นอนว่าเพิ่งออกเมื่อวานเองน่ะ ผมจะแก้ไขปัญหายังไง? จึงเริ่มเช็คว่าที่ staging ใช้เวอร์ชันอะไรกัน ก็พบว่า อ๋อ เรากำลังใช้ เวอร์ชันที่เป็นปัญหาอยู่นั่นเอง และเนื่องด้วยว่าผมใช้ Docker ในการ build image โดยที่ไม่ได้กำหนดเวอร์ชันของ PHP ถึงขั้นของ minor ทำให้ตอนที่ deploy ไปที่ staging เลยไปหยิบเอาเวอร์ชันล่าสุดมา ดังนั้นผมควรเปลี่ยนจากเดิม

FROM php7.4-fpm-alpine >>>>>> FROM php7.4.16-fpm-alpine

เพื่อรอจนกว่าทาง Docker Official ของ PHP จะปล่อยเวอร์ชัน 7.4.19 มา ผมถึงจะกลับไปใช้แบบเดิม . . .

บทส่งท้าย

ที่ร่ายมาซะยาวก็เพียงแค่จะอยากจะแชร์เรื่องขำๆ ที่เที่ยวหาวิธีแก้ไปเรื่อย เรียกได้ว่าเกือบจะแก้ไขปัญหาโดยการ ครอบ string ไปบน boolean แล้วล่ะครับ 5555+ แบบว่าจนปัญญาแล้วจริงๆ

$this->model->where('is_active', 'true')->first();

ถ้าทำแบบนี้จริง มีหวังต้องทำ test ใหม่หมดเลยนะ โชคดีที่เจอทางออก ก่อนที่จะไปในทางที่ สยองกว่าเรื่องที่เล่ามาอีก 55555+

ขอบคุณนะครับที่อ่านมาจนถึงตรงนี้

ความคิดเห็น

โพสต์ยอดนิยมจากบล็อกนี้

การเพิ่ม Font ภาษาไทยอื่นๆ เข้าไปใช้งานใน MPDF

เนื่องมาจากผมได้มีโอกาสจับงานที่ต้องแปลงหน้าเว็บให้เป็น PDF ลองๆ หาข้อมูลก็ไปเจอเจ้าตัวนี้ MPDF  ซึ่งจริงๆ แล้วก็มีให้ใช้งานอีกหลายๆ ตัว สาเหตุที่เลือกใช้งานตัวนี้เพราะมัน ค่อนข้างจะติดตั้ง และใช้งานง่าย และยังสามารถอ้างอิง CSS เข้ามาช่วยจัดหน้าตาใน PDF ได้อีกด้วย ซึ่งผลที่ได้เพี้ยนไปจากตัวจริงไม่มากครับ แต่ที่ทำให้เลือกใช้งานเพราะการเพิ่มฟ้อนท์ภาษาไทย เข้าไปนั้นค่อนข้างง่ายครับ เลยจะขอมาแนะนำดังนี้

การใช้งาน คำสั่ง file_exists() อย่างเข้าใจ

นี่อาจจะไม่เรื่องใหม่อะไรสำหรับ Professional ทั้งหลาย แต่ว่าสำหรับผมที่เข้าใจ และใช้งานอย่างผิดๆ มาตลอด จนบางทีก็เข้าใจว่า เราเขียนผิด หรือ คำสั่งมันใช้งานไม่ได้ วันนี้จะขอมาพูดถึงเรื่องของคำสั่ง PHP ที่ชื่อ file_exists เป็นคำสั่งที่ใช้สำหรับตรวจสอบไฟล์ว่ามีอยู่จริงหรือไม่? ตัวอย่างการใช้งานแบบผิดๆ ที่ผมใช้ก็คือ file_exists('/images/news/helloworld.jpg'); ผลลัพธ์ที่ได้คือ FALSE ถึงแม้ว้าจะมีไฟล์นั้นอยู่จริงก็ตาม ซึ่งในความเป็นจริงแล้ว การใช้งานที่ถูกต้องคือ file_exists($_SERVER['DOCUMENT_ROOT'] . '/images/news/helloworld.jpg'); จากตัวอย่างที่ถูกต้อง ทำให้เข้าใจแบบง่ายๆ ว่าคำสั่ง file_exists นั้นใช้สำหรับเช็ค path ในโฟลเดอร์จริงๆ เท่านั้น ไม่สามารถเช็คจาก URL ได้ อันนี้เป็นเรื่องง่ายๆ ที่ผมเข้าใจผิดมาอยู่นานเลยทีเดียวเลยต้องขอลงบันทึกเตือนตัวเองไว้อีกที :3

มาเข้าใจ และใช้ Datatable ดึงข้อมูลแบบ Server-side ด้วย PHP, MySQL กันเถอะ

มีช่วงหนึงผมเคยนำ datatable มาพัฒนาในงานแต่เกิดปัญหาเนื่องจากมีข้อมูลขนาดใหญ่ ทำให้เกิดการโหลดในครั้งแรกที่โหลดหน้าเพจนั้นๆ เนื่องจากผมใช้ Ajax ในการโหลดข้อมูลทั้งหมดมาในครั้งเดียวด้วยจำนวนข้อมูล 1000 ขึ้น ซึ่งตอนนั้นผมคิดว่าการทำ preload น่าจะช่วยได้ แต่ว่าถ้า user เกิดเผลอไปกด refesh หรือแก้ไขข้อมูลเวลากลับมาที่หน้าข้อมูลก็ต้องโหลดใหม่อีก ทำให้ผมเลิกใช้ datatable ไปเลย เพราะคิดว่ามันคงไม่เหมาะ แต่ในความจริงแล้ว datatable ก็ได้มีสิ่งที่มาแก้ในจุดนั้นได้ ซึ่งเรียกว่า server-side โดยการที่อนุญาตให้เรา query ข้อมูลออกมาก่อนแล้วส่งมาให้ datatable อ่านข้อมูลในจำนวนที่น้อยลง หลักการก็เหมือนๆ กับบทความการทำสร้าง XML จากข้อมูลขนาดใหญ่นั่นแหละครับ แต่มีเงื่อนไขเพียงแต่ว่า ต้อง Filter และส่งข้อมูลออกมาในรูปแบบที่ตรงตามหลักของ datatable เท่านั้น ( บางครั้งเวลาเข้าไปอ่าน Document หรือดู Example จะงงๆ ว่าอะไรเยอะแยะ ) โดยวันนี้จะมาแนะนำการใช้งานแบบง่ายๆ กันเลย :D