mymvc
项目介绍
基于composer 搭建自己的mvc练习,本人是个小白。参考easyswoole(传送门) 和thinkphp5(传送门)通过composer来练习搭建自己的mvc框架,没有考虑性能,都是凭感觉弄得。有什么建议可以发送给邮箱327907762@qq.com
命名规范
类库、函数文件统一以.php为后缀;
类的文件名均以命名空间定义,并且命名空间的路径和类库文件所在路径一致;
类名和类文件名保持一致,统一采用驼峰法命名(首字母大写);
1、入口文件
用户发起的请求都会经过应用的入口文件,通常是 public/index.php文件。当然,你也可以更改或者增加新的入口文件。
通常入口文件的代码都比较简单,一个普通的入口文件代码如下:
// 应用入口文件
define(__ROOT__, __DIR__ . '/');
define(__CONFIG__, __DIR__ . '/Data/Config/');
define(__EXECL_READER__, __ROOT__ . "/App/System/Component/Execl/Reader/");
require_once 'vendor/autoload.php';
use App\System\App;
App::run()->send();
一般入口文件以定义一些常量为主;
2、引导文件
self::init (); // 初始化加载配置
self::registerWhoops ();// 注册whoops来捕捉框架的异常
self::checkPhpVersion ();// 检测php版本
self::initCommon ();// 初始化公共信息
self::registerDi ();// 注册Di的依赖
self::registerRoute ();// 注册路由 // 清空实例化的
路由
通过fastRoute开发的路由,并且支持fastRoute所有方式,并且项目支持无限路由。该类允许您直接将其投入项目并立即开始使用。模块/控制器/操作这是默认的分发请求机制,系统会根据URL或者路由地址来判断当前请求的模块、控制器和操作名,并自动调用相应的访问控制器类,执行操作对应的方法。
简单的路由重写
class Router extends RouterInterface
{
function register(RouteCollector $routeCollector)
{
// TODO: Implement register() method.
// 路由拦截若未执行end,请求还会继续进入系统自带的控制器路由匹配
$routeCollector->get('/register', '/Api/Common/register');
// /loginIndex/1.html
$routeCollector->get('/loginIndex/{id:\d+}', '/Api/Login/index');
// /loginIndex/1/jll.html
$routeCollector->get('/loginIndex/{id:\d+}/{name:\d+}', '/Api/Login/index');
$routeCollector->get('/checkIsLogin', '/Api/Common/checkIsLogin');
$routeCollector->get('/getFriendRequests', '/Api/Friend/getFriendRequests');
$routeCollector->get('/dealFriendRequest', '/Api/Friend/dealFriendRequest');
$routeCollector->get('/getFriendList', '/Api/Friend/getFriendList');
// 测试URL /a
$routeCollector->get('/a', function (Request $request, Response $response) {
$response->write('this is write by router with not end ');
$response->response();
});
// 测试URL /a2
$routeCollector->get('/a2', function (Request $request, Response $response) {
$response->write('this is write by router2 with end ');
$response->response();
});
$routeCollector->post('/a3', function (Request $request, Response $response) {
$response->write('this is write by router2 with end ');
$response->response();
});
// /user/1/index.html
$routeCollector->get('/user/{id:\d+}', function (Request $request, Response $response, $id) {
$response->write("this is router user ,your id is {$id}");
$response->response();
});
}
public function getMethodNotAllowCallBack()
{
/*
* //需要处理逻用回调函数
* return function () {
* echo 111;
* };
*/
return null;
}
}
错误机制
错误捕捉通过Whoops插件
Whoops 是一个PHP库能够很容易处理错误和进行代码调试。该库提供了基于堆栈的错误处理,并提供一个好看的错误界面
数据库
MysqliDb -- Simple MySQLi wrapper and object mapper with prepared statements
基于easyswoole的spl库可以像java一样封装自己的bean
namespace App\Model\Setting;
use App\System\Component\Spl\SplBean;
class SettingBean extends SplBean
{
protected $key;
protected $value;
/**
* @return the $key
*/
public function getKey()
{
return $this->key;
}
/**
* @return the $value
*/
public function getValue()
{
return $this->value;
}
/**
* @param field_type $key
*/
public function setKey($key)
{
$this->key = $key;
}
/**
* @param field_type $value
*/
public function setValue($value)
{
$this->value = $value;
}
}
模板解析
Twig是一个灵活、高效并且安全的PHP模板引擎。
如果你使用过Smarty、Django或者Jinja这类基于文本的模板引擎的话,那么你会觉得Twig是很自然而然的事情。Twig严格遵守了PHP的信念,同时增加了在模板环境中很有用的函数,这些做法使得Twig不论是对设计师还是开发人员,都是非常友好的。
Twig的主要特征有:
高效:Twig将模板编译成了优化了的PHP文件,与原生的PHP代码比较而言,性能损耗非常小。
安全:Twig使用沙箱(sandbox)模式去运行模板中不被信任的代码。这使得我们可以选择Twig作为那些允许用户修改模板的应用的模板引擎。
灵活:Twig具有灵活的语法分析器和语法解析器,它允许开发人员定义自己的标签(tags)和过滤器(filters),并且创建自己的领域特定语言(DSL,domain specific language)。
模仿easyswoole遵循psr7的Response和Request类,在路由的之前通过DI容器注入,DI容器采用easyswoole的代码很好用。
依赖倒置原则(DIP):一种软件架构设计的原则(抽象概念)。
控制反转(IoC):一种反转流、依赖和接口的方式(DIP的具体实现方式)。
依赖注入(DI):IoC的一种实现方式,用来反转依赖(IoC的具体实现方式)。
IoC容器:依赖注入的框架,用来映射依赖,管理对象创建和生存周期(DI框架)。
static function registerDi() {
$config = self::$config;
// 通过DI 注入数据库$db = Di::getInstance()->get('mysql');
Di::getInstance ()->set ( 'mysql', MysqliDb::class, Array (
'host' => $config ['database'] ['host'],
'username' => $config ['database'] ['username'],
'password' => $config ['database'] ['password'],
'db' => $config ['database'] ['db'],
'port' => $config ['database'] ['port'],
'charset' => $config ['database'] ['charset'],
'prefix' => $config ['database'] ['prefix']
) );
// 通过DI 注入数据库视图 $twig = Di::getInstance()->get('twig');
Di::getInstance ()->set ( 'twig', TwigView::class, Array (
'config' => $config ['view']
) );
}
很感谢easyswoole的博主
通过他的easyswoolemvc让我更好的理解和规范的写出良好的的代码。
这是mvc的控制层的抽象层的实现。所有的controller实现类都写在如下图
namespace App\System\Http\AbstractInterface;
use App\System\Http\Message\Status;
use App\System\Http\Request;
use App\System\Http\Response;
use App\System\Component\View\TwigView;
use App\System\Component\Di;
abstract class Controller
{
private $actionName;
private $request;
private $response;
abstract function index();
public function __construct(string $actionName, Request $request, Response $response)
{
$this->request = $request;
$this->response = $response;
$this->actionName = $actionName;
if ($actionName == '__hook') {
$this->response()->withStatus(Status::CODE_BAD_REQUEST);
} else {
$this->__hook($actionName);
}
}
protected function actionNotFound($action): void
{
$this->response()->withStatus(Status::CODE_NOT_FOUND);
}
protected function afterAction($actionName): void
{}
protected function onException(\Throwable $throwable, $actionName): void
{
throw $throwable;
}
protected function onRequest($action): ?bool
{
return true;
}
protected function getActionName(): string
{
return $this->actionName;
}
protected function resetAction(string $action): void
{
$this->actionName = $action;
}
protected function __hook(?string $actionName): void
{
if ($this->onRequest($actionName) !== false) {
$actionName = $this->actionName;
// 支持在子类控制器中以private,protected来修饰某个方法不可见
$ref = new \ReflectionClass(static::class);
if ($ref->hasMethod($actionName) && $ref->getMethod($actionName)->isPublic()) {
try {
$this->$actionName(); // 这里面会重复执行
$this->afterAction($actionName);
} catch (\Throwable $exception) {
$this->onException($exception, $actionName);
}
} else {
throw new \Exception(static::class . "中没有找到" . $actionName);
}
} else {
throw new \Exception("没有权限访问" . $actionName);
}
}
protected function request(): Request
{
return $this->request;
}
protected function response(): Response
{
return $this->response;
}
protected function writeJson($statusCode = 200, $result = null, $msg = null)
{
$data = Array(
"code" => $statusCode,
"result" => $result,
"msg" => $msg
);
$this->response()->write(json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
$this->response()->withHeader('Content-type', 'application/json;charset=utf-8');
$this->response()->withStatus($statusCode);
}
protected function twig(): TwigView
{
return Di::getInstance()->get('twig');
}
/**
* * 浏览器友好的变量输出 * @param mixed $var 变量 * @param boolean $echo 是否输出 默认为True 如果为false 则返回输出字符串 * @param string $label 标签 默认为空 * @param boolean $strict 是否严谨 默认为true * @return void|string
*/
/**
* 浏览器友好的变量输出
*
* @param mixed $var
* 变量
* @param boolean $echo
* 是否输出 默认为True 如果为false 则返回输出字符串
* @param string $label
* 标签 默认为空
* @param boolean $strict
* 是否严谨 默认为true
* @return void|string
*/
protected function getNameSpace()
{
return self::class;
}
function __destruct()
{
$this->response->response();
}
}
控制器层的代码实现
namespace App\HttpController\Index;
use App\System\Http\AbstractInterface\Controller;
use App\Model\Setting\SettingModel;
class Index extends Controller {
public function index() {
$this->response()->withHeader('Content-type', 'text/html; charset=gbk;');
$this->response()->withHeader('X-Powered-By', 'PHP/6.0.0');
echo "
";
var_dump(1111);
echo "
";$this->twig ()->assign ( "aaaa", "111" );
$this->twig ()->assign ( "navigation", [
"aaaaa",
"bbbbb",
"ccccc"
] );
$this->twig ()->assign ( "navigation2", (new SettingModel ())->getSettings () );
$re = $this->twig ()->getTempLate ( "index" );
$this->response ()->write ( $re );
}
protected function onRequest($action): ?bool {
return true;
}
protected function afterAction($actionName): void {
}
}
模拟POST与GET请求 httpcurl 支持文件上传等
HttpCurl::request('http://example.com/', 'post', array(
'user_uid' => 'root',
'user_pwd' => '123456'
));
高效的execl操作比传统的phpexecl要写,虽然没有phpexecl灵活。但是满足基本的execl操作
剩下会更加丰富自己的mvc框架,在逐渐考虑性能。