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

Base62 Laravel Package

tuupola/base62

PHP library for base62 encoding and decoding, handy for short URLs and compact IDs. It encodes integers and binary data using GMP, BCMath, or a pure PHP fallback, so it works without extensions, and also supports base36, base58, and other bases through a simple, consistent API.

View on GitHub
Deep Wiki
Context7

Getting Started

Install with Composer — the package has no Laravel service provider, so it's a plain library you wire up yourself:

composer require tuupola/base62

The entry point is Tuupola\Base62. The constructor accepts an options array where characters sets the alphabet (default is 0-9A-Za-z) and encoder selects the backend (gmp, bcmath, php, or integer).

use Tuupola\Base62;

$base62 = new Base62();                 // defaults to GMP if the extension is present
$encoded = $base62->encode(123456789);  // "8m0Kx"
$decoded = $base62->decode('8m0Kx');    // "123456789"

Look first at the README's encoder matrix — the correct starting choice is dictated by whether your data is an integer (IDs, counters) or arbitrary binary (UUIDs, hashes, blobs). For a Laravel app the most common first use case is obfuscating auto-increment IDs in URLs: encode the model key on the way out, decode on the way in. Register the instance once so every controller, model accessor, and job uses the same alphabet.

Implementation Patterns

Singleton binding in a service provider. Base62 is stateless, so register it once and inject it rather than new-ing it everywhere:

$this->app->singleton(Base62::class, fn () => new Base62([
    'characters' => '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
    'encoder'    => Base62::GMP,
]));

Route model binding via a custom resolver. Keep numeric IDs in the database and encode only at the boundary. A resolveRouteBinding override on the model (or a getRouteKey/resolveRouteKey trait) gives you short URLs without schema changes:

public function getRouteKey() { return app(Base62::class)->encode($this->getKey()); }
public function resolveRouteBinding($value, $field = null) {
    $id = app(Base62::class)->decode($value);
    return $this->where($this->getKeyName(), $id)->firstOrFail();
}

Binary-safe tokens and short codes. When you need compact opaque values (invite codes, API tokens, share links), feed random_bytes() output into encode() — Base62 handles arbitrary byte strings, not just integers, so 16 raw bytes become ~22 chars. Decode returns the original byte string; store the hash of the decoded value if you need lookup, since encoded output isn't searchable against a numeric index.

Encoding before insert vs. after fetch. Two viable workflows: (a) store the raw integer and encode on read — flexible, no migration; (b) store the base62 string in a dedicated public_id column with a unique index — good when the value is exposed externally and you want to query by it directly. Option (b) needs the column to be VARBINARY/utf8mb4_bin because Base62 is case-sensitive (see Gotchas).

API resources. Centralize encoding in an ApiResource or a Base62Cast custom cast so responses are consistent and you never leak raw IDs. A cast that encodes on set/get lets models expose public_id transparently.

Custom alphabets. Pass characters to reorder or exclude confusable glyphs (0/O, 1/l) for human-typed codes — just ensure it's exactly 62 unique characters and consistent across every instance in the app, or decodes will silently produce garbage.

Gotchas and Tips

  • Case sensitivity everywhere. Base62 distinguishes a from A. MySQL's default utf8mb4_unicode_ci collation treats them as equal, so WHERE public_id = 'aB3' may match Ab3. Use a _bin collation or a BINARY column, and remember Laravel's where() inherits the column collation.
  • Integer mode vs. binary mode. encode(123) treats the input as a number only if you pass an int and use an integer-capable encoder; passing a numeric string like "00123" can be interpreted as binary data. Be explicit — cast to int for IDs, pass raw bytes for binary payloads. Leading zero bytes are the classic failure: binary encoding preserves them, integer encoding does not, so round-tripping "\x00\x01" as binary is safe but converting via integer is not.
  • Encoder backend matters. gmp is fastest and handles big values; bcmath is a fallback; the pure-PHP/integer encoders are limited by PHP_INT_MAX (~9.2e18) and will break on large byte strings. Pick one and stick to it — encoded strings are not interchangeable across encoders.
  • Decode returns a string, always. Even when you encoded an int, decode() gives you a numeric string. Cast before using in arithmetic or whereIn on integer columns, or Eloquent will happily compare against a string key.
  • No padding, no length guarantees. Encoded length varies. If you need fixed-width codes (e.g., 6-char coupons), left-pad the decoded value or check length on input — the library won't enforce it.
  • Don't sort by encoded values. Lexical order of Base62 output does not match numeric order; pagination or orderBy on an encoded column will look scrambled. Sort by the underlying integer.
  • Extension detection. The default encoder probes for GMP. In stripped-down containers (Alpine, minimal php:fpm) it silently falls back, which can change encoded output for the same input. Explicitly set encoder to remove the ambiguity, and confirm ext-gmp is installed in composer.json's require/suggest and your Dockerfile.
  • Debugging round-trips. If decode(encode($x)) !== $x, the usual culprits are: mismatched alphabets between encode/decode instances, a value that exceeded the integer backend's range, or URL/browser normalization of the code (don't strip or urlencode it — Base62 is already URL-safe). Write a quick round-trip test with your exact encoder and alphabet; it catches most issues in seconds.
  • Extension point. Subclass Base62 or wrap it in your own ShortId service if you need validation, length checks, or fixed alphabets — the base class has no validation hooks of its own.
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.
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
equipc/laravel-email-database-log
adedsec/nova-translated-labels