Skip to content

Latest commit

 

History

History
197 lines (191 loc) · 6.75 KB

[关键概念三]别名与类自动加载源码.md

File metadata and controls

197 lines (191 loc) · 6.75 KB

目录

别名Aliases

别名是底层BaseYii提供的功能,涉及到的函数都是静态的,Yii类与BaseYii的关系是继承关系,源码为

class Yii extends \yii\BaseYii
{
}

可以设置别名,如

Yii::setAlias("@a","aaaa");
Yii::setAlias("@a/b","aaaa/bbb");
Yii::setAlias("@c","@a/u");

涉及的方法为

public static $aliases = ['@yii' => __DIR__];
public static function setAlias($alias, $path)
{
    //可见第一个参数是否是@开头无所谓,即使没有@开头也会被强制加上
    if (strncmp($alias, '@', 1)) {
        $alias = '@' . $alias;
    }
    $pos = strpos($alias, '/');
    $root = $pos === false ? $alias : substr($alias, 0, $pos);
    if ($path !== null) {  //创建别名操作
        //别名的值是否还包含别名
        $path = strncmp($path, '@', 1) ? rtrim($path, '\\/') : static::getAlias($path);
        //如果要创建的别名不存在
        if (!isset(static::$aliases[$root])) {
            if ($pos === false) {
                static::$aliases[$root] = $path;
            } else {
                static::$aliases[$root] = [$alias => $path];
            }
        } elseif (is_string(static::$aliases[$root])) {  //要创建的别名存在
            if ($pos === false) {
                static::$aliases[$root] = $path;
            } else {
                static::$aliases[$root] = [
                    $alias => $path,
                    $root => static::$aliases[$root],
                ];
            }
        } else {  //要创建的别名存在
            static::$aliases[$root][$alias] = $path;
            krsort(static::$aliases[$root]);
        }
    } elseif (isset(static::$aliases[$root])) {   //删除别名操作
        if (is_array(static::$aliases[$root])) {
            unset(static::$aliases[$root][$alias]);
        } elseif ($pos === false) {
            unset(static::$aliases[$root]);
        }
    }
}

根据源码,如

Yii::setAlias("@ab","abc");
Yii::setAlias("@ab/x","123");
var_dump(Yii::$aliases);

首先会创建一个@ab的别名,值为abc;

array (size=1)
  '@ab' => string 'abc' (length=3)

然后存入$ab/x别名,底层的会将存@ab的结构改为数组

array (size=1)
  '@ab' => 
    array (size=2)
      '@ab/x' => string '123' (length=3)
      '@ab' => string 'abc' (length=3)

如果存入操作为

Yii::setAlias("@ab/x","abc");
Yii::setAlias("@ab/y","123");
var_dump(Yii::$aliases);

那么底层的结构为

array (size=1)
  '@ab' => 
    array (size=2)
      '@ab/y' => string '123' (length=3)
      '@ab/x' => string 'abc' (length=3)

删除一个别名也是setAlias

Yii::setAlias("@a",123);  //创建别名
Yii::setAlias("@a");   //删除别名

取出别名的操作为getAlias

public static function getAlias($alias, $throwException = true)
{
    //取出别名的第一个参数需要使用@开头
    if (strncmp($alias, '@', 1)) {
        // not an alias
        return $alias;
    }

    $pos = strpos($alias, '/');
    $root = $pos === false ? $alias : substr($alias, 0, $pos);
    //判断根别名是否存在
    if (isset(static::$aliases[$root])) {
        if (is_string(static::$aliases[$root])) {
            return $pos === false ? static::$aliases[$root] : static::$aliases[$root] . substr($alias, $pos);
        }
        foreach (static::$aliases[$root] as $name => $path) {
            if (strpos($alias . '/', $name . '/') === 0) {
                return $path . substr($alias, strlen($name));
            }
        }
    }
    //如果查找不到别名则异常
    if ($throwException) {
        throw new InvalidArgumentException("Invalid path alias: $alias");
    }

    return false;
}

Yii::setAlias("@a/b","123");
$res = Yii::getAlias("@a/b/ccc");  //123/ccc

类自动加载

yii是依赖于composer的,composer是有自动加载机制的,Yii在composer的类自动加载基础上又做了一层自己的加载机制

class Yii extends \yii\BaseYii
{
}

spl_autoload_register(['Yii', 'autoload'], true, true);
Yii::$classMap = require __DIR__ . '/classes.php';

spl_autoload_register底层是一个队列,可以注册多个加载机制,第三个参数为true表示将这个队列放在最前面
所以yii的加载机制为

  • 先用自己的类加载机制
  • 自己的找不到就用composer的类加载机制 Yii使用了classMap机制类来做类与文件路径的映射,这种加载机制非常快,但是缺点是代码量非常大
return [
  'yii\base\Action' => YII2_PATH . '/base/Action.php',
  'yii\base\ActionEvent' => YII2_PATH . '/base/ActionEvent.php',
  'yii\base\ActionFilter' => YII2_PATH . '/base/ActionFilter.php',
  'yii\base\Application' => YII2_PATH . '/base/Application.php',
  'yii\base\ArrayAccessTrait' => YII2_PATH . '/base/ArrayAccessTrait.php',
  'yii\base\Arrayable' => YII2_PATH . '/base/Arrayable.php',
  'yii\base\ArrayableTrait' => YII2_PATH . '/base/ArrayableTrait.php',
  'yii\base\BaseObject' => YII2_PATH . '/base/BaseObject.php',
  'yii\base\Behavior' => YII2_PATH . '/base/Behavior.php',
  'yii\base\BootstrapInterface' => YII2_PATH . '/base/BootstrapInterface.php',
  'yii\base\Component' => YII2_PATH . '/base/Component.php',
  'yii\base\Configurable' => YII2_PATH . '/base/Configurable.php',
  'yii\base\Controller' => YII2_PATH . '/base/Controller.php',
  'yii\base\DynamicContentAwareInterface' => YII2_PATH . '/base/DynamicContentAwareInterface.php',
  'yii\base\DynamicContentAwareTrait' => YII2_PATH . '/base/DynamicContentAwareTrait.php',
  'yii\base\DynamicModel' => YII2_PATH . '/base/DynamicModel.php',
  'yii\base\ErrorException' => YII2_PATH . '/base/ErrorException.php',
  'yii\base\ErrorHandler' => YII2_PATH . '/base/ErrorHandler.php',
  'yii\base\Event' => YII2_PATH . '/base/Event.php',
  ...

底层autoload逻辑为

public static function autoload($className)
{
    if (isset(static::$classMap[$className])) {  //如果在classMap中存在
        $classFile = static::$classMap[$className];
        if ($classFile[0] === '@') {  //如果这个类的文件地址是一个别名,则获取这个别名对应的值
            $classFile = static::getAlias($classFile);
        }
    } elseif (strpos($className, '\\') !== false) {  //如果className不包含\\,则使用别名找对应的值
        $classFile = static::getAlias('@' . str_replace('\\', '/', $className) . '.php', false);
        if ($classFile === false || !is_file($classFile)) {
            return;
        }
    } else {
        return;
    }

    include $classFile;

    if (YII_DEBUG && !class_exists($className, false) && !interface_exists($className, false) && !trait_exists($className, false)) {
        throw new UnknownClassException("Unable to find '$className' in file: $classFile. Namespace missing?");
    }
}