2010-03-17 31 views
1

UPDATE:Salesforce/PHP - 出站郵件(SOAP) - 內存限制問題? DOMDocument :: loadXML()標籤問題中的數據提前結束?

好,我想通了,看起來像FREAD有一個文件大小限制,改變了這

file_get_contents('php://input')

,但現在有SF給予java.net.SocketTimeoutException:讀超時錯誤和PHP方面沒有。我還添加了set_time_limit(0);到PHP腳本,如果我理解正確執行腳本,只要它需要。有什麼想法嗎?

BTW:我可以處理多達25個(即我測試過),但不是100


我使用Salesforce發送出站消息(通過SOAP)到另一臺服務器。服務器一次可以處理大約8條消息,但如果SOAP請求包含超過8條消息,則不會發回ACK文件。 SF可以在1個SOAP請求中發送多達100個出站消息,我認爲這導致了PHP的內存問題。如果我一個一個處理出站消息,它們都會很好,我甚至可以一次完成8個,沒有問題。但更大的集合不起作用。

錯誤的SF:

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

尋找在HTTP錯誤日誌我看到傳入的SOAP消息看起來越來越切斷其拋出一個PHP警告,指出:

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

PHP致命錯誤:

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

這使我相信PHP有內存問題,不能pa由於它的大小,rse傳入的消息。

我想我可以只設置:

ini_set('memory_limit', '64M'); // This has done nothing to fix the problem 

但這樣做是正確的做法?有沒有一種方法可以將其設置爲動態增加傳入的SOAP請求?

UPDATE:添加一些代碼從Salesforce

/** 
* 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']; 


/** 
* 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.BLAH.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://BLAH.com/outbound"> 
      <Ack>$tf</Ack> 
     </notifications> 
    </soapenv:Body> 
</soapenv:Envelope> 
ACK; 

    print trim($ACK); 
} 

例SOAP請求,將有加入到更大的請求多個通知節點。

<?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://BLAH.com/outbound"> 
    <OrganizationId>BLAH</OrganizationId> 
    <ActionId>BLAH</ActionId> 
    <SessionId xsi:nil="true"/> 
    <EnterpriseUrl>https://BLAH.com/</EnterpriseUrl> 
    <PartnerUrl>https://BLAH.com/</PartnerUrl> 
    <Notification> 
    <Id>BLAH</Id> 
    <sObject xmlns:sf="urn:sobject.BLAH.com" xsi:type="sf:Case"> 
    <sf:Id>BLAH</sf:Id> 
    <sf:CaseNumber>BLAH</sf:CaseNumber> 
    <sf:Case_Owner_ID_hidden__c>BLAH</sf:Case_Owner_ID_hidden__c> 
    <sf:CreatedDate>2010-03-17T12:11:33.000Z</sf:CreatedDate> 
    <sf:LastModifiedDate>2010-03-17T15:21:29.000Z</sf:LastModifiedDate> 
    <sf:OwnerId>BLAH</sf:OwnerId> 
    <sf:Status>BLAH</sf:Status> 
    </sObject> 
    </Notification> 
</notifications> 
</soapenv:Body> 
</soapenv:Envelope> 
+0

我認爲如果您達到內存限制,您會遇到內存不足的致命錯誤。我以爲它可能是post_max_size,但我不希望截斷數據,它應該只是沒有給你任何東西 – 2010-03-17 14:16:51

+0

請參閱更新 – 2010-03-17 16:58:35

+0

PHP錯誤發生在哪一行? – 2010-03-17 16:59:54

回答

2

一個PHP內存問題會說

PHP Fatal error: Out of memory (allocated 250871808)... 

這更可能是不正確的終止或從Salesforce平臺發起截斷的數據 - 試着從SF調試的第一個錯誤。

編輯:

OK,它看起來就像你在一個陳舊的方式抓取數據。嘗試更換fread()stream_get_contents(),還有echo $content後,你得到它來檢查輸出。

+0

請看更新 – 2010-03-17 16:59:31

+0

這就是我在更新中做的,它現在正在工作,但與Salesforce有問題並確認批量請求。我見過一些人說你可以發送整個SOAP消息的ACK文件,但是如果我爲每條消息做,我只能讓它工作。我知道這是另一個問題,但任何想法? – 2010-03-17 18:51:15