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

Supports Laravel Package

yansongda/supports

Supports is a small PHP utility library from the yansongda ecosystem that simplifies working with arrays, configuration, and pipelines. It provides handy helpers for array access and manipulation, config handling, and pipeline processing, making it useful for Laravel or PHP projects that need lightw...

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps to First Use

  1. Installation — pin to the current major, not a wildcard:

    composer require yansongda/supports:^4.1
    

    Runtime floor is PHP >= 8.2 and Guzzle 7.x. If your app is still on Laravel 8/9 with PHP 8.0–8.1, Composer will refuse the ^4.1 constraint — that is the constraint doing its job, not a packaging bug. Check with:

    composer why-not yansongda/supports ^4.1
    
  2. Where to look first. The package is a grab-bag of small utilities, so read the tree before reading the docs:

    src/
    ├── Json.php          # new in v4.1.3 — thin, strict wrapper over json_encode/decode
    ├── Pipeline.php
    ├── Helpers/Arr.php   # Arr::get / Arr::set and friends
    └── Traits/           # HasHttpRequest, ...
    

    Open the trait/class you intend to use and confirm the method signatures actually exist at 4.1.x. This package has been through two majors since 2020 and old blog posts reference methods that are gone.

  3. First use case — JSON at the boundaries (v4.1.3). The new Json class is the lowest-risk entry point: two methods, no config, no service provider.

    use Yansongda\Supports\Json;
    
    // Encode: UTF-8 preserved by default (no \uXXXX escaping)
    Json::encode(['name' => '中文']);   // '{"name":"中文"}'
    
    // Decode: assoc array by default (differs from native json_decode)
    Json::decode('{"name":"中文"}');    // ['name' => '中文']
    
    // Failure is loud, not silent
    Json::decode('{bad');               // throws \JsonException
    

    The whole point of the class is that you stop writing if (json_decode($x) === null) { ... } and stop forgetting JSON_THROW_ON_ERROR.

  4. Second use case — HTTP calls via the trait.

    use Yansongda\Supports\Traits\HasHttpRequest;
    
    class MyService
    {
        use HasHttpRequest;
    
        public function fetchData(): array
        {
            return $this->get('https://api.example.com/data', [
                'timeout'        => 10,
                'connectTimeout' => 5,
            ]);
        }
    }
    

    Verify the option keys against the trait source before you ship — Guzzle's own key is connect_timeout, and this package's camelCase wrapper is a common source of "why is my timeout ignored" confusion.


Implementation Patterns

1. Strict JSON at the edges (the main new workflow)

The Json class earns its keep where you currently have implicit, silently-failing encode/decode paths. Three places it pays off immediately:

Webhook / third-party payload parsing. You want a hard failure you can log and 400 on, not a null that propagates into your domain:

use Yansongda\Supports\Json;

final class WebhookController
{
    public function __invoke(Request $request): Response
    {
        try {
            $payload = Json::decode($request->getContent());
        } catch (\JsonException $e) {
            Log::warning('Malformed webhook payload', [
                'code' => $e->getCode(),   // a JSON_ERROR_* constant
                'msg'  => $e->getMessage(),
            ]);

            return response()->noContent(400);
        }

        ProcessWebhook::dispatch($payload);

        return response()->noContent();
    }
}

Persistence where byte-stability matters. Caching, idempotency keys, request signing, ETag generation — anywhere the same input must produce the same bytes:

$cacheKey = 'payload:'.hash('xxh128', Json::encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));

Note the explicit flags. The default is JSON_UNESCAPED_UNICODE only, so slashes are still escaped and key order is whatever PHP's hash order gives you — which is why ksort() before encoding is still your job for signatures.

A strict Eloquent cast, when you want invalid stored JSON to blow up rather than return null:

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Yansongda\Supports\Json;

class StrictJsonCast implements CastsAttributes
{
    public function get($model, string $key, $value, array $attributes): mixed
    {
        return $value === null ? null : Json::decode($value);
    }

    public function set($model, string $key, $value, array $attributes): ?string
    {
        return $value === null ? null : Json::encode($value);
    }
}

Use this only where corruption should be an incident. For ordinary model attributes, Laravel's built-in array / json cast is the right default — it never throws, which is usually what you want for user-facing reads.

2. Wrapping HTTP in a container-bound client

Don't scatter use HasHttpRequest across a dozen services. Compose it once into a named client and inject that:

namespace App\Support;

use Yansongda\Supports\Traits\HasHttpRequest;

class ApiClient
{
    use HasHttpRequest;

    protected function defaults(): array
    {
        return [
            'timeout'        => 30,
            'connectTimeout' => 5,
            'headers'        => ['Accept' => 'application/json'],
        ];
    }

    public function getUser(int $id): array
    {
        return $this->get("/users/{$id}", $this->defaults());
    }
}
// AppServiceProvider::register()
$this->app->singleton(ApiClient::class);

Then inject ApiClient into jobs, listeners and controllers. Testing becomes trivial because the seam is a class, not a trait mixed into five unrelated objects.

3. Arr for config and payload merging

The Arr helpers are still the best part of this package for day-to-day Laravel work — deep get with fallback, deep set without clobbering siblings:

use Yansongda\Supports\Helpers\Arr;

$config = Arr::merge($packageDefaults, config('services.acme'));

$timeout = Arr::get($config, 'http.timeout', 10);
Arr::set($config, 'http.timeout', 20);

Do not try to register it as a Laravel facade alias — the package ships no service provider and no facade. Use the class directly, or write a one-line facade of your own if your team insists on the ergonomics. Confusingly, Laravel ships its own Illuminate\Support\Arr with overlapping method names; always import one or the other explicitly to avoid silent divergence.

4. Pipeline for multi-step transformations

The package Pipeline mirrors Laravel's closely enough for simple chains, but it is a separate implementation. The practical guidance:

  • Inside a Laravel app, use Illuminate\Pipeline\Pipeline — you get container resolution of pipe class names, which is a large convenience the package version won't give you.
  • Inside package-adjacent code or non-Laravel scripts, the package Pipeline is fine:
use Yansongda\Supports\Pipeline;

$result = Pipeline::send($data)
    ->through([
        function ($data) { return $this->validateData($data); },
        function ($data) { return $this->sanitizeData($data); },
    ])
    ->then(fn ($data) => $this->storeData($data));

5. Middleware — use the framework's, not the package's

The tempting pattern is to wrap handle() in a package pipeline. Don't. Laravel's middleware already is a pipeline; nesting a second one produces a stack trace that is genuinely hard to read and gives up container resolution and terminable middleware. Reach for the package Pipeline only for domain logic inside a middleware, not for the middleware chain itself.

public function handle(Request $request, Closure $next): Response
{
    $request->setJson(Json::decode($request->getContent())); // strict, throws → exception handler

    return $next($request);
}

Gotchas and Tips

Behavioural pitfalls in the new Json class

1. Json::encode defaults to JSON_UNESCAPED_UNICODE — native does not.

This is the single most likely surprise. Native json_encode escapes non-ASCII; this wrapper doesn't. Anything that compares encoded output byte-for-byte (signature verification, cache keys, snapshot tests, stored fixtures) will now differ:

// Restore native behaviour when you need byte-compatibility
Json::encode($value, 0);

Conversely, if you want unescaped slashes too (URLs in JSON), you must opt in: Json::encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES).

2. Json::decode returns associative arrays by default — native returns stdClass.

Code ported from json_decode($x) that does $obj->name will break with "Attempt to read property on array". Either use ['name'], or pass the assoc flag explicitly:

Json::decode($json, assoc: false); // stdClass, native-like

3. \JsonException will take down queued jobs.

Throwing is the feature, but it converts a previously-silent null into a thrown exception. In a job or listener that means a failed job and a retry, or a failed_jobs row:

try {
    $data = Json::decode($payload);
} catch (\JsonException $e) {
    // Decide deliberately: fail fast, or dead-letter
    throw new PermanentJobFailure('Unparseable payload', previous: $e);
}

Audit every call site you migrate. A null that flowed downstream and quietly degraded into an empty array is now an incident — usually an improvement, occasionally a surprise at 3am.

4. Json::decode(null) throws.

Eloquent columns are frequently null. Guard before decoding: $value === null ? null : Json::decode($value).

5. The exception code is the raw JSON_ERROR_* constant.

Useful for branching (JSON_ERROR_DEPTH vs JSON_ERROR_SYNTAX) and for logging. $e->getMessage() is already descriptive; don't re-implement json_last_error_msg() around it.

6. It's not a replacement for response()->json().

Laravel's response factory does its own encoding with its own flags and error handling. Keep using it for HTTP responses; use Json for explicit encode/decode in your own code paths. Mixing the two gives you two different escaping policies in one API.

Upgrade pitfalls (from 2.x)

7. The v3 logger removal is still the biggest break. Yansongda\Supports\Logger no longer exists. If you are jumping from 2.x straight to 4.1, grep for it and swap to Laravel's facade:

\Log::info('Message'); // replaces Yansongda\Supports\Logger

8. PHP 8.0/8.1 projects cannot move to 4.x. You are stuck on the 2.x line (unmaintained) or you upgrade PHP. Plan that as a separate piece of work before you attempt the Composer bump.

Trait and integration pitfalls

9. HasHttpRequest method names collide easily. get/set/post are extremely common. A trait method silently wins over an inherited one, and PHP only tells you when signatures genuinely conflict. If a class both extends Model and uses the trait, you are asking for a get() ambiguity. Prefer composition — a dedicated ApiClient class that uses the trait — over mixing it into models, controllers or form requests.

10. Overriding a trait method with parent:: does not work. Traits have no parent. To instrument a trait method you must alias it:

class MyService
{
    use HasHttpRequest {
        get as protected traitGet;
    }

    public function get(string $uri, array $options = []): array
    {
        Log::debug('HTTP GET', ['uri' => $uri]);
        return $this->traitGet($uri, $options);
    }
}

11. Timeout key naming. The package's options array is a thin layer over Guzzle but is not identical. connectTimeout vs Guzzle's connect_timeout is the classic mismatch; verify which the trait forwards. Symptom: your connectTimeout: 1 has no effect and connections still hang.

12. No service provider, no facade, no config file. If you need Laravel-native ergonomics (facades, config publishing, DI auto-resolution), you are writing that glue yourself. Budget for it rather than hunting for a config/supports.php that doesn't exist.

Maintenance and sustainability

13. The "stale since 2020" reputation is out of date. v4.1.3 shipped 2026-10-10 with concrete additions. That said, the release cadence is driven by a single maintainer and features arrive as small utility classes rather than roadmap work — treat it as a utility belt you pin tightly (^4.1) rather than a framework dependency you build a foundation on.

14. Extension points are deliberately thin. There are no contracts, no driver system, no events. If you need a different HTTP transport, a different array helper behaviour, or retry semantics, you subclass or wrap — you do not extend. Design your wrappers (like the ApiClient above) so swapping the underlying package for spatie/array / plain Guzzle later is a one-file change.

15. Verify before adopting. With no facade and no docs site, the source is the contract. Before using any helper, confirm the method exists in vendor/yansongda/supports/src/ at the version you installed — several commonly cited methods from 2.x-era articles simply aren't there in 4.x.

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.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator