Salesforce / PHP - исходящие сообщения (SOAP) - проблема с лимитом памяти? DOMDocument :: loadXML () Преждевременное завершение данных в теге проблема? - PullRequest
1 голос
/ 17 марта 2010

UPDATE:

ОК, я понял, похоже, что у fread есть ограничение на размер файла, изменил это на

file_get_contents('php://input')

, но теперь наличие SF дает исключение java.net.SocketTimeoutException: ошибка тайм-аута чтения и ничего на стороне PHP. Я также добавил set_time_limit (0); к сценарию PHP, который, если я правильно понимаю, выполнить сценарий столько, сколько потребуется. Есть мысли?

Кстати: я могу обработать до 25 (что я тестировал), но не 100


Я использую Salesforce для отправки исходящих сообщений (через SOAP) на другой сервер. Сервер может обрабатывать около 8 сообщений одновременно, но не будет отправлять обратно файл ACK, если запрос SOAP содержит более 8 сообщений. SF может отправлять до 100 исходящих сообщений в одном запросе SOAP, и я думаю, что это вызывает проблемы с памятью в PHP. Если я обработаю исходящие сообщения 1 на 1, они все пройдут нормально, я могу даже сделать 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 имеет проблему с памятью и не может проанализировать входящее сообщение из-за его размера.

Я думал, что могу просто установить:

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

Но будет ли это правильным подходом? Есть ли способ, которым я мог бы установить это увеличение с входящим запросом SOAP динамически?

ОБНОВЛЕНИЕ: добавление кода

<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) . '
'; / ** * теперь у вас есть массив в виде $ record, и вы можете использовать * данные, которые вам нужны для обновлений или звонков в отдел продаж * все, что вам нужно сделать, здесь * @var $ resultArray ['MapsRecords'] * / foreach ($ resultArray ['MapsRecords'] как $ record) { // Просто печатает поля в массиве $ fields_string. = '
' . print_r($record,true) . '
'; } // Флаг, используемый для отправки ответа ACK $ fields_string. = "\ nACK Flag:". $ SObject-> ACK; // $ content_length $ fields_string. = "\ nContent Length (Размер исходящего сообщения):". $ Content_length; // Закрыть границу, чтобы отделить каждый запрос $ fields_string. = "\ n / ****************************************** *** / \ n "; // Записать сообщение и значения в файл $ fh = fopen (LOG_FILE_PATH. '/'. LOG_FILE_NAME, 'a'); FWRITE ($ ФХ, $ fields_string); $ ret_val = fclose ($ fh); } / ** * Анализировать SOAP-пакет исходящих сообщений Salesforce.com * в массив уведомлений и sObject. * @param XML [$ domDoc] SOAP-запрос в формате XML * @return объект / массив [$ result] приведёт XML к объекту массивов ** / function parseNotification ($ domDoc) { // Разобрать параметры уведомления в массив результатов $ result = array ("OrganizationId" => "", "ActionId" => "", "SessionId" => "", "EnterpriseUrl" => "", "PartnerUrl" => "", "sObject" => ноль, "MapsRecords" => array ()); // Создать sObject и заполнить поля, указанные в уведомлении $ sObjectNode = $ domDoc-> getElementsByTagName ("sObject") -> item (0); $ sObjType = $ sObjectNode-> getAttribute ("type"); if (substr_count ($ sObjType, "sf:")) { $ sObjType = substr ($ sObjType, 3); } $ result ["sObject"] = новый 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: для нескольких запросов необходимо добавить массив идентификаторов уведомлений * может переместить это внутри цикла или что-то * может и не нужно это делать * / // $tificationId [] = $ domDoc-> getElementsByTagName ("Id") -> item (0) -> textContent; // $ result ["sObject"] -> NotificationId = $tificationId; $ sObjectNodes = $ domDoc-> getElementsByTagNameNS ('urn: sobject.BLAH.com', '*'); $ result ["sObject"] -> fieldnames = array (); $ count = 0; $ tempMapRecord = array (); // Перебираем каждое уведомление sObject foreach ($ sObjectNodes как $ узел) { if ($ node-> localName == "Id") { if ($ count> 0) { $ result ["MapsRecords"] [] = $ tempMapRecord; $ tempMapRecord = array (); } // @note: добавлен strip_tags () для удаления всех тегов HTML $ tempMapRecord [$ node-> localName] = strip_tags ($ node-> textContent); } еще { // @note: добавлен strip_tags () для удаления всех тегов HTML $ tempMapRecord [$ node-> localName] = strip_tags ($ node-> textContent); } $ Подсчитывать ++; // установить флаг для ACK $ result ["sObject"] -> ACK = 'true'; } // Завершить последний элемент $ result ["MapsRecords"] [] = $ tempMapRecord; вернуть $ результат; } / ** * ACK для SalesForce, True / False (печатает заголовок) * @param object $ tf * @return $ ACK * / function response ($ tf) { $ ACK = <<< ACK <? xml version = "1.0" encoding = "utf-8"?> $ * 1039 тс * </ Soapenv: Body> </ Soapenv: Envelope> ACK; обрезка печати ($ ACK); }

Пример запроса SOAP от Salesforce, к большему запросу будет добавлено несколько узлов уведомлений.

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

1 Ответ

2 голосов
/ 17 марта 2010

Проблема с памятью PHP скажет

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

Скорее всего, это некорректно прерванные или усеченные данные, происходящие из платформы Salesforce - попробуйте отладить первую ошибку из SF.

EDIT:

ОК, похоже, вы пытаетесь получить данные устаревшим способом. Попробуйте заменить fread() на stream_get_contents(), а также echo $content сразу после получения, чтобы проверить вывод.

...