*
*
*
*
*
*
*
*
*
* SOAP server in PHP:
*
*
* 1));
* $server = new SoapServer(null,array('uri' => $_SERVER['REQUEST_URI'])); // non-WDSL mode
* $server->addFunction('updateFile');
* if($mpI->isMultiPart()) {
* $server->handle(substr($HTTP_RAW_POST_DATA, $mpI->index[0]['begin'], $mpI->index[0]['length']));
* } else {
* $server->handle();
* }
*
* function updateFile($input) {
* global $mpI;
* global $HTTP_RAW_POST_DATA;
* if($mpI->isMultiPart()) {
* // retrieve first attachment and save it to the directory 'cache'
* $mpI->loadMultiPartIndex(1);
* file_put_contents('cache/PNGfileWhichWillBeUpdated.png',
* substr($HTTP_RAW_POST_DATA, $mpI->index[1]['begin'], $mpI->index[1]['length']));
* $mpI->fetchMultiPartHeaders(); // run it only if you need parts headers
* }
* error_log( var_export(array('inputData' => $input, 'HTTPheadersOf1stAttachment' => $mpI->index[1]['headers']), true) );
* return(array('return' => 'true'));
* }
*
* ?>
*
*
* @see http://php.net/manual/en/class.soapserver.php PHP's SoapServer
* @since 2012-08-24
* @version 2013-12-19
* @author Juks
*/
class MultipartRawPostHelper {
/**
* Parameters, which can be overwritten by constructor
*
* @see MultipartRawPostHelper::__construct()
* @var array
*/
public $mutables = array(
'loadNextParts' => 0,
'newLine' => "\r\n"
);
/**
* Index, describing position of different parts in multipart message, one row per part
*
* Keys of elements inside row
* * boundaryBegin : index of multipart boundary, preceding part's HTTP headers and body
* * begin : index of body start
* * end : index of body end
* * length : end - begin
* * headers : part's HTTP headers, available after running fetchMultiPartHeaders()
*
* @see MultipartRawPostHelper::__construct()
* @see MultipartRawPostHelper::fetchMultiPartHeaders()
* @var array
*/
public $index = array();
/**
* @var string
*/
public $multipartBoundary = '';
/**
* True, if multipart message is indexed, there's no more un-indexed parts
*
* @var boolean
*/
public $isIndexCompleted = false;
/**
* How many parts are already indexed?
*
* @var boolean
*/
public $indexLength = 0;
/**
* Constructor
*
* @param array To load first x part's indexes during init, use array('loadNextParts'=>x)
* @return self
* @see MultipartRawPostHelper::$mutable
*/
function __construct($init = array()) {
$this->mutables = array_merge($this->mutables,$init);
foreach(apache_request_headers() as $k=>$v) {
if(strtoupper($k) == 'CONTENT-TYPE') {
$matches = array();
if(preg_match('/^multipart\/related;.*boundary="?([^"]+)"?/', $v, $matches)) {
$this->multipartBoundary = '--'.$matches[1];
}
break;
}
}
$this->loadMultiPartIndex($this->mutables['loadNextParts']);
}
/**
* Is current message multipart message or not?
*
* @return boolean
*/
function isMultiPart() {
return $this->multipartBoundary != '';
}
/**
* Load next $loadNextParts part's indexes
*
* @param int How many next parts will be indexed
* @return void
*/
function loadMultiPartIndex($loadNextParts) {
if($this->isMultiPart()) {
for($i=0; $i<$loadNextParts; $i++) {
if(!$this->indexNextMultiPart()) { break; }
}
}
}
/**
* Calculate index for next part
*
* @see MultipartRawPostHelper::$index
* @return boolean True, when indexing succeeded
*/
function indexNextMultiPart() {
if($this->isIndexCompleted) { return false; }
// do not copy or extract any parts of $HTTP_RAW_POST_DATA due memory concerns
global $HTTP_RAW_POST_DATA;
$currentIndex = count($this->index);
$this->index[] = array();
// find first boundary's beginning by boundary string, because you cannot be sure how many whitespaces precede it
if($currentIndex == 0) {
$this->index[$currentIndex]['boundaryBegin'] = strpos($HTTP_RAW_POST_DATA, $this->multipartBoundary, 0);
if($this->index[$currentIndex]['boundaryBegin'] === false) {
$this->isIndexCompleted = true;
return false;
}
} else {
$this->index[$currentIndex]['boundaryBegin'] = $this->index[$currentIndex-1]['end'] + strlen($this->mutables['newLine']);
}
// part's beginning is after boundary + part's header(s) + double newline
$this->index[$currentIndex]['begin'] = strpos($HTTP_RAW_POST_DATA, $this->mutables['newLine'].$this->mutables['newLine'], $this->index[$currentIndex]['boundaryBegin'] + strlen($this->multipartBoundary));
if($this->index[$currentIndex]['begin'] === false) {
$this->isIndexCompleted = true;
return false;
}
$this->index[$currentIndex]['begin'] += 2*strlen($this->mutables['newLine']);
$this->index[$currentIndex]['end'] = strpos($HTTP_RAW_POST_DATA, $this->multipartBoundary, $this->index[$currentIndex]['begin']);
if($this->index[$currentIndex]['end'] === false) {
$this->isIndexCompleted = true;
return false;
}
$this->index[$currentIndex]['end'] -= strlen($this->mutables['newLine']);
$this->indexLength++;
$this->index[$currentIndex]['length'] = $this->index[$currentIndex]['end'] - $this->index[$currentIndex]['begin'];
return true;
}
/**
* Fetch part's headers
*
* Generates array of part's internal HTTP headers into index row with access key 'headers'
* NB! Part must be already indexed!
*
* @see MultipartRawPostHelper::$index
* @return void
*/
function fetchMultiPartHeaders() {
global $HTTP_RAW_POST_DATA;
$i = 0;
while(
$i < count($this->index)
&& !isset($this->index[$i]['headers'])
&& isset($this->index[$i]['boundaryBegin'])
&& isset($this->index[$i]['begin'])
) {
$this->index[$i]['headers'] = array();
$headers_start = $this->index[$i]['boundaryBegin'] + strlen($this->multipartBoundary);
foreach(explode($this->mutables['newLine'],substr($HTTP_RAW_POST_DATA, $headers_start, $this->index[$i]['begin'] -$headers_start)) as $header) {
$header = trim($header);
if($header != '') { $this->index[$i]['headers'][] = $header; }
}
$i++;
}
}
}