+ Reply to Thread
Results 1 to 2 of 2

Thread: DOMDocument::loadXML() ... Premature end of data in tag

  1. #1
    ppafford is offline Junior Member
    Join Date
    Sep 2009
    Posts
    21

    DOMDocument::loadXML() ... Premature end of data in tag

    I'm using Salesforce to send outbound messages (via SOAP) to another server. The server can process about 8 messages at a time, but will not send back the ACK file if the SOAP request contains more than 8 messages. SF can send up to 100 outbound messages in 1 SOAP request and I think this is causing a memory issue with PHP. If I process the outbound messages 1 by 1 they all go through fine, I can even do 8 at a time with no issues. But larger sets are not working.

    ERROR in SF:

    org.xml.sax.SAXParseException: Premature end of file

    Looking in the HTTP error logs I see that the incoming SOAP message looks to be getting cut of which throws a PHP warning stating:

    DOMDocument::loadXML() ... Premature end of data in tag ...

    PHP Fatal error:

    Call to a member function getAttribute() on a non-object

    My Code to parse incoming SOAP Requests
    PHP Code:
    /**
     * To parse out incoming SOAP requests and insert the values into a database table
     * 
     * {@link http://www.mikesimonds.com/salesforce-php-tutorials/95-using-salesforce-outbound-soap-messages-php-2.html}
     */

    // Might need more memory?
    ini_set('memory_limit', '64M'); // So far this does nothing to help the bulk requests

    /**
     * Set the document root path
     * @var $doc_root
     */
    $doc_root = $_SERVER['DOCUMENT_ROOT'];

    /**
     * The soap.ini.php file holds all the values for the Database connections
     * as well as some of the email alerts that you can test with
     */
    include_once($doc_root . '/sf/support.sf.ini.php');

    /**
     * This is needed for the $sObject object variable creation
     * found in phptoolkit-11_0 package available from SalesForce
     */
    require_once(DOC_ROOT . SALESFORCE_DIRECTORY . SALESFORCE_PHP_TOOLKIT .'/soapclient/SforcePartnerClient.php'); 

    /**
     * Reads SOAP incoming message from Salesforce/MAPS
     * @var incoming SOAP request
     */
    $data = fopen('php://input','rb');

    $headers = getallheaders();
    $content_length = $headers['Content-Length'];
    $buffer_length = 1000; // Do I need this buffer? 
    $fread_length = $content_length + $buffer_length;

    $content = fread($data,$fread_length);

    /**
     * Parse values from soap string into DOM XML
     */
    $dom = new DOMDocument();
    $dom->loadXML($content);
    $resultArray = parseNotification($dom);
    $sObject = $resultArray["sObject"];

    // Can remove this once I figure out the bug
    $testing = false;

    // Set $testing to true if you would like to see the incoming SOAP request from SF
    if($testing) {
        // Make it look nice
        $dom->formatOutput = true;
        
        // Write message and values to a file
        $fh = fopen(LOG_FILE_PATH.'/'.LOG_FILE_NAME,'a');
        fwrite($fh,$dom->saveXML());
        $ret_val = fclose($fh);
    }

    /**
     * Checks if the SOAP request was parsed out,
     * the $sObject->ACK is set to a string value of true in
     * the parseNotification()
     * @var $sObject->ACK
     */
    if($sObject->ACK == 'true') {
        respond('true');
    } else {
        // This means something might be wrong
        mail(BAD_ACK_TO_EMAIL,BAD_ACK_EMAIL_SUBJECT,$content,BAD_ACK_EMAIL_HEADER_WITH_CC);
        respond('false');
    }

    if(WRITE_OUTPUT_TO_LOG_FILE) {
        // Clear variable
        $fields_string = "";
        
        /**
         * List common values of the SOAP request
         * @var $sObject
         */
        $fields_string .= "Organization Id: " . $sObject->OrganizationId . "\n";
        $fields_string .= "Action Id: " . $sObject->ActionId . "\n";
        //$fields_string .= "Session Id: " . $sObject->SessionId . "\n"; // Session Id is not being passed right now, don't need it
        $fields_string .= "Enterprise URL: " . $sObject->EnterpriseUrl . "\n";
        $fields_string .= "Partner URL: " . $sObject->PartnerUrl . "\n"; 
        
        /**
         * @todo: Still need to add the notification Id to an array or some sort
         */
        //$fields_string .= "Notification Id: " . $sObject->NotificationId . "\n"; 
        //$fields_string .= '<pre>' . print_r($sObject->NotificationId,true) . '</pre>';
        
        /**
         * now you have an array as $record and you can use the
         * data as you need to for updates or calls back to salesforce
         * whatever you need to do is here
         * @var $resultArray['MapsRecords']
         */
        foreach ($resultArray['MapsRecords'] as $record) {
            // Just prints the fields in the array
            $fields_string .= '<pre>' . print_r($record,true) . '</pre>';  
        }
        
        // Flag used to send ACK response
        $fields_string .= "\nACK Flag: " . $sObject->ACK;
        
        // $content_length
        $fields_string .= "\nContent Length (Outbound Message Size): " . $content_length;
        
        // Close Border to separate each request
        $fields_string .= "\n /*********************************************/ \n";
        
        // Write message and values to a file
        $fh = fopen(LOG_FILE_PATH.'/'.LOG_FILE_NAME,'a');
        fwrite($fh,$fields_string);
        $ret_val = fclose($fh); 
    }

    /**
     * Parse a Salesforce.com Outbound Message notification SOAP packet
     * into an array of notification parms and an sObject. 
     * @param   XML [$domDoc] SOAP request as XML
     * @return  object/array[ $result] typecast XML to object of arrays
     **/
    function parseNotification($domDoc) {  
        // Parse Notification parameters into result array
        $result = array("OrganizationId" => "",
                        "ActionId" => "",
                        "SessionId" => "",
                        "EnterpriseUrl" => "",
                        "PartnerUrl" => "",
                        "sObject" => null,
                        "MapsRecords" => array());

        // Create sObject and fill fields provided in notification
        $sObjectNode = $domDoc->getElementsByTagName("sObject")->item(0);
        $sObjType = $sObjectNode->getAttribute("type");
        
        if(substr_count($sObjType,"sf:")) {
            $sObjType = substr($sObjType,3);
        }
        
        $result["sObject"] = new SObject($sObjType);
        $result["sObject"]->type = $sObjType;    
        $result["sObject"]->OrganizationId = $domDoc->getElementsByTagName("OrganizationId")->item(0)->textContent;
        $result["sObject"]->ActionId = $domDoc->getElementsByTagName("ActionId")->item(0)->textContent;
        $result["sObject"]->SessionId = $domDoc->getElementsByTagName("SessionId")->item(0)->textContent;
        $result["sObject"]->EnterpriseUrl = $domDoc->getElementsByTagName("EnterpriseUrl")->item(0)->textContent;
        $result["sObject"]->PartnerUrl = $domDoc->getElementsByTagName("PartnerUrl")->item(0)->textContent;
        
        /**
         * @todo: for multiple requests, need to add an array of Notification Id's
         *        might move this inside the loop or something
         *        might not need to do this as well
         */
        //$notificationId[] = $domDoc->getElementsByTagName("Id")->item(0)->textContent;
        //$result["sObject"]->NotificationId = $notificationId;

        $sObjectNodes = $domDoc->getElementsByTagNameNS('urn:sobject.enterprise.soap.sforce.com','*');
        $result["sObject"]->fieldnames = array();
        $count = 0;
        $tempMapRecord = array();
        
        // Loop through each notification sObject
        foreach ($sObjectNodes as $node) {
            if ($node->localName == "Id") {
                if ($count > 0) {
                    $result["MapsRecords"][] = $tempMapRecord;
                    $tempMapRecord = array();                          
                }
                // @note: added the strip_tags() to strip out all HTML tags
                $tempMapRecord[$node->localName] = strip_tags($node->textContent);
            } else {
                // @note: added the strip_tags() to strip out all HTML tags
                $tempMapRecord[$node->localName] = strip_tags($node->textContent);
            }        
            $count++;
            
            // set flag for ACK
            $result["sObject"]->ACK = 'true';
        }
        // Finish last item
        $result["MapsRecords"][] = $tempMapRecord;
        
        return $result;
    }

    /**
     * ACK to SalesForce, True/False (Prints header)
     * @param object $tf
     * @return $ACK
     */
    function respond($tf) {
        $ACK = <<<ACK
    <?xml version "1.0" encoding "utf-8"?>
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <soapenv:Body>
            <notifications xmlns="http://soap.sforce.com/2005/09/outbound">
                <Ack>$tf</Ack>
            </notifications>
        </soapenv:Body>
    </soapenv:Envelope>
    ACK;

        print trim($ACK); 
    }

  2. #2
    ppafford is offline Junior Member
    Join Date
    Sep 2009
    Posts
    21
    OK I figured it out, looks like fread has a filesize limitation, changed this to file_get_contents('php://input'), but now having SF give a java.net.SocketTimeoutException: Read timed out error and nothing on the PHP side. I have also added set_time_limit(0); to the PHP script which if I understand correctly execute the script for as long as it takes. Any thoughts?

    BTW: I can process up to 25 (that I've tested) but not 100

    Code:
    set_time_limit(0); // No time limit on execution
    $data = 'php://input';
    $content = file_get_contents($data);
    instead of
    Code:
    $data = fopen('php://input','rb');
    
    $headers = getallheaders();
    $content_length = $headers['Content-Length'];
    $buffer_length = 1000;
    $fread_length = $content_length + $buffer_length;
    
    $content = fread($data,$fread_length);

+ Reply to Thread

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts

SEO by vBSEO 3.5.1