# engine **Repository Path**: zhnaglei/engine ## Basic Information - **Project Name**: engine - **Description**: 一个简易框架,用来理解现在市场上主流框架。 - **Primary Language**: PHP - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2018-04-26 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # engine #### 项目介绍 一个简易框架,用来理解现在市场上主流框架。 # @使用composer构建自己的框架 ## 1. 基础准备 1. 创建文件夹(为你的框架起一个牛逼的名称) 2. 在文件下创建composer.json文件 ~~~ { "require": { } } ~~~ 3.切换到创建文件下执行 ~~~ composer update ~~~ >执行到此,恭喜你,你的框架初始化已经完成。你已经迈出成功的一步。 ## 2. 构建路由 1. 我们使用macaw构建我们的路由 ~~~ 下载地址:https://github.com/NoahBuscher/Macaw 推荐使用composer安装 文档地址:https://github.com/NoahBuscher/Macaw ~~~ 2.将composer.json文件重构为: ~~~ { "require": { "noahbuscher/macaw": "dev-master" } } ~~~ 3. 新建 ./public 文件夹,这个文件夹将是用户唯一可见的部分,在文件中创建index.php文件 ~~~ '.$fu; }); Macaw::dispatch(); ~~~ >此处如果你可以看见Hello world!就表示成功了! ## 3. 设计MVC 1.规划文件夹 新建./app 文件夹,在 app 中创建 controllers、models、views 三个文件夹。 2.在controllers文件夹下创建 BaseController.php ~~~ class BaseController { public function __construct (){ } } ~~~ HomeController.php ~~~ class HomeController extends BaseController { public function home(){ echo 'hello world'; } } ~~~ 在路由配置处,新增一条路由。 ~~~ Macaw::get('/home', 'HomeController@home'); ~~~ 访问/home,将会出现 ~~~ Fatal error: Uncaught Error: Class 'HomeController' not found in D:\webroot\engine\vendor\noahbuscher\macaw\Macaw.php:94 ~~~ 报错没有找到HomeController,是因为我们没有让其自动加载。修改conpose.json文件 ~~~ { "require": { "noahbuscher/macaw": "dev-master" }, "autoload":{ "classmap": [ "app/controllers", "app/models" ] } } ~~~ 修改完成之后执行composer dump-autoload。 执行完再次访问,如果看见hello world你就有成功了一步。 2.新建 models/Article.php 文件 ~~~ class Article { public static function first() { $connection = mysql_connect("localhost","root","root"); if (!$connection) { die('Could not connect: ' . mysql_error()); } mysql_set_charset("UTF8", $connection); mysql_select_db("engine", $connection); $result = mysql_query("SELECT * FROM article limit 0,1"); if ($row = mysql_fetch_array($result)) { return $row; } mysql_close($connection); } } ~~~ 访问会报错,再次执行composer dump-autoload再次访问就可以看见你数据库里面么的结果集。 >需要在过程中创建数据库,创建表。 >PHP框架归根结底还是需要交于PHP去执行解析。 >一个 URL 驱动框架做的事情基本是这样的:入口文件 require 控制器,控制器 require 模型,模型和数据库交互得到数据返回给控制器,控制器再 require 视图,把数据填充进视图,返回给访客,流程结束。 ## 4. 使用ORM 1.数据库操作我们使用 illuminate/database 在composer.json文件中增加 illuminate/database ~~~ { "require": { "noahbuscher/macaw": "dev-master", "illuminate/database": "*" }, "autoload": { "classmap": [ "app/controllers", "app/models" ] } } ~~~ >增加之后执行composer update 2.在config目录下创建database.php ~~~ return [ //数据库驱动类型 'driver' => 'mysql', //数据库地址 'host' => 'localhost', //数据库 'database' => 'engine', //用户名 'username' => 'root', //账户密码 'password' => 'root', //字符集 'charset' => 'utf8', //引擎 'collation' => 'utf8_general_ci', //表前缀 'prefix' => '' ]; ~~~ 3.修改入口文件 ~~~ addConnection(require '../config/database.php'); $capsule->bootEloquent(); //路由配置 require '../config/routs.php'; ~~~ 4. 修改models下的article文件 ~~~ class Article extends Illuminate\Database\Eloquent\Model { public $timestamps=false; } ~~~ 参考文档:https://laravel-china.org/docs/laravel/5.5/eloquent >如果刷新可以看到原来的输出,就表示框架已经完成,至此我们已经实现了一个框架的基本雏形,基本上现在市面上所含有的框架都是这种模式了,大道至简。 ## 5. 视图装载 1.将入口文件index.php改写 ~~~ define('PUBLIC__PATH',__DIR__); require PUBLIC__PATH.'./../bootstrap.php'; //路由配置 require PUBLIC__PATH.'./../config/routs.php'; ~~~ 2.创建./bootstrap.php文件 ~~~ use Illuminate\Database\Capsule\Manager as Capsule; //定义常量 define('BASE_PATH',__DIR__); //自动载入文件 require BASE_PATH.'./vendor/autoload.php'; //数据库配置 $capsule = new Capsule; $capsule->addConnection(require BASE_PATH.'./config/database.php'); $capsule->bootEloquent(); ~~~ >我们将入口文件中的一部分拿出来,改写为bootstrap.php,将其作为一个启动器 3.我们的错误提示引入 filp/whoops 修改compose.json文件 ~~~ { "require": { "noahbuscher/macaw": "dev-master", "illuminate/database": "*", "filp/whoops": "*" }, "autoload": { "classmap": [ "app/controllers", "app/models" ] } } ~~~ >修改完之后执行composer update 在bootstrap.php添加错误提示 ~~~ addConnection(require BASE_PATH.'./config/database.php'); $capsule->bootEloquent(); //错误提示 $whoops=new \Whoops\Run; $whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler()); $whoops->register(); ~~~ 参考文档https://github.com/filp/whoops >修改路由文件 ~~~ use NoahBuscher\Macaw\Macaw; Macaw::get('/', function() { echo 'Hello world!'; }); //新增一条路由 Macaw::get('/home', 'HomeController@home'); //错误路由 Macaw::$error_callback=function (){ throw new Exception('路由无法匹配 404 not found'); }; Macaw::dispatch(); ~~~ >在地址栏随意输入,即可看到错误输出! 3.实现装载器 创建services目录,修改composer.json文件 ~~~ { "require": { "noahbuscher/macaw": "dev-master", "illuminate/database": "*", "filp/whoops": "*" }, "autoload": { "classmap": [ "app/controllers", "app/models", "services" ] } } ~~~ >修改完文件后执行composer dump-autoload 4.在servers目录下创建view.php ~~~ class View { const VIEW_BASE_PATH = '/app/views/'; public $view; public $data; public function __construct($view) { $this->view = $view; } public static function make($viewName = null) { if ( ! $viewName ) { throw new InvalidArgumentException("视图名称不能为空!"); } else { $viewFilePath = self::getFilePath($viewName); if ( is_file($viewFilePath) ) { return new View($viewFilePath); } else { throw new UnexpectedValueException("视图文件不存在!"); } } } public function with($key, $value = null) { $this->data[$key] = $value; return $this; } private static function getFilePath($viewName) { $filePath = str_replace('.', '/', $viewName); return BASE_PATH.self::VIEW_BASE_PATH.$filePath.'.php'; } public function __call($method, $parameters) { if (starts_with($method, 'with')) { return $this->with(snake_case(substr($method, 4)), $parameters[0]); } throw new BadMethodCallException("方法 [$method] 不存在!."); } } ~~~ 5.可修改BaseController ~~~ class BaseController { protected $view; public function __construct (){ } public function __destruct () { $view=$this->view; if($view instanceof View){ extract($view->data); require $view->view; } } } ~~~ 6.修改HomeController ~~~ class HomeController extends BaseController { public function home(){ $this->view=View::make('home')->with('article',Article::first()); } } ~~~ ## 6. 发送邮件 1.修改composer.json文件 ~~~ { "require": { "noahbuscher/macaw": "dev-master", "illuminate/database": "*", "filp/whoops": "*", "nette/mail": "*" }, "autoload": { "classmap": [ "app/controllers", "app/models", "services" ] } } ~~~ >修改完以后执行composer update >文档地址:https://doc.nette.org/en/2.2/mailing 2.创建配置文件mail ~~~ return [ 'host' => 'smtp.163.com', 'username' => '163@163.com', 'password' => 'password', 'secure' => '' //zhanglei520 ]; ~~~ 3.创建配置文件services/mail ~~~ class Mail extends \Nette\Mail\Message{ public $config; protected $from; protected $to; protected $title; protected $body; public function __construct($to) { $this->config = require BASE_PATH.'/config/mail.php'; $this->setFrom($this->config['username']); if ( is_array($to) ) { foreach ($to as $email) { $this->addTo($email); } } else { $this->addTo($to); } } public function from($from=null) { if ( !$from ) { throw new InvalidArgumentException("邮件发送地址不能为空!"); } $this->setFrom($from); return $this; } public static function to($to=null) { if ( !$to ) { throw new InvalidArgumentException("邮件接收地址不能为空!"); } return new Mail($to); } public function title($title=null) { if ( !$title ) { throw new InvalidArgumentException("邮件标题不能为空!"); } $this->setSubject($title); return $this; } public function content($content=null) { if ( !$content ) { throw new InvalidArgumentException("邮件内容不能为空!"); } $this->setHTMLBody($content); return $this; } } ~~~ 4.修改basecontrol文件 ~~~ class BaseController { protected $view; protected $mail; public function __construct (){ } public function __destruct () { $view=$this->view; if($view instanceof View){ extract($view->data); require $view->view; } $mail = $this->mail; if ( $mail instanceof Mail ) { $mailer = new Nette\Mail\SmtpMailer($mail->config); $mailer->send($mail); } } } ~~~ 5.修改homecontroller当测试文件 ~~~ class HomeController extends BaseController { public function home(){ $this->mail = Mail::to(['1908002007@qq.com']) ->from('张磊 <18109485312@163.com>') ->title('来自你身边的一份信!') ->content('你是一个大逗逼'); } } ~~~ >如果可以收到邮件表示发送邮件模块测试成功 ## 7. Redis缓存 1.修改composer文件 ~~~ { "require": { "noahbuscher/macaw": "dev-master", "illuminate/database": "*", "filp/whoops": "*", "nette/mail": "*", "predis/predis": "*" }, "autoload": { "classmap": [ "app/controllers", "app/models", "services" ] } } ~~~ 2.创建services/Redis.php ~~~ class Redis { const CONFIG_FILE = '/config/redis.php'; protected static $redis; public static function init() { self::$redis = new Client(require BASE_PATH.self::CONFIG_FILE); } public static function set($key,$value,$time=null,$unit=null) { self::init(); if ($time) { switch ($unit) { case 'h': $time *= 3600; break; case 'm': $time *= 60; break; case 's': case 'ms': break; default: throw new InvalidArgumentException('单位只能是 h m s ms'); break; } if ($unit=='ms') { self::_psetex($key,$value,$time); } else { self::_setex($key,$value,$time); } } else { self::$redis->set($key,$value); } } public static function get($key) { self::init(); return self::$redis->get($key); } public static function delete($key) { self::init(); return self::$redis->del($key); } private static function _setex($key,$value,$time) { self::$redis->setex($key,$time,$value); } private static function _psetex($key,$value,$time) { self::$redis->psetex($key,$time,$value); } } ~~~ 3.创建文件 ~~~ return [ 'host' => '127.0.0.1', 'port' => 6379 ]; ~~~ 4.测试用例 ~~~ Redis::set('key','value',5,'s'); echo Redis::get('key'); ~~~ >写到这里算是告一段落吧!如果有机会继续扩展,算是一个学习总结吧! > >如果有需要继续扩展的可以借鉴CI框架进行升级。 > >最后两张写的比较潦草,主要是其主要是对于一些工具类的使用。只要是看看文档就可以封装出操作类吧!