เรื่องของ PHP 6 เวอร์ชันที่ไม่เคยออก

PHP 6 ตั้งใจให้ทุก string ใน PHP เป็น Unicode แต่เลือกเก็บข้อความเป็น UTF-16 ในโลกที่ใช้ UTF-8 จนช้าและเข็นต่อไม่ไหว ทีม PHP พักโปรเจกต์ในปี 2010 แล้วโหวตข้ามเลข 6 ไปเป็น PHP 7 ในปี 2014 ทุกวันนี้ PHP จึงยังนับภาษาไทยเป็นไบต์

เผยแพร่
เวลาอ่าน
5 นาที
สรุปจาก
Eric L. Barnes · phpnews.net
ในโพสต์นี้ 8 หัวข้อ
  1. PHP มองข้อความเป็นไบต์ ไม่ใช่ตัวอักษร
  2. PHP 6 ตั้งใจให้ทุก string เป็น Unicode
  3. ฟีเจอร์อื่นของ PHP 6 ออกไปก่อนใน PHP 5.3 และ 5.4
  4. UTF-16 ทำให้ PHP ต้องแปลงข้อความทุกครั้งที่ข้อมูลเข้าออก
  5. ทีม PHP พัก PHP 6 ไว้ในเดือนมีนาคม 2010
  6. หนังสือ PHP 6 ออกมาก่อนตัวภาษา
  7. ทีม PHP โหวตข้ามเลข 6 ไปเป็น PHP 7
  8. สิ่งที่ยังเหลือจาก PHP 6 ในโค้ดวันนี้

PHP มองข้อความเป็นไบต์ ไม่ใช่ตัวอักษร

string ใน PHP เป็นแค่ลำดับของไบต์ PHP ไม่รู้ว่าไบต์เหล่านั้นเข้ารหัสแบบไหน ภาษาอังกฤษใช้หนึ่งไบต์ต่อตัวอักษรจึงไม่มีใครเห็นปัญหา แต่ตัวอักษรไทยใน UTF-8 ใช้สามไบต์ต่อตัว

strlen('ภาษาไทย');          // 21 นับเป็นไบต์
mb_strlen('ภาษาไทย');       // 7  นับเป็นตัวอักษร
grapheme_strlen('ที่นี่');   // 2  นับตามที่ตาเห็น

mb_strlen('ที่นี่') ได้ 6 เพราะสระบนกับวรรณยุกต์นับเป็นตัวอักษรแยก ถ้าจะตัดข้อความไปแสดงโดยไม่ให้วรรณยุกต์ลอยอยู่ตัวเดียว ต้องใช้ฟังก์ชัน grapheme_* จาก extension intl ส่วน Str::length() กับ Str::substr() ของ Laravel ใช้ mb_* อยู่ข้างใน ภาษาอื่นอย่าง Python 3 หรือ JavaScript ไม่ต้องคิดเรื่องนี้มากเท่า เพราะ string ของภาษาเหล่านั้นรู้จัก Unicode ตั้งแต่แรก

ในปี 2005 Andrei Zmievski เขียนในเอกสารออกแบบว่า PHP เป็นภาษาเดียวในสามตัวที่ขึ้นต้นด้วย P (Perl, Python, PHP) ที่ยังไม่รู้จักโลกหลายภาษา PHP 6 ตั้งใจแก้เรื่องนี้

PHP 6 ตั้งใจให้ทุก string เป็น Unicode

Andrei เริ่มงานนี้ที่ Yahoo ในเดือนกุมภาพันธ์ 2005 ร่วมกับคนจาก Zend และชุมชน PHP แผนที่ส่งเข้า mailing list ของทีมพัฒนา PHP (internals) ในเดือนสิงหาคมปีนั้นมีสามเรื่องหลัก

  • ใช้ ICU (International Components for Unicode) เป็นตัวจัดการ Unicode
  • เก็บข้อความใน PHP เป็น UTF-16 เพราะ ICU ใช้ UTF-16 อยู่แล้ว เอกสารยอมรับว่าเปลืองหน่วยความจำกับข้อความภาษาอังกฤษ แต่แลกกับการเขียนโค้ดที่ง่ายกว่า
  • มีสวิตช์ unicode_semantics เปิดโหมด Unicode ทั้งเว็บ และแยก string เป็นสองแบบ ข้อความ Unicode เป็นค่าเริ่มต้น ส่วนข้อมูลดิบใช้ b"..."

ปลายปี 2005 ทีมหลักแปดคนรวมทั้ง Rasmus Lerdorf ประชุมกันที่ปารีส แล้วตกลงว่า PHP 6 จะลบ register_globals, magic_quotes และ safe_mode ทิ้ง เพิ่ม namespace, late static binding (static::) และ integer แบบ 64 บิต บันทึกการประชุมครั้งนั้นมีสัญญาณเตือนอยู่แล้วว่าฟังก์ชัน string บางตัวช้าลงถึง 300% และทั้งแอปช้าลงถึง 25%

ฟีเจอร์อื่นของ PHP 6 ออกไปก่อนใน PHP 5.3 และ 5.4

ระหว่างที่งาน Unicode ยังไม่เสร็จ ฟีเจอร์อื่นในแผนก็ทยอยย้ายกลับมาลงสาย PHP 5

รุ่น ปีที่ออก ฟีเจอร์ที่เดิมตั้งใจไว้ให้ PHP 6
PHP 5.3 2009 namespace, late static binding, __callStatic และ extension intl
PHP 5.4 2012 trait, [] แทน array() และลบ register_globals, magic quotes, safe_mode

พอถึงตรงนี้สไลด์ของ Andrei เองก็สรุปว่า "PHP 6 = PHP 5 + Unicode" สิ่งเดียวที่เหลือให้ PHP 6 ทำคือเรื่องที่ยากที่สุด

UTF-16 ทำให้ PHP ต้องแปลงข้อความทุกครั้งที่ข้อมูลเข้าออก

ไฟล์ ฐานข้อมูล และ HTTP ใช้ UTF-8 กันเกือบหมด ถ้าข้างใน PHP เป็น UTF-16 ข้อความทุกชิ้นที่อ่านเข้ามาหรือส่งออกไปต้องแปลงหนึ่งรอบ Pierre Joye เขียนไว้ในปี 2010 ว่าข้อดีของ UTF-16 หายไปเกือบหมดกับต้นทุนการแปลงนี้ Stas Malyshev ชี้ทั้งเรื่องความเร็ว หน่วยความจำ และแอปเดิมที่พังแบบเดาไม่ได้ เช่นรหัสผ่านที่ใช้ไม่ได้เพราะ md5() ได้ข้อความที่แปลงมาอีกรอบ

ปัญหาอีกด้านคือคน ทุก extension ต้องแปลงให้รองรับ Unicode ซึ่งเป็นงานซ้ำ ๆ ที่น่าเบื่อ คนส่วนใหญ่ยังทำงานบน 5.2 กับ 5.3 ที่คนใช้จริง ปี 2011 Andrei เล่าในทอล์กของเขาว่าคนที่เข้าใจรายละเอียดของ Unicode กับ ICU จริง ๆ มีไม่ถึงสิบคน และถ้าเริ่มใหม่ได้ เขาน่าจะเลือก UTF-8

ทีม PHP พัก PHP 6 ไว้ในเดือนมีนาคม 2010

วันที่ 11 มีนาคม 2010 หลังถกกันว่าการแก้ใหญ่ชิ้นหนึ่งควรลง 5.3 หรือรอ 6 ก็มีคนเปิด branch 5.4 ขึ้นมาเลย อีกราวชั่วโมงต่อมา Rasmus ส่งอีเมลหัวข้อ "PHP 6" บอกว่างานนี้หยุดนิ่งมาพักใหญ่แล้ว ให้ย้ายโค้ด PHP 6 ไปไว้ใน branch แยก กลับมาพัฒนาสายหลัก และหาทางรองรับ Unicode ที่เบากว่าผ่าน mbstring กับ intl แทน

branch ของงาน Unicode ชุดนั้นยังอยู่ใน php-src บน GitHub ทุกวันนี้ ชื่อ experimental/first_unicode_implementation

หนังสือ PHP 6 ออกมาก่อนตัวภาษา

ระหว่างปี 2008 ถึง 2009 มีหนังสือหลายเล่มที่ใส่ PHP 6 ไว้บนปก เช่น PHP 6 and MySQL 5 for Dynamic Web Sites และ Professional PHP6 บางเล่มลากไปถึง MySQL 6 ที่ก็ไม่เคยออกเหมือนกัน RFC เรื่องชื่อรุ่นในปี 2014 ยอมรับว่าความเข้าใจผิดนี้ไม่ได้มาจากคนเขียนหนังสืออย่างเดียว เพราะนักพัฒนา PHP เองก็ไปพูดถึง PHP 6 ตามงานสัมมนาราวกับว่าแน่นอนแล้ว

ทีม PHP โหวตข้ามเลข 6 ไปเป็น PHP 7

ปี 2014 Dmitry Stogov เปิดตัว phpng ซึ่งเขียนแกนของ Zend Engine ใหม่จนเร็วขึ้นราว 20% ถึงกว่าเท่าตัวในแอปอย่าง WordPress ทีมโหวต 47 ต่อ 2 ให้ phpng เป็นฐานของรุ่นใหญ่ถัดไป คำถามที่เหลือคือจะเรียกรุ่นนี้ว่าอะไร

RFC "Name of Next Release of PHP" ของ Andrea Faulds ให้เลือกระหว่าง 6 กับ 7

  • ฝั่ง 6 บอกว่าเลขต่อจาก 5 ก็คือ 6 ตามหลัก semantic versioning
  • ฝั่ง 7 บอกว่ามี PHP 6 อยู่แล้ว และเป็นของคนละอย่าง ถ้าใช้ชื่อเดิมคนจะคิดว่าหนังสือและบทความเก่า ๆ ใช้ได้กับรุ่นใหม่

ผลออกมาเมื่อ 29 กรกฎาคม 2014 ฝั่ง 7 ชนะ 58 ต่อ 24 PHP 7.0 ออกในเดือนธันวาคม 2015 เร็วขึ้นราวเท่าตัวจาก 5.6 และมี scalar type, return type และ ?? ที่เราใช้กันทุกวัน แต่ไม่มี string แบบ Unicode และ PHP 8 ทุกรุ่นถึงตอนนี้ก็ยังไม่มี

สิ่งที่ยังเหลือจาก PHP 6 ในโค้ดวันนี้

  • b"..." กับ (binary) ใส่ไว้ตั้งแต่ PHP 5.2.1 เพื่อเตรียมรับ PHP 6 แต่ใน PHP 5 ขึ้นไปไม่มีผลอะไร (binary) เลิกใช้ (deprecated) ไปแล้วใน PHP 8.5 และ prefix b อยู่ในรายการที่จะเลิกใช้ใน PHP 8.7
  • extension intl เกิดจากงาน ICU ของ PHP 6 และยังเป็นทางที่ PHP ใช้จัดรูปแบบวันที่ ตัวเลข และเรียงข้อความตาม locale รวมถึงฟังก์ชัน grapheme_* ที่นับภาษาไทยตามที่ตาเห็น
  • ฟังก์ชัน mb_* ยังเป็นของที่ต้องใช้ทุกครั้งที่นับหรือตัดข้อความภาษาไทย ถ้าใช้ strlen() หรือ substr() ตรง ๆ ข้อความอาจขาดกลางตัวอักษร แล้วกลายเป็นตัว � บนหน้าเว็บ

เรื่องข้อความภาษาไทยกับการเข้ารหัสยังไปโผล่ที่อื่นด้วย อย่างไฟล์ CSV ที่เปิดใน Excel แล้วอ่านไม่ออก ซึ่งเล่าไว้ในวิธีทำไฟล์ CSV ภาษาไทยให้ Excel อ่านออก ส่วนใครที่อยากรู้ว่าแต่ละรุ่นของ PHP ได้รับการดูแลนานแค่ไหน ลองอ่านอายุ support ของ PHP แต่ละเวอร์ชัน ต่อได้ครับ

ต้นฉบับของ Eric L. Barnes เล่าละเอียดกว่านี้มาก ทั้งข้อความจาก mailing list และลิงก์ไปถึง commit จริงของแต่ละช่วง

อ่านต่อ

ล่าสุด

ดูทั้งหมด →