在看了石頭大大幾篇關於'Mix-in' 混成 的文章,有了一個想法:若是命名空間也可以混成的話,是否能增加更多彈性,再研究之後發現可以利用 __autoload 與 動態 require_once 達到需求
作法如下:
- 以目錄當作區分 一個目錄代表一個命名空間。如nameapsce1
- 目錄內每一個檔案為一個類別,檔案名稱必須與類別名稱相同,但是不能使用命名空間(這部份要由程式自動產生)
- 建立__autoload與專門處理對應的類別如下
//預設產生檔案的目錄
const SCRIPT_FILE_TEMP_DIR='/dev/shm';
//
const DEBUG=true;
//若要使用memcache 快取可在此設定
const CONFIG_MEMCACHED_HOST='127.0.0.1';
const CONFIG_MEMCACHED_PORT=11211;
const CONFIG_MEMCACHED_TIMEOUT=3600;
function __autoload($name)
{
$tmpdir=getcwd();
if($tmpdir!=__DIR__)
{
chdir(__DIR__);
AliasNameSpace::autoload($name);
chdir($tmpdir);
}
else
{
AliasNameSpace::autoload($name);
}
}
/**
*AliasNameSpace
*實作命名空間建立與混成功能
*以目錄為命名空間
*AliasNameSpace::addNameSpace("Newnamespace","namespace");// Newnamespace=[namespace]
*AliasNameSpace::addNameSpace("Newnamespace","namespace2");//Newnamespace=[namespace,namespace2]後者優先
*AliasNameSpace::addNameSpace("Newnamespace3","Newnamespace");// Newnamespace3=[namespace,namespace2]
*AliasNameSpace::addNameSpaceClass("Newnamespacename","namespace5",'class1');//Newnamespacename.class1=[namespace5.class1]
*AliasNameSpace::addNameSpaceClass("Newnamespacename","namespace6",'class2');//Newnamespacename.class1=[namespace5.class1,namespace6.class2]
*AliasNameSpace::addNameSpace("Newnamespace3","namespace4");//Newnamespace3=[namespacename,namespacename2,namespace4]
*AliasNameSpace::scanClass('Newnamespacename');找出命名空間所有類別
*use Newnamespacename\class1 as class1
*$n=new class1();
*/
class AliasNameSpace
{
const readCache=false;//memcache 讀取快取(直接跳過運算),開發時請勿使用
const writeCache=false;//memcache 寫入快取
const globleval='*';
private static $aliasNameSpace=array();
private static $aliasNameSpaceClass=array();
private static $memcache;
private static function loadcache($name)
{
if(self::writeCache||self::readCache)
{
self::$memcache = new Memcache;
self::$memcache->connect(CONFIG_MEMCACHED_HOST, CONFIG_MEMCACHED_PORT) or die ("Could not connect");
if(self::readCache)
{
$tempfile=self::$memcache->get(md5(__FILE__.__CLASS__.$name),MEMCACHE_COMPRESSED);
if($tempfile==false)
{
return false;
}
else
{
require_once $tempfile;
return true;
}
}
else
{
return false;
}
}
else
{
return false;
}
}
private static function listNameSpace($namespacename)
{
$r=array();
if(isset(self::$aliasNameSpace[$namespacename]))
{
$alias=self::$aliasNameSpace[$namespacename];
foreach($alias as $key=>$val)
{
$r[]=$val;
if($val['aliasnamespace']==$namespacename)
{
continue;
}
else
{
foreach(self::listNameSpace($val['aliasnamespace']) as $v)
{
$r[]=$v;
}
}
}
}
//if(DEBUG) print_r($r);
return $r;
}
private static function listaliasNameSpaceClass($namespacename,$class)
{
$r=array();
if(isset(self::$aliasNameSpaceClass[$namespacename][self::globleval]))//繼承全域而來的優先權較小
{
$alias=self::$aliasNameSpaceClass[$namespacename][self::globleval];
foreach($alias as $key=>$val)
{
if($val['aliasnamespace']==$namespacename)
{
continue;
}
else
{
foreach(self::listaliasNameSpaceClass($val['aliasnamespace'],$class) as $v)
{
$r[]=$v;
}
}
}
}
if(isset(self::$aliasNameSpaceClass[$namespacename][$class]))
{
$alias=self::$aliasNameSpaceClass[$namespacename][$class];
foreach($alias as $key=>$val)
{
$r[]=$val;
if($val['aliasnamespace']==$namespacename&&$val['aliasclass']==$class)
{
continue;
}
else
{
foreach(self::listaliasNameSpaceClass($val['aliasnamespace'],$val['aliasclass']) as $v)
{
$r[]=$v;
}
}
}
}
//if(DEBUG) print_r($r);
return $r;
}
private static function toNameSpaceClass($name)
{
$pos=strrpos($name,"\\");
$namespacename=substr($name,0,$pos);
$classname=substr($name,$pos+1);
return array('namespacename'=>$namespacename,'classname'=>$classname);
}
private static function file_exists($namespacename,$classname,&$path)
{
$path=__DIR__.DIRECTORY_SEPARATOR.
str_replace("\\",DIRECTORY_SEPARATOR,$namespacename).
DIRECTORY_SEPARATOR.$classname.'.php';
if(file_exists($path))
{
return true;
}
else
{
return false;
}
}
private static function includefile($name,$namespacename,$path)
{
$tempfile=SCRIPT_FILE_TEMP_DIR.DIRECTORY_SEPARATOR.md5($name.'@'.filemtime($path)).'.php';
if(file_exists($tempfile))
{
}
else
{
$contents=file_get_contents($path);
$contents="".$contents;
file_put_contents($tempfile,$contents);
}
if(self::writeCache)
{
echo self::$memcache->set(md5(__FILE__.__CLASS__.$name),$tempfile, MEMCACHE_COMPRESSED,CONFIG_MEMCACHED_TIMEOUT) ;
}
require_once $tempfile;
}
public static function removeNameSpace($namespace,$aliasnamespace,$priority=null)
{
if(isset(self::$aliasNameSpace[$namespace]))
{
foreach(self::$aliasNameSpace[$namespace] as $key=>$val)
{
if($val['aliasnamespace']==$aliasnamespace)
{
if($priority===null)
{
unset(self::$aliasNameSpace[$namespace][$key]);
self::removeNameSpaceClass($namespace,$aliasnamespace,self::globleval);
return true;
}
elseif($key==$priority)
{
return false;
}
else
{
unset(self::$aliasNameSpace[$namespace][$key]);
self::removeNameSpaceClass($namespace,$aliasnamespace,self::globleval);
return true;
}
}
}
}
return true;
}
public static function addNameSpace($namespace,$aliasnamespace,$priority=1000)
{
if(isset(self::$aliasNameSpace[$namespace]))
{
if(self::removeNameSpace($namespace,$aliasnamespace,$priority))
{
if(isset(self::$aliasNameSpace[$namespace][$priority]))
{
self::addNameSpace($namespace,self::$aliasNameSpace[$namespace][$priority]['aliasnamespace'],$priority-1);//預設覆蓋之前設定
self::$aliasNameSpace[$namespace][$priority]['aliasnamespace']=$aliasnamespace;
ksort(self::$aliasNameSpace[$namespace]);
self::addNameSpaceClass($namespace,$aliasnamespace,self::globleval,$priority);
return true;
}
else
{
self::$aliasNameSpace[$namespace][$priority]['aliasnamespace']=$aliasnamespace;
ksort(self::$aliasNameSpace[$namespace]);
self::addNameSpaceClass($namespace,$aliasnamespace,self::globleval,$priority);
return true;
}
}
}
else
{
self::$aliasNameSpace[$namespace][$priority]['aliasnamespace']=$aliasnamespace;
self::addNameSpaceClass($namespace,$aliasnamespace,self::globleval,$priority);
return true;
}
}
public static function removeNameSpaceClass($namespace,$aliasnamespace,$aliasclass,$priority=null)
{
$class=$aliasclass;
if(isset(self::$aliasNameSpaceClass[$namespace][$class]))
{
foreach(self::$aliasNameSpaceClass[$namespace][$class] as $key=>$val)
{
if($val['aliasnamespace']==$aliasnamespace&&$val['aliasclass']==$aliasclass)
{
if($priority===null)
{
unset(self::$aliasNameSpaceClass[$namespace][$class][$key]);
return true;
}
elseif($key==$priority)
{
return false;
}
else
{
unset(self::$aliasNameSpaceClass[$namespace][$class][$key]);
return true;
}
}
}
}
return true;
}
public static function addNameSpaceClass($namespace,$aliasnamespace,$aliasclass,$priority=1000)
{
$class=$aliasclass;
if(isset(self::$aliasNameSpaceClass[$namespace][$class]))
{
if(self::removeNameSpaceClass($namespace,$aliasnamespace,$aliasclass,$priority))
{
if(isset(self::$aliasNameSpaceClass[$namespace][$class][$priority]))
{
self::addNameSpaceClass(
$namespace,
self::$aliasNameSpaceClass[$namespace][$class][$priority]['aliasnamespace'],
self::$aliasNameSpaceClass[$namespace][$class][$priority]['aliasclass'],
$priority-1 //預設覆蓋之前設定
);
self::$aliasNameSpaceClass[$namespace][$class][$priority]['aliasnamespace']=$aliasnamespace;
self::$aliasNameSpaceClass[$namespace][$class][$priority]['aliasclass']=$aliasclass;
ksort(self::$aliasNameSpaceClass[$namespace][$class]);
return true;
}
else
{
self::$aliasNameSpaceClass[$namespace][$class][$priority]['aliasnamespace']=$aliasnamespace;
self::$aliasNameSpaceClass[$namespace][$class][$priority]['aliasclass']=$aliasclass;
ksort(self::$aliasNameSpaceClass[$namespace][$class]);
return true;
}
}
}
else
{
self::$aliasNameSpaceClass[$namespace][$class][$priority]['aliasnamespace']=$aliasnamespace;
self::$aliasNameSpaceClass[$namespace][$class][$priority]['aliasclass']=$aliasclass;
return true;
}
}
public static function autoload($name)
{
if(self::loadcache($name))
{
return true;
}
else
{
$names=self::toNameSpaceClass($name);
self::addNameSpace($names['namespacename'],$names['namespacename'],0);
self::addNameSpaceClass($names['namespacename'],$names['namespacename'],$names['classname'],0);
//if(DEBUG) print_r($names);
//if(DEBUG) print_r(self::$aliasNameSpace);
//if(DEBUG) print_r(self::$aliasNameSpaceClass);
//if(DEBUG) echo $name.PHP_EOL;
$pathlist=array();
foreach(array_reverse(self::listaliasNameSpaceClass($names['namespacename'],$names['classname'])) as $alias)
{
if(self::file_exists($alias['aliasnamespace'],$alias['aliasclass'],$path))
{
self::includefile($name,$names['namespacename'],$path);
if(DEBUG) echo "listaliasNameSpaceClass:載入類別:{$name}(載入路徑:".implode(PHP_EOL,$pathlist)."::{$path})".PHP_EOL;
return true;
}
else
{
$pathlist[]=$path;
}
}
foreach(array_reverse(self::listNameSpace($names['namespacename'])) as $alias)
{
if(self::file_exists($alias['aliasnamespace'], $names['classname'],$path))
{
self::includefile($name,$names['namespacename'],$path);
if(DEBUG) echo "listNameSpace:載入類別:{$name}(載入路徑:".implode(PHP_EOL,$pathlist)."::{$path})".PHP_EOL;
return true;
}
else
{
$pathlist[]=$path;
}
}
if(self::file_exists($names['namespacename'],$names['classname'],$path))
{
self::includefile($name,$names['namespacename'],$path);
if(DEBUG) echo "載入類別:{$name}(載入路徑:".implode(PHP_EOL,$pathlist)."::{$path})".PHP_EOL;
return true;
}
else
{
die("無法載入類別:{$name}(載入路徑:".implode(PHP_EOL,$pathlist).")".PHP_EOL);
}
}
}
public static function scanClass($namespacename)
{
$r=array();
//if(DEBUG) echo $namespacename.PHP_EOL;
//if(DEBUG) print_r(self::$aliasNameSpace);
//if(DEBUG) print_r(self::$aliasNameSpaceClass);
$path=__DIR__.DIRECTORY_SEPARATOR.str_replace("\\",DIRECTORY_SEPARATOR,$namespacename);
if(file_exists($path))
{
$classdir = dir($path);
while (false !== ($entry = $classdir->read()))
{
if(substr($entry,-4)=='.php')
{
$r[substr($entry,0,-4)]='origin';
}
}
$classdir->close();
}
foreach(self::listNameSpace($namespacename) as $alias)
{
if($alias['aliasnamespace']==$namespacename) continue;
foreach(self::scanClass($alias['aliasnamespace']) as $key => $val)
{
if($key==self::globleval)
{
continue;
}
$r[$key]='aliasNameSpace';
}
}
if(isset(self::$aliasNameSpaceClass[$namespacename]))
{
foreach(self::$aliasNameSpaceClass[$namespacename] as $class=>$alias)
{
if($class==self::globleval)
{
continue;
}
$r[$class]='aliasNameSpaceClass';
}
}
return $r;
}
}
本著作由曾德雄(cedriccefc2002@gmail.com)製作,以創用CC 姓名標示-相同方式分享 3.0 台灣 授權條款釋出。