/vendors/FX.php
PHP | 2057 lines | 1694 code | 120 blank | 243 comment | 376 complexity | 5c74857a4e884cd5cb042b215bfb1590 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- <?php
- #### FX.php #############################################################
- # #
- # By: Chris Hansen with Chris Adams, Gjermund Thorsen, and others #
- # Version: 4.5.1 #
- # Date: 28 Feb 2008 #
- # License: Artistic License and addendum (included with release) #
- # Web Site: www.iviking.org #
- # Details: FX is a free open-source PHP class for accessing FileMaker #
- # and other databases. For complete details about this class, #
- # please visit www.iviking.org. #
- # #
- #########################################################################
- define("FX_VERSION", '4.5.1'); // Current version information for FX.php. New constants as of version 4.0.
- define("FX_VERSION_FULL", "FX.php version 4.5.1 (28 Feb 2008) by Chris Hansen, Chris Adams, Gjermund Thorsen, and others.");
- require_once('FX_Error.php'); // This version of FX.php includes object based error handling. See
- // FX_Error.php for more information.
- if (! defined('DEBUG_FUZZY')) { // This version of FX.php includes the FX Fuzzy Debugger (turned off by default.)
- define('DEBUG_FUZZY', false);
- }
- require_once('FX_constants.php'); // The constants in this file are designed to be used with DoFXAction()
- define("EMAIL_ERROR_MESSAGES", FALSE); // Set this to TRUE to enable emailing of specific error messages.
- define("DISPLAY_ERROR_MESSAGES", TRUE); // Set this to FALSE to display the $altErrorMessage to the user.
- $webmasterEmailAddress = 'webmaster@yourdomain.com'; // If you set the above to TRUE, enter the appropriate email address on this line.
- $emailFromAddress = 'you@yourdomain.com'; // Sets who the error message will show as the sender.
- function EmailError ($errorText)
- {
- global $webmasterEmailAddress;
- global $emailFromAddress;
- if (EMAIL_ERROR_MESSAGES) {
- $emailSubject = "PHP Server Error";
- $emailMessage = "The following error just occured:\r\n\r\nMessage: {$errorText}\r\n\r\n**This is an automated message**";
- $emailStatus = mail($webmasterEmailAddress, $emailSubject, $emailMessage, "From: $emailFromAddress\r\n");
- }
- }
- function EmailErrorHandler ($FXErrorObj)
- {
- $altErrorMessage = 'The Server was unable to process your request.<br />The WebMaster has been emailed.<br /> Thank you for your patience.';
- EmailError($FXErrorObj->message);
- if (DISPLAY_ERROR_MESSAGES) {
- echo($FXErrorObj->message);
- } else {
- echo($altErrorMessage);
- }
- return true;
- }
- class FX
- {
- // These are the basic database variables.
- var $dataServer = "";
- var $dataServerType = 'FMPro7';
- var $dataPort;
- var $dataPortSuffix;
- var $urlScheme;
- var $useSSLProtocol = false;
- var $database = "";
- var $layout = ""; // the layout to be accessed for FM databases. For SQL, the table to be accessed.
- var $responseLayout = "";
- var $groupSize;
- var $currentSkip = 0;
- var $defaultOperator = 'bw';
- var $dataParams = array();
- var $sortParams = array();
- var $actionArray = array(
- // for backwards compatibility
- "-delete" =>"-delete",
- "-dup" =>"-dup",
- "-edit" =>"-edit",
- "-find" =>"-find",
- "-findall" =>"-findall",
- "-findany" =>"-findany",
- "-new" =>"-new",
- "-view" =>"-view",
- "-dbnames" =>"-dbnames",
- "-layoutnames" =>"-layoutnames",
- "-scriptnames" =>"-scriptnames",
- "-sqlquery" =>"-sqlquery",
- // new params for DoFXAction
- "delete" =>"-delete",
- "duplicate" =>"-dup",
- "update" =>"-edit",
- "perform_find" =>"-find",
- "show_all" =>"-findall",
- "show_any" =>"-findany",
- "new" =>"-new",
- "view_layout_objects" =>"-view",
- "view_database_names" =>"-dbnames",
- "view_layout_names" =>"-layoutnames",
- "view_script_names" =>"-scriptnames"
- );
- // Variables to help with SQL queries
- var $primaryKeyField = '';
- var $modifyDateField = '';
- var $dataKeySeparator = '';
- var $fuzzyKeyLogic = false;
- var $genericKeys = false;
- var $selectColsSet = false;
- var $selectColumns = '';
- // These are the variables to be used for storing the retrieved data.
- var $fieldInfo = array();
- var $currentData = array();
- var $valueLists = array();
- var $totalRecordCount = -1;
- var $foundCount = -1;
- var $dateFormat = "";
- var $timeFormat = "";
- var $dataURL = "";
- var $dataURLParams = "";
- var $dataQuery = "";
- // Variables used to track how data is moved in and out of FileMaker. Used when UTF-8 just doesn't cut it (as when working with Japanese characters.)
- // This and all related code were submitted by Masayuki Nii.
- // Note that if either of these variables are simply empty, UTF-8 is the default.
- var $charSet = ''; // Determines how outgoing data is encoded.
- var $dataParamsEncoding = ''; // Determines how incoming data is encoded.
- // Flags and Error Tracking
- var $currentFlag = '';
- var $currentRecord = '';
- var $currentField = '';
- var $currentValueList = '';
- var $fieldCount = 0;
- var $columnCount = -1; // columnCount is ++ed BEFORE looping
- var $fxError = 'No Action Taken';
- var $errorTracking = 0;
- var $useInnerArray = true; // Do NOT change this variable directly. Use FlattenInnerArray() or the appropriate param of action method.
- // These variables will be used if you need a password to access your data.
- var $DBUser = 'FX';
- var $DBPassword = ''; // This can be left blank, or replaced with a default or dummy password.
- var $userPass = '';
- // These variables are related to sending data to FileMaker via a Post.
- var $defaultPostPolicy = true;
- var $isPostQuery;
- var $useCURL = true;
- // When returning your data via the 'object' return type, these variables will contain the database meta data
- var $lastLinkPrevious = '';
- var $lastLinkNext = '';
- var $lastFoundCount = -2;
- var $lastFields = array();
- var $lastURL = '';
- var $lastQuery = '';
- var $lastQueryParams = array();
- var $lastErrorCode = -2;
- var $lastValueLists = array();
- var $lastDebugMessage = '';
- // Other variables
- var $invalidXMLChars = array("\x0B", "\x0C", "\x12");
- /*
- Translation arrays used with str_replace to handle special
- characters in UTF-8 data received from FileMaker. The two arrays
- should have matching numeric indexes such that $UTF8SpecialChars[0]
- contains the raw binary equivalent of $UTF8HTMLEntities[0].
- This would be a perfect use for strtr(), except that it only works
- with single-byte data. Instead, we use preg_replace, which means
- that we need to delimit our match strings
- Please note that in this latest release I've removed the need for
- the include files which contained long lists of characters. Gjermund
- was sure there was a better way and he was right. With the two six
- element arrays below, every unicode character is allowed for. Let
- me know how this works for you. A link to Gjermund's homepage can
- be found in the FX Links section of www.iViking.org.
- */
- var $UTF8SpecialChars = array(
- "|([\xC2-\xDF])([\x80-\xBF])|e",
- "|(\xE0)([\xA0-\xBF])([\x80-\xBF])|e",
- "|([\xE1-\xEF])([\x80-\xBF])([\x80-\xBF])|e",
- "|(\xF0)([\x90-\xBF])([\x80-\xBF])([\x80-\xBF])|e",
- "|([\xF1-\xF3])([\x80-\xBF])([\x80-\xBF])([\x80-\xBF])|e",
- "|(\xF4)([\x80-\x8F])([\x80-\xBF])([\x80-\xBF])|e"
- );
- var $UTF8HTMLEntities = array(
- "\$this->BuildExtendedChar('\\1','\\2')",
- "\$this->BuildExtendedChar('\\1','\\2','\\3')",
- "\$this->BuildExtendedChar('\\1','\\2','\\3')",
- "\$this->BuildExtendedChar('\\1','\\2','\\3','\\4')",
- "\$this->BuildExtendedChar('\\1','\\2','\\3','\\4')",
- "\$this->BuildExtendedChar('\\1','\\2','\\3','\\4')"
- );
- function BuildExtendedChar ($byteOne, $byteTwo="\x00", $byteThree="\x00", $byteFour="\x00")
- {
- if (ord($byteTwo) >= 128) {
- $tempChar = substr(decbin(ord($byteTwo)), -6);
- if (ord($byteThree) >= 128) {
- $tempChar .= substr(decbin(ord($byteThree)), -6);
- if (ord($byteFour) >= 128) {
- $tempChar .= substr(decbin(ord($byteFour)), -6);
- $tempChar = substr(decbin(ord($byteOne)), -3) . $tempChar;
- } else {
- $tempChar = substr(decbin(ord($byteOne)), -4) . $tempChar;
- }
- } else {
- $tempChar = substr(decbin(ord($byteOne)), -5) . $tempChar;
- }
- } else $tempChar = $byteOne;
- $tempChar = '&#' . bindec($tempChar) . ';';
- return $tempChar;
- }
- function ClearAllParams ()
- {
- $this->userPass = "";
- $this->dataURL = "";
- $this->dataURLParams = "";
- $this->dataQuery = "";
- $this->dataParams = array();
- $this->sortParams = array();
- $this->fieldInfo = array();
- $this->valueLists = array();
- $this->fieldCount = 0;
- $this->currentSkip = 0;
- $this->currentData = array();
- $this->columnCount = -1;
- $this->currentRecord = "";
- $this->currentField = "";
- $this->currentFlag = "";
- $this->isPostQuery = $this->defaultPostPolicy;
- $this->primaryKeyField = '';
- $this->modifyDateField = '';
- $this->dataKeySeparator = '';
- $this->fuzzyKeyLogic = false;
- $this->genericKeys = false;
- $this->useInnerArray = true;
- }
- function ErrorHandler ($errorText)
- {
- $this->fxError = $errorText;
- $this->errorTracking = 3300;
- return $errorText;
- }
- function FX ($dataServer, $dataPort=591, $dataType='', $dataURLType='')
- {
- $this->dataServer = $dataServer;
- $this->dataPort = $dataPort;
- $this->dataPortSuffix = ":" . $dataPort;
- if (strlen($dataType) > 0) {
- $this->dataServerType = $dataType;
- }
- if (strlen($dataURLType) > 0 && ($dataType == 'FMPro7' || $dataType == 'FMPro8' || $dataType == 'FMPro9') && strtolower($dataURLType) == 'https') {
- $this->useSSLProtocol = true;
- $this->urlScheme = 'https';
- } else {
- $this->useSSLProtocol = false;
- $this->urlScheme = 'http';
- }
- $this->ClearAllParams();
- $this->lastDebugMessage = '<p>Instantiating FX.php.</p>';
- }
- function CreateCurrentSort ()
- {
- $currentSort = "";
- foreach ($this->sortParams as $key1 => $value1) {
- foreach ($value1 as $key2 => $value2) {
- $$key2 = $value2;
- }
- $lowerCaseDataServerType = strtolower($this->dataServerType);
- if (substr($lowerCaseDataServerType, 0, 5) == 'fmpro' && substr($lowerCaseDataServerType, -1) > 6) {
- if ($sortOrder == "") {
- $currentSort .= "&-sortfield.{$key1}=" . str_replace ("%3A%3A", "::", rawurlencode($field));
- }
- else {
- $currentSort .= "&-sortfield.{$key1}=" . str_replace ("%3A%3A", "::", rawurlencode($field)) . "&-sortorder.{$key1}=" . $sortOrder;
- }
- } else {
- if ($sortOrder == "") {
- $currentSort .= "&-sortfield=" . str_replace ("%3A%3A", "::", rawurlencode($field));
- }
- else {
- $currentSort .= "&-sortfield=" . str_replace ("%3A%3A", "::", rawurlencode($field)) . "&-sortorder=" . $sortOrder;
- }
- }
- }
- return $currentSort;
- }
- function CreateCurrentSearch ()
- {
- $currentSearch = '';
- foreach ($this->dataParams as $key1 => $value1) {
- foreach ($value1 as $key2 => $value2) {
- $$key2 = $value2;
- }
- if ($op == "" && $this->defaultOperator == 'bw') {
- $currentSearch .= "&" . str_replace ("%3A%3A", "::", urlencode($name)) . "=" . urlencode($value);
- } else {
- if ($op == "") {
- $op = $this->defaultOperator;
- }
- switch (strtolower($this->dataServerType)) {
- case 'fmpro5':
- case 'fmpro6':
- case 'fmpro5/6':
- $currentSearch .= "&-op=" . $op . "&" . str_replace("%3A%3A", "::", urlencode($name)) . "=" . urlencode($value);
- break;
- case 'fmpro7':
- case 'fmpro8':
- case 'fmpro9':
- $tempFieldName = str_replace("%3A%3A", "::", urlencode($name));
- $currentSearch .= "&" . $tempFieldName . ".op=" . $op . "&" . $tempFieldName . "=" . urlencode($value);
- break;
- }
- }
- }
- return $currentSearch;
- }
- function AssembleCurrentSearch ($layRequest, $skipRequest, $currentSort, $currentSearch, $action, $FMV=6)
- {
- $tempSearch = '';
- $tempSearch = "-db=" . urlencode($this->database); // add the name of the database...
- $tempSearch .= $layRequest; // and any layout specified...
- if ($FMV < 7) {
- $tempSearch .= "&-format=-fmp_xml"; // then set the FileMaker XML format to use...
- }
- $tempSearch .= "&-max=$this->groupSize$skipRequest"; // add the set size and skip size data...
- $tempSearch .= $currentSort . $currentSearch . "&" . $action; // finally, add sorting, search parameters, and action data.
- return $tempSearch;
- }
- function StartElement($parser, $name, $attrs) // The functions to start XML parsing begin here
- {
- switch(strtolower($name)) {
- case "data":
- $this->currentFlag = "parseData";
- if (! $this->useInnerArray) {
- $this->currentData[$this->currentRecord][$this->currentField] = "";
- } else {
- $this->currentData[$this->currentRecord][$this->currentField][$this->currentFieldIndex] = "";
- }
- break;
- case "col":
- $this->currentFieldIndex = 0;
- ++$this->columnCount;
- $this->currentField = $this->fieldInfo[$this->columnCount]['name'];
- if ($this->useInnerArray) {
- $this->currentData[$this->currentRecord][$this->currentField] = array();
- }
- break;
- case "row":
- foreach ($attrs as $key => $value) {
- $key = strtolower($key);
- $$key = $value;
- }
- if (substr_count($this->dataURL, '-dbnames') > 0 || substr_count($this->dataURL, '-layoutnames') > 0) {
- $modid = count($this->currentData);
- }
- $this->currentRecord = $recordid . '.' . $modid;
- $this->currentData[$this->currentRecord] = array();
- break;
- case "field":
- if ($this->charSet != '' && defined('MB_OVERLOAD_STRING')) {
- foreach ($attrs as $key => $value) {
- $key = strtolower($key);
- $this->fieldInfo[$this->fieldCount][$key] = mb_convert_encoding($value, $this->charSet, 'UTF-8');
- }
- } else {
- foreach ($attrs as $key => $value) {
- $key = strtolower($key);
- $this->fieldInfo[$this->fieldCount][$key] = $value;
- }
- }
- $this->fieldInfo[$this->fieldCount]['extra'] = ''; // for compatibility w/ SQL databases
- if (substr_count($this->dataURL, '-view') < 1) {
- $this->fieldCount++;
- }
- break;
- case "style":
- foreach ($attrs as $key => $value) {
- $key = strtolower($key);
- $this->fieldInfo[$this->fieldCount][$key] = $value;
- }
- break;
- case "resultset":
- foreach ($attrs as $key => $value) {
- switch(strtolower($key)) {
- case "found":
- $this->foundCount = (int)$value;
- break;
- }
- }
- break;
- case "errorcode":
- $this->currentFlag = "fmError";
- break;
- case "valuelist":
- foreach ($attrs as $key => $value) {
- if (strtolower($key) == "name") {
- $this->currentValueList = $value;
- }
- }
- $this->valueLists[$this->currentValueList] = array();
- $this->currentFlag = "values";
- $this->currentValueListElement = -1;
- break;
- case "value":
- $this->currentValueListElement++;
- $this->valueLists[$this->currentValueList][$this->currentValueListElement] = "";
- break;
- case "database":
- foreach ($attrs as $key => $value) {
- switch(strtolower($key)) {
- case "dateformat":
- $this->dateFormat = $value;
- break;
- case "records":
- $this->totalRecordCount = $value;
- break;
- case "timeformat":
- $this->timeFormat = $value;
- break;
- }
- }
- break;
- default:
- break;
- }
- }
- function ElementContents($parser, $data)
- {
- switch($this->currentFlag) {
- case "parseData":
- if ($this->dataParamsEncoding != '' && defined('MB_OVERLOAD_STRING')) {
- if (! $this->useInnerArray) {
- $this->currentData[$this->currentRecord][$this->currentField] .= mb_convert_encoding($data, $this->charSet, 'UTF-8');
- } else {
- $this->currentData[$this->currentRecord][$this->currentField][$this->currentFieldIndex] .= mb_convert_encoding($data, $this->charSet, 'UTF-8');
- }
- } else {
- if (! $this->useInnerArray) {
- $this->currentData[$this->currentRecord][$this->currentField] .= preg_replace($this->UTF8SpecialChars, $this->UTF8HTMLEntities, $data);
- } else {
- $this->currentData[$this->currentRecord][$this->currentField][$this->currentFieldIndex] .= preg_replace($this->UTF8SpecialChars, $this->UTF8HTMLEntities, $data);
- }
- }
- break;
- case "fmError":
- $this->fxError = $data;
- break;
- case "values":
- $this->valueLists[$this->currentValueList][$this->currentValueListElement] .= preg_replace($this->UTF8SpecialChars, $this->UTF8HTMLEntities, $data);
- break;
- }
- }
- function EndElement($parser, $name)
- {
- switch(strtolower($name)) {
- case "data":
- $this->currentFieldIndex++;
- $this->currentFlag = "";
- break;
- case "col":
- break;
- case "row":
- $this->columnCount = -1;
- break;
- case "field":
- if (substr_count($this->dataURL, '-view') > 0) {
- $this->fieldCount++;
- }
- break;
- case "errorcode":
- case "valuelist":
- $this->currentFlag = "";
- break;
- }
- } // XML Parsing Functions End Here
- function RetrieveFMData ($action)
- {
- $data = '';
- if ($this->DBPassword != '') { // Assemble the Password Data
- $this->userPass = $this->DBUser . ':' . $this->DBPassword . '@';
- }
- if ($this->layout != "") { // Set up the layout portion of the query.
- $layRequest = "&-lay=" . urlencode($this->layout);
- }
- else {
- $layRequest = "";
- }
- if ($this->currentSkip > 0) { // Set up the skip size portion of the query.
- $skipRequest = "&-skip=$this->currentSkip";
- } else {
- $skipRequest = "";
- }
- $currentSort = $this->CreateCurrentSort();
- $currentSearch = $this->CreateCurrentSearch();
- $this->dataURL = "http://{$this->userPass}{$this->dataServer}{$this->dataPortSuffix}/FMPro"; // First add the server info to the URL...
- $this->dataURLParams = $this->AssembleCurrentSearch($layRequest, $skipRequest, $currentSort, $currentSearch, $action);
- $this->dataURL .= '?' . $this->dataURLParams;
- if ((defined("DEBUG") and DEBUG) or DEBUG_FUZZY) {
- $currentDebugString = "<p>Using FileMaker URL: <a href=\"{$this->dataURL}\">{$this->dataURL}</a></p>\n";
- $this->lastDebugMessage .= $currentDebugString;
- if (defined("DEBUG") and DEBUG) {
- echo $currentDebugString;
- }
- }
- if (defined("HAS_PHPCACHE") and defined("FX_USE_PHPCACHE") and strlen($this->dataURLParams) <= 510 and (substr_count($this->dataURLParams, '-find') > 0 || substr_count($this->dataURLParams, '-view') > 0 || substr_count($this->dataURLParams, '-dbnames') > 0 || substr_count($this->dataURLParams, '-layoutnames') > 0)) {
- $data = get_url_cached($this->dataURL);
- if (! $data) {
- return new FX_Error("Failed to retrieve cached URL in RetrieveFMData()");
- }
- $data = $data["Body"];
- } elseif ($this->isPostQuery) {
- if ($this->useCURL && defined("CURLOPT_TIMEVALUE")) {
- $curlHandle = curl_init(str_replace($this->dataURLParams, '', $this->dataURL));
- curl_setopt($curlHandle, CURLOPT_POST, 1);
- curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $this->dataURLParams);
- ob_start();
- if (! curl_exec($curlHandle)) {
- $this->lastDebugMessage .= "<p>Unable to connect to FileMaker. Use the DEBUG constant and try connecting with the resulting URL manually.<br />\n";
- $this->lastDebugMessage .= "You should also double check the user name and password used, the server address, and Web Companion configuration.</p>\n";
- return new FX_Error("cURL could not retrieve Post data in RetrieveFMData(). A bad URL is the most likely reason.");
- }
- curl_close($curlHandle);
- $data = trim(ob_get_contents());
- ob_end_clean();
- if (substr($data, -1) != '>') {
- $data = substr($data, 0, -1);
- }
- } else {
- $dataDelimiter = "\r\n";
- $socketData = "POST /FMPro HTTP/1.0{$dataDelimiter}";
- if (strlen(trim($this->userPass)) > 1) {
- $socketData .= "Authorization: Basic " . base64_encode($this->DBUser . ':' . $this->DBPassword) . $dataDelimiter;
- }
- $socketData .= "Host: {$this->dataServer}:{$this->dataPort}{$dataDelimiter}";
- $socketData .= "Pragma: no-cache{$dataDelimiter}";
- $socketData .= "Content-length: " . strlen($this->dataURLParams) . $dataDelimiter;
- $socketData .= "Content-type: application/x-www-form-urlencoded{$dataDelimiter}";
- // $socketData .= "Connection: close{$dataDelimiter}";
- $socketData .= $dataDelimiter . $this->dataURLParams;
- $fp = fsockopen ($this->dataServer, $this->dataPort, $this->errorTracking, $this->fxError, 30);
- if (! $fp) {
- $this->lastDebugMessage .= "<p>Unable to connect to FileMaker. Use the DEBUG constant and try connecting with the resulting URL manually.<br />\n";
- $this->lastDebugMessage .= "You should also double check the user name and password used, the server address, and Web Companion configuration.</p>\n";
- return new FX_Error( "Could not fsockopen the URL in retrieveFMData" );
- }
- fputs ($fp, $socketData);
- while (!feof($fp)) {
- $data .= fgets($fp, 128);
- }
- fclose($fp);
- $pos = strpos($data, chr(13) . chr(10) . chr(13) . chr(10)); // the separation code
- $data = substr($data, $pos + 4) . "\r\n";
- }
- } else {
- $fp = fopen($this->dataURL, "r");
- if (! $fp) {
- $this->lastDebugMessage .= "<p>Unable to connect to FileMaker. Use the DEBUG constant and try connecting with the resulting URL manually.<br />\n";
- $this->lastDebugMessage .= "You should also double check the user name and password used, the server address, and Web Companion configuration.</p>\n";
- return new FX_Error("Could not fopen URL in RetrieveFMData.");
- }
- while (!feof($fp)) {
- $data .= fread($fp, 4096);
- }
- fclose($fp);
- }
- $data = str_replace($this->invalidXMLChars, '', $data);
- return $data;
- }
- function RetrieveFM7Data ($action)
- {
- $data = '';
- if ($this->DBPassword != '' || $this->DBUser != 'FX') { // Assemble the Password Data
- $this->userPass = $this->DBUser . ':' . $this->DBPassword . '@';
- }
- if ($this->layout != "") { // Set up the layout portion of the query.
- $layRequest = "&-lay=" . urlencode($this->layout);
- if ($this->responseLayout != "") {
- $layRequest .= "&-lay.response=" . urlencode($this->responseLayout);
- }
- }
- else {
- $layRequest = "";
- }
- if ($this->currentSkip > 0) { // Set up the skip size portion of the query.
- $skipRequest = "&-skip={$this->currentSkip}";
- } else {
- $skipRequest = "";
- }
- $currentSort = $this->CreateCurrentSort();
- $currentSearch = $this->CreateCurrentSearch();
- if ($action == '-view') {
- $FMFile = 'FMPXMLLAYOUT.xml';
- } else {
- $FMFile = 'FMPXMLRESULT.xml';
- }
- $this->dataURL = "{$this->urlScheme}://{$this->userPass}{$this->dataServer}{$this->dataPortSuffix}/fmi/xml/{$FMFile}"; // First add the server info to the URL...
- $this->dataURLParams = $this->AssembleCurrentSearch($layRequest, $skipRequest, $currentSort, $currentSearch, $action, 7);
- $this->dataURL .= '?' . $this->dataURLParams;
- if ((defined("DEBUG") and DEBUG) or DEBUG_FUZZY) {
- $currentDebugString = "<p>Using FileMaker URL: <a href=\"{$this->dataURL}\">{$this->dataURL}</a></p>\n";
- $this->lastDebugMessage .= $currentDebugString;
- if (defined("DEBUG") and DEBUG) {
- echo $currentDebugString;
- }
- }
- if (defined("HAS_PHPCACHE") and defined("FX_USE_PHPCACHE") and strlen($this->dataURLParams) <= 510 and (substr_count($this->dataURLParams, '-find') > 0 || substr_count($this->dataURLParams, '-view') > 0 || substr_count($this->dataURLParams, '-dbnames') > 0 || substr_count($this->dataURLParams, '-layoutnames') > 0)) {
- $data = get_url_cached($this->dataURL);
- if (! $data) {
- return new FX_Error("Failed to retrieve cached URL in RetrieveFM7Data()");
- }
- $data = $data["Body"];
- } elseif ($this->isPostQuery) {
- if ($this->useCURL && defined("CURLOPT_TIMEVALUE")) {
- $curlHandle = curl_init(str_replace($this->dataURLParams, '', $this->dataURL));
- curl_setopt($curlHandle, CURLOPT_POST, 1);
- curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $this->dataURLParams);
- ob_start();
- if (! curl_exec($curlHandle)) {
- $this->lastDebugMessage .= "<p>Unable to connect to FileMaker. Use the DEBUG constant and try connecting with the resulting URL manually.<br />\n";
- $this->lastDebugMessage .= "You should also double check the user name and password used, the server address, and WPE configuration.</p>\n";
- return new FX_Error("cURL could not retrieve Post data in RetrieveFM7Data(). A bad URL is the most likely reason.");
- }
- curl_close($curlHandle);
- $data = trim(ob_get_contents());
- ob_end_clean();
- if (substr($data, -1) != '>') {
- $data = substr($data, 0, -1);
- }
- } else {
- $dataDelimiter = "\r\n";
- $socketData = "POST /fmi/xml/{$FMFile} HTTP/1.0{$dataDelimiter}";
- if ((defined("DEBUG") and DEBUG) or DEBUG_FUZZY) {
- $currentDebugString = "<p>Using socket [$socketData] - FileMaker URL: <a href=\"{$this->dataURL}\">{$this->dataURL}</a></p>\n";
- $this->lastDebugMessage .= $currentDebugString;
- if (defined("DEBUG") and DEBUG) {
- echo $currentDebugString;
- }
- }
- if (strlen(trim($this->userPass)) > 1) {
- $socketData .= "Authorization: Basic " . base64_encode($this->DBUser . ':' . $this->DBPassword) . $dataDelimiter;
- }
- $socketData .= "Host: {$this->dataServer}:{$this->dataPort}{$dataDelimiter}";
- $socketData .= "Pragma: no-cache{$dataDelimiter}";
- $socketData .= "Content-length: " . strlen($this->dataURLParams) . $dataDelimiter;
- $socketData .= "Content-type: application/x-www-form-urlencoded{$dataDelimiter}";
- $socketData .= $dataDelimiter . $this->dataURLParams;
- // Check if SSL is required
- if ($this->useSSLProtocol) {
- $protocol = "ssl://";
- } else {
- $protocol = "";
- }
- // debug to see what protocol is being used
- if ((defined("DEBUG") and DEBUG) or DEBUG_FUZZY) {
- $currentDebugString = "<p>Domain and Protocol are {$protocol}{$this->dataServer}</p>\n";
- $this->lastDebugMessage .= $currentDebugString;
- if (defined("DEBUG") and DEBUG) {
- echo $currentDebugString;
- }
- }
- $fp = fsockopen ($protocol . $this->dataServer, $this->dataPort, $this->errorTracking, $this->fxError, 30);
- if (! $fp) {
- $this->lastDebugMessage .= "<p>Unable to connect to FileMaker. Use the DEBUG constant and try connecting with the resulting URL manually.<br />\n";
- $this->lastDebugMessage .= "You should also double check the user name and password used, the server address, and WPE configuration.</p>\n";
- return new FX_Error( "Could not fsockopen the URL in retrieveFM7Data" );
- }
- fputs ($fp, $socketData);
- while (!feof($fp)) {
- $data .= fgets($fp, 128);
- }
- fclose($fp);
- $pos = strpos($data, chr(13) . chr(10) . chr(13) . chr(10)); // the separation code
- $data = substr($data, $pos + 4) . "\r\n";
- }
- } else {
- $fp = fopen($this->dataURL, "r");
- if (! $fp) {
- $this->lastDebugMessage .= "<p>Unable to connect to FileMaker. Use the DEBUG constant and try connecting with the resulting URL manually.<br />\n";
- $this->lastDebugMessage .= "You should also double check the user name and password used, the server address, and WPE configuration.</p>\n";
- return new FX_Error("Could not fopen URL in RetrieveFM7Data.");
- }
- while (!feof($fp)) {
- $data .= fread($fp, 4096);
- }
- fclose($fp);
- }
- $data = str_replace($this->invalidXMLChars, '', $data);
- return $data;
- }
- function BuildSQLSorts ()
- {
- $currentOrderBy = '';
- if (count($this->sortParams) > 0) {
- $counter = 0;
- $currentOrderBy .= ' ORDER BY ';
- foreach ($this->sortParams as $key1 => $value1) {
- foreach ($value1 as $key2 => $value2) {
- $$key2 = $value2;
- }
- if ($counter > 0) {
- $currentOrderBy .= ', ';
- }
- $currentOrderBy .= "{$field}";
- if (substr_count(strtolower($sortOrder), 'desc') > 0) {
- $currentOrderBy .= ' DESC';
- }
- ++$counter;
- }
- return $currentOrderBy;
- }
- }
- function BuildSQLQuery ($action)
- {
- $currentLOP = 'AND';
- $logicalOperators = array();
- $LOPCount = 0;
- $currentQuery = '';
- $counter = 0;
- $whereClause = '';
- switch ($action) {
- case '-find':
- foreach ($this->dataParams as $key1 => $value1) {
- foreach ($value1 as $key2 => $value2) {
- $$key2 = $value2;
- }
- switch ($name) {
- case '-lop':
- $LOPCount = array_push($logicalOperators, $currentLOP);
- $currentLOP = $value;
- $currentSearch .= "(";
- break;
- case '-lop_end':
- $currentLOP = array_pop($logicalOperators);
- --$LOPCount;
- $currentSearch .= ")";
- break;
- case '-recid':
- if ($counter > 0) {
- $currentSearch .= " {$currentLOP} ";
- }
- $currentSearch .= $this->primaryKeyField . " = '" . $value . "'";
- ++$counter;
- break;
- case '-script':
- case '-script.prefind':
- case '-script.presort':
- return new FX_Error("The '-script' parameter is not currently supported for SQL.");
- break;
- default:
- if ($op == "") {
- $op = $this->defaultOperator;
- }
- if ($counter > 0) {
- $currentSearch .= " {$currentLOP} ";
- }
- switch ($op) {
- case 'eq':
- $currentSearch .= $name . " = '" . $value . "'";
- break;
- case 'neq':
- $currentSearch .= $name . " != '" . $value . "'";
- break;
- case 'cn':
- $currentSearch .= $name . " LIKE '%" . $value . "%'";
- break;
- case 'bw':
- $currentSearch .= $name . " LIKE '" . $value . "%'";
- break;
- case 'ew':
- $currentSearch .= $name . " LIKE '%" . $value . "'";
- break;
- case 'gt':
- $currentSearch .= $name . " > '" . $value . "'";
- break;
- case 'gte':
- $currentSearch .= $name . " >= '" . $value . "'";
- break;
- case 'lt':
- $currentSearch .= $name . " < '" . $value . "'";
- break;
- case 'lte':
- $currentSearch .= $name . " <= '" . $value . "'";
- break;
- default: // default is a 'begins with' search for historical reasons (default in FM)
- $currentSearch .= $name . " LIKE '" . $value . "%'";
- break;
- }
- ++$counter;
- break;
- }
- }
- while ($LOPCount > 0) {
- --$LOPCount;
- $currentSearch .= ")";
- }
- $whereClause = ' WHERE ' . $currentSearch; // set the $whereClause variable here, to distinguish this from a "finall" request
- case '-findall': //
- if ($this->selectColsSet) {
- $currentQuery = "SELECT {$this->selectColumns} FROM {$this->layout}{$whereClause}" . $this->BuildSQLSorts();
- } else {
- $currentQuery = "SELECT * FROM {$this->layout}{$whereClause}" . $this->BuildSQLSorts();
- }
- break;
- case '-delete':
- foreach ($this->dataParams as $key1 => $value1) {
- foreach ($value1 as $key2 => $value2) {
- $$key2 = $value2;
- }
- if ($name == '-recid') {
- $currentQuery = "DELETE FROM {$this->layout} WHERE {$this->primaryKeyField} = '{$value}'";
- }
- }
- break;
- case '-edit':
- $whereClause = '';
- $currentQuery = "UPDATE {$this->layout} SET ";
- foreach ($this->dataParams as $key1 => $value1) {
- foreach ($value1 as $key2 => $value2) {
- $$key2 = $value2;
- }
- if ($name == '-recid') {
- $whereClause = " WHERE {$this->primaryKeyField} = '{$value}'";
- } else {
- if ($counter > 0) {
- $currentQuery .= ", ";
- }
- $currentQuery .= "{$name} = '{$value}'";
- ++$counter;
- }
- }
- $currentQuery .= $whereClause;
- break;
- case '-new':
- $tempColList = '(';
- $tempValueList = '(';
- foreach ($this->dataParams as $key1 => $value1) {
- foreach ($value1 as $key2 => $value2) {
- $$key2 = $value2;
- }
- if ($name == '-recid') {
- $currentQuery = "DELETE FROM {$this->layout} WHERE {$this->primaryKeyField} = '{$value}'";
- }
- if ($counter > 0) {
- $tempColList .= ", ";
- $tempValueList .= ", ";
- }
- $tempColList .= $name;
- $tempValueList .= "'{$value}'";
- ++$counter;
- }
- $tempColList .= ')';
- $tempValueList .= ')';
- $currentQuery = "INSERT INTO {$this->layout} {$tempColList} VALUES {$tempValueList}";
- break;
- }
- $currentQuery .= ';';
- return $currentQuery;
- }
- function RetrieveMySQLData ($action)
- {
- if (strlen(trim($this->dataServer)) < 1) {
- return new FX_Error('No MySQL server specified.');
- }
- if (strlen(trim($this->dataPort)) > 0) {
- $tempServer = $this->dataServer . ':' . $this->dataPort;
- } else {
- $tempServer = $this->dataServer;
- }
- $mysql_res = @mysql_connect($tempServer, $this->DBUser, $this->DBPassword); // although username and password are optional for this function, FX.php expects them to be set
- if ($mysql_res == false) {
- return new FX_Error('Unable to connect to MySQL server.');
- }
- if ($action != '-dbopen') {
- if (! mysql_select_db($this->database, $mysql_res)) {
- return new FX_Error('Unable to connect to specified MySQL database.');
- }
- }
- if (substr_count($action, '-db') == 0 && substr_count($action, 'names') == 0) {
- $theResult = mysql_query('SHOW COLUMNS FROM ' . $this->layout);
- if (! $theResult) {
- return new FX_Error('Unable to access MySQL column data: ' . mysql_error());
- }
- $counter = 0;
- $keyPrecedence = 0;
- while ($tempRow = mysql_fetch_assoc($theResult)) {
- $this->fieldInfo[$counter]['name'] = $tempRow['Field'];
- $this->fieldInfo[$counter]['type'] = $tempRow['Type'];
- $this->fieldInfo[$counter]['emptyok'] = $tempRow['Null'];
- $this->fieldInfo[$counter]['maxrepeat'] = 1;
- $this->fieldInfo[$counter]['extra'] = $tempRow['Key'] . ' ' . $tempRow['Extra'];
- if ($this->fuzzyKeyLogic) {
- if (strlen(trim($this->primaryKeyField)) < 1 || $keyPrecedence < 3) {
- if (substr_count($this->fieldInfo[$counter]['extra'], 'UNI ') > 0 && $keyPrecedence < 3) {
- $this->primaryKeyField = $this->fieldInfo[$counter]['name'];
- $keyPrecedence = 3;
- } elseif (substr_count($this->fieldInfo[$counter]['extra'], 'auto_increment') > 0 && $keyPrecedence < 2) {
- $this->primaryKeyField = $this->fieldInfo[$counter]['name'];
- $keyPrecedence = 2;
- } elseif (substr_count($this->fieldInfo[$counter]['extra'], 'PRI ') > 0 && $keyPrecedence < 1) {
- $this->primaryKeyField = $this->fieldInfo[$counter]['name'];
- $keyPrecedence = 1;
- }
- }
- }
- ++$counter;
- }
- }
- switch ($action) {
- case '-dbopen':
- case '-dbclose':
- return new FX_Error('Opening and closing MySQL databases not available.');
- break;
- case '-delete':
- case '-edit':
- case '-find':
- case '-findall':
- case '-new':
- $this->dataQuery = $this->BuildSQLQuery($action);
- if (FX::isError($this->dataQuery)) {
- return $this->dataQuery;
- }
- case '-sqlquery': // note that there is no preceding break, as we don't want to build a query
- $theResult = mysql_query($this->dataQuery);
- if ($theResult === false) {
- return new FX_Error('Invalid query: ' . mysql_error());
- } elseif ($theResult !== true) {
- if (substr_count($action, '-find') > 0 || substr_count($this->dataQuery, 'SELECT ') > 0) {
- $this->foundCount = mysql_num_rows($theResult);
- } else {
- $this->foundCount = mysql_affected_rows($theResult);
- }
- if ($action == '-dup' || $action == '-edit') {
- // pull in data on relevant record
- }
- $currentKey = '';
- while ($tempRow = mysql_fetch_assoc($theResult)) {
- foreach ($tempRow as $key => $value) {
- if ($this->useInnerArray) {
- $tempRow[$key] = array($value);
- }
- if ($key == $this->primaryKeyField) {
- $currentKey = $value;
- }
- }
- if ($this->genericKeys || $this->primaryKeyField == '') {
- $this->currentData[] = $tempRow;
- } else {
- $this->currentData[$currentKey] = $tempRow;
- }
- }
- } else {
- $this->currentData = array();
- }
- break;
- case '-findany':
- break;
- case '-dup':
- break;
- }
- $this->fxError = 0;
- return true;
- }
- function RetrievePostgreSQLData ($action)
- {
- $connectString = '';
- $unsupportedActions = array('-dbnames', '-layoutnames', '-scriptnames', '-dbopen', '-dbclose');
- if (in_array($action, $unsupportedActions)) {
- return new FX_Error("The requested Action ({$action}) is not supported in PostgreSQL via FX.php.");
- }
- if (strlen(trim($this->dataServer)) > 0) {
- $connectString .= " host={$this->dataServer}";
- }
- if (strlen(trim($this->dataPort)) > 0) {
- $connectString .= " port={$this->dataPort}";
- }
- if (strlen(trim($this->database)) > 0) {
- $connectString .= " dbname={$this->database}";
- }
- if (strlen(trim($this->DBUser)) > 0) {
- $connectString .= " user={$this->DBUser}";
- }
- if (strlen(trim($this->DBPassword)) > 0) {
- $connectString .= " password={$this->DBPassword}";
- }
- if (strlen(trim($this->urlScheme)) > 0 && $this->urlScheme == 'https') {
- $connectString .= " sslmode=require";
- }
- $postresql_res = @pg_connect($connectString);
- if ($postresql_res == false) {
- return new FX_Error("Unable to connect to PostgreSQL server. (" . pg_last_error($postresql_res) . ")");
- }
- $theResult = pg_query($postresql_res, "SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_name ='{$this->layout}'");
- if (! $theResult) {
- return new FX_Error('Unable to access PostgreSQL column data: ' . pg_last_error($postresql_res));
- }
- $counter = 0;
- $keyPrecedence = 0;
- while ($tempRow = @pg_fetch_array($theResult, $counter, PGSQL_ASSOC)) {
- $this->fieldInfo[$counter]['name'] = $tempRow['column_name'];
- $this->fieldInfo[$counter]['type'] = $tempRow['data_type'];
- $this->fieldInfo[$counter]['emptyok'] = $tempRow['is_nullable'];
- $this->fieldInfo[$counter]['maxrepeat'] = 1;
- ++$counter;
- }
- switch ($action) {
- case '-delete':
- case '-edit':
- case '-find':
- case '-findall':
- case '-new':
- $this->dataQuery = $this->BuildSQLQuery($action);
- if (FX::isError($this->dataQuery)) {
- return $this->dataQuery;
- }
- case '-sqlquery': // note that there is no preceding break, as we don't want to build a query
- $theResult = pg_query($this->dataQuery);
- if (! $theResult) {
- return new FX_Error('Invalid query: ' . pg_last_error($postresql_res));
- }
- if (substr_count($action, '-find') > 0 || substr_count($this->dataQuery, 'SELECT ') > 0) {
- $this->foundCount = pg_num_rows($theResult);
- } else {
- $this->foundCount = pg_a…
Large files files are truncated, but you can click here to view the full file