2010-03-17 19 views
1

MISE À JOUR:Salesforce/PHP - messages sortants (SOAP) - problème de limite de mémoire? DOMDocument :: loadXML() Fin prématurée des données dans un problème de balise?

OK j'ai tout compris, ressemble fread a une limitation de taille du fichier, a changé cela

file_get_contents('php://input')

, mais maintenant avoir donner une SF java.net.SocketTimeoutException: Lire expiré erreur et rien du côté PHP. J'ai également ajouté set_time_limit (0); au script PHP qui si je comprends bien exécuter le script aussi longtemps que cela prend. Des pensées?

BTW: Je peux traiter jusqu'à 25 (que je l'ai testé) mais pas 100


J'utilise Salesforce pour envoyer des messages sortants (via SOAP) vers un autre serveur. Le serveur peut traiter environ 8 messages à la fois, mais ne renverra pas le fichier ACK si la requête SOAP contient plus de 8 messages. SF peut envoyer jusqu'à 100 messages sortants dans une requête SOAP et je pense que cela provoque un problème de mémoire avec PHP. Si je traite les messages sortants 1 par 1, tout va bien, je peux même faire 8 à la fois sans problèmes. Mais les plus gros ensembles ne fonctionnent pas.

erreur dans SF:

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

Vous cherchez dans les journaux d'erreur HTTP, je vois que le message SOAP entrant semble être se couper de ce qui jette un avertissement PHP indiquant:

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

PHP Fatal erreur:

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

Cela me conduit à croire que PHP a un problème de mémoire et ne peut pas pa rse le message entrant en raison de sa taille.

Je pensais que je pouvais mettre:

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

Mais Serait-ce la bonne approche? Y at-il un moyen que je pourrais définir cela pour augmenter dynamiquement avec la demande SOAP entrante?

MISE À JOUR: Ajout d'un 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']; 


/** 
* 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); 
} 

Exemple SOAP Demande de Salesforce, il y aurait des noeuds de notification multiples ajoutés à une demande plus grande.

<?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

Je pense que vous obtiendriez une erreur fatale en cas de mémoire insuffisante si vous atteigniez la limite de mémoire. Je pensais que cela aurait pu être post_max_size mais je ne m'attendrais pas à ce que les données soient tronquées, cela ne devrait rien vous donner. –

+0

Veuillez voir la mise à jour –

+0

Sur quelle ligne se situe l'erreur PHP? –

Répondre

2

Un problème de mémoire PHP dire

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

Ceci est plus susceptible d'être mal pris fin ou des données tronquées provenant de la plate-forme Salesforce - essayez de débogage la première erreur de SF.

EDIT:

OK, il semble que vous saisir des données d'une manière archaïque. Essayez de remplacer fread() par stream_get_contents(), ainsi que par echo $content directement après l'avoir vérifié.

+0

S'il vous plaît voir la mise à jour –

+0

oui c'est ce que j'ai fait dans la mise à jour, ça fonctionne maintenant, mais ayant des problèmes avec Salesforce et accusant réception de la demande en bloc. J'ai vu certains dire que vous pourriez envoyer le fichier ACK pour l'ensemble du message SOAP mais je ne peux le faire fonctionner que si je le fais pour chaque message. Je sais que c'est une autre question mais des pensées? –