Menu

Saturday, November 11, 2017

[PHP - MQ] การติดตั้ง mqseries สำหรับ PHP บน Ubuntu
[PHP - MQ] How to Install mqseries for PHP on Ubuntu

เนื่องจากมีความจำเป็นต้องส่งข้อความไปยัง IBM MQ Server จากโปรแกรมที่พัฒนาด้วย Laravel framework ซึ่งเป็น PHP framework ทำให้ต้องใช้ mqseries เป็นตัวช่วยในการเชื่อมต่อและส่งผ่านข้อความไปยัง IBM MQ Server

ข้อสำคัญ mqseries สำหรับ PHP นี้ จะทำงานได้สมบูรณ์ก็ต่อเมื่อมีการติดตั้ง IBM MQ Client ไว้ภายในเครื่องด้วย

สมมติว่า ใช้ Homestead เป็น environment ในการพัฒนา และมี folder Project เป็น shared folder ใน Homestead

วิธีการติดตั้งก็จะเป็นตามข้างล่างนี้
  1. ดาวน์โหลด mqseries จาก http://pecl.php.net/package/mqseries เลือก 0.14.1 (0.14.2 มีปัญหา)
  2. เอาไฟล์ที่ดาวน์โหลดมา ไปไว้ใน folder Project/ibm
  3. เข้าไปในเครื่องของ Homestead ด้วยคำสั่ง vagrant ssh
  4. หลังจากเข้าไปในเครื่องแล้ว ให้รันคำสั่ง sudo su เพื่อเข้าใช้ในสิทธิ์ root
  5. ถ้ายังไม่เคย compile mqseries มาก่อน ให้รันคำสั่งดังนี้
    cd เข้า Project/ibm
    tar -zxvf mqseries-0.14.1.tgz
    cd mqseries-0.14.1
    phpize
    ./configure --with-libdir=lib64
    make
    
  6. สร้าง config file ของ mqseries ด้วยคำสั่งดังนี้
    cp modules/mqseries.so /usr/lib/php5/20131226
    
  7. สร้างไฟล์ /etc/php5/cli/conf.d/mqseries.ini และใส่ข้อความตามด้านล่างนี้แล้วบันทึก
    ; Enable mqseries extension module
    extension=mqseries.so
  8. ทำการ copy file ข้างต้นไปยัง /etc/php5/fpm/conf.d/ พร้อมแก้ไข permission ตามคำสั่งดังนี้
    cd /etc/php5
    cp cli/conf.d/mqseries.ini fpm/conf.d/
    cd /etc/php5/fpm/conf.d/
    chmod 777 mqseries.ini
    
  9. ทำการ restart service php5-fpm ตามคำสั่งดังนี้
    service php5-fpm restart
    
  10. ทดสอบการใช้งาน โดยลองรันคำสั่งดังนี้
    cd เข้า Project/ibm/mqseries-0.14.1/example
    php put.php
    

Saturday, October 28, 2017

[PHP] การใช้ preg_match สำหรับ validate Emoticon
[PHP] How to Use preg_match for Emoticon Validation

เนื่องจาก Emoticon แต่ละตัวจะมี unicode เฉพาะตัว ซึ่งสามารถดูได้จาก The Unicode Standard 10.0

แต่การ validate โดยใช้ preg_match ของ PHP จำเป็นจะต้องใช้ code ที่เป็น UTF-8 ซึ่งเราสามารถตรวจสอบได้จากเว็บ Unicode character inspector

สมมติว่า เราต้องการ validate Emoticon ตามข้างล่าง

😋 😐

เมื่อตรวจสอบ Unicode และ UTF-8 ของ Emoticon ข้างต้นจะได้ข้อมูลดังนี้
Unicode: 1F60B และ 1F610 ตามลำดับ
UTF-8: F0 9F 98 8B และ F0 9F 98 90 ตามลำดับ

เราจะใช้ค่าของ UTF-8 ในการเขียน RegEx สำหรับการ validate ดังนี้
UTF-8: F0 9F 98 8B
จะได้ RegEx: \x{F0}\x{9F}\x{98}\x{8B}

และ

UTF-8: F0 9F 98 90
จะได้ RegEx: \x{F0}\x{9F}\x{98}\x{90}

ตัวอย่างโค้ดของการ validate Emoticon ข้างต้นจะเป็นดังนี้
<?php

$value = '😋 😐';
echo preg_match('/^\x{F0}\x{9F}\x{98}\x{8B} \x{F0}\x{9F}\x{98}\x{90}$/', $value);

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับ Unicode Regular Expressions อ่านได้ที่ http://www.regular-expressions.info/unicode.html

Monday, August 14, 2017

[NodeJS] Optional Field ของ Joi ไม่อนุญาตค่า null และ empty string
[NodeJS] Optional Field of Joi Validator not Allow Null and Empty String

เนื่องจากใช้ HapiJS เป็น framework ของโปรเจค จึงใช้ Joi เป็น validator ซึ่งก็เหมือน Framework ตัวอื่นๆ คือ สามารถระบุได้ว่า field ใดนั้น required หรือ field ใดนั้นไม่ required

Joi มี method สำหรับการระบุข้างต้น คือ required() และ optional() ตามลำดับ ซึ่งถ้าไม่ระบุจะเป็น optional โดยอัตโนมัติ แต่มันไม่ได้จบเพียงเท่านี้ เพราะ Library ตัวนี้มีบางอย่างที่ขัดกับ validator ของ framework อื่นหลบซ่อนอยู่

สำหรับ required() จะเป็นปกติเหมือน framework ทั่วไป คือ field นั้นต้องมีค่า ซึ่งค่านั้นต้องไม่ใช่ null หรือ empty string

สำหรับ optional() จะมีความแตกต่างจาก framework อื่น คือ field นั้นต้องมีค่า (ซึ่งค่านั้นต้องไม่ใช่ null หรือ empty string) หรือ field นั้นต้องหายไปเลย (undefined) อย่างใดอย่างหนึ่งเท่านั้น

อย่างไรก็ตาม สำหรับ framework ทั่วไป ในกรณีที่ไม่ระบุว่า required ความเป็นไปได้ของ field นั้น คือ มีค่าเป็นอะไรก็ได้รวมถึง null และ empty string ด้วย หรือ field นั้นจะหายไปเลยก็ได้

จะเห็นได้ว่า Joi ต่อให้เป็น optional ก็ไม่สามารถส่งค่า null หรือ empty string มาได้ วิธีแก้ไข คือ เพิ่ม .allow(null) และ .allow('') ต่อท้าย field ที่เป็น optional ด้วย

ปล. มีคนเปิด Issue เกี่ยวกับเรื่องนี้ ให้รวมค่า null กับ empty string เป็น optional ของ Joi ด้วย แต่ก็ได้คำตอบว่า มันโคตรจะไม่ปกติเลยหรือมันไม่ใช่ common use case ซึ่งจากคำตอบก็รู้แล้วแน่ๆว่า จะไม่มีการแก้ไขเกี่ยวกับเรื่องนี้ในอนาคตแน่นอน

สำหรับข้อมูลเพิ่มเติม สามารถอ่านได้ที่
How to allow a string or null?
Joi.string().optional() doesn't treat an empty string as unset

[NodeJS] การแปลงวันที่จาก ค.ศ. เป็น พ.ศ. หรือจาก พ.ศ. เป็น ค.ศ.
[NodeJS] How to Convert Date from A.D. to B.E. or from B.E. to A.D.

ถ้าจะกล่าวถึง Library ที่เกี่ยวกับวันที่และเวลาสำหรับ NodeJS หลายๆคนคงคิดถึง momentjs งานนี้ก็เช่นกัน ด้วยการที่คิดว่าการแปลงปี ค.ศ. เป็น พ.ศ. สลับไปมา มันก็แค่เพิ่มหรือลบปีไป 543 ปีเท่านั้น

สมมติว่า dateStr เป็นวันที่ที่เป็นปี ค.ศ. ที่จะแปลงเป็นปี พ.ศ. ก็จะได้โค้ดประมาณนี้
let date = moment(dateStr, 'DD/MM/YYYY');
if (date.isValid()) {
    date.add(543, 'years');
    return date.format('DD/MM/YYYY');
}
หรือ
let date = moment(dateStr, 'DD/MM/YYYY');
if (date.isValid()) {
    date.subtract(543, 'years');
    return date.format('DD/MM/YYYY');
}
แต่งานนี้ก็ไม่ผ่าน Tester ฝีมือดีของเรา เมื่อ Tester ทำการทดสอบด้วยวันที่ 29 กุมภาพันธ์ของปีที่เป็น Leap year

กรณีที่ต้องการแปลงจาก ค.ศ. เป็น พ.ศ. ยกตัวอย่างเช่น 29 กุมภาพันธ์ ค.ศ. 1968 (29/02/1968) เมื่อทำการแปลงตามโค้ดข้างต้น สิ่งที่คาดหวังคือ 29 กุมภาพันธ์ พ.ศ. 2511 แต่สิ่งที่ได้ไม่เป็นเช่นนั้น

กรณีที่ต้องการแปลงจาก พ.ศ. เป็น ค.ศ. ยกตัวอย่างเช่น 29 กุมภาพันธ์ พ.ศ. 2511 (29/02/2511) เมื่อทำการแปลงตามโค้ดข้างต้น สิ่งที่คาดหวังคือ 29 กุมภาพันธ์ ค.ศ. 1968 แต่สิ่งที่ได้ คือ Invalid date

ทั้งสองกรณีเกิดจาก momentjs จะทำการแปลงวันที่ในรูปแบบปี ค.ศ. เท่านั้น โค้ดข้างต้นจึงเป็นการบวกลบปี ค.ศ. เพียงเท่านั้น ไม่ใช่การแปลงเป็นปี พ.ศ. แต่อย่างใด

หลังจากนั้น ก็ต้องเปลี่ยนวิธีการแปลงปีเป็นแบบข้อความแทน โดยใช้ Regular Expression ก็จะได้โค้ดตามด้านล่าง
const DATE_REGEXP: RegExp = new RegExp('^(0?[1-9]|[1-2][0-9]|3[0-1])/(0?[1-9]|1[0-2])/([0-9]{4})$', 'gi');
dateStr = dateStr.replace(DATE_REGEXP,
    (str: string, day: string, month: string, year: string) => {
        return `${day}/${month}/${parseInt(year, 10) + 543}`;
});
หรือ
const DATE_REGEXP: RegExp = new RegExp('^(0?[1-9]|[1-2][0-9]|3[0-1])/(0?[1-9]|1[0-2])/([0-9]{4})$', 'gi');
dateStr = dateStr.replace(DATE_REGEXP,
    (str: string, day: string, month: string, year: string) => {
        return `${day}/${month}/${parseInt(year, 10) - 543}`;
});
ปล. งานนี้ต้องขอบคุณ Tester เป็นอย่างมากที่คิดเคสได้รอบครอบขนาดนี้ ถ้าทดสอบด้วยวันที่อื่นคงไม่มีทางเจอบั๊กตัวนี้แน่นอน (โอกาสมีเพียงหนึ่งวันในสี่ปีเท่านั้น)

Monday, July 17, 2017

[NodeJS] Base64 Encryption และ Decryption โดยไม่ต้องลง package เพิ่มเติม
[NodeJS] Base64 Encryption and Decryption without Requiring Additional Packages

Base64 เป็นรูปแบบการแปลงข้อมูล Binary เป็นข้อมูลแบบ String สำหรับการใช้งานที่ไม่รองรับข้อมูลแบบ Binary เช่น การส่งข้อมูลไปยัง JSON API

Encryption
let bitmap = fs.readFileSync(imgPath);
let imgBase64 = new Buffer(bitmap).toString('base64');
Decryption
let bitmap = Buffer.from(imgBase64, 'base64');

Saturday, July 8, 2017

[NodeJS] Mongoose: connect กับ createConnection ต่างกันยังไง
[NodeJS] Mongoose: What is the Different between connect and createConnection

การเชื่อมต่อ Mongdb ผ่าน Mongoose ทำได้ 2 วิธี คือ
  1. mongoose.connect(DB_URL) เป็นการสั่งให้ default connection เชื่อมต่อไปยัง Mongodb 
  2. mongoose.createConnection(DB_URL) เป็นการสร้าง connection ใหม่ เพื่อเชื่อมต่อไปยัง Mongodb
ซึ่งทั้ง 2 วิธี มีการใช้งานแตกต่างกัน คือ
  1. mongoose.connect(DB_URL)
    mongoose.connect(DB_URL);
    let db = mongoose.connection;
    db.model(MODEL_NAME).find();
  2. mongoose.createConnection(DB_URL)
    let db = mongoose.createConnection(DB_URL);
    db.model(MODEL_NAME).find();

Saturday, June 24, 2017

[NodeJS] สรุปสิ่งที่ต้องระวังสำหรับ NodeJS Packages: Mongoose
[NodeJS] Warning for NodeJS Packages: Mongoose

เมื่อสัปดาห์ที่ผ่านมา เจอปัญหาเกี่ยวกับ node packages แบบรายวันเลย โดยเฉพาะ mongoose เลยขอบันทึกเก็บไว้หน่อย
  • Method ของ Mongoose บางอัน return Promise บางอัน return Thenable
    • พวก save, create จะ return Promise
    • พวก findXXX จะ return Thenable
    • พวกที่ return Thenable สามารถใช้ exec() ต่อ เพื่อให้ return Promise ได้
    • จะใช้อันไหนไปเช็คในเอกสารดูอีกทีนะ (Thenable ใช้ .catch ไม่ได้นะจ๊ะ)
  • Moment เวลาเซฟเป็น object ผ่าน Mongoose มันเซฟเป็นค่าตัวเลข (เดาว่า Unix Timestamp) เวลาจะใช้ก็อย่าลืมแปลงกลับไปเป็น Moment ก่อนนะ
    • ถ้าอยากให้เซฟเป็นวันที่เลย ก็ใช้ toDate() ก่อนเซฟ มันจะเซฟเป็นแบบ ISODate 
  • Error ของ Promise ถ้า catch ไว้แล้วใช้ JSON.stringify(error) จะได้ "{}" เสมอ ไม่แน่ใจว่าเป็นเฉพาะ error ของ Mongoose หรือเปล่า ถ้าอยากได้ข้อความ error ก็ต้องใช้ error.message แทนนะ