mudge / engine
在一个银行假日周末编写的微型PHP网络框架。
Requires (Dev)
- phpunit/phpunit: ^6.1
This package is not auto-updated.
Last update: 2024-09-29 04:48:55 UTC
README
在一个银行假日周末编写的微型PHP网络框架。
当前版本:未发布
支持的PHP版本 7.1, 7.2
安装
$ composer create-project mudge/engine-skeleton:dev-master my-project $ cd my-project $ php -S localhost:8080 -t public
设计
很容易把HTTP请求响应周期想象成一个纯函数 f
,它接受一些请求(可能是原始HTTP字符串或某些包装对象),并产生一个单独的响应(再次,可能是原始字符串或包装对象)
+-------------------+ +---+ +----------------------------------------+
| GET / HTTP/2 | --> | f | --> | HTTP/2 200 |
| Host: example.com | +---+ | Content-Type: text/html; charset=utf-8 |
+-------------------+ | |
Request | <!DOCTYPE html>... |
+----------------------------------------+
Response
然而,这种思维模型并不完全正确,因为并不是所有的请求都会一次性产生一个单独的响应。例如,响应可以分块发送:可能是一个请求立即接收到一些响应头,然后以部分的形式返回正文。还有可能响应永远不会结束,通过连续流式传输(例如,想想Twitter流式API)。
因此,Engine采用了另一种方法:将典型的请求响应周期建模为一个函数,该函数接受两个请求和响应对象,后者是一种开放文件句柄,允许用户在处理过程中的任何时刻发送头部信息等。这意味着响应完全通过副作用创建,这是一种更混乱的思考方式,但更接近现实。
+-------------------+ +---+
| GET / HTTP/2 | --> | |
| Host: example.com | | |
+-------------------+ | |
Request | f |
| |
+-------------------+ | |
| | --> | |
+-------------------+ +---+
Response
Engine使用MVC模式,但控制器是初始化了Request
、Response
和日志记录器的对象。
Request
是一个值对象,提供对请求URI、方法、任何GET
或POST
参数以及任何Cookies或会话数据的访问。
Response
对象允许您通过各种方法向请求发送数据
redirect(string $location): void
:发送重定向头部;header(string $header): void
:发送任意头部;render(string $template, array $variables = []): void
:使用给定的变量渲染Twig模板;notFound(): void
:返回一个名为404.html
的模板的404 Not Found
响应;forbidden(): void
:返回一个名为403.html
的模板的403 Forbidden
响应;methodNotAllowed(): void
:返回一个名为405.html
的模板的405 Method Not Allowed
响应。
请注意,所有方法都返回void
,因为它们完全通过副作用工作:即向客户端发送数据。
因此,控制器操作是控制器实例上的典型方法,可以访问request
、response
和logger
(有关示例,请参阅用法)。由于这些都是普通的PHP对象,因此可以在控制器之间共享行为,所有常规技术(如组合和继承)都可用。
至于控制器的实例化和动作调用:这是由Router
负责的,它包含HTTP方法和路径到控制器类和动作的映射,例如
+-------------+ +----------------------+
| GET / | --> | HomeController#index |
+-------------+ +----------------------+
+-------------+ +---------------------------+
| POST /login | --> | SessionsController#create |
+-------------+ +---------------------------+
Engine Skeleton项目将创建一个public/index.php
,它设置一个新的Router
,用默认路由填充它,创建一个Request
和一个空的Response
,并根据需要进行路由。
+-------------------+ +-----------------------------+ +------------------+
| GET / HTTP/2 | --> | GET / -> HomepageController | --> | |
__O__ --> | Host: example.com | | index | | index |
| +-------------------+ +-----------------------------+ | |
| Request Router | |
| | |
| +--------+ | |
| <------------------------------------------------- | | --> | |
/ \ | | <-- | |
Client +--------+ +------------------+
Response HomepageController
用法
使用引擎骨架来创建一个新的引擎Web应用程序
$ composer create-project mudge/engine-skeleton:dev-master my-project
这将生成一个在my-project
中的项目布局(不显示vendor
目录的内容)
.
├── README.md
├── composer.json
├── composer.lock
├── log
├── public
│ ├── css
│ │ └── app.css
│ └── index.php
├── src
│ └── HomepageController.php
├── templates
│ ├── 404.html
│ ├── base.html
│ └── index.html
├── tests
│ └── HomepageControllerTest.php
├── tmp
└── vendor
然后您可以启动开发服务器
$ cd my-project $ php -S localhost:8080 -t public
您可以在Web浏览器中访问https://:8080并看到来自Engine的欢迎页面
您可以运行自动化的测试
$ ./vendor/bin/phpunit
控制器
默认情况下,您的项目将期望src
目录中的所有内容都在App
命名空间中。您可以通过从Engine\Controller
继承来实现自己的控制器
<?php declare(strict_types=1); namespace App; use Engine\Controller; /* Controllers are just plain classes that will be instantiated with a Request, Response and logger. */ final class HomepageController extends Controller { public function index(): void { /* Prevent CSRF attacks using tokens in form submissions. */ $this->verifyCsrfToken(); /* Query arguments or post data can be accessed through Parameters. */ $this->params->fetch('name'); /* The response object can be used to render templates and send them to the user. */ $this->response->render('index.html', ['csrf_token' => $this->session->crsfToken()]); /* Or, use the convenience methods on the controller itself. */ $this->renderForm('index.html'); /* Use convenience methods for common responses. */ $this->response->notFound(); $this->response->forbidden(); $this->response->redirect('http://www.example.com'); } }
路由
您Web应用程序的主要入口是public/index.php
,它会在每次请求时执行。项目骨架已经为您生成,但您可以按需编辑。
您几乎肯定想要编辑路由,将请求路由到您自己的控制器操作,但您也可以在这里配置日志和模板缓存。
<?php declare(strict_types=1); /** * Ensure all encoding is in UTF-8. */ mb_internal_encoding('UTF-8'); mb_http_output('UTF-8'); require_once __DIR__ . '/../vendor/autoload.php'; use Monolog\Logger; use Monolog\Handler\StreamHandler; use Engine\{Router, Request, Response}; /** * Set up logging. * * By default, this will log INFO-level messages to ../log/app.log */ $logger = new Logger('app'); $logger->pushHandler(new StreamHandler(__DIR__ . '/../log/app.log', Logger::INFO)); /** * Set up templating. * * By default, this will load templates from ../templates and cache them * to ../tmp (you may want to disable the cache during development) */ $loader = new \Twig_Loader_Filesystem(__DIR__ . '/../templates'); $twig = new \Twig_Environment($loader, ['cache' => __DIR__ . '/../tmp']); /** * Set up the request and response. */ $request = Request::fromGlobals(); $response = new Response($twig, $logger); /** * Set up the router. * * Add any routes of your own here, e.g. * * $router->get('/login', 'App\SessionsController', 'new'); * $router->post('/login', 'App\SessionsController', 'create'); */ $router = new Router($logger); $router->root('App\HomepageController', 'index'); /** * Serve the request with the response. */ $router->route($request, $response);
为什么是"Engine?"
因为我15年前编写过的各种博客系统都不可避免地被称为"Engine"。
许可证
版权所有 © 2017 Paul Mucur
在MIT许可证下分发。