<?php
/*

This code is application server that allows to display and edit database table
contents with native Borland Delphi components using SOAP.

1. Your must have XML-RPC and DOMXML extesions installed on your Web server
2. Grab latest ADODB library from http://php.weblogs.com and install it.
3. Edit database connection properties with your values

4. Open Borland Delphi 7 IDE and create new application
5. Place TSOAPConnection, TClientDataset, TDatasource, TDBGrid components on to the main form
6. Set TSOAPConnection "URL" property to URL to this file on your server,
   set TSOAPConnection "UseSOAPAdapter" property to false and "Connected" property to true
7. Set TClientDataset "RemoteProvider" property to name of TSOAPConnection component instanse name,
   "ProviderName" to any value, "Active" property to true
8. Set TDataSource "Dataset" property to name of TClientDataset instanse,
   "Active" propery to true
9. Set TDBGrid "DataSource" property to name of TDataSource instance name.
   Your should see now contents of the database table in grid.
10. Compile and run application. 

To apply updates to server your must call ApplyUpdates method of the TClientDataset

This code is ALPHA release.
Any suggestions is wellcome.
This code is under GPL

Author: Denis O.Philippov <denis at atlant dot ru>

*/

require("adodb/adodb.inc.php");

header("Content-type: text/xml");
$ado2midas = array(
    C => "string",
    X => "bin.hex",
    N => "r8",
    I => "i",
    R => "+",
    B => "bin.hex"
);

$ADODB_FETCH_MODE = 2;

$conn = &AdonewConnection("mysql");
$conn -> pconnect($host, $user, $pass, $bd); 

$tablename = "suatabela";
$HTTP_RAW_POST_DATA = str_replace("base64Binary", "string", $HTTP_RAW_POST_DATA);

$s = xmlrpc_server_create();
xmlrpc_server_register_method($s,'AS_GetRecords', 'AS_GetRecords');
xmlrpc_server_register_method($s,'AS_ApplyUpdates', 'AS_ApplyUpdates');

$out=xmlrpc_server_call_method($s, $HTTP_RAW_POST_DATA, $response, $output_options = array(
                       "output_type" => "xml",
                       "verbosity" => "no_white_space",
                       "escaping" => array("markup", "non-ascii", "non-print"),
                       "version" => "soap 1.1",
                       "encoding" => "utf-8"
                      )
);
echo str_replace(array("<item>","</item>"), "", $out);

function nodeDump($node)
{
    $output = array();
    //$output = print_r($node, TRUE);
    //$output = str_replace(")\n", '', $output);        
    //$output .= '    ' . '[tagName] => ' . $node->tagName . " \n";
   
    $numOfAttribs = $node->attributes->length;       
    for ($i = 0; $i < $numOfAttribs; $i++)
    {
        $output[$node->attributes->item($i)->nodeName] = $node->attributes->item($i)->nodeValue;
    }
   
    //$output .= '    [nodeValue] => ' .  $node->nodeValue;   
    //$output .= ')';
	//echo $output;
    return $output;
}


function HandleXmlError($errno, $errstr, $errfile, $errline)
{
    if ($errno==E_WARNING && (substr_count($errstr,"DOMDocument::loadXML()")>0))
    {
        throw new DOMException($errstr);
    }
    else
        return false;
}

function XmlLoader($strXml)
{
    set_error_handler('HandleXmlError');
    $dom = new DOMDocument();
    $dom->loadXml($strXml);   
    restore_error_handler();
    return $dom;
 }

function getdatapacket($sql){
    global $conn, $ado2midas;
    if($rs = ($conn ->execute($sql))){
        $doc = new DomDocument('<?xml version="1.0" encoding="UTF-8" standalone="yes"?><DATAPACKET Version="2.0">');
        //$datapacket = $doc->document_element();
        $metadata = $doc->createElement("METADATA");
        $metadata = $doc->appendChild($metadata);
        $fields = $doc->createElement("FIELDS");
        $fields = $metadata->appendChild($fields);
        $fieldcount = $rs->FieldCount( );
        for($i=0; $i < $fieldcount; $i++) {
            $_fld = $rs -> Fetchfield($i);
            $type = $rs -> metatype($_fld ->type);
            $types[$i] = $_fld;
            $metatype[$i] = $type;
            $field = $doc -> createElement("FIELD");
            $field = $fields -> appendChild($field);
            $field-> setAttribute("attrname", $_fld -> name);
            $field-> setAttribute("fieldtype", $ado2midas[$type]);
            if($type == "C" || $type == "X" || $type == "B"){
                $field-> setAttribute("WIDTH", $_fld -> max_length);
            }
            if($type == "X"){
                $field-> setAttribute("SUBTYPE", "Text");
            }elseif($type == "B"){
                $field-> setAttribute("SUBTYPE", "Graphics");
            }
        }
        $params = $doc -> createElement("PARAMS");
        $params = $metadata -> appendChild($params);
        $rowdata = $doc -> createElement("ROWDATA");
        $rowdata = $doc->appendChild($rowdata);
        while($rw = $rs -> fetchrow()){
            $row = $doc -> createElement("ROW");
            $row = $rowdata -> appendChild($row);
            for($i=0; $i< $fieldcount; $i++){
                if($metatype[$i] == "X "|| $metatype[$i] == "B"){
                    $row -> setAttribute($types[$i] -> name, base64_encode($rw[$types[$i] -> name]));
                }else{
                    $row -> setAttribute($types[$i] -> name, iconv("windows-1251", "UTF-8", $rw[$types[$i] -> name]));
                }
            }
        }
        $ret->RecsOut = $rs -> RecordCount( );
        $ret->return =     str_replace('<DATAPACKET Version="2.0">\'?>','<DATAPACKET Version="2.0">',$doc->SaveXml()).'</DATAPACKET>';
		//echo str_replace('</DATAPACKET>','',$doc->SaveXml()).'</DATAPACKET>';
        return $ret;
    }else{
        return false;
    }
}

function AS_GetRecords($method, $params){
    global $conn, $tablename;
    $providername = $params[ProviderName];
    $count = $params["Count"];
    $options = $params[Option];
    $commandtext = $params[CommandText];
    $params = $params[Params];
    $ownerdata = $params[OwnerData];
    $_ret = getdatapacket("select * from $tablename  ");
    xmlrpc_set_type(&$_ret->return, "base64");
    return array( "Params" => $params, "OwnerData" => $ownerdata, 
    "RecsOut" => $_ret -> RecsOut, "return" => $_ret->return);
}

function AS_ApplyUpdates($method, $params){
    global $conn, $ado2midas, $tablename;
    $ProviderName = &$params[ProviderName];
    $Delta = &$params[Delta];
    $MaxErrors = &$params[MaxErrors];
    $OwnerData = &$params[OwnerData];
    $errorcount = 0;
    $Delta = base64_decode($Delta);
    $Delta = str_replace('<?xml version="1.0" standalone="yes"?>',
                '<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>',
                $Delta);
					
    $doc = new DOMDocument;
	$dom->preserveWhiteSpace = FALSE;
	$doc->LoadXML($Delta);
    $meta = $doc->getElementsByTagName("FIELD");

    foreach ($meta as $defs){
        $defs = nodeDump($defs);
        while(list($key, $value) = each($defs)){
            $_defs[$key]= value;
        }
        $fields[$_defs["attrname"]]["type"]=$_defs["fieldtype"];
    }
    $rows = $doc->getElementsByTagName("ROW");
	foreach ($rows as $__attr){
	   $newsrows[] = $__attr;
	}
	$rows = $newsrows;
	for ($i=0;$i < sizeof($rows);$i++){
        unset($attr);
		$__attr = nodeDump($rows[$i]);
		$_SESSION['__attr'] = $__attr;
        while(list($key, $value) = each($__attr)){
            $attr[$key]=$value;
        }
        unset($__attr);
        if($attr["RowState"] == 2){ //deleted
            reset($attr);
            unset($where);
            while(list($k, $a) = each($attr)){
                if($k != "RowState" && strlen($a)>0){
                    if($fields[$k]["type"] == "bin.hex"){
                        $where[]="$k = '".base64_decode($a)."'";
                    }else{
                        $where[]="$k = '$a'";
                    }
                }
            }
            if(sizeof($where) > 0){
                $sql = "delete from $tablename where ".join(" and ", $where);
                if(!$conn -> execute($sql)){
                    $errorcount ++;
                }
            }
        }elseif($attr["RowState"] == 4){ //inserted record
            reset($attr);
            unset($line);
            while(list($k, $a) = each($attr)){
                if(strlen($a) >0){
                    if($k != "RowState"){
                        if($fields[$k]["type"] == "bin.hex"){
                            $line[]="'".base64_decode($a)."'";
                        }else{
                            $line[]="'$a'";
                        }
                        $_fields[]=$k;
                    }
                }
            }
            if(sizeof($line) > 0){
                $sql = "insert into $tablename(".join(", ", $_fields).") values (".join(", ", $line).")";
                if(!$conn -> execute($sql)){
                    $errorcount ++;
                }
            }
        }elseif($attr["RowState"] == 1){ //updated record
            $_attr1 = nodeDump($rows[$i+1]);
            unset($_attr);
            while(list($k, $v) = each($_attr1)){
                $_attr[$k]=$v;
            }
            unset($_attr1);
            reset($attr);
            unset($where);
            while(list($k, $a) = each($attr)){
                if(strlen($a) >0){
                    if(!($k == "RowState" || $fields[$k]["type"] == "bin.hex" ||
                     $fields[$k]["type"] == "r8")){
                        $where[]="$k = '".utf8_decode($a)."'";
                    }
                }
            }
            reset($_attr);
            unset($new);
            while(list($k, $a) = each($_attr)){
                if($k != "RowState"){
                    if($fields[$k]["type"] == "bin.hex"){
                        $new[]="$k = '".base64_decode($a)."'";
                    }else{
                        $new[]="$k = '".utf8_decode($a)."'";
                    }
                }
            }
            if(sizeof($where) > 0 && sizeof($new) > 0){
                $sql = "update $tablename set ".join(", ", $new)." where ".join(" and ", $where);
                if(!$conn -> execute($sql)){
                    $errorcount ++;
                }
            }
        }
    }
    //$ret="";
    xmlrpc_set_type($ret, "base64");
    return array(ErrorCount => $errorcount, ownerdata => $ownerdata, "return" => $ret);
}
?>