We have changed from a Windows 10 (XAMPP, PHP 7.4) web server to an Ubuntu Apache2 PHP 7.4 ODBC 17 server without making any code changes.
However, when we run the same script, we receive a different response in the RestAPI.
Windows: (how we want it to be)
"data": [
{
"ID": 1,
"MSK_Version": 3.09,
"Materialstamm": "2022-01-18 11:33:07",
"Main_Sync": "2022-06-15 08:45:51",
"DB_Version": "2018-10-31 16:21:09",
"Kill_System": null,
"Broadcast": "Neue Version verfügbar.",
"Adressen": "2019-03-09 15:26:10",
"Projekte": "2019-07-11 16:58:44",
"Dokumente_Historie": 10,
"Abfrage_Timeout": null,
"Verbindungstimeout": 20000,
"Abfrageintervall": 5
}
],
"success": true
}
Ubuntu:
{
"data": [
{
"ID": 1,
"MSK_Version": "3.09",
"Materialstamm": "2022-01-18 11:33:07",
"Main_Sync": "2022-06-15 08:45:51",
"DB_Version": "2018-10-31 16:21:09",
"Kill_System": "",
"Broadcast": "Neue Version verfügbar.",
"Adressen": "2019-03-09 15:26:10",
"Projekte": "2019-07-11 16:58:44",
"Dokumente_Historie": 10,
"Abfrage_Timeout": null,
"Verbindungstimeout": 20000,
"Abfrageintervall": 5
}
],
"success": true
}
The locale settings for PHP are set to "de_DE.utf-8".
We have set all locale settings from ubuntu to "de_DE.utf-8".
Also have we try to run the same on Debian with ODBC18 – PHP8.0 with the same result.
On all XAMPP (Windows) servers it runs correctly.
EDIT
CODE:
API CLASS
<?php
class API {
public static function sendError( $text, $responseCode = 500 ) {
API::sendResponse( false, array(
"error" => $text
), $responseCode );
}
public static function sendResponse( $success, $data, $responseCode = 0 ) {
if( !$responseCode ) {
$responseCode = $success ? 200 : 500;
}
http_response_code( $responseCode );
$data["success"] = $success;
echo my_json_encode( $data );
exit();
}
// read one record from the result
public static function readRecord( $result, $pSet ) {
$data = $result->fetchAssoc();
if( !$data ) {
return null;
}
foreach( array_keys( $data ) as $f ) {
if( IsBinaryType( $pSet->getFieldType( $f ) ) ) {
$data[ $f ] = base64_encode( $data[ $f ] );
}
}
return $data;
}
// read result into array of records
public static function readResult( $result, $pSet, $recordLimit = 0 ) {
$ret = array();
while( ( !$recordLimit || count( $ret ) < $recordLimit ) && ( $data = API::readRecord( $result, $pSet ) ) ) {
$ret[] = $data;
}
return $ret;
}
public static function login() {
if( !Security::hasLogin() ) {
return true;
}
$authType = GetGlobalData("restAuth");
if( $authType == REST_BASIC ) {
// Authorization: Basic <base64_encode(username:password)>
$username = "";
$password = "";
$username = $_SERVER["PHP_AUTH_USER"];
$password = $_SERVER["PHP_AUTH_PW"];
if( !$username ) {
$loginHeader = getHttpHeader('Authorization') . "";
if( substr( $loginHeader, 0, 6 ) !== 'Basic ' ) {
header( 'WWW-Authenticate: Basic realm="REST API"');
return false;
}
$token = base64_decode( substr( $loginHeader, 6) );
$colonPos = strpos( $token, ':' );
if( $colonPos === false ) {
return false;
}
$username = substr( $token, 0, $colonPos );
$password = substr( $token, $colonPos + 1 );
}
return Security::login( $username, $password, false, true );
}
if ( $authType == REST_APIKEY ) {
$APIkey = "";
if( isset( $_SERVER["HTTP_X_AUTH_TOKEN"] ) )
$APIkey = $_SERVER["HTTP_X_AUTH_TOKEN"];
else if( isset( $_GET["apikey"] ) )
$APIkey = $_GET["apikey"];
else if( isset( $_POST["apikey"] ) )
$APIkey = $_POST["apikey"];
if( !strlen( $APIkey ) )
return false;
if( Security::hardcodedLogin() ) {
if( GetGlobalData("APIkey", "") == $APIkey ) {
Security::createHardcodedSession();
return true;
}
return false;
}
$dataSource = getLoginDataSource();
$dc = new DsCommand();
$dc->filter = DataCondition::FieldEquals( GetGlobalData("APIkeyField"), $APIkey );
$rs = $dataSource->getSingle( $dc );
if( !$rs )
return false;
$loginSet = ProjectSettings::getForLogin();
$cipherer = RunnerCipherer::getForLogin( $loginSet );
$userData = $cipherer->DecryptFetchedArray( $rs->fetchAssoc() );
return Security::login( $userData[ Security::usernameField() ], $userData[ Security::passwordField() ], true, true );
}
return false;
}
public static function keysFromRequest( $pSet ) {
$keys = array();
foreach( $pSet->getTableKeys() as $i => $k ) {
$keys[ $k ] = postvalue( "editid" . ( $i + 1 ) );
}
return $keys;
}
public static function valuesFromRequest( $pSet ) {
$values = array();
foreach( $pSet->getFieldsList() as $f ) {
if( isset( $_POST[ $f ] ) ) {
$values[ $f ] = $_POST[ $f ];
if( IsBinaryType( $pSet->getFieldType( $f ) ) ) {
$values[ $f ] = base64_decode( $values[ $f ] );
}
}
}
return $values;
}
}
?>
API
<?php
@ini_set("display_errors","1");
$restApiCall = true;
require_once("../include/dbcommon.php");
add_nocache_headers();
require_once(getabspath( "api/api.php"));
if( !GetGlobalData("restCreate") ) {
return;
}
header("Content-Type: application/json");
if( !API::login() ) {
API::sendError( 'Access denied', 401 );
}
// dont' remember anything
session_destroy();
$table = findTable( postvalue("table") );
if( !$table ) {
API::sendError( 'Unknown table name', 403 );
}
$pSet = new ProjectSettings( $table );
$eventsObject = &getEventObject( $table );
$cipherer = new RunnerCipherer( $table, $pSet );
$action = postvalue("action");
if( $action === "list" ) {
if( !$pSet->pageTypeAvailable("list") ) {
API::sendError( "operation not supported" );
}
if( !Security::userCan( "S", $table ) ) {
API::sendError( "operation not allowed" );
}
$dataSource = getDataSource( $table, $pSet );
$srchObj = SearchClause::getSearchObject( $table );
$dc = new DsCommand();
$dc->filter = DataCondition::_And( array(
Security::SelectCondition( "S", $pSet ),
$srchObj->getSearchDataCondition()
));
$order = postvalue("orderby");
if( $order ) {
$orderFields = explode( ";", $order );
$projectFields = $pSet->getFieldsList();
$dc->order[] = array();
foreach( $orderFields as $f ) {
$dir = substr( $f, 0, 1 ) == "d" ? "desc": "asc";
$field = trim( substr( $f, 1 ) );
if( in_array( $field, $projectFields ) ) {
$dc->order[] = array("column" => $field, "dir" => $dir);
}
}
}
if( postvalue( "skip" ) ) {
$dc->startRecord = (int)postvalue( "skip" );
}
if( postvalue( "records" ) ) {
$dc->reccount = (int)postvalue( "records" );
} else {
$dc->reccount = 200;
}
$rs = $dataSource->getList( $dc );
if( !$rs ) {
API::sendError( $dataSource->lastError() );
}
$data = API::readResult( $rs, $pSet, $dc->reccount );
$fields = postvalue("fields");
if($fields){
$arrFields = explode(",",$fields);
foreach($data as $dkey=>$row){
foreach($row as $key=>$field){
if(!in_array($key,$arrFields))
unset($data[$dkey][$key]);
}
}
}
API::sendResponse( true, array("data" => $data ) );
}
if( $action === "view" ) {
if( !$pSet->pageTypeAvailable("view") ) {
API::sendError( "operation not supported" );
}
if( !Security::userCan( "S", $table ) ) {
API::sendError( "operation not allowed" );
}
$dataSource = getDataSource( $table, $pSet );
$dc = new DsCommand();
$dc->keys = API::keysFromRequest( $pSet );
$dc->filter = Security::SelectCondition( "S", $pSet );
$rs = $dataSource->getSingle( $dc );
if( !$rs ) {
API::sendError( $dataSource->lastError() );
}
API::sendResponse( true, array("data" => API::readRecord( $rs, $pSet ) ) );
}
if( $action === "update" ) {
if( !$pSet->pageTypeAvailable("edit") ) {
API::sendError( "operation not supported" );
}
if( !Security::userCan( "E", $table ) ) {
API::sendError( "operation not allowed" );
}
$dataSource = getDataSource( $table, $pSet );
$dc = new DsCommand();
$dc->keys = API::keysFromRequest( $pSet );
$dc->filter = Security::SelectCondition( "S", $pSet );
$rs = $dataSource->getSingle( $dc );
$data = $rs->fetchAssoc();
if(!$data){
echo "false";
exit();
}
$dataSource = getDataSource( $table, $pSet );
$oldKeys = API::keysFromRequest( $pSet );
$newRecordData = API::valuesFromRequest( $pSet );
$oldRecordData = null;
if( $eventsObject->exists("BeforeEdit") || $eventsObject->exists("AfterEdit") ) {
$dc = new DsCommand();
$dc->filter = Security::SelectCondition( "E", $pSet );
$dc->keys = $oldKeys;
$fetchedArray = $dataSource->getSingle( $dc )->fetchAssoc();
$oldRecordData = $cipherer->DecryptFetchedArray( $fetchedArray );
}
if( $eventsObject->exists("BeforeEdit") ) {
$usermessage = "";
$keyWhereClause = KeyWhere( $oldKeys, $table );
$pageObj = null;
$beforeEdit = $eventsObject->BeforeEdit( $newRecordData,
$keyWhereClause,
$oldRecordData,
$oldKeys,
$usermessage,
false,
$pageObj );
if( !$beforeEdit ) {
API::sendResponse( false, array( "success" => false, "error" => $usermessage ) );
}
}
$dc = new DsCommand();
$dc->keys = $oldKeys;
$dc->filter = Security::SelectCondition( "E", $pSet );
$dc->values = $newRecordData;
$ret = $dataSource->updateSingle( $dc );
if( $ret && $eventsObject->exists("AfterEdit") ) {
$keys = $oldKeys;
foreach( $newRecordData as $f => $v ) {
if( isset( $keys[ $f ] ) ) {
$keys[ $f ] = $v;
}
}
$keyWhereClause = KeyWhere( $keys, $table );
$pageObj = null;
$eventsObject->AfterEdit( $newRecordData,
$keyWhereClause,
$oldRecordData,
$keys,
false,
$pageObj );
}
if( $ret ) {
API::sendResponse( true, array( "success" => true ) );
} else {
API::sendResponse( false, array( "success" => false, "error" => $dataSource->lastError() ) );
}
API::sendResponse( $ret["success"], $ret );
}
if( $action === "insert" ) {
if( !$pSet->pageTypeAvailable("add") ) {
API::sendError( "operation not supported" );
}
if( !Security::userCan( "A", $table ) ) {
API::sendError( "operation not allowed" );
}
$newRecordData = API::valuesFromRequest( $pSet );
if( $eventsObject->exists("BeforeAdd") ) {
$usermessage = "";
$pageObj = null;
if( !$eventsObject->BeforeAdd( $newRecordData, $usermessage, false, $pageObj ) ) {
API::sendResponse( false, array( "success" => false, "error" => $usermessage ) );
}
}
$dataSource = getDataSource( $table, $pSet );
$dc = new DsCommand();
$dc->values = $newRecordData;
$ret = $dataSource->insertSingle( $dc );
if( $ret && $eventsObject->exists("AfterAdd") ) {
$pageObj = null;
$newRecordData = $ret;
$keys = array();
foreach( $pSet->getTableKeys() as $kf ) {
$keys[ $kf ] = $newRecordData[ $kf ];
}
$eventsObject->AfterAdd( $newRecordData, $keys, false, $pageObj );
}
if( $ret !== false ) {
$k = $pSet->getTableKeys();
API::sendResponse( true, array( "success" => true, $k[0] => $ret[$k[0]] ) );
} else {
API::sendResponse( false, array( "success" => false, "error" => $dataSource->lastError() ) );
}
}
if( $action === "delete" ) {
if( !$pSet->pageTypeAvailable("list") ) {
API::sendError( "operation not supported" );
}
if( !Security::userCan( "D", $table ) ) {
API::sendError( "operation not allowed" );
}
$dataSource = getDataSource( $table, $pSet );
$dc = new DsCommand();
$dc->keys = API::keysFromRequest( $pSet );
$dc->filter = Security::SelectCondition( "S", $pSet );
$rs = $dataSource->getSingle( $dc );
$data = $rs->fetchAssoc();
if(!$data){
echo "false";
exit();
}
$dataSource = getDataSource( $table, $pSet );
$dc = new DsCommand();
$dc->keys = API::keysFromRequest( $pSet );
$dc->filter = Security::SelectCondition( "D", $pSet );
$whereClause = "";
$deletedValues = array();
if( $eventsObject->exists("BeforeDelete") || $eventsObject->exists("AfterDelete") ) {
$deletedResult = $dataSource->getSingle( $dc );
if( $deletedResult )
$deletedValues = $cipherer->DecryptFetchedArray( $deletedResult->fetchAssoc() );
$whereClause = KeyWhere( $dc->keys, $table );
}
if( $eventsObject->exists("BeforeDelete") ) {
$userMessage = "";
$pageObj = null;
if( !$eventsObject->BeforeDelete( $whereClause, $deletedValues, $userMessage, $pageObj ) ) {
API::sendResponse( false, array( "success" => false, "error" => $userMessage ) );
}
}
$ret = $dataSource->deleteSingle( $dc );
if( $ret && $eventsObject->exists("AfterDelete") ) {
$userMessage = "";
$pageObj = null;
$eventsObject->AfterDelete( $whereClause, $deletedValues, $userMessage, $pageObj );
}
if( $ret ) {
API::sendResponse( true, array( "success" => true ) );
} else {
API::sendResponse( false, array( "success" => false, "error" => $dataSource->lastError() ) );
}
}
API::sendError( "unknown operation" );
?>
2
Answers
API CLASS
API
The way it is output now, shows that it is casted as a string, not as a numeric value.
Try casting it right before the output, with
(float) $version
PHP does not inherit by default the system locale settings. You have to either set it manually, by adding at the beginning of the script: