Google


Monthly Archive for August, 2007

Web Browser หลักที่ควรเอาไว้ทดสอบ Web Site ของเรา

มีคนถามมาว่าใช้อะไรทดสอบเว็บบ้าง สำหรับผมก็มีเท่านี้แหละ

  • Firefox 2 (Default and Debug)
  • IE 6 (on VPC 2007)
  • IE 7
  • Opera
  • Safari for Windows

รวมแล้วก็ 5 ตัว เวลาส่งงานจะได้สบายใจ และ user ของเว็บที่เราส่งงานไปเค้าจะได้ไม่ด่าเราตอนหลังว่าทำเว็บห่วย ๆ ไม่รองรับ web browser ที่เค้าใช้ ;)

จริง ๆ ยังมี Tools อีกหลายตัวที่เอาไว้ทดสอบเพิ่มเติมอย่าง The Proxomitron สำหรับลอง Monitor packet ของ HTTP หรือพวก Firebug เอาไว้ดูพวก error ในเว็บ (เป็น Extension ของ Firefox) เดี่ยวไว้ว่าง ๆ จะเอา review ครับ (จริง ๆ มีเว็บที่เค้า review อยู่หลายเว็บนา …… ลองหาก่อนก็ได้ หุๆๆ )

อีกหนึ่งปัญหาที่ทำให้อุปกรณ์ที่ต่อกับ USB Port แล้วใช้งานไม่ได้

ได้รับเมลมาว่า

    ขณะใช้งาน USB Device อย่าง H/D External ที่เอา H/D Notebook มาใส่กล่อง แล้ว copy ไฟล์ขนาดใหญ่ หรือใช้งานหนัก ๆ ก็ค้างไปเฉย เกิดเพราะอะไร ? หรือบางครั้งเสียบ H/D External หน้าเครื่องแล้วเปิดไม่ขึ้นแต่พอไปเสียบหลังเครื่องกลับใช้งานได้ปกติ ควรแก้ปัญหายังไง

ดูจากคำถามแล้วเนี่ย ต้องบอกก่อนว่าตามสเป็คของ USB 1.0 – 2.0 ที่อยู่ที่เครื่อง computer จะจ่ายไฟที่ 5 V (volts) และ 100 mA ถึง 500 mA ต่อ 1 port ครับ ขึ้นอยู่กับว่าต่อผ่าน USB Hub/Front Panel หรือต่อโดยตรงกับที่ M/B แต่ถ้าคุณใช้ USB Hub แบบไม่มีแหล่งจ่ายไฟ มาต่อแยกทุก port จะลดลงเหลือแค่ port ละ 100mA (เมื่อต่อครบทุก port) หรือ 400mA (เมื่อต่อเพียงแค่ 1 port) แต่ถ้าซื้อ USB Hub แบบมีแหล่งจ่ายไฟจะได้แรงดันไฟฟ้าที่ 500mA ต่อ 1 port เท่ากับต่อที่ computer ครับ

ซึ่งกระแสไฟที่ส่วนใหญ่ H/D External ต้องการคือ 500mA (เต็มที่ของการจ่ายกระแสไฟของ USB port เลย) ถ้าต่ำกว่านั้นอาจจะมีปัญหาได้ครับ ส่วนพวก Flash Drive ต่าง ๆ ที่ขาย ๆ กันจะต้องการกระแสไฟอยู่ที่ 100mA – 200mA โดยทั่วไปครับ (ส่วนใหญ่จะ 100mA) บางครั้งการต่อไม่ติดเนี่ย ก็เกิดจากกระแสไฟที่จ่ายมาให้กับอุปกรณ์ไม่เพียงพอครับ

[เพิ่มเติมสำหรับ iPod]

บางครั้งใน iPod เนี่ย เราสามารถเอา USB Hub ที่มีแหล่งจากไฟมาใช้ชาร์จไฟได้เหมือนกับชาร์จไฟจากคอมพิวเตอร์เลย (เป็นทั้งที่ชาร์จไฟ และ USB Hub ไปในตัวเสียเลย)

จากรูปด้านล่างผมนั้นผมซื้อ USB Hub ที่มีแหล่งจ่ายไฟมาต่อเป็น adapter สำหรับ change ไฟให้กับ iPod จากไฟบ้านครับ (ในรูปราคา 790 บาทครับ)

เวลาดูอุปกรณ์สำหรับเอามาชาร์จกับ iPod เนี่ยต้องรู้ก่อนว่า iPod นั้นต้องการกระแสไฟฟ้าเท่าไหร่ ซึ่งใน iPod นั้นกระแสไฟในการประจุไฟต้องไม่ต่ำกว่า 400mA ครับซึ่งร่วมถึงการใช้งานตามปกติด้วย (เช่นการส่งข้อมูลไฟล์ต่าง ๆ ) โดย adapter นั้นแนะนำว่าไม่ควรเกิน 500mA (จริง ๆ เกินได้ครับเพราะแบตฯ li-ion นั้นการประจุไฟแบบกระแสไฟไม่ใช่ความต่างศักดิ์ แต่การใช้กระแสไฟมากเกินไปอาจทำให้แบตฯ li-ion ใน iPod ร้อนเกินไปครับ) และความต่างศักดิ์ต้องอยู่ระหว่าง 4.75–5.25 V. ครับ

อ้างอิงจาก http://en.wikipedia.org/wiki/USB

Routing URL for PHP

ตอนนี้ในส่วนของ PHM (PHPHoffman Framework) ที่ต้องปรับ Architecture ใหม่แล้ว สิ่งที่ต้องปรับก่อนและทำมาได้ 7-8 วันคือส่วนของ Routing URL ที่ปรับเปลี่ยนจากการโยนทุกอย่างไปที่ mod_rewrite ของ Apache ซึ่งผมมองว่ามันดูไม่ค่อยดีเท่าไหร่ ประกอบกับถ้าจะทำ น่าจะทำที่ php มากกว่า อย่างน้อย ๆ ทำ cache ก็ยังง่ายกว่าอยู่ จึงนั่งดูว่าใน CMS อย่าง Drupal และ Blogware อย่าง WordPress เค้าทำยังไง ซึ่งนั่งไล่ดู code เค้าแล้วตัว mod_rewrite ที่เค้าใช้ก็มีประมาณ code ด้านล่างเท่านั้นที่เป็นส่วนหลัก ๆ

ไฟล์ .htaccess

CODE:
  1. <ifmodule mod_rewrite.c>
  2.     RewriteEngine On
  3.     RewriteCond %{REQUEST_FILENAME} !-d
  4.     RewriteCond %{REQUEST_FILENAME} !-f
  5.     RewriteRule ^(.*)$ index.php?qs=$1 [QSA,L]
  6. </ifmodule>

จะเห็นว่าโยน String หลัง directory ทั้งหมดใส่ลงไปใน QueryString ชื่อว่า "qs" ทั้งหมด แล้วเอาไอ้ String ทั้งหมดไปตัดแต่งใน PHP เอาทีหลัง ซึ่งอย่างที่บอกไปว่า PHP มี cache ที่ดีอยู่แล้ว ผมเลยคิดว่าเราจะใช้มันแทน

สิ่งต่อมาที่ต้องคิดคือการ config ไฟล์ routing ที่ควรจะเป็นแบบไหนดี นั่งไล่ดูทั้ง RoR (Ruby On Rails), CakePHP และบทความตามเว็บต่าง ๆ มักจะใช้ Style ของ RoR ทั้งนั้นเพียงซึ่งผมเอามาเพียงแต่ pattern ของการกำหนด connect ที่ map URL กับ action ของระบบเท่านั้น ส่วนการ config ผมกลับโยนลงไปใน XML Style แทน ดังต่ออย่างต่อไปนี้

ไฟล์ routing.xml

XML:
  1. <routing><!-- High Priority -->  
  2.     <map pattern="login" action="user/login" />
  3.     <map pattern="signup" action="user/signup" />
  4.     <map pattern="signout" action="user/logout" />
  5.     <map pattern="userdetail/:value" action="user/detail/:value" />
  6.     <!-- Begin Default Routing URL -->
  7.     <map pattern=":controller/:action/:value" />
  8.     <map pattern=":controller/:action" />  
  9.     <map pattern=":page" />
  10.     <!-- End Default Routing URL -->  
  11. </routing><!-- Low Priority -->

จากตัวโครงสร้างการ map ตัว url นั้นจะแบ่งเป็นสองส่วนคือ pattern กับ action โดยตัว pattern เป็นตัวที่บ่งบอกถึงส่วนที่เข้ามาในตัวแปร qs ที่รับเข้ามา ส่วน action คือตัวที่ระบบจะทำการ recursion กลับมาอีกครั้งเพื่อทำ default routing ด้านล่างอีกที โดยตัว keyword จะมีอยู่ 4 ตัวคือ :controller, :action, :value และ :page ซึ่งทั้งสามตัวเป็นส่วนสำคัญในการควบคุมการเกิด action ของระบบจาก pattern ที่เพิ่มเติมมานอกเนื่อจาก 3 ตัวที่เป็น default ของทั้งหมด จริง ๆ ตอนนี้อาจจะพัฒนาเพิ่มโดยที่ developer สามารถเพิ่ม keyword ลงไปได้อย่างง่าย ๆ จากไฟล์ XML Style ตัวนี้ ตอนนี้กำลังหาส่วนที่ทำให้ง่ายที่สุดก่อน

ต่อมาคือการ parse ตัว XML เราใช้ตัว HTML/XML Parser Class ของ Dennis Pallett  (หารายละเอียดได้จาก entry เก่า ๆ ครับ) ซึ่งเป็นตัว HTML/XML Parser Class ที่อยู่ใน Core ของ PHM อยู่แล้ว

โดย Pattern มาตรฐานคือ /page, /controller/action หรือ /controller/action/value ซึ่งตอนนี้ตัวที่ทำผมทำเป็น Class เพื่อง่ายต่อการนำไปใช้ โดยตัว code ก็คือ

ไฟล์ routing.php

PHP:
  1. <?php
  2. /**
  3.  * Routing URL Class for PHP
  4.  *
  5.  * This is a helper class that is used to Routing URL.
  6.  *
  7.  * @author   Ford AntiTrust, annop@thaicyberpoint.com, http://www.thaicyberpoint.com
  8.  * @copyright Ford AntiTrust, 2007
  9.  * @package Routing URL
  10.  * @version 0.1a.2
  11.  */
  12.  require_once("HTMLXML_Parser.php");
  13.  
  14. class RoutingURL extends HTMLXML_Parser{
  15.    
  16.     private $qs;
  17.     private $array_xml;
  18.     private $match_data;
  19.     private $path_routingfile = "routing.xml";
  20.     private $keyword = array(
  21.                                         ':controller'   => '[a-zA-Z0-9][a-zA-Z0-9_\-\.]*',
  22.                                         ':action'        => '[a-zA-Z0-9][a-zA-Z0-9_\-\.]*',
  23.                                         ':value'         => '[a-zA-Z0-9][a-zA-Z0-9_\-\;\.=]*',
  24.                                         ':page'          => '[a-zA-Z0-9][a-zA-Z0-9_\-\;\.=]*'
  25.                                         );
  26.  
  27.     function __construct($qs, $routingpathfile = NULL) {    
  28.         if(empty($qs)) return;
  29.         if(!empty($routingpathfile)) $this->set_routingpathfile($routingpathfile);
  30.  
  31.         $this->qs = $this->filter_slash_at_last_char($qs);
  32.  
  33.         $this->array_xml = parent::parse(file_get_contents($this->get_routingpathfile()));
  34.  
  35.         foreach($this->array_xml[0]['children'] as $key=>$val) {
  36.  
  37.             $pattern = $this->filter_slash_at_last_char($val['attr']['PATTERN']);
  38.             $action = $this->filter_slash_at_last_char($val['attr']['ACTION']);
  39.             $raw_pattern = explode('/', $pattern);
  40.  
  41.             foreach($this->keyword as $keywordkey=>$keywordval)          
  42.                 $pattern = eregi_replace($keywordkey, $keywordval, $pattern);        
  43.            
  44.             // Test Output
  45.             // echo $this->filter_slash_at_last_char($val['attr']['PATTERN']), ' <- ',$pattern, ' <- ',$this->qs, '<br />';
  46.  
  47.             if($pattern == $this->qs && !empty($action)) {
  48.                     $this->__construct($action);
  49.                 break;
  50.             }
  51.             elseif(eregi('^'.$pattern.'$', $this->qs)) {
  52.                 $filter_pattern = explode('/', $pattern);
  53.                 $array_qs = explode('/', $this->qs);
  54.                 $index = 0;
  55.                 foreach($filter_pattern as $patternkey=>$patternval){
  56.                     $this->match_data[$raw_pattern[$index]] = $array_qs[$index];
  57.                     if(isset($this->keyword[$raw_pattern[$index]]))
  58.                         $action = eregi_replace($raw_pattern[$index], $array_qs[$index],$action);                    
  59.                     $index++;
  60.                 }
  61.                 if(!empty($action))
  62.                     $this->__construct($action);
  63.                 break;
  64.             }
  65.         }
  66.     }
  67.     function get_match_data() {
  68.         return $this->match_data;
  69.     }
  70.     function filter_slash_at_last_char($pattern) {
  71.         return (substr($pattern, -1) == "/"  ? substr($pattern, 0, -1) : $pattern);
  72.     }
  73.     function set_routingpathfile($path) {
  74.         $this->path_routingfile = $path;
  75.     }
  76.     function get_routingpathfile() {
  77.         return $this->path_routingfile;
  78.     }
  79. }
  80. $obj = new RoutingURL($_GET['qs']);
  81. print_r($obj->get_match_data());
  82. ?>

เมื่อทดสอบ จาก URL : http://127.0.0.1/routing/login http://127.0.0.1/routing/login ตัว method "get_match_data" จะส่ง Array ออกมาเป็น

PHP:
  1. Array ( [:controller] => user [:action] => login )

เราก็เอาค่าใน Array ไปใช้ประโยชน์ในการ map ตัว controller เพื่อเรียก Class มาสร้างเป็น Object, map ตัว action เพื่อไปเรียก method ใน object และ value สำหรับ map parameter ลงในตัวแปรของ method นั้น ๆ ต่อไป

โดยรวมตอนนี้ทั้งหมด OK แล้ว ทดสอบมาได้ 2 วันผลเป็นที่น่าพอใจ สิ่งที่ควรระวังที่สุดในการทำ Routing URL คือ Infinite loop ในการเรียก action ซึ่งก็เหมือนกับของ RoR นั้นแหละ อันนี้คงต้องระวังกันหน่อยครับ จริง ๆ มันก็ลักษณะการ recursion ไปเรื่อยจนเจอ Terminate case นั้นแหละ ตอนนี้ก็ต้องปรับกันต่อไป และทำ Performance tuning ด้วย รวมถึง Test เปรียบเทียบกับแบบใช้ mod_rewrite ว่าอันไหนเร็วกว่า, ใช้ง่ายและปรับแต่งได้ง่ายกว่ากันด้วย ;)

[Download]

[Update, 15:28, 24/08/2007]
มีคนถามว่าทำไมต้อง XML Style ด้วย ? คำตอบก็เพราะว่าต่อไปจะเขียน Java App หรือ .NET App ตัวเล็ก ๆ เพื่อเข้าไปเขียนหรือจัดการมันแทนที่จะมานั่งเขียนใน XML เช่นมี Textbox 2 ตัวไว้ใส่ข้อมูล โดยช่องนึงไว้ใส่ pattern อีกอันไว้ใส่ action แล้วก็กด update หรือจัดการอื่น ๆ เสีย แค่นั้นเอง หรือถ้ามีซับซ้อนมากขึ้น เราก็ปรับ Tools ให้มันเข้าไปใช้งานได้ง่ายขึ้น ประมาณนั้น

[Update, 15:40, 24/08/2007]
เดี่ยวเรื่อง Document จะตามมาอีกทีครับ ตอนนี้ขอปรับแต่งและทดสอบให้มากกว่านี้อีกหน่อย แต่แบบนี้น่าจะทำให้ได้ idea มากขึ้่น และสิ่งหนึ่งที่ผมคิดไว้คือถ้า server เป็น iis ตัว url มันก็จะถูกปรับเพียงเล็กน้อยเท่านั้น ซึ่งแค่เขียน funciton เพิ่มสำหรับ detect ค่า config สัก 1 ตัวเพื่อบอกว่าเราใช้ mod_rewrite อยู่หรือไม่ ถ้าไม่ได้ใช้ก็ก็แค่เพิ่ม QueryString ลงไปใน path URL เท่านั้นเอง ซึ่งลดการทำงานของ Developer ในการต้องมานั่งกังวลกับการเข้ากันได้ของตัว Web App ที่เขียนขึ้นมา ซึ่งได้นำ Concept แบบเดียวกับ Drupal นั้นเอง

[Update, 16:55, 24/08/2007]
มีการแจ้งข้อผิดพลาดเข้ามาแล้วครับ ตอนนี้แก้ไขแล้วเรียบร้อย ;)

[Update, 23:00, 25/08/2007]
นอนไม่ค่อยหลับตื่นมาเจอ bug นิดหน่อยแก้เสียเลย -_-'