Wick 0.8 in PHP4

Wick Ref: http://codeigniter.com/wiki/Wick/
Matchbox Ref: http://code.google.com/p/matchbox/

ผมพบว่าใน php4 จะมีการพยายามโหลดคลาส Wick สองครั้งครับ
(ผมตั้ง config ให้ autoload libraries Wick)

จริงๆ แล้ว Zacharias Knudsen เค้าบอกว่า Wick มัน php4 compatible นะครับ. ทำให้ผมไม่แน่ใจว่าที่ผมเจอ Error Redeclare นี่เพราะผมแก้ Framework ไปหลายจุดหรือป่าว?

แต่ไม่มีปัญหาครับ Error Redeclare เรื่องเล็กนิดเดียวครับ ก็แค่ดัก If เข้าไปนิดหน่อยก็เรียบร้อย แต่ไม่รู้ว่าจะมีผลต่อ Performance หรือป่าว และ Reliable ?. ในส่วนของ Performance ก็ไม่รู้จะทดสอบอย่างไรเหมือนกัน แต่ผมได้ทำการทดลองใช้งาน $this->load->uri ทั้งใน App และใน Module ซึ่งยังสามารถทำงานได้ปรกติ ก็ถือว่าโอเคครับ (มั้งนะ)

ถ้ามีเวลาเหลือ ผมคงจะหาวิธีแก้ที่สาเหตุ มันจริงๆ อีกทีครับ 🙂

แก้ไขแบบง่ายๆ ครับ  (เติมโค๊ด ๓ ตำแหน่ง)

จุดแรก

[php]if (class_exists('Wick')===FALSE) { // <-- Insert this line (at line 36)[/php]

จุดที่สอง

[php]function Wick ()
{
<span style="color: #0000ff;">static $php4_run_once; // <– Insert this line</span>
<span style="color: #0000ff;">if (isset($php4_run_once)) // <– Insert this line
return; // <– Insert this line
else // <– Insert this line
$php4_run_once = true; // <– Insert this line</span>
$ci = &get_instance();[/php]

จุดที่สาม

[php]} // <-- Insert this line</span>
/* End of file Wick.php */
/* Location: ./system/application/libraries/Wick.php */[/php]

รักสามเศร้า (BASE)Loader, (APP)Loader และ MY_Loader

รักสามเศร้าในไฟล์ /system/codeigniter/Common.php

ในฟังก์ชัน load_class ตรงส่วนที่เป็นการตัดสินใจว่าจะดึง library จากใน BASEPATH หรือ APPPATH มีโค๊ดตามนี้

if (file_exists(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT))
{
    require(BASEPATH.'libraries/'.$class.EXT);
    require(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT);
    $is_subclass = TRUE;
}
else
{
    if (file_exists(APPPATH.'libraries/'.$class.EXT))
    {
        require(APPPATH.'libraries/'.$class.EXT);
        $is_subclass = FALSE;
    }
    else
    {
        require(BASEPATH.'libraries/'.$class.EXT);
        $is_subclass = FALSE;
    }
}

จะเห็นว่าถ้ามี MY_Loader.php อยู่ใน /system/application/libraries/ ก็จะเข้าที่เงื่อนไขแรกเลย. ซึ่งมันจะโหลด Loader.php ซึ่งอยู่ใน /system/libraries/ โดยไม่สนว่าจะมี Loader.php อยู่ใน /system/application/libraries/ หรือไม่ ทั้งๆ ที่จริงๆ แล้วควรให้ความสำคัญกับ Loader ที่อยู่ใน APPPATH ก่อน BASEPATH. ตรงนี้ผมก็ไม่แน่ใจเหมือนกันว่าจุดประสงค์ที่แท้จริงของเงื่อนไขนี้คืออะไร แต่มันไม่ตรงกับความต้องการของผม ก็เลยต้องจัดการปรับเปลี่ยนเล็กน้อย เป็นแบบนี้แทนครับ

if (file_exists(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT))
{
    if (file_exists(APPPATH.'libraries/'.$class.EXT))
        require(APPPATH.'libraries/'.$class.EXT);
    else
        require(BASEPATH.'libraries/'.$class.EXT);
    require(BASEPATH.'libraries/'.$class.EXT);
    require(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT);
    $is_subclass = TRUE;
}
else
{
    if (file_exists(APPPATH.'libraries/'.$class.EXT))
    {
        require(APPPATH.'libraries/'.$class.EXT);
        $is_subclass = FALSE;
    }
    else
    {
        require(BASEPATH.'libraries/'.$class.EXT);
        $is_subclass = FALSE;
    }
}

Matchbox 0.9.4 URI

Matchbox Ref: http://code.google.com/p/matchbox/

ผมตั้งชื่อ Module กับชื่อ Controller ใน Module เป็นชื่อเดียวกันครับ ก็เลยเกิดข้อผิดพลาดในการ parse route ตอนแรกก็เข้าใจว่าเป็น Bug ของ Matchbox ครับ. แต่จริงๆ แล้วไม่ใช่ครับ เป็นความจงใจของ Zacharias Knudsen เค้าครับ

มีกฎดังนี้ครับ
www.example.com/module/controller/method/parameter

  1. The first segment represents the module in which the controller can be found.
  2. The second segment represents the controller that should be invoked.
  3. The third segment represents the method that should be called.
  4. The fourth, and any additional segments, represents the variables that will be passed to the controller.

และมีเงื่อนไขดังนี้

  1. If the controller is located in a subfolder then the subfolder must be added as another segment between the module and controller segments.
    (ถ้า controller อยู่ใน subfolder ให้ใส่ subfolder ไปใน URI ด้วย ตรงระหว่าง module กับ controller อันนี้ค่อนข้าง make sence ครับ ไม่มีอะไรมาก)
  2. If the controller have the same name as the module, the controller segment can be omitted.
    (ถ้า controller ชื่อเดียวกับ module ให้ละชื่อ controller ใน URI ครับ ก็จะกลายเปน www.example.com/module/method/parameter)
  3. If the controller is in a subfolder and have the same name as the subfolder, the controller segment can be omitted.
    (ถ้า controller ชื่อเดียวกับ subfolder ให้ละชื่อ controller ใน URI )

แล้วถ้า module, subfolder และ controller ชื่อเดียวกันหมดละ!?

อันนี้ผมทำการทดลองให้แล้วครับ ก็คือให้ละชื่อ controller เช่นเดิมครับ โดยจะต้องระบุ module และ subfolder ไว้ใน URI

Codeigniter MY_Loader in PHP4

ใน /system/codeigniter/CodeIgniter.php จะมีทางแยกระหว่าง php4 กับ php5 อยู่ ซึ่ง php4 จะโหลดคลาส Loader ขึ้นมาก่อนและโหลดคลาส CI_Base (ใน /system/codeigniter/Base4.php) ซึ่งจะสืบทอด (extends) จาก CI_Loader ตรงนี้จึงเป็นจุดบอดเวลาที่เราต้องการจะเขียนคลาส MY_Loader เพื่อใช้งาน เพราะ Base4 มันเขียนไว้ว่ายังไงก็จะสืบถอดจาก CI_Loader ให้ได้ซะอย่างงั้น

ผมพยายามหาทางออก ที่จะไม่ต้องแก้ไขตัว Core ของ CodeIgniter แต่ก็จนปัญหาครับ. จริงๆ แล้วมันมีอยู่อีกแบบนึงคือ เขียน CI_Loader ขึ้นมาใหม่เองทั้งหมด (แบบที่ Matchbox ทำ) แล้ววางไว้ที่ /system/application/libraries/Loader.php แต่ก็ดูจะผิดจุดประสงค์ไปหน่อยครับ เพราะว่า MY_Loader ของผมต้องการจะต่อเติม หรือแก้ไขเฉพาะบาง method ที่อยู่ใน CI_Loaderเท่านั้น.

สุดท้ายก็ต้องตัดสินใจแก้ที่ Core ของมันครับ ที่จะแก้คือไฟล์ /system/codeigniter/Base4.php ครับ

จากเดิม

class CI_Base extends CI_Loader {
    function CI_Base()
    {
        // This allows syntax like $this->load->foo() to work
        parent::CI_Loader();
        $this->load =& $this; // This allows resources used within controller constructors to work
        global $OBJ;
        $OBJ = $this->load; // Do NOT use a reference.
    }
}

เปลี่ยนเป็น

if (class_exists( config_item('subclass_prefix')."Loader" ))
{
    eval('
    class CI_Base extends '.config_item('subclass_prefix')."Loader".' {
        function CI_Base()
        {
            // This allows syntax like $this->load->foo() to work
            parent::'.config_item('subclass_prefix')."Loader".'();
            $this->load =& $this; // This allows resources used within controller constructors to work
            global $OBJ;
            $OBJ = $this->load; // Do NOT use a reference.
        }
    }
    ');
}
else
{
    class CI_Base extends CI_Loader {
        function CI_Base()
        {
            // This allows syntax like $this->load->foo() to work
            parent::CI_Loader();
            $this->load =& $this; // This allows resources used within controller constructors to work
            global $OBJ;
            $OBJ = $this->load; // Do NOT use a reference.
        }
    }
}

แค่นี้ก็จะสามารถใช้งาน MY_Loader ได้แล้วล่ะครับ

function fgetcsv with Thai Language

ฟังก์ชั่น fgetcsv ใช้สำหรับอ่านไฟล์ csv โดยมันจะทำการ parse ออกมาเป็น Array ให้เราเลยครับ. แต่ก่อนตอนที่ผมยังไม่รู้จักคำสั่งนี้ นั่งเขียนตัว Parse เองตั้งนานครับ แถมยังมีบั๊กอีกต่างหาก (- -‘)

เข้าเรื่องครับ… ผมยังไม่ได้ศึกษาอย่างจริงจังเหมือนกัน ว่าสาเหตุมันคืออะไรกันแน่ (เพราะขี้เกียจครับ) เอาเป็นบอกปัญหาและวิธีแก้เลยละกันนะครับ

ปัญหา คือ มันไม่สามารถ parse ภาษาไทยได้ครับ  (เฉพาะบาง server ที่มี setting อะไรบางไม่ปรกติ)

วิธีแก้ ก็ง่ายๆ ครับ.. ใช้คำสั่ง  setlocale  ครับ.  ประมาณนี้

<?php
    setlocale ( LC_ALL, 'en_US.UTF-8' );
    ...
    ...
    $fields = fgetcsv ( $fp , $maxlength , $separator );
?>

ผมก็ยังไม่ได้ค้นคว้าเหมือนกันว่า มันมีตัวเลือกอะไรบ้าง และมีผลอย่างไร  แต่เอาเป็นว่าให้ใช้งานได้พอครับ