Introduction

In this tutorial, we will be looking at accessing or integrating Bitcoin into applications using php. PHP is a popular general-purpose scripting language that is especially suited to web development.

Before we get started

You will need access to a bitcoin node. We suggest executing against a node configured in regtest mode so that we can have the freedom of playing with various scenarios without having to loose real money. You can however execute these against either the testnet or mainnet configurations.

Note:
If you don't currently have access to a bitcoin development environment set up, dont' worry, we have your back! We've setup a web based mechanism which provisions your very own private session that includes these tools and comes preconfigured with a bitcoin node in regtest mode. https://bitcoindev.network/bitcoin-cli-sandbox/
Alternatively, we have also provided a simple docker container configured in regtest mode that you can install for testing purposes.

gr0kchain:~ $ docker volume create --name=bitcoind-data
gr0kchain:~ $ docker run -v bitcoind-data:/bitcoin --name=bitcoind-node -d \
     -p 18444:18444 \
     -p 127.0.0.1:18332:18332 \
     bitcoindevelopernetwork/bitcoind-regtest

Installing composer and php-bitcoinrpc

You will also need to use PHP 7.1 or higher
For PHP 5.6 and 7.0 use php-bitcoinrpc v2.0.x.

You can confirm your version of php as follows.

$ php -version
PHP 7.1.23 (cli) (built: Nov  7 2018 18:20:35) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies

Finally we will also install the required php packages using composer.

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

To install denpa/php-bitcoinrpc, we can execute the following command.

$ composer.phar require denpa/php-bitcoinrpc

Alternatively, you can the add these lines to composer.json.

"require": {
    "denpa/php-bitcoinrpc": "^2.1"
}

Then execute the following command.

php composer.phar install

Getting started

Once we have all the prerequisites as outlined above, we can proceed to create a new file called index.php with the following.

/**
 * Don't forget to include composer autoloader by uncommenting line below
 * if you've not already done it anywhere else in your project.
 **/
// require 'vendor/autoload.php';

use Denpa\Bitcoin\Client as BitcoinClient;

$bitcoind = new BitcoinClient('http://bitcoin:local321@localhost:18332/');

You can also use an associative array with the following to key properties to define your bitcoind settings.

$bitcoind = new BitcoinClient([
    'scheme'        => 'http',                 // optional, default http
    'host'          => 'localhost',            // optional, default localhost
    'port'          => 18332,                   // optional, default 8332
    'user'          => 'bitcoin',              // required
    'password'      => 'local321',          // required
    'ca'            => '/etc/ssl/ca-cert.pem'  // optional, for use with https scheme
    'preserve_case' => false,                  // optional, send method names as defined instead of lowercasing them
]);

Note
You will need to specify the parameters for your local environment. The above parameters should work with the docker image shown earlier. For the 'ca' parameter you will need to generate your own certificates.

Next up, we add a simple RPC getblockchaininfo call. We are wrapping this in json_encode for the sake of legibility.

$data = $bitcoind->getBlockChainInfo();
echo json_encode($data, JSON_PRETTY_PRINT);

Executing this script should provide you with the JSON RPC output as follows.

$ php ./index.php
{
    "result": {
        "chain": "regtest",
        "blocks": 6341,
        "headers": 6341,
        "bestblockhash": "4fbeaf64826a96acd7779d4b3ad58f16a10cf36465e4c3ed8bb9dadf779daaee",
        "difficulty": 4.656542373906925e-10,
        "mediantime": 1551968875,
        "verificationprogress": 1,
        "chainwork": "000000000000000000000000000000000000000000000000000000000000318c",
        "pruned": false,
        "softforks": [
            {
                "id": "bip34",
                "version": 2,
                "enforce": {
                    "status": true,
                    "found": 1000,
                    "required": 750,
                    "window": 1000
                },
                "reject": {
                    "status": true,
                    "found": 1000,
                    "required": 950,
                    "window": 1000
                }
            },
            {
                "id": "bip66",
                "version": 3,
                "enforce": {
                    "status": true,
                    "found": 1000,
                    "required": 750,
                    "window": 1000
                },
                "reject": {
                    "status": true,
                    "found": 1000,
                    "required": 950,
                    "window": 1000
                }
            },
            {
                "id": "bip65",
                "version": 4,
                "enforce": {
                    "status": true,
                    "found": 1000,
                    "required": 750,
                    "window": 1000
                },
                "reject": {
                    "status": true,
                    "found": 1000,
                    "required": 950,
                    "window": 1000
                }
            }
        ],
        "bip9_softforks": [
            {
                "id": "csv",
                "status": "active"
            }
        ]
    },
    "error": null,
    "id": 0
}

For more methods available consult the Bitcoin Core API Documentation.

Here is an example of how you can query the bitcoin core node for information relating to a block using the getblock RPC call.

/**
 * Get block info.
 */
$block = $bitcoind->getBlock('000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f');

$block('hash')->get();     // 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
$block['height'];          // 0 (array access)
$block->get('tx.0');       // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
$block->count('tx');       // 1
$block->has('version');    // key must exist and CAN NOT be null
$block->exists('version'); // key must exist and CAN be null
$block->contains(0);       // check if response contains value
$block->values();          // array of values
$block->keys();            // array of keys
$block->random(1, 'tx');   // random block txid
$block('tx')->random(2);   // two random block txid's
$block('tx')->first();     // txid of first transaction
$block('tx')->last();      // txid of last transaction

/**
 * Send transaction.
 */
$result = $bitcoind->sendToAddress('mmXgiR6KAhZCyQ8ndr2BCfEq1wNG2UnyG6', 0.1);
$txid = $result->get();

/**
 * Get transaction amount.
 */
$result = $bitcoind->listSinceBlock();
$bitcoin = $result->sum('transactions.*.amount');
$satoshi = \Denpa\Bitcoin\to_satoshi($bitcoin);

To send asynchronous requests, add Async to the method names.

$bitcoind->getBlockAsync(
    '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f',
    function ($response) {
        // success
    },
    function ($exception) {
        // error
    }
);

You can also send requests using the request method.

/**
 * Get block info.
 */
$block = $bitcoind->request('getBlock', '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f');

$block('hash');            // 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
$block['height'];          // 0 (array access)
$block->get('tx.0');       // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
$block->count('tx');       // 1
$block->has('version');    // key must exist and CAN NOT be null
$block->exists('version'); // key must exist and CAN be null
$block->contains(0);       // check if response contains value
$block->values();          // get response values
$block->keys();            // get response keys
$block->first('tx');       // get txid of the first transaction
$block->last('tx');        // get txid of the last transaction
$block->random(1, 'tx');   // get random txid

/**
 * Send transaction.
 */
$result = $bitcoind->request('sendtoaddress', 'mmXgiR6KAhZCyQ8ndr2BCfEq1wNG2UnyG6', 0.06);
$txid = $result->get();

Alternatively there is a requestAsync method for asynchronous calls.

$bitcoind->requestAsync(
    'getBlock',
    '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f',
    function ($response) {
        // success
    },
    function ($exception) {
        // error
    }
);

Multi-Wallet RPC

You can use wallet($name) function to do a Multi-Wallet RPC call:

/**
 * Get wallet2.dat balance.
 */
$balance = $bitcoind->wallet('wallet2.dat')->getbalance();

echo $balance->get(); // 0.10000000

Exceptions

  • Denpa\Bitcoin\Exceptions\BadConfigurationException - thrown on bad client configuration.
  • Denpa\Bitcoin\Exceptions\BadRemoteCallException - thrown on getting error message from daemon.
  • Denpa\Bitcoin\Exceptions\ConnectionException - thrown on daemon connection errors (e. g. timeouts)

Helpers

The Denpa\Bitcoin package also provides the following helpers to assist with value handling.

to_bitcoin()

Converts value in satoshi to bitcoin.

echo Denpa\Bitcoin\to_bitcoin(100000); // 0.00100000

to_satoshi()

Converts value in bitcoin to satoshi.

echo Denpa\Bitcoin\to_satoshi(0.001); // 100000

to_ubtc()

Converts value in bitcoin to ubtc/bits.

echo Denpa\Bitcoin\to_ubtc(0.001); // 1000.0000

to_mbtc()

Converts value in bitcoin to mbtc.

echo Denpa\Bitcoin\to_mbtc(0.001); // 1.0000

to_fixed()

Trims float value to precision without rounding.

echo Denpa\Bitcoin\to_fixed(0.1236, 3); // 0.123

Resources