|
|
<?php
|
|
|
class LtAutoloader
|
|
|
{
|
|
|
public $conf = array(
|
|
|
/**
|
|
|
* 是否自动加载定义了函数的文件
|
|
|
*
|
|
|
* 可选项:
|
|
|
* # true 自动加载
|
|
|
* # false 跳过函数,只自动加载定义了class或者interface的文件
|
|
|
*/
|
|
|
"load_function" => true,
|
|
|
|
|
|
/**
|
|
|
* 要扫描的文件类型
|
|
|
*
|
|
|
* 若该属性设置为array("php","inc","php3"),
|
|
|
* 则扩展名为"php","inc","php3"的文件会被扫描,
|
|
|
* 其它扩展名的文件会被忽略
|
|
|
*/
|
|
|
"allow_file_extension" => array("php", "inc"),
|
|
|
|
|
|
/**
|
|
|
* 不扫描的目录
|
|
|
*
|
|
|
* 若该属性设置为array(".svn", ".setting"),
|
|
|
* 则所有名为".setting"的目录也会被忽略
|
|
|
*/
|
|
|
"skip_dir_names" => array(".svn"),
|
|
|
);
|
|
|
|
|
|
public $storeHandle;
|
|
|
public $autoloadPath;
|
|
|
protected $functionFileMapping;
|
|
|
protected $fileStore;
|
|
|
|
|
|
public function init()
|
|
|
{
|
|
|
if (!is_object($this->storeHandle))
|
|
|
{
|
|
|
$this->storeHandle = new LtStoreMemory;
|
|
|
$this->fileStore = new LtStoreFile;
|
|
|
$this->fileStore->prefix = 'LtAutoloader-token-cache';
|
|
|
$this->fileStore->useSerialize = true;
|
|
|
$this->fileStore->init();
|
|
|
}
|
|
|
// Whether scanning directory
|
|
|
if (0 == $this->storeHandle->get(".class_total") && 0 == $this->storeHandle->get(".function_total"))
|
|
|
{
|
|
|
$this->storeHandle->add(".class_total", 0);
|
|
|
$this->storeHandle->add(".function_total", 0);
|
|
|
$this->storeHandle->add(".functions", array(), 0);
|
|
|
$autoloadPath = $this->preparePath($this->autoloadPath);
|
|
|
foreach($autoloadPath as $key => $path)
|
|
|
{
|
|
|
if (is_file($path))
|
|
|
{
|
|
|
$this->addFileMap($path);
|
|
|
unset($autoloadPath[$key]);
|
|
|
}
|
|
|
}
|
|
|
$this->scanDirs($autoloadPath);
|
|
|
unset($autoloadPath);
|
|
|
}
|
|
|
// Whether loading function files
|
|
|
if ($this->conf["load_function"])
|
|
|
{
|
|
|
$this->loadFunction();
|
|
|
}
|
|
|
spl_autoload_register(array($this, "loadClass"));
|
|
|
}
|
|
|
|
|
|
public function loadFunction()
|
|
|
{
|
|
|
if ($functionFiles = $this->storeHandle->get(".functions"))
|
|
|
{
|
|
|
foreach ($functionFiles as $functionFile)
|
|
|
{
|
|
|
include($functionFile);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public function loadClass($className)
|
|
|
{
|
|
|
if ($classFile = $this->storeHandle->get(strtolower($className)))
|
|
|
{
|
|
|
include($classFile);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
protected function convertPath($path)
|
|
|
{
|
|
|
$path = str_replace("\\", "/", $path);
|
|
|
if (!is_readable($path))
|
|
|
{
|
|
|
trigger_error("Directory is not exists/readable: {$path}");
|
|
|
return false;
|
|
|
}
|
|
|
$path = rtrim(realpath($path), '\\/');
|
|
|
if ("WINNT" != PHP_OS && preg_match("/\s/i", $path))
|
|
|
{
|
|
|
trigger_error("Directory contains space/tab/newline is not supported: {$path}");
|
|
|
return false;
|
|
|
}
|
|
|
return $path;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* The string or an Multidimensional array into a one-dimensional array
|
|
|
*
|
|
|
* @param array $ or string $var
|
|
|
* @return array one-dimensional array
|
|
|
*/
|
|
|
protected function preparePath($var)
|
|
|
{
|
|
|
$ret = array();
|
|
|
if (!is_array($var))
|
|
|
{
|
|
|
$var = array($var);
|
|
|
}
|
|
|
$i = 0;
|
|
|
while (isset($var[$i]))
|
|
|
{
|
|
|
if (!is_array($var[$i]) && $path = $this->convertPath($var[$i]))
|
|
|
{
|
|
|
$ret[] = $path;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
foreach($var[$i] as $v)
|
|
|
{
|
|
|
$var[] = $v;
|
|
|
}
|
|
|
}
|
|
|
unset($var[$i]);
|
|
|
$i ++;
|
|
|
}
|
|
|
return $ret;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Using iterative algorithm scanning subdirectories
|
|
|
* save autoloader filemap
|
|
|
*
|
|
|
* @param array $dirs one-dimensional
|
|
|
* @return
|
|
|
*/
|
|
|
protected function scanDirs($dirs)
|
|
|
{
|
|
|
$i = 0;
|
|
|
while (isset($dirs[$i]))
|
|
|
{
|
|
|
$dir = $dirs[$i];
|
|
|
$files = scandir($dir);
|
|
|
foreach ($files as $file)
|
|
|
{
|
|
|
if (in_array($file, array(".", "..")) || in_array($file, $this->conf["skip_dir_names"]))
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
$currentFile = $dir . DIRECTORY_SEPARATOR . $file;
|
|
|
if (is_file($currentFile))
|
|
|
{
|
|
|
$this->addFileMap($currentFile);
|
|
|
}
|
|
|
else if (is_dir($currentFile))
|
|
|
{
|
|
|
// if $currentFile is a directory, pass through the next loop.
|
|
|
$dirs[] = $currentFile;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
trigger_error("$currentFile is not a file or a directory.");
|
|
|
}
|
|
|
} //end foreach
|
|
|
unset($dirs[$i]);
|
|
|
$i ++;
|
|
|
} //end while
|
|
|
}
|
|
|
|
|
|
protected function parseLibNames($src)
|
|
|
{
|
|
|
$libNames = array();
|
|
|
$tokens = token_get_all($src);
|
|
|
$level = 0;
|
|
|
$found = false;
|
|
|
$name = '';
|
|
|
foreach ($tokens as $token)
|
|
|
{
|
|
|
if (is_string($token))
|
|
|
{
|
|
|
if ('{' == $token)
|
|
|
{
|
|
|
$level ++;
|
|
|
}
|
|
|
else if ('}' == $token)
|
|
|
{
|
|
|
$level --;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
list($id, $text) = $token;
|
|
|
if (T_CURLY_OPEN == $id || T_DOLLAR_OPEN_CURLY_BRACES == $id)
|
|
|
{
|
|
|
$level ++;
|
|
|
}
|
|
|
if (0 < $level)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
switch ($id)
|
|
|
{
|
|
|
case T_STRING:
|
|
|
if ($found)
|
|
|
{
|
|
|
$libNames[strtolower($name)][] = $text;
|
|
|
$found = false;
|
|
|
}
|
|
|
break;
|
|
|
case T_CLASS:
|
|
|
case T_INTERFACE:
|
|
|
case T_FUNCTION:
|
|
|
$found = true;
|
|
|
$name = $text;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return $libNames;
|
|
|
}
|
|
|
|
|
|
protected function addClass($className, $file)
|
|
|
{
|
|
|
$key = strtolower($className);
|
|
|
if ($existedClassFile = $this->storeHandle->get($key))
|
|
|
{
|
|
|
trigger_error("duplicate class [$className] found in:\n$existedClassFile\n$file\n");
|
|
|
return false;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
$this->storeHandle->add($key, $file);
|
|
|
$this->storeHandle->update(".class_total", $this->storeHandle->get(".class_total") + 1);
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
protected function addFunction($functionName, $file)
|
|
|
{
|
|
|
$functionName = strtolower($functionName);
|
|
|
if (isset($this->functionFileMapping[$functionName]))
|
|
|
{
|
|
|
$existedFunctionFile = $this->functionFileMapping[$functionName];
|
|
|
trigger_error("duplicate function [$functionName] found in:\n$existedFunctionFile\n$file\n");
|
|
|
return false;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
$this->functionFileMapping[$functionName] = $file;
|
|
|
$this->storeHandle->update(".functions", array_unique(array_values($this->functionFileMapping)));
|
|
|
$this->storeHandle->update(".function_total", count($this->functionFileMapping));
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
protected function addFileMap($file)
|
|
|
{
|
|
|
if (!in_array(pathinfo($file, PATHINFO_EXTENSION), $this->conf["allow_file_extension"]))
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
$libNames = array();
|
|
|
if ($this->fileStore instanceof LtStore)
|
|
|
{
|
|
|
$cachedFileLastModified = (int) @filemtime($this->fileStore->getFilePath($file));
|
|
|
if ($cachedFileLastModified < filemtime($file) || !is_array(($libNames = $this->fileStore->get($file))))
|
|
|
{
|
|
|
$libNames = $this->parseLibNames(trim(file_get_contents($file)));
|
|
|
if (0 < $cachedFileLastModified)
|
|
|
{
|
|
|
$this->fileStore->update($file, $libNames);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
$this->fileStore->add($file, $libNames);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
$libNames = $this->parseLibNames(trim(file_get_contents($file)));
|
|
|
}
|
|
|
|
|
|
foreach ($libNames as $libType => $libArray)
|
|
|
{
|
|
|
$method = "function" == $libType ? "addFunction" : "addClass";
|
|
|
foreach ($libArray as $libName)
|
|
|
{
|
|
|
$this->$method($libName, $file);
|
|
|
}
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
}
|