這一年協助做了一個小型的網站系統

時間急迫下,一月完成雛型後

在三月又發布第一次改版

使用到七月底,總覺得有非常多不便管理與維護之狀況

  • PHP本質上是腳本式語言,網頁AJAX使用的request

    最基本的設計就是一個request對應至一個檔案

    越多的request,檔案就越多越雜亂

  • PHP檔案模組之間要重用,必須不斷的include

    比較麻煩的一點是,目前的運行狀態會承接被include檔案的執行結果

    越多層的include處理必須更為小心

    PHP也支援function與物件導向

    但是還沒辦法解決全部的問題

  • 腳本式語言開始執行時一切幾乎從頭開始

    需要什麼模組檔案必須要一個一個自己include,非常沒有彈性

    當然可以用include的方式解決

    但單一種的include還是沒辦法應付所有效率、利用度等相關問題

    (某個request沒必要用到的模組放太多會浪費資源)

    PHP支援auto load,但是僅限於物件導向的class使用時觸發

    但是每個request要寫個 include在最前頭看起來還是非常不爽的

    最好是能將被不同的層面區隔開來,彼次不知對方存在為妙

現在是個管理彈性重於執行時間考量的年代

因此我開始思考是不是能有一個比較有彈性的開發、管理與運行方式?

blog01
blog02
blog03


一想原生可執行程式開始執行的時候,其實本質上也是一種腳本式運行方式

程式正式開始運行前,必須要對電腦環境做初始化

現今許多常見的程式語言都將系統初始層面 (startup) 與使用者定義(User-defined) 層面切開

定義一個使用者定義程式碼的起始點 (通常叫做 main)

PHP發展到現在已經有物件導向、命名空間 (name space)、autoload等特性

這些觀念都可以用,所以仿效 main起始點的概念

自己做一個 PHP 版的startup給自己的小型網站用 (硬是要自幹www)

因為本人經驗太少

從八月且戰且走設計實作,用到現在也不斷依需要增修了幾樣特性

不過大致上的框框已差不多定型

 

如何使用

PHP可以作為一般程式執行 (命令列執行) 或者網頁後端服務

作為腳本式語言,要使用startup 模組還是要在某個地方 include

沒搞好還是會讓程式檔案變多且雜亂

命令列執行較簡單些,因為執行者有一明確的執行目標

亦可直接執行 startup,但為保有執行路徑之彈性

建議以另一檔案包覆 (include) 並執行之 (下圖 1)

網頁的request要如何透過少數的起始點和 startup搭上線?原則上可以 URL rewrite解決

 

參數化設定

startup以參數方式適應各式環境

startup 會自動讀取 startup.config.php 設定檔

為增加目錄放置目錄的彈性,startup利用PHP include path尋找檔案

如此 startup.config.php便能自由放至於不同目錄下,只要執行時工作路徑符合即可

startup.config.php設定檔案,讓開發人員設定 startup參數或自定義常數 (下圖2)

目前定義之 startup參數如下

  •  __AUTOLOAD:可設定為 true或"enabled",代表開啟PHP class autoload機制

    PHP autoload機制讓class將被使用時將可以動態載入 (載入方式由開發人員決定)

    其實個人也不知道讓 autololad機制能夠設定開啟與關閉有什麼用處

    或許可搭配 __initialize_complete與其他較大型的 framework結合用吧 ?

  • __INCLUDE_PATH:設定PHP include path,設定之值會自動附加於

    PHP預設 include path與 startup預設include path之後,路徑以逗點做分隔。

  • __PRE_INCLUDE_FILES:部分模組定義於此讓startup起始時預先 include,

    將以設定後之 PHP include path做檔案尋找,各檔案以逗點作分隔。

    多用於非class定義

    相關之檔案使用 (如純函式模組)。

  • __SYSTEM_TIMEZONE:系統預設時區,目前尚無撰寫相關程式碼。

  • __REQUIRED_PHP_VERSION:指示PHP最低相容程度,或特定之PHP版本。

    若版本不相符時,startup將終止所有後續運行,並產生錯誤訊息。

  • __DEBUG:開啟或關閉 debug標示。在程式運行中,可直接使用本常數,

    或以 __debug存取 (目前建議)

    日後將再增設 startup存取支援函式(2014.12.29)

  • __MAIN__AUTOLOAD啟用下生效。startup運行完畢後以純物件導向之方式運行。
    本常數定義使用者定義程式起始點所在之 class。建議填入完整命名空間路徑

本質上startup.config.php也還是 PHP腳本檔

若需讓系統常數設定與自定義常數也能夠各自分隔

開發人員也能採以 include方式執行「根設定檔」 (下圖 3)

當然也能夠執行任何程式碼,但強烈建議純粹做為定義常數使用。

 

使用者定義程式起始點怎麼用

起始點 method 必須標記為 static (下圖4)

startup定義之程式起始點名稱為「main」或 「tmain」 (以 main為優先)

 

(舊式PHP用法中,若method 名稱與 class相同時,會被當作建構子而不能標記為 static)

起始點接受兩參數

參數 1為 program parameter,型態為陣列,隨 PHP執行方法不同而有以下差異

  • HTTP GET:參數 1 將傳入URL query string解譯值 (PHP之 _GET變數)

  • HTTP POST:參數 1 將傳入 HTTP form data值 (PHP之 _POST變數)

  • 命令列執行:參數 1將傳入命令列參數 (PHP之 _SERVER["argv"] 變數)

參數 2為 PHP 系統參數,型態為 array,startup僅整理傳入一部分系統參數

包含:

  • requestType:即PHP之 _SERVER["REQUEST_METHOD"] 變數

  • query:即 PHP 之 _GET變數,經嘗試 (_POST 與 _GET可並列使用)

  • server:即 PHP之 _SERVER變數

  • environment:即 PHP之 _ENV變數

  • http-files:即 PHP之 _FILES變數

  • http-cookie:即 PHP之 _COOKIE變數

 

執行前後有什麼?

在初始化至程式起始點之間若開發人員有定義「__init_complete」 function,則執行之

當使用者定義程式結束後,若開發人員有定義「__exit」function,則執行之

目前用途不明 www

 

startup autoload怎麼讀取抓取定義檔

startup 將命名空間與檔案系統路徑綁定

命名空間傳入 autoload後,將自動變更為檔案系統路徑 (下圖5)

並依 PHP include path做檔案搜尋並載入之

class名稱必須與檔案名稱相同

連同程式起始點之機制,此部分幾乎等同於 Java的 package 概念

故此部分該重新定義為仿效 Java所之機制思考

PHP運作機制下,一個檔案內可包含不只一個 class定義

建議一檔案定義一 class以便於管理,除非需要類似 Java Inner Class這樣的輔助型類別定義

為便於區分與管理 class與 interface

class檔案必須以 .class.php作副檔名;interface則以 interface.php作為副檔名

主檔名部分,startup 強制將 interface 類之PHP名稱定義為

「以大寫 I 開頭,後續接上大寫字母開頭之名稱」,例如 IInterface

startup將載入Interface.interface.php檔案;

Interface則會被視為 class載入 Interface.class.php檔案。

 

blog04.png
blog05.png
blog06.png blog07.png
blog08.png  

 

程式碼

作了一個小型實例供人參考

過去寫作業不怕抄,只怕別人抄不了

如果能激發更多構思那更好

但還是希望各為取用者都是道德人

取用前能夠先告知一聲

(highlighter排版似乎有些問題,勞煩自行處理,或直接聯繫取檔)

<?PHP

define('__STARTUP_VERSION',"1.0");

@include_once("startup.config.php");

$__debug=(defined("__DEBUG")&&(__DEBUG ==="enable"|| __DEBUG));

if(defined("__REQUIRED_PHP_VERSION")&& __REQUIRED_PHP_VERSION !=""){
$__required_php_version= __REQUIRED_PHP_VERSION;
$match=array();
$result=preg_match(
'/^([1-9][0-9]*\.[0-9]+(\.[0-9]+)?)(\+)?$/',
$__required_php_version,$match
);
if(!$result){
$__required_php_version="5.3+";
$match=array("","5.3.0",".0","+");
}

$result=($match[3]==="+")?
PHP_VERSION>=$match[1]:
PHP_VERSION===$__required_php_version;
if(!$result)
die("Version Incorrect, Required Version $__required_php_version");

unset($match,$result);
}

$__path_delimiter=(PHP_OS==="WINNT")?";":":";
$__include_path=implode(
$__path_delimiter,array(
dirname(__FILE__),
dirname(__FILE__)."/Modules"
)
);
if(defined("__INCLUDE_PATH")&&is_string(__INCLUDE_PATH)&& __INCLUDE_PATH !==""){

$tmp=explode(",", __INCLUDE_PATH);
foreach($tmpas$key=>$value)
$tmp[$key]=trim($value);
$tmp=implode($__path_delimiter,$tmp);

$__include_path.="$__path_delimiter$tmp";

unset($tmp);
}
$__old_include_path=
set_include_path(get_include_path()."$__path_delimiter$__include_path");

$__pre_include_files="";
if(defined("__PRE_INCLUDE_FILES")&&is_string(__PRE_INCLUDE_FILES)&& __PRE_INCLUDE_FILES !=="")
$__pre_include_files.=",". __PRE_INCLUDE_FILES;
if($__pre_include_files!==""){
$tmp=explode(",",$__pre_include_files);
foreach($tmpas$key=>$value){
$tmp[$key]=trim($value);
if($__debug)
include_once("{$tmp[$key]}");
else
@include_once("{$tmp[$key]}");
}
unset($tmp);
}

if(defined("__AUTOLOAD")&&
(__AUTOLOAD ===true|| __AUTOLOAD ==="enable"))
	spl_autoload_register(function($class)use($__debug){
$file=str_replace("\\","/",$class);
$className=basename($file);
$isInterface=$className[0]==='I'&&ctype_upper($className[1]);
$file=($isInterface)?
"$file.interface.php":"$file.class.php";

if($__debug)
include_once($file);
else
@include_once($file);

if(!$isInterface&&
method_exists($class,"classInitialize"))
call_user_func(array($class,"classInitialize"));
});

if(is_callable("__init_complete"))
call_user_func("__init_complete");

if(defined("__MAIN_CLASS")&&is_string(__MAIN_CLASS)){

if(class_exists(__MAIN_CLASS)){
// Avoid conflict between class name and method name
if(!method_exists(__MAIN_CLASS,$start="main")&&
!method_exists(__MAIN_CLASS,$start="tmain"))
die("Cannot find entry method 'main' or 'tmain'");

if(!isset($_SERVER['REQUEST_METHOD']))
$args=$_SERVER['argv'];
elseif($_SERVER['REQUEST_METHOD']==="GET")
$args=$_GET;
elseif($_SERVER['REQUEST_METHOD']==="POST")
$args=$_POST;
else
$args=$_SERVER['argv'];

unset($dirname,$file);

$status=call_user_func_array(
array(__MAIN_CLASS,$start),
array(
$args,
array(
'requestType'=>(isset($_SERVER['REQUEST_METHOD']))?
$_SERVER['REQUEST_METHOD']:"COMMAND",
'query'=>$_GET,
'server'=>$_SERVER,
'environment'=>$_ENV,
'http-files'=>$_FILES,
'http-cookie'=>$_COOKIE
)
)
);

unset($status,$start,$args);
}
}

if(isset($__old_include_path))
$__old_include_path=set_include_path($__old_include_path);

if(is_callable("__exit"))
call_user_func("__exit");

?>

 

文章標籤
創作者介紹
創作者 wylokgo101 的頭像
wylokgo101

豆棚瓜架雨如絲 - WYLOKGO101

wylokgo101 發表在 痞客邦 留言(0) 人氣()