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

Common Utils Laravel Package

webman-tech/common-utils

A collection of common utility helpers for the webman PHP framework, providing reusable tools to simplify everyday development tasks. Designed for webman-tech projects, it offers practical shared functions and classes that reduce boilerplate and keep application code cleaner and more consistent.

View on GitHub
Deep Wiki
Context7

Getting Started

webman-tech/common-utils is not a feature package — it is a compatibility shim. It gives you one API for the things that differ between Webman, Laravel, and plain CLI: path resolution, the container, the current request/response, sessions, routes, config, env, JSON, and logging. The value only appears once you have code that must run in more than one of those environments (e.g. a library, a shared module, a console command that reuses app code).

Install

composer require webman-tech/common-utils

Note the repo is a read-only subtree split of webman-tech/components-monorepo. Do not open issues or PRs against it; all changes land in the monorepo. Treat the package as vendored, versioned, third-party code — pin a version and read the source when in doubt.

Where to look first (in this order)

  1. src/functions.php — the global helpers (base_path(), runtime_path(), logger(), config()). This is the surface you will actually type 90% of the time.
  2. src/Runtime.php + src/RuntimeCustomRegister.php — the detection/registration mechanism everything else hangs off.
  3. src/Request.php, src/Response.php, src/Route.php, src/Config.php — the adapters.
  4. src/Testing/Factory.php and src/Testing/Test*.php — how you make any of this testable.
  5. tests/Unit/CommonUtils/ in the monorepo — the real usage examples.

First use case: a console command that reuses app code

In CLI there is no framework bootstrapping, so nothing is auto-detected. You must register a runtime before any helper works:

use WebmanTech\CommonUtils\RuntimeCustomRegister;

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

// now the helpers resolve
$log = base_path('runtime/logs/app.log');
logger()->info('command started');

From that point on, any code written against Request::getCurrent(), config(), logger(), Response::make() runs unchanged in the command, in Webman, and in Laravel.

Implementation Patterns

1. Write to the abstraction, branch only at the edges

Business/service code should never see a framework class. Keep the runtime check in one place — a bootstrap, a service provider, a Webman Install.php-style entry:

use WebmanTech\CommonUtils\Runtime;

if (Runtime::isWebman()) {
    // wire Webman-specific listeners here
} elseif (Runtime::isCli()) {
    // CLI-only wiring
}

Everything downstream stays framework-agnostic.

2. Request as an ambient, mutable object

Request::getCurrent() returns a wrapper over the live underlying request. Mutations write through:

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

$request->withCustomData('traceId', $traceId); // visible to later code, no plumbing

This is the intended pattern: an auth middleware or trace middleware stamps data onto the request, and controllers read it via getCustomData() — no need to thread a DTO through every signature. It is a deliberate trade-off of explicit dependency-passing for ergonomics.

3. Response as a fluent builder ending in getRaw()

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

getRaw() yields the framework-native response object, which is what Webman/Laravel controllers and middleware expect to be returned. Always terminate the chain with it; returning the wrapper itself will not be understood by the framework.

4. Cross-framework middleware via BaseMiddleware

This is the single highest-leverage pattern in the package. One middleware class, usable in both Webman and Laravel:

final class TraceMiddleware extends BaseMiddleware
{
    protected function processRequest(Request $request, \Closure $handler): Response
    {
        $request->withCustomData('traceId', uniqid('trace_', true));

        $response = $handler($request);

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

Because request mutations write through to the real object, you do not re-inject the request into the handler — the contract is $handler($request) returning a Response. Register it through each framework's native middleware config; the class itself is portable.

5. Config with closure defaults

Config::get() accepts a closure as the default, which is how you lazily build objects without eager instantiation:

$client = config('services.foo.client', fn () => new FooClient(config('services.foo.key')));

This is the pattern to use for anything expensive or environment-dependent — a good replacement for singletons in shared libraries. The config() helper routes through Config, so it works in CLI too (once a runtime is registered).

6. JSON: always Json::encode()

use WebmanTech\CommonUtils\Json;
use WebmanTech\CommonUtils\Json\Expression;

$payload = [
    'value'    => $data,                 // NaN/INF/non-UTF-8 tolerated
    'callback' => new Expression('function () { alert(1); }'), // emitted unquoted
];
$body = Json::encode($payload);

Expression is the escape hatch for inlining JS into a JSON blob for a template. Use it only when you control the string — it bypasses quoting entirely.

7. Env: read once, treat as immutable

use WebmanTech\CommonUtils\EnvAttr;

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

EnvAttr auto-loads the env file on first get() and then locks itself read-only. Treat env as configuration frozen at boot — read it at the top of a bootstrap and pass values down, rather than calling EnvAttr::get() deep inside services.

8. Testing: register a fake runtime once

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

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

After that, Request::getCurrent(), Route::getCurrent(), Config, Session, and the container all resolve to Testing\Test* fakes with no change to production code. This is why the abstraction exists — the alternative is mocking framework internals.

Per-test cleanup:

beforeEach(function () {
    Request::getCurrent()->clear();
    Route::getCurrent()->clear();
});

9. Caches are in-memory only

ArrayCache / NullCache exist to satisfy a cache dependency in tests and short-lived CLI runs. NullCache is the right choice when you want to assert "no caching happened." Neither is a substitute for a real store across requests.

10. Views

View is a plain-PHP template renderer — no Blade/Twig dependency. Use it for mail bodies, CLI output, and small HTML fragments that must render identically in all three runtimes.

Gotchas and Tips

UnsupportedRuntime in CLI is the #1 error. Nothing is auto-detected outside a web request. Any script, queue-worker bootstrap, or PHPUnit entry point must call RuntimeCustomRegister::register([...]) before touching a helper. If you see InvalidArgumentException about runtime, this is it — not a bug.

Never type-hint a framework request. public function handle(\Webman\Http\Request $request) makes the class unusable in Laravel and in tests. Type-hint WebmanTech\CommonUtils\Request, or take no argument and use Request::getCurrent().

Never call the raw escape hatch for header reads. $request->getRaw()->header('X-Foo') works in Webman and silently diverges in Laravel. The whole point of the wrapper is uniform semantics — $request->header('X-Foo') is the only correct form. Reserve getRaw() for the final hand-off to the framework.

Request mutation is a side effect. withHeaders() / withCustomData() write through to the live request object. That is convenient, but it also means the same request object handed to two middlewares carries the first one's changes. If you need per-scope data, key it uniquely (traceId, authUser) rather than reusing generic keys.

EnvAttr::set() throws by default. The first get() flips the class into read-only mode. To mutate (tests and bootstrap only):

EnvAttr::changeSupportReadonly(false);
EnvAttr::set('APP_DEBUG', true);

Do not scatter this across a codebase — if you find yourself flipping it in application code, your config is being read too late.

Route::clear() is a test-only API. It wipes the global route table. Calling it from a service provider or a request lifecycle will silently delete your production routes. Guard it behind the test bootstrap and nowhere else.

Test pollution is the second-most-common failure. The Test* fakes are process-global singletons. Without clear() in beforeEach, a request or route from test A leaks into test B, and you get order-dependent failures. Clear both Request and Route every time, even if a test "doesn't touch routing."

json_encode() returning false is a symptom, not a bug. NaN, INF, or invalid UTF-8 (common with GBK/legacy DB data) will fail. Json::encode() handles those cases, and Encoding exists for fixing source strings before they reach the encoder. If you are debugging an empty API response, check for false before checking anything else.

Pin the version and read the source. Zero stars and zero dependents means no community stack traces, no blog posts, and no Stack Overflow answers. The repository is a read-only split, so there is no issue tracker on it either — changes come from webman-tech/components-monorepo. Budget for reading src/ directly; the code is small and the adapters are the definitive documentation.

Extension point is RuntimeCustomRegister, not subclassing. When you need a fourth environment (Swoole standalone, RoadRunner, a bespoke container), register a custom runtime rather than subclassing Request/Response. Adapters are selected by runtime; overriding one in isolation will produce a half-consistent stack.

Middleware contract is Closure(Request): Response. Returning something else (a raw array, a framework response) breaks the abstraction at the boundary and defeats the point of BaseMiddleware. If you need to short-circuit, build the response with Response::make()->...->getRaw() inside the middleware and return the wrapper's downstream result unmodified.

Keep functions.php helpers at the edges. base_path(), runtime_path(), logger(), config() are convenient, but they are global state. In library code, accept a path or a LoggerInterface as a constructor argument and default to the helper — that keeps unit tests trivial even without Factory::registerTestRuntime().

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.
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
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource