Advertisement
kotvalera83

Excel_reader.php

Apr 15th, 2013
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 18.02 KB | None | 0 0
  1. // Load the spreadsheet reader library
  2. $this->load->library('excel_reader');
  3.  
  4. // Read the spreadsheet via a relative path to the document
  5. // for example $this->excel_reader->read('./uploads/file.xls');
  6. $this->excel_reader->read('path/to/file.xls');
  7.  
  8. // Get the contents of the first worksheet
  9. $worksheet = $this->excel_reader->worksheets[0];
  10.  
  11. ```
  12.  
  13. Excel_reader.php:
  14.  
  15.  
  16. <?php
  17. /**
  18.  * Excel reader class
  19.  *
  20.  * PHP class for reading an Excel spreadsheet document.
  21.  *
  22.  * @author James Gifford
  23.  * @copyright Copyright (c) 2006, James Gifford
  24.  * @link http://jamesgifford.com My Website
  25.  * @link sc.openoffice.org/excelfileformat.pdf Excel format documentation
  26.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  27.  * @category Spredsheets
  28.  * @version 0.1.2
  29.  * @filesource
  30.  * @todo Too many things to list at this time
  31.  */
  32. class Excel_reader
  33. {
  34.     /**
  35.      * The raw contents of the spredsheet document
  36.      *
  37.      * @access private
  38.      * @var string
  39.      */
  40.     var $_document;
  41.    
  42.     /**
  43.      * The size of standard sectors, in bytes
  44.      *
  45.      * @access public
  46.      * @var long
  47.      */
  48.     var $_sector_size;
  49.    
  50.     /**
  51.      * The size of short sectors, in bytes
  52.      *
  53.      * @access private
  54.      * @var long
  55.      */
  56.     var $_short_sector_size;
  57.    
  58.     /**
  59.      * The minimum size of a standard sector, in bytes
  60.      *
  61.      * @access private
  62.      * @var long
  63.      */
  64.     var $_standard_size;
  65.    
  66.     /**
  67.      * The sector allocation table
  68.      *
  69.      * @access private
  70.      * @var array
  71.      */
  72.     var $_sat;
  73.    
  74.     /**
  75.      * The short-sector allocation table
  76.      *
  77.      * @access private
  78.      * @var array
  79.      */
  80.     var $_ssat;
  81.    
  82.     /**
  83.      * The directory
  84.      *
  85.      * @access private
  86.      * @var array
  87.      */
  88.     var $_directory;
  89.    
  90.     /**
  91.      * The short-stream container stream
  92.      *
  93.      * @access private
  94.      * @var string
  95.      */
  96.     var $_sscs;
  97.    
  98.     /**
  99.      * The shared string table
  100.      *
  101.      * @access private
  102.      * @var array
  103.      */
  104.     var $_sst;
  105.    
  106.     /**
  107.      * The worksheets of the spreadsheet
  108.      *
  109.      * @access public
  110.      * @var array
  111.      */
  112.     var $worksheets;
  113.    
  114.     /**
  115.      * Constructor
  116.      */
  117.     function Excel_reader ($file = '')
  118.     {
  119.         $this->_init();
  120.        
  121.         if ($file !== '')
  122.         {
  123.             return $this->read($file);
  124.         }
  125.     }
  126.    
  127.     /**
  128.      * Initialize class variables
  129.      *
  130.      * @access private
  131.      * @return void
  132.      */
  133.     function _init ()
  134.     {
  135.         $this->_document = '';
  136.         $this->_sector_size = 0;
  137.         $this->_short_sector_size = 0;
  138.         $this->_standard_size = 0;
  139.         $this->_sat = array();
  140.         $this->_ssat = array();
  141.         $this->_directory = array();
  142.         $this->_sscs = '';
  143.     }
  144.    
  145.     /**
  146.      * Read a spredsheet document
  147.      *
  148.      * @access public
  149.      * @param string the path to the spreadsheet document
  150.      * @return void
  151.      */
  152.     function read ($file)
  153.     {
  154.         if (!(is_readable($file)))
  155.         {
  156.             return false;
  157.         }
  158.        
  159.         if (!($this->_document = file_get_contents($file)))
  160.         {
  161.             return false;
  162.         }
  163.        
  164.         $this->_parse_header();
  165.         $this->_parse_directory();
  166.     }
  167.    
  168.     /**
  169.      * Extract document values from the header sector and build data tables
  170.      *
  171.      * @access private
  172.      * @return void
  173.      */
  174.     function _parse_header ()
  175.     {
  176.         $header = substr($this->_document, 0, 512);
  177.        
  178.         if (substr($header, 0, 8) != pack('H*', 'd0cf11e0a1b11ae1'))
  179.         {
  180.             show_error('Invalid file format');
  181.         }
  182.        
  183.         $this->_sector_size = pow(2, $this->_get_value($header, 30, 2));
  184.         $this->_short_sector_size = pow(2, $this->_get_value($header, 32, 2));
  185.         $this->_standard_size = $this->_get_value($header, 56, 4);
  186.        
  187.         $this->_build_sat($header);
  188.         $this->_build_ssat($this->_get_value($header, 60, 4), $this->_get_value($header, 64, 4));
  189.         $this->_build_directory($this->_get_value($header, 48, 4));
  190.     }
  191.    
  192.     /**
  193.      * Build the sector allocation table
  194.      *
  195.      * @access private
  196.      * @param string the header stream
  197.      * @return void
  198.      */
  199.     function _build_sat ($header)
  200.     {
  201.         $msat = array();
  202.        
  203.         for ($i = 76; $i < $this->_sector_size && $this->_get_value($header, $i, 4) != 0xffffffff; $i += 4)
  204.         {
  205.             $msat[] = $this->_get_value($header, $i, 4);
  206.         }
  207.        
  208.         if (($msat_start = $this->_get_value($header, 68, 4)) != 0xfffffffe)
  209.         {
  210.             $msat_size = $this->_get_value($header, 72, 4);
  211.            
  212.             // TODO: complete the construction of the msat for larger documents
  213.         }
  214.        
  215.         foreach ($msat as $sat_sid)
  216.         {
  217.             $sector = substr($this->_document, ((1 + $sat_sid) * $this->_sector_size), $this->_sector_size);
  218.            
  219.             for ($i = 0; $i < $this->_sector_size; $i += 4)
  220.             {
  221.                 $this->_sat[] = $this->_get_value($sector, $i, 4);
  222.             }
  223.         }
  224.        
  225.         if ((count($this->_sat) / ($this->_sector_size / 4)) != $this->_get_value($header, 44, 4))
  226.         {
  227.             show_error('Currupted data found in file');
  228.         }
  229.     }
  230.    
  231.     /**
  232.      * Build the short-sector allocation table
  233.      *
  234.      * @access private
  235.      * @param long the startong sid
  236.      * @param long the size of the ssat
  237.      * @return void
  238.      */
  239.     function _build_ssat ($ssat_start, $ssat_size)
  240.     {
  241.         if ($ssat_start == 0xfffffffe)
  242.         {
  243.             return;
  244.         }
  245.        
  246.         $ssat_chain = array($ssat_start);
  247.         $ssat_sid = $ssat_start;
  248.        
  249.         while ($this->_sat[$ssat_sid] != 0xfffffffe)
  250.         {
  251.             $ssat_chain[] = $ssat_sid = $this->_sat[$ssat_sid];
  252.         }
  253.        
  254.         foreach ($ssat_chain as $ssat_sid)
  255.         {
  256.             $sector = substr($this->_document, ((1 + $ssat_sid) * $this->_sector_size), $this->_sector_size);
  257.            
  258.             for ($i = 0; $i < $this->_sector_size; $i += 4)
  259.             {
  260.                 $this->_ssat[] = $this->_get_value($sector, $i, 4);
  261.             }
  262.         }
  263.        
  264.         if ((count($this->_ssat) / ($this->_sector_size / 4)) != $ssat_size)
  265.         {
  266.             show_error('Corrupted data found in file');
  267.         }
  268.     }
  269.    
  270.     /**
  271.      * Build the directory
  272.      *
  273.      * @access private
  274.      * @param long the starting sid
  275.      * @return void
  276.      */
  277.     function _build_directory ($directory_start)
  278.     {
  279.         $directory_chain = array($directory_start);
  280.         $directory_sid = $directory_start;
  281.        
  282.         while ($this->_sat[$directory_sid] != 0xfffffffe)
  283.         {
  284.             $directory_chain[] = $directory_sid = $this->_sat[$directory_sid];
  285.         }
  286.        
  287.         foreach ($directory_chain as $directory_sid)
  288.         {
  289.             $sector = substr($this->_document, ((1 + $directory_sid) * $this->_sector_size), $this->_sector_size);
  290.            
  291.             for ($i = 0; $i < 4; $i++)
  292.             {
  293.                 $this->_directory[] = substr($sector, ($i * 128), 128);
  294.             }
  295.         }
  296.     }
  297.    
  298.     /**
  299.      * Extract values from directory entries and build data tables and streams
  300.      *
  301.      * @access private
  302.      * @return void
  303.      */
  304.     function _parse_directory ()
  305.     {
  306.         foreach ($this->_directory as $entry_did => $entry)
  307.         {
  308.             $entry_name = '';
  309.            
  310.             for ($i = 0; $i < (($this->_get_value($entry, 64, 2) - 2) / 2); $i++)
  311.             {
  312.                 $entry_name .= chr($this->_get_value(substr($entry, 0, 64), $i * 2, 2));
  313.             }
  314.            
  315.             if ($this->_get_value($entry, 66, 1) == 5)
  316.             {
  317.                 $this->_build_sscs($entry);
  318.             }
  319.             else if ($entry_name == 'Workbook' || $entry_name == 'Book')
  320.             {
  321.                 $this->_parse_workbook($entry);
  322.             }
  323.             else
  324.             {
  325.                 // TODO: Handle the other directory entries
  326.             }
  327.         }
  328.     }
  329.    
  330.     /**
  331.      * Build the short-stream container stream
  332.      *
  333.      * @access private
  334.      * @param string the root entry stream
  335.      * @return void
  336.      */
  337.     function _build_sscs ($root)
  338.     {    
  339.         $sscs_start = $this->_get_value($root, 116, 4);
  340.         $sscs_size = $this->_get_value($root, 120, 4);
  341.        
  342.         if ($sscs_start == 0xfffffffe)
  343.         {
  344.             return;
  345.         }
  346.        
  347.         $sscs_chain = array($sscs_start);
  348.         $sscs_sid = $sscs_start;
  349.        
  350.         while ($this->_sat[$sscs_sid] != 0xfffffffe)
  351.         {
  352.             $sscs_chain[] = $sscs_sid = $this->_sat[$sscs_sid];
  353.         }
  354.        
  355.         foreach ($sscs_chain as $sscs_sid)
  356.         {
  357.             $this->_sscs .= substr($this->_document, ((1 + $sscs_sid) * $this->_sector_size), $this->_sector_size);
  358.         }
  359.     }
  360.    
  361.     /**
  362.      * Parse the workbook directory entry
  363.      *
  364.      * @access private
  365.      * @param string the workbook stream
  366.      * @return void
  367.      */
  368.     function _parse_workbook ($entry)
  369.     {
  370.         $workbook_start = $this->_get_value($entry, 116, 4);
  371.         $workbook_size = $this->_get_value($entry, 120, 4);
  372.        
  373.         $workbook_chain = array($workbook_start);
  374.         $workbook_sid = $workbook_start;
  375.        
  376.         if ($workbook_size >= $this->_standard_size)
  377.         {
  378.             while ($this->_sat[$workbook_sid] != 0xfffffffe)
  379.             {
  380.                 $workbook_chain[] = $workbook_sid = $this->_sat[$workbook_sid];
  381.             }
  382.         }
  383.         else
  384.         {
  385.             while ($this->_ssat[$workbook_sid] != 0xfffffffe)
  386.             {
  387.                 $workbook_chain[] = $workbook_sid = $this->_ssat[$workbook_sid];
  388.             }
  389.         }
  390.        
  391.         foreach ($workbook_chain as $workbook_sid)
  392.         {
  393.             if ($workbook_size >= $this->_standard_size)
  394.             {
  395.                 $this->workbook .= substr($this->_document, ((1 + $workbook_sid) * $this->_sector_size), $this->_sector_size);
  396.             }
  397.             else
  398.             {
  399.                 $this->workbook .= substr($this->_sscs, ($workbook_sid * $this->_short_sector_size), $this->_short_sector_size);
  400.             }
  401.         }
  402.        
  403.         $this->workbook = substr($this->workbook, 0, $workbook_size);
  404.        
  405.         $this->biff_version = $this->_get_value($this->workbook, 1, 1);
  406.        
  407.         $i = 0;
  408.         $identifier = 0;
  409.         $record_identifier = $this->_get_value($this->workbook, $i, 2);
  410.        
  411.         while ($record_identifier != 0x000a)
  412.         {
  413.             $record_size = $this->_get_value($this->workbook, ($i + 2), 2);
  414.             $record_data = substr($this->workbook, ($i + 4), $record_size);
  415.            
  416.             if ($record_identifier != 0x003c)
  417.             {
  418.                 $identifier = $record_identifier;
  419.             }
  420.            
  421.             switch ($identifier)
  422.             {
  423.                 case 0x00fc:
  424.                     $total_strings = $this->_get_value($this->workbook, $i + 4, 4);
  425.                     $sst_strings = $this->_get_value($this->workbook, $i + 8, 4);
  426.                    
  427.                     $offset = 0;
  428.                     for ($k = 0; $k < $sst_strings; $k++)
  429.                     {
  430.                         $string_size = $this->_get_value($this->workbook, $i + 12 + $offset, 2);
  431.                        
  432.                         $this->_sst[] = substr($this->workbook, $i + 12 + $offset + 3, $string_size);
  433.                        
  434.                         $offset += $string_size + 3;
  435.                     }
  436.                     break;
  437.                
  438.                 case 0x0085:
  439.                     $worksheets[] = $record_data;
  440.                     break;
  441.                    
  442.                 default:
  443.                     // TODO: Handle other workbook records
  444.                     break;
  445.             }
  446.            
  447.             $i += ($record_size + 4);
  448.             $record_identifier = $this->_get_value($this->workbook, $i, 2);    
  449.         }
  450.        
  451.         foreach ($worksheets as $index => $worksheet)
  452.         {
  453.             $this->_parse_worksheet($worksheet, $index);
  454.         }
  455.     }
  456.    
  457.     /**
  458.      * Parse a worksheet
  459.      *
  460.      * @access private
  461.      * @param string portion of document stream containing the worksheet
  462.      * @param int the index of this worksheet
  463.      * @return void
  464.      */
  465.     function _parse_worksheet ($stream, $index)
  466.     {
  467.         $worksheet_start = $this->_get_value($stream, 0, 4);
  468.        
  469.         $length = $this->_get_value($stream, 6, 1);
  470.         $something = $this->_get_value($stream, 7, 1);
  471.         $sheet_name = substr($stream, 8, $length);
  472.        
  473.         $this->worksheet = substr($this->workbook, $worksheet_start);
  474.        
  475.         $j = 0;
  476.         $worksheet_identifier = 0;
  477.         $worksheet_record_identifier = $this->_get_value($this->worksheet, $j, 2);
  478.        
  479.         while ($worksheet_record_identifier != 0x000a)
  480.         {
  481.             $worksheet_record_size = $this->_get_value($this->worksheet, ($j + 2), 2);
  482.             $worksheet_record_data = substr($this->worksheet, ($j + 4), $worksheet_record_size);
  483.            
  484.             if ($worksheet_record_identifier != 0x003c)
  485.             {
  486.                 $worksheet_identifier = $worksheet_record_identifier;
  487.             }
  488.            
  489.             switch ($worksheet_identifier)
  490.             {
  491.                 case 0x00fd:
  492.                     $row = $this->_get_value($this->worksheet, $j + 4, 2);
  493.                     $col = $this->_get_value($this->worksheet, $j + 6, 2);
  494.                     $sst_index = $this->_get_value($this->worksheet, $j + 10, 4);
  495.                    
  496.                     $this->worksheets[$index][$row][$col] = $this->_sst[$sst_index];
  497.                     break;
  498.                
  499.                 case 0x0006:
  500.                 case 0x0203:
  501.                     $row = $this->_get_value($this->worksheet, $j + 4, 2);
  502.                     $col = $this->_get_value($this->worksheet, $j + 6, 2);
  503.                     $low_value = $this->_get_value($this->worksheet, $j + 10, 4);
  504.                     $high_value = $this->_get_value($this->worksheet, $j + 14, 4);
  505.                    
  506.                     $this->worksheets[$index][$row][$col] = $this->_get_number($low_value, $high_value);
  507.                     break;
  508.                
  509.                 case 0x027e:
  510.                     $row = $this->_get_value($this->worksheet, $j + 4, 2);
  511.                     $col = $this->_get_value($this->worksheet, $j + 6, 2);
  512.                     $rk_value = $this->_get_value($this->worksheet, $j + 10, 4);
  513.                    
  514.                     $this->worksheets[$index][$row][$col] = $this->_get_rk($rk_value);
  515.                     break;
  516.                
  517.                 case 0x00bd:
  518.                     $row = $this->_get_value($this->worksheet, $j + 4, 2);
  519.                     $first_col = $this->_get_value($this->worksheet, $j + 6, 2);
  520.                     $last_col = $this->_get_value($this->worksheet, $j + 2 + $worksheet_record_size, 2);
  521.                    
  522.                     for ($i = 0; $i <= $last_col - $first_col; $i++)
  523.                     {
  524.                         $rk_value = $this->_get_value($this->worksheet, $j + 10 + ($i * 6), 4);
  525.                        
  526.                         $this->worksheets[$index][$row][$first_col + $i] = $this->_get_rk($rk_value);
  527.                     }
  528.                     break;
  529.                
  530.                 default:
  531.                     // TODO: Handle other worksheet records
  532.                     break;
  533.             }
  534.            
  535.             $j += ($worksheet_record_size + 4);
  536.             $worksheet_record_identifier = $this->_get_value($this->worksheet, $j, 2);
  537.         }
  538.     }
  539.    
  540.     /**
  541.      * Convert hexidecimal values to decimal
  542.      *
  543.      * @access private
  544.      * @param string the stream from which to obtain the bytes
  545.      * @param int the offset within the stream to start reading
  546.      * @param int the number of bytes to read from the stream
  547.      * @return long
  548.      */
  549.     function _get_value ($stream, $offset = 0, $length = 1)
  550.     {
  551.         if (strlen($stream) < ($offset + $length) || $length < 1)
  552.         {
  553.             return false;
  554.         }
  555.        
  556.         $string = substr($stream, $offset, $length);
  557.         $value = 0;
  558.        
  559.         for ($i = 0; $i < strlen($string); $i++)
  560.         {
  561.             $value += (ord($string[$i]) * pow(256, $i));
  562.         }
  563.        
  564.         return $value;
  565.     }
  566.    
  567.     /**
  568.      * Convert an rk value to a decimal number
  569.      *
  570.      * @access private
  571.      * @param long the rk value
  572.      * @return mixed
  573.      */
  574.     function _get_rk ($rk)
  575.     {
  576.         if ($rk & 2)
  577.         {
  578.             $value = ($rk & 0xfffffffc) >> 2;
  579.         }
  580.         else
  581.         {
  582.             $exp = ((($rk & 0x7ff00000) >> 20) - 1023);
  583.             $value = (0x100000 | ($rk & 0x000ffffc)) / pow(2, (20 - $exp));
  584.            
  585.             if (($rk & 0x80000000) >> 31)
  586.             {
  587.                 $value = -$value;
  588.             }
  589.         }
  590.        
  591.         if ($rk & 1)
  592.         {
  593.             $value /= 100;
  594.         }
  595.        
  596.         return $value;
  597.     }
  598.    
  599.     /**
  600.      * Convert a IEEE 754 floating point number into a decimal number
  601.      *
  602.      * @access private
  603.      * @param long low value
  604.      * @param long high value
  605.      * @return mixed
  606.      */
  607.     function _get_number ($low, $high)
  608.     {
  609.         $exp = ((($high & 0x7ff00000) >> 20) - 1023);
  610.         $value = (0x100000 | ($high & 0x000fffff)) / pow(2, (20 - $exp));
  611.        
  612.         if (($low & 0x80000000) >> 31)
  613.         {
  614.             $value += (1 / pow(2, (21 - $exp)));
  615.         }
  616.        
  617.         $value += (($low & 0x7fffffff) / pow(2, (52 - $exp)));
  618.        
  619.         if (($high & 0x80000000) >> 31)
  620.         {
  621.             $value = -$value;
  622.         }
  623.        
  624.         return $value;
  625.     }
  626. }
  627.  
  628. ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement