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

Base58 Laravel Package

stephenhill/base58

Base58 encoding and decoding for PHP with support for the Bitcoin and Flickr alphabets, arbitrary-precision math via GMP or BCMath, and a Laravel service provider for quick integration into your application.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the library with Composer:

composer require stephenhill/base58

Check which math backend you have available. The library can use GMP, BCMath, or a pure-PHP fallback, but GMP is significantly faster:

php -m | grep -E 'gmp|bcmath'

Minimal usage:

use StephenHill\Base58;

$base58 = new Base58(); // default Bitcoin alphabet, auto-selects GMP/BCMath/native math

$encoded = $base58->encode('Hello World!');
$decoded = $base58->decode($encoded);

var_dump($encoded); // "2NEpo7TZRRrLZSi2U"
var_dump($decoded); // "Hello World!"

As of v2.1.0 there are two static constructors, which remove most of the manual wiring:

use StephenHill\Base58;

$base58 = Base58::create();      // new instance (accepts the same args as the constructor)
$shared = Base58::singleton();   // one shared instance per process

The default alphabet is the Bitcoin alphabet:

123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz

In a Laravel app, bind a configured singleton in AppServiceProvider. With v2.1.0 this is a one-liner if you are happy with the defaults:

use StephenHill\Base58;

public function register(): void
{
    $this->app->singleton(Base58::class, fn () => Base58::singleton());
}

If you need a configured alphabet or a forced adapter, keep the explicit construction (see below) and wrap it in create():

use StephenHill\Base58;
use StephenHill\BCMathAdapter;
use StephenHill\GMPMathAdapter;
use StephenHill\NativeMathAdapter;

public function register(): void
{
    $this->app->singleton(Base58::class, function () {
        $adapter = extension_loaded('gmp')
            ? new GMPMathAdapter()
            : (extension_loaded('bcmath')
                ? new BCMathAdapter()
                : new NativeMathAdapter());

        return Base58::create(config('base58.alphabet'), $adapter);
    });
}

Where to look first: the StephenHill\Base58 class itself is small; the important extension points are the constructor alphabet argument, the new create()/singleton() static helpers, and the MathAdapterInterface implementations. The test suite is the fastest place to find known-answer vectors for encode/decode behavior.

First useful day-to-day use case: turning a UUID or 16 random bytes into a shorter, copy-paste-friendly public identifier:

$publicId = app(Base58::class)->encode(random_bytes(16)); // roughly 21–22 characters

Implementation Patterns

Configuration and alphabet selection. Keep the alphabet in config so every service uses the same one:

// config/base58.php
return [
    'alphabet' => env(
        'BASE58_ALPHABET',
        '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
    ),
];

Use one alphabet per identifier namespace. If you use the Flickr alphabet somewhere, treat those IDs as a different type from Bitcoin-alphabet IDs.

$flickr = Base58::create('123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ');

Choose create() vs singleton() deliberately. Base58::create() gives you an isolated instance — use it in tests, jobs, and anywhere you want a specific alphabet. Base58::singleton() gives you one shared, already-configured instance for the whole process; it is the right default for a stateless codec that only ever uses one alphabet. Do not mix the two for different alphabets in the same process.

Encode and decode UUIDs. Pack the canonical UUID hex into 16 bytes, then encode. Keep the byte order consistent.

function encodeUuid(Base58 $base58, string $uuid): string
{
    return $base58->encode(hex2bin(str_replace('-', '', $uuid)));
}

function decodeUuid(Base58 $base58, string $encoded): string
{
    $hex = bin2hex($base58->decode($encoded));

    return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split($hex, 4));
}

Validate user input. Do not use a loose regex if you can call decode() and catch the library exception:

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use StephenHill\Base58;

class Base58String implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if (!is_string($value) || $value === '') {
            $fail('The :attribute must be a non-empty Base58 string.');
            return;
        }

        try {
            app(Base58::class)->decode($value);
        } catch (\InvalidArgumentException) {
            $fail('The :attribute is not valid Base58.');
        }
    }
}

Persist encoded IDs deliberately. If the database column stores the Base58 string, no cast is needed; encode at the service/API boundary. If the column stores raw bytes and you want Base58 in and out of Eloquent, a custom cast is reasonable:

public function set($model, $key, $value, $attributes): array
{
    return [$key => $value === null ? null : app(Base58::class)->encode($value)];
}

public function get($model, $key, $value, $attributes): ?string
{
    return $value === null ? null : app(Base58::class)->decode($value);
}

Route model binding. If the model stores an encoded public_id, return that column from getRouteKeyName(). If it stores raw binary, override resolveRouteBinding() to decode the route parameter first.

Base58Check if you need Bitcoin compatibility. This package performs raw Base58 only. Bitcoin addresses require a version byte plus a 4-byte checksum:

$payload = "\x00" . $hash160;
$checksum = substr(hash('sha256', hash('sha256', $payload, true), true), 0, 4);
$address = $base58->encode($payload . $checksum);

Adapter choice. Force the GMP adapter in production when available. The native adapter is fine for occasional short IDs but will be noticeably slower for bulk work. Do not instantiate GMPMathAdapter on a host without ext-gmp. Note that singleton() and a bare create() will auto-select; only pass an adapter explicitly when you need to pin the backend.

API resources. Encode once at the boundary and expose a stable string in JSON resources. Avoid making every controller remember the alphabet.

Gotchas and Tips

  • Base58 is encoding, not encryption, hashing, or compression. It is fully reversible; never use it to hide secrets.
  • The library does not implement Base58Check. Raw Base58 has no checksum, so a typo that still uses valid alphabet characters can decode successfully to the wrong bytes. Add your own checksum or use Base58Check for user-facing codes.
  • Base58 is case-sensitive. MySQL’s default utf8mb4_unicode_ci collation is case-insensitive, which can produce wrong lookups and uniqueness conflicts. Store IDs in a case-sensitive column (utf8mb4_bin, VARBINARY, or equivalent) or compare with binary semantics.
  • decode() throws InvalidArgumentException for characters outside the alphabet. Wrap untrusted input in try/catch; otherwise malformed URL segments become 500 errors.
  • Custom alphabets must be exactly 58 unique characters. The constructor validates this, but keep alphabets in config rather than scattered through the codebase.
  • decode() returns raw binary. var_dump() output is not useful; use bin2hex(), unpack(), or compare against expected byte strings.
  • Leading zero bytes are significant. The Bitcoin convention maps each leading \x00 to a leading 1; include a known vector in your tests if you persist fixed-width values.
  • Encoded length varies slightly for fixed-size random inputs. Sixteen random bytes usually produce 21–22 Base58 characters, not always exactly 22. Size database columns with headroom and avoid assuming fixed length.
  • Empty input normally returns an empty string. Treat that as invalid at the application layer if an empty ID has no meaning.
  • Base58 expands data by roughly 1.37× and is much slower than Base64. Use it for identifiers, keys, and human-facing tokens, not large blobs or file payloads.
  • Base58 output is alphanumeric and safe in URLs, but it is not suitable for case-insensitive contexts such as hostnames or systems that normalize case.
  • Base58::singleton() is process-wide, not container-scoped. That is fine because the object is stateless, but it bypasses Laravel’s container: tests that rebind Base58::class will not affect code that calls Base58::singleton() directly, and a multi-tenant app that varies the alphabet per tenant should not use it. Prefer the container binding (app(Base58::class)) inside application code and reserve the static helpers for the service provider.
  • Singleton() state persists across Octane requests and long-running queue workers. The codec carries no request data, so this is safe, but do not add mutable per-request state to anything you obtain from it.
  • The constructor/create() signature has evolved (v2.1.0 added the statics). Check the released class signature before assuming argument order for alphabet and adapter, and pin a minor version.
  • Pin a version and keep known-answer tests for both encode and decode. This is a small library with a limited ecosystem; upgrade confidence depends on your own vectors, especially around leading zeros and adapter behavior.
  • Generate identifiers with random_bytes(), not uniqid() or rand(). Base58 changes representation only; it adds no entropy.
  • When debugging, log a hash of malformed input rather than the raw value if the encoded data may be sensitive. The exception message tells you the input was invalid but may not identify the exact offending character.
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.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
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