พัฒนา WordPress Plugin แบบกำหนดเอง: โครงสร้าง Hook และการเผยแพร่ปลั๊กอินน้ำหนักเบา
หนึ่ง เมื่อไหร่ควรเขียนปลั๊กอินเอง
พฤติกรรมกำหนดเองทั้งหมดใน WordPress ต้องผูกกับ Hook ไม่ว่าจะเล็กแค่เปลี่ยนอีเมลผู้ส่ง หรือใหญ่ถึงเพิ่มฟิลด์กำหนดเอง ก็แก้ได้ด้วย PHP ไม่กี่สิบบรรทัด ปัญหาคือหลายคนคิดถึงอย่างแรกคือไปค้นในคลังปลั๊กอิน
ตัดสินว่าจะติดตั้งปลั๊กอินหรือเขียนเอง: ถ้าความต้องการเป็นแค่ “เปลี่ยนพฤติกรรมตายตัวหนึ่งอย่าง” เช่น เปลี่ยนผู้ส่งเริ่มต้น เพิ่ม JS กำหนดเอง เปลี่ยนความยาวบทคัดย่อ การเขียนปลั๊กอินเองสะอาดกว่าติดตั้งของบุคคลที่สาม ดูกรอบการเลือกปลั๊กอินทั่วไปได้ที่เทคโนโลยีสำหรับเว็บไซต์แบรนด์ของตัวเองบน WordPress。
จุดที่ขัดกับสัญชาตญาณคือ functions.php ยิ่งเขียนยาวขึ้นไม่ใช่ความชำนาญ แต่เป็นการฝังระเบิดไว้ให้การเปลี่ยนธีมครั้งหน้า เมื่ออัปเดตหรือเปลี่ยนธีม functions.php จะถูกเขียนทับ โค้ดหายหมด วิธีที่ถูกต้องคือย้ายตรรกะกำหนดเองเข้าไปในปลั๊กอินแยก ธีมดูแลแค่รูปลักษณ์
การตั้งชื่อก็ต้องมีระเบียบ ชื่อโฟลเดอร์ปลั๊กอินใช้คำนำหน้าบวกฟังก์ชัน เช่น crater-inquiry-source เพื่อเลี่ยงชื่อซ้ำกับปลั๊กอินทางการ ชื่อฟังก์ชันก็ใส่คำนำหน้าเช่นกัน crater_register_source เพื่อป้องกันชนกับฟังก์ชันของปลั๊กอินอื่น
การชนกันของชื่อฟังก์ชันเป็นข้อผิดพลาดร้ายแรงที่พบบ่อยที่สุดของปลั๊กอิน WordPress ถ้าปลั๊กอินสองตัวเขียน function register_source() เหมือนกัน ตัวที่โหลดทีหลังจะจอขาวทันที ฟังก์ชันกำหนดเองทั้งหมดต้องมีคำนำหน้าเฉพาะ
| ขนาดความต้องการ | รูปแบบที่แนะนำ | จำนวนไฟล์ |
|---|---|---|
| แก้เล็ก ๆ 1 ถึง 3 อย่าง | ปลั๊กอินไฟล์เดียว | php 1 ไฟล์ |
| ฟังก์ชันที่เกี่ยวข้องกัน 3 ถึง 5 อย่าง | ปลั๊กอินไฟล์เดียว + ค่าคงที่ | php 1 ไฟล์ |
| ฟังก์ชันอิสระ 5 อย่างขึ้นไป | ปลั๊กอินโครงสร้างโฟลเดอร์ | ไฟล์หลัก + includes + assets |
| ต้องมีหน้าตั้งค่าในหลังบ้าน | ปลั๊กอินโครงสร้างโฟลเดอร์ | เพิ่ม admin-page.php |
สอง โครงสร้างขั้นต่ำของปลั๊กอินไฟล์เดียว
ปลั๊กอิน WordPress ที่รันได้ อย่างน้อยต้องการแค่ไฟล์ php ไฟล์เดียว มีคอมเมนต์หัวปลั๊กอินตอนต้นเพื่อบอก WP ว่านี่คือปลั๊กอิน วางไฟล์ไว้ใต้ wp-content/plugins/ แล้วหลังบ้านจะเห็น
คอมเมนต์หัวปลั๊กอินมีรูปแบบตายตัว: Plugin Name, Description, Version, Author ถ้าไม่มีคอมเมนต์นี้ WP จะไม่รู้จักว่าเป็นปลั๊กอิน
ตัวอย่างที่ 1: เปลี่ยนอีเมลผู้ส่งเริ่มต้น ทั้งปลั๊กอินมีแค่ 10 บรรทัด แขวน filter สองตัวคือ wp_mail_from กับ wp_mail_from_name แล้วเปลี่ยน wordpress@ เริ่มต้นเป็น noreply@example.com ไม่ต้องมีหน้าตั้งค่าเลย
ปลั๊กอินแบบใช้ filter ล้วนเหมาะที่สุดสำหรับเริ่มต้น ไม่มีฐานข้อมูล ไม่มีหน้าแอดมิน ไม่มี cron job ถ้ามีปัญหาก็แค่ปิดปลั๊กอินแล้วกลับมาเหมือนเดิม
ถ้าต้องเก็บค่าตั้งค่า ให้ใช้ตาราง options ที่ WordPress มีมาให้ เก็บเป็น array หนึ่งเรกคอร์ด ไม่ต้องสร้างตารางเอง ตั้งค่าง่ายๆ ใช้ get_option กับ update_option ก็พอ
| ประเภทของ hook | hook ที่ใช้บ่อย | ใช้ทำอะไร |
|---|---|---|
| filter | wp_mail_from、the_content | แก้ไขเนื้อหาที่แสดงผล |
| action | wp_head、admin_init | แทรกโค้ดหรือทำงานบางอย่าง |
| สิ่งที่ต้องทำในหลังบ้าน | admin_menu、admin_init | เพิ่มหน้าตั้งค่า ทำงานเฉพาะหลังบ้าน |
| action ฝั่งหน้าบ้าน | wp_enqueue_scripts | โหลด CSS/JS ทำงานเฉพาะหน้าบ้าน |
ตำแหน่งที่แขวน hook เป็นเรื่องสำคัญต่อประสิทธิภาพ admin_init จะทำงานเฉพาะตอนโหลดหลังบ้าน wp_head ทำงานเฉพาะตอนโหลดหน้าบ้าน อย่าเอา logic ที่ใช้เฉพาะหลังบ้านไปแขวนกับ init เพราะจะทำให้ทุก request ฝั่งหน้าบ้านทำงานเปล่าๆ ทุกครั้ง
การโหลด CSS และ JS ของตัวเองต้องใช้ hook wp_enqueue_scripts ห้าม echo ใน wp_head โดยตรง เพราะการ echo ตรงๆ จะทำให้สคริปต์ถูกโหลดซ้ำ
ตัวอย่างที่ 3: ปลั๊กอินต้องโหลดสคริปต์ตรวจสอบฟอร์มในหน้าฟอร์ม ใช้ wp_enqueue_script ลงทะเบียน ระบุ dependency เป็น jquery ตั้งพารามิเตอร์ตัวที่ห้าเป็น true เพื่อให้สคริปต์โหลดที่ส่วนท้าย ไม่บล็อกการแสดงผลหน้าแรก
สาม การเปิดใช้งาน การถอนการติดตั้ง และการล้างข้อมูล
เมื่อปลั๊กอินสร้างตารางหรือเขียน option ต้องจัดการตอนเปิดใช้งานและถอนการติดตั้ง เปิดใช้งานก็สร้างตาราง ถอนการติดตั้งก็ลบตาราง ไม่งั้นถอนปลั๊กอินไปแล้วยังมีขยะค้างในฐานข้อมูล
register_activation_hook ทำงานตอนเปิดใช้งานปลั๊กอิน เหมาะกับการสร้างตารางและเขียน option เริ่มต้น ส่วน uninstall.php ทำงานตอนลบปลั๊กอิน เหมาะกับการล้างข้อมูล สังเกตว่า deactivate กับ uninstall ไม่เหมือนกัน: ปิดใช้งานไม่ลบข้อมูล ลบปลั๊กอินถึงจะลบ
ตัวอย่างที่ 2: ปลั๊กอินบันทึกแหล่งที่มาของ inquiry ลงตารางที่สร้างเอง ตอนเปิดใช้งานใช้ dbDelta สร้างตาราง ตอนถอนการติดตั้งก็ DROP TABLE ถ้าผู้ใช้ปิดแล้วเปิดใหม่ ข้อมูลยังอยู่ ถ้าลบปลั๊กอิน ตารางก็ถูกลบไปด้วย
| อายุการใช้งาน | hook | สิ่งที่ต้องทำ |
|---|---|---|
| เปิดใช้งานปลั๊กอิน | register_activation_hook | สร้างตาราง เขียน option เริ่มต้น |
| ปิดใช้งานปลั๊กอิน | register_deactivation_hook | ล้าง cron job ไม่แตะข้อมูล |
| ลบปลั๊กอิน | uninstall.php | ลบตาราง ลบ option |
| ทุกครั้งที่โหลด | plugins_loaded | กำหนดค่าคงที่ โหลด text domain |
เรื่องความปลอดภัยที่ต้องระวัง: ในปลั๊กอินที่เขียนเอง ทุกจุดที่รับข้อมูลจากผู้ใช้ต้องใช้ $wpdb->prepare ในการประมวลผล ห้ามต่อ SQL ตรงๆ ส่วนการตั้งค่าพื้นฐานเรื่องบทบาทผู้ใช้และสิทธิ์หลังบ้าน ดูได้ที่WordPress Security Baseline ส่วนความเข้ากันได้ของเวอร์ชัน PHP ดูได้ที่การอัปเกรดเวอร์ชัน PHP สำหรับเว็บไซต์。
สี่ การเผยแพร่และดูแล: ปลั๊กอินส่วนตัวไม่ขึ้นคลังทางการ
ปลั๊กอินส่วนตัวที่เขียนให้เว็บลูกค้ารายเดียว ไม่ต้องส่งขึ้น wordpress.org แค่แพ็กเป็น zip แล้วอัปโหลดผ่านหลังบ้าน หรือ git clone ไปไว้ในโฟลเดอร์ wp-content/mu-plugins ก็ได้ ปลั๊กอินแบบ MU จะเปิดใช้งานอัตโนมัติและปิดผิดพลาดไม่ได้
ต้องทำ version management ด้วย เลข Version ในหัวปลั๊กอินให้เพิ่มขึ้นทุกครั้งที่แก้ แล้วหลังบ้านจะเห็นว่ามีอัปเดต อย่าแก้ไฟล์ปลั๊กอินบนเว็บ production โดยตรง ให้แก้ที่เครื่องตัวเองแล้วค่อยอัปโหลด
ปลั๊กอินที่ทีม crater พัฒนาเองหลายตัวก็ใช้แนวคิดนี้ ดูรายละเอียดได้ที่เทคโนโลยีสแตกของ crater ทั้งหมดนี้แก้ปัญหาเล็กๆ ที่เจาะจง และแต่ละตัวมีไม่เกินสองไฟล์
| วิธีเผยแพร่ | เหมาะกับ | ข้อดี | ความเสี่ยง |
|---|---|---|---|
| อัปโหลด zip ผ่านหลังบ้าน | ส่งมอบปลั๊กอินส่วนตัวให้ลูกค้า | ลูกค้าปิดใช้งานเองได้ | อาจถูกลบโดยไม่ตั้งใจ |
| โฟลเดอร์ mu-plugins | logic หลักห้ามหยุดทำงาน | เปิดใช้งานอัตโนมัติ ปิดผิดพลาดไม่ได้ | อัปเดตต้องทำเอง |
| ส่งขึ้น wordpress.org | ปลั๊กอินสาธารณะทั่วไป | อัปเดตอัตโนมัติ | ต้องผ่านการตรวจ เขียนเอกสาร |
อย่ามองข้ามการจัดการเลข Version ทุกครั้งที่แก้ให้เพิ่มเลข Version ในหัวปลั๊กอิน รายการปลั๊กอินในหลังบ้านจึงจะขึ้นป้ายว่ามีอัปเดต
ปลั๊กอินส่วนตัวที่ดูแลเองไม่มีช่องทางอัปเดตอย่างเป็นทางการ แนะนำให้ใช้ git จัดการ ตอน deploy ก็ git pull ลงโฟลเดอร์บนเซิร์ฟเวอร์ได้เลย อย่าแก้ไฟล์บนเว็บ production โดยตรง
FAQ คำถามที่พบบ่อย
กรณีไหนควรเขียนปลั๊กอินเองแทนการติดตั้งของคนอื่น?
เขียนโค้ดไว้ใน functions.php ของธีมไม่ได้หรือ?
ปลั๊กอิน WordPress ที่รันได้ขั้นต่ำต้องใช้กี่บรรทัด?
ปลั๊กอินสร้างตารางข้อมูลไว้ ตอนถอนการติดตั้งต้องลบเองไหม?
ปลั๊กอินส่วนตัวต้องส่งขึ้น wordpress.org ไหม?
ทำไมโค้ดปลั๊กอินของฉันถึงทำงานทุกครั้งที่เปิดหน้าเว็บ?
ทิ้งท้าย
คุณค่าของปลั๊กอินที่เขียนเองไม่ได้อยู่ที่การอวดฝีมือ แต่อยู่ที่การดึง logic เล็ก ๆ ที่ควบคุมได้กลับมาจากคลังปลั๊กอินของบุคคลที่สาม ปลั๊กอินบุคคลที่สามอัปเดตแล้วพฤติกรรมอาจเปลี่ยน แต่โค้ดของเราอยู่ในมือเราเสมอ
ลงมือสามขั้น: ย้าย logic ที่เขียนเองใน functions.php ทีละส่วนเข้าไปในปลั๊กอินไฟล์เดียว ถ้าสร้างตารางไว้ก็เขียน uninstall เพื่อล้างข้อมูล ปลั๊กอินส่วนตัวให้ทำเป็น zip หรือวางใน mu-plugins ไม่ต้องไปส่งขึ้นคลังทางการให้วุ่นวาย
คำถามที่เจอบ่อย: ควรใช้ Composer หรือ framework มาเขียนปลั๊กอินไหม? ปลั๊กอินเบา ๆ ที่ใช้กับเว็บลูกค้ารายเดียวไม่จำเป็น
การใส่ framework หมายถึงโฟลเดอร์ vendor จะเพิ่มขึ้นหลายสิบ MB และทุกครั้งที่อัปเดตต้องดูแล dependency เรื่องที่เขียน 300 บรรทัดจบได้ การใส่ framework คือสร้างความยุ่งยากให้ตัวเอง
จะคุ้มที่จะใช้ Composer และ namespace ก็ต่อเมื่อปลั๊กอินจะทำเป็นผลิตภัณฑ์สาธารณะ ต้องเขียน unit test และต้องมีหลายคนช่วยกันทำ
เสริมเรื่องความปลอดภัยอีกนิด: การจัดการฟอร์มหลังบ้านในปลั๊กอินต้องใช้ check_admin_referer ตรวจ nonce เพื่อป้องกันการปลอมแปลงคำขอข้ามไซต์ นี่คือมาตรฐานของฟอร์มหลังบ้าน WordPress ถ้าละเลยก็เท่ากับเปิดช่องหลังบ้านโล่ง ๆ
เวลาส่งข้อมูลที่ผู้ใช้กรอกออกไปหน้าเว็บ ต้องใช้ฟังก์ชัน escape อย่าง esc_html, esc_attr ไม่ใช่ echo ตรง ๆ ต่อให้เป็นค่าตั้งค่าที่เราเขียนเองในหลังบ้าน เวลาส่งออกหน้าบ้านก็ต้อง escape
สุดท้ายขอแนะนำลำดับการเริ่มต้น ครั้งแรกที่เขียนปลั๊กอิน ให้เริ่มจาก filter ล้วน ๆ อย่างการเปลี่ยนชื่อผู้ส่งหรือเปลี่ยนความยาว excerpt ยังไม่ต้องแตะฐานข้อมูล ไม่ต้องแตะหน้าหลังบ้าน
ทำให้ครบวงจรสักครั้งตั้งแต่เขียนโค้ด อัปโหลด เปิดใช้งาน จนเห็นผล แล้วค่อย ๆ เพิ่มฟีเจอร์ ถ้าเริ่มแรกก็เขียนปลั๊กอินที่มีตารางข้อมูลและหน้าหลังบ้านเลย จะสับสนกับ hook และ lifecycle ได้ง่าย
ปลั๊กอินยิ่งเขียนยิ่งเล็กคือสิ่งที่ถูกต้อง ก่อนเพิ่มฟีเจอร์ทุกครั้งให้ถามตัวเองก่อนว่าใช้ hook ที่มีอยู่เขียนสักยี่สิบบรรทัดจบได้ไหม ถ้าได้ก็ไม่ต้องสร้างตาราง ถ้าได้ก็ไม่ต้องเพิ่มหน้าหลังบ้าน
คุมปริมาณโค้ดปลั๊กอินให้ไม่เกิน 300 บรรทัด คือความรับผิดชอบต่อตัวเองและต่อลูกค้า ถ้าเกินระดับนี้ ส่วนใหญ่แสดงว่าถึงเวลาต้องใช้ปลั๊กอินสำเร็จรูปจากบุคคลที่สามแล้ว
ย้ำอีกครั้งเป็นครั้งสุดท้าย: อย่าเอาตัวอย่าง logic ที่เขียนเองไปกองไว้ใน functions.php ของธีม ย้ายเข้าไปในปลั๊กอินแยก ธีมถึงจะเปลี่ยนได้อย่างสบายใจ และลูกค้าถึงจะดูแลเองได้ในระยะยาว
ประโยคเดียวที่จำง่าย: เรื่องที่แก้ได้ด้วย hook 20 บรรทัด อย่าไปติดตั้งปลั๊กอินบุคคลที่สาม 5000 บรรทัด — ยิ่งโค้ดน้อย ยิ่งควบคุมได้ง่าย
