BaseTank类作为我们框架的核心类,会提供组织框架的一系列功能。
其中包括:
1.提供文件路径定义别名的功能
2.提供类自动加载的功能
3.提供创建框架基础对象的功能(使用DI容器提供的功能)
基于以上设想我们设计了BaseTank类的结构:
静态属性:
1. $app: 持有application对象,是一个单例
2. $classMap: 注册了类的索引,数组键是类名,数组值是对应的类文件路径
3. $aliasMap: 别名索引,所有注册的别名,在这里体现
4. $container:DI容器,在创建对象时会使用它提供的依赖注入机制
静态方法:
1.提供注册别名的方法: registerAlias
2.提供解析别名的方法: parseAlias
3.提供类的自动加载机制: autoload
4.提供创建对象的方法,基于DI: generateObject
5.提供给对象属性批量赋值的方法: batchSetProperty
1.别名的数据结构
['@@root'] => [
'@@root/aa' => 'p3/p4/p5',
'@@root' => 'p1/p2',
]
别名以@@作为前缀,@@+开头部分即根部分相同的作为一组,组内按键值倒序排列。
倒序排列会造成在设置别名时,要时刻注意排序。在解析别名时,要遵循最长匹配原则。
起初想使用类似于DFA算法实现关键词过滤类似的方式,但没有做深入探讨,所以使用这种简单的方式来实现。
registerAlias方法:
public static function registerAlias($alias, $filePath = '')
{
if (!empty($filePath)) {
$dirSeparator = static::getDirSeparator();
if (strncmp($alias, '@@', 2) !== 0) {
$alias = '@@'.$alias;
}
$firstSeparatorPos = strpos($alias, $dirSeparator);
$isMultipleLayers = $firstSeparatorPos !== false ? true : false;
$filePathHasAlias = strncmp($filePath, '@@', 2) !== 0 ? false : true;
$filePath = $filePathHasAlias ? static::parseAlias($filePath) : rtrim($filePath, static::CLASS_SEPARATOR.$dirSeparator);
if ($isMultipleLayers) {
$rootNode = substr($alias, 0, $firstSeparatorPos);
} else {
$rootNode = $alias;
}
if (!isset(static::$aliasMap[$rootNode]) || empty(static::$aliasMap[$rootNode])) {
static::$aliasMap[$rootNode] = [
$alias => $filePath,
];
} else if (is_array(static::$aliasMap[$rootNode])){
static::$aliasMap[$rootNode][$alias] = $filePath;
krsort(static::$aliasMap[$rootNode]);
}
}
}
parseAlias方法:
public static function parseAlias($alias)
{
if (strncmp($alias, '@@', 2)) {
return $alias;
}
$dirSeparator = static::getDirSeparator();
$firstSeparatorPos = strpos($alias, $dirSeparator);
$isMultipleLayers = $firstSeparatorPos !== false ? true : false;
if ($isMultipleLayers) {
$rootNode = substr($alias, 0, $firstSeparatorPos);
} else {
$rootNode = $alias;
}
if (isset(static::$aliasMap[$rootNode]) && !empty(static::$aliasMap[$rootNode])) {
foreach (static::$aliasMap[$rootNode] as $eachAlias => $eachFilePath) {
if (strpos($alias . $dirSeparator, $eachAlias . $dirSeparator) === 0) {
return $eachFilePath . substr($alias, strlen($eachAlias));
}
}
}
}
2.类的自动加载
构造autoload方法,该方法现使用classMap匹配路径,然后使用别名解析路径,只有这两种方式才能寻找到某个类所在的文件路径并require到脚本中。
到时把该autoload方法通过spl_autoload_register方法注册到php类自动加载的调用堆栈中即可。
类自动加载方法:
public static function autoload($className)
{
$dirSeparator = static::getDirSeparator();
//备注:框架类的自动加载,交由composer处理.如果不想使用composer处理,必须使用别名机制.
if (isset(static::$classMap[$className])) {
$classFilePath = static::$classMap[$className];
if (strpos($classFilePath, '@@') !== false) {
$classFilePath = static::parseAlias($classFilePath);
}
} else if(strpos($className, static::CLASS_SEPARATOR) !== false) {
$classFilePath = static::parseAlias('@@' . str_replace(static::CLASS_SEPARATOR, $dirSeparator, $className) . '.php');
if ($classFilePath === false || !is_file($classFilePath)) {
return;
}
} else {
return;
}
if (isset($classFilePath) && file_exists($classFilePath)) {
include_once($classFilePath);
}
}
3.创建框架基础对象
该功能通过调用container的方法,委托给container完成,到实现container的地方我们再来详解。
创建对象方法:
public static function generateObject()
{
//此方法实现和container的实现有关,所以到实现container的地方我们再回头实现它
}
好,欲知下事如何,且听下回分解……
github源码:https://github.com/2lovecode/tank