PageRenderTime 58ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/FX.php

https://github.com/APD183/fxphp
PHP | 1011 lines | 793 code | 112 blank | 106 comment | 157 complexity | ec48cc954796511588595421b8a11c73 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. <?php
  2. #### FX.php #############################################################
  3. # #
  4. # By: Chris Hansen with Chris Adams, G G Thorsen, Masayuki Nii, #
  5. # and others #
  6. # Version: 6.0 #
  7. # Date: 3 Feb 2012 #
  8. # License: Artistic License and addendum (included with release) #
  9. # Web Site: www.iviking.org #
  10. # Details: FX is a free open-source PHP class for accessing FileMaker #
  11. # and other databases. For complete details about this class, #
  12. # please visit www.iviking.org. #
  13. # #
  14. #########################################################################
  15. define("FX_VERSION", '6.0'); // Current version information for FX.php. New constants as of version 4.0.
  16. define("FX_VERSION_FULL", 'FX.php version ' . FX_VERSION . ' (3 Feb 2012) by Chris Hansen, Chris Adams, G G Thorsen, Masayuki Nii, and others.');
  17. require_once('FX_Error.php'); // FX.php includes object based error handling. See FX_Error.php for more info.
  18. if (! defined('DEBUG_FUZZY')) { // This version of FX.php includes the FX Fuzzy Debugger (turned off by default.)
  19. define('DEBUG_FUZZY', false);
  20. }
  21. require_once('FX_constants.php'); // The constants in this file are designed to be used with DoFXAction()
  22. define("EMAIL_ERROR_MESSAGES", FALSE); // Set this to TRUE to enable emailing of specific error messages.
  23. define("DISPLAY_ERROR_MESSAGES", TRUE); // Set this to FALSE to display the $altErrorMessage to the user.
  24. $webmasterEmailAddress = 'webmaster@yourdomain.com'; // If you set the above to TRUE, enter the appropriate email address on this line.
  25. $emailFromAddress = 'you@yourdomain.com'; // Sets who the error message will show as the sender.
  26. function EmailError ($errorText) {
  27. global $webmasterEmailAddress;
  28. global $emailFromAddress;
  29. if (EMAIL_ERROR_MESSAGES) {
  30. $emailSubject = "PHP Server Error";
  31. $emailMessage = "The following error just occured:\r\n\r\nMessage: {$errorText}\r\n\r\n**This is an automated message**";
  32. $emailStatus = mail($webmasterEmailAddress, $emailSubject, $emailMessage, "From: $emailFromAddress\r\n");
  33. }
  34. }
  35. function EmailErrorHandler ($FXErrorObj) {
  36. $altErrorMessage = 'The Server was unable to process your request.<br />The WebMaster has been emailed.<br /> Thank you for your patience.';
  37. EmailError($FXErrorObj->message);
  38. if (DISPLAY_ERROR_MESSAGES) {
  39. echo($FXErrorObj->message);
  40. } else {
  41. echo($altErrorMessage);
  42. }
  43. return true;
  44. }
  45. class FX {
  46. // These are the basic database variables.
  47. var $dataServer = "";
  48. var $dataServerType = 'fmpro';
  49. var $dataServerVersion = 7;
  50. var $dataPort;
  51. var $dataPortSuffix;
  52. var $urlScheme;
  53. var $useSSLProtocol = false;
  54. var $verifyPeer = true;
  55. var $database = "";
  56. var $layout = ""; // the layout to be accessed for FM databases. For SQL, the table to be accessed.
  57. var $responseLayout = "";
  58. var $groupSize;
  59. var $currentSkip = 0;
  60. var $defaultOperator = 'bw';
  61. var $findquerynumber = 1;
  62. var $findquerystring = '';
  63. var $dataParams = array();
  64. var $sortParams = array();
  65. var $actionArray = array(
  66. // for backwards compatibility
  67. "-delete" =>"-delete",
  68. "-dup" =>"-dup",
  69. "-edit" =>"-edit",
  70. "-find" =>"-find",
  71. "-findall" =>"-findall",
  72. "-findany" =>"-findany",
  73. '-findquery' =>'-findquery',
  74. "-new" =>"-new",
  75. "-view" =>"-view",
  76. "-dbnames" =>"-dbnames",
  77. "-layoutnames" =>"-layoutnames",
  78. "-scriptnames" =>"-scriptnames",
  79. "-sqlquery" =>"-sqlquery",
  80. // new params for DoFXAction
  81. "delete" =>"-delete",
  82. "duplicate" =>"-dup",
  83. "update" =>"-edit",
  84. "perform_find" =>"-find",
  85. "show_all" =>"-findall",
  86. "show_any" =>"-findany",
  87. "new" =>"-new",
  88. "view_layout_objects" =>"-view",
  89. "view_database_names" =>"-dbnames",
  90. "view_layout_names" =>"-layoutnames",
  91. "view_script_names" =>"-scriptnames"
  92. );
  93. // Variables to help with SQL queries
  94. var $primaryKeyField = '';
  95. var $modifyDateField = '';
  96. var $dataKeySeparator = '';
  97. var $fuzzyKeyLogic = false;
  98. var $genericKeys = false;
  99. var $selectColsSet = false;
  100. var $selectColumns = '';
  101. // These are the variables to be used for storing the retrieved data.
  102. var $fieldInfo = array();
  103. var $currentData = array();
  104. var $valueLists = array();
  105. var $totalRecordCount = -1;
  106. var $foundCount = -1;
  107. var $dateFormat = "";
  108. var $timeFormat = "";
  109. var $dataQuery = "";
  110. // 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.)
  111. // This and all related code were submitted by Masayuki Nii.
  112. // Note that if either of these variables are simply empty, UTF-8 is the default.
  113. var $charSet = ''; // Determines how outgoing data is encoded.
  114. var $dataParamsEncoding = ''; // Determines how incoming data is encoded.
  115. var $remainNames = array(); // Added by Masayuki Nii(nii@msyk.net) Dec 18, 2010
  116. var $remainNamesReverse = array(); // Added by Masayuki Nii(nii@msyk.net) Jan 23, 2010
  117. var $portalAsRecord =false; // Added by Masayuki Nii(nii@msyk.net) Dec 18, 2010
  118. var $usePortalIDs = false; // for use with the RetrieveFM7VerboseData.class "fmalt"
  119. // Flags and Error Tracking
  120. var $fieldCount = 0;
  121. var $fxError = 'No Action Taken';
  122. var $errorTracking = 0;
  123. var $useInnerArray = null; // Do NOT change this variable directly. Use FlattenInnerArray() or the appropriate param of action method.
  124. var $useReturnJSONResult = false;
  125. var $useReturnJSONFullArrayResult = false;
  126. var $useComma2Period = false;
  127. // These variables will be used if you need a password to access your data.
  128. var $DBUser = 'FX';
  129. var $DBPassword = ''; // This can be left blank, or replaced with a default or dummy password.
  130. var $userPass = '';
  131. // These variables are related to sending data to FileMaker via a Post.
  132. var $defaultPostPolicy = true;
  133. var $isPostQuery;
  134. var $defaultFOpenPolicy = false;
  135. var $isFOpenQuery;
  136. var $useCURL = true;
  137. var $customPrimaryKey = '';
  138. // When returning your data via the 'object' return type, these variables will contain the database meta data
  139. var $lastLinkPrevious = '';
  140. var $lastLinkNext = '';
  141. var $lastFoundCount = -2;
  142. var $lastFields = array();
  143. var $lastURL = '';
  144. var $lastQuery = '';
  145. var $lastQueryParams = array();
  146. var $lastErrorCode = -2;
  147. var $lastValueLists = array();
  148. var $lastDebugMessage = '';
  149. // Other variables
  150. var $fuzzyFXPass = ''; // this is to handle the fact that I couldn't provide a default value for a pass-by-value param in PHP4
  151. // Constructor
  152. function FX ($dataServer, $dataPort=80, $dataType='', $dataURLType='') {
  153. $this->dataServer = $dataServer;
  154. $this->dataPort = $dataPort;
  155. $this->dataPortSuffix = ":" . $dataPort;
  156. if (strlen($dataType) > 0) {
  157. $this->dataServerType = substr(strtolower($dataType), 0, 5);
  158. }
  159. if ($this->dataServerType == 'fmpro' || $this->dataServerType == 'fmalt') {
  160. $this->dataServerVersion = intval(str_replace($this->dataServerType, '', strtolower($dataType)));
  161. } else {
  162. $this->dataServerVersion = 0;
  163. }
  164. if (((strlen($dataURLType) > 0 && $this->dataServerVersion >= 7 && $this->dataServerType == 'fmpro') || ($this->dataServerType == 'fmalt')) && strtolower($dataURLType) == 'https') {
  165. $this->useSSLProtocol = true;
  166. $this->urlScheme = 'https';
  167. } else {
  168. $this->useSSLProtocol = false;
  169. $this->urlScheme = 'http';
  170. }
  171. $this->ClearAllParams();
  172. $this->lastDebugMessage = '<p>Instantiating FX.php.</p>';
  173. }
  174. function BuildExtendedChar ($byteOne, $byteTwo="\x00", $byteThree="\x00", $byteFour="\x00") {
  175. if (ord($byteTwo) >= 128) {
  176. $tempChar = substr(decbin(ord($byteTwo)), -6);
  177. if (ord($byteThree) >= 128) {
  178. $tempChar .= substr(decbin(ord($byteThree)), -6);
  179. if (ord($byteFour) >= 128) {
  180. $tempChar .= substr(decbin(ord($byteFour)), -6);
  181. $tempChar = substr(decbin(ord($byteOne)), -3) . $tempChar;
  182. } else {
  183. $tempChar = substr(decbin(ord($byteOne)), -4) . $tempChar;
  184. }
  185. } else {
  186. $tempChar = substr(decbin(ord($byteOne)), -5) . $tempChar;
  187. }
  188. } else $tempChar = $byteOne;
  189. $tempChar = '&#' . bindec($tempChar) . ';';
  190. return $tempChar;
  191. }
  192. function ClearAllParams () {
  193. $this->userPass = "";
  194. $this->dataQuery = "";
  195. $this->dataParams = array();
  196. $this->sortParams = array();
  197. $this->fieldInfo = array();
  198. $this->valueLists = array();
  199. $this->fieldCount = 0;
  200. $this->currentSkip = 0;
  201. $this->currentData = array();
  202. $this->columnCount = -1;
  203. $this->isPostQuery = $this->defaultPostPolicy;
  204. $this->isFOpenQuery = $this->defaultFOpenPolicy;
  205. $this->primaryKeyField = '';
  206. $this->modifyDateField = '';
  207. $this->dataKeySeparator = '';
  208. $this->fuzzyKeyLogic = false;
  209. $this->genericKeys = false;
  210. $this->useInnerArray = null;
  211. $this->remainNames = array(); // Added by Masayuki Nii(nii@msyk.net) Dec 18, 2010
  212. $this->remainNamesReverse = array(); // Added by Masayuki Nii(nii@msyk.net) Jan 23, 2011
  213. $this->portalAsRecord = false; // Added by Masayuki Nii(nii@msyk.net) Dec 18, 2010
  214. $this->findquerynumber = 1; // added by Nick Salonen
  215. $this->findquerystring = ''; // added by Nick Salonen
  216. }
  217. function ErrorHandler ($errorText) {
  218. $this->fxError = $errorText;
  219. $this->errorTracking = 3300;
  220. return $errorText;
  221. }
  222. function ExecuteQuery ($action) {
  223. switch ($this->dataServerType) {
  224. case 'fmpro':
  225. if ($this->dataServerVersion >= 7) {
  226. require_once('datasource_classes/RetrieveFM7Data.class.php');
  227. $datasourceClassName = 'RetrieveFM7Data';
  228. $datasourceDescription = 'FileMaker Server 7+';
  229. } else {
  230. require_once('datasource_classes/RetrieveFM5Data.class.php');
  231. $datasourceClassName = 'RetrieveFM5Data';
  232. $datasourceDescription = 'FileMaker Pro 5/6';
  233. }
  234. break;
  235. case 'fmalt':
  236. // calls to FMView require this fix as of server version 12 to 12.0v2, so far
  237. if ($action == '-view')
  238. {
  239. require_once('datasource_classes/RetrieveFM7Data.class.php');
  240. $datasourceClassName = 'RetrieveFM7Data';
  241. $datasourceDescription = 'FileMaker Server 7+';
  242. } else {
  243. require_once('datasource_classes/RetrieveFM7VerboseData.class.php');
  244. $datasourceClassName = 'RetrieveFM7VerboseData';
  245. $datasourceDescription = 'FileMaker Server 7+ Verbose';
  246. }
  247. break;
  248. case 'openb':
  249. require_once('datasource_classes/RetrieveFXOpenBaseData.class.php');
  250. $datasourceClassName = 'RetrieveFXOpenBaseData';
  251. $datasourceDescription = 'OpenBase';
  252. break;
  253. case 'mysql':
  254. require_once('datasource_classes/RetrieveFXMySQLData.class.php');
  255. $datasourceClassName = 'RetrieveFXMySQLData';
  256. $datasourceDescription = 'MySQL';
  257. break;
  258. case 'postg':
  259. require_once('datasource_classes/RetrieveFXPostgreSQLData.class.php');
  260. $datasourceClassName = 'RetrieveFXPostgreSQLData';
  261. $datasourceDescription = 'PostgreSQL';
  262. break;
  263. case 'odbc':
  264. require_once('datasource_classes/RetrieveFXODBCData.class.php');
  265. $datasourceClassName = 'RetrieveFXODBCData';
  266. $datasourceDescription = 'ODBC';
  267. break;
  268. case 'cafep':
  269. require_once('datasource_classes/RetrieveCafePHP4pcData.class.php');
  270. $datasourceClassName = 'RetrieveCafePHP4pcData';
  271. $datasourceDescription = 'CAFEphp';
  272. break;
  273. }
  274. // Query and handle data
  275. if ((defined("DEBUG") and DEBUG) or DEBUG_FUZZY) {
  276. $currentDebugString = "<p>Accessing {$datasourceDescription} data.</p>\n";
  277. $this->lastDebugMessage .= $currentDebugString;
  278. if (defined("DEBUG") and DEBUG) {
  279. echo $currentDebugString;
  280. }
  281. }
  282. $queryObject = new $datasourceClassName($this);
  283. $result = $queryObject->doQuery($action);
  284. if (FX::isError($result)) {
  285. return $result;
  286. }
  287. $queryObject->cleanUp();
  288. }
  289. function BuildLinkQueryString () {
  290. $tempQueryString = '';
  291. if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'POST') {
  292. $paramSetCount = 0;
  293. $appendFlag = true;
  294. foreach ($_POST as $key => $value) {
  295. if ($appendFlag && strcasecmp($key, '-foundSetParams_begin') != 0 && strcasecmp($key, '-foundSetParams_end') != 0) {
  296. if (is_array($value))
  297. {
  298. foreach($value as $innertkey => $innertvalue)
  299. {
  300. $tempQueryString .= urlencode($key.'[]') . '='.$innertvalue.'&';
  301. }
  302. } else {
  303. $tempQueryString .= urlencode($key) . '=' . urlencode($value) . '&';
  304. }
  305. } elseif (strcasecmp($key, '-foundSetParams_begin') == 0) {
  306. $appendFlag = true;
  307. if ($paramSetCount < 1) {
  308. $tempQueryString = '';
  309. ++$paramSetCount;
  310. }
  311. } elseif (strcasecmp($key, '-foundSetParams_end') == 0) {
  312. $appendFlag = false;
  313. }
  314. }
  315. } else {
  316. $beginTagLower = strtolower('-foundSetParams_begin');
  317. $endTagLower = strtolower('-foundSetParams_end');
  318. if (! isset($_SERVER['QUERY_STRING'])) {
  319. $_SERVER['QUERY_STRING'] = '';
  320. }
  321. $queryStringLower = strtolower($_SERVER['QUERY_STRING']);
  322. if (substr_count($queryStringLower, $beginTagLower) > 0 && substr_count($queryStringLower, $beginTagLower) == substr_count($queryStringLower, $endTagLower)) {
  323. $tempOffset = 0;
  324. for ($i = 0; $i < substr_count($queryStringLower, $beginTagLower); ++$i) {
  325. $tempBeginFoundSetParams = strpos($queryStringLower, $beginTagLower, $tempOffset);
  326. $tempEndFoundSetParams = strpos($queryStringLower, $endTagLower, $tempOffset) + (strlen($endTagLower) - 1);
  327. $tempFoundSetParams = substr($_SERVER['QUERY_STRING'], $tempBeginFoundSetParams, ($tempEndFoundSetParams - $tempBeginFoundSetParams) + 1);
  328. $tempQueryString .= preg_replace("/(?i)$beginTagLower=[^&]*&(.*)&$endTagLower/", "\$1", $tempFoundSetParams);
  329. $tempOffset = $tempEndFoundSetParams;
  330. }
  331. } else {
  332. $tempQueryString = $_SERVER['QUERY_STRING'];
  333. }
  334. $tempQueryString = preg_replace("/skip=[\d]*[&]?/", "", $tempQueryString);
  335. }
  336. return $tempQueryString;
  337. }
  338. function AssembleDataSet ($returnData) {
  339. $dataSet = array();
  340. $FMNext = $this->currentSkip + $this->groupSize;
  341. $FMPrevious = $this->currentSkip - $this->groupSize;
  342. switch ($returnData) {
  343. case 'object':
  344. $dataSet = $this->currentData;
  345. if ($FMNext < $this->foundCount || $FMPrevious >= 0) {
  346. $tempQueryString = $this->BuildLinkQueryString();
  347. } else {
  348. $tempQueryString = '';
  349. }
  350. if ($FMNext >= $this->foundCount) {
  351. $this->lastLinkNext = "";
  352. } else {
  353. $this->lastLinkNext = $_SERVER['SCRIPT_NAME'] . "?skip=$FMNext&{$tempQueryString}";
  354. }
  355. if ($FMPrevious < 0) {
  356. $this->lastLinkPrevious = "";
  357. } else {
  358. $this->lastLinkPrevious = $_SERVER['SCRIPT_NAME'] . "?skip=$FMPrevious&{$tempQueryString}";
  359. }
  360. $this->lastFoundCount = $this->foundCount;
  361. $this->lastFields = $this->fieldInfo;
  362. $this->lastQuery = $this->dataQuery;
  363. $this->lastQueryParams = $this->dataParams;
  364. $this->lastErrorCode = $this->fxError;
  365. $this->lastValueLists = $this->valueLists;
  366. if (DEBUG_FUZZY && $this->lastErrorCode != 0) {
  367. require_once('FX_Fuzzy_Debugger.php');
  368. $fuzzyErrorData = new FX_Fuzzy_Debugger($this, $this->fuzzyFXPass);
  369. if ($fuzzyErrorData->fuzzyOut !== false) {
  370. $this->lastDebugMessage .= $fuzzyErrorData->fuzzyOut;
  371. }
  372. }
  373. break;
  374. case 'full':
  375. $dataSet['data'] = $this->currentData;
  376. if (defined('FX_OBJECTIVE')) {
  377. $dataSet['object'] = new ObjectiveFX($dataSet['data']);
  378. }
  379. case 'basic':
  380. if ($FMNext < $this->foundCount || $FMPrevious >= 0) {
  381. $tempQueryString = $this->BuildLinkQueryString();
  382. } else {
  383. $tempQueryString = '';
  384. }
  385. if ($FMNext >= $this->foundCount) {
  386. $dataSet['linkNext'] = "";
  387. } else {
  388. $dataSet['linkNext'] = $_SERVER['SCRIPT_NAME'] . "?skip=$FMNext&{$tempQueryString}";
  389. }
  390. if ($FMPrevious < 0) {
  391. $dataSet['linkPrevious'] = "";
  392. } else {
  393. $dataSet['linkPrevious'] = $_SERVER['SCRIPT_NAME'] . "?skip=$FMPrevious&{$tempQueryString}";
  394. }
  395. $this->lastFoundCount = $this->foundCount;
  396. $this->lastFields = $this->fieldInfo;
  397. $this->lastQuery = $this->dataQuery;
  398. $this->lastQueryParams = $this->dataParams;
  399. $this->lastErrorCode = $this->fxError;
  400. $this->lastValueLists = $this->valueLists;
  401. $dataSet['foundCount'] = $this->foundCount;
  402. $dataSet['fields'] = $this->fieldInfo;
  403. $dataSet['URL'] = $this->lastURL;
  404. $dataSet['query'] = $this->dataQuery;
  405. $dataSet['errorCode'] = $this->fxError;
  406. $dataSet['valueLists'] = $this->valueLists;
  407. if (DEBUG_FUZZY && $this->lastErrorCode != 0) {
  408. require_once('FX_Fuzzy_Debugger.php');
  409. $fuzzyErrorData = new FX_Fuzzy_Debugger($this, $this->fuzzyFXPass);
  410. if ($fuzzyErrorData !== false) {
  411. $this->lastDebugMessage .= $fuzzyErrorData;
  412. }
  413. }
  414. break;
  415. }
  416. $this->ClearAllParams();
  417. /*
  418. // Added to github 4/4-2014
  419. if( $this->useReturnJSONFullArrayResult == true ) {
  420. // Not sure if array_values() are needed
  421. $dataSet = json_encode( array_values( $dataSet ) );
  422. }
  423. */
  424. return $dataSet;
  425. }
  426. function FMAction ($Action, $returnDataSet, $returnData, $useInnerArray) {
  427. if ($this->useInnerArray === null) {
  428. $this->useInnerArray = $useInnerArray;
  429. }
  430. $queryResult = $this->ExecuteQuery($this->actionArray[strtolower($Action)]);
  431. if (FX::isError($queryResult)){
  432. if (EMAIL_ERROR_MESSAGES) {
  433. EmailErrorHandler($queryResult);
  434. }
  435. return $queryResult;
  436. }
  437. if ($returnDataSet) {
  438. $dataSet = $this->AssembleDataSet($returnData);
  439. return $dataSet;
  440. } else {
  441. $this->ClearAllParams();
  442. return true;
  443. }
  444. }
  445. // The functions above (with the exception of the FX constructor) are intened to be called from other functions within FX.php (i.e. private functions).
  446. // The functions below are those which are intended for general use by developers (i.e. public functions).
  447. // Once I'm quite sure that most people are using PHP5, I'll release a version using the improved object model of PHP5.
  448. function isError($data) {
  449. return (bool)(is_object($data) &&
  450. (strtolower(get_class($data)) == 'fx_error' ||
  451. is_subclass_of($data, 'fx_error')));
  452. }
  453. function SetCharacterEncoding ($encoding) { // This is the more general of the encoding functions (see notes below, and the functions documentation.)
  454. $this->charSet = $encoding;
  455. $this->dataParamsEncoding = $encoding;
  456. // When using a different type of encoding downstream than upstream, you must call this function -- SetCharacterEncoding() --
  457. // to set downstream encoding (the way data FROM the database is encoded) BEFORE calling SetDataParamsEncoding().
  458. // When this function is called alone, both instance valiables are set to the same value.
  459. // *IMPORTANT*: Using either this function or the next one is moot unless you have multi-byte support compliled into PHP (e.g. Complete PHP).
  460. }
  461. function SetDataParamsEncoding ($encoding) { // SetDataParamsEncoding() is used to specify the encoding of parameters sent to the database (upstream encoding.)
  462. $this->dataParamsEncoding = $encoding;
  463. }
  464. // the layout parameter is equivalent to the table to be used in SQL queries
  465. function SetDBData ($database, $layout="", $groupSize=50, $responseLayout="") {
  466. $this->database = $database;
  467. $this->layout = $layout;
  468. $this->groupSize = $groupSize;
  469. $this->responseLayout = $responseLayout;
  470. $this->ClearAllParams();
  471. $this->lastDebugMessage .= '<p>Configuring database connection...</p>';
  472. }
  473. function SetDBPassword ($DBPassword, $DBUser='FX') { // Note that for historical reasons, password is the FIRST parameter for this function
  474. if ($DBUser == '') {
  475. $DBUser = 'FX';
  476. }
  477. $this->DBPassword = $DBPassword;
  478. $this->DBUser = $DBUser;
  479. $this->lastDebugMessage .= '<p>Setting user name and password...</p>';
  480. }
  481. function SetDBUserPass ($DBUser, $DBPassword='') { // Same as above function, but paramters are in the opposite order
  482. $this->SetDBPassword($DBPassword, $DBUser);
  483. }
  484. function SetCustomPrimaryKey( $fieldname ) {
  485. $this->customPrimaryKey = $fieldname;
  486. }
  487. function SetNumberAutoConversionComma2PeriodForDecimal( ) {
  488. $this->useComma2Period = true;
  489. /* $this->fieldInfo[$i]['type']
  490. if( $this->fieldInfo[$i]['type'] == "NUMBER" && useComma2Period == true ){
  491. $this->fieldContent = str_replace( ',', '.', $this->fieldContent );
  492. }
  493. */
  494. }
  495. function SetDefaultOperator ($op) {
  496. $this->defaultOperator = $op;
  497. return true;
  498. }
  499. // start of findquery section
  500. /**
  501. * Returns the "q" number (q1, q2, etc) if a duplicate name/value pair exists already, else returns a false value (q cannot be 0 anyway).
  502. *
  503. * @param mixed $name
  504. * @param mixed $value
  505. */
  506. function FindQuery_DuplicateExists($name, $value)
  507. {
  508. $currentParamList = $this->GetKeyPairDataParams();
  509. for($c = 1; $c <= $this->findquerynumber; $c++)
  510. {
  511. if (isset($currentParamList['-q'.$c]))
  512. {
  513. $cname = $currentParamList['-q'.$c];
  514. $cvalue = $currentParamList['-q'.$c.'.value'];
  515. if ($cname == $name && $cvalue == $value)
  516. {
  517. return $c;
  518. }
  519. }
  520. }
  521. return false;
  522. }
  523. /**
  524. * when using FMFindQuery, appends name and value pairs to the findquery query string. optionally (doModify=false), returns the string for further manipulation.
  525. * example: $searchFields = array();
  526. $searchFields[] = array('zAssignedGroup::ID_Group', $group);
  527. $searchFields[] = array('DateClosed', $startdate.'...'.$enddate);
  528. $wo_find->FindQuery_Append($searchFields);
  529. * note the two arrays, to allow multiple of the same key in one find, to handle fields with multiple values separated by return.
  530. * @param mixed $namevaluepair
  531. * @param mixed $doModify
  532. */
  533. function FindQuery_Append($namevaluepair = array(), $doModify = true)
  534. {
  535. if (is_array($namevaluepair) && count($namevaluepair) > 0)
  536. {
  537. $appendquerystring = '';
  538. foreach($namevaluepair as $fieldInfo) // fieldInfo is just an array with [0] as name, and [1] as data
  539. {
  540. $foundFlagQNumber = $this->FindQuery_DuplicateExists($fieldInfo[0], $fieldInfo[1]);
  541. if (!$foundFlagQNumber)
  542. {
  543. $this->AddDBParam('-q'.$this->findquerynumber, $fieldInfo[0]);
  544. $this->AddDBParam('-q'.$this->findquerynumber.'.value', $fieldInfo[1]);
  545. // add the find to the end of the string
  546. $appendquerystring .= ',q'.$this->findquerynumber;
  547. $this->findquerynumber++;
  548. } else {
  549. // the exact namevalue pair was found and attempted to be searched on again.
  550. $appendquerystring .= ',q'.$foundFlagQNumber;
  551. }
  552. }
  553. if ($appendquerystring != '')
  554. {
  555. $appendquerystring = substr($appendquerystring, 1); // strip beginning comma.
  556. if ($doModify)
  557. {
  558. $this->findquerystring .= ';('.$appendquerystring . ')';
  559. } else {
  560. return ';('.$appendquerystring . ')';
  561. }
  562. }
  563. }
  564. }
  565. /**
  566. * exact duplicate of FindQuery_Append except for the '!' near teh end..
  567. *
  568. * @param mixed $namevaluepair
  569. * @param mixed $doModify
  570. */
  571. function FindQuery_Omit($namevaluepair = array(), $doModify = true)
  572. {
  573. $str = $this->FindQuery_Append($namevaluepair, false); // send false to not modify the internal query string.
  574. if ($doModify)
  575. {
  576. // the string will come back looking like: ;(q1) so we have to strip the initial semicolon.
  577. $this->findquerystring .= ';!'.substr($str, 1);
  578. } else {
  579. return ';!'.substr($str, 1);
  580. }
  581. }
  582. function GetKeyPairDataParams()
  583. {
  584. $temp = array();
  585. foreach($this->dataParams as $key=>$row)
  586. {
  587. $name = $row['name'];
  588. $value = $row['value'];
  589. $temp[$name] = $value;
  590. }
  591. return $temp;
  592. }
  593. /**
  594. * Fields will be an array of fields you want to make an AND find on.
  595. * the second param will be the querystring to be used.
  596. * @param mixed $fields
  597. */
  598. function FindQuery_AND($namevaluepair = array(), $fieldnames = array(), $querystring = '', $doModify = true)
  599. {
  600. if ($querystring == '')
  601. {
  602. $querystring = $this->findquerystring; // use the internal querystring by default.
  603. }
  604. $qnumlist = array(); // used to keep a list of the qnum we are ANDing.
  605. if (is_array($namevaluepair) && count($namevaluepair) > 0)
  606. {
  607. foreach($namevaluepair as $fieldInfo)
  608. {
  609. $qnum = $this->FindQuery_DuplicateExists($fieldInfo[0], $fieldInfo[1]);
  610. if (!$qnum)
  611. {
  612. $this->FindQuery_Append(array(array($fieldInfo[0], $fieldInfo[1])), false); // add parameters to the list of possible query params but don't modify the query yet.
  613. $qnum = $this->FindQuery_DuplicateExists($fieldInfo[0], $fieldInfo[1]); // find the q number after it has been created in the dataParams.
  614. }
  615. if ($qnum !== false) $qnumlist[] = $qnum;
  616. }
  617. if ($this->findquerystring == '') // if starting with an AND, then do this
  618. {
  619. foreach($qnumlist as $num)
  620. {
  621. // make sure that the query data is not already in this section ex: (q2,q2) is illegal
  622. $newquerystring .= ',q'.$num;
  623. }
  624. $newquerystring = ';('. substr($newquerystring, 1) .')'; // strip off initial comma
  625. } else {
  626. $findquerypieces = explode(';', $this->findquerystring);
  627. $newquerystring = '';
  628. foreach ($findquerypieces as $findquerypiece)
  629. {
  630. if (!empty($findquerypiece))
  631. {
  632. $newquerystring .= ';'.substr($findquerypiece, 0, (strlen($findquerypiece)-1));
  633. if (strpos($findquerypiece, '!') === false)
  634. {
  635. // if (count($fieldnames) == 0 || in_array( /// check for field in dataParams for this query piece
  636. foreach($qnumlist as $num)
  637. {
  638. // make sure that the query data is not already in this section ex: (q2,q2) is illegal
  639. if (strpos($findquerypiece, 'q'.$num.')') == false && strpos($findquerypiece, 'q'.$num.',') === false)
  640. {
  641. $newquerystring .= ',q'.$num;
  642. }
  643. }
  644. }
  645. $newquerystring .= ')';
  646. }
  647. // else {
  648. // $newquerystring .= ';'.$findquerypiece;
  649. // }
  650. }
  651. }
  652. }
  653. $this->findquerystring = $newquerystring;
  654. }
  655. // end of findquery section
  656. function AddDBParam ($name, $value, $op="") { // Add a search parameter. An operator is usually not necessary.
  657. if ($this->dataParamsEncoding != '' && function_exists('mb_convert_encoding')) {
  658. $this->dataParams[]["name"] = mb_convert_encoding($name, $this->dataParamsEncoding, $this->charSet);
  659. end($this->dataParams);
  660. $convedValue = mb_convert_encoding($value, $this->dataParamsEncoding, $this->charSet);
  661. /* Masayuki Nii added at Oct 10, 2009 */
  662. if (!defined('SURROGATE_INPUT_PATCH_DISABLED') && $this->charSet == 'UTF-8' && $this->dataServerVersion < 12) {
  663. $count = 0;
  664. for ($i=0; $i< strlen($value); $i++) {
  665. $c = ord(substr( $value, $i, 1 ));
  666. if ( ( $c == 0xF0 )&&( (ord(substr( $value, $i+1, 1 )) & 0xF0) == 0xA0 )) {
  667. $i += 4; $count++;
  668. }
  669. }
  670. $convedValue .= str_repeat( mb_convert_encoding(chr(0xE3).chr(0x80).chr(0x80), $this->dataParamsEncoding, 'UTF-8'), $count );
  671. }
  672. $this->dataParams[key($this->dataParams)]["value"] = $convedValue;
  673. // =======================
  674. } else {
  675. $this->dataParams[]["name"] = $name;
  676. end($this->dataParams);
  677. $this->dataParams[key($this->dataParams)]["value"] = $value;
  678. }
  679. $this->dataParams[key($this->dataParams)]["op"] = $op;
  680. }
  681. function AddDBParamArray ($paramsArray, $paramOperatorsArray=array()) { // Add an array of search parameters. An operator is usually not necessary.
  682. foreach ($paramsArray as $key => $value) {
  683. if (isset($paramOperatorsArray[$key]) && strlen(trim($paramOperatorsArray[$key])) > 0) {
  684. $this->AddDBParam($key, $value, $paramOperatorsArray[$key]);
  685. } else {
  686. $this->AddDBParam($key, $value);
  687. }
  688. }
  689. }
  690. function SetPortalRow ($fieldsArray, $portalRowID=0, $relationshipName='') {
  691. foreach ($fieldsArray as $fieldName => $fieldValue) {
  692. if (strlen(trim($relationshipName)) > 0 && substr_count($fieldName, '::') < 1) {
  693. $this->AddDBParam("{$relationshipName}::{$fieldName}.{$portalRowID}", $fieldValue);
  694. } else {
  695. $this->AddDBParam("{$fieldName}.{$portalRowID}", $fieldValue);
  696. }
  697. }
  698. }
  699. function SetRecordID ($recordID) {
  700. if (! is_numeric($recordID) || (intval($recordID) != $recordID)) {
  701. if ((defined("DEBUG") and DEBUG) or DEBUG_FUZZY) {
  702. $currentDebugString = "<p>RecordIDs must be integers. Value passed was &quot;{$recordID}&quot;.</p>\n";
  703. $this->lastDebugMessage .= $currentDebugString;
  704. if (defined("DEBUG") and DEBUG) {
  705. echo $currentDebugString;
  706. }
  707. }
  708. }
  709. $this->AddDBParam('-recid', $recordID);
  710. }
  711. function SetModID ($modID) {
  712. if (! is_numeric($modID) || (intval($modID) != $modID)) {
  713. if ((defined("DEBUG") and DEBUG) or DEBUG_FUZZY) {
  714. $currentDebugString = "<p>ModIDs must be integers. Value passed was &quot;{$modID}&quot;.</p>\n";
  715. $this->lastDebugMessage .= $currentDebugString;
  716. if (defined("DEBUG") and DEBUG) {
  717. echo $currentDebugString;
  718. }
  719. }
  720. }
  721. $this->AddDBParam('-modid', $modID);
  722. }
  723. function SetLogicalOR () {
  724. $this->AddDBParam('-lop', 'or');
  725. }
  726. // FileMaker 7 only
  727. function SetFMGlobal ($globalFieldName, $globalFieldValue) {
  728. $this->AddDBParam("{$globalFieldName}.global", $globalFieldValue);
  729. }
  730. function PerformFMScript ($scriptName) { // This function is only meaningful when working with FileMaker data sources
  731. $this->AddDBParam('-script', $scriptName);
  732. }
  733. function PerformFMScriptPrefind ($scriptName) { // This function is only meaningful when working with FileMaker data sources
  734. $this->AddDBParam('-script.prefind', $scriptName);
  735. }
  736. function PerformFMScriptPresort ($scriptName) { // This function is only meaningful when working with FileMaker data sources
  737. $this->AddDBParam('-script.presort', $scriptName);
  738. }
  739. function AddSortParam ($field, $sortOrder='', $performOrder=0) { // Add a sort parameter. An operator is usually not necessary.
  740. if ($performOrder > 0) {
  741. $this->sortParams[$performOrder]["field"] = $field;
  742. $this->sortParams[$performOrder]["sortOrder"] = $sortOrder;
  743. } else {
  744. if (count($this->sortParams) == 0) {
  745. $this->sortParams[1]["field"] = $field;
  746. } else {
  747. $this->sortParams[]["field"] = $field;
  748. }
  749. end($this->sortParams);
  750. $this->sortParams[key($this->sortParams)]["sortOrder"] = $sortOrder;
  751. }
  752. }
  753. function FMSkipRecords ($skipSize) {
  754. $this->currentSkip = $skipSize;
  755. }
  756. function FMPostQuery ($isPostQuery = true) {
  757. $this->isPostQuery = $isPostQuery;
  758. }
  759. function FMFOpenQuery ($isFOpenQuery = true) {
  760. $this->isFOpenQuery = $isFOpenQuery;
  761. }
  762. function FMUseCURL ($useCURL = true) {
  763. $this->useCURL = $useCURL;
  764. }
  765. // By default, FX.php adds an extra layer to the returned array to allow for repeating fields and portals.
  766. // When these are not present, or when accessing SQL data, this may not be desirable. FlattenInnerArray() removes this extra layer.
  767. function FlattenInnerArray () {
  768. $this->useInnerArray = false;
  769. }
  770. // This will give you the fields and contents pr record as JSON
  771. function ReturnJSON () {
  772. $this->useReturnJSONResult = false;
  773. }
  774. // This will give you the whole FMPXMLRESULT as JSON
  775. function ReturnJSONFullArray () {
  776. $this->useReturnJSONFullArrayResult = false;
  777. }
  778. /* The actions that you can send to FileMaker start here */
  779. function FMDBOpen () {
  780. $queryResult = $this->ExecuteQuery("-dbopen");
  781. if (FX::isError($queryResult)){
  782. return $queryResult;
  783. }
  784. }
  785. function FMDBClose () {
  786. $queryResult = $this->ExecuteQuery("-dbclose");
  787. if (FX::isError($queryResult)){
  788. return $queryResult;
  789. }
  790. }
  791. function FMDelete ($returnDataSet = false, $returnData = 'basic', $useInnerArray = true) {
  792. return $this->FMAction("-delete", $returnDataSet, $returnData, $useInnerArray);
  793. }
  794. function FMDup ($returnDataSet = true, $returnData = 'full', $useInnerArray = true) {
  795. return $this->FMAction("-dup", $returnDataSet, $returnData, $useInnerArray);
  796. }
  797. function FMEdit ($returnDataSet = true, $returnData = 'full', $useInnerArray = true) {
  798. return $this->FMAction("-edit", $returnDataSet, $returnData, $useInnerArray);
  799. }
  800. function FMFind ($returnDataSet = true, $returnData = 'full', $useInnerArray = true) {
  801. return $this->FMAction("-find", $returnDataSet, $returnData, $useInnerArray);
  802. }
  803. function FMFindAll ($returnDataSet = true, $returnData = 'full', $useInnerArray = true) {
  804. return $this->FMAction("-findall", $returnDataSet, $returnData, $useInnerArray);
  805. }
  806. function FMFindAny ($returnDataSet = true, $returnData = 'full', $useInnerArray = true) {
  807. return $this->FMAction("-findany", $returnDataSet, $returnData, $useInnerArray);
  808. }
  809. function FMFindQuery ($returnDataSet = true, $returnData = 'full', $useInnerArray = true)
  810. {
  811. return $this->FMAction("-findquery", $returnDataSet, $returnData, $useInnerArray);
  812. }
  813. function FMNew ($returnDataSet = true, $returnData = 'full', $useInnerArray = true) {
  814. return $this->FMAction("-new", $returnDataSet, $returnData, $useInnerArray);
  815. }
  816. function FMView ($returnDataSet = true, $returnData = 'full', $useInnerArray = true) {
  817. return $this->FMAction("-view", $returnDataSet, $returnData, $useInnerArray);
  818. }
  819. function FMDBNames ($returnDataSet = true, $returnData = 'full', $useInnerArray = true) {
  820. return $this->FMAction("-dbnames", $returnDataSet, $returnData, $useInnerArray);
  821. }
  822. function FMLayoutNames ($returnDataSet = true, $returnData = 'full', $useInnerArray = true) {
  823. return $this->FMAction("-layoutnames", $returnDataSet, $returnData, $useInnerArray);
  824. }
  825. function FMScriptNames ($returnDataSet = true, $returnData = 'full', $useInnerArray = true) {
  826. return $this->FMAction("-scriptnames", $returnDataSet, $returnData, $useInnerArray);
  827. }
  828. // DoFXAction() is a general purpose action function designed to streamline FX.php code
  829. function DoFXAction ($currentAction, $returnDataSet = true, $useInnerArray = false, $returnType = 'object') {
  830. return $this->FMAction($currentAction, $returnDataSet, $returnType, $useInnerArray);
  831. }
  832. /* The actions that you can send to FileMaker end here */
  833. // PerformSQLQuery() is akin to the FileMaker actions above with two differences:
  834. // 1) It is SQL specific
  835. // 2) The SQL query passed is the sole determinant of the query performed (AddDBParam, etc. will be ignored)
  836. function PerformSQLQuery ($SQLQuery, $returnDataSet = true, $useInnerArray = false, $returnData = 'object') {
  837. $this->dataQuery = $SQLQuery;
  838. return $this->FMAction("-sqlquery", $returnDataSet, $returnData, $useInnerArray);
  839. }
  840. // SetDataKey() is used for SQL queries as a way to provide parity with the RecordID/ModID combo provided by FileMaker Pro
  841. function SetDataKey ($keyField, $modifyField = '', $separator = '.') {
  842. $this->primaryKeyField = $keyField;
  843. $this->modifyDateField = $modifyField;
  844. $this->dataKeySeparator = $separator;
  845. return true;
  846. }
  847. // SetSelectColumns() allows users to specify which columns should be returned by an SQL SELECT statement
  848. function SetSelectColumns ($columnList) {
  849. $this->selectColsSet = true;
  850. $this->selectColumns = $columnList;
  851. return true;
  852. }
  853. // SQLFuzzyKeyLogicOn() can be used to have FX.php make it's best guess as to a viable key in an SQL DB
  854. function SQLFuzzyKeyLogicOn ($logicSwitch = false) {
  855. $this->fuzzyKeyLogic = $logicSwitch;
  856. return true;
  857. }
  858. // By default, FX.php uses records' keys as the indices for the returned array. UseGenericKeys() is used to change this behavior.
  859. function UseGenericKeys ($genericKeys=true) {
  860. $this->genericKeys = $genericKeys;
  861. return true;
  862. }
  863. // Added by Masayuki Nii(nii@msyk.net) Dec 18, 2010
  864. // Modified by msyk, Feb 1-6, 2012
  865. function RemainAsArray (
  866. $rArray1,$rArray2=NULL,$rArray3=NULL,$rArray4=NULL,$rArray5=NULL,
  867. $rArray6=NULL,$rArray7=NULL,$rArray8=NULL,$rArray9=NULL,$rArray10=NULL,
  868. $rArray11=NULL,$rArray12=NULL) {
  869. $this->portalAsRecord = false;
  870. $counter = 0;
  871. for ( $i=1 ; $i<13 ; $i++ ) {
  872. $valName = "rArray{$i}";
  873. if ( ! isset($$valName) ) {
  874. break;
  875. }
  876. if (is_array($$valName)) {
  877. $this->portalAsRecord = true;
  878. $isFirstTime = true;
  879. $firstItemName = '';
  880. foreach($$valName as $item) {
  881. if($isFirstTime) {
  882. $isFirstTime = false;
  883. $firstItemName = $item;
  884. $this->remainNamesReverse[$item] = true;
  885. } else {
  886. $this->remainNamesReverse[$item] = $firstItemName;
  887. }
  888. $this->remainNames[$counter] = $item;
  889. $counter++;
  890. }
  891. } else {
  892. $this->remainNames[$counter] = $$valName;
  893. $this->remainNamesReverse[$$valName] = true;
  894. $counter++;
  895. }
  896. }
  897. }
  898. }
  899. ?>