上节,我们研读了Pimple容器的具体实现,这节我们来查看Slim默认容器的实现细节。简单浏览代码我们可知,Slim默认容器其实就是对Pimple容器进行的一层封装。前面我们已知Slim\Container完成了集成默认配置项,注册默认服务。现在,让我们接着看剩下的代码。
/**
* 通过标识符查找容器的条目并将其返回
* Finds an entry of the container by its identifier and returns it.
*
* @param string $id Identifier of the entry to look for.
*
* @throws ContainerValueNotFoundException No entry was found for this identifier.
* @throws ContainerException Error while retrieving the entry.
*
* @return mixed Entry.
*/
public function get($id)
{
// 检查参数或对象是否存在
if (!$this->offsetExists($id)) {
throw new ContainerValueNotFoundException(sprintf('Identifier "%s" is not defined.', $id));
}
try {
// 根据$id返回一个参数或对象
return $this->offsetGet($id);
} catch (\InvalidArgumentException $exception) {
// 是否该类offsetGet()出现异常。
if ($this->exceptionThrownByContainer($exception)) {
throw new SlimContainerException(
sprintf('Container error while retrieving "%s"', $id),
null,
$exception
);
} else {
throw $exception;
}
}
}
为了研读进度,我们暂时不会详细讲解异常,后面我们会专门准备一个章节来研读异常类。但大致对异常有个了解还是必须的。
PHP标准库提供了内置了异常类。
异常
- BadFunctionCallException —错误函数调用异常
- BadMethodCallException —错误方法调用异常
- DomainException —作用域异常
- InvalidArgumentException —非法参数异常
- LengthException —长度异常
- LogicException —逻辑异常
- OutOfBoundsException —违背安全原则异常
- OutOfRangeException —越界索引异常
- OverflowException —上溢异常
- RangeException —范围异常
- RuntimeException —运行时异常
- UnderflowException —下溢异常
- UnexpectedValueException —意外数值异常
让我们了解容器是如何使用异常类的吧
private function exceptionThrownByContainer(\InvalidArgumentException $exception)
{
// 跟踪异常(包括文件地址、报错类、函数信息等)
$trace = $exception->getTrace()[0];
return $trace['class'] === PimpleContainer::class && $trace['function'] === 'offsetGet';
}
$exception异常对象的getTrace()方法返回了一个数组,数组项中包括报错类,报错文件地址等信息。这里取第一条,然后返回是否该类的offsetGet方法出现异常。
为何要这样写呢?
其实不难理解,当程序出错时,我们一般要想确定异常报错位置,都会自定义一个异常类。但这里使用了SPL的标准异常类,报错信息中也可能存在多处有\InvalidArgumentException异常,那要如何确定当前方法就是出错位置呢?这里采用自定义判断函数根据跟踪信息来确定报错位置的方式。
OK,让我们一起浏览剩下的代码吧。
/**
* 如果容器可以返回给定标识符的条目,则返回true。
* Returns true if the container can return an entry for the given identifier.
* Returns false otherwise.
*
* @param string $id Identifier of the entry to look for.
*
* @return boolean
*/
public function has($id)
{
return $this->offsetExists($id);
}
/********************************************************************************
* Magic methods for convenience
*******************************************************************************/
// 魔术方法,读取不可访问的属性时__get会使用,然后调用get方法
public function __get($name)
{
return $this->get($name);
}
// 魔术方法,当对不可访问属性调用isset()或empty()时,__isset()会被调用
public function __isset($name)
{
return $this->has($name);
}
经过一段时间的努力,相信大家对容器已经有个清晰的了解啦。虽然路途艰险,但我们终究克服了困难,默默成长,取得了前进的一大步。柚子也相信每个坚持到这里的人都是执着的,值得夸赞的!哈哈!