第5章 SAI Framework 开发环境
本章大纲
- PHP 环境
- PHP OOP
- 项目目录
- PHP Namespace
- Autoload
- 配置文件
- Storage
- Extension
- 开发调试
- 错误处理
- 第一个 SAI 项目
- 本章运行验证
5.1 PHP 环境
SAI Framework 的基础开发语言采用 PHP。
选择 PHP OOP 作为 SAI Framework 的工程基础,并不是因为 SAI 属于 Web,而是因为 PHP 本身具备比较完整的:
类
对象
接口
继承
封装
命名空间
异常
文件系统
模块化
等面向对象开发能力。
因此,一个最基本的 SAI 开发环境可以是:
操作系统
↓
PHP
↓
SAI Framework
↓
Individual
最简单的环境只需要 PHP 即可。
例如:
PHP 8.x
就可以运行基础 SAI 程序。
如果项目需要 Web 应用,再增加:
Web Server
例如:
Apache
Nginx
PHP Built-in Server
但需要明确:
Web Server 是 SAI Web 应用的运行环境,不是 SAI Framework 本身的组成条件。
5.2 PHP OOP
SAI Framework 的核心对象都可以通过 PHP Class 表达。
例如:
class ObjectEntity
{
protected $id;
protected $name;
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
}
创建对象:
$room = new ObjectEntity(
'room_001',
'Room'
);
这样:
ObjectEntity
│
└── room_001
就成为一个实际运行中的 PHP Object。
SAI Framework 可以进一步把不同概念分别建立为 Class:
Information
Element
Object
Property
Relation
Rule
Fact
State
Memory
Experience
Ability
Engine
Manager
Central
Individual
例如:
class Property
{
protected $name;
protected $value;
public function __construct($name, $value)
{
$this->name = $name;
$this->value = $value;
}
}
这就是 SAI 最基础的对象建模方式。
5.3 项目目录
SAI Framework 需要一个清晰的目录结构。
本教程采用下面的基础结构:
sai/
│
├── app/
│ ├── Core/
│ ├── Information/
│ ├── Scene/
│ ├── Individual/
│ ├── Central/
│ ├── Element/
│ ├── Object/
│ ├── Relation/
│ ├── Rule/
│ ├── Perception/
│ ├── Cognition/
│ ├── Memory/
│ ├── Reasoning/
│ ├── Decision/
│ ├── Behavior/
│ ├── Expression/
│ ├── Template/
│ ├── Renderer/
│ ├── Adapter/
│ ├── Device/
│ ├── Feedback/
│ ├── Experience/
│ ├── Learning/
│ └── Extension/
│
├── config/
│
├── storage/
│
├── extensions/
│
├── templates/
│
├── public/
│ └── index.php
│
└── bootstrap.php
需要特别注意:
这只是本教程建立的架构目录,本章示例中的目录和文件属于教程设计;除非实际创建并运行,否则不能把它们描述为已经存在的项目。
目录职责
app/
核心 PHP 类。
app/
是 SAI Framework 的主要代码区域。
config/
配置:
config/
├── app.php
├── individual.php
├── memory.php
└── device.php
storage/
运行过程中产生的数据:
storage/
├── memory/
├── experience/
├── state/
├── logs/
└── runtime/
extensions/
扩展:
extensions/
├── engine/
├── adapter/
├── renderer/
├── device/
└── custom/
templates/
表现模板:
templates/
├── web/
├── api/
├── ui/
└── device/
public/
如果使用 Web:
public/index.php
可以作为 Web 入口。
5.4 PHP Namespace
当 SAI Framework 的 Class 数量越来越多时,如果所有类都处于全局命名空间,就容易出现名称冲突。
例如:
class Object
{
}
但是 PHP 或其他程序中可能已经存在同名定义。
因此 SAI Framework 使用 Namespace。
例如:
namespace SAI\Object;
class ObjectEntity
{
}
使用:
use SAI\Object\ObjectEntity;
$object = new ObjectEntity();
这样可以形成清晰的模块边界:
SAI
├── Information
├── Object
├── Relation
├── Memory
├── Reasoning
├── Decision
└── Behavior
例如:
namespace SAI\Memory;
class Memory
{
}
以及:
namespace SAI\Reasoning;
class ReasoningEngine
{
}
Namespace 不只是解决名称冲突。
它还可以表达:
一个 Class 属于 SAI Framework 的哪个模块。
5.5 Autoload
如果一个项目只有几个 PHP 文件,可以手动:
require_once 'Object.php';
require_once 'Memory.php';
require_once 'Rule.php';
但是 SAI Framework 会有大量 Class。
手动加载很快会变得复杂。
因此需要 Autoload。
最常见的方式是根据 Namespace 自动寻找 Class 文件。
例如:
SAI\Object\ObjectEntity
对应:
app/Object/ObjectEntity.php
一个简单的 Autoloader:
spl_autoload_register(function ($class) {
$prefix = 'SAI\\';
if (strpos($class, $prefix) !== 0) {
return;
}
$relative = substr($class, strlen($prefix));
$file = __DIR__
. '/app/'
. str_replace('\\', '/', $relative)
. '.php';
if (is_file($file)) {
require_once $file;
}
});
于是:
use SAI\Object\ObjectEntity;
$object = new ObjectEntity();
在需要 Class 时自动加载。
运行关系:
new ObjectEntity()
↓
Namespace
↓
Autoload
↓
寻找 PHP 文件
↓
require
↓
Class Loaded
这样 Framework 就不需要在每个文件里不断写大量 require_once。
5.6 配置文件
SAI Framework 的运行参数应该与代码分离。
例如:
config/app.php
可以保存:
<?php
return array(
'name' => 'SAI Framework',
'version' => '0.1.0',
'debug' => true
);
读取:
$config = require __DIR__ . '/config/app.php';
echo $config['name'];
可以进一步拆分:
config/
├── app.php
├── individual.php
├── information.php
├── memory.php
├── behavior.php
└── device.php
例如:
return array(
'name' => 'Individual-001',
'type' => 'general'
);
配置与代码分离的好处是:
代码
≠
运行参数
修改 Individual 的名称,不需要修改核心 Class。
5.7 Storage
SAI 是持续运行的 Individual,因此不能只有 PHP Class。
运行过程中还会产生:
Memory
State
Experience
Log
Runtime Data
因此需要 Storage。
例如:
storage/
├── memory/
├── experience/
├── state/
├── logs/
└── runtime/
Memory
例如:
storage/memory/
保存:
Fact
Knowledge
History
Memory Data
Experience
storage/experience/
保存:
Experience
Action Result
Historical Pattern
State
storage/state/
保存:
Individual State
Object State
Device State
Logs
storage/logs/
用于:
运行记录
错误记录
异常记录
调试记录
Runtime
storage/runtime/
可以用于:
临时数据
运行缓存
任务状态
需要注意:
Storage 是数据持久化和运行数据管理机制,不等于 Memory 本身。
Memory 是 SAI 的认知组成部分。
Storage 是工程上的数据保存设施。
两者必须区分。
5.8 Extension
SAI Framework 需要允许开发者增加新的能力。
例如核心 Framework:
Core
Information
Cognition
Memory
Reasoning
Decision
Behavior
外部可以增加:
Robot
Drone
Vehicle
Sensor
IndustrialDevice
形成:
SAI Framework
│
├── Core
│
└── Extensions
├── Robot
├── Drone
└── Vehicle
Extension 可以增加:
Engine
Adapter
Renderer
Object
Device
Template
例如:
class RobotAdapter
{
public function execute($command)
{
// 设备执行
}
}
核心 Framework 不需要因此修改成机器人专用 Framework。
这就是开放扩展思想。
5.9 开发调试
开发 SAI Framework 时,调试非常重要。
PHP 可以打开错误显示:
error_reporting(E_ALL);
ini_set('display_errors', '1');
开发环境可以使用:
Debug = true
生产环境则应该关闭详细错误输出:
Debug = false
调试时可以检查:
Class 是否加载
Object 是否创建
Information 是否进入
Engine 是否运行
Memory 是否保存
Rule 是否匹配
Decision 是否产生
Behavior 是否执行
例如:
var_dump($individual);
或者记录:
file_put_contents(
'storage/logs/sai.log',
date('Y-m-d H:i:s') . " Individual started\n",
FILE_APPEND
);
随着 Framework 成熟,可以进一步建立统一 Logger。
5.10 错误处理
SAI Framework 是一个长期运行的系统,因此错误处理不能只依赖:
echo error
应该建立统一的异常机制。
例如:
try {
$individual->run();
} catch (\Exception $e) {
echo $e->getMessage();
}
Framework 可以进一步定义:
SAIException
├── ConfigurationException
├── ObjectException
├── EngineException
├── MemoryException
├── DeviceException
└── ExtensionException
例如:
class EngineException extends \Exception
{
}
然后:
throw new EngineException(
'ReasoningEngine failed.'
);
统一错误处理可以让 Central 或 Application 对异常进行协调。
例如:
Exception
↓
Central
↓
Detection
↓
Diagnosis
↓
Recovery / Stop
↓
Log
这样错误处理可以逐渐与后面的 Self Maintenance 联系起来。
5.11 第一个 SAI 项目
现在建立一个最小的 SAI 项目。
本节只是教程中的最小运行示例设计,下面代码需要实际保存到本地 PHP 环境后才能进行运行验证。
目录:
sai-demo/
│
├── app/
│ └── Individual/
│ └── Individual.php
│
├── config/
│ └── app.php
│
├── bootstrap.php
│
└── index.php
5.11.1 Individual
文件:
app/Individual/Individual.php
代码:
<?php
namespace SAI\Individual;
class Individual
{
protected $id;
protected $name;
protected $state;
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
$this->state = 'created';
}
public function start()
{
$this->state = 'running';
}
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function getState()
{
return $this->state;
}
}
这个 Class 目前非常简单。
它只表达:
Individual
├── id
├── name
└── state
以及:
start()
5.11.2 配置文件
文件:
config/app.php
代码:
<?php
return array(
'name' => 'SAI Framework',
'version' => '0.1.0',
'debug' => true
);
5.11.3 Bootstrap
文件:
bootstrap.php
代码:
<?php
error_reporting(E_ALL);
ini_set('display_errors', '1');
spl_autoload_register(function ($class) {
$prefix = 'SAI\\';
if (strpos($class, $prefix) !== 0) {
return;
}
$relative = substr($class, strlen($prefix));
$file = __DIR__
. '/app/'
. str_replace('\\', '/', $relative)
. '.php';
if (is_file($file)) {
require_once $file;
}
});
Bootstrap 当前只负责:
PHP 环境初始化
↓
错误设置
↓
Autoload
5.11.4 index.php
文件:
index.php
代码:
<?php
require_once __DIR__ . '/bootstrap.php';
$config = require __DIR__ . '/config/app.php';
use SAI\Individual\Individual;
$individual = new Individual(
'individual_001',
'SAI Individual'
);
$individual->start();
echo '<pre>';
echo "Framework: "
. $config['name']
. PHP_EOL;
echo "Version: "
. $config['version']
. PHP_EOL;
echo "Individual ID: "
. $individual->getId()
. PHP_EOL;
echo "Individual Name: "
. $individual->getName()
. PHP_EOL;
echo "State: "
. $individual->getState()
. PHP_EOL;
echo '</pre>';
5.12 本章运行验证
这一节非常重要。
教程代码写出来,不等于项目已经验证成功。
只有实际执行后,才能确认运行结果。
例如在项目目录执行:
php -v
确认 PHP 环境。
然后:
php -l bootstrap.php
检查 Bootstrap 语法。
再检查:
php -l app/Individual/Individual.php
检查 Individual。
最后:
php -l index.php
检查入口文件。
如果使用 PHP CLI,可以运行:
php index.php
预期结构应该类似:
Framework: SAI Framework
Version: 0.1.0
Individual ID: individual_001
Individual Name: SAI Individual
State: running
这里的“预期”只是根据代码推导出的结果。
在没有实际运行环境验证之前,不能把它称为已经验证通过。
本章完整运行关系
第一个 SAI 项目的实际工程关系可以简化为:
index.php
│
↓
bootstrap.php
│
├── Error Configuration
│
└── Autoload
│
↓
config/app.php
│
↓
Individual
│
├── id
├── name
└── state
│
↓
start()
│
↓
state = running
│
↓
Output
进一步进入完整 SAI Framework 后:
Bootstrap
↓
Application
↓
Container
↓
Individual
↓
Central
↓
Information
↓
Perception
↓
Cognition
↓
Memory
↓
Reasoning
↓
Decision
↓
Behavior
也就是说,本章建立的只是开发基础。
还没有真正实现完整的:
Perception
Cognition
Memory
Reasoning
Decision
Behavior
Learning
这些内容将在后续章节逐步建立。
本章小结
SAI Framework 的开发环境可以归纳为:
PHP
↓
OOP
↓
Namespace
↓
Autoload
↓
Configuration
↓
Storage
↓
Extension
↓
Debug
↓
Exception
↓
SAI Framework
↓
Individual
其中最重要的几个工程原则是:
1. PHP 是实现语言
PHP
↓
OOP
↓
SAI
但 SAI 的概念不能被限制成 Web。
2. Namespace 建立模块边界
SAI\Information
SAI\Object
SAI\Memory
SAI\Reasoning
SAI\Decision
3. Autoload 管理 Class 加载
Class
↓
Namespace
↓
Autoload
↓
PHP File
4. Configuration 与代码分离
Code
≠
Configuration
5. Storage 与 Memory 分离
Storage
=
工程数据保存
Memory
=
Individual 的记忆机制
6. Extension 保持 Framework 开放
Core
↓
Extension
↓
Custom Individual
7. 最重要的是运行验证
编写代码
↓
语法检查
↓
实际运行
↓
检查输出
↓
确认结果
因此,第5章真正建立的是:
一个可以逐步向 SAI Framework 演进的 PHP OOP 工程基础,而不是把几个 PHP 文件简单组合起来。
下一章进入 第6章 SAI Framework 项目目录结构,将把 Core、Information、Scene、Individual、Central、Cognition、Memory、Reasoning、Decision、Behavior、Template、Renderer、Adapter、Device、Learning、Extension 等目录的职责逐一确定,并形成完整的 Framework 目录体系。