Parse XML - Write to a database --- PLEASE HELP!

Discussion in 'PHP' started by maros174, Mar 4, 2010.

  1. #1
    This will kill me, it is taking my whole work day - each day, checking for errors so my visitors don't notice.
    I would be eternally grateful if anybody can help me in solving this...

    BACKGROUND:
    I have a financial website that display's Stock Market Data from my country's stock exchange.
    Stock Market Data is read from an XML file and written to a database.
    Data is refreshed every ten minutes (around 40 times per day), for 200+ stocks.

    PROBLEM:
    Most of the time everything works fine, but, several times each day, during a refresh, a data for a random stock, usually only one, is read only partialy.
    (for example. instead of HighPrice=320.99, to a database is written HighPrice=99, or, instead of DailyLow=4270.01, database contains DailyLow=70.01 )

    PROBLEM SCRIPTS:
    XML.class.php - parses XML data and places it in a structured PHP array (I think the error is somewhere here)
    ZSE.class.php - reads the data and writes it to a database.

    Potentially important facts:
    • XML is allways clean of errors.
    • Time of data collection(refresh) is not important, sometimes the error appears at 10:25, sometimes at 12:35, sometimes at 15:45, etc...
    • Usually the error is limitied to only one stock out of 200+, rarely two or three of them have errors.
    • Data is collected for several fields - but only one has an error: High Price, Low Price, Closing Price, Buy Price, Sell Price, Volume, Average Price. But every time the data is read partially it is limited to only one field. For example, sometimes the Closing price has an error (to a database is written 16 instead of 316), sometimes some other price like daily low price or average price.
    • Dellimiter is not an issue, the data is read partially independent of it. For e.g. sometimes instead of Average Price=24.49 to a database is written only the decimal part 49, sometimes instead of SellPrice=3420 database contains SellPrice=20.

    Example:
    [​IMG]


    XML.class.php
    <?php
        
        /**
        * This class parses XML data and places it in a structured PHP array. Array keys are equal to XML tags and each array element
        * can either be a scalar value or an array. In the former case, this is the data found inside the XML tags in question and in the
        * latter the array will have numeric keys, each of which as a value has an array that conforms to the same rules as the whole 
        * outer array. 
        * The typical piece of data can be retrieved as $resultingArray['level1node'][2]['level2node'][6]['level3node'][0]...
        */
        class XML {
            
            var $_fileName;
            var $_xmlpath = array();
            var $_values = array();
            var $_levelsNumbering = array();
            var $_currentDepth = 0;
            
            /**
            * Common PHP XML function for proccessing opening tags. It updates breadcrumbs arrays by adding new element to the existing node
            * list or initialising new node list if needed.
            */
            function startElement($parser, $name, $attributes){
                
                $this->_currentDepth++;
                $this->_xmlpath[$this->_currentDepth] = $name;
                
                if (isset($this->_levelsNumbering[$this->_currentDepth][$name])) {
                    $this->_levelsNumbering[$this->_currentDepth][$name]++;
                }
                else {
                    $this->_levelsNumbering[$this->_currentDepth][$name] = 0;
                }
                
            }
            
            /**
            * Common PHP XML function for proccessing closing tags. It updates breadcrumbs arrays by removing an element from the node
            * list and removes all finished branches of XML paths (once the node is closed, it's children list is finalized).
            */
            function endElement($parser, $name){
                
                $this->_currentDepth--;
                unset($this->_levelsNumbering[$this->_currentDepth + 2]);
                
                array_pop($this->_xmlpath);
            }
            
            /**
            * Common PHP XML function for proccessing intratag data. It uses breadcrumbs arrays to construct the resulting array's structure
            * for each piece of data found in the XML.
            * 
            */
            function characterData($parser, $data) {
                
                $data = trim($data);
                
                if ($data != '') {
                    
                    $reference = &$this->_values;
                    for ($i = 1; $i <= $this->_currentDepth; $i++) {
                        
                        $name = $this->_xmlpath[$i];
                        
                        if (!isset($reference[$name])) {
                            $reference[$name] = array();
                        }
                        
                        if ($i == $this->_currentDepth) {
                            $reference[$name][$this->_levelsNumbering[$i][$name]] = $data;
                        }
                        else {
                            if (!isset($reference[$name][$this->_levelsNumbering[$i][$name]])) {
                                $reference[$name][$this->_levelsNumbering[$i][$name]] = array();
                            }
                        }
                        
                        $reference = &$reference[$name][$this->_levelsNumbering[$i][$name]];
                    }
                }
            }
            
            function parse($_xmlLocation) {
                
                $this->_fileName = $_xmlLocation;
     
                $xml_parser = xml_parser_create();
                xml_set_object($xml_parser, $this);
                xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false);
                xml_set_element_handler($xml_parser, 'startElement', 'endElement');
                xml_set_character_data_handler($xml_parser, 'characterData');
                if (!($fp = fopen($this->_fileName, 'r'))) {
                    die('could not open XML input');
                }
                
                while ($file_content = fread($fp, 4096)) {
                    if (!xml_parse($xml_parser, $file_content, feof($fp))) {
                        die(sprintf("XML error: %s at line %d", xml_error_string(xml_get_error_code($xml_parser)), xml_get_current_line_number($xml_parser)));
                    }
                }
                xml_parser_free($xml_parser);
                
                return $this->_values;
            }
            
        }
        
        
    ?>
    PHP:

    ZSE.class.php - I'dont think that the error is here, it is probably in XML.class.php...
    I suspect that only a function getMarketTradingData could be an issue (on lines 167-209), everything above just writes to a database
    <?php
        include_once('Database.class.php');
        include_once('XML.class.php');
     
        
        /**
        * ZSE class is used to update several database tables: stockHistoricData and stockHistoricDataDaily. Table stock is filled or updated 
        * if the stock being proccessed is not already entered or it's data is not complete. Table stockHistoricData holds all historic data on all
        * stocks, and is updated from stockHistoricDataDaily table. Table stockHistoricDataDaily holds only today's most recent intraday data.
        * 
        */
        class ZSE extends Database {
            
            /**
            * Class constructor. NOTE: This is a PHP4 style constructor and should be renamed to PHP5 style __construct().
            * 
            */
            private $zsdp;
            private $opvl;
            
            function ZSE () {
                $this->Database();
     
            }
            
            /**
            * Central function of the class. This function collects market and stocks data and inserts it into stockHistoricData and 
            * stockHistoricDataDaily tables. Additionally, it cleans these tables up.
            * 
            */
            function proccessMarketTradingData () {
                
                $marketDailyData = $this->getMarketTradingData();
                
                if($marketDailyData === false || $marketDailyData['stockHistoricDailyData'] === false)return;
                $tickersToReport = array();
                $tradingDataSqls = array();
                
            foreach ($marketDailyData['stockHistoricDailyData'] as $stockISIN => $stockDailyData) {
            
                $stockGeneralDataDb = $this->getStockGeneralData($stockDailyData['ticker'], $stockDailyData, $tickersToReport);
                if(!isset($stockGeneralDataDb['stock_id']))continue;
     
                $exist = $this->get("SELECT stockHistoricDataDaily_id FROM stockHistoricDataDaily WHERE stock_id = {$stockGeneralDataDb['stock_id']}");
                $sql = "SELECT stockHistoricData_close FROM stockHistoricData WHERE stock_id = {$stockGeneralDataDb['stock_id']} 
                                                                              AND stockHistoricData_date < DATE(NOW()) 
                                                                              ORDER BY stockHistoricData_date DESC LIMIT 1;";
                
                $close = $this->get($sql);
                
                if($close[0]['stockHistoricData_close'] != 0 && $stockDailyData['close'] != 0){
                    $stockDailyData['realChange'] = round(((($stockDailyData['close']/$close[0]['stockHistoricData_close'])-1)* 100),5);
                }else $stockDailyData['realChange'] = 0;
                
                if($exist[0]['stockHistoricDataDaily_id']){
                    $sqlDaily = $stockDailyData['volume'] ? "UPDATE stockHistoricDataDaily SET  
                                                                                        stockHistoricDataDaily_lastChange = NOW(),
                                                                                        stockHistoricDataDaily_date = NOW(),  
                                                                                        stockHistoricDataDaily_high = '{$stockDailyData['high']}', 
                                                                                        stockHistoricDataDaily_low = '{$stockDailyData['low']}', 
                                                                                        stockHistoricDataDaily_close = '{$stockDailyData['close']}', 
                                                                                        stockHistoricDataDaily_buy = '{$stockDailyData['buy']}', 
                                                                                        stockHistoricDataDaily_sell = '{$stockDailyData['sell']}',                         
                                                                                        stockHistoricDataDaily_volume = '{$stockDailyData['volume']}', 
                                                                                        stockHistoricDataDaily_average = '{$stockDailyData['average']}',
                                                                                        stockHistoricDataDaily_change = '{$stockDailyData['realChange']}',  
                                                                                        stockHistoricDataDaily_zseChange = '{$stockDailyData['change']}', 
                                                                                        stockHistoricDataDaily_turnover = '{$stockDailyData['turnover']}'
                                                                                       WHERE stock_id = {$stockGeneralDataDb['stock_id']}"
                                                      :"UPDATE stockHistoricDataDaily SET  
                                                                                        stockHistoricDataDaily_lastChange = NOW(), 
                                                                                        stockHistoricDataDaily_high = 0, 
                                                                                        stockHistoricDataDaily_low = 0, 
                                                                                        stockHistoricDataDaily_buy = '{$stockDailyData['buy']}', 
                                                                                        stockHistoricDataDaily_sell = '{$stockDailyData['sell']}',                         
                                                                                        stockHistoricDataDaily_volume = '{$stockDailyData['volume']}', 
                                                                                        stockHistoricDataDaily_average = 0,
                                                                                        stockHistoricDataDaily_change = 0,  
                                                                                        stockHistoricDataDaily_zseChange = 0, 
                                                                                        stockHistoricDataDaily_turnover = 0
                                                                                       WHERE stock_id = {$stockGeneralDataDb['stock_id']}";     
                }
                else {
                      $sqlDaily = $stockDailyData['volume'] = "INSERT INTO stockHistoricDataDaily (
                                                                                                    stockHistoricDataDaily_id, 
                                                                                                    stockHistoricDataDaily_lastChange, 
                                                                                                    stock_id, 
                                                                                                    stockHistoricDataDaily_date, 
                                                                                                    stockHistoricDataDaily_high, 
                                                                                                    stockHistoricDataDaily_low, 
                                                                                                    stockHistoricDataDaily_close,
                                                                                                    stockHistoricDataDaily_buy,
                                                                                                    stockHistoricDataDaily_sell,
                                                                                                    stockHistoricDataDaily_volume, 
                                                                                                    stockHistoricDataDaily_average,
                                                                                                    stockHistoricDataDaily_change,  
                                                                                                    stockHistoricDataDaily_zseChange, 
                                                                                                    stockHistoricDataDaily_turnover
                                                                                                  ) VALUES (
                                                                                                    NULL, 
                                                                                                    NOW(), 
                                                                                                    '{$stockGeneralDataDb['stock_id']}', 
                                                                                                    NOW(), 
                                                                                                    '{$stockDailyData['high']}', 
                                                                                                    '{$stockDailyData['low']}', 
                                                                                                    '{$stockDailyData['close']}', 
                                                                                                    '{$stockDailyData['buy']}',
                                                                                                    '{$stockDailyData['sell']}',
                                                                                                    '{$stockDailyData['volume']}', 
                                                                                                    '{$stockDailyData['average']}',
                                                                                                    '{$stockDailyData['realChange']}', 
                                                                                                    '{$stockDailyData['change']}', 
                                                                                                    '{$stockDailyData['turnover']}');";
                }
     
                $tradingDataSqls[] = $sqlDaily;
                if($marketDailyData['stockHistoricData'] === false)continue;
                if(!in_array($stockGeneralDataDb['stock_ticker'],$marketDailyData['stockHistoricData']))continue;
     
                        $sqlHistoric = "INSERT INTO stockHistoricData (
                                stockHistoricData_id, 
                                stockHistoricData_lastChange, 
                                stock_id, 
                                stockHistoricData_date, 
                                stockHistoricData_high, 
                                stockHistoricData_low, 
                                stockHistoricData_close, 
                                stockHistoricData_volume, 
                                stockHistoricData_average, 
                                stockHistoricData_change, 
                                stockHistoricData_zseChange, 
                                stockHistoricData_turnover
                              ) VALUES (
                                NULL, 
                                NOW(), 
                                '{$stockGeneralDataDb['stock_id']}', 
                                NOW(), 
                                '{$stockDailyData['high']}', 
                                '{$stockDailyData['low']}', 
                                '{$stockDailyData['close']}', 
                                '{$stockDailyData['volume']}', 
                                '{$stockDailyData['average']}', 
                                '{$stockDailyData['realChange']}', 
                                '{$stockDailyData['change']}', 
                                '{$stockDailyData['turnover']}');";
                      $tradingDataSqls[] = $sqlHistoric;
            }
          
                $sqlCleanupHistoric = "DELETE FROM stockHistoricData WHERE stockHistoricData_date = DATE(NOW()) 
                                                                 AND stockHistoricData_lastChange < (NOW() - 10);";
                
                $tradingDataSqls[] = $sqlCleanupHistoric;
                
                            
                foreach ($tradingDataSqls as $sql) {
                    $this->execute($sql);
                    //echo $sql . '<br>';
                    echo mysql_error();
                    //echo '<hr>';
                    
                }
                
                exit;                   
            }
            
            /**
            * Reads the XML file from ZSE and formats the data for usage in other functions. The idea is that in case of any changes regarding 
            * the source of data all the needed reprogramming can be limited to this function.
            * 
            */
            function getMarketTradingData () {
                
                  $xmlParser = new XML();
                $fileLocation = 'http://webservice.zse.hr/delayed/Delayed15MarketData.xml';
                $xmlContents = $xmlParser->parse($fileLocation);
                
            $this->saveXML('MarketData', $fileLocation);
                if(!$this->saveMarketIndex($xmlContents['ZSETradingSystemData'][0]))return false;
      
                $tradingData = $xmlContents['ZSETradingSystemData'][0]['OrderBook'];
                $stockData = array();
                $stockHistoricData = array();
                $stockHistoricDailyData = array();
      
                foreach ($tradingData as $stockTradingData) {
                          if($stockTradingData['MarketName'][0] != 'EQTY')continue;
                          
                        $stockDailyData['ticker'] = $stockTradingData['Ticker'][0];
                        $stockDailyData['market'] = $stockTradingData['MarketName'][0];
                        $stockDailyData['isin'] = $stockTradingData['ISIN'][0];
                        $stockDailyData['volume'] = $stockTradingData['Volume'][0];
                        $stockDailyData['sell'] = $stockTradingData['BestBid'][0];
                        $stockDailyData['buy'] = $stockTradingData['BestAsk'][0];               
                        $stockDailyData['high'] = $stockTradingData['High'][0];
                        $stockDailyData['low'] = $stockTradingData['Low'][0];
                        $stockDailyData['close'] = $stockTradingData['Last'][0];
                        $stockDailyData['average'] = $stockTradingData['WeightedAveragePrice'][0];
                        $stockDailyData['change'] = $stockTradingData['PriceChange'][0];
                        $stockDailyData['turnover'] = $stockTradingData['Turnover'][0];
                        
                        $stockHistoricDailyData[$stockDailyData['isin']] = $stockDailyData;
                        if ($stockTradingData['Volume'][0] == '' || $stockTradingData['Volume'][0] == 0)continue;
                        
                  $stockHistoricData[] = $stockDailyData['ticker'];
                }
            
                $stockData['stockHistoricDailyData'] = (count($stockHistoricDailyData)> 0)?$stockHistoricDailyData:false;
                $stockData['stockHistoricData'] = (count($stockHistoricData)> 0)?$stockHistoricData:false;
                return $stockData;
            }
            
            
            
        }
    ?>
    PHP:
     
    maros174, Mar 4, 2010 IP