Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message
webman-tech-common-utils-best-practices
webman-tech-common-utils-best-practices
webman-tech/common-utils
claude-code
runtime-abstraction
cross-framework
middleware
http-abstraction
webman
helpers
cli

跨框架统一 API。触发:Request/Response 抽象、路径函数、运行时感知、JSON 序列化、跨框架中间件、测试环境。

Install
php artisan boost:add-skill webman-tech/common-utils

Save this content to: .claude/skills/webman-tech-common-utils-best-practices/SKILL.md

# webman-tech/common-utils 最佳实践

## 核心原则

1. **只依赖公共 API**:使用 `Request/Response/Route/Container` 等抽象层,禁止直接引用具体框架类
2. **运行时感知**:组件会自动识别当前运行时,命令行和测试环境需要手动注册
3. **Helper 优先**:能用 `base_path()/config()/logger()` 等 Helper 函数就不要直接调用底层类

---

## 运行时与路径

### 在命令行/脚本中使用

命令行环境下运行时未自动识别,需手动注册:

```php
use WebmanTech\CommonUtils\Runtime;
use WebmanTech\CommonUtils\RuntimeCustomRegister;

RuntimeCustomRegister::register([
    'base_path' => '/path/to/project',
    'runtime_path' => '/path/to/project/runtime',
]);

// 之后可以直接使用 Helper
$path = base_path();
$logger = logger();
```

不要在命令行中直接使用 Webman 的原生路径函数,因为它们依赖 Webman 运行时上下文:

```php
// ❌ 直接调用 Webman 函数,命令行中可能不存在
$path = \support\App::runtimePath();

// ✅ 通过 RuntimeCustomRegister 注册后使用抽象层 Helper
$path = runtime_path();
```

### 判断当前运行时

```php
use WebmanTech\CommonUtils\Runtime;

if (Runtime::isWebman()) {
    // Webman 特有逻辑
}
if (Runtime::isCli()) {
    // 命令行逻辑
}
```

---

## HTTP 请求与响应

### 获取当前请求

始终通过 `Request::getCurrent()` 获取当前请求:

```php
use WebmanTech\CommonUtils\Request;

$request = Request::getCurrent();
$all = $request->allPostJson();
$userId = $request->getCustomData('userId');
```

避免直接使用框架类或类型提示具体框架的 Request(无法在其他环境中运行):

```php
// ✅ 通过抽象层操作(跨框架兼容)
$request = Request::getCurrent();

// ❌ 避免:直接使用框架类,无法在其他环境中运行
$request = request(); // Webman 专属函数

// ❌ 避免:直接类型提示具体框架的 Request
public function handle(\Webman\Http\Request $request)
```

### 生成响应

```php
use WebmanTech\CommonUtils\Json;
use WebmanTech\CommonUtils\Response;

return Response::make()
    ->withStatus(201)
    ->withHeaders(['X-Request-Id' => $traceId])
    ->withBody(Json::encode(['id' => $userId]))
    ->getRaw();
```

---

## 跨框架中间件

继承 `BaseMiddleware` 实现的中间件可以在 Webman 和 Laravel 中复用:

```php
use WebmanTech\CommonUtils\Middleware\BaseMiddleware;
use WebmanTech\CommonUtils\Request;
use WebmanTech\CommonUtils\Response;

final class TraceMiddleware extends BaseMiddleware
{
    protected function processRequest(Request $request, \Closure $handler): Response
    {
        $request->withHeaders(['X-Trace-Id' => uniqid('trace_', true)]);

        $response = $handler($request);

        return $response->withHeaders(['X-Response-Time' => microtime(true)]);
    }
}
```

关键点:对请求的修改(`withHeaders()`、`withCustomData()`)会直接写回底层真实请求对象,不需要额外传递。

```php
// ❌ 绕过抽象层,Webman 和 Laravel 行为不一致
$request->getRaw()->header('X-Foo');

// ✅ 通过抽象层操作
$request->header('X-Foo');
```

避免在中间件中直接操作框架原生 Request(会绕过抽象层,导致 Webman 和 Laravel 行为不一致)。

---

## JSON 序列化

推荐始终使用 `Json::encode()` 而非原生 `json_encode()`:

```php
// ❌ 原生 json_encode 遇到 NaN/非 UTF-8 会失败
$json = json_encode($data, JSON_UNESCAPED_UNICODE);
```

`Json::encode()` 自动处理 INF/NAN、非 UTF-8、Expression 等边界情况。

当需要在前端模板中内联 JS 表达式时,使用 `Json\Expression`:

```php
use WebmanTech\CommonUtils\Json\Expression;

$data = [
    'callback' => new Expression('function() { alert(1); }'),
];
// callback 不会被引号包裹,直接输出为 JS 代码
```

---

## 环境变量(EnvAttr)

`EnvAttr` 首次调用 `get()` 后自动加载并进入只读模式:

```php
use WebmanTech\CommonUtils\EnvAttr;

$debug = EnvAttr::get('APP_DEBUG', false);
```

如需在运行时修改(仅限测试或初始化阶段):

```php
// ✅ 先关闭只读,再修改
EnvAttr::changeSupportReadonly(false);
EnvAttr::set('APP_DEBUG', true);
```

直接修改会抛异常(默认只读模式已开启)。

---

## 缓存

详见 [references/cache.md](references/cache.md)。

---

## 测试环境搭建

### 注册测试运行时

在测试入口文件中调用 `Factory::registerTestRuntime()`:

```php
// tests/bootstrap.php
use WebmanTech\CommonUtils\Testing\Factory;

Factory::registerTestRuntime(__DIR__ . '/test-demo');
```

之后所有公共 API 自动使用测试假体,无需关心框架差异。

### 在测试中使用

```php
use WebmanTech\CommonUtils\Request;
use WebmanTech\CommonUtils\Route;

test('example', function () {
    $request = Request::getCurrent(); // 返回 TestRequest
    $route = Route::getCurrent();     // 返回 TestRoute

    // 业务测试...

    // 清理,避免污染下一个测试
    Request::getCurrent()->clear();
    Route::getCurrent()->clear();
});
```

`Route::clear()` 会清空全局路由,**仅在测试或 CLI 初始化时调用**,线上环境禁用。

---

## 常见错误

| 错误 | 原因 | 解决 |
|------|------|------|
| `InvalidArgumentException` / `UnsupportedRuntime` | 运行时不可识别且未注册自定义实现 | 调用 `RuntimeCustomRegister::register()` 注册 |
| `EnvAttr` 修改报错 | 默认只读模式已开启 | 先调用 `EnvAttr::changeSupportReadonly(false)` |
| 测试间数据污染 | TestRequest/TestRoute 未清理 | 在 `beforeEach` 中调用 `clear()` |
| `json_encode` 返回 `false` | 数据含 NaN/INF/非 UTF-8 | 改用 `Json::encode()` |
| `Route::clear()` 导致线上路由丢失 | 在生产代码中调用了 clear | 仅在测试中使用 |

webman-tech/common-utils 最佳实践

核心原则

  1. 只依赖公共 API:使用 Request/Response/Route/Container 等抽象层,禁止直接引用具体框架类
  2. 运行时感知:组件会自动识别当前运行时,命令行和测试环境需要手动注册
  3. Helper 优先:能用 base_path()/config()/logger() 等 Helper 函数就不要直接调用底层类

运行时与路径

在命令行/脚本中使用

命令行环境下运行时未自动识别,需手动注册:

use WebmanTech\CommonUtils\Runtime;
use WebmanTech\CommonUtils\RuntimeCustomRegister;

RuntimeCustomRegister::register([
    'base_path' => '/path/to/project',
    'runtime_path' => '/path/to/project/runtime',
]);

// 之后可以直接使用 Helper
$path = base_path();
$logger = logger();

不要在命令行中直接使用 Webman 的原生路径函数,因为它们依赖 Webman 运行时上下文:

// ❌ 直接调用 Webman 函数,命令行中可能不存在
$path = \support\App::runtimePath();

// ✅ 通过 RuntimeCustomRegister 注册后使用抽象层 Helper
$path = runtime_path();

判断当前运行时

use WebmanTech\CommonUtils\Runtime;

if (Runtime::isWebman()) {
    // Webman 特有逻辑
}
if (Runtime::isCli()) {
    // 命令行逻辑
}

HTTP 请求与响应

获取当前请求

始终通过 Request::getCurrent() 获取当前请求:

use WebmanTech\CommonUtils\Request;

$request = Request::getCurrent();
$all = $request->allPostJson();
$userId = $request->getCustomData('userId');

避免直接使用框架类或类型提示具体框架的 Request(无法在其他环境中运行):

// ✅ 通过抽象层操作(跨框架兼容)
$request = Request::getCurrent();

// ❌ 避免:直接使用框架类,无法在其他环境中运行
$request = request(); // Webman 专属函数

// ❌ 避免:直接类型提示具体框架的 Request
public function handle(\Webman\Http\Request $request)

生成响应

use WebmanTech\CommonUtils\Json;
use WebmanTech\CommonUtils\Response;

return Response::make()
    ->withStatus(201)
    ->withHeaders(['X-Request-Id' => $traceId])
    ->withBody(Json::encode(['id' => $userId]))
    ->getRaw();

跨框架中间件

继承 BaseMiddleware 实现的中间件可以在 Webman 和 Laravel 中复用:

use WebmanTech\CommonUtils\Middleware\BaseMiddleware;
use WebmanTech\CommonUtils\Request;
use WebmanTech\CommonUtils\Response;

final class TraceMiddleware extends BaseMiddleware
{
    protected function processRequest(Request $request, \Closure $handler): Response
    {
        $request->withHeaders(['X-Trace-Id' => uniqid('trace_', true)]);

        $response = $handler($request);

        return $response->withHeaders(['X-Response-Time' => microtime(true)]);
    }
}

关键点:对请求的修改(withHeaders()、withCustomData())会直接写回底层真实请求对象,不需要额外传递。

// ❌ 绕过抽象层,Webman 和 Laravel 行为不一致
$request->getRaw()->header('X-Foo');

// ✅ 通过抽象层操作
$request->header('X-Foo');

避免在中间件中直接操作框架原生 Request(会绕过抽象层,导致 Webman 和 Laravel 行为不一致)。


JSON 序列化

推荐始终使用 Json::encode() 而非原生 json_encode():

// ❌ 原生 json_encode 遇到 NaN/非 UTF-8 会失败
$json = json_encode($data, JSON_UNESCAPED_UNICODE);

Json::encode() 自动处理 INF/NAN、非 UTF-8、Expression 等边界情况。

当需要在前端模板中内联 JS 表达式时,使用 Json\Expression:

use WebmanTech\CommonUtils\Json\Expression;

$data = [
    'callback' => new Expression('function() { alert(1); }'),
];
// callback 不会被引号包裹,直接输出为 JS 代码

环境变量(EnvAttr)

EnvAttr 首次调用 get() 后自动加载并进入只读模式:

use WebmanTech\CommonUtils\EnvAttr;

$debug = EnvAttr::get('APP_DEBUG', false);

如需在运行时修改(仅限测试或初始化阶段):

// ✅ 先关闭只读,再修改
EnvAttr::changeSupportReadonly(false);
EnvAttr::set('APP_DEBUG', true);

直接修改会抛异常(默认只读模式已开启)。


缓存

详见 references/cache.md。


测试环境搭建

注册测试运行时

在测试入口文件中调用 Factory::registerTestRuntime():

// tests/bootstrap.php
use WebmanTech\CommonUtils\Testing\Factory;

Factory::registerTestRuntime(__DIR__ . '/test-demo');

之后所有公共 API 自动使用测试假体,无需关心框架差异。

在测试中使用

use WebmanTech\CommonUtils\Request;
use WebmanTech\CommonUtils\Route;

test('example', function () {
    $request = Request::getCurrent(); // 返回 TestRequest
    $route = Route::getCurrent();     // 返回 TestRoute

    // 业务测试...

    // 清理,避免污染下一个测试
    Request::getCurrent()->clear();
    Route::getCurrent()->clear();
});

Route::clear() 会清空全局路由,仅在测试或 CLI 初始化时调用,线上环境禁用。


常见错误

错误 原因 解决
InvalidArgumentException / UnsupportedRuntime 运行时不可识别且未注册自定义实现 调用 RuntimeCustomRegister::register() 注册
EnvAttr 修改报错 默认只读模式已开启 先调用 EnvAttr::changeSupportReadonly(false)
测试间数据污染 TestRequest/TestRoute 未清理 在 beforeEach 中调用 clear()
json_encode 返回 false 数据含 NaN/INF/非 UTF-8 改用 Json::encode()
Route::clear() 导致线上路由丢失 在生产代码中调用了 clear 仅在测试中使用
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource