PHP Session Handler: memcache vs memcached

ว่ากันด้วย Memcache ใน PHP มันมีโมดูลอยู่ 2 ตัว ซึ่งคอนฟิกต่างกันนิดหน่อย

ถ้าเอามาใช้เป็น session.save_handler แล้วเขียนผิดหรือเขียนสลับกัน มันจะพังเงียบๆ เลยจ้า

Memcache

PECL: http://pecl.php.net/memcache

Debian/Ubuntu: php5-memcache

session.save_handler = memcache
;session.save_path = "unix:///var/run/memcached/memcached.sock:0"
session.save_path = "tcp://127.0.0.1:11211?persistent=1&weight=1&timeout=1&retry_interval=15"

Memcached

PECL: http://pecl.php.net/memcached

Debian/Ubuntu: php5-memcached

session.save_handler = memcached
session.save_path = "127.0.0.1:11211"

ปิดระบบ Revision ใน WordPress

แก้ wp-config.php เติมลงไปว่า

define ('WP_POST_REVISIONS', 0);

ไปที่ db ของ WordPress แล้วรัน SQL ต่อไปนี้

DELETE FROM wp_posts WHERE post_type='revision'

ป.ล. ชื่อ table (wp_posts) อาจแตกต่างกันไป แล้วแต่ prefix ที่ตั้งค่าไว้

WordPress Bug: auto upgrade plugin ทำปลั๊กอินพัง

หลังจาก @lewcpe ช่วยลง FTP บนเครื่องที่ภาคให้แล้ว ก็ได้โอกาสลองใช้ฟีเจอร์สั่ง Auto-upgrade ของ wordpress ฟีเจอร์นี้คือการที่ wordpress จะทำการ upgrade ตัว plugin ให้เราโดยอัตโนมัติ เพียงแค่เรากรอก user/pass ของ FTP เข้าไปแล้ว WordPress จะเข้าไปจัดการให้เราเอง

 Wordpress Auto-Upgrade
ตัวอย่างหน้า Upgrade

เมื่อระบบพร้อม เราก็ทดลองกับ wassup plugin เป็นตัวแรก ทุกอย่างก็ผ่านไปด้วยดี จนกระทั่งขั้นตอนการอัพเกรดเสร็จเรียบร้อย แต่เมื่อกดไปหน้าไหนก็จะเจอข้อความโวยวายดังนี้อยู่บนหัว

Warning: include_once(…blabla…/blog/wp-content/plugins/wassup/) [function.include-once]: failed to open stream: Success in …blabla…/blog/wp-settings.php on line 425

จากข้อความข้างบน แปลว่ามีปัญหากับคำสั่ง include_once ในไฟล์ wp-settings.php บรรทัดที่ 425

เอาแล้วไง! ก็เริ่ม debug หาสาเหตุไปเรื่อยๆ จนพบว่ามันเกิดจากตัวแปร $plugin ที่ปกติต้องชี้ไปยังไฟล์ มันดันขึ้นเป็นโฟลเดอร์ แต่เจ้า include_once นั้นออกแบบมาให้อ่านไฟล์ มันก็เลยโวยวายออกมา

เรื่องที่ตัวแปร $plugin มี directory path โผล่มานี่ช่างมันเพราะขี้เกียจหา ประเด็นคือทำไม php มันยังรัน include_once ทั้งๆที่ก่อนหน้านั้นก็มี if คอยตรวจสอบตัวแปร $plugin อยู่ตั้งหลายเงื่อนไข ลองดูโค้ด

424
425
if (  != $plugin && 0 == validate_file($plugin) && file_exists(WP_PLUGIN_DIR . '/' . $plugin) )
    include_once(WP_PLUGIN_DIR . '/' . $plugin);

ถึงได้พบว่าปัญหาอยู่ตรงเงื่อนไขหลังสุด file_exists(path) เนื่องจากคำสั่งนี้จะ return TRUE ไม่ว่า path ที่ใส่มาจะเป็น file หรือ directory ก็ตาม ทำให้ค่า $plugin ที่ถูกส่งมาในสภาพ directory นั้นผ่านเงื่อนไขนี้ไปได้ แล้วไปรันต่อใน include_once บรรทัดถัดมา

อันนี้แก้ไม่ยาก แค่เปลี่ยนไปใช้คำสั่ง is_file(path) แทนก็จบ เพราะคำสั่งนี้ทั้งตรวจสอบว่า path ที่ส่งให้คือ file หรือเปล่า โค้ดใหม่ก็จะเป็น

424
425
if (  != $plugin && 0 == validate_file($plugin) && is_file(WP_PLUGIN_DIR . '/' . $plugin) )
    include_once(WP_PLUGIN_DIR . '/' . $plugin);

เพียงเท่านี้! ข้อความ Warning มากมายก็จะอันตรธานหายไป

ทีนี้เมื่อเราแก้ได้แล้วจะเก็บไว้คนเดียวก็ยังไงๆอยู่ ก็เลยตัดสินใจรายงานบั๊กไปยังทางทีมงาน WordPress เนี่ยแหละปัญหา หลังจากอ่านขั้นตอนการ Report Bugs แล้ว ก็เริ่มถอดใจ

ตอนแรกเกือบจะเปลี่ยนใจไม่แจ้งแล้ว แต่ลองไปค้นหาบั๊กเก่าๆดู ถึงได้พบว่ามีคนเจอปัญหาเดียวกันเมื่อสัปดาห์ที่แล้วนี่เอง แล้วเค้าก็เขียน Patch ไว้ให้เรียบร้อยแล้ว เป็นอันว่าคงแก้ไขในรุ่นที่จะถึงใกล้ๆนี้

Bug reported
Report Bug

wps.diff
Patch ที่มีคนส่งมาให้

ว่าแล้วก็มาอัพบล๊อกเก็บเป็นข้อมูลไว้ ถ้ามันยังไม่แก้ในรุ่นใกล้ๆ เผื่อเราลงใหม่จะได้รู้ว่าต้องแก้ไปตรงไหนบ้าง

รัน PHP4 และ PHP5 คู่กันบน Apache 2

พอดีช่วงนี้มีงานคอนฟิก Server เจ้าของเครื่องเขาจำเป็นต้องใช้ เลยไปลองหาๆวิธีมาทำดู เท่าที่อ่านมามีหลายวิธี แต่ที่ดูง่ายสุดคือรัน PHP5 เป็น Module ส่วน PHP4 รันเป็น CGI

ปัญหาแรกคือ Repository (Debian Lenny) ที่ใช้มันไม่มี PHP4 ให้โหลด (พึ่งมารู้ที่หลังว่าที่อื่นมี – -") เลยต้องโหลด Source มา Compile เอง ก็ขลุกขลักนิดหน่อย แต่สุดท้ายก็ Compile ผ่าน เสร็จแล้วก็ไปแก้คอนฟิก Apache ให้เฉพาะบาง vhost รัน .php ผ่าน PHP4 CGI (/cgi-bin/php4/php) แทนที่จะเป็น PHP5 Apache Module

วิธีคอนฟิก เริ่มแรก (หลังจาก Compile เสร็จ) ก็ Copy โฟลเดอร์ /sapi/cgi ไปไว้ที่ /home/php4/cgi เสร็จแล้วแก้ /etc/apache2/httpd.conf ให้อ่าน cgi-bin จากที่นี่

ScriptAlias /cgi-bin/php4/ /home/php4/cgi
<Directory "/home/php4/cgi">
    AllowOverride None
    Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
    Order allow,deny
    Allow from all
</Directory>

ที่เหลือก็แก้ vhost อันที่อยากให้เป็น PHP4 โดยเพิ่มคำสั่งพวกนี้เข้าไป

AddType application/php4 .php .phtml .php3
AddType application/php4-source .phps
Action application/php4 /cgi-bin/php4/php
Action application/php4-source /cgi-bin/php4/php

เท่านี้ vhost นั้นๆก็จะรันด้วย PHP4 แล้วครับ ทดสอบกันได้โดยคำสั่ง phpinfo();

แถมอีกนิด ถ้าอยากให้ vhost เดียวกันรันได้ทั้ง 2 รุ่น ก็ทำได้โดยแก้นามสกุลตรง AddType จาก php เป็นอย่างอื่น (เช่น .php4) แล้วทีนี้เวลาต้องการให้ script ไหนรันด้วย PHP4 ก็ตั้งนามสกุลเป็น .php4

AddType application/php4 .php4
Action application/php4 /cgi-bin/php4/php

ตามนี้เลยนะจ๊ะ :)

urlencode() : POST Data

หาสาเหตุตั้งนานว่าทำไมระบบ IMAP Login ที่เขียนไว้ใช้สำหรับลงทะเบียนค่าย eXceed มีปัญหากับบางคน ที่แท้เป็นเพราะลืมใส่ urlencode() ครอบ Username กับ Password ก่อนส่งข้อมูลไปยัง IMAP API

ถ้าใช้ imap_open() ตรงๆก็คงไม่มีปัญหา พอดีว่าข้อมูลถูกส่งผ่าน POST ไปยังตัว IMAP API ก่อนเพื่อเก็บ Log ที่จริงตอนแรก จำเป็นต้องมี API เพราะ Server อยู่ข้างนอกมหาลัยทำให้ต่อ IMAP เข้ามาไม่ได้ เลยต้องเอา API ไปรันไว้ในเครื่องที่ภาค แล้วค่อยต่อจากข้างนอกผ่าน POST แทน (แต่ ณ ตอนนี้ สามารถต่อ IMAP จากนอกมหาลัยได้แล้ว)

urlencode()

ปัญหาง่ายจริงๆ ไม่น่าโง่พลาดเลยเรา = ="