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

Core Laravel Package

tether/core

Tether Core supplies foundational building blocks for Laravel projects: shared service providers, helper utilities, and configuration scaffolding that keep packages consistent. A lightweight base layer for teams building modular applications.

View on GitHub
Deep Wiki
Context7

Getting Started

tether/core is a low-adoption package (10 stars, 0 dependents), which changes how you should approach it: treat it as source code you are adopting, not a framework you are consuming. Read the repository before you run composer require.

Step 1 — Verify the package does what you think it does.

git clone https://github.com/lrakauskas/tether-core
cd tether-core
cat composer.json      # PHP + Laravel version constraints — check against your app
cat README.md          # install steps, config publishing, public API
ls src/ tests/         # tests are your only reliable documentation here

The composer.json require block is the first thing to read. Look for pins on illuminate/* or symfony/* — a stale pin will block your framework upgrades more reliably than any bug in the source.

Step 2 — Map the surface you actually care about.

The description advertises four things. Look for each explicitly:

Advertised capability Where to look What to check
Typed contracts + base classes src/Contracts/, src/*/ Are contracts interface-only, or do they leak concrete types?
Auto-discovered service providers composer.json extra.laravel.providers Does discovery require a config key, or is it unconditional?
Config merging + publishable assets src/*ServiceProvider.php, config/ Are config keys namespaced? Is merge shallow or recursive?
Array/string/collection helpers likely src/Support/, src/Helpers/, or plain functions Do they overlap Illuminate\Support\Str / Arr? How are they namespaced?

Step 3 — Run the test suite in isolation.

composer install
vendor/bin/phpunit   # or pest, per composer.json scripts

Green tests in a package with no dependents tell you the author's intent works on the author's machine. They do not tell you it works inside your app's container, with your config cache, alongside your other packages.

First use case. The realistic day-one use is a shared internal foundation package: you have two or three in-house Laravel packages, and you want common contracts (e.g. a PackageServiceProvider base, a HasConfig trait, consistent config publishing) without copying boilerplate between them. Install it into one of those packages, in a branch, not into the host application:

composer require tether/core:^x.y   # pin exactly, see Gotchas

Then write one small consumer — a service provider extending whatever base the package offers — and confirm it boots, merges config, and publishes correctly. If that doesn't work in ten minutes, stop; the package is not saveable cheaply at this maturity.

Implementation Patterns

Pattern 1 — Extend the base provider rather than reimplementing plumbing.

If tether/core ships a service-provider base class, the intended day-to-day use is inheritance. The pattern you want:

namespace App\Packages\Billing\Providers;

use Tether\Core\Providers\PackageServiceProvider; // confirm namespace in src/

class BillingServiceProvider extends PackageServiceProvider
{
    protected function bootPackage(): void
    {
        // package-specific boot: routes, migrations, publishing
    }
}

Before relying on the base class, open it and answer three questions: does it call parent::boot() internally (forcing you into a lifecycle you can't control), does it register singletons into the container with static state, and does it hardcode config filenames? Base-class inheritance is the highest-friction integration point in any "core" package — you are coupling to the author's lifecycle, and that is the hardest thing to back out later.

Pattern 2 — Depend on the contracts, not the traits.

The description separates "typed contracts" from "traits and base classes," and that distinction is your main leverage. Contracts are cheap to isolate behind an adapter; traits and base classes are not.

use Tether\Core\Contracts\SomethingInterface;

class MyThing implements SomethingInterface
{
    // ...
}

If the contracts are genuinely interface-only, you can later swap the implementation and keep MyThing unchanged. If the contracts reference concrete classes from the package, that escape hatch is gone and you should say so in your decision record.

Pattern 3 — Wrap the helpers.

"Framework-agnostic helpers for arrays, strings, and collections" almost certainly overlaps with Illuminate\Support\Str, Arr, and Collection. Don't sprinkle Tether\Core\...\array_get() through your codebase — that is exactly the coupling you cannot undo. Either:

  • use Laravel's first-party helpers directly (they are documented, maintained, and zero-cost), and treat the package's versions as redundant; or
  • if the package's helpers do something Laravel's don't, write thin named wrappers in app/Support/ and call those:
// app/Support/Text.php
final class Text
{
    public static function slugPath(string $value): string
    {
        return \Tether\Core\Support\Str::pathSafeSlug($value);
    }
}

Now upgrading, forking, or removing the package is a one-file change.

Pattern 4 — Adapter binding for anything on a request path.

For a package at this maturity, adopt behind an internal interface. In an AppServiceProvider:

$this->app->bind(
    \App\Contracts\IdGenerator::class,
    \App\Adapters\TetherIdGenerator::class
);

The adapter is a handful of lines; it converts an irreversible dependency into a swappable one. Do this before the first real call site, not after the third.

Pattern 5 — Config publishing and merge discipline.

Confirm the flow works end to end:

php artisan vendor:publish --tag=tether-config   # confirm the actual tag in the provider
php artisan config:cache                         # then verify nothing breaks

The config-cache step is the one people skip and the one that catches merge bugs. If the provider uses mergeConfigFrom() with a shallow array merge, nested defaults get wiped by a single top-level key in the published file. Test with a partial config to see.

Workflow tips.

  • Pin the exact version in composer.json ("tether/core": "1.2.3", not "^1.2") and review the diff before bumping. With no changelog, every upgrade is a read-the-source exercise.
  • Keep the package out of your host application's autoload path initially — install it into the one internal package that needs it, so the blast radius is a subtree rather than the whole app.
  • If you adopt, write a short internal decision record at that moment: which version, which contracts you depend on, the adapter class, and the exit plan. That document is the thing that makes the fallback cheap in twelve months.

Gotchas and Tips

Overlap with first-party Laravel is the most likely disappointment. The description reads like a thin layer over primitives Laravel already ships — Str, Arr, Collection, mergeConfigFrom, ServiceProvider. If, on reading the source, most of the package turns out to be re-exports or near-copies, the marginal value is low and you should use the framework directly. Verify this before anything else; it may end the evaluation in fifteen minutes, which is a good outcome.

Traits and base classes are not a boundary. "Zero-dependency, framework-agnostic" sounds like insulation but says nothing about coupling to the package. Extending a base provider or use-ing a trait hardwires your code to the author's lifecycle. Contracts are a boundary; traits are not.

Static state and global helpers break test isolation. If helpers are defined as bare functions (function tether_arr_get(...)) or as facades resolving static state, parallel test runs and container swaps get unpredictable. Check whether the helpers are autoloaded via files in composer.json — if they are function-based, they cannot be mocked and cannot be swapped behind your adapter.

Config merge depth. mergeConfigFrom() does a shallow array_merge. Multi-level config merges silently drop defaults when a user publishes a partial file. Test with an incomplete published config; the bug shows up as a null where a default should be, usually in production.

Auto-discovery and the package manifest cache. After installing, php artisan package:discover must list the provider. If it doesn't — wrong extra.laravel.providers key, or a provider that isn't in the manifest — you get silently missing bindings. Run php artisan about and check the cached manifest at bootstrap/cache/packages.php when something "should be" registered but isn't.

No changelog means no upgrade signal. With zero dependents there is no community reporting regressions, and with a small audience there may be no release notes. Pin exact versions, and diff src/ between versions before bumping. A patch-level breaking change is plausible here.

Namespace and facade collisions. Small packages sometimes register facades or aliases with generic names (Core, Support). Check config/app.php aliases and container bindings for collisions with existing packages — a collision manifests as the wrong class resolving, which is a nasty debugging session.

The dependency tree is the actual upgrade risk. Before committing, run:

composer why-not illuminate/support ^12.0   # against your next planned framework version
composer show tether/core --tree

A single stale transitive pin blocks your whole framework upgrade. This is the most common way small packages become expensive, and it is invisible until you try to move Laravel major versions.

Debugging approach. Because documentation is thin, use the provider as the entry point: put dump() / ray() calls in the package's own register() and boot(), or better, subclass the provider in a scratch package and override methods one at a time to see which are called and in what order. That is faster than reading the full source linearly.

Extension points are probably the base class itself. In a package this size, the intended extension mechanism is usually "subclass the provider" or "implement the contract." If you find yourself needing to modify src/ to make it work, that is the point to stop and fork — do not patch via a vcs repository or a patches Composer plugin on something this small; own the fork explicitly or walk away.

Bus factor is one, and it's operational, not technical. Before adopting, calendar a quarterly dependency review. A silent-abandonment scenario has no alert — you only find out when you try to upgrade and nothing works. The adapter boundary from Pattern 4 limits the rewrite; the calendar entry limits the surprise.

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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle