ginger-tek/routy 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

ginger-tek/routy

Composer 安装命令:

composer require ginger-tek/routy

包简介

A simple, robust PHP router for fast app and API development

README 文档

README

Routy

A simple, robust PHP router for fast app and API development

Getting Started

Composer

composer require ginger-tek/routy
require 'vendor/autoload.php';

use GingerTek\Routy;

$app = new Routy;

Starter Example

Handlers for each route can be any kind of callable, such as regular functions, arrow functions, closure variables, or static class methods.

$app = new Routy;

// Standard Function
$app->get('/', function (Routy $app) {
  $app->sendData('Hello!');
});

// Arrow Function
$app->get('/', fn () => $app->sendData('Hello!'););

// Closure
$handler = function (Routy $app) {
  $app->sendData('Hello!');
};
$app->get('/closure', $handler);

// Static Class Method
class ProductsController {
  public static function list(Routy $app) {
    ...
    $app->sendJson($products);
  }
}

$app->get('/products', \ProductsController::list(...));

Configurations

You can pass an associative array of optional configurations to the constructor.

  • root to set the root app directory when running from a sub-directory, i.e. public/. Defaults to current directory.
  • base to set a global base URI when running from a sub-directory
  • render to set a default template rendering strategy to use in the render() response method. Defaults to false.
$app = new Routy([
  'root' => '../',
  // i.e. app instance is created in public/index.php and your app root is one directory above

  'render' => function () {},
  // this will be called by the render method and the returned string value will be sent as the response

  'base' => '/api',
  // i.e. your app files are in /wwwroot/my-api-app and is accessed via https://domain.com/api
])

If you need to use any of these configuration values later on, you can access them using getConfig(); however, you can not update configurations after instantiation.

new \PDO('sqlite:' . $app->getConfig('root') . '/app.db');

Features

Method Wrappers

Use the method wrappers for routing GET, POST, PUT, PATCH, or DELETE method requests. There is also a catch-all wrapper for matching on all standard HTTP methods, including HEAD and OPTIONS.

$app->get('/products', ...); // HTTP GET
$app->post('/products/:id', ...); // HTTP POST
$app->put('/products', ...); // HTTP PUT
$app->patch('/products/:id', ...); // HTTP PATCH
$app->delete('/products/:id', ...); // HTTP DELETE
$app->any('/products/:id', ...); // HTTP GET, POST, PUT, PATCH, DELETE, HEAD, or OPTIONS

Use * for the route argument to match on any route.

$app->get('*', ...); // HTTP GET for all routes
$app->any('*', ...); // Any standard HTTP method for all routes

Custom Routing

You can also use the route() method directly, which is what the common method wrappers use underneath, to craft more specific method conditions on which to match.

$app->route('GET|POST', '/form', ...); // HTTP GET and POST for the /form route
$app->route('GET|POST|PUT', '/products', ...); // HTTP GET, POST and PUT for the /products route

Dynamic Routes

To define dynamic route parameters, use the :param syntax and access them via the getParam() method on the $app context. The values are URL-decoded automatically.

$app->get('/products/:id', function(Routy $app) {
  $id = $app->getParam('id');
  // ...
});

Middleware

Global Middleware

If you want to define global middleware, you can use the use() method. Any middleware or route handler callable must have one argument to accept the current Routy instance.

(See Context Sharing about sharing data between middleware/handlers)

function authenticate(Routy $app) {
  if(!($token = @$app->getHeaders()['authorization']))
    $app->end(401);
  $app->setCtx('user', parseToken($token));
}

$app->use(authenticate(...));

Route Middleware

All arguments set after the URI string argument are considered middleware functions, including the route handler, so you can define as many as needed.

$app->get('/products', authenticate(...), function (Routy $app) {
  $userId = $app->getCtx('user')->id;
  $items = getProductsByUser($userId);
  $app->sendJson($items);
});

Context Sharing

To share data between handlers/middleware or provide a global resource to the instance, use the setCtx() and getCtx(). Any data type can be passed in for the value.

$app->setCtx('db', new PDO('sqlite:myData.db'));
...
$app->get('/products', function (Routy $app) {
  $db = $app->getCtx('db');
  $stmt = $db->prepare('select * from products');
  $stmt->execute();
  $result = $stmt->fetch();
  ...
})

Route Groups

You can define route groups using the group() method.

$app = new Routy;

$app->group('/products', function (Routy $app) {
  $app->post('/', ...);
  $app->get('/', ...);
  $app->get('/:id', ...);
  $app->put('/:id', ...);
  $app->delete('/:id', ...);
});

You can also add middleware to your nested routes, which will apply it to all the routes nested within.

$app->group('/products', authenticate(...), function (Routy $app) {
  $app->get('/', ...);
});

Fallback Routes

Fallbacks are used for returning custom 404 responses, or to perform other logic before returning.

To set a fallback route, use the fallback() method to set a handler function, which will automatically have the HTTP 404 response header set.

Fallback routes are scoped to wherever they are defined, and will only be reached if they match the incoming URI's parent path.

$app = new Routy;                        

$app->group('/products', function (Routy $app) {
  $app->get('/', fn (Routy $app) => $app->sendJson([]));

  // GET /products/asdf will end up here
  $app->fallback(fn () => $app->render('product-not-found'));
});

// GET /asdf will end up here
$app->fallback(fn () => $app->render('not-found'));

Serve Static Files (SPA)

To serve static files from a specified directory via a proxy route, use the serveStatic() method after all other normal route definitions. You can use this to serve asset files or a whole SPA from the same app. If the requested URI is a directory, an index.html file will be served, if one exists, and client-side routing will take over. Otherwise, if any requested file is not found, a generic 404 response with be sent back.

NOTE: Serving static files is typically best performed by a web server (Apache/nginx/Caddy) via rewrite rules, so this is a convienence for less demanding applications. Consider your performance requirements in production scenarios when using this feature.

$app = new Routy;

$app->group('/api', ApiController::index(...));
$app->serveStatic('/nm', 'node_modules');
$app->serveStatic('/', 'public');

Request Properties

You can access the incoming request via the uri and method properties on the $app instance.

$app->uri;     // /some/route
$app->method;  // GET, POST, PUT, etc.

Request Helper Methods

There are a few helper methods for handling incoming requests.

getQuery()

Use to retrieve an incoming URL query parameter. Key lookup is case-sensitive. Returns false if not found.

$name = $app->getQuery('name'); // <= /some/route?name=John%20Doe
echo $name; // John Doe

getParam()

Use to retrieve an incoming URI request parameter. Key lookup is case-sensitive. Returns false if not found.

$param = $app->getParam('param'); // <= /some/route/:param (i.e., bob)
echo $param; // bob

getBody()

Use to retrieve the incoming payload data. JSON data will automatically be decoded and form data will be cast to a standard object for cleaner syntax. For all other data types, body data will be left as is.

$app->get('/products', function (Routy $app) {
  $body = $app->getBody();
  $body->someProperty;  // JSON: { "someProperty": "..." }
                        // or
  $body->username;      // multipart/form-data: <input name="username">
});

getHeader()

Use to retrieve an incoming HTTP header by name. Lookup is case-insensitive, i.e. both Content-Type and content-type will work.

$app->get('/products', function (Routy $app) {
  $authToken = $app->getHeader('authorization'); // 'Bearer eyhdgs9d8fg9s7d87f...'
});

getFiles()

Use to retrieve uploaded files from multipart/form-data requests. Returns an object array of all files.

<form method="POST" action="/upload" enctype="multipart/form-data">
  <input type="file" name="field-name" required>
  <button type="submit">Submit</button>
</form>
$app->post('/upload', function (Routy $app) {
  $files = $app->getFiles('field-name');
  // Destructure assignment for a single file upload
  [$file] = $app->getFiles('field-name');
});

Response Helper Methods

There are plenty of helper methods for handling responses.

sendData()

Use to return string data or a file's raw contents.

$app->sendData('<h1>Raw HTML</h1>');

If the data is a file path, the Content-Type will be automatically detected, if it has a known MIME type.

$app->sendData('path/to/file.html');

Otherwise, the Content-Type can be specified explicitly.

$app->sendData($base64EncodedImage, 'image/png');
$app->sendData($pathToFileWithNoExtension, 'text/csv');

sendJson()

Use to return data as a JSON string

$app->sendJson(['prop' => 'value']); // { "prop": "value" }
$app->sendJson([1, 2, ['three' => 4], 5]); // [1, 2, { "three: 4 }, 5]

render()

Use to render a view file, calling the configured render strategy callback. Template rendering strategy is left up to the developer, allowing for the use any kind of templating engine.

To configure a template rendering strategy, set the render option on your Routy instance to a callback. This callback must follow this argument signature:

function myRenderCallback(string $view, array $context, Routy $app): string

The callback must return a string value of your rendered content, which will be returned as the response. The app instance can be useful for acceessing the root path when dealing with nested directory structures.

Below is an example using Twig:

$app = new Routy([
  'render' => function (string $view, array $context, Routy $app): string {
    $loader = new \Twig\Loader\FilesystemLoader($app->getConfig('root') . 'views/');
    $twig = new \Twig\Environment($loader);
    $model['app'] = $app;
    return $twig->render("$view.html.twig", $model);
  }
]);
...
$app->render('home'); // views/home.html.twig
$app->render('about'); // views/about.html.twig

Another example using plain PHP templating:

$app = new Routy([
  'render' => function (string $view, array $context, Routy $app): string {
    ob_start();
    $context['view'] = $app->getConfig('root') . "views/$view.php";
    extract($context, EXTR_OVERWRITE);
    include $app->getConfig('root') . 'views/_layout.php';
    return ob_get_clean();
  }
]);
...
$app->render('home'); // views/home.php
$app->render('about'); // views/about.php

status()

Use to set the HTTP status code. This method can chained to other response methods.

$app->post('/products', function (Routy $app) {
  $app->status(400)->sendJson(['error' => 'Bad payload']);
  // or
  $app->status(201)->sendData('<p>Successfully created!</p>');
});

redirect()

Use to send a temporary or permanent redirect to a new URL.

$app->redirect('/go/here'); // HTTP 302
$app->redirect('/new/permanent/location', true); // HTTP 301

end()

Use to return immediately with an optional HTTP status code.

$app->end(); // Defaults to 200 = Success/OK
$app->end(401); // Unauthorized
$app->end(403); // Forbidden
$app->end(404); // Not Found

ginger-tek/routy 适用场景与选型建议

ginger-tek/routy 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 338 次下载、GitHub Stars 达 1, 最近一次更新时间为 2023 年 12 月 23 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「templating」 「routing」 「rest」 「api」 「router」 「mpa」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 ginger-tek/routy 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 ginger-tek/routy 我们能提供哪些服务?
定制开发 / 二次开发

基于 ginger-tek/routy 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 338
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 1
  • 点击次数: 12
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 1
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2023-12-23