changeset 23:929c05ba3b2f

fixed sort issue?
author Charlie Root
date Fri, 17 Oct 2025 17:31:07 -0400
parents 303b85d5b561
children e53add5ddac5
files program/lib/Roundcube/rcube_result_thread.php
diffstat 1 files changed, 61 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/program/lib/Roundcube/rcube_result_thread.php	Wed Oct 15 14:06:27 2025 -0400
+++ b/program/lib/Roundcube/rcube_result_thread.php	Fri Oct 17 17:31:07 2025 -0400
@@ -74,12 +74,12 @@
             return;
         }
 
-        $data = array_shift($data);
+        $data = array_first($data);
         $data = trim($data);
         $data = preg_replace('/[\r\n]/', '', $data);
         $data = preg_replace('/\s+/', ' ', $data);
 
-        $this->raw_data = $this->parse_thread($data);
+        $this->raw_data = empty($data) ? '' : $this->parse_thread($data);
     }
 
     /**
@@ -132,8 +132,9 @@
      */
     public function count_messages()
     {
-        if ($this->meta['messages'] !== null)
+        if (!empty($this->meta['messages'])) {
             return $this->meta['messages'];
+        }
 
         if (empty($this->raw_data)) {
             $this->meta['messages'] = 0;
@@ -153,10 +154,14 @@
     /**
      * Returns maximum message identifier in the result
      *
-     * @return int Maximum message identifier
+     * @return int|null Maximum message identifier
      */
     public function max()
     {
+        if ($this->is_empty()) {
+            return null;
+        }
+
         if (!isset($this->meta['max'])) {
             $this->meta['max'] = (int) @max($this->get());
         }
@@ -166,10 +171,14 @@
     /**
      * Returns minimum message identifier in the result
      *
-     * @return int Minimum message identifier
+     * @return int|null Minimum message identifier
      */
     public function min()
     {
+        if ($this->is_empty()) {
+            return null;
+        }
+
         if (!isset($this->meta['min'])) {
             $this->meta['min'] = (int) @min($this->get());
         }
@@ -179,8 +188,8 @@
     /**
      * Slices data set.
      *
-     * @param $offset Offset (as for PHP's array_slice())
-     * @param $length Number of elements (as for PHP's array_slice())
+     * @param int $offset Offset (as for PHP's array_slice())
+     * @param int $length Number of elements (as for PHP's array_slice())
      */
     public function slice($offset, $length)
     {
@@ -207,9 +216,9 @@
         $this->meta          = array();
         $this->meta['count'] = 0;
 
-        while ($start < $datalen &&
-	       (($pos = @strpos($this->raw_data, self::SEPARATOR_ELEMENT,
-			       $start)) != false|| ($pos = $datalen))
+        while ($start < $datalen
+            && (($pos = strpos($this->raw_data, self::SEPARATOR_ELEMENT, $start)) !== false
+                || ($pos = $datalen))
         ) {
             $len   = $pos - $start;
             $elem  = substr($this->raw_data, $start, $len);
@@ -274,7 +283,7 @@
         ));
 
         if (preg_match("/($begin)$msgid($end)/", $this->raw_data, $m,
-            $get_index ? PREG_OFFSET_CAPTURE : null)
+            $get_index ? PREG_OFFSET_CAPTURE : 0)
         ) {
             if ($get_index) {
                 $idx = 0;
@@ -361,8 +370,9 @@
             $regexp  = '(' . $element . '|' . $item . ')';
 
             if (isset($this->meta['pos'][$index])) {
-                if (preg_match('/([0-9]+)/', $this->raw_data, $m, null, $this->meta['pos'][$index]))
+                if (preg_match('/([0-9]+)/', $this->raw_data, $m, 0, $this->meta['pos'][$index])) {
                     $result = $m[1];
+                }
             }
             else if (isset($this->meta['pos'][$index-1])) {
                 // get chunk of data after previous element
@@ -391,7 +401,7 @@
         // Finally use less effective method
         $data = $this->get();
 
-        return $data[$index];
+        return $data[$index] ?? null;
     }
 
     /**
@@ -421,7 +431,7 @@
      */
     public function sort($index)
     {
-        $this->sort_order = $index->get_parameters('ORDER');
+        $this->order = $index->get_parameters('ORDER');
 
         if (empty($this->raw_data)) {
             return;
@@ -440,9 +450,9 @@
         // in memory at a time
 
         // Assign roots
-        while ($start < $datalen &&
-	       (($pos = @strpos($this->raw_data, self::SEPARATOR_ELEMENT,
-				$start)) != false || ($pos = $datalen))
+        while (
+            ($start < $datalen && ($pos = strpos($this->raw_data, self::SEPARATOR_ELEMENT, $start)))
+            || ($start < $datalen && ($pos = $datalen))
         ) {
             $len   = $pos - $start;
             $elem  = substr($this->raw_data, $start, $len);
@@ -468,9 +478,9 @@
         $result = array_fill_keys($result, null);
         $start = 0;
 
-        while ($start < $datalen &&
-	       (($pos = @strpos($this->raw_data, self::SEPARATOR_ELEMENT,
-			       $start)) != false || ($pos = $datalen))
+        while (
+            ($start < $datalen && ($pos = strpos($this->raw_data, self::SEPARATOR_ELEMENT, $start)))
+            || ($start < $datalen && ($pos = $datalen))
         ) {
             $len   = $pos - $start;
             $elem  = substr($this->raw_data, $start, $len);
@@ -496,9 +506,9 @@
         $result  = array();
         $start   = 0;
 
-        while ($start < $datalen && 
-               (($pos = @strpos($this->raw_data, self::SEPARATOR_ELEMENT, $start)) != false
-            ||  ($pos = $datalen))
+        while ($start < $datalen
+            && (($pos = strpos($this->raw_data, self::SEPARATOR_ELEMENT, $start)) !== false
+                || ($pos = $datalen))
         ) {
             $len   = $pos - $start;
             $elem  = substr($this->raw_data, $start, $len);
@@ -569,16 +579,16 @@
     protected function parse_thread($str, $begin = 0, $end = 0, $depth = 0)
     {
         // Don't be tempted to change $str to pass by reference to speed this up - it will slow it down by about
-        // 7 times instead :-) See comments on http://uk2.php.net/references and this article:
-        // http://derickrethans.nl/files/phparch-php-variables-article.pdf
+        // 7 times instead :-) See comments on https://www.php.net/references and this article:
+        // https://derickrethans.nl/files/phparch-php-variables-article.pdf
         $node = '';
         if (!$end) {
             $end = strlen($str);
         }
 
-        // Let's try to store data in max. compacted stracture as a string,
+        // Let's try to store data in max. compacted structure as a string,
         // arrays handling is much more expensive
-        // For the following structure: THREAD (2)(3 6 (4 23)(44 7 96))
+        // For the following structure: THREAD (2)(3 6 (4 23)(44 7 96))((11)(12))
         // -- 2
         // -- 3
         //     \-- 6
@@ -588,8 +598,11 @@
         //         \-- 44
         //               \-- 7
         //                    \-- 96
+        // -- 11
+        //      \-- 12
         //
-        // The output will be: 2,3^1:6^2:4^3:23^2:44^3:7^4:96
+        // The output will be: 2 3~1:6~2:4~3:23~2:44~3:7~4:96 11~1:12
+        // Note: The "11" thread has no root, we use the first message as root
 
         if ($str[$begin] != '(') {
             // find next bracket
@@ -641,7 +654,26 @@
                     }
                 }
 
-                $thread = $this->parse_thread($str, $start + 1, $off - 1, $depth);
+                // Handle threads with missing parent by using first message as root
+                if (substr_compare($str, '((', $start, 2) === 0) {
+                    // Extract the current thread, e.g. "((1)(2))"
+                    $thread = substr($str, $start, $off - $start);
+                    // Length of the first token, e.g. "(1)"
+                    $len = strspn($thread, '(0123456789', 1) + 1;
+                    // Extract the token and modify it to look like a thread root
+                    $token = substr($thread, 1, $len);
+                    // Warning: The order is important
+                    $token = str_replace('(', '', $token);
+                    $token = str_replace(' ', ' (', $token);
+                    $token = str_replace(')', ' ', $token);
+                    $thread  = substr_replace($thread, $token, 1, $len);
+                    // Parse the thread
+                    $thread = $this->parse_thread($thread, 0, 0, $depth);
+                }
+                else {
+                    $thread = $this->parse_thread($str, $start + 1, $off - 1, $depth);
+                }
+
                 if ($thread) {
                     if (!$depth) {
                         if ($node) {