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

Bencode Laravel Package

arokettu/bencode

A PHP library for encoding and decoding Bencode data, commonly used with BitTorrent files. It provides a simple API, stream support, and flexible dictionary handling for reliable serialization in PHP applications.

Deep Wiki
Context7

Getting Started

Install with Composer:

composer require arokettu/bencode

The whole library is essentially a static facade around one class, Arokettu\Bencode\Bencode. Open src/Bencode.php first — it will show you the complete public surface (encode, decode, and the file helpers load / dump in current releases). Everything else in src/ is supporting types and exceptions.

The canonical first use case is a torrent/metainfo file:

use Arokettu\Bencode\Bencode;

$torrent = Bencode::load('/path/to/file.torrent'); // or Bencode::decode(file_get_contents(...))

echo $torrent['announce'];
echo $torrent['info']['name'];
echo strlen($torrent['info']['pieces']); // binary blob, 20 bytes per piece

Round-tripping is symmetric:

$encoded = Bencode::encode(['bar' => 'spam', 'foo' => 42]);
$decoded = Bencode::decode('d3:bar4:spam3:fooi42ee');

Start by testing encode/decode against a real .torrent file and against a hand-written string. If those two work, you understand the package.

Implementation Patterns

Service wrapper around the facade. Don't scatter Bencode::decode() calls through controllers. Wrap it in a small service or value-object hydrator so you control error handling and collection typing in one place:

final class TorrentFile
{
    public static function fromPath(string $path): self
    {
        try {
            $data = Bencode::load($path);
        } catch (\Throwable $e) {
            throw new InvalidTorrentException('Unreadable torrent', 0, $e);
        }
        // validate required keys before touching them
        return new self($data);
    }
}

Always validate the decoded shape. Bencode decoding gives you plain PHP arrays/scalars plus whatever wrapper types the library uses for special cases. Treat the result as untrusted input: check that info, announce, and pieces exist and have the expected types before indexing.

Encode, don't hand-build. When you need to produce bencode (e.g. for a tracker response or a test fixture), build a PHP array and pass it to Bencode::encode(). Hand-writing d...e strings is a reliable source of bugs.

Disambiguate empty collections explicitly. PHP has a single array type, so an empty PHP array is ambiguous. The library exposes wrapper types under Arokettu\Bencode\Types (list/dictionary markers — verify exact class names against your installed version) so you can force le versus de:

use Arokettu\Bencode\Types\ListType;
use Arokettu\Bencode\Types\DictionaryType;

Bencode::encode(new ListType([]));         // "le"
Bencode::encode(new DictionaryType([]));   // "de"

Use these whenever a struct is legitimately empty — tracker responses and torrent metadata frequently have empty dictionaries that must not collapse to le.

Use load/dump for file-oriented workflows, decode/encode for in-memory. load reads the file for you; dump writes it. For streaming or very large payloads, decode once and cache the parsed structure rather than re-parsing per request.

Compute the info-hash from raw bytes, not from re-encoding. The BitTorrent info-hash is the SHA-1 of the exact byte range of the info dictionary as it appears in the file. Locate the 4:info key in the raw file, walk the bencode structure to find its end offset, and hash that slice with sha1($slice, true). Re-encoding via Bencode::encode($decoded['info']) may produce different bytes (key order, integer formatting, wrapper types) and silently give you the wrong hash.

Test against known vectors. Use the classic d3:bar4:spam3:fooi42ee fixture for a smoke test, then add real torrents and a malformed-input case. Bencode has enough edge cases that a two-line test suite will not catch regressions.

Integration tip: keep piece data away from string-mangling. pieces is a concatenation of raw 20-byte SHA-1 digests. Do not pass it through UTF-8-aware functions, json_encode, or anything that re-encodes strings. Slice it with substr($pieces, $i * 20, 20).

Gotchas and Tips

  • Empty array ambiguity is the #1 trap. [] encodes as either le or de depending on the library's heuristic or the wrapper you use. Any protocol struct that can be empty must use the explicit list/dictionary types. This bites tracker responses (d8:intervali1800e5:peers0:e vs le) and torrent metadata.
  • Binary strings are first-class. Bencode strings are byte strings, not UTF-8. Never validate or transform decoded string values with multibyte functions unless you know the semantic (e.g. name is usually UTF-8, pieces is definitely not). Use bin2hex() when debugging binary values.
  • Integers are 64-bit-limited in PHP. Very large bencode integers can overflow native int. Check whether your installed version has a big-integer option/type (the package advertises big-int support) and configure it if you decode arbitrary peer-supplied data. Otherwise a huge integer may throw or be silently coerced.
  • Keys must be strings and are sorted on encode. Bencode dictionaries require byte-wise sorted keys. The encoder handles this, but if you build arrays with integer-like keys, PHP will convert them to int, and the encoder may reject or mishandle them. Keep dictionary keys as strings.
  • Decoding malformed input throws. Wrap decode/load in try/catch around the package's exception namespace (Arokettu\Bencode\Exceptions). Don't assume a false return; check the actual exception class in your version.
  • Trailing garbage. Validate whether your version rejects trailing bytes after the top-level value or ignores them. For security-sensitive parsing (peer messages, tracker responses) you want strict behavior; confirm it explicitly.
  • Re-encoding is not canonicalization. There is no guarantee that encode(decode($x)) === $x byte-for-byte, because decode/encode may normalize integer width, key order, or collection typing. If you need byte-identical output (info-hash, signature verification), work with the original substring.
  • Memory behavior. decode and load materialize the entire structure. Large torrents with many pieces are typically fine, but don't decode in a hot loop — parse once and cache. If you need a true streaming decoder, check whether the current release exposes one before rolling your own.
  • Debugging tip. When a decode fails, hexdump the first few bytes (bin2hex(substr($data, 0, 64))) and compare against the expected tag (d, l, i, or a decimal length prefix). Most failures are an off-by-one in a length prefix or a missing e.
  • Extension points. The Types namespace is the intended extension surface for special values (lists, dictionaries, big integers). Check whether it exposes interfaces you can implement for custom types before reaching for serialize or string hacks.
  • Version pinning. The package is at a 2025 release with a small user base (~30 stars). Pin a specific version in composer.json, read the changelog before upgrading, and keep an integration test with a real torrent on hand — small libraries change their type/option surface between minor versions more often than you'd expect.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
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