Moo 是一个 PHP 微型框架,旨在让生活和调试变得痛苦不堪

dev-main 2023-08-14 14:25 UTC

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\RequestMoo\ResponseMoo\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->requestbefore之前创建,其值从PHP全局变量$_SERVER$_GET$_POST$_FILES中获取。

默认情况下,请求体为空!您可以使用ini覆盖它。

$moo->before = function () ($moo) {
    $moo->request->body = file_get_contents('php://input');
};

响应

Moo\Response类包括

  • body
  • 头部
  • code
  • message

响应对象$moo->responsebefore之前创建,并在请求生命周期中可以修改。响应体不序列化,假设您在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