前两天得到1份 QEEPHP v3的测试版本... 新功能确实很好,而且变小了很多,虽然离正式版的出现还有些距离,但是已经能看出
很多的改变,比如 事件机制,实体操作层的改变,MVC的处理改变 等等....
此处 因为之前的 CoreApp-mini 已经写了很多,其里面并没有对模块实现进行限制,可以使用多种方式来实现...
之前 缺少MVC的完整实现,此处正好把QEE 的MVC模式引过来....
因为 v3里面C处 没有 模块/命名空间/控制器/动作 这么一说 只有 action 但是 action 是可以存在 以 . 分隔的 多级
所以 现有的qee2.1的 url 路由 可以 修剪下 就能 放到 qee v3版本里面的MVC 中,
将 UDI 转成 .分隔的actionName 应该很好做到
同理 actionName 到UDI应该也好实现
主要就是在 这2个的转换上面
同理 actionName 到UDI应该也好实现
主要就是在 这2个的转换上面
因为 CoreApp-mini 里面已经有了个正向的url路由,所以也就没有引入qee的路由...
CoreApp-mini 的定位就是 简单,够用, 小系统的专用框架,大的复杂的项目可以使用 QEEPHP来实现
先上图:

再上代码:
<?php
/*
* 简易MVC组件[改自Qee v3 beta]
* -- 带特定的目录结构
* -- 不再有控制器这个物件
* -- by sese coreapp-mini
*/
/**
* 视图对象
*/
class MvcView
{
/**
* 视图文件所在目录
*
* @var string
*/
public $view_dir;
/**
* 视图默认使用的布局
*
* @var string
*/
public $view_layout;
/**
* 默认使用的视图
*
* @var string
*/
public $viewname;
/**
* 视图变量
*
* @var array
*/
public $vars;
/**
* 构造函数
*
* @param string $view_dir
* @param string $viewname
* @param array $vars
*/
function __construct($view_dir, $viewname, array $vars)
{
$this->view_dir = $view_dir;
$this->vars = $vars;
$this->vars['_BASE_DIR'] = get_request_dir();
$this->viewname = $viewname;
}
/**
* 渲染一个视图文件,返回结果
*
* @return string
*/
function execute()
{
$viewname = $this->viewname;
$child = new MvcViewLayer($this, $viewname);
$error_reporting = ini_get('error_reporting');
error_reporting($error_reporting & ~E_NOTICE);
$child->parse();
$layer = $child;
while (($parent = $layer->parent) != null)
{
$parent->parse($layer->blocks);
$layer = $parent;
}
error_reporting($error_reporting);
return $child->root()->contents;
}
/**
* 查找指定视图文件
*
* @param string $viewname
*
* @return string
*/
function view_filename($viewname)
{
$filename = str_replace('.', DIRECTORY_SEPARATOR, $viewname) . '.php';
return $this->view_dir . DIRECTORY_SEPARATOR . $filename;
}
}
/**
* 视图层
*/
class MvcViewLayer
{
/**
* 该层所属的视图对象
*
* @var MvcView
*/
public $view;
/**
* 父层对象
*
* @var MvcViewLayer
*/
public $parent;
/**
* 视图名称
*
* @var string
*/
public $viewname;
/**
* 该层的内容
*
* @var string
*/
public $contents;
/**
* 该层区块的内容
*
* @var array
*/
public $blocks = array();
/**
* 该层的区块
*
* @var array
*/
private $_block_stack = array();
/**
* 预定义的区块
*
* @var array
*/
private $_predefined_blocks = array();
/**
* 构造函数
*
* @param MvcView $view
* @param string $viewname
*/
function __construct(MvcView $view, $viewname)
{
$this->view = $view;
$this->viewname = $viewname;
}
/**
* 返回该层的顶级层(最底层的视图)
*
* @return MvcViewLayer
*/
function root()
{
return ($this->parent) ? $this->parent->root() : $this;
}
/**
* 分析视图,并返回结果
*
* @param array $predefined_blocks
*/
function parse(array $predefined_blocks = array())
{
$this->_predefined_blocks = $predefined_blocks;
ob_start();
extract($this->view->vars);
include $this->view->view_filename($this->viewname);
$this->contents = ob_get_clean();
$this->_predefined_blocks = null;
foreach ($this->blocks as $block_name => $contents)
{
$search = "%_view_block.{$block_name}_%";
if (strpos($this->contents, $search) !== false)
{
$this->contents = str_replace($search, $contents, $this->contents);
}
}
}
/**
* 从指定层继承
*
* @param string $viewname
*/
function extend($viewname)
{
$this->parent = new MvcViewLayer($this->view, $viewname);
}
/**
* 定义一个区块
*
* @param string $block_name
* @param boolean $append
*/
function block($block_name, $append = false)
{
array_push($this->_block_stack, array($block_name, $append));
ob_start();
}
/**
* 结束最后定义的一个区块
*/
function endblock()
{
list($block_name, $append) = array_pop($this->_block_stack);
$contents = ob_get_clean();
$this->_create_block($contents, $block_name, $append);
}
/**
* 定义一个空区块
*
* @param string $block_name
* @param boolean $append
*/
function empty_block($block_name, $append = false)
{
$this->_create_block('', $block_name, $append);
}
/**
* 载入一个视图片段
*
* @param string $viewname 视图片段名
*/
function element($viewname)
{
$__filename = $this->view->view_filename("_elements/{$viewname}");
extract($this->view->vars);
include $__filename;
}
/**
* 完成一个区块
*
* @param string $contents
* @param string $block_name
* @param boolean $append
*/
private function _create_block($contents, $block_name, $append)
{
if (isset($this->_predefined_blocks[$block_name]))
{
if ($append)
{
$contents .= $this->_predefined_blocks[$block_name];
}
else
{
$contents = $this->_predefined_blocks[$block_name];
}
}
$this->blocks[$block_name] = $contents;
echo "%_view_block.{$block_name}_%";
}
}
/**
* MvcAction没有找到
*
*/
class MvcActionMissingException extends Exception {
/**
* 动作名
*
* @var string
*/
public $actionName;
/**
* 构造函数
*
* @param string $actionName
*/
function __construct($actionName){
$this->actionName = $actionName;
parent::__construct(sprintf('缺少Action "%s".',$actionName));
}
}
/**
* 动作对象基础类
*/
abstract class MvcBaseAction
{
/**
* 应用程序对象
*
* @var MvcAppEntry
*/
public $app;
/**
* 动作名称
*
* @var string
*/
public $name;
/**
* 当前请求
*
* @var MvcRequest
*/
public $request;
/**
* 执行结果
*
* @var mixed
*/
public $result;
/**
* 构造函数
*
* @param MvcAppEntry $app
* @param string $name
*/
function __construct($app, $name)
{
$this->app = $app;
$this->name = $name;
$this->request = $app->request;
}
/**
* UDI 转 MVC Action 标识
*/
static function formatUDIAction(array $udi){
return sprintf('%s.%s.%s',$udi[Router::module],$udi[Router::operate],$udi[Router::action]);
}
/**
* 执行动作
*/
function __execute()
{
if (!$this->__before_execute()) return;
if ($this->validate_input())
{
$result = $this->execute();
if (!is_null($result)) $this->result = $result;
if (!$this->validate_output())
{
$this->on_validate_output_failed();
}
}
else
{
$this->on_validate_input_failed();
}
$this->__after_execute();
}
/**
* 执行指定的视图对象
*
* @param array $vars
*/
function view(array $vars = null)
{
if (!is_array($vars)) $vars = array();
$this->result = $this->app->view($this->name, $vars);
}
/**
* 应用程序执行的动作内容,在继承的动作对象中必须实现此方法
*
* 返回值会被保存到动作对象的 $result 属性中。
*
* @return mixed
*/
abstract function execute();
/**
* 继承类覆盖此方法,用于在执行请求前过滤并验证输入数据
*
* 如果返回 false 则阻止调用 execute() 方法,并调用 validate_input_failed() 方法。
*
* @return bool
*/
function validate_input()
{
return true;
}
/**
* 继承类覆盖此方法,用于在执行请求后过滤并验证输出数据
*
* 如果返回 false 则调用 validate_output_failed() 方法。
*
* @return bool
*/
function validate_output()
{
return true;
}
/**
* 请求前对数据进行验证失败时调用此方法
*/
function on_validate_input_failed()
{
}
/**
* 请求执行后对数据进行验证失败时调用此方法
*/
function on_validate_output_failed()
{
}
/**
* 执行动作之前调用,如果返回 false 则阻止动作的执行
*
* @return bool
*/
protected function __before_execute()
{
return true;
}
/**
* 执行动作之后调用
*/
protected function __after_execute()
{
}
}
/**
* 封装一个请求
*/
class MvcRequest
{
/**
* GET 数据
*
* @var array
*/
public $get;
/**
* POST 数据
*
* @var array
*/
public $post;
/**
* COOKIE 数据
*
* @var array
*/
public $cookie;
/**
* SESSION 数据
*
* @var array
*/
public $session;
function __construct($get, $post, $cookie, $session)
{
$this->get = $get;
$this->post = $post;
$this->cookie = $cookie;
$this->session = $session;
}
/**
* 从 GET 取得数据,如果指定数据不存在则返回 $default 指定的默认值
*
* @param string $name
* @param mixed $default
*
* @return mixed
*/
function get($name, $default = null)
{
return isset($this->get[$name]) ? $this->get[$name] : $default;
}
/**
* 从 POST 取得数据,如果指定数据不存在则返回 $default 指定的默认值
*
* @param string $name
* @param mixed $default
*
* @return mixed
*/
function post($name, $default = null)
{
return isset($this->post[$name]) ? $this->post[$name] : $default;
}
/**
* 从 COOKIE 取得数据,如果指定数据不存在则返回 $default 指定的默认值
*
* @param string $name
* @param mixed $default
*
* @return mixed
*/
function cookie($name, $default = null)
{
return isset($this->cookie[$name]) ? $this->cookie[$name] : $default;
}
/**
* 从 SESSION 取得数据,如果指定数据不存在则返回 $default 指定的默认值
*
* @param string $name
* @param mixed $default
*
* @return mixed
*/
function session($name, $default = null)
{
return isset($this->session[$name]) ? $this->session[$name] : $default;
}
}
/**
* 用与保存和读取应用程序设置的工具类
*/
class MvcConfig
{
/**
* 应用程序设置
*
* @var array
*/
protected $_config = array();
/**
* 导入设置
*
* @param array $config
*/
function import(array $config)
{
$this->_config = array_merge($this->_config, $config);
}
/**
* 读取指定的设置,如果不存在则返回$default参数指定的默认值
*
* @param string $item
* @param mixed $default
* @param bool $found
*
* @return mixed
*/
function get($item, $default = null, & $found = false)
{
if (is_array($item))
{
$found = false;
foreach ($item as $key)
{
$return = $this->get($key, $default, $found);
if ($found) return $return;
}
return $default;
}
if (strpos($item, '/') === false)
{
$found = array_key_exists($item, $this->_config);
return $found ? $this->_config[$item] : $default;
}
list($keys, $last) = self::_get_nested_keys($item);
$config =& $this->_config;
foreach ($keys as $key)
{
if (array_key_exists($key, $config))
{
$config =& $config[$key];
}
else
{
return $default;
}
}
$found = array_key_exists($last, $config);
return $found ? $config[$last] : $default;
}
/**
* 修改指定的设置
*
* @param string $item
* @param mixed $value
*/
function set($item, $value)
{
if (strpos($item, '/') === false)
{
$this->_config[$item] = $value;
}
list($keys, $last) = self::_get_nested_keys($item);
$config =& $this->_config;
foreach ($keys as $key)
{
if (!array_key_exists($key, $config))
{
$config[$key] = array();
}
$config =& $config[$key];
}
$config[$last] = $value;
}
static private function _get_nested_keys($key)
{
$keys = normalize($key, '/');
$last = array_pop($keys);
return array($keys, $last);
}
}
/**
* MvcAppEntry 类封装了一个基本的应用程序对象
*
* 如果需要定制应用程序对象,开发者可以从 MvcAppEntry 派生自己的继承类。
* 在coreapp-mini 中 每个 MvcAppEntry 对应一个内属的模块
*/
class MvcAppEntry
{
/**
* 当前请求
*
* @var MvcRequest
*/
public $request;
/**
* 当前MvcAppEntry 对应的设置对象
*
* @var MvcConfig
*/
public $config;
/**
* 应用程序基本路径
*
* @var string
*/
private $_base_path;
/**
* 应用模块的标识
*
* @var string
*/
private $_app_id;
/**
* 工具对象集合
*
* @var array
*/
private $_tools_instance = array();
/**
* 应用程序实例对象集合
*
* @var array
*/
private static $_instances = array();
/**
* 构造函数
*
* 参数 $config 包含应用模块的设置,必须包含如下4个键
*
* app.id: 应用模块的标识,如 pwadmin
* app.base_path: 应用模块相对于根目录的基准路径,如 /modules/pwadmin
* app.defentry: 应用模块的初始执行入口
* app.creator: 应用模块的创建者,如 色色
*
* @param array $config
* @param bool $set_instance 是否增加到实例对象列表,是则可以通过 instance(app_id)返回MvcAppEntry对象
*/
function __construct(array $config, $set_instance = true)
{
$must_opt = array(
'app.id',
'app.base_path',
'app.defentry',
'app.creator',
);
foreach ($must_opt as $opt){
if (isset($config[$opt]) && !empty($config[$opt])) continue;
throw new Exception(sprintf('需要的键 "%s" 没有找到.', $opt));
}
$this->config = new MvcConfig();
$this->config->import($config);
$this->_app_id = $this->config->get('app.id');
$this->_base_path = rtrim($this->config->get('app.base_path'), '/\\');
$autoload_tools = $this->config->get('app.autoload_tools',null);
if ($autoload_tools){
$autoload_tools = normalize($autoload_tools);
foreach ($autoload_tools as $name)
{
$tool = $this->tool($name);
if (method_exists($tool, 'autorun')) $tool->autorun();
}
}
$this->request = new MvcRequest($_GET,
$_POST,
isset($_COOKIE) ? $_COOKIE : array(),
isset($_SESSION) ? $_SESSION : array());
if ($set_instance) {
self::$_instances[strtolower(trim($this->_app_id))] = $this;
}
}
/**
* 取得应用程序实例
*
* @param string $app_id
*
* @return MvcAppEntry
*/
static function instance($app_id)
{
if (!empty($app_id)){
$app_id = strtolower(trim($this->_app_id));
if ( isset(self::$_instances[$app_id]) && (self::$_instances[$app_id] instanceof MvcAppEntry) )
return self::$_instances[$app_id];
}
return null;
}
/**
* 返回应用程序根目录
*
* @return string
*/
function base_path()
{
return $this->_base_ath;
}
/**
* 返回应用模块的标识
*
* @return string
*/
function app_id()
{
return $this->_app_id;
}
/**
* 执行应用程序
*
* @param string $action_name 这个应该是 将 udi 转换之后的名称
*
* @return mixed
*/
function run($action_name = null)
{
// 解析请求 URL 中的动作名称
if (is_null($action_name))
{
$action_name = $this->config->get('app.defentry');
}
$action_name = self::_format_action_name($action_name);
// 动作对象
$action_class_name = str_replace('.', '_', $action_name) . 'Action';
$action_class_file = $this->_base_path . '/actions/' . str_replace('.', '/', $action_name) . '.php';
CoreApp::import($action_class_file,true);
if (!class_exists($action_class_name,false))
{
return $this->_process_result($this->_on_action_not_found($action_class_name));
}
// 执行动作
$action = new $action_class_name($this, $action_name);
/* @var $action BaseAction */
$action->__execute();
return $this->_process_result($action->result);
}
/**
* 生成 URL
*
* @param string $url
* @param array $params
*
* @return string
*/
function url($url, array $params = null)
{
return url($url,$params);
}
/**
* 取得指定的视图对象
*
* @param string $viewname
* @param array $vars
*
* @return View
*/
function view($viewname, array $vars)
{
return new View($this->_base_ath . '/views', $viewname, $vars);
}
/**
* 根据 tools 设定创建并返回指定的工具对象
*
* @param string $toolname
*
* @return object
*/
function tool($toolname)
{
if (!isset($this->_tools_instance[$toolname]))
{
$tool_config = $this->config->get("app.tools/{$toolname}");
if (is_array($tool_config))
{
$class = $tool_config['class'];
$class_file = $tool_config['file'];
}
else
{
if (is_string($tool_config) && !empty($tool_config))
{
$class = $tool_config;
}
else
{
$class = ucfirst($toolname) . 'Tool';
}
$file = $this->_base_path . '/tools/' . $class . '.php';
if (!is_array($tool_config)) $tool_config = array();
}
CoreApp::import($file,true);
$this->_tools_instance[$toolname] = new $class($this, $tool_config);
}
return $this->_tools_instance[$toolname];
}
/**
* 确定指定的工具对象是否存在
*
* @param string $toolName
*
* @return bool
*/
function has_tool($toolName)
{
return isset($this->_tools[$toolName]);
}
/**
* 处理动作对象的执行结果
*
* @param mixed $result
*/
protected function _process_result($result)
{
$charset = $this->config->get('app.output_charset', 'utf-8');
if (is_object($result) && method_exists($result, 'execute'))
{
if (!headers_sent())
{
header('X-Powered-By-CoreAppMini: ' . CoreApp::VER);
header("Content-Type: text/html; charset={$charset}");
}
return $result->execute();
}
elseif (is_string($result))
{
if (!headers_sent())
{
header('X-Powered-By-CoreAppMini: ' . CoreApp::VER);
header("Content-Type: text/html; charset={$charset}");
}
return $result;
}
else
{
return $result;
}
}
/**
* 指定的控制器或动作没有找到
*
* @param string $action_name
*/
protected function _on_action_not_found($action_name)
{
throw new MvcActionMissingException($action_name);
}
/**
* 格式化动作名称
*
* @param string $action_name
*
* @return string
*/
protected static function _format_action_name($action_name)
{
$action_name = strtolower($action_name);
if (strpos($action_name, '.') !== false)
{
$action_name = preg_replace('/\.+/', '.', $action_name);
}
$action_name = trim($action_name, ". \t\r\n\0\x0B");
return preg_replace('/[^a-z\.]/', '', $action_name);
}
}
确实可以使用 哈哈哈 修改了下一个方法




