## psr-3規范
```
composer require psr/log
```
(`psr` 見 關于`psr規范解釋`)
這篇的跟`數據庫連接 (多例模式)` 有些類似,同樣是 `多例模式`
## 創建配置文件 config/log.php
```
<?php
return [
'default' => 'file1',
'channels' => [
'file1' => [ // 文件類型的日志
'driver' => 'stack',
'path' => FRAME_BASE_PATH.'/storage/',
'format' => '[%s][%s] %s', // 格式化類型 分別代表:[日期][日志級別]消息
],
'file2' => [
'driver' => 'daily',
'path' => FRAME_BASE_PATH.'/storage/',
'format' => '[%s][%s] %s', // 格式化類型
]
]
];
```
## 創建core/log/driver/StackLogger.php
這個日志是把所有內容都放同一個文件的。
現在可以直接 `new` 這個類,傳遞配置進去就可以了。
但是這樣不完善,如果想用 按日期分類的日志怎么辦?
所以想要一個 `調用者` 來控制,這個 `調用者是多例的`。
```
<?php
namespace core\log\driver;
use Illuminate\Contracts\Validation\ValidatesWhenResolved;
use Psr\Log\AbstractLogger;
use Psr\Log\LoggerInterface;
use Psr\Log\LoggerTrait;
class StackLogger extends AbstractLogger
{
protected $config;
public function __construct(array $config)
{
$this->config = $config;
}
// 來自: https://learnku.com/docs/psr/psr-3-logger-interface/1607
/**
* @example 代碼:app('log')->info('{language} is the best language in the world’,['language' => 'php']) 返回: php is the best language in the world
* @example 說白了 就是替換而已
* @param $message 原本消息
* @param $context 上下文 要替換的
* @return string
*/
public function interpolate($message, array $context = array())
{
// 構建一個花括號包含的鍵名的替換數組
$replace = array();
foreach ($context as $key => $val) {
// 檢查該值是否可以轉換為字符串
if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
$replace['{' . $key . '}'] = $val;
}
}
// 替換記錄信息中的占位符,最后返回修改后的記錄信息。
return strtr($message, $replace);
}
/**
* @inheritDoc
*/
public function log($level, $message, array $context = array())
{
if( is_array($message))
$message = var_export($message,true) . var_export($context,true); // 設置true 不輸出
else if( is_string($message)) // 內容是字符串 并且 $context是數組 替換占位符
$message = $this->interpolate($message,$context);
$message = sprintf($this->config['format'],date('y-m-d h:m:s'),$level,$message); // 根據配置文件格式化
/**
* error_log函數解釋
* @param $message 日志內容
* @param $message_type 類型 3是寫入到文件
* @param $destination 因為是3 填文件路徑
*/
error_log($message.PHP_EOL,3,$this->config['path'].'/php_frame.log');
}
}
```
### 關于AbstractLogger.php的解釋
這個是 `psr-log` 自帶的類,很簡單,只有幾個方法。
可以讓我們少些幾行代碼。

## 創建調用者 core/log/Logger.php
```
<?php
namespace core\log;
use core\log\driver\DailyLogger;
use core\log\driver\StackLogger;
class Logger
{
protected $channels = []; // 所有的實例化的通道 就是多例而已
protected $config;
public function __construct()
{
$this->config = \App::getContainer()->get('config')->get('log');
}
public function channel($name = null)
{
if(! $name) // 沒選擇名字
$name = $this->config['default'];
if( isset($this->channels[$name]))
return $this->channels[$name];
$config = \App::getContainer()->get('config')->get('log.channels.'.$name);
//如:$config['driver'] = stack, 則調用createStack($config);
return $this->channels['name'] = $this->{'create'.ucfirst($config['driver'])}($config);
}
// 放在同一個文件
public function createStack($config)
{
return new StackLogger($config);
}
public function __call($method, $parameters)
{
return $this->channel()->$method(...$parameters);
}
}
```
## 綁定到容器

## 運行



## 結尾

`driver` = `daily` 本教程未實現,太多代碼了。
但是讀者可自行實現。
- 前言
- 基礎篇
- 1. 第一步 創建框架目錄結構
- 2. 引入composer自動加載
- 3. php自動加載 (解釋篇)
- 4. 創建容器 注冊樹模式
- 5. 關于psr規范解釋
- 6. 關于"容器" "契約" "依賴注入" (解釋篇)
- 7. 添加函數文件helpers.php
- 8. 初始化請求(Request)
- 9. 響應 (Response)
- 10. 路由一 (路由組實現)
- 11. 路由二 (加入中間件)
- 12. 配置信息 (類似laravel)
- 13. 數據庫連接 (多例模式)
- 14. 查詢構造器 (query builder)
- MVC實現
- M 模型實現 (數據映射 + 原型 模式)
- C 控制器實現 + 控制器中間件
- V 視圖實現 (Laravel Blade 引擎)
- V 視圖切換成 ThinkPhp 模板 引擎)
- 其他輪子
- 日志
- 自定義異常 (異常托管)
- 單元測試 (phpunit)
- 替換成swoole的http服務器
- 協程上下文解決request問題
- qps測試
- 發布到packagist.org