'; $this->encode_node($node); echo ''; return ob_get_clean(); } function encode_node(&$node) { switch (gettype($node)) { case 'array': // if (is_array($node)) if ($this->detect_map($node)) { echo ''; foreach ($node as $key => &$value) { echo '', htmlspecialchars($key, ENT_NOQUOTES), ''; $this->encode_node($value); } echo ''; } else { echo ''; foreach ($node as &$value) { $this->encode_node($value); } echo ''; } break; case 'integer': // else if (is_int($node)) echo '', htmlspecialchars($node, ENT_NOQUOTES), ''; break; case 'double': // else if (is_float($node)) echo '', htmlspecialchars($node, ENT_NOQUOTES), ''; break; case 'boolean': // else if (is_bool($node)) if ($node) echo 'true'; else echo 'false'; break; case 'object': // else if (is_object($node)) switch (get_class($node)) { case "llsd_UUID": echo '', htmlspecialchars($node->Get(), ENT_NOQUOTES), ''; break; case "llsd_URI": echo '', htmlspecialchars($node->Get(), ENT_NOQUOTES), ''; break; case "llsd_Date": echo '', htmlspecialchars($node->Get(), ENT_NOQUOTES), ''; break; case "llsd_Binary": echo ''; $this->encode_binary($node); echo ''; break; default: echo '', htmlspecialchars($node, ENT_NOQUOTES), ''; break; } break; case 'NULL': // else if ($node === null) echo ''; break; default: //else echo '', htmlspecialchars($node, ENT_NOQUOTES), ''; } } 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! */ ?>