PageRenderTime 32ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/system/library/extra/simplexlsx.php

https://gitlab.com/miechuliv/livesell
PHP | 397 lines | 295 code | 50 blank | 52 comment | 61 complexity | a324f15c2f06bc39f1774784618f0922 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1, BSD-3-Clause
  1. <?php
  2. // SimpleXLSX php class v0.4
  3. // MS Excel 2007 workbooks reader
  4. // Example:
  5. // $xlsx = new SimpleXLSX('book.xlsx');
  6. // print_r( $xlsx->rows() );
  7. // Example 2:
  8. // $xlsx = new SimpleXLSX('book.xlsx');
  9. // print_r( $xlsx->rowsEx() );
  10. // Example 3:
  11. // $xlsx = new SimpleXLSX('book.xlsx');
  12. // print_r( $xlsx->rows(2) ); // second worksheet
  13. //
  14. // 0.4 sheets(), sheetsCount(), unixstamp( $excelDateTime )
  15. // 0.3 - fixed empty cells (Gonzo patch)
  16. class SimpleXLSX {
  17. // Don't remove this string! Created by Sergey Schuchkin from http://www.sibvison.ru - professional php developers team 2010-2011
  18. private $sheets;
  19. private $hyperlinks;
  20. private $package;
  21. private $sharedstrings;
  22. // scheme
  23. const SCHEMA_OFFICEDOCUMENT = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument';
  24. const SCHEMA_RELATIONSHIP = 'http://schemas.openxmlformats.org/package/2006/relationships';
  25. const SCHEMA_SHAREDSTRINGS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings';
  26. const SCHEMA_WORKSHEETRELATION = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet';
  27. function __construct( $filename ) {
  28. $this->_unzip( $filename );
  29. $this->_parse();
  30. }
  31. function sheets() {
  32. return $this->sheets;
  33. }
  34. function sheetsCount() {
  35. return count($this->sheets);
  36. }
  37. function worksheet( $worksheet_id ) {
  38. if ( isset( $this->sheets[ $worksheet_id ] ) ) {
  39. $ws = $this->sheets[ $worksheet_id ];
  40. if (isset($ws->hyperlinks)) {
  41. $this->hyperlinks = array();
  42. foreach( $ws->hyperlinks->hyperlink as $hyperlink ) {
  43. $this->hyperlinks[ (string) $hyperlink['ref'] ] = (string) $hyperlink['display'];
  44. }
  45. }
  46. return $ws;
  47. } else
  48. throw new Exception('Worksheet '.$worksheet_id.' not found.');
  49. }
  50. function dimension( $worksheet_id = 1 ) {
  51. $ws = $this->worksheet($worksheet_id);
  52. $ref = (string) $ws->dimension['ref'];
  53. $d = explode(':', $ref);
  54. $index = $this->_columnIndex( $d[1] );
  55. return array( $index[0]+1, $index[1]+1);
  56. }
  57. // sheets numeration: 1,2,3....
  58. function rows( $worksheet_id = 1 ) {
  59. $ws = $this->worksheet( $worksheet_id);
  60. $rows = array();
  61. $curR = 0;
  62. foreach ($ws->sheetData->row as $row) {
  63. foreach ($row->c as $c) {
  64. list($curC,) = $this->_columnIndex((string) $c['r']);
  65. $rows[ $curR ][ $curC ] = $this->value($c);
  66. }
  67. $curR++;
  68. }
  69. return $rows;
  70. }
  71. function rowsEx( $worksheet_id = 1 ) {
  72. $rows = array();
  73. $curR = 0;
  74. if (($ws = $this->worksheet( $worksheet_id)) === false)
  75. return false;
  76. foreach ($ws->sheetData->row as $row) {
  77. foreach ($row->c as $c) {
  78. list($curC,) = $this->_columnIndex((string) $c['r']);
  79. $rows[ $curR ][ $curC ] = array(
  80. 'name' => (string) $c['r'],
  81. 'value' => $this->value($c),
  82. 'href' => $this->href( $c ),
  83. );
  84. }
  85. $curR++;
  86. }
  87. return $rows;
  88. }
  89. // thx Gonzo
  90. function _columnIndex( $cell = 'A1' ) {
  91. if (preg_match("/([A-Z]+)(\d+)/", $cell, $matches)) {
  92. $col = $matches[1];
  93. $row = $matches[2];
  94. $colLen = strlen($col);
  95. $index = 0;
  96. for ($i = $colLen-1; $i >= 0; $i--)
  97. $index += (ord($col{$i}) - 64) * pow(26, $colLen-$i-1);
  98. return array($index-1, $row-1);
  99. } else
  100. throw new Exception("Invalid cell index.");
  101. }
  102. function value( $cell ) {
  103. // Determine data type
  104. $dataType = (string)$cell["t"];
  105. switch ($dataType) {
  106. case "s":
  107. // Value is a shared string
  108. if ((string)$cell->v != '') {
  109. $value = $this->sharedstrings[intval($cell->v)];
  110. } else {
  111. $value = '';
  112. }
  113. break;
  114. case "b":
  115. // Value is boolean
  116. $value = (string)$cell->v;
  117. if ($value == '0') {
  118. $value = false;
  119. } else if ($value == '1') {
  120. $value = true;
  121. } else {
  122. $value = (bool)$cell->v;
  123. }
  124. break;
  125. case "inlineStr":
  126. // Value is rich text inline
  127. $value = $this->_parseRichText($cell->is);
  128. break;
  129. case "e":
  130. // Value is an error message
  131. if ((string)$cell->v != '') {
  132. $value = (string)$cell->v;
  133. } else {
  134. $value = '';
  135. }
  136. break;
  137. default:
  138. // Value is a string
  139. $value = (string)$cell->v;
  140. // Check for numeric values
  141. if (is_numeric($value) && $dataType != 's') {
  142. if ($value == (int)$value) $value = (int)$value;
  143. elseif ($value == (float)$value) $value = (float)$value;
  144. elseif ($value == (double)$value) $value = (double)$value;
  145. }
  146. }
  147. return $value;
  148. }
  149. function href( $cell ) {
  150. return isset( $this->hyperlinks[ (string) $cell['r'] ] ) ? $this->hyperlinks[ (string) $cell['r'] ] : '';
  151. }
  152. function _unzip( $filename ) {
  153. // Clear current file
  154. $this->datasec = array();
  155. // Package information
  156. $this->package = array(
  157. 'filename' => $filename,
  158. 'mtime' => filemtime( $filename ),
  159. 'size' => filesize( $filename ),
  160. 'comment' => '',
  161. 'entries' => array()
  162. );
  163. // Read file
  164. $oF = fopen($filename, 'rb');
  165. $vZ = fread($oF, $this->package['size']);
  166. fclose($oF);
  167. // Cut end of central directory
  168. $aE = explode("\x50\x4b\x05\x06", $vZ);
  169. // Normal way
  170. $aP = unpack('x16/v1CL', $aE[1]);
  171. $this->package['comment'] = substr($aE[1], 18, $aP['CL']);
  172. // Translates end of line from other operating systems
  173. $this->package['comment'] = strtr($this->package['comment'], array("\r\n" => "\n", "\r" => "\n"));
  174. // Cut the entries from the central directory
  175. $aE = explode("\x50\x4b\x01\x02", $vZ);
  176. // Explode to each part
  177. $aE = explode("\x50\x4b\x03\x04", $aE[0]);
  178. // Shift out spanning signature or empty entry
  179. array_shift($aE);
  180. // Loop through the entries
  181. foreach ($aE as $vZ) {
  182. $aI = array();
  183. $aI['E'] = 0;
  184. $aI['EM'] = '';
  185. // Retrieving local file header information
  186. // $aP = unpack('v1VN/v1GPF/v1CM/v1FT/v1FD/V1CRC/V1CS/V1UCS/v1FNL', $vZ);
  187. $aP = unpack('v1VN/v1GPF/v1CM/v1FT/v1FD/V1CRC/V1CS/V1UCS/v1FNL/v1EFL', $vZ);
  188. // Check if data is encrypted
  189. // $bE = ($aP['GPF'] && 0x0001) ? TRUE : FALSE;
  190. $bE = false;
  191. $nF = $aP['FNL'];
  192. $mF = $aP['EFL'];
  193. // Special case : value block after the compressed data
  194. if ($aP['GPF'] & 0x0008) {
  195. $aP1 = unpack('V1CRC/V1CS/V1UCS', substr($vZ, -12));
  196. $aP['CRC'] = $aP1['CRC'];
  197. $aP['CS'] = $aP1['CS'];
  198. $aP['UCS'] = $aP1['UCS'];
  199. $vZ = substr($vZ, 0, -12);
  200. }
  201. // Getting stored filename
  202. $aI['N'] = substr($vZ, 26, $nF);
  203. if (substr($aI['N'], -1) == '/') {
  204. // is a directory entry - will be skipped
  205. continue;
  206. }
  207. // Truncate full filename in path and filename
  208. $aI['P'] = dirname($aI['N']);
  209. $aI['P'] = $aI['P'] == '.' ? '' : $aI['P'];
  210. $aI['N'] = basename($aI['N']);
  211. $vZ = substr($vZ, 26 + $nF + $mF);
  212. if (strlen($vZ) != $aP['CS']) {
  213. $aI['E'] = 1;
  214. $aI['EM'] = 'Compressed size is not equal with the value in header information.';
  215. } else {
  216. if ($bE) {
  217. $aI['E'] = 5;
  218. $aI['EM'] = 'File is encrypted, which is not supported from this class.';
  219. } else {
  220. switch($aP['CM']) {
  221. case 0: // Stored
  222. // Here is nothing to do, the file ist flat.
  223. break;
  224. case 8: // Deflated
  225. $vZ = gzinflate($vZ);
  226. break;
  227. case 12: // BZIP2
  228. if (! extension_loaded('bz2')) {
  229. if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
  230. @dl('php_bz2.dll');
  231. } else {
  232. @dl('bz2.so');
  233. }
  234. }
  235. if (extension_loaded('bz2')) {
  236. $vZ = bzdecompress($vZ);
  237. } else {
  238. $aI['E'] = 7;
  239. $aI['EM'] = "PHP BZIP2 extension not available.";
  240. }
  241. break;
  242. default:
  243. $aI['E'] = 6;
  244. $aI['EM'] = "De-/Compression method {$aP['CM']} is not supported.";
  245. }
  246. if (! $aI['E']) {
  247. if ($vZ === FALSE) {
  248. $aI['E'] = 2;
  249. $aI['EM'] = 'Decompression of data failed.';
  250. } else {
  251. if (strlen($vZ) != $aP['UCS']) {
  252. $aI['E'] = 3;
  253. $aI['EM'] = 'Uncompressed size is not equal with the value in header information.';
  254. } else {
  255. if (crc32($vZ) != $aP['CRC']) {
  256. $aI['E'] = 4;
  257. $aI['EM'] = 'CRC32 checksum is not equal with the value in header information.';
  258. }
  259. }
  260. }
  261. }
  262. }
  263. }
  264. $aI['D'] = $vZ;
  265. // DOS to UNIX timestamp
  266. $aI['T'] = mktime(($aP['FT'] & 0xf800) >> 11,
  267. ($aP['FT'] & 0x07e0) >> 5,
  268. ($aP['FT'] & 0x001f) << 1,
  269. ($aP['FD'] & 0x01e0) >> 5,
  270. ($aP['FD'] & 0x001f),
  271. (($aP['FD'] & 0xfe00) >> 9) + 1980);
  272. //$this->Entries[] = &new SimpleUnzipEntry($aI);
  273. $this->package['entries'][] = array(
  274. 'data' => $aI['D'],
  275. 'error' => $aI['E'],
  276. 'error_msg' => $aI['EM'],
  277. 'name' => $aI['N'],
  278. 'path' => $aI['P'],
  279. 'time' => $aI['T']
  280. );
  281. } // end for each entries
  282. }
  283. function getPackage() {
  284. return $this->package;
  285. }
  286. function getEntryData( $name ) {
  287. $dir = dirname( $name );
  288. $name = basename( $name );
  289. foreach( $this->package['entries'] as $entry)
  290. if ( $entry['path'] == $dir && $entry['name'] == $name)
  291. return $entry['data'];
  292. }
  293. function unixstamp( $excelDateTime ) {
  294. $d = floor( $excelDateTime ); // seconds since 1900
  295. $t = $excelDateTime - $d;
  296. return ($d > 0) ? ( $d - 25569 ) * 86400 + $t * 86400 : $t * 86400;
  297. }
  298. function _parse() {
  299. // Document data holders
  300. $this->sharedstrings = array();
  301. $this->sheets = array();
  302. // Read relations and search for officeDocument
  303. $relations = simplexml_load_string( $this->getEntryData("_rels/.rels") );
  304. foreach ($relations->Relationship as $rel) {
  305. if ($rel["Type"] == SimpleXLSX::SCHEMA_OFFICEDOCUMENT) {
  306. // Found office document! Read relations for workbook...
  307. $workbookRelations = simplexml_load_string($this->getEntryData( dirname($rel["Target"]) . "/_rels/" . basename($rel["Target"]) . ".rels") );
  308. $workbookRelations->registerXPathNamespace("rel", SimpleXLSX::SCHEMA_RELATIONSHIP);
  309. // Read shared strings
  310. $sharedStringsPath = $workbookRelations->xpath("rel:Relationship[@Type='" . SimpleXLSX::SCHEMA_SHAREDSTRINGS . "']");
  311. $sharedStringsPath = (string)$sharedStringsPath[0]['Target'];
  312. $xmlStrings = simplexml_load_string($this->getEntryData( dirname($rel["Target"]) . "/" . $sharedStringsPath) );
  313. if (isset($xmlStrings) && isset($xmlStrings->si)) {
  314. foreach ($xmlStrings->si as $val) {
  315. if (isset($val->t)) {
  316. $this->sharedstrings[] = (string)$val->t;
  317. } elseif (isset($val->r)) {
  318. $this->sharedstrings[] = $this->_parseRichText($val);
  319. }
  320. }
  321. }
  322. // Loop relations for workbook and extract sheets...
  323. foreach ($workbookRelations->Relationship as $workbookRelation) {
  324. if ($workbookRelation["Type"] == SimpleXLSX::SCHEMA_WORKSHEETRELATION) {
  325. $this->sheets[ str_replace( 'rId', '', (string) $workbookRelation["Id"]) ] =
  326. simplexml_load_string( $this->getEntryData( dirname($rel["Target"]) . "/" . dirname($workbookRelation["Target"]) . "/" . basename($workbookRelation["Target"])) );
  327. }
  328. }
  329. break;
  330. }
  331. }
  332. // Sort sheets
  333. ksort($this->sheets);
  334. }
  335. private function _parseRichText($is = null) {
  336. $value = array();
  337. if (isset($is->t)) {
  338. $value[] = (string)$is->t;
  339. } else {
  340. foreach ($is->r as $run) {
  341. $value[] = (string)$run->t;
  342. }
  343. }
  344. return implode(' ', $value);
  345. }
  346. }
  347. ?>