diff --git a/_tests/codechecker/code_quality.php b/_tests/codechecker/code_quality.php
index 69a2043e0..656d3ca22 100644
--- a/_tests/codechecker/code_quality.php
+++ b/_tests/codechecker/code_quality.php
@@ -1033,7 +1033,7 @@ function check_command($command, $depth, $function_guard = '', $nogo_parameters
                 }
                 break;
             case 'FOREACH_map':
-                $passes = ensure_type(array('array'), check_expression($c[1], false, false, $function_guard), $c_pos, 'Foreach must take array');
+                $passes = ensure_type(array('array', 'object'), check_expression($c[1], false, false, $function_guard), $c_pos, 'Foreach must take array');
                 if ($passes) {
                     infer_expression_type_to_variable_type('array', $c[1]);
                 }
@@ -1639,7 +1639,7 @@ function check_assignment($c, $c_pos, $function_guard = '')
     if ($target[0] == 'ARRAY_APPEND') {
         if (empty($target[2][2])) { // Simple variable, meaning we can test to see if it's an array
             $v_type = check_variable($target[1], true, $function_guard);
-            $passes = ensure_type(array('array'), $v_type, $c_pos, 'Can only append to an array (not ' . $v_type . ')');
+            $passes = ensure_type(array('array', 'object'), $v_type, $c_pos, 'Can only append to an array (not ' . $v_type . ')');
             if ($passes) {
                 infer_expression_type_to_variable_type('array', $c[3]);
             }
@@ -1995,7 +1995,7 @@ function check_variable($variable, $reference = false, $function_guard = '')
                 $LOCAL_VARIABLES[$gid]['is_global'] = true;
             }
             check_expression($next[1]);
-            $passes = ensure_type(array('array', 'string'), $type, $variable[3], 'Variable must be an array/string due to dereferencing');
+            $passes = ensure_type(array('array', 'string', 'object'), $type, $variable[3], 'Variable must be an array/string due to dereferencing');
             if (ensure_type(array('string'), $type, $variable[3], '', true, true) == 'string') {
                 $type_2 = check_expression($variable[2][1]);
                 $passes = ensure_type(array('integer'), $type_2, $variable[3], 'String character extraction must use an integer');
diff --git a/data_custom/functions.bin b/data_custom/functions.bin
index d95807e66..d48c6d388 100644
Binary files a/data_custom/functions.bin and b/data_custom/functions.bin differ
diff --git a/sources/comcode_renderer.php b/sources/comcode_renderer.php
index 7a396b669..e99f10311 100644
--- a/sources/comcode_renderer.php
+++ b/sources/comcode_renderer.php
@@ -764,7 +764,9 @@ function _do_tags_comcode($tag, $attributes, $embed, $comcode_dangerous, $pass_i
             }
 
             if (isset($temp_tpl->preprocessable_bits)) {
-                $temp_tpl->preprocessable_bits = array_merge($temp_tpl->preprocessable_bits, $embed->preprocessable_bits);
+                foreach ($embed->preprocessable_bits as $preprocessable_bit) {
+                    $temp_tpl->preprocessable_bits[] = $preprocessable_bit;
+                }
             }
 
             $type = $attributes['param'];
diff --git a/sources/symbols.php b/sources/symbols.php
index 66da26968..6fd987425 100644
--- a/sources/symbols.php
+++ b/sources/symbols.php
@@ -1627,7 +1627,7 @@ function ecv_CDN_FILTER($lang, $escaped, $param)
 function ecv_LOAD_PANEL($lang, $escaped, $param)
 {
     $temp_array = array();
-    handle_symbol_preprocessing(array($escaped, TC_SYMBOL, 'LOAD_PANEL', $param), $temp_array); // Late preprocessing. Should not be needed in case of full screen output (as this was properly preprocessed), but is in other cases
+    handle_symbol_preprocessing(make_preprocessable_bit($escaped, TC_SYMBOL, 'LOAD_PANEL', $param), $temp_array); // Late preprocessing. Should not be needed in case of full screen output (as this was properly preprocessed), but is in other cases
 
     foreach ($param as $i => $p) {
         if (is_object($p)) {
@@ -2946,7 +2946,7 @@ function ecv_MAKE_RELATIVE_DATE($lang, $escaped, $param)
 function ecv_LOAD_PAGE($lang, $escaped, $param)
 {
     $temp_array = array();
-    handle_symbol_preprocessing(array($escaped, TC_SYMBOL, 'LOAD_PAGE', $param), $temp_array); // Late preprocessing. Should not be needed in case of full screen output (as this was properly preprocessed), but is in other cases
+    handle_symbol_preprocessing(make_preprocessable_bit($escaped, TC_SYMBOL, 'LOAD_PAGE', $param), $temp_array); // Late preprocessing. Should not be needed in case of full screen output (as this was properly preprocessed), but is in other cases
 
     foreach ($param as $i => $p) {
         if (is_object($p)) {
@@ -3033,7 +3033,7 @@ function ecv_BLOCK($lang, $escaped, $param)
 {
     if (!$GLOBALS['STATIC_TEMPLATE_TEST_MODE']) { // Normal operation
         $temp_array = array();
-        handle_symbol_preprocessing(array($escaped, TC_SYMBOL, 'BLOCK', $param), $temp_array); // Late preprocessing. Should not be needed in case of full screen output (as this was properly preprocessed), but is in other cases
+        handle_symbol_preprocessing(make_preprocessable_bit($escaped, TC_SYMBOL, 'BLOCK', $param), $temp_array); // Late preprocessing. Should not be needed in case of full screen output (as this was properly preprocessed), but is in other cases
 
         foreach ($param as $i => $p) {
             if (is_object($p)) {
@@ -3210,7 +3210,7 @@ function ecv_FIND_SCRIPT($lang, $escaped, $param)
 function ecv_CSS_TEMPCODE($lang, $escaped, $param)
 {
     $temp_array = array();
-    handle_symbol_preprocessing(array($escaped, TC_SYMBOL, 'CSS_TEMPCODE', $param), $temp_array); // Late preprocessing. Should not be needed in case of full screen output (as this was properly preprocessed), but is in other cases
+    handle_symbol_preprocessing(make_preprocessable_bit($escaped, TC_SYMBOL, 'CSS_TEMPCODE', $param), $temp_array); // Late preprocessing. Should not be needed in case of full screen output (as this was properly preprocessed), but is in other cases
 
     $_value = css_tempcode();
     $value = $_value->evaluate();
@@ -3234,7 +3234,7 @@ function ecv_CSS_TEMPCODE($lang, $escaped, $param)
 function ecv_JS_TEMPCODE($lang, $escaped, $param)
 {
     $temp_array = array();
-    handle_symbol_preprocessing(array($escaped, TC_SYMBOL, 'JS_TEMPCODE', $param), $temp_array); // Late preprocessing. Should not be needed in case of full screen output (as this was properly preprocessed), but is in other cases
+    handle_symbol_preprocessing(make_preprocessable_bit($escaped, TC_SYMBOL, 'JS_TEMPCODE', $param), $temp_array); // Late preprocessing. Should not be needed in case of full screen output (as this was properly preprocessed), but is in other cases
 
     $_value = javascript_tempcode(((isset($param[0])) && ($param[0]) != '') ? $param[0] : null);
     $value = $_value->evaluate();
diff --git a/sources/tempcode.php b/sources/tempcode.php
index ef3338fc5..e115532c8 100644
--- a/sources/tempcode.php
+++ b/sources/tempcode.php
@@ -18,6 +18,8 @@
  * @package    core
  */
 
+/*EXTRA FUNCTIONS: Spl.+*/
+
 /**
  * Standard code module initialisation function.
  *
@@ -299,7 +301,11 @@ function build_closure_tempcode($type, $name, $parameters, $escaping = null)
         }
     }
 
-    $ret = new Tempcode(array(array($myfunc => $funcdef), array(array(array($myfunc, ($parameters === null) ? array() : $parameters, $type, $name, '')))));
+    $seq_parts = new SplDoublyLinkedList();
+    $seq_parts_group = new SplDoublyLinkedList();
+    $seq_parts_group[] = make_seq_part($myfunc, ($parameters === null) ? array() : $parameters, $type, $name, '');
+    $seq_parts[] = $seq_parts_group;
+    $ret = new Tempcode(array(array($myfunc => $funcdef), $seq_parts));
     if ($type === TC_LANGUAGE_REFERENCE) {
         $ret->pure_lang = true;
     }
@@ -478,6 +484,46 @@ function closure_loop($param, $args, $main_function)
     return $value;
 }
 
+/**
+ * Make a seq-part tuple.
+ *
+ * @param  string $myfunc Function to call
+ * @param  array $parameters Map of parameters
+ * @param  integer $type A TC_* constant
+ * @param  string $name A name parameter, e.g. the name of a symbol for TC_SYMBOL
+ * @param  LANGUAGE_NAME $current_lang The current language
+ * @return object Tuple
+ */
+function make_seq_part($myfunc, $parameters, $type, $name, $current_lang)
+{
+    $seq_part = new SplFixedArray(5);
+    $seq_part[0] = $myfunc;
+    $seq_part[1] = $parameters;
+    $seq_part[2] = $type;
+    $seq_part[3] = $name;
+    $seq_part[4] = $current_lang;
+    return $seq_part;
+}
+
+/**
+ * Make a preprocessable-bit tuple.
+ *
+ * @param  array $escaped List of *_ESCAPED constants
+ * @param  integer $type A TC_* constant
+ * @param  string $name A name parameter, e.g. the name of a symbol for TC_SYMBOL
+ * @param  array $parameters Map of parameters
+ * @return object Tuple
+ */
+function make_preprocessable_bit($escaped, $type, $name, $parameters)
+{
+    $pp_bit = new SplFixedArray(4);
+    $pp_bit[0] = $escaped;
+    $pp_bit[1] = $type;
+    $pp_bit[2] = $name;
+    $pp_bit[3] = $parameters;
+    return $pp_bit;
+}
+
 /**
  * Convert a string to Tempcode.
  *
@@ -496,7 +542,10 @@ function make_string_tempcode($string)
     $myfunc = 'string_attach_' . $generator_base . '_' . strval($generator_num)/*We'll inline it actually rather than calling, for performance   fast_uniqid()*/
     ;
     $code_to_preexecute = array($myfunc => "\$tpl_funcs['$myfunc']=\"echo \\\"" . php_addslashes_twice($string) . "\\\";\";\n");
-    $seq_parts = array(array(array($myfunc, array(), TC_KNOWN, '', '')));
+    $seq_parts = new SplDoublyLinkedList();
+    $seq_parts_group = new SplDoublyLinkedList();
+    $seq_parts_group[] = make_seq_part($myfunc, array(), TC_KNOWN, '', '');
+    $seq_parts[] = $seq_parts_group;
     return new Tempcode(array($code_to_preexecute, $seq_parts));
 }
 
@@ -558,7 +607,7 @@ function apply_tempcode_escaping($escaped, &$value)
             $value = filter_naughty_harsh($value, true);
         }
     }
-    if (($GLOBALS['XSS_DETECT']) && ($escaped !== array())) {
+    if (($GLOBALS['XSS_DETECT']) && (!empty($escaped))) {
         ocp_mark_as_escaped($value);
     }
 
@@ -611,7 +660,7 @@ function apply_tempcode_escaping_inline($escaped, $value)
             $value = filter_naughty_harsh($value, true);
         }
     }
-    if (($GLOBALS['XSS_DETECT']) && ($escaped !== array())) {
+    if (($GLOBALS['XSS_DETECT']) && (!empty($escaped))) {
         ocp_mark_as_escaped($value);
     }
 
@@ -869,9 +918,9 @@ function do_template($codename, $parameters = null, $lang = null, $light_error =
         }
         $out->seq_parts = $_data->seq_parts;
 
-        foreach ($out->seq_parts as &$seq_parts_group) {
-            foreach ($seq_parts_group as &$seq_part) {
-                if ($seq_part[1] !== array()) {
+        foreach ($out->seq_parts as $seq_parts_group) {
+            foreach ($seq_parts_group as $seq_part) {
+                if (!empty($seq_part[1])) {
                     $seq_part[1] = array();
                 }
             }
@@ -940,7 +989,7 @@ function dependencies_are_good($dep, $tcp_time)
 /**
  * Certain symbols need preprocessing, before the output stream is made.
  *
- * @param  array $seq_part Symbol details
+ * @param  object $seq_part Symbol details
  * @param  array $children Where we store children stuff
  */
 function handle_symbol_preprocessing($seq_part, &$children)
@@ -1341,7 +1390,7 @@ function handle_symbol_preprocessing($seq_part, &$children)
 class Tempcode
 {
     public $code_to_preexecute;
-    public $seq_parts; // List of list of closure pairs: (0) function name, and (1) parameters, (2) type, (3) name         We use a 2D list to make attach ops very fast
+    public $seq_parts; // List of list of extended closure pairs: (0) function name, and (1) parameters, (2) type, (3) name, (4) current language         We use a 2D list to make attach ops very fast
     public $preprocessable_bits; // List of tuples: escape (ignored), type (e.g. TC_SYMBOL), name, parameters
     public $pure_lang;
     public $evaluate_echo_offset_group = 0;
@@ -1364,8 +1413,8 @@ class Tempcode
         $this->cached_output = null;
 
         if (!isset($details)) {
-            $this->preprocessable_bits = array();
-            $this->seq_parts = array();
+            $this->preprocessable_bits = new SplDoublyLinkedList();
+            $this->seq_parts = new SplDoublyLinkedList();
             $this->code_to_preexecute = array();
         } else {
             $this->code_to_preexecute = $details[0];
@@ -1389,14 +1438,14 @@ class Tempcode
                                 case 'PAGE_LINK':
                                 case 'LOAD_PAGE':
                                 case 'LOAD_PANEL':
-                                    $pp_bits[] = array(array(), TC_SYMBOL, $seq_part[3], $seq_part[1]);
+                                    $pp_bits[] = make_preprocessable_bit(array(), TC_SYMBOL, $seq_part[3], $seq_part[1]);
                                     break;
                             }
                         } elseif ($seq_part[2] === TC_DIRECTIVE) {
                             switch ($seq_part[3]) {
                                 case 'INCLUDE':
                                 case 'FRACTIONAL_EDITABLE':
-                                    $pp_bits[] = array(array(), TC_DIRECTIVE, $seq_part[3], $seq_part[1]);
+                                    $pp_bits[] = make_preprocessable_bit(array(), TC_DIRECTIVE, $seq_part[3], $seq_part[1]);
                                     break;
                             }
                         }
@@ -1412,7 +1461,7 @@ class Tempcode
 
                 $this->preprocessable_bits = $pp_bits;
             } else {
-                $this->preprocessable_bits = array();
+                $this->preprocessable_bits = new SplDoublyLinkedList();
             }
         }
 
@@ -1437,8 +1486,8 @@ class Tempcode
      */
     public function decache()
     {
-        foreach ($this->seq_parts as &$seq_parts_group) {
-            foreach ($seq_parts_group as &$seq_part) {
+        foreach ($this->seq_parts as $seq_parts_group) {
+            foreach ($seq_parts_group as $seq_part) {
                 foreach ($seq_part[1] as $val) {
                     if (isset($val->codename/*faster than is_object*/)) {
                         $val->decache();
@@ -1483,8 +1532,8 @@ class Tempcode
         $this->cached_output = null;
 
         if (isset($attach->codename)/*faster than is_object*/) { // Consider it another piece of Tempcode
-            foreach ($attach->seq_parts as $seq_part_group) {
-                $this->seq_parts[] = $seq_part_group;
+            foreach ($attach->seq_parts as $seq_parts_group) {
+                $this->seq_parts[] = clone $seq_parts_group;
             }
 
             $this->code_to_preexecute += $attach->code_to_preexecute;
@@ -1499,11 +1548,11 @@ class Tempcode
                 $this->children[] = array($attach->codename, isset($attach->children) ? $attach->children : array(), isset($attach->fresh) ? $attach->fresh : false);
             }
         } else { // Consider it a string
-            if (end($this->seq_parts) !== false) {
-                $end = &$this->seq_parts[key($this->seq_parts)];
+            if ($this->seq_parts->isEmpty()) {
+                $this->seq_parts[] = new SplDoublyLinkedList();
+                $end = $this->seq_parts[0];
             } else {
-                $this->seq_parts[] = array();
-                $end = &$this->seq_parts[0];
+                $end = $this->seq_parts->top();
             }
 
             static $generator_base = null;
@@ -1516,7 +1565,7 @@ class Tempcode
             $myfunc = 'string_attach_' . $generator_base . '_' . strval($generator_num);/*We'll inline it actually rather than calling, for performance   fast_uniqid()*/
             $funcdef = "\$tpl_funcs['$myfunc']=\"echo \\\"" . php_addslashes_twice($attach) . "\\\";\";\n";
             $this->code_to_preexecute[$myfunc] = $funcdef;
-            $end[] = array($myfunc, array(), TC_KNOWN, '', '');
+            $end[] = make_seq_part($myfunc, array(), TC_KNOWN, '', '');
         }
 
         $this->codename = '(mixed)';
@@ -1557,7 +1606,7 @@ class Tempcode
         $this->cached_output = null;
         list($this->seq_parts, $this->preprocessable_bits, $this->codename, $this->pure_lang, $this->code_to_preexecute) = $result;
         if ($GLOBALS['OUTPUT_STREAMING']) {
-            $this->preprocessable_bits = array();
+            $this->preprocessable_bits = new SplDoublyLinkedList();
         }
 
         if ($forced_reload_details[6] === null) {
@@ -1585,33 +1634,26 @@ class Tempcode
      */
     protected function _mark_all_as_escaped($top_level = true)
     {
-        static $done = array();
-
-        foreach ($this->seq_parts as &$seq_parts_group) {
-            foreach ($seq_parts_group as &$seq_part) {
-                if (!isset($seq_part[1]['_escaped'])) {
-                    foreach ($seq_part[1] as &$val) {
+        foreach ($this->seq_parts as $seq_parts_group) {
+            foreach ($seq_parts_group as $seq_part) {
+                $parameters = $seq_part[1];
+                if (!isset($parameters['_escaped'])) {
+                    foreach ($parameters as $key => $val) {
                         if (is_string($val)) {
                             ocp_mark_as_escaped($val);
+                            $parameters[$key] = $val;
+                            $seq_part[1] = $parameters;
                         } elseif (is_object($val)) {
                             $val->_mark_all_as_escaped(false);
                         }
                     }
-                    if (!isset($seq_part[1][0])) { // Only if it's a parameter map, not a parameter list
-                        $seq_part[1]['_escaped'] = true; // Temporarily mark as escaped. Many seq_parts share a referenced list of parameters, and its naive/slow to re-mark for each
+                    if (!isset($parameters[0])) { // Only if it's a parameter map, not a parameter list
+                        $parameters['_escaped'] = true; // Temporarily mark as escaped. Many seq_parts share a referenced list of parameters, and its naive/slow to re-mark for each
+                        $seq_part[1] = $parameters;
                     }
-                    $done[] = &$seq_part[1];
                 }
             }
         }
-
-        if ($top_level) {
-            // Remove the escaping markers, as the escaping marking does not persist with serialisation
-            foreach ($done as $d) {
-                unset($d['_escaped']);
-            }
-            $done = array();
-        }
     }
 
     /**
@@ -1639,7 +1681,7 @@ class Tempcode
         $this->cached_output = null;
         list($this->seq_parts, $this->preprocessable_bits, $this->codename, $this->pure_lang, $this->code_to_preexecute) = $result;
         if ($GLOBALS['OUTPUT_STREAMING']) {
-            $this->preprocessable_bits = array();
+            $this->preprocessable_bits = new SplDoublyLinkedList();
         }
 
         if ($GLOBALS['XSS_DETECT']) {
@@ -1661,7 +1703,7 @@ class Tempcode
         foreach ($this->seq_parts as $seq_parts_group) {
             foreach ($seq_parts_group as $seq_part) {
                 if ($i === $at) {
-                    return ($seq_part[1] === array());
+                    return empty($seq_part[1]);
                 }
                 $i++;
             }
@@ -1692,7 +1734,7 @@ class Tempcode
             foreach ($this->preprocessable_bits as $preprocessable_bit) {
                 foreach ($preprocessable_bit[3] as $i => $param) {
                     if ((($preprocessable_bit[2] !== 'SET') || (($i >= 1))) && (isset($param->codename/*faster than is_object*/))) {
-                        $preprocessable_bit[3][$i] = $param->bind($parameters, '<' . $codename . '>');
+                        $param = $param->bind($parameters, '<' . $codename . '>');
                     }
                 }
                 $out->preprocessable_bits[] = $preprocessable_bit;
@@ -1733,16 +1775,17 @@ class Tempcode
             }
         }
 
-        $out->seq_parts[0] = array();
+        $out->seq_parts = new SplDoublyLinkedList();
+        $new_seq_parts_group = new SplDoublyLinkedList();
         foreach ($this->seq_parts as $seq_parts_group) {
             foreach ($seq_parts_group as $seq_part) {
                 if ((($seq_part[0][0] !== 's') || (substr($seq_part[0], 0, 14) !== 'string_attach_')) && ($seq_part[2] !== TC_LANGUAGE_REFERENCE)) {
-                    $seq_part[1] = &$parameters; // & is to preserve memory
+                    $seq_part[1] = $parameters;
                 }
-                $out->seq_parts[0][] = $seq_part;
+                $new_seq_parts_group[] = clone $seq_part;
             }
         }
-
+        $out->seq_parts[] = $new_seq_parts_group;
         return $out;
     }
 
@@ -1756,14 +1799,16 @@ class Tempcode
     {
         $this->cached_output = null;
 
-        if ($this->seq_parts === array()) {
+        if ($this->seq_parts->isEmpty()) {
             return;
         }
 
-        foreach ($this->seq_parts as &$seq_parts_group) {
-            foreach ($seq_parts_group as &$seq_part) {
+        foreach ($this->seq_parts as $seq_parts_group) {
+            foreach ($seq_parts_group as $seq_part) {
                 if ((($seq_part[0][0] !== 's') || (substr($seq_part[0], 0, 14) !== 'string_attach_')) && ($seq_part[2] !== TC_LANGUAGE_REFERENCE)) {
-                    $seq_part[1][$parameter] = $value;
+                    $parameters = $seq_part[1];
+                    $parameters[$parameter] = $value;
+                    $seq_part[1] = $parameters;
                 }
             }
         }
diff --git a/sources/tempcode_compiler.php b/sources/tempcode_compiler.php
index 7d32e5509..ccbe7038a 100755
--- a/sources/tempcode_compiler.php
+++ b/sources/tempcode_compiler.php
@@ -18,7 +18,7 @@
  * @package    core
  */
 
-/*EXTRA FUNCTIONS: ILess_Autoloader|ILess_Parser|ILess_Cache_FileSystem|less_proxy_compile|proc_.*|escapeshellarg|stream_get_contents*/
+/*EXTRA FUNCTIONS: Spl.+|ILess_Autoloader|ILess_Parser|ILess_Cache_FileSystem|less_proxy_compile|proc_.*|escapeshellarg|stream_get_contents*/
 
 /**
  * Standard code module initialisation function.
@@ -162,13 +162,14 @@ function compile_template($data, $template_name, $theme, $lang, $tolerate_errors
     require_code('lang');
     require_code('urls');
     $cl = fallback_lang();
+    $eval = mixed();
     $bits = array_values(preg_split('#(?<!\\\\)(\{(?![A-Z][a-z])(?=[\dA-Z\$\+\!\_]+[\.`%\*=\;\#\-~\^\|\'&/@+]*))|((?<!\\\\)\,)|((?<!\\\\)\})#', $data, -1, PREG_SPLIT_DELIM_CAPTURE));  // One error mail showed on a server it had weird indexes, somehow. Hence the array_values call to reindex it
     $count = count($bits);
     $stack = array();
     $current_level_mode = PARSE_NO_MANS_LAND;
     $current_level_data = array();
     $current_level_params = array();
-    $preprocessable_bits = array();
+    $preprocessable_bits = new SplDoublyLinkedList();
     $num_preprocessable_bits = 0;
     $preprocessable_bits_stack = array();
     for ($i = 0; $i < $count; $i++) {
@@ -184,7 +185,7 @@ function compile_template($data, $template_name, $theme, $lang, $tolerate_errors
         switch ($next_token) {
             case '{':
                 // Open a new level
-                $stack[] = array($current_level_mode, $current_level_data, $current_level_params, null, null, null, count($preprocessable_bits));
+                $stack[] = array($current_level_mode, $current_level_data, $current_level_params, null, null, null, $preprocessable_bits->count());
                 ++$i;
                 $next_token = isset($bits[$i]) ? $bits[$i] : null;
                 if ($next_token === null) {
@@ -330,7 +331,9 @@ function compile_template($data, $template_name, $theme, $lang, $tolerate_errors
                         if (!$no_preprocess) {
                             switch ($first_param) { // These need preprocessing
                                 case '""':
-                                    array_splice($preprocessable_bits, $num_preprocessable_bits); // Remove anything preprocessable marked inside the comment
+                                    while ($preprocessable_bits->count() > $num_preprocessable_bits) {
+                                        $preprocessable_bits->pop(); // Remove anything preprocessable marked inside the comment
+                                    }
                                     break;
 
                                 case '"REQUIRE_CSS"':
@@ -346,7 +349,7 @@ function compile_template($data, $template_name, $theme, $lang, $tolerate_errors
                                             $tpl_funcs = array();
                                             $eval = debug_eval('return array(' . $_opener_params . ');', $tpl_funcs, array(), $cl);
                                             if (is_array($eval)) {
-                                                $pp_bit = array(array(), TC_SYMBOL, str_replace('"', '', $first_param), $eval);
+                                                $pp_bit = make_preprocessable_bit(array(), TC_SYMBOL, str_replace('"', '', $first_param), $eval);
                                                 $preprocessable_bits[] = $pp_bit;
                                             }
                                             break 2;
@@ -357,10 +360,14 @@ function compile_template($data, $template_name, $theme, $lang, $tolerate_errors
                                     foreach ($opener_params as $param) {
                                         $myfunc = 'tcpfunc_' . fast_uniqid();
                                         $funcdef = build_closure_function($myfunc, $param);
-                                        $symbol_params[] = new Tempcode(array(array($myfunc => $funcdef), array(array(array($myfunc, array(/* Is currently unbound */), TC_KNOWN, '', ''))))); // Parameters will be bound in later.
+                                        $seq_parts = new SplDoublyLinkedList();
+                                        $seq_parts_group = new SplDoublyLinkedList();
+                                        $seq_parts_group[] = make_seq_part($myfunc, array(/* Is currently unbound */), TC_KNOWN, '', '');
+                                        $seq_parts[] = $seq_parts_group;
+                                        $symbol_params[] = new Tempcode(array(array($myfunc => $funcdef), $seq_parts)); // Parameters will be bound in later.
                                     }
 
-                                    $pp_bit = array(array(), TC_SYMBOL, str_replace('"', '', $first_param), $symbol_params);
+                                    $pp_bit = make_preprocessable_bit(array(), TC_SYMBOL, str_replace('"', '', $first_param), $symbol_params);
 
                                     $preprocessable_bits[] = $pp_bit;
                                     break;
@@ -444,7 +451,7 @@ function compile_template($data, $template_name, $theme, $lang, $tolerate_errors
 
                     case PARSE_LANGUAGE_REFERENCE:
                         $new_line = 'ecv($cl,array(' . implode(',', $escaped) . '),' . strval(TC_LANGUAGE_REFERENCE) . ',' . $first_param . ',array(' . $_opener_params . '))';
-                        if (($_opener_params === '') && ($escaped === array())) { // Optimise it out for simple case?
+                        if (($_opener_params === '') && (empty($escaped))) { // Optimise it out for simple case?
                             $tpl_funcs = array();
                             $looked_up = do_lang(debug_eval('return ' . $first_param . ';', $tpl_funcs, array(), $cl), null, null, null, $lang, false);
                             if ($looked_up !== null) {
@@ -468,7 +475,7 @@ function compile_template($data, $template_name, $theme, $lang, $tolerate_errors
                             }
                             $temp .= ')';
 
-                            if ($escaped === array()) {
+                            if (empty($escaped)) {
                                 $current_level_data[] = $temp;
                             } else {
                                 $s_escaped = '';
@@ -501,12 +508,12 @@ function compile_template($data, $template_name, $theme, $lang, $tolerate_errors
                     }
                     if ($eval === 'START') { // START
                         // Open a new directive level
-                        $stack[] = array($current_level_mode, $current_level_data, $current_level_params, $past_level_mode, $past_level_data, $past_level_params, count($preprocessable_bits));
+                        $stack[] = array($current_level_mode, $current_level_data, $current_level_params, $past_level_mode, $past_level_data, $past_level_params, $preprocessable_bits->count());
                         $current_level_data = array();
                         $current_level_params = array();
                         $current_level_mode = PARSE_DIRECTIVE_INNER;
                         if ($opener_params === array(array('"NO_PREPROCESSING"'))) {
-                            array_push($preprocessable_bits_stack, $preprocessable_bits); // So anything inside will end up being thrown away when we pop back to what we had before in $preprocessable_bits
+                            $preprocessable_bits_stack[] = $preprocessable_bits; // So anything inside will end up being thrown away when we pop back to what we had before in $preprocessable_bits
                         }
                     } elseif ($eval === 'END') { // END
                         // Test that the top stack does represent a started directive, and close directive level
@@ -576,7 +583,7 @@ function compile_template($data, $template_name, $theme, $lang, $tolerate_errors
                                 $tpl_funcs = array();
                                 $eval = debug_eval('return array(' . $directive_params . ');', $tpl_funcs, array(), $cl);
                                 if (is_array($eval)) {
-                                    $pp_bit = array(array(), TC_DIRECTIVE, str_replace('"', '', $directive_name), $eval);
+                                    $pp_bit = make_preprocessable_bit(array(), TC_DIRECTIVE, str_replace('"', '', $directive_name), $eval);
                                     $preprocessable_bits[] = $pp_bit;
                                 }
                                 break;
@@ -588,7 +595,7 @@ function compile_template($data, $template_name, $theme, $lang, $tolerate_errors
                             case 'NO_PREPROCESSING':
                                 $current_level_data[] = implode('.', $past_level_data);
                                 $preprocessable_bits = array_pop($preprocessable_bits_stack);
-                                $num_preprocessable_bits = count($preprocessable_bits);
+                                $num_preprocessable_bits = $preprocessable_bits->count();
                                 break;
 
                             case 'IF':
@@ -705,7 +712,9 @@ function compile_template($data, $template_name, $theme, $lang, $tolerate_errors
                                         }
                                         list($_current_level_data, $_preprocessable_bits) = compile_template($filecontents, $eval, $theme, $lang);
                                         $current_level_data = array_merge($current_level_data, $_current_level_data);
-                                        $preprocessable_bits = array_merge($preprocessable_bits, $_preprocessable_bits);
+                                        foreach ($_preprocessable_bits as $preprocessable_bit) {
+                                            $preprocessable_bits[] = $preprocessable_bit;
+                                        }
                                         break;
                                     }
                                 }
@@ -977,28 +986,32 @@ function template_to_tempcode($text, $symbol_pos = 0, $inside_directive = false,
     $parts_group = array();
     foreach ($parts as $part) {
         if (($output_streaming) && (strpos($part, '$bound_') !== false)) { // Start a new seq_part, so output streaming can break at this parameter reference
-            if ($parts_group !== array()) {
+            if (!empty($parts_group)) {
                 $parts_groups[] = $parts_group;
             }
             $parts_group = array();
         }
         $parts_group[] = $part;
     }
-    if ($parts_group !== array()) {
+    if (!empty($parts_group)) {
         $parts_groups[] = $parts_group;
     }
 
     $funcdefs = array();
-    $seq_parts = array();
-    foreach ($parts_groups as $parts_group) {
-        $myfunc = 'tcpfunc_' . fast_uniqid() . '_' . strval(count($seq_parts) + 1);
+    $seq_parts = new SplDoublyLinkedList();
+    foreach ($parts_groups as $i => $parts_group) {
+        $myfunc = 'tcpfunc_' . fast_uniqid() . '_' . strval($i + 1);
         $funcdef = build_closure_function($myfunc, $parts_group);
         $funcdefs[$myfunc] = $funcdef;
-        $seq_parts[] = array(array($myfunc, array(/* Is currently unbound */), TC_KNOWN, '', ''));
+        $seq_parts_group = new SplDoublyLinkedlist();
+        $seq_parts_group[] = make_seq_part($myfunc, array(/* Is currently unbound */), TC_KNOWN, '', '');
+        $seq_parts[] = $seq_parts_group;
     }
 
     $ret = new Tempcode(array($funcdefs, $seq_parts)); // Parameters will be bound in later.
-    $ret->preprocessable_bits = array_merge($ret->preprocessable_bits, $preprocessable_bits);
+    foreach ($preprocessable_bits as $preprocessable_bit) {
+        $ret->preprocessable_bits[] = $preprocessable_bit;
+    }
     $ret->codename = $codename;
     return $ret;
 }
diff --git a/sources/tempcode_optimiser.php b/sources/tempcode_optimiser.php
index 07ae1d508..acda87e8c 100644
--- a/sources/tempcode_optimiser.php
+++ b/sources/tempcode_optimiser.php
@@ -18,6 +18,8 @@
  * @package    core
  */
 
+/*EXTRA FUNCTIONS: Spl.+*/
+
 /*
     Note that optimisation also happens in Tempcode compilation:
      - Comments optimised out.
@@ -39,17 +41,21 @@ function optimise_tempcode(&$ob)
 {
     // Merge seq_part groups (they were only put in separately to make attach super-fast)
     if (isset($ob->seq_parts[0])) {
-        $cnt = count($ob->seq_parts);
-        for ($i = 1; $i < $cnt; $i++) {
-            $ob->seq_parts[0] = array_merge($ob->seq_parts[0], $ob->seq_parts[$i]);
+        $new_seq_parts = new SplDoublyLinkedList();
+        $new_seq_parts_group = new SplDoublyLinkedList();
+        foreach ($ob->seq_parts as $seq_parts_group) {
+            foreach ($seq_parts_group as $seq_part) {
+                $new_seq_parts_group[] = $seq_part;
+            }
         }
-        $ob->seq_parts = array($ob->seq_parts[0]);
+        $new_seq_parts[] = $new_seq_parts_group;
+        $ob->seq_parts = $new_seq_parts;
     }
 
     // Remove unused bindings (we don't do inclusion tests individual seq_parts, as the binding sets are often shared via references)
     $found = array();
-    foreach ($ob->seq_parts as &$seq_part_group) {
-        foreach ($seq_part_group as &$seq_part) {
+    foreach ($ob->seq_parts as $seq_parts_group) {
+        foreach ($seq_parts_group as $seq_part) {
             $code = $ob->code_to_preexecute[$seq_part[0]];
 
             // Indication of dynamic variable use, cannot optimise
@@ -73,8 +79,8 @@ function optimise_tempcode(&$ob)
             }
         }
     }
-    foreach ($ob->seq_parts as &$seq_part_group) {
-        foreach ($seq_part_group as &$seq_part) {
+    foreach ($ob->seq_parts as $seq_parts_group) {
+        foreach ($seq_parts_group as $seq_part) {
             if ((($seq_part[2] == TC_KNOWN) || ($seq_part[2] == TC_PARAMETER)) && (isset($ob->code_to_preexecute[$seq_part[0]]))) {
                 foreach ($seq_part[1] as $key => $_) {
                     if (is_integer($key)) {
@@ -82,7 +88,9 @@ function optimise_tempcode(&$ob)
                     }
 
                     if (!isset($found[$key])) {
-                        unset($seq_part[1][$key]);
+                        $parameters = $seq_part[1];
+                        unset($parameters[$key]);
+                        $seq_part[1] = $parameters;
                     }
                 }
             }
