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>