BEncoded – handling torrent files in PHP
  1. 1. Preface
    1. 1.1. Demo
    2. 1.2. Usage example
    3. 1.3. Download & license
    4. 1.4. Path referencing
  2. 2. API
    1. 2.1. Static methods
      1. 2.1.1. Decode (str)
      2. 2.1.2. Encode (data, [tempChar])
      3. 2.1.3. TypeOf (value)
      4. 2.1.4. HashOf (nodes, [raw])
        1. 2.1.4.1. Example
    2. 2.2. Instance methods
      1. 2.2.1. __construct ([str])
      2. 2.2.2. TempChar ([new])
        1. 2.2.2.1. Examples
      3. 2.2.3. FromString (str)
      4. 2.2.4. FromFile (file)
      5. 2.2.5. ToString ()
      6. 2.2.6. ToFile (file)
      7. 2.2.7. Root ()
      8. 2.2.8. ValueOf (name)
      9. 2.2.9. Get (name)
      10. 2.2.10. Set (name, value)
      11. 2.2.11. Copy (src, dest)
      12. 2.2.12. Exchange (node_1, node_2)
      13. 2.2.13. Delete (name)
      14. 2.2.14. Export ([name])
      15. 2.2.15. Dump (value, [prefix])
      16. 2.2.16. NewNode (type, name)
      17. 2.2.17. SetEmpty (name)
      18. 2.2.18. Cast (name, asType, [onlyIfNum])
      19. 2.2.19. InfoHash ([raw])
        1. 2.2.19.1. Example
    3. 2.3. Functions
      1. 2.3.1. bdecode (s, [&pos])
      2. 2.3.2. bencode (d, [tempChar])
      3. 2.3.3. bskip (key, &value, [tempChar])

BEncoded is a PHP OOP wrapper for two lightenc.php functions from Theory.org for manipulating bencoded data. It lets you transform bencoded files, retrieve data from them, add some, delete, calculate info hashes and do other things using a simple interface.

Can't want to see? Download now or view its source.

First version on 1 July 2011; last update on 23 January 2012.

Preface

Bencoding is used in BitTorrent files (.torrent) to serialize arrays, integers and strings into a single string that can be stored and safely unserialized later.

References pages:

Demo

Try uploading a bencoded file (such as any .torrent file) in the box below (drag & drop works in Firefox and Chrome). You will see the contents of this file that was retrieved using the BEncoded class.

Drop a .torrent file inside…

Or click here to Browse...

The demo form above uses FileDrop JavaScript uploader.

Usage example

PHP
<?php
require 'bencoded.php';

$be = new BEncoded;

$be->NewNode('dict''child');
$be->Set('child/key''value');
$be->Set('child/_key''_val');
echo 
$be->ToString();           // => d5:childd4:_key4:_val3:key5:valueee

echo $be->Get('child/key');     // => value

var_export$be->Get('child') );
  
// => array ( 'isDct' => true, 'key' => 'value', '_key' => '_val', )
  // isDct is an internal marker used to differentiate between Dictionaries and Lists.

$be->FromFile('1.torrent');
// or this, which is the same:
//$be = new BEncoded(file_get_contents('1.torrent'));
echo $be->InfoHash();           // => DFE39EFFF439B78BB2DD2CAEB69B72ACFE660710

Download & license

BEncoded class is released in public domain – feel free to use it as you wish.
BEncoded uses two functions writen by someone else, which, however, are also in public domain judging from the head comment.

Please give feedback in the comments if you are using this class or need some help. As of today, this class is known to be used at i-Tools.org.

Path referencing

Some functions (Get, Delete, etc.) support referencing of nodes using a special path format. Its syntax is the same as for web paths: /root_node/child/sub/... with some additions:

API

Everything is performed using PHPBEncoded class; one instance represents one bundle of bencoded data.

PHPBEncoded uses exceptions to report errors – currently there's just one exception class, PHPEBEncode, which is just like standard PHP Exception class with one added property – PHPEBEncode->$obj – which contains an instance of PHPBEncoded that has caused the error or PHPnull if a static method has caused it.

Static methods

Decode (str)

PHP
static function Decode($str);

Decodes PHP$str into a dictionary, list, integer or string. Throws EBEncode if couldn't.

Encode (data, [tempChar])

PHP
static function Encode($data$tempChar null);

Turns PHP$data into bencoded string. Throws EBEncode if couldn't.

$tempChar
If given and not PHPnull, all nodes which name start with PHP$tempChar will be excluded from result; see also bskip().

TypeOf (value)

PHP
static function TypeOf($value);

Returns bencoded value type:

HashOf (nodes, [raw])

PHP
static function HashOf($nodes$raw false);

Calculates bencoded hashSHA-1 of the bencoded string. Returns an upper-case string 40 characters long (PHP$raw == false) or raw binary string 20 characters long (PHP$raw == true).

Can be used to calculate torrent info-hashes (hash of torrent's info dictionary) as explained here.

See also PHPBEncoded->InfoHash instance method that can be used to quickly retrieve the hash from currently loaded .torrent file.

Example
PHP
$value = array('str1''str2');
echo 
BEncoded::HashOf($value);

Output:

920EBA4204B15839A1DD57D359C3609ABF4FEF3A

Instance methods

PHPBEncoded class has the following public properties (which are not recommended to be changed directly):

$nodes
Associative array of data that this PHPBEncoded instance owns and operates upon.
$tempChar
Current temporary variable prefix. Read and write using TempChar method.

__construct ([str])

PHP
function __construct($str null);

$str
Optional string to load initial data from (using FromString).

TempChar ([new])

PHP
function TempChar($new null);

If PHP$new is given, sets new PHPBEncoded->$tempChar that is used to omit keys (when exporting using ToString, Encode, bencode and others) from all dictionaries that start with this specific substring. This can be used to create temporary variables or some other internal fields that must not be present in exported (encoded) data.

Returns new temporary character. See also bskip.

Default temp char is PHPnull meaning no keys are excluded. To set temp char to PHPnull after it has been set pass empty string (PHP''), not PHPnull.

Usage:

PHP
echo 'Current: '$be->TempChar();
echo 
'New: ',     $be->TempChar('_');
echo 
'It\'s null again: '$be->TempChar('');
Examples
PHP
$be = new BEncoded;
$be->NewNode('dict''/');
$be->Set('key''value');
$be->Set('_key''_val');
$be->Set('other'1.23);

echo 
'Current temp char: '$be->TempChar(), "\n";
echo 
$be->Export(), "\n\n";

echo 
'New temp char: '$be->TempChar('_'), "\n";
echo 
$be->Export(), "\n\n";

echo 
'No temp char again: '$be->TempChar(''), "\n";
echo 
$be->Export(), "\n\n";

Output:

Current temp char:
key:  'value'
_key:  '_val'
other:  1.2

New temp char: _
key:  'value'
other:  1.2

No temp char again:
key:  'value'
_key:  '_val'
other:  1.2

As you can see, when temp char is «_» all keys starting with underscore were ignored. This works on all children as well:

PHP
$be = new BEncoded();
$be->NewNode('dict''child');
$be->Set('child/key''value');
$be->Set('child/_key''_val');

echo 
'Current temp char: '$be->TempChar(), "\n";
echo 
$be->Export(), "\n\n";

echo 
'New temp char: '$be->TempChar('_'), "\n";
echo 
$be->Export(), "\n\n";

Output:

Current temp char:
child:
  key:  'value'
  _key:  '_val'

New temp char: _
child:
  key:  'value'

FromString (str)

PHP
function FromString($str);

Loads unserialized data from bencoded string PHP;str into the PHPBEncoded instance. Returns nothing. Throws EBEncode if PHP$str decodes to a non-array (neither a list nor a dictionary).

See also FromFile, Decode.

FromFile (file)

PHP
function FromFile($file);

Loads unserialized data from bencoded file PHP;file into the PHPBEncoded instance. Returns nothing. Throws EBEncode if the file couldn't be read.

See also FromString, Decode.

ToString ()

PHP
function ToString();

Serializes data of current PHPBEncoded instance using bencoding and returns it as a string.

ToFile (file)

PHP
function ToFile($file);

Serializes data of current PHPBEncoded instance using bencoding and saves it to file PHP$file. Returns number of bytes written. Throws EBEncode if the file couldn't be written.

Root ()

PHP
function Root();

Returns the root of the current node tree.

Attention: it's only a shallow copy meaning that if you change all but immediate items of the result you'll effectively change the data within the PHPBEncoded instance.

ValueOf (name)

PHP
function ValueOf($name);

Returns value of the node located on given path (PHP$name).

Attention: it's only a shallow copy meaning that if you change all but immediate items of the result you'll effectively change the data within the PHPBEncoded instance.

Get (name)

PHP
function Get($name);

Alias for ValueOf; returns value of the node located on given path (PHP$name).

Attention: it's only a shallow copy meaning that if you change all but immediate items of the result you'll effectively change the data within the PHPBEncoded instance.

Set (name, value)

PHP
function Set($name$value);

Sets the value of node located on given path (PHP$name) to PHP$value. Returns new value.

Copy (src, dest)

PHP
function Copy($src$dest);

Copies the value of node PHP$src to PHP$dest. Returns the copied value of PHP$src.

Attention: a regular PHP shallow copy is performed meaning that all objects and references are copied by reference.

Exchange (node_1, node_2)

PHP
function Exchange($node_1$node_2);

Swaps values of two nodes so that PHP$node_2 acquires the old value of PHP$node_1 and PHP$node_1 acquires the value of PHP$node_2. Returns nothing.

Delete (name)

PHP
function Delete($name);

Deletes node located on given path (PHP$name) by setting it to PHParray(). Returns an empty array (PHParray()).

Export ([name])

PHP
function Export($name '');

Mostly debug method useful for recursively dumping a particular node value. Unlike Dump Export takes node path instead of value to dump. If PHP$name is omitted the entire tree is exported.

Returns the dumped string – see Dump for details.

Note: if you see non-numeric list keys prefixed with # it means this list was Cast from a dictionary. As explained there, it's normal and such keys will be treated as numeric when encoding (using ToString, Encode, bencode or others).

Dump (value, [prefix])

PHP
function Dump($value$prefix '');

Mostly debug method useful for recursively dumping a particular value.

$value
The value (not node path) to dump.
$prefix
Optional string that will be prepended to each line for dict or list items – this is used to indent nested nodes.

See also Export which is the same but takes a node path to dump instead of some value.

Current TempChar is respected and fitting keys are skipped.

Returns a string like this:

announce:  'http://bt4.rutracker.org/ann
announce-list:
  #0
    #0  'http://bt4.rutracker.org/ann'
  #1
    #0  'http://retracker.local/announce'
  #2
    #0  'http://ix4.rutracker.net/ann'
comment:  'http://rutracker.org/forum/viewtopic.php'
creation date:  1278929913.0

Scalar types (integers and strings) are output using var_export() while array types are output recursively with indentation and their keys in the lead (prefixed with # for lists).

The example above was produced from this tree:

PHP
array (
  
'announce' => 'http://bt4.rutracker.org/ann',
  
'announce-list' =>
  array (
    
=>
    array (
      
=> 'http://bt4.rutracker.org/ann',
    ),
    
=>
    array (
      
=> 'http://retracker.local/announce',
    ),
    
=>
    array (
      
=> 'http://ix4.rutracker.net/ann',
    ),
  ),
  
'comment' => 'http://rutracker.org/forum/viewtopic.php',
  
'creation date' => 1278929913,
)

NewNode (type, name)

PHP
function NewNode($type$name);

Creates a new node and returns its value.

$type
Determines the type of the new node; value is one of these:
$name
Node path to put the new value in.

Throws EBEncode if PHP$type is invalid.

SetEmpty (name)

PHP
function SetEmpty($name);

Sets node located on path %%$name)) to «empty» value and returns it. New value depends on previous node type:

integer
Sets to PHP0.
string or undefined
Sets to PHP''.
list
Sets to PHParray() (empty list).
dict
Sets to PHParray() (empty dictionary).

Cast (name, asType, [onlyIfNum])

PHP
function Cast($name$asType$onlyIfNum false) {

Typecasts (converts) node located on path PHP$name)) into new type$asTypePHPDoes nothing if the node is already of type$asType%%.

Returns new value (or the previous value if no casting was done).

Throws EBEncode when:

Convertions:

str → int
Errors if source string is not numeric, otherwise converts it to PHP float.
int → str
Simply converts the number into a string as $value.
int/str → list
Creates an empty list and puts current node value as its first item (PHParray(=> $value)).
int/str → dict
Creates an empty dictionary and puts current node value under numeric key PHP0 (PHParray(=> $value)).
list/dict → int/str
Errors.
list → dict
Converts old list into a dictionary keeping old numeric keys.
dict → list
Removes isDct marker from the value marking it as a list. This means that when encoding (see PHPBEncoded->ToString, PHPBEncoded::Encode, bencode) this value it will gain numeric keys.

Attention: casting dict to list does not mean it loses its named keys – they are retained but ignored. If you cast the same dict into list and then back into dict it will regain its previous key names. If you want to remove the keys you need to do this on your own and then Set the new value to PHP$name.

InfoHash ([raw])

PHP
function InfoHash($raw false)%%

Returns an info hash of a BitTorrent .torrent file. Info hash is simply a 40-character SHA-1 hash of bencoded string of the torrent's info dictionary (that holds all file information).

$raw
If PHPtrue, returns binary SHA-1 hash that is 20 chars long; if PHPfalse the hash is converted to a 40-charactered uppercase string.

Calls PHPBEncoded::HashOf static method on current info node.

Example
PHP
$be = new BEncoded;
$be->FromFile('my.torrent');
echo 
'Torrent infohash: '$be->InfoHash(), "\n";
echo 
'Direct calculation: 'BEncoded::HashOf($be->Get('info'));

Output:

Torrent infohash: DFE39EFFF439B78BB2DD2CAEB69B72ACFE660710
Direct calculation: DFE39EFFF439B78BB2DD2CAEB69B72ACFE660710

Functions

These functions represent the encoding/decoding core; bdecode and bencode were taken from Theory.org and fixed by me (see their description for exact changes).

PHPBEncoded uses these functions in some of its methods so in general you don't need to call them directly but would construct an instance of PHPBEncoded or use its static methods.

bdecode (s, [&pos])

PHP
function bdecode($s, &$pos 0);

Decodes an input string PHP$s starting at offset PHP$pos, updating the latter if it was given.

OOP wrappers are PHPBEncoded::Decode (static) and PHPBEncoded->FromString and other instance methods;

Changes in comparison to the original function:

  1. Fixed reading of integers larger than 2147483647 – instead of truncating to this value they are now converted to floats.

bencode (d, [tempChar])

PHP
function bencode(&$d$tempChar null);

Turns PHP$d into a bencoded string omitting keys starting with PHP$tempChar – see bskip and PHPBEncoded->TempChar. PHP$d is passed by reference only to avoid copying of the input data (that can be large) when PHP passes it to this function.

OOP wrappers are PHPBEncoded::Encode (static) and PHPBEncoded->ToString and other instance methods;

Changes in comparison to the original function:

  1. bskip was added and, consequently, PHPnull values and dict keys starting with PHP$tempChar (see PHPBEncoded->TempChar) are now skippedю
  2. PHPEBEncode exception is now thrown if list contains non-numeric keys.

bskip (key, &value, [tempChar])

PHP
function bskip($key, &$value$tempChar null);

A helper function that returns PHPtrue if PHP$key of the given PHP$value array must be omitted from encoded result or PHPfalse if it should be included. THis is used to:

This function is used in bencode.


This is the end of PHPBEncoded API description. Go to the beginning, download the class or drop a comment below.

Thank you for visiting! ~ Proger_XP