Technical Documentation

Introduction

Configuration

<?php

define( "DB_USERNAME", "public" );
define( "DB_PASSPORT", "public" );
define(    "SSL_BASE", "/local/settings/services/webapibridge/ssl/" );

Main

<?php

main();

function main()
{
    header( "Content-type:text/plain" );

    $db_domain  = DetermineDatabaseDomain();
    $db_info    = LookupDatabaseInfo     ( $db_domain );
    $db_version = LookupDatabaseVersion  ( $db_info   );
    $sp_name    = GenerateSPName();
    $parameters = LookupParameters       ( $db_info, $db_version, $sp_name );
    $results    = CallStoredProcedure    ( $db_info, $db_version, $sp_name, $parameters );
    $json       = TranslateToJSON        ( $results );

    echo $json;
}

Determine database domain

In order to allow a Web API Bridge to act as an API service for arbitrary domains, the hostname (or IP address) of the database server to be connected to, as well as the name of the database instance to be interrogated, is configured using DNS text records.

The prefix 'db.' is added to the API domain called to create the domain that is queried -- for example, 'api.example.com' would become 'db.api.example.com'. If a local domain is called, such as 'api.example.com.local', the prefix 'local-db.' is added to the API domain called, and the '.local' suffix is removed, to create the domain that is queried -- for example, 'api.example.com.local' would become 'local-db.api.example.com'.

function DetermineDatabaseDomain()
{
    $api_domain = $_SERVER["SERVER_NAME"];

    if ( false != strpos( $api_domain, ".local" ) )
    {
        $db_domain = "local-db." . str_replace( '.local', '', $api_domain );
    }
    else
    {
        $db_domain = "db." . $api_domain;
    }

    return $db_domain;
}

Lookup database info

function LookupDatabaseInfo( $db_domain )
{
    $db_info = (object) array( "dbname" => null, "hosts" => null );

    error_log( $db_domain );

    if ( apcu_exists( $db_domain ) )
    {
        $db_info = apcu_fetch( $db_domain );

        error_log( "Cached: $db_domain" );
    }
    else
    {
        $records = dns_get_record( $db_domain, DNS_TXT );
        $n       = count( $records );

        if ( 0 < $n )
        {
            $db_info = ExtractDBInfoFromDNSRecords( $records );
        }
        else
        {
            $db_info = ExtractDBInfoFromDBDomain( $db_domain );
        }

        apcu_add( $db_domain, $db_info );

        error_log( "Caching: $db_domain; returned $n records" );
    }

    return $db_info;
}
function ExtractDBInfoFromDNSRecords( $records )
{
    $dbname = null;
    $hosts  = null;

    foreach ( $records as $record )
    {
        $host    = array_key_exists(    "host", $record ) ? $record[   "host"] : "";
        $class   = array_key_exists(   "class", $record ) ? $record[  "class"] : "";
        $ttl     = array_key_exists(     "ttl", $record ) ? $record[    "ttl"] : "";
        $type    = array_key_exists(    "type", $record ) ? $record[   "type"] : "";
        $txt     = array_key_exists(     "txt", $record ) ? $record[    "txt"] : "";

        $bits = explode( "@", $txt );
        
        switch ( count( $bits ) )
        {
        case 2:
            $dbname = $bits[0];
            $hosts  = explode( ",", $bits[1] );
            break;
        
        case 1:
            $hosts  = explode( ",", $bits[0] );
            break;
        }
    }

    return (object) array( "dbname" => $dbname, "hosts" => $hosts );
}
function ExtractDBInfoFromDBDomain( $db_domain )
{
    $truncated = str_replace( "www.", "", $db_domain );
    $truncated = str_replace( "api-", "", $truncated );
    $truncated = str_replace( "api.", "", $truncated );
    $bits      = explode( ".", $truncated );

    $n = count( $bits );

    for ( $i = 1; $i < $n; $i++ )
    {
        if ( ! IsTLD( $bits[$i], $i ) )
        {
            $dbname = $bits[$i];
            break;
        }
    }

    return (object) array( "dbname" => $dbname, "hosts" => "localhost" );
}
function IsTLD( $domain_part, $level = 1 )
{
	switch ( $level )
	{
	case "1":
		switch ( $domain_part )
		{
		case "au":
		case "com":
		case "net";
		case "org";
		case "co":
		case "info":
		case "xyz":
		case "local":
			return true;
			
		default:
			return false;
		}
		break;

	case "2":
		switch ( $domain_part )
		{
		case "com":
		case "net";
		case "org";
		case "id":
		case "conf":
			return true;
			
		default:
			return false;
		}
		break;
	
	default:
		return false;
	}
}

Determine database version

function LookupDatabaseVersion( $db_info )
{
	$db_version = "";

    error_log( $db_info->dbname );
    error_log( var_export( $db_info->hosts, true ) );


    define( "DB_HOSTNAME", $db_info->hosts[0] );
    define( "DB",          $db_info->dbname   );

    $result = \replicantdb\ReplicantDB::CallInfo( "SHOW DATABASES", null );

    if ( "ERROR" == $result->status )
    {
        $hostname = $result->hostname;

        error_log( "Could not connect to database on $hostname: " . $result->error );
    }
    else
    if ( ("OK" == $result->status) && count( $result->results ) )
    {
        $db_name = "";
    
        foreach( $result->results as $tuple )
        {
            $tmp = array_get( $tuple, "Database" );

            //error_log( "Checking: $tmp" );

            if ( string_startsWith( $tmp, DB ) )
            {
                $db_name = $tmp;
            }
        }
        $db_version = $db_name ? str_replace( DB, "", $db_name ) : $db_version;

        if ( $db_name )
        {
            if ( defined( "VERBOSE" ) && VERBOSE ) error_log( "Found: $db_name" );
        }
        else
        {
            error_log( "Could not find database for: " . DB );
        }
    }

    return $db_version;
}

Generate stored procedure name

function GenerateSPName()
{
}

Lookup parameters

function LookupParameters( $db_info, $db_version, $sp_name )
{
}

Call stored procedure

function CallStoredProcedure( $db_info, $db_version, $sp_name, $parameters )
{
}

Translate to JSON

function TranslateToJSON( $results )
{
}