PageRenderTime 52ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/QuickBooks/Cast.php

https://github.com/bigtunacan/quickbooks-php
PHP | 646 lines | 323 code | 68 blank | 255 comment | 40 complexity | c853189b938b96c5c3a3ea0e66e4e813 MD5 | raw file
Possible License(s): EPL-1.0
  1. <?php
  2. /**
  3. * Casts to cast data types to QuickBooks qbXML values and fit values in qbXML fields
  4. *
  5. * Copyright (c) 2010 Keith Palmer / ConsoliBYTE, LLC.
  6. * All rights reserved. This program and the accompanying materials
  7. * are made available under the terms of the Eclipse Public License v1.0
  8. * which accompanies this distribution, and is available at
  9. * http://www.opensource.org/licenses/eclipse-1.0.php
  10. *
  11. * @package QuickBooks
  12. * @subpackage Cast
  13. */
  14. /**
  15. * QuickBooks casts and data types
  16. */
  17. class QuickBooks_Cast
  18. {
  19. static public function datatype($type_or_action, $field)
  20. {
  21. }
  22. static public function maxlength($type_or_action, $field)
  23. {
  24. }
  25. /**
  26. * Make sure all characters are ASCII (and thus will work with UTF-8)
  27. *
  28. * We have a lot of content that gets pasted in from Microsoft Word, which
  29. * often generates some very wierd characters, which seem to not always
  30. * convert correctly to UTF-8 and then get rejected by the QuickBooks Web
  31. * Connector. This method tries to make sure that everything that should
  32. * get converted gets converted, and gets converted cleanly.
  33. *
  34. * There is probably a better way to do this...
  35. *
  36. * @param string $str The string to convert to ASCII
  37. * @return string
  38. */
  39. /*
  40. static protected function _castCharset($str)
  41. {
  42. // These extended ASCII characters get mapped to things in the normal ASCII character set
  43. $replace = array(
  44. chr(129) => 'u',
  45. chr(130) => 'e',
  46. chr(131) => 'a',
  47. chr(132) => 'a',
  48. chr(133) => 'a',
  49. chr(134) => 'a',
  50. chr(136) => 'e',
  51. chr(137) => 'e',
  52. chr(138) => 'e',
  53. chr(139) => 'i',
  54. chr(140) => 'i',
  55. chr(141) => 'i',
  56. chr(142) => 'A',
  57. chr(143) => 'A',
  58. chr(144) => 'E',
  59. chr(145) => 'ae',
  60. chr(146) => 'AE',
  61. chr(147) => 'o',
  62. chr(148) => 'o',
  63. chr(149) => 'o',
  64. chr(150) => 'u',
  65. chr(151) => 'u',
  66. chr(152) => '_',
  67. chr(153) => 'O',
  68. chr(154) => 'U',
  69. chr(158) => '_',
  70. chr(160) => 'a',
  71. chr(161) => 'i',
  72. chr(162) => 'o',
  73. chr(163) => 'u',
  74. chr(164) => 'n',
  75. chr(165) => 'N',
  76. chr(173) => 'i',
  77. chr(174) => '<',
  78. chr(175) => '>',
  79. chr(179) => '|',
  80. chr(196) => '-',
  81. chr(242) => '>=',
  82. chr(243) => '<=',
  83. chr(246) => '/',
  84. chr(247) => '~',
  85. chr(249) => '.',
  86. chr(250) => '.',
  87. chr(252) => '_',
  88. );
  89. $count = strlen($str);
  90. for ($i = 0; $i < $count; $i++)
  91. {
  92. $ord = ord($str{$i});
  93. if ($ord != ord("\t") and
  94. $ord != ord("\n") and
  95. $ord != ord("\r") and
  96. ($ord < 32 or $ord > 126))
  97. {
  98. if (isset($replace[$ord]))
  99. {
  100. $str{$i} = $replace[$ord];
  101. }
  102. else
  103. {
  104. $str{$i} = ' ';
  105. }
  106. }
  107. }
  108. return $str;
  109. }
  110. */
  111. /**
  112. * Convert certain strings to their abbreviations
  113. *
  114. * QuickBooks often uses unusually short field lengths. This function will
  115. * convert common long words to shorter abbreviations in an attempt to make
  116. * a string fit cleanly into the very short fields.
  117. *
  118. * @param string $value The value to apply the abbreviations to
  119. * @return string
  120. */
  121. static protected function _castAbbreviations($value)
  122. {
  123. $abbrevs = array(
  124. 'Administration' => 'Admin.',
  125. 'Academic' => 'Acad.',
  126. 'Academy' => 'Acad.',
  127. 'Association' => 'Assn.',
  128. 'Boulevard' => 'Blvd.',
  129. 'Building' => 'Bldg.',
  130. 'College' => 'Coll.',
  131. 'Company' => 'Co.',
  132. 'Consolidated' => 'Consol.',
  133. 'Corporation' => 'Corp.',
  134. 'Incorporated' => 'Inc.',
  135. 'Department' => 'Dept.',
  136. 'Division' => 'Div.',
  137. 'District' => 'Dist.',
  138. 'Eastern' => 'E.',
  139. 'Government' => 'Govt.',
  140. 'International' => 'Intl.',
  141. 'Institute' => 'Inst.',
  142. 'Institution' => 'Inst.',
  143. 'Laboratory' => 'Lab.',
  144. 'Liberty' => 'Lib.',
  145. 'Library' => 'Lib.',
  146. 'Limited' => 'Ltd.',
  147. 'Manufacturing' => 'Mfg.',
  148. 'Manufacturer' => 'Mfr.',
  149. 'Miscellaneous' => 'Misc.',
  150. 'Museum' => 'Mus.',
  151. 'Northern' => 'N.',
  152. 'Northeastern' => 'NE', // This is *before* Northeast so we don't get "NEern"
  153. 'Northeast' => 'NE',
  154. 'Regional' => 'Reg.', // This is *before* Region so we don't get "Reg.al"
  155. 'Region' => 'Reg.',
  156. 'School' => 'Sch.',
  157. 'Services' => 'Svcs.', // This is *before* Service so that we don't get "Svc.s"
  158. 'Service' => 'Svc.',
  159. 'Southern' => 'S.',
  160. 'Southeastern' => 'SE',
  161. 'Southeast' => 'SE',
  162. 'University' => 'Univ.',
  163. 'Western' => 'W.',
  164. );
  165. return str_ireplace(array_keys($abbrevs), array_values($abbrevs), $value);
  166. }
  167. /**
  168. * Shorten a string to a specific length by truncating or abbreviating the string
  169. *
  170. * QuickBooks often uses unusually short field lengths. This function can
  171. * be used to try to make long strings fit cleanly into the QuickBooks
  172. * fields. It tries to do a few things:
  173. * - Convert long words to shorter abbreviations
  174. * - Remove non-ASCII characters
  175. * - Truncate the string if it's still too long
  176. *
  177. * @param string $value The string to shorten
  178. * @param integer $length The max. length the string should be
  179. * @param boolean $with_abbrevs Whether or not to abbreviate some long words to shorten the string
  180. * @return string The shortened string
  181. */
  182. static protected function _castTruncate($value, $length, $with_abbrevs = true)
  183. {
  184. //$value = QuickBooks_Cast::_castCharset($value);
  185. if (strlen($value) > $length)
  186. {
  187. if ($with_abbrevs)
  188. {
  189. $value = QuickBooks_Cast::_castAbbreviations($value);
  190. }
  191. if (strlen($value) > $length)
  192. {
  193. $value = substr($value, 0, $length);
  194. }
  195. }
  196. // This breaks the UTF8 encoding
  197. //return utf8_encode($value);
  198. // Just return the data
  199. return $value;
  200. }
  201. /**
  202. *
  203. *
  204. *
  205. */
  206. static protected function _fnmatch($pattern, $str)
  207. {
  208. $arr = array(
  209. '\*' => '.*',
  210. '\?' => '.'
  211. );
  212. return preg_match('#^' . strtr(preg_quote($pattern, '#'), $arr) . '$#i', $str);
  213. }
  214. /**
  215. * Cast a value to ensure that it will fit in a particular field within QuickBooks
  216. *
  217. * QuickBooks has some strange length limits on some fields (the max.
  218. * length of the CompanyName field for Customers is only 41 characters,
  219. * etc.) so this method provides an easy way to cast the data type and data
  220. * length of a value to the correct type and length for a specific field.
  221. *
  222. * @param string $object_type The QuickBooks object type (Customer, Invoice, etc.)
  223. * @param string $field_name The QuickBooks field name (these correspond to the qbXML field names: Addr1, Name, CompanyName, etc.)
  224. * @param mixed $value The value you want to cast
  225. * @param boolean $use_abbrevs There are a lot of strings which can be abbreviated to shorten lengths, this is whether or not you want to use those abbrevaitions ("University" to "Univ.", "Incorporated" to "Inc.", etc.)
  226. * @param boolean $htmlspecialchars
  227. * @return string
  228. */
  229. static public function cast($type_or_action, $field, $value, $use_abbrevs = true, $htmlspecialchars = true)
  230. {
  231. $type_or_action = strtolower($type_or_action);
  232. static $files = array();
  233. if (!count($files))
  234. {
  235. $dh = opendir(dirname(__FILE__) . '/QBXML/Schema/Object');
  236. while (false !== ($file = readdir($dh)))
  237. {
  238. if ($file{0} == '.' or substr($file, -6, 6) != 'Rq.php')
  239. {
  240. continue;
  241. }
  242. $files[] = $file;
  243. }
  244. sort($files);
  245. }
  246. /*
  247. if ($htmlspecialchars)
  248. {
  249. $entities = array(
  250. '&' => '&amp;',
  251. '<' => '&lt;',
  252. '>' => '&gt;',
  253. //'\'' => '&apos;',
  254. '"' => '&quot;',
  255. );
  256. // First, *unreplace* things so that we don't double escape them
  257. $value = str_replace(array_values($entities), array_keys($entities), $value);
  258. // Then, replace XML entities
  259. $value = str_replace(array_keys($entities), array_values($entities), $value);
  260. //$value = htmlspecialchars($value, ENT_QUOTES, null, false);
  261. }
  262. */
  263. $types = array();
  264. $types3 = array();
  265. $types5 = array();
  266. reset($files);
  267. foreach ($files as $file)
  268. {
  269. $substr = substr($file, 0, -4);
  270. $substrlower = strtolower($substr);
  271. $types[$substrlower] = $substr;
  272. $substr3 = substr($file, 0, -3 + -3);
  273. $substr3lower = strtolower($substr3);
  274. $substr5 = substr($file, 0, -3 + -6);
  275. $substr5lower = strtolower($substr5);
  276. if (!isset($types3[$substr3lower]))
  277. {
  278. $types3[$substr3lower] = $substr;
  279. }
  280. if (!isset($types5[$substr5lower]))
  281. {
  282. $types5[$substr5lower] = $substr;
  283. }
  284. }
  285. /*
  286. print(' looking for schema: ' . $type_or_action . "\n");
  287. print_r($types);
  288. print_r($types3);
  289. print_r($types5);
  290. */
  291. $class = null;
  292. $schema = null;
  293. if (isset($types[$type_or_action]))
  294. {
  295. QuickBooks_Loader::load('/QuickBooks/QBXML/Schema/Object/' . $types[$type_or_action] . '.php');
  296. $class = 'QuickBooks_QBXML_Schema_Object_' . $types[$type_or_action];
  297. $schema = new $class();
  298. }
  299. else if (isset($types3[$type_or_action])) // substr -3
  300. {
  301. QuickBooks_Loader::load('/QuickBooks/QBXML/Schema/Object/' . $types3[$type_or_action] . '.php');
  302. $class = 'QuickBooks_QBXML_Schema_Object_' . $types3[$type_or_action];
  303. $schema = new $class();
  304. }
  305. else if (isset($types5[$type_or_action])) // substr -5
  306. {
  307. QuickBooks_Loader::load('/QuickBooks/QBXML/Schema/Object/' . $types5[$type_or_action] . '.php');
  308. $class = 'QuickBooks_QBXML_Schema_Object_' . $types5[$type_or_action];
  309. $schema = new $class();
  310. }
  311. //else
  312. //{
  313. // return $value;
  314. //}
  315. //print(' casting using schema: ' . get_class($schema) . "\n");
  316. if ($class and $schema)
  317. {
  318. if (!$schema->exists($field) and false !== strpos($field, '_'))
  319. {
  320. $field = str_replace('_', ' ', $field);
  321. }
  322. if ($schema->exists($field))
  323. {
  324. switch ($schema->dataType($field))
  325. {
  326. case QUICKBOOKS_DATATYPE_STRING:
  327. $maxlength = $schema->maxLength($field);
  328. // Use only ASCII characters
  329. //$value = QuickBooks_Cast::_castCharset($value);
  330. // Make sure it'll fit in the allocated field length
  331. if (is_int($maxlength) and $maxlength > 0)
  332. {
  333. $value = QuickBooks_Cast::_castTruncate($value, $maxlength, $use_abbrevs);
  334. }
  335. break;
  336. case QUICKBOOKS_DATATYPE_DATE:
  337. if ($value)
  338. {
  339. $value = date('Y-m-d', strtotime($value));
  340. }
  341. break;
  342. case QUICKBOOKS_DATATYPE_DATETIME:
  343. if ($value)
  344. {
  345. $value = date('Y-m-d', strtotime($value)) . 'T' . date('H:i:s', strtotime($value));
  346. }
  347. break;
  348. case QUICKBOOKS_DATATYPE_ENUM:
  349. // do nothing
  350. break;
  351. case QUICKBOOKS_DATATYPE_ID:
  352. // do nothing
  353. break;
  354. case QUICKBOOKS_DATATYPE_FLOAT:
  355. $value = (float) $value;
  356. break;
  357. case QUICKBOOKS_DATATYPE_BOOLEAN:
  358. if ($value and $value !== 'false')
  359. {
  360. $value = 'true';
  361. }
  362. else
  363. {
  364. $value = 'false';
  365. }
  366. break;
  367. case QUICKBOOKS_DATATYPE_INTEGER:
  368. $value = (int) $value;
  369. break;
  370. }
  371. }
  372. }
  373. /*
  374. if ($htmlspecialchars)
  375. {
  376. $entities = array(
  377. '&' => '&amp;',
  378. '<' => '&lt;',
  379. '>' => '&gt;',
  380. //'\'' => '&apos;',
  381. '"' => '&quot;',
  382. );
  383. // First, *unreplace* things so that we don't double escape them
  384. $value = str_replace(array_values($entities), array_keys($entities), $value);
  385. // Then, replace XML entities
  386. $value = str_replace(array_keys($entities), array_values($entities), $value);
  387. //$value = htmlspecialchars($value, ENT_QUOTES, null, false);
  388. }
  389. */
  390. if ($htmlspecialchars)
  391. {
  392. //print("DECODING");
  393. $entities = array(
  394. '&' => '&amp;',
  395. '<' => '&lt;',
  396. '>' => '&gt;',
  397. //'\'' => '&apos;',
  398. '"' => '&quot;',
  399. );
  400. // First, *unreplace* things so that we don't double escape them
  401. $value = str_replace(array_values($entities), array_keys($entities), $value);
  402. // Then, replace XML entities
  403. $value = str_replace(array_keys($entities), array_values($entities), $value);
  404. //$value = htmlspecialchars($value, ENT_QUOTES, null, false);
  405. //print($value . "\n\n\n");
  406. // UTF8 character handling, decode UTF8 to character decimal codes
  407. $value = QuickBooks_Cast::_decodeUTF8($value);
  408. //die($value . "\n\n");
  409. }
  410. return $value;
  411. }
  412. /**
  413. * Test a string to see if it has 8 bit symbols in it
  414. *
  415. * @param string $string
  416. * @param string $charset
  417. * @return bool
  418. */
  419. static protected function _is8Bit($string, $charset = '')
  420. {
  421. if (preg_match("/^iso-8859/i", $charset))
  422. {
  423. $needle = '/\240|[\241-\377]/';
  424. }
  425. else
  426. {
  427. $needle = '/[\200-\237]|\240|[\241-\377]/';
  428. }
  429. return preg_match("$needle", $string);
  430. }
  431. /**
  432. * Converts string from an encoded string to UTF8
  433. *
  434. * @param string $string Text with numeric unicode entities
  435. * @return string UTF-8 text
  436. */
  437. static protected function _encodeUTF8($string)
  438. {
  439. // Don't run encoding function, if there is no encoded characters
  440. if (!preg_match("'&#[0-9]+;'", $string))
  441. {
  442. return $string;
  443. }
  444. $string = preg_replace("/&#([0-9]+);/e", "QuickBooks_Cast_unicodetoutf8('\\1')", $string);
  445. // $string=preg_replace("/&#[xX]([0-9A-F]+);/e","unicodetoutf8(hexdec('\\1'))",$string);
  446. return $string;
  447. }
  448. /**
  449. * Decode a UTF-8 string to an entity encoded string
  450. *
  451. * @param string $string Encoded string
  452. * @return string Decoded string
  453. */
  454. static protected function _decodeUTF8($string)
  455. {
  456. // don't do decoding when there are no 8bit symbols
  457. if (!QuickBooks_Cast::_is8Bit($string, 'utf-8'))
  458. {
  459. return $string;
  460. }
  461. // decode four byte unicode characters
  462. $string = preg_replace(
  463. "/([\360-\367])([\200-\277])([\200-\277])([\200-\277])/e",
  464. "'&#'.((ord('\\1')-240)*262144+(ord('\\2')-128)*4096+(ord('\\3')-128)*64+(ord('\\4')-128)).';'",
  465. $string);
  466. // decode three byte unicode characters
  467. $string = preg_replace(
  468. "/([\340-\357])([\200-\277])([\200-\277])/e",
  469. "'&#'.((ord('\\1')-224)*4096+(ord('\\2')-128)*64+(ord('\\3')-128)).';'",
  470. $string);
  471. // decode two byte unicode characters
  472. $string = preg_replace(
  473. "/([\300-\337])([\200-\277])/e",
  474. "'&#'.((ord('\\1')-192)*64+(ord('\\2')-128)).';'",
  475. $string);
  476. // remove broken unicode
  477. $string = preg_replace("/[\200-\237]|\240|[\241-\377]/", '?', $string);
  478. return $string;
  479. }
  480. }
  481. /**
  482. * Return UTF8 symbol when unicode character number is provided
  483. *
  484. * @param int $var Decimal unicode value
  485. * @return string UTF-8 character
  486. */
  487. function QuickBooks_Cast_unicodetoutf8($var)
  488. {
  489. if ($var < 128)
  490. {
  491. $ret = chr ($var);
  492. }
  493. else if ($var < 2048)
  494. {
  495. // Two byte utf-8
  496. $binVal = str_pad (decbin ($var), 11, '0', STR_PAD_LEFT);
  497. $binPart1 = substr ($binVal, 0, 5);
  498. $binPart2 = substr ($binVal, 5);
  499. $char1 = chr (192 + bindec ($binPart1));
  500. $char2 = chr (128 + bindec ($binPart2));
  501. $ret = $char1 . $char2;
  502. }
  503. else if ($var < 65536)
  504. {
  505. // Three byte utf-8
  506. $binVal = str_pad (decbin ($var), 16, '0', STR_PAD_LEFT);
  507. $binPart1 = substr ($binVal, 0, 4);
  508. $binPart2 = substr ($binVal, 4, 6);
  509. $binPart3 = substr ($binVal, 10);
  510. $char1 = chr (224 + bindec ($binPart1));
  511. $char2 = chr (128 + bindec ($binPart2));
  512. $char3 = chr (128 + bindec ($binPart3));
  513. $ret = $char1 . $char2 . $char3;
  514. }
  515. else if ($var < 2097152)
  516. {
  517. // Four byte utf-8
  518. $binVal = str_pad (decbin ($var), 21, '0', STR_PAD_LEFT);
  519. $binPart1 = substr ($binVal, 0, 3);
  520. $binPart2 = substr ($binVal, 3, 6);
  521. $binPart3 = substr ($binVal, 9, 6);
  522. $binPart4 = substr ($binVal, 15);
  523. $char1 = chr (240 + bindec ($binPart1));
  524. $char2 = chr (128 + bindec ($binPart2));
  525. $char3 = chr (128 + bindec ($binPart3));
  526. $char4 = chr (128 + bindec ($binPart4));
  527. $ret = $char1 . $char2 . $char3 . $char4;
  528. }
  529. else if ($var < 67108864)
  530. {
  531. // Five byte utf-8
  532. $binVal = str_pad (decbin ($var), 26, '0', STR_PAD_LEFT);
  533. $binPart1 = substr ($binVal, 0, 2);
  534. $binPart2 = substr ($binVal, 2, 6);
  535. $binPart3 = substr ($binVal, 8, 6);
  536. $binPart4 = substr ($binVal, 14,6);
  537. $binPart5 = substr ($binVal, 20);
  538. $char1 = chr (248 + bindec ($binPart1));
  539. $char2 = chr (128 + bindec ($binPart2));
  540. $char3 = chr (128 + bindec ($binPart3));
  541. $char4 = chr (128 + bindec ($binPart4));
  542. $char5 = chr (128 + bindec ($binPart5));
  543. $ret = $char1 . $char2 . $char3 . $char4 . $char5;
  544. }
  545. else if ($var < 2147483648)
  546. {
  547. // Six byte utf-8
  548. $binVal = str_pad(decbin($var), 31, '0', STR_PAD_LEFT);
  549. $binPart1 = substr($binVal, 0, 1);
  550. $binPart2 = substr($binVal, 1, 6);
  551. $binPart3 = substr($binVal, 7, 6);
  552. $binPart4 = substr($binVal, 13,6);
  553. $binPart5 = substr($binVal, 19,6);
  554. $binPart6 = substr($binVal, 25);
  555. $char1 = chr(252 + bindec($binPart1));
  556. $char2 = chr(128 + bindec($binPart2));
  557. $char3 = chr(128 + bindec($binPart3));
  558. $char4 = chr(128 + bindec($binPart4));
  559. $char5 = chr(128 + bindec($binPart5));
  560. $char6 = chr(128 + bindec($binPart6));
  561. $ret = $char1 . $char2 . $char3 . $char4 . $char5 . $char6;
  562. }
  563. else
  564. {
  565. // there is no such symbol in utf-8
  566. $ret = '?';
  567. }
  568. return $ret;
  569. }