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

Think Helper Laravel Package

topthink/think-helper

Helper library for ThinkPHP offering everyday utility classes and functions, including array and string manipulation (Arr, Str), a collection-like Set, hashing, time helpers, and base64url encoding. A lightweight dependency that keeps common tasks out of your application code.

View on GitHub
Deep Wiki
Context7

Getting Started

topthink/think-helper is a zero-config static utility library — there is no service provider, no publishable config, no migrations. Installation is the entire setup:

composer require topthink/think-helper

Check composer.lock before you install. In a ThinkPHP 6/8 application this package is almost always already present as a transitive dependency of topthink/framework. Adding it explicitly pins a version constraint that can fight the framework's own constraint and produce a solver conflict on composer update. Only require it directly if you are building a standalone package or a non-ThinkPHP project that wants the helpers.

Where to look first, in this order:

  1. src/helper/Arr.php — dot-notation array access, filtering, plucking, sorting. This is where 80% of real usage lands.
  2. src/helper/Str.php — camel, snake, studly, random, uuid, slug, replaceFirst.
  3. src/helper.php — the global function file. Skim it once; it is short and everything is function_exists-guarded.
  4. src/helper/Set.php — an ArrayAccess wrapper with dot access. Read it last; it is the least-used piece.

First use case — reading nested config with a default, without a chain of isset():

use think\helper\Arr;
use think\helper\Str;

$options = Arr::get($config, 'payments.alipay.options', []);   // default when the path is missing
Arr::set($config, 'payments.alipay.options.timeout', 15);      // creates intermediate keys

$method = 'get' . Str::studly($field) . 'Attribute';           // 'getUser_nameAttribute' -> 'getUserNameAttribute'

If those two calls do what you expect on your first try, you have learned the API surface you will actually use. Everything else in the package is a variation on them.

Implementation Patterns

Config and options merging. The idiomatic pattern is defaults → user overrides → dot access, never raw array indexing:

$defaults = ['retries' => 3, 'backoff' => ['base' => 1, 'max' => 60]];

$resolved = array_replace_recursive($defaults, $userOptions);
$max = Arr::get($resolved, 'backoff.max');

array_replace_recursive plus Arr::get gives you deep merge plus safe reads. Avoid Arr::set for merging — it replaces subtrees wholesale rather than recursing.

Snake/camel mapping at the boundary. The most common day-to-day job in a ThinkPHP app is translating database column names into API payload keys. Do it in one place, at the serializer, not scattered through controllers:

$payload = [];
foreach ($row as $column => $value) {
    $payload[Str::camel($column)] = $value;
}

// and back again for writes
$column = Str::snake($requestKey);

Keep the mapping mechanical and bidirectional so you can round-trip a payload in a test and assert it equals the original.

Whitelisting input before mass assignment. Arr::only / Arr::except are the cheapest guard against over-posting:

$data = Arr::only($request->post(), ['name', 'email', 'bio']);

Be clear about what this is not: it performs no validation, no type coercion, and no nested-shape checking beyond the top level. Treat it as a shape filter that runs before your validator, not instead of it. For nested payloads you need to whitelist each level explicitly, because Arr::only is shallow.

In-memory filtering for small sets. Arr::where and Arr::pluck are genuinely convenient for shaping a handful of rows you already have — building a select-options list, filtering a config array, reducing a permission map:

$emails = Arr::pluck(Arr::where($users, fn ($u) => $u['active']), 'email', 'id');

Push this into SQL as soon as the set is large. These helpers walk the whole array in PHP; they are clarity tools, not query tools.

Deterministic hashing of payloads. Arr::sortRecursive before json_encode + hash gives you a stable cache key or request signature that does not change when key order changes:

$key = 'sig:' . hash('sha256', json_encode(Arr::sortRecursive($payload)));

This is the single most useful non-obvious pattern in the package for anyone doing webhook signing or idempotency keys.

tap / value / with in service classes. If your version's src/helper.php ships these, they are worth adopting for builder-style code where you need to run a side effect mid-chain without breaking the chain:

$query = tap(User::where('active', 1), fn ($q) => $q->where('tenant_id', $tenantId))->select();

Scan the file once and standardize on whichever of these your codebase will use — mixing hand-rolled $x = ...; return $x; blocks with tap() in the same codebase reads worse than either consistently.

Testing. Every helper is static and pure (except Set and the mutating Arr methods). You can unit-test your mapping and key-generation logic with no container, no database, no framework bootstrap. If you are writing a package that needs array/string helpers, depending on this instead of the full framework keeps your test suite fast.

Gotchas and Tips

Dot notation is unconditional. Arr::get explodes the key on ., so a literal key containing a dot is unreachable — Arr::get(['a.b' => 1], 'a.b') looks for nested a → b and returns the default. If your data has dotted keys (common with some config formats and Elasticsearch documents), you must index directly. The same applies to Arr::has, Arr::forget, and Set.

Silent failure is the default behavior everywhere. Missing keys return the default; Arr::forget on a nonexistent path does nothing; Arr::set happily creates intermediate levels. Nothing throws. A typo in a dotted path becomes a null three layers downstream. Mitigate by passing explicit defaults at every call site and asserting on the resolved config in at least one test.

Arr::get is not Laravel's data_get. It handles arrays and ArrayAccess objects, not arbitrary objects via property access. Passing an Eloquent model or a plain DTO and expecting Arr::get($model, 'user.name') to work is a common false assumption — it will return the default. Convert with ->toArray() first, or read properties directly.

Check the & on mutating methods. Arr::pull, Arr::prepend, and friends take the subject by reference in most versions of this package. If you pass an expression or a method return value, you get a PHP notice or a silently discarded mutation. Assign to a variable first, then pull.

Do not subclass Str to override behavior. The class calls its own methods via self::, so static dispatch resolves to the parent implementation even when you subclass. If you need project-specific string behavior, write a wrapper class that delegates to Str, don't extend it.

PHP 8 native-function collisions. Newer PHP versions ship str_contains, str_starts_with, and str_ends_with natively. If src/helper.php declares polyfills, they are function_exists-guarded and therefore inert on PHP 8 — but if your application also declares them unguarded, you get a fatal redeclare. Prefer the native functions in new code and audit app/ for hand-rolled polyfills before upgrading PHP.

Version floor. The 3.x line requires a modern PHP; legacy ThinkPHP 5 projects are on the 1.x line. Check composer.json's require.php against your deployment target before backporting a helper into an old service, and pin accordingly. The last release is recent, but the API has been stable for years — do not assume a version bump means new capabilities.

Debugging a bad lookup. Arr::dot($array) flattens the structure into exactly the keys that Arr::get will match against. When a lookup silently returns the default, dump Arr::dot() and read the actual keys — it resolves the mismatch in seconds versus stepping through the traversal by hand.

Performance expectations. These are readability helpers. Arr::get in a loop over 100k rows is measurably slower than direct $row['a']['b'] ?? null. Use them at config boundaries, request shaping, and serialization; use direct access in hot inner loops.

Licensing. Apache-2.0, not MIT. Fine for commercial use, but if your dependency scanner or legal template assumes the ThinkPHP stack is uniformly MIT, flag it once rather than getting surprised during an audit.

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.
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
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console