# Arsenals **Repository Path**: orionis/Arsenals ## Basic Information - **Project Name**: Arsenals - **Description**: PHP开源开发框架,用于学习回顾PHP使用。 - **Primary Language**: PHP - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2014-01-10 - **Last Updated**: 2020-12-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README #Arsenals 开发文档 **版本号: 1.0 DEV** **作者: 管宜尧** ***2014/1/4 0:04:56*** ##第一章 架构指南 Arsenals采用了基于MVC的架构,同时支持Service层的扩展。 - **控制器(controller)** 控制器主要负责处理用户的请求,选择相应的Service层或者是模型层对用户的请求进行处理,并负责选择视图展示需要输出的内容。 - **服务层(service)** *可选* 服务层是可选的,完成系统业务逻辑的处理。 - **模型层(model)** 在没有服务层的情况下,模型层完成业务逻辑的处理并对数据库操作进行处理,在服务层存在的情况下,模型层主要负责与数据库之间进行交互,为Service层实现与下层数据库的交互,而让Service层只关注业务逻辑的处理。 - **视图层(view)** 视图层负责页面内容的展示,将模型的数据进行渲染,生成HTML或者json格式的数据返回给客户端,用户最终内容的展示。 - **过滤器(filter)** 过滤器类似于java servlet中的Filter,用户对用户请求处理前后进行预处理,如权限控制、异常处理等。 系统采用了 *PHP 5.3* 开始支持的 **命名空间** ,在开发过程中,可以实现业务代码的灵活组织,非常简单的实现 ***HMVC*** 架构,以便对业务逻辑进行分离,更加清晰的实现模块化的开发。 ##第二章 开发规范 ####命名规范 1. 对于类,采用驼峰命名法进行命名,如`SessionUtils` 2. 对于成员变量,变量,采用_分割的小写方式命名,如`$cate_name` 3. 对于方法,采用首字母小写的驼峰命名, 如`getUserName()` 4. 普通函数采用小写加_的命名方法,如`function _exception_handler()` 4. 对于PHP常量,采用大写,如`define('BASE_PATH', '\\')` 5. 在PHP类中,如果成员是private或者protected的,则在名称前加一个 **\_** ,如`protected function _output()` 6. 文件命名,所有的类文件命名以含有的类名为准,比如类`SessionUtils`所在的PHP文件应该命名为 ***SessionUtils.php*** , 不要使用类似SessionUtils.class.php等格式的命名,自动加载机制无法加载此种命名方法。 ##第三章 快速开始 本章主要介绍如何快速开始一个Arsenals项目的开发。 为了方便快速建立一个项目,框架提供了项目构建工具,用于快速创建一个项目,该工具位于Tools目录下,使用方式如下:
php app.php -n Demo
命令具体用法参见 [杂项-快速构建项目骨架](#other-quickbuild) 部分。 - **目录结构** 建立你的项目目录,本例以Demo为例。
	Demo
	|-caches		缓存目录
	|-configs
		|-config.php  	项目基本配置
		|-database.php  数据库连接配置
		|-filter.php  	过滤器配置
		|-hook.php  	钩子配置
		|_router.php  	路由配置
	|-controllers		控制器
	|-filter		过滤器
	|-hooks			钩子目录
	|-models		模型
	|_views			视图
	
实际上,该目录结构并不是必须的,除configs目录是固定之外,其余目录都会根据namespace进行寻找,因此,可以根据程序需要自定义文件存储结构(必须与命名空间相匹配的目录结构)。 - **入口文件** 项目的入口文件 ```index.php```
	use Demo\DemoBootstrap;
	
	// 定义项目配置
	define('APP_NAME', 'Demo');
	define('BASE_PATH', realpath(dirname(__FILE__)) . DIRECTORY_SEPARATOR);
	
	// 项目入口
	require BASE_PATH . 'Demo/DemoBootstrap.php';
	$instance = new DemoBootstrap();
	$instance->startup();
	
在项目Demo下,建立项目初始化文件DemoBootstrap.php文件。
	namespace Demo;
	use Arsenals\Core\Router as Route;
	
	require BASE_PATH . 'Arsenals' . DIRECTORY_SEPARATOR . 'ArsenalsBootstrap.php';
	// 自定义的初始化文件必须继承自ArsenalsBootstrap类
	class DemoBootstrap extends \Arsenals\ArsenalsBootstrap {
		// run方法为项目提供了更加方便的配置方式,可以
		// 在此文件中自定义路由以及其它一些个性化配置等
		public function run() {
			Route::map("art", '\\Demo\\controllers\\Articles@lists');
			Route::map("articles/lists", function ($input){
				return $input->get("cat");
			});
		}
	
	}
	
- **项目配置** 配置文件并不是必须的,如果没有在项目下建立配置文件,将会采用框架内置的配置文件对系统进行配置,自定义配置可以覆盖默认配置。 - **部署运行** 完成项目配置之后就可以简单的写一个控制器,然后运行程序查看结果了。 写一个简单的测试控制器Index控制器:
	namespace Demo\controllers;
	use Arsenals\Core\Abstracts\Controller;
	
	class Index extends Controller{
		public function index(){
			return "hello,world";
		}
	}
	
##第四章 参考文档 ###常量定义
常量用途
APP_PATH项目路径
CONFIG_PATH配置文件所在路径
VIEW_PATH视图所在路径
VIEW_LAYER采用的视图实现,默认是SimpleView,值为带有命名空间的试图实现名称
MODEL_NAMESPACE模型命名空间
SERVICE_NAMESPACE服务层命名空间
CONTROLLER_NAMESPACE控制层命名空间
FILTER_NAMESPACE过滤器命名空间
###项目配置 ####基本配置config.php
配置项默认值作用
theme defaults 默认主题
site_url 站点访问地址
hook_enabled false 是否允许钩子调用
####数据库配置database.php
配置项默认值作用
data_source Arsenals\\Core\\Database\\MySQL\\MySQLiDataSource 默认采用的数据源
global array('prefix') 数据库访问全局配置
mysql array('host','port','db_name','user','password','char_set','dbcollat') mysql连接配置
pdo (暂未实现) array('dsn','user','password') PDO连接配置
####路由配置router.php 下面是默认的路由配置。
配置项默认值作用
default_controller Index 默认控制器
default_action index 默认访问的方法
route 空数组 以数组的kv形式提供路由映射,key为url(支持正则), value为控制器方法字符串标识
默认情况下访问配置文件中配置的控制其方法,一般访问方法为:控制器名称/方法名(首字母小写即可)。 如果需要自定义路由,可以在项目入口文件中定义如下:
public function run() {
	//Route::map("articles/lists", '\\Demo\\controllers\\Articles@lists');
	Route::map("articles/lists", function ($input){
		return $input->get("cat");
	});
}
如果没有自定义路由,将采用系统内置路由规则,系统对所有的控制器方法注入了$input参数,该参数为Input类的实例,方便开发。 Route::map方法支持任意数量参数,可以在第三个参数开始任意传递变量,该参数将会自动附加到回调函数或者控制器方法中,例如:
Route::map('^原创音乐$', function($media, $action){
	return "这里是原创音乐:{$media} {$action}";
}, "MP3", "play");
**小提示** : *如果方法或者回调函数 **第一个** 参数为Input类型的对象,则可以自动注入Input对象,其它参数顺序后移一位。* ###钩子 Arsenals框架提供了系统运行各个阶段的钩子函数功能,使对系统的扩展成为了可能,要使用钩子功能,需要在 ```config.php``` 配置文件中配置 ```hook_enabled``` 为 ```true```。 钩子的配置文件为config目录下的 ```hook.php``` 文件,配置规则如下:
return array(
	// before_system为挂载点
	// 挂载点挂载的功能通过数组进行配置
	'before_system' => array(
		// 执行的为DemoHook对象的beforeSystem1方法
		'Demo\\hooks\\DemoHook@beforeSystem1',
		
		// 执行的为DemoHook的静态方法beforeSystem2
		'Demo\\hooks\\DemoHook::beforeSystem2'
	)	
);
系统内置挂载点:
挂载点用途
before_system 系统初始化之前
after_system 系统初始化之后
before_controller 控制器之前
after_controller 控制器之后
###字段校验规则 Arsenals框架提供了字段校验功能,开发过程中,可以方便的使用框架提供的字段校验功能对来自用户的输入信息进行校验。 要使用字段校验功能,需要使用Input类的静态方法validate方法。
\Arsenals\Core\Input::validate($var, $type, $optionals = null)
- ```$var``` 需要检验的字段值 - ```$type``` 校验采用的规则 - ```$optionals``` 额外的参数,可以为空 ####系统内置校验规则: - 正则表达式(指定type为Input::REGEXP), 第三个参数optionals为正则表达式 - 单一规则 int, boolean, float, validate_url, validate_email, validate_ip, string, stripped, email, url, number_int, number_float, magic_quotes - 组合规则
规则用途
required 字段必须
string 字符串,可以指定长度(最小长度,最大长度)
number, int, float 数字,可以指定范围(最小值,最大值)
range 指定范围(最小值,最大值)
len 指定长度(最小长度,最大长度)
id ID校验,默认为数字
in 指定在给定范围内, 如in:1,2,3 取值只允许1,2,3之一
注意: 多个规则之间用|进行分隔。 如果需要指定规则参数,使用如下规则: 规则名:参数1,参数2 如果需要使用自定义的规则也很简单,使用Input的validateRuleRegister方法,为Input类注册自定义的校验规则。
\Arsenals\Core\Input::validateRuleRegister($rule_name, $entity_name)
```$rule_name``` 为定义的规则名称,```$entity_name``` 为规则实现(使用系统约定的对象指定方式)。 ###模板引擎 默认是采用系统内置的简单模板的,该模板使用纯PHP。 在控制器中渲染模板引擎只需要执行 ```ViewAndModel::make()``` 或者 ```VM::make()``` 方法即可获取解析之后的模板内容。 可以通过在入口文件中定义常量 **VIEW_LAWER** 修改默认采用的模板引擎,默认值为: **Arsenals\Core\Views\SimpleView** ,采用纯PHP模板。 例如: ```define('VIEW_LAYER', 'Arsenals\Core\Views\SimpleView');``` ####内置模板引擎ArsenalsTemplates 如果需要使用模板引擎,可以采用系统内置模板引擎 ```ArsenalsTemplates``` , 采用该模板引擎后,视图代码中可以使用内置标签,标签语法类似于Java EE中的JSTL语法。 使用前需要先配置使用该模板引擎,在项目入口文件中定义常量:
define('VIEW_LAYER', 'Arsenals\Core\Views\ArsenalsTemplates');
该模板引擎默认是使用 ```.html``` 作为模板文件扩展名,因此,所有的模板文件均为 *.html格式。 在模板中可以使用内置标签库语法进行模板编写。 #####标签库 - out - if - elif - else - include - while - foreach/loop - func 例如: 模板文件index.html中:
	<html>
		<head>
			<title>样例</title>
		</head>
		<body>
			<div>
				<c:out value="$value" escape="true" default="hello" >
			</div>
		</body>
	</html>
#####模板编译缓存 为了提高效率,模板文件在第一次运行时会编译成 ```.php``` 的缓存文件,以使得在程序运行过程中省略掉模板编译过程,提高运行效率。 默认编译缓存在缓存目录下的views文件夹中,并且保持了模板文件的原始文件结构以及文件名称(除扩展名之外)。 #####标签变量规范 模板引擎是基于文本解析的,因此,对标签项的配置比较严格,因此,需要按照标签变量规范进行标签内容录入。如下变量必须进行替换:
原始字符替换字符
> gt
< lt
== eq
!= neq
>= gte
<= lte
\ .(这里指的是命名空间分隔符)
例如:
	<c:func func="Demo.hello()" />
	输出为:<?php echo Demo\hello(); ?>

	<c:if test="$a gt 5">
		${var}
	</c:if>
	输出为:
		<?php if($a > 5) { ?>
		<?php echo $var; ?>
		<?php } ?>
####第三方模板引擎 系统还提供了一个基于Twig的模板引擎,不过需要手动加入Twig的类库才可以使用。加入Twig的类库之后,需要在项目的入口文件中定义Twig的Autoloader.php文件的路径常量,如下所示: ```define('TWIG_LIB', 'Twig所在目录/Autoloader.php');``` 接下来再配置VIEW_LAWER值为 **'Arsenals\Libraries\Twig\TwigView'** 即可。 ####自定义模板引擎 除了使用内置模板引擎之外,开发者可以使用自定义的模板引擎,使用自定义的模板引擎也很简单,只需要实现自定义的视图解析类,自定义视图解析类实现 **Arsenals\Core\Views\View** 接口即可。 View接口中只包含一个方法, ```parse($vm)```, $vm为```Arsenals\Core\Views\ViewAndModel```类型的对象,其中保存了要显示的视图名称以及传递给视图的数据模型。 ```parse($vm)``` 方法完成视图解析,并返回解析后的视图内容。 例如:
namespace Demo\DemoViews;
use Arsenals\Core\Views\View;

class DemoView implements View{  
	public function parse($vm){  
		// $vm->getDatas(); 传递给视图的数据数组
		// 例如: @extract($vm->getDatas());

		// $vm->getView(); 要显示的视图名称

		return "解析之后的视图字符串";
	}
}
配置常量VIEW_LAWE为 ```Demo\DemoViews\DemoView```即可。 ###日志系统 开发过程中要进行日志记录,可以通过使用内置的 ```\Arsenals\Core\Log``` 类进行日志记录,默认情况下,系统日志功能是关闭的。 如果要启用日志功能,需要在项目入口文件中定义常量 ```LOG``` 为 ```true``` 。 默认情况下,系统会使用内置的文件方式进行日志写入,你也可以通过自定义日志实现进行接管日志写入操作,系统日志实现由常量 ```LOG_IMPL``` 定义,默认值为 ```Arsenals\\Core\\Logs\\FileLogImpl``` , 采用基于文件的方式。 要写入日志,使用一下方法:
$log = Registry::load('Arsenals\\Core\\Log');
$log->debug("日志内容", '日志类型');
日志类型含有三种: info, debug, error, warning。 如果需要获取日志内容, 使用 ```getLogs(日志级别)``` 方法。 如果需要控制写入日志的级别,可以通过log.php配置文件进行配置:
return array(
	'log_levels' => array('info', 'error', 'warning', 'debug') // 允许的日志级别
);
####自定义日志实现 要使用自定义的日志实现,需要实现 ```Arsenals\Core\Logs``` 接口, 该接口含有两个方法:
public function write($level, $message);
public function getLogs($level = null);
然后再入口文件中定义日志实现就可以了。
define('LOG_IMPL', '你的日志实现类(包含命名空间)');
###杂项

快速构建项目骨架

通过使用Tools下的快速构建工具可以快速的构建一个基本项目的骨架,构建命令如下:
	php app.php -n 项目名称 [额外参数]
可选参数如下:
参数功能默认值
author开发者姓名操作系统用户
db_prefix数据库表前缀项目名称小写_
db_dbname数据库名称db_项目名称小写
db_user数据库用户名root
db_password数据库密码
db_host数据库地址localhost
db_port数据库端口号3306
view_theme默认视图主题default
model模型目录models
controller控制器目录controllers
cache缓存目录caches
filterFilter目录filters
例如: 需要构建一个示例程序Demo。
D:\xampp\htdocs>php init.php -n CMS --db_prefix demo_ --author developer --db_
dbname test
上面的命令构建了一个项目,名称为CMS, 数据表前缀为demo_, 开发者为developer, 数据库名称为test, 其它配置项使用的是默认值。 ####异常处理/错误处理 可以在项目入口文件index.php中通过定义常量 ```ERROR_HANDLER``` 和 ```EXCEPTION_HANDLER``` 使用自定义的异常处理。 例如:
define('ERROR_HANDLER', 'Arsenals\\Core\\_error_handler');
define('EXCEPTION_HANDLER', 'Arsenals\\Core\\_exception_handler');

function _error_handler($errno, $errstr, $errfile, $errline){
	_D("文件{$errfile}的第{$errline}行有一个错误,错误代码为{$errno}, 错误描述:{$errstr}");
}

function _exception_handler(\Exception $exception){
	_D("文件{$exception->getFile()}的第{$exception->getLine()}行抛出异常, 错误代码为 {$exception->getCode()}, 错误描述 :{$exception->getMessage()}");
}
####内置异常 框架实现中包含了一些可能会常用到的异常,在开发过程中可以直接使用内置异常类。 系统内置异常统一在命名空间 ```Arsenals\Core\Exceptions``` 下,并且都继承了 ```Arsenals\Core\Exceptions\ArsenalsException``` 异常基类。
内置异常类用途
AccessDeniedException 拒绝访问异常,一般用于没有权限访问
ClassNotFoundException 找不到类
ClassTypeException 类不是合法的类型
FormInvalidException 表单异常,如表单校验异常
FuncParamException 函数,方法调用参数异常
NoRecoredException 没有查询到指定基类异常
PageNotFoundException 找不到要访问的页面异常
QueryException 查询异常
RedefineException 重复定义异常
TypeErrorException 错误的类型
###扩展类库 ####图形验证码 扩展库中包含简单验证码实现,调用方式非常简单:
$captcha = Registry::load('\Arsenals\Libraries\Images\Captcha');
$code = $captcha->generageCode();
$captcha->createImage($code);
注意的是,```createImage``` 方法可以接收两个参数,第一个参数为验证码字符串,第二个参数为可选,如果为NULL,则验证码直接输出到浏览器,或者可以为要保存验证码的文件名。 ####文件上传 框架扩展库中集成了一个简易的文件上传类,该类位于 ```\Arsenals\Libraries\Files\Uploader.php```。 使用该类可以简单的实现文件上传操作,使用示例如下:
$uploader = \Arsenals\Core\Registry::load('\Arsenals\Libraries\Files\Uploader');
$up_status = $uploader->upload(上传字段, 目标文件名);
需要注意的是,```upload``` 方法再上传完成后的返回值up_status在上传成功时为文件的访问地址。 ####图像处理 框架扩展库中内置了一个简易的图像处理工具类```\Arsenals\Libraries\Image\ImageUtils```, 使用该工具类可以轻松的完成对图片的缩放,创建缩略图,添加水印等。 要创建一个图片的缩略图,使用如下方法:
use \Arsenals\Libraries\Image\ImageUtils;
ImageUtils::thumb(原文件名,最大宽度, 最大高度[, 目标文件名[,压缩率=80]])
注意的是,目标文件名如果为空的话,则会将处理后的图片文件内容发送到浏览器(直接输出)。 如果要添加水印,则使用如下方法:
use \Arsenals\Libraries\Images\ImageUtils;
ImageUtils::watermark(array(
	'source_file' => $filename, 
	'watermark'=> 'test.gif', 
	'pos_x'=>-40, 
	'pos_y'=>-40
));
可选属性为如下: - source_file 源文件 - watermark 水印图片文件 - pos_x 水印左边距(负数为右边距) - pos_y 水印上边距(负数为下边距) - dest_file 存储为目标文件,默认不存储,直接输出 - alpha 水印透明度,默认75 ####SAE(新浪云计算平台)支持 要启用SAE支持,可以通过钩子挂载系统启动前期,加入SAE支持库实现。 首先,需要在项目配置文件 ```config.php``` 中启用钩子, ```hook_enabled``` 为 ```true``` ; 其次,在hook.php配置文件中配置系统运行前钩子实现:
'before_system' => array( 
		'Demo\\hooks\\DemoHook@beforeSystem'
)
在DemoHook类中,定义载入SAE支持类库:
public function beforeSystem(){
	$sae = new \Arsenals\Libraries\Sae\SaeInit();
	$sae->init();
}
这样,就启用了对SAE的支持了,程序开发过程中,可以通过使用常量 ```IS_SAE``` 对是否是SAE环境进行判断。 为了实现SAE与传统环境之间的兼容,开发过程中注意不要使用本地文件系统操作函数,而使用系统内置的函数代替:
\Arsenals\Core\file_put_contents
\Arsenals\Core\file_get_contents
\Arsenals\Core\file_exists
\Arsenals\Core\opendir
\Arsenals\Core\readdir
\Arsenals\Core\unlink
\Arsenals\Core\closedir
\Arsenals\Core\is_file
\Arsenals\Core\move_uploaded_file