source: spip-zone/_plugins_/fulltext/lib/simplexlsx.class.php @ 52790

Last change on this file since 52790 was 52790, checked in by kent1@…, 8 years ago

if (!defined("_ECRIRE_INC_VERSION")) return; sur tout fichier PHP pour sécurité future principalement

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