Panada Core Blueprint: A Proposal for Version 1.X

By - 17 October 2011 11:50:03

Salah satu dari hasil MeetUp Panada yang baru saja dilaksanakan adalah menjadikan Framework ini siap digunakan ke dalam lingkungan produksi yang dapat digunakan secara luas. Untuk bisa mencapai tahap ini, perlu disiapkan core system yang mapan dan teruji.

Agar setiap komponen dapat dievaluasi secara trasnparan, maka perlu disiapkan blueprint yang memberikan gambaran rinci tentang Panada. Dengan tujuan tersebut, maka dokumentasi ini dibuat.

Sederhana, Alamiah dan Ramping

Semenjak dari awal dibuat, misi Panada adalah bertujuan menjadi Framework yang sederhana, alamiah dan ramping. Setiap developer, terutama yang sebelumnya masih belum biasa menggunakan Framework diharapkan tidak terlalu mengalami kesulitan ketika menggunakan Panada. Kemudian, diharapkan juga tidak terlalu banyak aturan yang mengikat dan bisa menggunakan fitur-fitur yang telah disediakan secara native oleh PHP secara maksimal.

Selain menjelaskan core Panada secara umum, dokumentasi ini juga ditujukan sebagai proposal mengenai Panada versi 1.X. Secara mendasar Panada versi 1.X tetap menggunakan __autoload function untuk mendapatkan setiap resources.

function __autoload($class){


    $path   = str_ireplace('\\', '/',$class);
    include $path.'.php';
}

Berikut adalah fungsi __autoload pada Panada versi 0.X:

function __autoload($class){


    $prefix = explode('_', $class);
    $folder = strtolower($prefix[0]);
    $var_name = strtolower($prefix[1]);


    $file   = $folder .'/'. $var_name;


    include_once $file.'.php';
}

Namespace

Seperti yang terlihat di atas, bahwa Panada versi 1.X sudah tidak menggunakan underscore (_) sebagai pemisah lokasi, tetapi menggunakan backslash (\) yang dihasilkan dengan penggunaan namespace yang mulai ada di PHP sejak versi 5.3.X.

Penggunaan namespace juga akan memudahkan jika sebuah file letaknya berada di dalam sub-folder misal, sub-folder controller, sub-folder model atau sub-folder library. Fleksibelitas seperti ini tidak didaptkan pada Panada versi 0.X.

Berikut adalah contoh gambaran struktur Panada versi 1.X dengan menggunakan namespace sebagai locator:

Buat sebuah file dengan nama gear.php:

<?php
function __autoload($class){


    $path   = str_ireplace('\\', '/',$class);
    include $path.'.php';
}


$instance = new Controllers\Home;


$instance->index();

Buat sebuah folder dengan nama Controllers dan sebuah file di dalamnya dengan nama Home.php:

<?php
namespace Controllers;


class Home {


    public function index(){
        echo __METHOD__;
    }
}

Berikut adalah gambaran struktur file dan folder dari aplikasi di atas:

app/
    gear.php
    Controllers/
        Home.php

Akses file gear.php dari browser untuk melihat outputnya, misal http://localhost/app/gear.php.

Output di browser kurang lebih akan menampilkan:

Controllers\Home::index

Sebagaimana terlihat di sini, penggunaan namespace memberikan cara yang lebih efisien dalam pengambilan sebuah file bila dibandingkan dengan cara sebelumnya. Cara yang sama juga bisa kita terapkan untuk meload sebuah class yang ada di dalam sub-folder Controllers, berikut contohnya:

Buat sebuah folder baru di dalam folder Controllers dan beri nama Page. Di dalam folder ini buat sebuah nama Blog.php dan kemudian isikan file ini dengan class seperti berikut:

<?php
namespace Controllers\Page;


class Blog {


    public function index(){
        echo __METHOD__;
    }
}

Struktkur aplikasi di atas akan seperti:

app/
    gear.php
    Controllers/
        Home.php
        Page/
            Blog.php

Kembali ke file gear.php dan ubah bagian:

$instance = new Controllers\Home;

menjadi:

$instance = new Controllers\Page\Blog;

Kemudian kembali akses file gear.php dari browser dan hasilnya adalah:

Controllers\Page\Blog::index

Cara ini juga memberikan kemudahan seandainya controller Blog ingin diakses dari controller home. Berikut adalah kode file Home.php

<?php
namespace Controllers;
use Controllers\Page as Page;


class Home {


    public function __construct(){


        $this->blog = new Page\Blog;
    }


    public function index(){


        echo $this->blog->index();
    }
}

Ubah kembali variable $instance di dalam file gear.php menjadi seperti semula:

$instance = new Controllers\Home;

Hal yang sama juga berlaku untuk pemanggilan sebaliknya, yaitu dari calss Blog memanggil class Home. Berikut adalah file Blog.php-nya:

<?php
namespace Controllers\Page;
use Controllers;


class Blog {


    public function __construct(){


        $this->home = new Controllers\Home;
    }


    public function index(){
        echo $this->home->index();
    }
}

Prinsip yang sama bisa kita terapkan untuk pemanggilan class model dan library. Untuk mencobanya buat dua buah folder masing-masing bernama Models dan Libraries yang lokasinya satu level dengan folder Controllers. Buat sebuah file dengan nama SampleData.php di dalam folder Models dan sebuah file dengan nama MyLibs.php di dalam folder Libraries. Berikut isi dari masing-masing file tersebut:

<?php
namespace Models;


class SampleData {


    public function getData(){


        return 'My data';
    }
}
<?php
namespace Libraries;


class MyLibs {


    public function test(){


        return 'This is from library';
    }
}

Struktur aplikasi di atas akan seperti berikut:

app/
    gear.php
    Controllers/
        Home.php
        Page/
            Blog.php
    Models/
        SampleData.php
    Libraries/
        MyLibs.php

Sekarang kita akan memanggil sebuah class model dan sebuah class library dari dalam controller. Kembali ke file controller Home.php dan edit menjadi seperti berikut:

<?php
namespace Controllers;
use Models, Libraries;


class Home {


    public function __construct(){


        $this->sampleData = new Models\SampleData;
        $this->myLibs = new Libraries\MyLibs;
    }


    public function index(){


        echo $this->sampleData->getData();
        echo '<br />';
        echo $this->myLibs->test();
    }
}

Kemudian kembali akses file gear.php dari browser. Terlihat di sini bahwa dengan mudah kita bisa me-load resource dari sebuah class model dan library dengan mudah.

Berikut adalah contoh bagaimana sebuah class model bisa menggunakan resource dari class library atau sebaliknya.

<?php
namespace Models;
use Libraries;


class SampleData {


    public function getData(){


        $lib = new Libraries\MyLibs;
        return $lib->test();
    }
}
<?php
namespace Libraries;
use Models;


class MyLibs {


    public function test(){


        $mod = new Models\SampleData;
        return $mod->getData();
    }
}

Instansi Otomatis VS Lazy Load

Pada Panada versi 0.X jika sebuah class, dimana di dalamnya melakukan instansi class lain yang dilakukan di dalam constructor, maka secara otomatis instance dari class yang dinisialisasi di dalam contructor tersebut akan bisa digunakan juga di class-class lain. Penjelasan tentang hal ini bisa dilihat di sini http://panadaframework.com/documentation/id/index.html#feature. Prinsip yang sama juga digunakan pada feature autoload. Berikut adalah contoh kodenya yang ada di file gear.php versi 0.X:

$panada_cacher  = Panada_cacher::instance();


if( ! isset($Panada->$var_name) ) {
    $Panada->$var_name = new $class_name;
    $panada_cacher->class_object[$var_name] = $Panada->$var_name;
}


$panada_cacher->defined_object = array_keys(get_object_vars($Panada));

Dan berikut adalah bagian yang mendistribusikan setiap object:

public static function assigner(){


    $Panada         = Panada::instance();
    $panada_cacher  = Panada_cacher::instance();


    if( isset($panada_cacher->defined_object) )
        foreach ($panada_cacher->defined_object as $key) {
            if($key != 'config'){
                foreach ($panada_cacher->class_object as $class => $object)
                    if( $class !== $key)
                        if( isset($Panada->$key) && is_object($Panada->$key) )
                            $Panada->$key->$class = $object;
            }
        }
}

Hal ini membawa konsekuensi kepada banyaknya memory yang akan digunakan. Semakin banyak class yang diload, maka akan semakin banyak penggunaan memory. Selain itu semakin banyak class yang diload akan semakin memperlama proses iterasi.

Untuk mendapatkan hasil yang hampir sama dan dengan cara yang lebih efisien, kita bisa memanfaatkan feature magic method yang ada di PHP versi 5.1. Untuk contoh implementasi di Panada versi 1.X silahkan buat tiga buah class baru di dalam file gear.php dengan nama Models, Libraries dan Controllers dan berikut adalah contoh kodenya:

<?php
class Models {


    public function __get($name){


        $class = 'Models\\'.ucwords($name);
        $object = new $class;
        $object->libraries = new Libraries;
        return $object;
    }
}


class Libraries {


    public function __get($name){


        $class = 'Libraries\\'.ucwords($name);


        return new $class;
    }
}


class Controllers {


    public function __get($name){


        return new Models();
    }


}


function __autoload($class){


    $path   = str_ireplace('\\', '/',$class);
    include $path.'.php';
}


$instance = new Controllers\Home;


$instance->index();

Kembali ke file model SampleData.php:

<?php
namespace Models;


class SampleData {


    public function getData(){


        return 'My data';
    }
}

Edit file controller Home.php menjadi seperti berikut:

<?php
namespace Controllers;


class Home extends \Controllers {


    public function index(){
        echo $this->models->sampleData->getData();
    }
}

Kembali eksekusi file gear.php dari browser. Dari contoh ini kita bisa meload secara otomatis dengan cara yang lebih cepat.

Sekarang kita akan mencoba meload sebuah class library dari dalam class model SampleData:

<?php
namespace Models;


class SampleData {


    public function getData(){


        return $this->libraries->myLibs->test();
    }
}

Kembali eksekusi file gear.php dari browser. Hasilnya adalah resource class library yang dipanggil dari dalam class model. Hal yang sama juga bisa diterapkan jika salah satu calss library ingin memanggil sebuah atau beberapa class model.

Jika kita kembali ke file gear.php dan memperhatikan bagain berikut:

public function __get($name){


    $class = 'Models\\'.ucwords($name);
    $object = new $class;
    $object->libraries = new Libraries;
    return $object;
}

Maka pemanggilan class secara dinamis akan berhasil jika tidak dilakukan di dalam method constructor. Apabila kita ubah class SampleData menjadi seperti berikut, maka ini akan menghasilkan error:

<?php
namespace Models;


class SampleData {


    public function __construct(){


        $this->myLibs = $this->libraries->myLibs;
    }


    public function getData(){


        return $this->myLibs->test();
    }
}

Cara pemanggilan di atas hanya akan berhasil dilakukan di dalam class controller. Hal ini disebabkan Class controller sudah melakukan extends terhadap class Controllers.

Untuk mengatasinya, kita tetap bisa menginstansi class secara langsung di dalam method constructor seperti contoh berikut:

<?php
namespace Models;
use Libraries;


class SampleData {


    public function __construct(){


        $this->myLibs = new Libraries\MyLibs;
    }


    public function getData(){


        return $this->myLibs->test();
    }
}

Struktur File dan Folder

Untuk menjadikan aplikasi lebih fleksibel dan pemisahan antara aplikasi dan core framework, maka berikut ini adalah gambaran direktori Panada versi 1.X:

app/
    index.php
    Controllers/
    Models/
    views/
    config/
    assests/
panada/
    gear.php
    Drivers/
        Database/
        Cache/
        Session/
    Resources/
        Controllers.php
        Models.php
        Libraries.php
    Moduls/
    vendors/

Keterangan:

  • app - Pada Panada versi sebelumnya nama folder aplikasi utama adalah apps. Karena satu folder ini berisi sebuah aplikasi, maka namanya dirubah menjadi app.
  • Folder app dan panada satu level - Ini untuk memberi gambaran bahwa sebuah core bisa memiliki banyak aplikasi berbeda. Contohnya:

            app/
            app2/
            blog/
            forum/
            panada/
        
  • index.php - Setiap request diterima oleh file ini. Di dalamnya akan meng-include file gear.php dan mendeklarasikan beberapa konstanta seperti contoh berikut:

            <?php
            define('APP', dirname(__FILE__) . '/');
            define('GEAR', '../panada/');
    
    
            require_once GEAR.'gear.php';
        
  • Controllers - Tempat file class controller atau sub folder controller.
  • Models - Tempat file class model atau sub folder model.
  • views - Tempat file view atau sub folder view.
  • config - Tempat file-file config.
  • assets - Semua file-file static seperti javascript css, images dll.
  • Drivers - Semua class-class driver seperti DB, cache dan session.
  • Resources - Semua class-class yang menjalankan sistem utama Panada seperti class Controllers, class Models, class Libraries dll.
  • Moduls - Tempat sub aplikasi/modul.
  • vendors - Adalah folder untuk meletakan folder-folder dari class-class atau library dari pihak ketiga seperti PHPMailer, Class OAuth, PHPPDF, Smarty dll. Karena strukturnya berbeda-beda dari setiap vendor, maka dibutuhkan suatu class yang bertugas untuk meng-import instance dari masing-masing vendor. Contoh melakukan importnya:

            $this->smarty = new Resources\Import::vendor('Smarty/SmartyClass');
            $this->recaptcha = new Resources\Import::vendor('Recaptcha/RecaptchaClass');
        

Pada struktur di atas, class Controllers, Models, dan Libraries yang semuala ada di dalam file gear.php dipisahkan dan dijadikan file tersendiri di dalam folder Resources.

Naming Conventions

  • Syntax - Penamaan class, interface, method, function, variable, class properties/fields, dan constants mengikuti panduan PHP yang ada di halaman ini http://svn.php.net/viewvc/php/php-src/trunk/CODING_STANDARDS?view=co.
  • Namespace - Karena tidak ada standar resmi dari PHP tentang penamaan namespace, dan jika melihat contoh-contoh pembuatan namespace yang ada di halaman http://www.php.net/manual/en/language.namespaces.php, serta panduan yang diberikan di halaman ini http://framework.zend.com/manual/en/coding-standard.naming-conventions.html, maka penamaan namespace selalu di awali dengan huruf kapital (PascalCase).
  • Folder - Jika sebuah folder berisi paket namespace, maka nama folder tersebuh harus diawali dengan huruf kapital (PascalCase). Hal ini untuk memudahkan proses include sebuah file yang ada di dalam folder tersebut yang lokasinya didapat dari nama manespace-nya. Sebaliknya jika tidak, maka gunakan huruf non-kapital.
  • File - Jika file tersebut berisis sebuah class, maka gunakan PascalCase style pada namanya. Dan sebaliknya, jika tidak maka tidak perlu menggunakan style ini.
  • Autoload Properties - Jika melakukan load otomatis seperti contoh berikut:

                <?php
                namespace Models;
    
    
                class SampleData {
    
    
                    public function getData(){
    
    
                        return $this->libraries->myLibs->test();
                    }
                }
    


    maka mengikuti standar penamaan sebuah variable atau properties, bagian libraries dan myLibs walaupun mengacu ke class Libraries dan MyLibs harus diawali dengan huruf non-kapital (camelCase). Panada akan otomatis melakukan PascalCase kepada dua properties ini dengan menggunakan fungsi ucwords().

Comment