loskoderos / moo
Moo 是一个 PHP 微型框架,旨在让生活和调试变得痛苦不堪
Requires
- php: >=8.0
- loskoderos/generic-php: dev-master
This package is not auto-updated.
Last update: 2024-09-23 19:28:58 UTC
README
问题是 什么 是 Moo,而是 为什么 需要 Moo?
每个 PHP 开发者都不得不创建自己的框架。其中大部分都是垃圾,这个也不例外。
因此,Moo 框架是 PHP 恶魔的产物,旨在让生活和调试变得痛苦。创建它只需要几个小时,但要找出什么和为什么工作或不起作用可能需要几天时间。像大多数微框架一样,Moo 非常接近 HTTP 世界,由一系列纯 PHP 类组成,没有外部依赖。仅包含路由器、请求、响应,完成。
当然,使用任何其他现成的框架会更简单、更快、更明智,但这就是 Moo!
为什么选择 Moo?
为什么不。
为什么使用 Moo?
老实说,99% 的时间你不应该使用这个框架。不要为了你和其他开发者的福祉和精神健康而这样做。
我能想到的唯一例外是,当你考虑在服务器上的某个地方使用原始 .php 脚本时。如果是这种情况,Moo 实际上可能很有用。
正在寻找一些不错的 PHP 框架?去学习 Symfony、Laravel 或任何真正有社区支持的框架。这个没有。实际上,你可以尝试编写自己的微框架,就像 Moo 一样,以学习和验证你的 PHP 技能。
安装
如果你真的需要,这里就是,只需使用 composer。
composer config minimum-stability dev
composer require loskoderos/moo:dev-main
你好世界
最简单的 Moo 应用程序。
<?php $moo = new Moo\Moo(); $moo->get('/', function () { return "Hello, this is Moo!"; }); $moo();
用法
这是一个示例 Moo 应用程序,展示了更多功能,如状态容器、插件和参数化路由。
<?php $moo = new Moo\Moo(); // Use moo as service container, inject dependency. $moo->bookService = new App\BookService(); // Override the before hook to set Content-Type header. $moo->before = function () use ($moo) { $moo->response->headers->set('Content-Type', 'application/json'); }; // Override the after hook to serialize output to JSON. $moo->after = function () use ($moo) { $moo->response->body = json_encode($moo->response->body); } // Custom plugin. $moo->findBookById = function ($bookId) use ($moo) { return $moo->bookService->find($bookId); } // Define index handler. $moo->get('/', function () { echo "Hello, this is Moo bookstore!"; }); // Define `GET /books/<id>` handler. $moo->get('/books/(\d+)', function ($bookId) use ($moo) { return $moo->findBookById($bookId); }); // Run Moo. $moo();
模板
Moo 支持原生 PHP 模板,但你也可以扩展基本类并使用任何其他引擎渲染模板。请参阅 examples
中的 website
示例。
use Moo\Moo; use Moo\Template; $moo = new Moo(); // Create template renderer. $moo->template = new Template(__DIR__ . '/templates', [ 'foo' => 'bar' ]); // Custom template plugin. $moo->template->date = function () { return date('Y-m-d'); }; // Index page handler. $moo->get('/', function () use ($moo) { return $moo->template->render('index.phtml', [ 'hello' => 'world' ]); });
示例 PHP 模板代码。
<div>foo = <?php echo $foo ?></div> <div>hello = <?php echo $hello ?></div> <div>date = <?php echo $this->date() ?></div>
优雅的 Moo
有些人可能需要编写漂亮的代码,如果你需要样式,你可以尝试以下方法。
<?php use Moo\Moo; class Application extends Moo { public function __construct() { parent::__construct(); $this->get('/', [$this, 'index']); } public function index() { echo 'Hello World'; } } $app = new Application(); $app();
测试它并让你的同事开心。
<?php use PHPUnit\Framework\TestCase; use Moo\Request; class ApplicationTest extends TestCase { public function testIndex() { $request = new Request(); $request->method = 'GET'; $request->uri = '/'; $app = new Application(); $app->flush = null; // You don't want output buffer flush in the unit tests. $app($request); $this->assertEquals(200, $app->response->code); $this->assertEquals('Hello World', $app->response->body); } }
嵌套
你实际上可以用一个 Moo 实例作为另一个 Moo 的回调。
<?php use Moo\Moo; $usersMoo = new Moo(); $usersMoo->get('/users/(\d+)', function ($id) { // ... }); $booksMoo = new Moo(); $booksMoo->get('/books/(\d+)', function ($id) { // ... }); $moo = new Moo() $moo->route('/users/.*', $usersMoo); $moo->route('/books/.*', $booksMoo); $moo();
请注意,当嵌套时,仅使用顶级 flush
,因此来自 $moo
的输出只发送一次。
示例
在 examples
目录中有一些示例。要运行它们,您可以使用内置的 PHP 服务器。
php -S 0.0.0.0:8080 examples/hello-world/index.php
文档
Moo 的目标是简单性、灵活性和易用性。
概念
Moo 用 PHP 编写,基于闭包。从设计角度来看,它是一个前端控制器,Moo\Moo
类公开了一组标准 HTTP 方法,用于将路由处理程序作为闭包绑定。此外,Moo 还充当状态容器,并可以扩展插件。
所有 Moo 组件都位于 PSR-4 Moo
命名空间中。主要组件是 Moo\Moo
类。有三个模型:Moo\Request
、Moo\Response
、Moo\Route
和作为调度器的 Moo\Router
。
Moo 执行输出缓冲,因此您可以在闭包中简单地使用 echo 或返回可序列化的值。
生命周期
当你调用 $moo(...)
时,以下是会发生的事情
路由
您可以绑定处理程序到标准 HTTP 方法
- GET:
$moo->get(...)
- HEAD:
$moo->head(...)
- POST:
$moo->post(...)
- PUT:
$moo->put(...)
- DELETE:
$moo->delete(...)
- CONNECT:
$moo->connect(...)
- OPTIONS:
$moo->options(...)
- TRACE:
$moo->trace(...)
- PATCH:
$moo->patch(...)
您可以使用$moo->route(...)
匹配多个方法。
路由匹配从第一个到最后一个。如果没有路由与请求匹配,将调用错误处理器$moo->error(...)
。
参数
路由可以使用正则表达式参数化。
$moo->post('/orders/(\d+)/items/(\d+)', function ($orderId, $itemId) use ($moo) { return $moo->orderService->findOrderItem($orderId, $itemId); });
前后钩子
有两个请求处理器,前置和后置请求。
$moo->before = function () { echo "Hey I'm the before hook"; }; $moo->after = function () { echo "Hey I'm the after hook"; }; $moo();
错误处理
默认情况下,异常由错误处理器处理,当没有路由匹配请求时也是如此。
$moo->error = function(\Exception $exc) use ($moo) { $moo->response = new Response([ 'code' => $exc->getCode() > 0 ? $exc->getCode() : 500, 'message' => StatusCode::message($exc->getCode()), 'body' => $exc->getMessage() ]); };
请求
Moo\Request
类包括
- 方法
- uri
- 头部
- 查询
- post
- 文件
- body
请求对象$moo->request
在before
之前创建,其值从PHP全局变量$_SERVER
、$_GET
、$_POST
、$_FILES
中获取。
默认情况下,请求体为空!您可以使用ini覆盖它。
$moo->before = function () ($moo) { $moo->request->body = file_get_contents('php://input'); };
响应
Moo\Response
类包括
- body
- 头部
- code
- message
响应对象$moo->response
在before
之前创建,并在请求生命周期中可以修改。响应体不序列化,假设您在after
中的flush
之前进行序列化。
状态
您可以使用Moo保持您的应用程序状态。
$moo->something = 123; $moo->myState = [ 'config' => [ 'host' => 'localhost', 'port' => 1234 ] ];
插件
您可以使用自定义函数扩展Moo。
$moo->foobar = function () { return 123; }; $moo->foobar();
刷新和输出缓冲
默认情况下,所有输出都缓存在$moo()
执行结束时刷新。您可以通过设置自定义刷新处理器来覆盖此行为。
$moo->flush = function () use ($moo) { header('HTTP/1.1 ' . $moo->response->code . ' ' . $moo->response->message); foreach ($moo->response->headers as $name => $value) { header($name.': '.$value, true); } echo $moo->response->body; };
测试
Moo是单元测试的,只需运行make test
。
贡献
欢迎贡献,请提交pull请求。
许可证
MIT