Added report parser and LLSD classes
This commit is contained in:
50
htdocs/lib/ReportParser.php
Normal file
50
htdocs/lib/ReportParser.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
/* ReportParser.php
|
||||||
|
* Parses incoming raw crash reports
|
||||||
|
*
|
||||||
|
* Description
|
||||||
|
* @package Singularity Crash Processor
|
||||||
|
* @author Latif Khalifa <latifer@streamgrid.net>
|
||||||
|
* @copyright Copyright © 2012, Latif Khalifa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files
|
||||||
|
* (the "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||||
|
* persons to whom the Software is furnished to do so, subject to the
|
||||||
|
* following conditions:
|
||||||
|
*
|
||||||
|
* - The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once SITE_ROOT.'/lib/llsd_classes.php';
|
||||||
|
require_once SITE_ROOT.'/lib/llsd_decode.php';
|
||||||
|
|
||||||
|
class ReportParser
|
||||||
|
{
|
||||||
|
function parse($id)
|
||||||
|
{
|
||||||
|
global $DB;
|
||||||
|
$q = kl_str_sql("select * from raw_reports where report_id=!i", $id);
|
||||||
|
if (!$res = $DB->query($q) OR !$row = $DB->fetchRow($res))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$data = new stdClass;
|
||||||
|
$DB->loadFromDbRow($data, $res, $row);
|
||||||
|
$data->report = llsd_decode($data->raw_data);
|
||||||
|
unset($data->raw_data);
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
213
htdocs/lib/llsd_classes.php
Normal file
213
htdocs/lib/llsd_classes.php
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @file llsd_classes.php
|
||||||
|
* @brief Classes used to encode LLSD objects in PHP native objects.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2007&license=mit$
|
||||||
|
*
|
||||||
|
* Copyright (c) 2007-2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
class llsd_UUID
|
||||||
|
{
|
||||||
|
static $_NULL_UUID = '00000000-0000-0000-0000-000000000000';
|
||||||
|
var $Value;
|
||||||
|
|
||||||
|
function llsd_UUID()
|
||||||
|
{
|
||||||
|
$this->Value = self::$_NULL_UUID;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CheckUUID($UUID)
|
||||||
|
{
|
||||||
|
$uuidlen = strlen($UUID);
|
||||||
|
if (32 === $uuidlen)
|
||||||
|
{
|
||||||
|
return ctype_xdigit($UUID);
|
||||||
|
}
|
||||||
|
elseif (36 === $uuidlen)
|
||||||
|
{
|
||||||
|
// We support UUIDs in 8-4-4-4-12 form
|
||||||
|
return ctype_xdigit(substr($UUID, 0, 8)) &&
|
||||||
|
($UUID[8] == '-') &&
|
||||||
|
ctype_xdigit(substr($UUID, 9, 4)) &&
|
||||||
|
($UUID[13] == '-') &&
|
||||||
|
ctype_xdigit(substr($UUID, 14, 4)) &&
|
||||||
|
($UUID[18] == '-') &&
|
||||||
|
ctype_xdigit(substr($UUID, 19, 4)) &&
|
||||||
|
($UUID[23] == '-') &&
|
||||||
|
ctype_xdigit(substr($UUID, 24, 12));
|
||||||
|
}
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Set($UUID)
|
||||||
|
{
|
||||||
|
if ($UUID == '')
|
||||||
|
{
|
||||||
|
$this->Value = self::$_NULL_UUID;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->CheckUUID($UUID))
|
||||||
|
{
|
||||||
|
$this->Value = $UUID;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unset($this->Value);
|
||||||
|
throw new ImproperInvocationException('Invalid UUID string passed to class');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get()
|
||||||
|
{
|
||||||
|
if (isset($this->Value))
|
||||||
|
{
|
||||||
|
// Return actual UUID
|
||||||
|
return $this->Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Null UUID - Mimics Python's UUID class
|
||||||
|
return self::$_NULL_UUID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function __toString()
|
||||||
|
{
|
||||||
|
return $this->Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class llsd_URI
|
||||||
|
{
|
||||||
|
var $Value;
|
||||||
|
|
||||||
|
function llsd_URI()
|
||||||
|
{
|
||||||
|
$this->Value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function Set($URI)
|
||||||
|
{
|
||||||
|
$this->Value = $URI;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get()
|
||||||
|
{
|
||||||
|
return $this->Value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class llsd_Date
|
||||||
|
{
|
||||||
|
var $Value;
|
||||||
|
|
||||||
|
function llsd_Date()
|
||||||
|
{
|
||||||
|
$this->Value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function Set($Date)
|
||||||
|
{
|
||||||
|
$this->Value = $Date;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get()
|
||||||
|
{
|
||||||
|
return $this->Value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class llsd_Undef
|
||||||
|
{
|
||||||
|
function __toString()
|
||||||
|
{
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class llsd_Binary
|
||||||
|
{
|
||||||
|
var $Value;
|
||||||
|
var $Encoding;
|
||||||
|
|
||||||
|
function Set($EncodedData = '', $Encoding = 'base64')
|
||||||
|
{
|
||||||
|
switch ($Encoding)
|
||||||
|
{
|
||||||
|
case 'base64':
|
||||||
|
$this->Value = base64_decode($EncodedData);
|
||||||
|
|
||||||
|
if ($this->Value === FALSE)
|
||||||
|
{
|
||||||
|
// Decode failed
|
||||||
|
unset($this->Value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->Value))
|
||||||
|
{
|
||||||
|
// Decode successful
|
||||||
|
$this->Encoding = $Encoding;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Data invalid
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetData()
|
||||||
|
{
|
||||||
|
if (isset($this->Value))
|
||||||
|
return $this->Value;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetEncoding()
|
||||||
|
{
|
||||||
|
if (isset($this->Encoding))
|
||||||
|
return $this->Encoding;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString() {
|
||||||
|
return $this->GetData();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
?>
|
||||||
277
htdocs/lib/llsd_decode.php
Normal file
277
htdocs/lib/llsd_decode.php
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @file llsd_decode.php
|
||||||
|
* @brief Parse XML serialized LLSD.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2007&license=mit$
|
||||||
|
*
|
||||||
|
* Copyright (c) 2007-2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
class LLSDParser
|
||||||
|
{
|
||||||
|
var $parser;
|
||||||
|
|
||||||
|
var $result;
|
||||||
|
var $inLLSDElement;
|
||||||
|
|
||||||
|
var $stack;
|
||||||
|
var $keyStack;
|
||||||
|
/* These parallel stacks represent the state of nested structures being
|
||||||
|
built up. The deepest structure being built is at the end. When each
|
||||||
|
data value is completely parsed, it is stored in the array at the end
|
||||||
|
of $stack. If the end of the $keyStack is a string, then that is the
|
||||||
|
key to store it under if it is False, then we are building an array and
|
||||||
|
the new value is placed at the end. If the stacks are empty when we
|
||||||
|
finish parsing a value, then it is the top level value and it is the
|
||||||
|
$result. */
|
||||||
|
|
||||||
|
var $depth;
|
||||||
|
var $skipping;
|
||||||
|
var $skipThrough;
|
||||||
|
|
||||||
|
var $currentContent;
|
||||||
|
var $currentEncoding;
|
||||||
|
|
||||||
|
|
||||||
|
// *TODO: This is really only parsing "XML".
|
||||||
|
// It does not validate that the XML is actually
|
||||||
|
// valid LLSD. We /may/ want to fix this someday.
|
||||||
|
// Or if it's a performance hit, we may want to at
|
||||||
|
// least check for valid LLSD in development mode,
|
||||||
|
// but not in production.
|
||||||
|
//
|
||||||
|
function LLSDParser()
|
||||||
|
{
|
||||||
|
$this->parser = xml_parser_create();
|
||||||
|
$this->result = null;
|
||||||
|
$this->inLLSDElement = false;
|
||||||
|
|
||||||
|
$this->stack = array();
|
||||||
|
$this->keyStack = array();
|
||||||
|
|
||||||
|
$this->depth = 0;
|
||||||
|
$this->skipping = false;
|
||||||
|
$this->skipThrough;
|
||||||
|
|
||||||
|
$this->currentContent = '';
|
||||||
|
|
||||||
|
xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, False);
|
||||||
|
xml_set_object($this->parser, $this);
|
||||||
|
xml_set_element_handler($this->parser, 'tag_open', 'tag_close');
|
||||||
|
xml_set_character_data_handler($this->parser, 'cdata');
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetLLSDObject()
|
||||||
|
{
|
||||||
|
return $this->result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse($data)
|
||||||
|
{
|
||||||
|
$result = xml_parse($this->parser, $data);
|
||||||
|
|
||||||
|
if( $result == 0 )
|
||||||
|
{
|
||||||
|
$errno = xml_get_error_code( $this->parser );
|
||||||
|
$errstr = xml_error_string( $errno );
|
||||||
|
|
||||||
|
$line = xml_get_current_line_number( $this->parser );
|
||||||
|
$col = xml_get_current_column_number( $this->parser );
|
||||||
|
|
||||||
|
$msg = "$errstr (line $line, col $col)";
|
||||||
|
|
||||||
|
throw new Exception( $msg, $errno );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startSkipping()
|
||||||
|
{
|
||||||
|
$this->skipping = true;
|
||||||
|
$this->skipThrough = $this->depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tag_open($parser, $tag, $attributes)
|
||||||
|
{
|
||||||
|
$this->depth += 1;
|
||||||
|
if ($this->skipping)
|
||||||
|
return;
|
||||||
|
|
||||||
|
$this->currentContent = '';
|
||||||
|
|
||||||
|
switch ($tag)
|
||||||
|
{
|
||||||
|
case 'llsd':
|
||||||
|
if ($this->inLLSDElement)
|
||||||
|
return $this->startSkipping();
|
||||||
|
$this->inLLSDElement = true;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 'key':
|
||||||
|
if (empty($this->keyStack) or end($this->keyStack) === false)
|
||||||
|
return $this->startSkipping();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->inLLSDElement)
|
||||||
|
return $this->startSkipping();
|
||||||
|
|
||||||
|
switch ($tag)
|
||||||
|
{
|
||||||
|
case 'binary':
|
||||||
|
$this->currentEncoding = $attributes['encoding'];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'map':
|
||||||
|
$this->stack[] = array();
|
||||||
|
$this->keyStack[] = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'array':
|
||||||
|
$this->stack[] = array();
|
||||||
|
$this->keyStack[] = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tag_close($parser, $tag)
|
||||||
|
{
|
||||||
|
$this->depth -= 1;
|
||||||
|
if ($this->skipping)
|
||||||
|
{
|
||||||
|
if ($this->depth < $this->skipThrough)
|
||||||
|
{
|
||||||
|
$this->skipping = false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch ($tag)
|
||||||
|
{
|
||||||
|
case 'llsd':
|
||||||
|
$this->inLLSDElement = false;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 'key':
|
||||||
|
array_pop($this->keyStack);
|
||||||
|
$this->keyStack[] = $this->currentContent;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!$this->inLLSDElement) return;
|
||||||
|
|
||||||
|
$content = $this->currentContent;
|
||||||
|
$value = null;
|
||||||
|
switch ($tag)
|
||||||
|
{
|
||||||
|
case 'undef':
|
||||||
|
$value = null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'boolean':
|
||||||
|
$value = $content == 'true' || $content == '1';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'integer':
|
||||||
|
$value = (int)$content;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'real':
|
||||||
|
$value = (float)$content;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'string':
|
||||||
|
$value = (string)$content;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'uuid':
|
||||||
|
$value = new llsd_UUID;
|
||||||
|
$value->Set($content);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'date':
|
||||||
|
$value = new llsd_Date;
|
||||||
|
$value->Set($content);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'uri':
|
||||||
|
$value = new llsd_URI;
|
||||||
|
$value->Set($content);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'binary':
|
||||||
|
$value = new llsd_Binary;
|
||||||
|
$value->Set($content, $this->currentEncoding);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'array':
|
||||||
|
case 'map':
|
||||||
|
$value = array_pop($this->stack);
|
||||||
|
array_pop($this->keyStack);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$value = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (empty($this->stack))
|
||||||
|
{
|
||||||
|
$this->result = $value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$n = count($this->stack) - 1;
|
||||||
|
$struct = &$this->stack[$n];
|
||||||
|
$key = $this->keyStack[$n];
|
||||||
|
if ($key === false)
|
||||||
|
{
|
||||||
|
$struct[] = $value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$struct[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cdata($parser, $cdata)
|
||||||
|
{
|
||||||
|
if ($this->skipping)
|
||||||
|
return;
|
||||||
|
$this->currentContent .= $cdata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function llsd_decode($str)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$LLSDParser = new LLSDParser();
|
||||||
|
$LLSDParser->parse($str);
|
||||||
|
}
|
||||||
|
catch (Exception $e)
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $LLSDParser->GetLLSDObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
253
htdocs/lib/llsd_encode.php
Normal file
253
htdocs/lib/llsd_encode.php
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @file llsd_endcode.php
|
||||||
|
* @brief Serialize LLSD to XML.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2007&license=mit$
|
||||||
|
*
|
||||||
|
* Copyright (c) 2007-2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
// VERY IMPORTANT
|
||||||
|
// Read the optimization notes at the end of this file before editing.
|
||||||
|
|
||||||
|
class LLSD_Encoder
|
||||||
|
{
|
||||||
|
function encode(&$node)
|
||||||
|
{
|
||||||
|
ob_start();
|
||||||
|
echo '<llsd>';
|
||||||
|
$this->encode_node($node);
|
||||||
|
echo '</llsd>';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
function encode_node(&$node)
|
||||||
|
{
|
||||||
|
switch (gettype($node))
|
||||||
|
{
|
||||||
|
case 'array': // if (is_array($node))
|
||||||
|
if ($this->detect_map($node))
|
||||||
|
{
|
||||||
|
echo '<map>';
|
||||||
|
foreach ($node as $key => &$value)
|
||||||
|
{
|
||||||
|
echo '<key>',
|
||||||
|
htmlspecialchars($key, ENT_NOQUOTES),
|
||||||
|
'</key>';
|
||||||
|
$this->encode_node($value);
|
||||||
|
}
|
||||||
|
echo '</map>';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo '<array>';
|
||||||
|
foreach ($node as &$value)
|
||||||
|
{
|
||||||
|
$this->encode_node($value);
|
||||||
|
}
|
||||||
|
echo '</array>';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'integer': // else if (is_int($node))
|
||||||
|
echo '<integer>',
|
||||||
|
htmlspecialchars($node, ENT_NOQUOTES),
|
||||||
|
'</integer>';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'double': // else if (is_float($node))
|
||||||
|
echo '<real>',
|
||||||
|
htmlspecialchars($node, ENT_NOQUOTES),
|
||||||
|
'</real>';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'boolean': // else if (is_bool($node))
|
||||||
|
if ($node) echo '<boolean>true</boolean>';
|
||||||
|
else echo '<boolean>false</boolean>';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'object': // else if (is_object($node))
|
||||||
|
switch (get_class($node))
|
||||||
|
{
|
||||||
|
case "llsd_UUID":
|
||||||
|
echo '<uuid>',
|
||||||
|
htmlspecialchars($node->Get(), ENT_NOQUOTES),
|
||||||
|
'</uuid>';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "llsd_URI":
|
||||||
|
echo '<uri>',
|
||||||
|
htmlspecialchars($node->Get(), ENT_NOQUOTES),
|
||||||
|
'</uri>';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "llsd_Date":
|
||||||
|
echo '<date>',
|
||||||
|
htmlspecialchars($node->Get(), ENT_NOQUOTES),
|
||||||
|
'</date>';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "llsd_Binary":
|
||||||
|
echo '<binary encoding="',
|
||||||
|
htmlspecialchars($node->GetEncoding(), ENT_QUOTES),
|
||||||
|
'">';
|
||||||
|
$this->encode_binary($node);
|
||||||
|
echo '</binary>';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
echo '<string>',
|
||||||
|
htmlspecialchars($node, ENT_NOQUOTES),
|
||||||
|
'</string>';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'NULL': // else if ($node === null)
|
||||||
|
echo '<undef/>';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: //else
|
||||||
|
echo '<string>',
|
||||||
|
htmlspecialchars($node, ENT_NOQUOTES),
|
||||||
|
'</string>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function detect_map(&$node)
|
||||||
|
{
|
||||||
|
// This routine accounts for only about 10% of the time
|
||||||
|
$index = 0;
|
||||||
|
foreach ($node as $key => &$value)
|
||||||
|
{
|
||||||
|
if ($key !== $index) return true;
|
||||||
|
++$index;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function encode_string(&$node)
|
||||||
|
{
|
||||||
|
# NB: This function has been in-lined into encode_node()
|
||||||
|
echo htmlspecialchars($node, ENT_NOQUOTES);
|
||||||
|
# NB: DO NOT add a charset argument ('UTF-8')
|
||||||
|
# In that case, PHP only supports 16-bit Unicode chars.
|
||||||
|
# Which is horribly, horribly broken.
|
||||||
|
}
|
||||||
|
|
||||||
|
function encode_attribute(&$node)
|
||||||
|
{
|
||||||
|
# NB: This function has been in-lined into encode_node()
|
||||||
|
echo htmlspecialchars($node, ENT_QUOTES);
|
||||||
|
# NB: DO NOT add a charset argument ('UTF-8')
|
||||||
|
# In that case, PHP only supports 16-bit Unicode chars.
|
||||||
|
# Which is horribly, horribly broken.
|
||||||
|
}
|
||||||
|
|
||||||
|
function encode_binary(&$node)
|
||||||
|
{
|
||||||
|
$encoding = $node->GetEncoding();
|
||||||
|
|
||||||
|
if ($encoding == "base64")
|
||||||
|
{
|
||||||
|
echo base64_encode($node->Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function llsd_encode(&$node)
|
||||||
|
{
|
||||||
|
$encoder = new LLSD_Encoder();
|
||||||
|
return $encoder->encode($node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OPTIMIZATION
|
||||||
|
|
||||||
|
This file has been heavily optimized as it has been shown to be one of the
|
||||||
|
significant bottlenecks in the system. Due to the vagaries of PHP, many of
|
||||||
|
these optimizations are counter to good coding practice. Do not undo these
|
||||||
|
changes without careful profiling and understanding. All of these methods
|
||||||
|
were confirmed by careful timing tests. Many are listed among common PHP
|
||||||
|
optimizations by other programmers on the Internet.
|
||||||
|
|
||||||
|
1) Generating the string.
|
||||||
|
Three output methods were compared, and are shown with relative timing:
|
||||||
|
concatenating strings 1.00 (baseline)
|
||||||
|
writing to php://temp 1.14
|
||||||
|
output buffering 0.90
|
||||||
|
|
||||||
|
Savings: 10%.
|
||||||
|
|
||||||
|
|
||||||
|
2) Using switch(gettype($node))
|
||||||
|
The PHP manual cautions on using gettype() in that they don't guarantee that
|
||||||
|
the returned strings won't change in future versions of PHP. The unit tests
|
||||||
|
will catch if this changes in a way that breaks this code.
|
||||||
|
|
||||||
|
The recommend way of using a cascade of if tests on is_array(), is_int(), etc.
|
||||||
|
is retained in the comments here for reference, but should not be used.
|
||||||
|
|
||||||
|
Savings: 3.5%
|
||||||
|
|
||||||
|
|
||||||
|
3) Inlining calls
|
||||||
|
Function call overhead, even to compiled in library functions, is very
|
||||||
|
expensive in PHP. Calls to encode_string and encode_attribute were inlined
|
||||||
|
into encode_node. The functions were left in, as the comments therein are
|
||||||
|
extremely important for future programmers.
|
||||||
|
|
||||||
|
Savings: 10%
|
||||||
|
|
||||||
|
4) Combining echo statements
|
||||||
|
Echo in PHP is not a function, and can take a number of values in a single
|
||||||
|
statement. Though the code would be clearer with multiple echo statements in
|
||||||
|
a many places, they have been combined into one statement. Note that it is not
|
||||||
|
worth returning a string from a called function just so that it can be combined
|
||||||
|
into a single echo statement of the caller. In those cases, it is best to just
|
||||||
|
echo from within the called function.
|
||||||
|
|
||||||
|
Savings: 6%
|
||||||
|
|
||||||
|
5) Using foreach
|
||||||
|
While the PHP manual states that foreach($a as $k => $v) is the same as
|
||||||
|
reset($k); while (($k, $v) = each($a)), the foreach version is faster.
|
||||||
|
|
||||||
|
Savings: 24%
|
||||||
|
|
||||||
|
6) Using & in foreach
|
||||||
|
Coding foreach with a reference on the value yields an improvement.
|
||||||
|
|
||||||
|
Savings: 6%
|
||||||
|
-----------
|
||||||
|
Total Savings after all optimizations: 46%, or 1.8x faster!
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
?>
|
||||||
Reference in New Issue
Block a user