WordPress.org

Make WordPress Core

Ticket #7760: replace.patch

File replace.patch, 167.8 KB (added by Simek, 12 years ago)
  • atomlib.php

     
    2121         * @var array
    2222         * @access public
    2323         */
    24     var $links = array();
    25     /**
    26      * Stores Categories
    27      * @var array
    28      * @access public
    29      */
    30     var $categories = array();
     24        var $links = array();
    3125        /**
     26         * Stores Categories
     27         * @var array
     28         * @access public
     29         */
     30        var $categories = array();
     31        /**
    3232         * Stores Entries
    3333         *
    3434         * @var array
    3535         * @access public
    3636         */
    37     var $entries = array();
     37        var $entries = array();
    3838}
    3939
    4040/**
     
    4848         * @var array
    4949         * @access public
    5050         */
    51     var $links = array();
    52     /**
    53     * Stores Categories
    54     * @var array
     51        var $links = array();
     52        /**
     53        * Stores Categories
     54        * @var array
    5555         * @access public
    56     */
    57     var $categories = array();
     56        */
     57        var $categories = array();
    5858}
    5959
    6060/**
     
    6464 */
    6565class AtomParser {
    6666
    67     var $NS = 'http://www.w3.org/2005/Atom';
    68     var $ATOM_CONTENT_ELEMENTS = array('content','summary','title','subtitle','rights');
    69     var $ATOM_SIMPLE_ELEMENTS = array('id','updated','published','draft');
     67        var $NS = 'http://www.w3.org/2005/Atom';
     68        var $ATOM_CONTENT_ELEMENTS = array('content','summary','title','subtitle','rights');
     69        var $ATOM_SIMPLE_ELEMENTS = array('id','updated','published','draft');
    7070
    71     var $debug = false;
     71        var $debug = false;
    7272
    73     var $depth = 0;
    74     var $indent = 2;
    75     var $in_content;
    76     var $ns_contexts = array();
    77     var $ns_decls = array();
    78     var $content_ns_decls = array();
    79     var $content_ns_contexts = array();
    80     var $is_xhtml = false;
    81     var $is_html = false;
    82     var $is_text = true;
    83     var $skipped_div = false;
     73        var $depth = 0;
     74        var $indent = 2;
     75        var $in_content;
     76        var $ns_contexts = array();
     77        var $ns_decls = array();
     78        var $content_ns_decls = array();
     79        var $content_ns_contexts = array();
     80        var $is_xhtml = false;
     81        var $is_html = false;
     82        var $is_text = true;
     83        var $skipped_div = false;
    8484
    85     var $FILE = "php://input";
     85        var $FILE = "php://input";
    8686
    87     var $feed;
    88     var $current;
     87        var $feed;
     88        var $current;
    8989
    90     function AtomParser() {
     90        function AtomParser() {
    9191
    92         $this->feed = new AtomFeed();
    93         $this->current = null;
    94         $this->map_attrs_func = create_function('$k,$v', 'return "$k=\"$v\"";');
    95         $this->map_xmlns_func = create_function('$p,$n', '$xd = "xmlns"; if(strlen($n[0])>0) $xd .= ":{$n[0]}"; return "{$xd}=\"{$n[1]}\"";');
    96     }
     92                $this->feed = new AtomFeed();
     93                $this->current = null;
     94                $this->map_attrs_func = create_function('$k,$v', 'return "$k=\"$v\"";');
     95                $this->map_xmlns_func = create_function('$p,$n', '$xd = "xmlns"; if(strlen($n[0])>0) $xd .= ":{$n[0]}"; return "{$xd}=\"{$n[1]}\"";');
     96        }
    9797
    98     function _p($msg) {
    99         if($this->debug) {
    100             print str_repeat(" ", $this->depth * $this->indent) . $msg ."\n";
    101         }
    102     }
     98        function _p($msg) {
     99                if($this->debug) {
     100                        print str_repeat(" ", $this->depth * $this->indent) . $msg ."\n";
     101                }
     102        }
    103103
    104     function error_handler($log_level, $log_text, $error_file, $error_line) {
    105         $this->error = $log_text;
    106     }
     104        function error_handler($log_level, $log_text, $error_file, $error_line) {
     105                $this->error = $log_text;
     106        }
    107107
    108     function parse() {
     108        function parse() {
    109109
    110         set_error_handler(array(&$this, 'error_handler'));
     110                set_error_handler(array(&$this, 'error_handler'));
    111111
    112         array_unshift($this->ns_contexts, array());
     112                array_unshift($this->ns_contexts, array());
    113113
    114         $parser = xml_parser_create_ns();
    115         xml_set_object($parser, $this);
    116         xml_set_element_handler($parser, "start_element", "end_element");
    117         xml_parser_set_option($parser,XML_OPTION_CASE_FOLDING,0);
    118         xml_parser_set_option($parser,XML_OPTION_SKIP_WHITE,0);
    119         xml_set_character_data_handler($parser, "cdata");
    120         xml_set_default_handler($parser, "_default");
    121         xml_set_start_namespace_decl_handler($parser, "start_ns");
    122         xml_set_end_namespace_decl_handler($parser, "end_ns");
     114                $parser = xml_parser_create_ns();
     115                xml_set_object($parser, $this);
     116                xml_set_element_handler($parser, "start_element", "end_element");
     117                xml_parser_set_option($parser,XML_OPTION_CASE_FOLDING,0);
     118                xml_parser_set_option($parser,XML_OPTION_SKIP_WHITE,0);
     119                xml_set_character_data_handler($parser, "cdata");
     120                xml_set_default_handler($parser, "_default");
     121                xml_set_start_namespace_decl_handler($parser, "start_ns");
     122                xml_set_end_namespace_decl_handler($parser, "end_ns");
    123123
    124         $this->content = '';
     124                $this->content = '';
    125125
    126         $ret = true;
     126                $ret = true;
    127127
    128         $fp = fopen($this->FILE, "r");
    129         while ($data = fread($fp, 4096)) {
    130             if($this->debug) $this->content .= $data;
     128                $fp = fopen($this->FILE, "r");
     129                while ($data = fread($fp, 4096)) {
     130                        if($this->debug) $this->content .= $data;
    131131
    132             if(!xml_parse($parser, $data, feof($fp))) {
    133                 trigger_error(sprintf(__('XML error: %s at line %d')."\n",
    134                     xml_error_string(xml_get_error_code($xml_parser)),
    135                     xml_get_current_line_number($xml_parser)));
    136                 $ret = false;
    137                 break;
    138             }
    139         }
    140         fclose($fp);
     132                        if(!xml_parse($parser, $data, feof($fp))) {
     133                                trigger_error(sprintf(__('XML error: %s at line %d')."\n",
     134                                        xml_error_string(xml_get_error_code($xml_parser)),
     135                                        xml_get_current_line_number($xml_parser)));
     136                                $ret = false;
     137                                break;
     138                        }
     139                }
     140                fclose($fp);
    141141
    142         xml_parser_free($parser);
     142                xml_parser_free($parser);
    143143
    144         restore_error_handler();
     144                restore_error_handler();
    145145
    146         return $ret;
    147     }
     146                return $ret;
     147        }
    148148
    149     function start_element($parser, $name, $attrs) {
     149        function start_element($parser, $name, $attrs) {
    150150
    151         $tag = array_pop(split(":", $name));
     151                $tag = array_pop(split(":", $name));
    152152
    153         switch($name) {
    154             case $this->NS . ':feed':
    155                 $this->current = $this->feed;
    156                 break;
    157             case $this->NS . ':entry':
    158                 $this->current = new AtomEntry();
    159                 break;
    160         };
     153                switch($name) {
     154                        case $this->NS . ':feed':
     155                                $this->current = $this->feed;
     156                                break;
     157                        case $this->NS . ':entry':
     158                                $this->current = new AtomEntry();
     159                                break;
     160                };
    161161
    162         $this->_p("start_element('$name')");
    163         #$this->_p(print_r($this->ns_contexts,true));
    164         #$this->_p('current(' . $this->current . ')');
     162                $this->_p("start_element('$name')");
     163                #$this->_p(print_r($this->ns_contexts,true));
     164                #$this->_p('current(' . $this->current . ')');
    165165
    166         array_unshift($this->ns_contexts, $this->ns_decls);
     166                array_unshift($this->ns_contexts, $this->ns_decls);
    167167
    168         $this->depth++;
     168                $this->depth++;
    169169
    170         if(!empty($this->in_content)) {
     170                if(!empty($this->in_content)) {
    171171
    172             $this->content_ns_decls = array();
     172                        $this->content_ns_decls = array();
    173173
    174             if($this->is_html || $this->is_text)
    175                 trigger_error("Invalid content in element found. Content must not be of type text or html if it contains markup.");
     174                        if($this->is_html || $this->is_text)
     175                                trigger_error("Invalid content in element found. Content must not be of type text or html if it contains markup.");
    176176
    177             $attrs_prefix = array();
     177                        $attrs_prefix = array();
    178178
    179             // resolve prefixes for attributes
    180             foreach($attrs as $key => $value) {
    181                 $with_prefix = $this->ns_to_prefix($key, true);
    182                 $attrs_prefix[$with_prefix[1]] = $this->xml_escape($value);
    183             }
     179                        // resolve prefixes for attributes
     180                        foreach($attrs as $key => $value) {
     181                                $with_prefix = $this->ns_to_prefix($key, true);
     182                                $attrs_prefix[$with_prefix[1]] = $this->xml_escape($value);
     183                        }
    184184
    185             $attrs_str = join(' ', array_map($this->map_attrs_func, array_keys($attrs_prefix), array_values($attrs_prefix)));
    186             if(strlen($attrs_str) > 0) {
    187                 $attrs_str = " " . $attrs_str;
    188             }
     185                        $attrs_str = join(' ', array_map($this->map_attrs_func, array_keys($attrs_prefix), array_values($attrs_prefix)));
     186                        if(strlen($attrs_str) > 0) {
     187                                $attrs_str = " " . $attrs_str;
     188                        }
    189189
    190             $with_prefix = $this->ns_to_prefix($name);
     190                        $with_prefix = $this->ns_to_prefix($name);
    191191
    192             if(!$this->is_declared_content_ns($with_prefix[0])) {
    193                 array_push($this->content_ns_decls, $with_prefix[0]);
    194             }
     192                        if(!$this->is_declared_content_ns($with_prefix[0])) {
     193                                array_push($this->content_ns_decls, $with_prefix[0]);
     194                        }
    195195
    196             $xmlns_str = '';
    197             if(count($this->content_ns_decls) > 0) {
    198                 array_unshift($this->content_ns_contexts, $this->content_ns_decls);
    199                 $xmlns_str .= join(' ', array_map($this->map_xmlns_func, array_keys($this->content_ns_contexts[0]), array_values($this->content_ns_contexts[0])));
    200                 if(strlen($xmlns_str) > 0) {
    201                     $xmlns_str = " " . $xmlns_str;
    202                 }
    203             }
     196                        $xmlns_str = '';
     197                        if(count($this->content_ns_decls) > 0) {
     198                                array_unshift($this->content_ns_contexts, $this->content_ns_decls);
     199                                $xmlns_str .= join(' ', array_map($this->map_xmlns_func, array_keys($this->content_ns_contexts[0]), array_values($this->content_ns_contexts[0])));
     200                                if(strlen($xmlns_str) > 0) {
     201                                        $xmlns_str = " " . $xmlns_str;
     202                                }
     203                        }
    204204
    205             array_push($this->in_content, array($tag, $this->depth, "<". $with_prefix[1] ."{$xmlns_str}{$attrs_str}" . ">"));
     205                        array_push($this->in_content, array($tag, $this->depth, "<". $with_prefix[1] ."{$xmlns_str}{$attrs_str}" . ">"));
    206206
    207         } else if(in_array($tag, $this->ATOM_CONTENT_ELEMENTS) || in_array($tag, $this->ATOM_SIMPLE_ELEMENTS)) {
    208             $this->in_content = array();
    209             $this->is_xhtml = $attrs['type'] == 'xhtml';
    210             $this->is_html = $attrs['type'] == 'html' || $attrs['type'] == 'text/html';
    211             $this->is_text = !in_array('type',array_keys($attrs)) || $attrs['type'] == 'text';
    212             $type = $this->is_xhtml ? 'XHTML' : ($this->is_html ? 'HTML' : ($this->is_text ? 'TEXT' : $attrs['type']));
     207                } else if(in_array($tag, $this->ATOM_CONTENT_ELEMENTS) || in_array($tag, $this->ATOM_SIMPLE_ELEMENTS)) {
     208                        $this->in_content = array();
     209                        $this->is_xhtml = $attrs['type'] == 'xhtml';
     210                        $this->is_html = $attrs['type'] == 'html' || $attrs['type'] == 'text/html';
     211                        $this->is_text = !in_array('type',array_keys($attrs)) || $attrs['type'] == 'text';
     212                        $type = $this->is_xhtml ? 'XHTML' : ($this->is_html ? 'HTML' : ($this->is_text ? 'TEXT' : $attrs['type']));
    213213
    214             if(in_array('src',array_keys($attrs))) {
    215                 $this->current->$tag = $attrs;
    216             } else {
    217                 array_push($this->in_content, array($tag,$this->depth, $type));
    218             }
    219         } else if($tag == 'link') {
    220             array_push($this->current->links, $attrs);
    221         } else if($tag == 'category') {
    222             array_push($this->current->categories, $attrs);
    223         }
     214                        if(in_array('src',array_keys($attrs))) {
     215                                $this->current->$tag = $attrs;
     216                        } else {
     217                                array_push($this->in_content, array($tag,$this->depth, $type));
     218                        }
     219                } else if($tag == 'link') {
     220                        array_push($this->current->links, $attrs);
     221                } else if($tag == 'category') {
     222                        array_push($this->current->categories, $attrs);
     223                }
    224224
    225         $this->ns_decls = array();
    226     }
     225                $this->ns_decls = array();
     226        }
    227227
    228     function end_element($parser, $name) {
     228        function end_element($parser, $name) {
    229229
    230         $tag = array_pop(split(":", $name));
     230                $tag = array_pop(split(":", $name));
    231231
    232         $ccount = count($this->in_content);
     232                $ccount = count($this->in_content);
    233233
    234         # if we are *in* content, then let's proceed to serialize it
    235         if(!empty($this->in_content)) {
    236             # if we are ending the original content element
    237             # then let's finalize the content
    238             if($this->in_content[0][0] == $tag &&
    239                 $this->in_content[0][1] == $this->depth) {
    240                 $origtype = $this->in_content[0][2];
    241                 array_shift($this->in_content);
    242                 $newcontent = array();
    243                 foreach($this->in_content as $c) {
    244                     if(count($c) == 3) {
    245                         array_push($newcontent, $c[2]);
    246                     } else {
    247                         if($this->is_xhtml || $this->is_text) {
    248                             array_push($newcontent, $this->xml_escape($c));
    249                         } else {
    250                             array_push($newcontent, $c);
    251                         }
    252                     }
    253                 }
    254                 if(in_array($tag, $this->ATOM_CONTENT_ELEMENTS)) {
    255                     $this->current->$tag = array($origtype, join('',$newcontent));
    256                 } else {
    257                     $this->current->$tag = join('',$newcontent);
    258                 }
    259                 $this->in_content = array();
    260             } else if($this->in_content[$ccount-1][0] == $tag &&
    261                 $this->in_content[$ccount-1][1] == $this->depth) {
    262                 $this->in_content[$ccount-1][2] = substr($this->in_content[$ccount-1][2],0,-1) . "/>";
    263             } else {
    264                 # else, just finalize the current element's content
    265                 $endtag = $this->ns_to_prefix($name);
    266                 array_push($this->in_content, array($tag, $this->depth, "</$endtag[1]>"));
    267             }
    268         }
     234                # if we are *in* content, then let's proceed to serialize it
     235                if(!empty($this->in_content)) {
     236                        # if we are ending the original content element
     237                        # then let's finalize the content
     238                        if($this->in_content[0][0] == $tag &&
     239                                $this->in_content[0][1] == $this->depth) {
     240                                $origtype = $this->in_content[0][2];
     241                                array_shift($this->in_content);
     242                                $newcontent = array();
     243                                foreach($this->in_content as $c) {
     244                                        if(count($c) == 3) {
     245                                                array_push($newcontent, $c[2]);
     246                                        } else {
     247                                                if($this->is_xhtml || $this->is_text) {
     248                                                        array_push($newcontent, $this->xml_escape($c));
     249                                                } else {
     250                                                        array_push($newcontent, $c);
     251                                                }
     252                                        }
     253                                }
     254                                if(in_array($tag, $this->ATOM_CONTENT_ELEMENTS)) {
     255                                        $this->current->$tag = array($origtype, join('',$newcontent));
     256                                } else {
     257                                        $this->current->$tag = join('',$newcontent);
     258                                }
     259                                $this->in_content = array();
     260                        } else if($this->in_content[$ccount-1][0] == $tag &&
     261                                $this->in_content[$ccount-1][1] == $this->depth) {
     262                                $this->in_content[$ccount-1][2] = substr($this->in_content[$ccount-1][2],0,-1) . "/>";
     263                        } else {
     264                                # else, just finalize the current element's content
     265                                $endtag = $this->ns_to_prefix($name);
     266                                array_push($this->in_content, array($tag, $this->depth, "</$endtag[1]>"));
     267                        }
     268                }
    269269
    270         array_shift($this->ns_contexts);
     270                array_shift($this->ns_contexts);
    271271
    272         $this->depth--;
     272                $this->depth--;
    273273
    274         if($name == ($this->NS . ':entry')) {
    275             array_push($this->feed->entries, $this->current);
    276             $this->current = null;
    277         }
     274                if($name == ($this->NS . ':entry')) {
     275                        array_push($this->feed->entries, $this->current);
     276                        $this->current = null;
     277                }
    278278
    279         $this->_p("end_element('$name')");
    280     }
     279                $this->_p("end_element('$name')");
     280        }
    281281
    282     function start_ns($parser, $prefix, $uri) {
    283         $this->_p("starting: " . $prefix . ":" . $uri);
    284         array_push($this->ns_decls, array($prefix,$uri));
    285     }
     282        function start_ns($parser, $prefix, $uri) {
     283                $this->_p("starting: " . $prefix . ":" . $uri);
     284                array_push($this->ns_decls, array($prefix,$uri));
     285        }
    286286
    287     function end_ns($parser, $prefix) {
    288         $this->_p("ending: #" . $prefix . "#");
    289     }
     287        function end_ns($parser, $prefix) {
     288                $this->_p("ending: #" . $prefix . "#");
     289        }
    290290
    291     function cdata($parser, $data) {
    292         $this->_p("data: #" . str_replace(array("\n"), array("\\n"), trim($data)) . "#");
    293         if(!empty($this->in_content)) {
    294             array_push($this->in_content, $data);
    295         }
    296     }
     291        function cdata($parser, $data) {
     292                $this->_p("data: #" . str_replace(array("\n"), array("\\n"), trim($data)) . "#");
     293                if(!empty($this->in_content)) {
     294                        array_push($this->in_content, $data);
     295                }
     296        }
    297297
    298     function _default($parser, $data) {
    299         # when does this gets called?
    300     }
     298        function _default($parser, $data) {
     299                # when does this gets called?
     300        }
    301301
    302302
    303     function ns_to_prefix($qname, $attr=false) {
    304         # split 'http://www.w3.org/1999/xhtml:div' into ('http','//www.w3.org/1999/xhtml','div')
    305         $components = split(":", $qname);
     303        function ns_to_prefix($qname, $attr=false) {
     304                # split 'http://www.w3.org/1999/xhtml:div' into ('http','//www.w3.org/1999/xhtml','div')
     305                $components = split(":", $qname);
    306306
    307         # grab the last one (e.g 'div')
    308         $name = array_pop($components);
     307                # grab the last one (e.g 'div')
     308                $name = array_pop($components);
    309309
    310         if(!empty($components)) {
    311             # re-join back the namespace component
    312             $ns = join(":",$components);
    313             foreach($this->ns_contexts as $context) {
    314                 foreach($context as $mapping) {
    315                     if($mapping[1] == $ns && strlen($mapping[0]) > 0) {
    316                         return array($mapping, "$mapping[0]:$name");
    317                     }
    318                 }
    319             }
    320         }
     310                if(!empty($components)) {
     311                        # re-join back the namespace component
     312                        $ns = join(":",$components);
     313                        foreach($this->ns_contexts as $context) {
     314                                foreach($context as $mapping) {
     315                                        if($mapping[1] == $ns && strlen($mapping[0]) > 0) {
     316                                                return array($mapping, "$mapping[0]:$name");
     317                                        }
     318                                }
     319                        }
     320                }
    321321
    322         if($attr) {
    323             return array(null, $name);
    324         } else {
    325             foreach($this->ns_contexts as $context) {
    326                 foreach($context as $mapping) {
    327                     if(strlen($mapping[0]) == 0) {
    328                         return array($mapping, $name);
    329                     }
    330                 }
    331             }
    332         }
    333     }
     322                if($attr) {
     323                        return array(null, $name);
     324                } else {
     325                        foreach($this->ns_contexts as $context) {
     326                                foreach($context as $mapping) {
     327                                        if(strlen($mapping[0]) == 0) {
     328                                                return array($mapping, $name);
     329                                        }
     330                                }
     331                        }
     332                }
     333        }
    334334
    335     function is_declared_content_ns($new_mapping) {
    336         foreach($this->content_ns_contexts as $context) {
    337             foreach($context as $mapping) {
    338                 if($new_mapping == $mapping) {
    339                     return true;
    340                 }
    341             }
    342         }
    343         return false;
    344     }
     335        function is_declared_content_ns($new_mapping) {
     336                foreach($this->content_ns_contexts as $context) {
     337                        foreach($context as $mapping) {
     338                                if($new_mapping == $mapping) {
     339                                        return true;
     340                                }
     341                        }
     342                }
     343                return false;
     344        }
    345345
    346     function xml_escape($string)
    347     {
    348             return str_replace(array('&','"',"'",'<','>'),
    349                 array('&amp;','&quot;','&apos;','&lt;','&gt;'),
    350                 $string );
    351     }
     346        function xml_escape($string)
     347        {
     348                        return str_replace(array('&','"',"'",'<','>'),
     349                                array('&amp;','&quot;','&apos;','&lt;','&gt;'),
     350                                $string );
     351        }
    352352}
    353353
    354354?>
  • class-IXR.php

     
    2020 * @since 1.5
    2121 */
    2222class IXR_Value {
    23     var $data;
    24     var $type;
     23        var $data;
     24        var $type;
    2525
    26     function IXR_Value ($data, $type = false) {
    27         $this->data = $data;
    28         if (!$type) {
    29             $type = $this->calculateType();
    30         }
    31         $this->type = $type;
    32         if ($type == 'struct') {
    33             /* Turn all the values in the array in to new IXR_Value objects */
    34             foreach ($this->data as $key => $value) {
    35                 $this->data[$key] = new IXR_Value($value);
    36             }
    37         }
    38         if ($type == 'array') {
    39             for ($i = 0, $j = count($this->data); $i < $j; $i++) {
    40                 $this->data[$i] = new IXR_Value($this->data[$i]);
    41             }
    42         }
    43     }
     26        function IXR_Value ($data, $type = false) {
     27                $this->data = $data;
     28                if (!$type) {
     29                        $type = $this->calculateType();
     30                }
     31                $this->type = $type;
     32                if ($type == 'struct') {
     33                        /* Turn all the values in the array in to new IXR_Value objects */
     34                        foreach ($this->data as $key => $value) {
     35                                $this->data[$key] = new IXR_Value($value);
     36                        }
     37                }
     38                if ($type == 'array') {
     39                        for ($i = 0, $j = count($this->data); $i < $j; $i++) {
     40                                $this->data[$i] = new IXR_Value($this->data[$i]);
     41                        }
     42                }
     43        }
    4444
    45     function calculateType() {
    46         if ($this->data === true || $this->data === false) {
    47             return 'boolean';
    48         }
    49         if (is_integer($this->data)) {
    50             return 'int';
    51         }
    52         if (is_double($this->data)) {
    53             return 'double';
    54         }
    55         // Deal with IXR object types base64 and date
    56         if (is_object($this->data) && is_a($this->data, 'IXR_Date')) {
    57             return 'date';
    58         }
    59         if (is_object($this->data) && is_a($this->data, 'IXR_Base64')) {
    60             return 'base64';
    61         }
    62         // If it is a normal PHP object convert it in to a struct
    63         if (is_object($this->data)) {
     45        function calculateType() {
     46                if ($this->data === true || $this->data === false) {
     47                        return 'boolean';
     48                }
     49                if (is_integer($this->data)) {
     50                        return 'int';
     51                }
     52                if (is_double($this->data)) {
     53                        return 'double';
     54                }
     55                // Deal with IXR object types base64 and date
     56                if (is_object($this->data) && is_a($this->data, 'IXR_Date')) {
     57                        return 'date';
     58                }
     59                if (is_object($this->data) && is_a($this->data, 'IXR_Base64')) {
     60                        return 'base64';
     61                }
     62                // If it is a normal PHP object convert it in to a struct
     63                if (is_object($this->data)) {
    6464
    65             $this->data = get_object_vars($this->data);
    66             return 'struct';
    67         }
    68         if (!is_array($this->data)) {
    69             return 'string';
    70         }
    71         /* We have an array - is it an array or a struct ? */
    72         if ($this->isStruct($this->data)) {
    73             return 'struct';
    74         } else {
    75             return 'array';
    76         }
    77     }
     65                        $this->data = get_object_vars($this->data);
     66                        return 'struct';
     67                }
     68                if (!is_array($this->data)) {
     69                        return 'string';
     70                }
     71                /* We have an array - is it an array or a struct ? */
     72                if ($this->isStruct($this->data)) {
     73                        return 'struct';
     74                } else {
     75                        return 'array';
     76                }
     77        }
    7878
    79     function getXml() {
    80         /* Return XML for this value */
    81         switch ($this->type) {
    82             case 'boolean':
    83                 return '<boolean>'.(($this->data) ? '1' : '0').'</boolean>';
    84                 break;
    85             case 'int':
    86                 return '<int>'.$this->data.'</int>';
    87                 break;
    88             case 'double':
    89                 return '<double>'.$this->data.'</double>';
    90                 break;
    91             case 'string':
    92                 return '<string>'.htmlspecialchars($this->data).'</string>';
    93                 break;
    94             case 'array':
    95                 $return = '<array><data>'."\n";
    96                 foreach ($this->data as $item) {
    97                     $return .= '  <value>'.$item->getXml()."</value>\n";
    98                 }
    99                 $return .= '</data></array>';
    100                 return $return;
    101                 break;
    102             case 'struct':
    103                 $return = '<struct>'."\n";
    104                 foreach ($this->data as $name => $value) {
     79        function getXml() {
     80                /* Return XML for this value */
     81                switch ($this->type) {
     82                        case 'boolean':
     83                                return '<boolean>'.(($this->data) ? '1' : '0').'</boolean>';
     84                                break;
     85                        case 'int':
     86                                return '<int>'.$this->data.'</int>';
     87                                break;
     88                        case 'double':
     89                                return '<double>'.$this->data.'</double>';
     90                                break;
     91                        case 'string':
     92                                return '<string>'.htmlspecialchars($this->data).'</string>';
     93                                break;
     94                        case 'array':
     95                                $return = '<array><data>'."\n";
     96                                foreach ($this->data as $item) {
     97                                        $return .= '  <value>'.$item->getXml()."</value>\n";
     98                                }
     99                                $return .= '</data></array>';
     100                                return $return;
     101                                break;
     102                        case 'struct':
     103                                $return = '<struct>'."\n";
     104                                foreach ($this->data as $name => $value) {
    105105                                        $name = htmlspecialchars($name);
    106                     $return .= "  <member><name>$name</name><value>";
    107                     $return .= $value->getXml()."</value></member>\n";
    108                 }
    109                 $return .= '</struct>';
    110                 return $return;
    111                 break;
    112             case 'date':
    113             case 'base64':
    114                 return $this->data->getXml();
    115                 break;
    116         }
    117         return false;
    118     }
     106                                        $return .= "  <member><name>$name</name><value>";
     107                                        $return .= $value->getXml()."</value></member>\n";
     108                                }
     109                                $return .= '</struct>';
     110                                return $return;
     111                                break;
     112                        case 'date':
     113                        case 'base64':
     114                                return $this->data->getXml();
     115                                break;
     116                }
     117                return false;
     118        }
    119119
    120     function isStruct($array) {
    121         /* Nasty function to check if an array is a struct or not */
    122         $expected = 0;
    123         foreach ($array as $key => $value) {
    124             if ((string)$key != (string)$expected) {
    125                 return true;
    126             }
    127             $expected++;
    128         }
    129         return false;
    130     }
     120        function isStruct($array) {
     121                /* Nasty function to check if an array is a struct or not */
     122                $expected = 0;
     123                foreach ($array as $key => $value) {
     124                        if ((string)$key != (string)$expected) {
     125                                return true;
     126                        }
     127                        $expected++;
     128                }
     129                return false;
     130        }
    131131}
    132132
    133133/**
     
    137137 * @since 1.5
    138138 */
    139139class IXR_Message {
    140     var $message;
    141     var $messageType;  // methodCall / methodResponse / fault
    142     var $faultCode;
    143     var $faultString;
    144     var $methodName;
    145     var $params;
    146     // Current variable stacks
    147     var $_arraystructs = array();   // The stack used to keep track of the current array/struct
    148     var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array
    149     var $_currentStructName = array();  // A stack as well
    150     var $_param;
    151     var $_value;
    152     var $_currentTag;
    153     var $_currentTagContents;
    154     // The XML parser
    155     var $_parser;
    156     function IXR_Message ($message) {
    157         $this->message = $message;
    158     }
    159     function parse() {
    160         // first remove the XML declaration
    161         $this->message = preg_replace('/<\?xml(.*)?\?'.'>/', '', $this->message);
    162         if (trim($this->message) == '') {
    163             return false;
    164         }
    165         $this->_parser = xml_parser_create();
    166         // Set XML parser to take the case of tags in to account
    167         xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
    168         // Set XML parser callback functions
    169         xml_set_object($this->_parser, $this);
    170         xml_set_element_handler($this->_parser, 'tag_open', 'tag_close');
    171         xml_set_character_data_handler($this->_parser, 'cdata');
    172         if (!xml_parse($this->_parser, $this->message)) {
    173             /* die(sprintf('XML error: %s at line %d',
    174                 xml_error_string(xml_get_error_code($this->_parser)),
    175                 xml_get_current_line_number($this->_parser))); */
    176             return false;
    177         }
    178         xml_parser_free($this->_parser);
    179         // Grab the error messages, if any
    180         if ($this->messageType == 'fault') {
    181             $this->faultCode = $this->params[0]['faultCode'];
    182             $this->faultString = $this->params[0]['faultString'];
    183         }
    184         return true;
    185     }
    186     function tag_open($parser, $tag, $attr) {
    187         $this->_currentTagContents = '';
    188         $this->currentTag = $tag;
    189         switch($tag) {
    190             case 'methodCall':
    191             case 'methodResponse':
    192             case 'fault':
    193                 $this->messageType = $tag;
    194                 break;
    195             /* Deal with stacks of arrays and structs */
    196             case 'data':    // data is to all intents and puposes more interesting than array
    197                 $this->_arraystructstypes[] = 'array';
    198                 $this->_arraystructs[] = array();
    199                 break;
    200             case 'struct':
    201                 $this->_arraystructstypes[] = 'struct';
    202                 $this->_arraystructs[] = array();
    203                 break;
    204         }
    205     }
    206     function cdata($parser, $cdata) {
    207         $this->_currentTagContents .= $cdata;
    208     }
    209     function tag_close($parser, $tag) {
    210         $valueFlag = false;
    211         switch($tag) {
    212             case 'int':
    213             case 'i4':
    214                 $value = (int) trim($this->_currentTagContents);
    215                 $valueFlag = true;
    216                 break;
    217             case 'double':
    218                 $value = (double) trim($this->_currentTagContents);
    219                 $valueFlag = true;
    220                 break;
    221             case 'string':
    222                 $value = $this->_currentTagContents;
    223                 $valueFlag = true;
    224                 break;
    225             case 'dateTime.iso8601':
    226                 $value = new IXR_Date(trim($this->_currentTagContents));
    227                 // $value = $iso->getTimestamp();
    228                 $valueFlag = true;
    229                 break;
    230             case 'value':
    231                 // "If no type is indicated, the type is string."
    232                 if (trim($this->_currentTagContents) != '') {
    233                     $value = (string)$this->_currentTagContents;
    234                     $valueFlag = true;
    235                 }
    236                 break;
    237             case 'boolean':
    238                 $value = (boolean) trim($this->_currentTagContents);
    239                 $valueFlag = true;
    240                 break;
    241             case 'base64':
    242                 $value = base64_decode( trim( $this->_currentTagContents ) );
    243                 $valueFlag = true;
    244                 break;
    245             /* Deal with stacks of arrays and structs */
    246             case 'data':
    247             case 'struct':
    248                 $value = array_pop($this->_arraystructs);
    249                 array_pop($this->_arraystructstypes);
    250                 $valueFlag = true;
    251                 break;
    252             case 'member':
    253                 array_pop($this->_currentStructName);
    254                 break;
    255             case 'name':
    256                 $this->_currentStructName[] = trim($this->_currentTagContents);
    257                 break;
    258             case 'methodName':
    259                 $this->methodName = trim($this->_currentTagContents);
    260                 break;
    261         }
    262         if ($valueFlag) {
    263             if (count($this->_arraystructs) > 0) {
    264                 // Add value to struct or array
    265                 if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {
    266                     // Add to struct
    267                     $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;
    268                 } else {
    269                     // Add to array
    270                     $this->_arraystructs[count($this->_arraystructs)-1][] = $value;
    271                 }
    272             } else {
    273                 // Just add as a paramater
    274                 $this->params[] = $value;
    275             }
    276         }
    277         $this->_currentTagContents = '';
    278     }
     140        var $message;
     141        var $messageType;  // methodCall / methodResponse / fault
     142        var $faultCode;
     143        var $faultString;
     144        var $methodName;
     145        var $params;
     146        // Current variable stacks
     147        var $_arraystructs = array();   // The stack used to keep track of the current array/struct
     148        var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array
     149        var $_currentStructName = array();  // A stack as well
     150        var $_param;
     151        var $_value;
     152        var $_currentTag;
     153        var $_currentTagContents;
     154        // The XML parser
     155        var $_parser;
     156        function IXR_Message ($message) {
     157                $this->message = $message;
     158        }
     159        function parse() {
     160                // first remove the XML declaration
     161                $this->message = preg_replace('/<\?xml(.*)?\?'.'>/', '', $this->message);
     162                if (trim($this->message) == '') {
     163                        return false;
     164                }
     165                $this->_parser = xml_parser_create();
     166                // Set XML parser to take the case of tags in to account
     167                xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
     168                // Set XML parser callback functions
     169                xml_set_object($this->_parser, $this);
     170                xml_set_element_handler($this->_parser, 'tag_open', 'tag_close');
     171                xml_set_character_data_handler($this->_parser, 'cdata');
     172                if (!xml_parse($this->_parser, $this->message)) {
     173                        /* die(sprintf('XML error: %s at line %d',
     174                                xml_error_string(xml_get_error_code($this->_parser)),
     175                                xml_get_current_line_number($this->_parser))); */
     176                        return false;
     177                }
     178                xml_parser_free($this->_parser);
     179                // Grab the error messages, if any
     180                if ($this->messageType == 'fault') {
     181                        $this->faultCode = $this->params[0]['faultCode'];
     182                        $this->faultString = $this->params[0]['faultString'];
     183                }
     184                return true;
     185        }
     186        function tag_open($parser, $tag, $attr) {
     187                $this->_currentTagContents = '';
     188                $this->currentTag = $tag;
     189                switch($tag) {
     190                        case 'methodCall':
     191                        case 'methodResponse':
     192                        case 'fault':
     193                                $this->messageType = $tag;
     194                                break;
     195                        /* Deal with stacks of arrays and structs */
     196                        case 'data':    // data is to all intents and puposes more interesting than array
     197                                $this->_arraystructstypes[] = 'array';
     198                                $this->_arraystructs[] = array();
     199                                break;
     200                        case 'struct':
     201                                $this->_arraystructstypes[] = 'struct';
     202                                $this->_arraystructs[] = array();
     203                                break;
     204                }
     205        }
     206        function cdata($parser, $cdata) {
     207                $this->_currentTagContents .= $cdata;
     208        }
     209        function tag_close($parser, $tag) {
     210                $valueFlag = false;
     211                switch($tag) {
     212                        case 'int':
     213                        case 'i4':
     214                                $value = (int) trim($this->_currentTagContents);
     215                                $valueFlag = true;
     216                                break;
     217                        case 'double':
     218                                $value = (double) trim($this->_currentTagContents);
     219                                $valueFlag = true;
     220                                break;
     221                        case 'string':
     222                                $value = $this->_currentTagContents;
     223                                $valueFlag = true;
     224                                break;
     225                        case 'dateTime.iso8601':
     226                                $value = new IXR_Date(trim($this->_currentTagContents));
     227                                // $value = $iso->getTimestamp();
     228                                $valueFlag = true;
     229                                break;
     230                        case 'value':
     231                                // "If no type is indicated, the type is string."
     232                                if (trim($this->_currentTagContents) != '') {
     233                                        $value = (string)$this->_currentTagContents;
     234                                        $valueFlag = true;
     235                                }
     236                                break;
     237                        case 'boolean':
     238                                $value = (boolean) trim($this->_currentTagContents);
     239                                $valueFlag = true;
     240                                break;
     241                        case 'base64':
     242                                $value = base64_decode( trim( $this->_currentTagContents ) );
     243                                $valueFlag = true;
     244                                break;
     245                        /* Deal with stacks of arrays and structs */
     246                        case 'data':
     247                        case 'struct':
     248                                $value = array_pop($this->_arraystructs);
     249                                array_pop($this->_arraystructstypes);
     250                                $valueFlag = true;
     251                                break;
     252                        case 'member':
     253                                array_pop($this->_currentStructName);
     254                                break;
     255                        case 'name':
     256                                $this->_currentStructName[] = trim($this->_currentTagContents);
     257                                break;
     258                        case 'methodName':
     259                                $this->methodName = trim($this->_currentTagContents);
     260                                break;
     261                }
     262                if ($valueFlag) {
     263                        if (count($this->_arraystructs) > 0) {
     264                                // Add value to struct or array
     265                                if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {
     266                                        // Add to struct
     267                                        $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;
     268                                } else {
     269                                        // Add to array
     270                                        $this->_arraystructs[count($this->_arraystructs)-1][] = $value;
     271                                }
     272                        } else {
     273                                // Just add as a paramater
     274                                $this->params[] = $value;
     275                        }
     276                }
     277                $this->_currentTagContents = '';
     278        }
    279279}
    280280
    281281/**
     
    285285 * @since 1.5
    286286 */
    287287class IXR_Server {
    288     var $data;
    289     var $callbacks = array();
    290     var $message;
    291     var $capabilities;
    292     function IXR_Server($callbacks = false, $data = false) {
    293         $this->setCapabilities();
    294         if ($callbacks) {
    295             $this->callbacks = $callbacks;
    296         }
    297         $this->setCallbacks();
    298         $this->serve($data);
    299     }
    300     function serve($data = false) {
    301         if (!$data) {
    302             global $HTTP_RAW_POST_DATA;
    303             if (!$HTTP_RAW_POST_DATA) {
    304                die('XML-RPC server accepts POST requests only.');
    305             }
    306             $data = $HTTP_RAW_POST_DATA;
    307         }
    308         $this->message = new IXR_Message($data);
    309         if (!$this->message->parse()) {
    310             $this->error(-32700, 'parse error. not well formed');
    311         }
    312         if ($this->message->messageType != 'methodCall') {
    313             $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall');
    314         }
    315         $result = $this->call($this->message->methodName, $this->message->params);
    316         // Is the result an error?
    317         if (is_a($result, 'IXR_Error')) {
    318             $this->error($result);
    319         }
    320         // Encode the result
    321         $r = new IXR_Value($result);
    322         $resultxml = $r->getXml();
    323         // Create the XML
    324         $xml = <<<EOD
     288        var $data;
     289        var $callbacks = array();
     290        var $message;
     291        var $capabilities;
     292        function IXR_Server($callbacks = false, $data = false) {
     293                $this->setCapabilities();
     294                if ($callbacks) {
     295                        $this->callbacks = $callbacks;
     296                }
     297                $this->setCallbacks();
     298                $this->serve($data);
     299        }
     300        function serve($data = false) {
     301                if (!$data) {
     302                        global $HTTP_RAW_POST_DATA;
     303                        if (!$HTTP_RAW_POST_DATA) {
     304                           die('XML-RPC server accepts POST requests only.');
     305                        }
     306                        $data = $HTTP_RAW_POST_DATA;
     307                }
     308                $this->message = new IXR_Message($data);
     309                if (!$this->message->parse()) {
     310                        $this->error(-32700, 'parse error. not well formed');
     311                }
     312                if ($this->message->messageType != 'methodCall') {
     313                        $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall');
     314                }
     315                $result = $this->call($this->message->methodName, $this->message->params);
     316                // Is the result an error?
     317                if (is_a($result, 'IXR_Error')) {
     318                        $this->error($result);
     319                }
     320                // Encode the result
     321                $r = new IXR_Value($result);
     322                $resultxml = $r->getXml();
     323                // Create the XML
     324                $xml = <<<EOD
    325325<methodResponse>
    326326  <params>
    327     <param>
    328       <value>
    329         $resultxml
    330       </value>
    331     </param>
     327        <param>
     328          <value>
     329                $resultxml
     330          </value>
     331        </param>
    332332  </params>
    333333</methodResponse>
    334334
    335335EOD;
    336         // Send it
    337         $this->output($xml);
    338     }
    339     function call($methodname, $args) {
    340         if (!$this->hasMethod($methodname)) {
    341             return new IXR_Error(-32601, 'server error. requested method '.
    342                 $methodname.' does not exist.');
    343         }
    344         $method = $this->callbacks[$methodname];
    345         // Perform the callback and send the response
    346         if (count($args) == 1) {
    347             // If only one paramater just send that instead of the whole array
    348             $args = $args[0];
    349         }
    350         // Are we dealing with a function or a method?
    351         if (substr($method, 0, 5) == 'this:') {
    352             // It's a class method - check it exists
    353             $method = substr($method, 5);
    354             if (!method_exists($this, $method)) {
    355                 return new IXR_Error(-32601, 'server error. requested class method "'.
    356                     $method.'" does not exist.');
    357             }
    358             // Call the method
    359             $result = $this->$method($args);
    360         } else {
    361             // It's a function - does it exist?
    362             if (is_array($method)) {
    363                 if (!method_exists($method[0], $method[1])) {
    364                     return new IXR_Error(-32601, 'server error. requested object method "'.
    365                         $method[1].'" does not exist.');
    366                 }
    367             } else if (!function_exists($method)) {
    368                 return new IXR_Error(-32601, 'server error. requested function "'.
    369                     $method.'" does not exist.');
    370             }
    371             // Call the function
    372             $result = call_user_func($method, $args);
    373         }
    374         return $result;
    375     }
     336                // Send it
     337                $this->output($xml);
     338        }
     339        function call($methodname, $args) {
     340                if (!$this->hasMethod($methodname)) {
     341                        return new IXR_Error(-32601, 'server error. requested method '.
     342                                $methodname.' does not exist.');
     343                }
     344                $method = $this->callbacks[$methodname];
     345                // Perform the callback and send the response
     346                if (count($args) == 1) {
     347                        // If only one paramater just send that instead of the whole array
     348                        $args = $args[0];
     349                }
     350                // Are we dealing with a function or a method?
     351                if (substr($method, 0, 5) == 'this:') {
     352                        // It's a class method - check it exists
     353                        $method = substr($method, 5);
     354                        if (!method_exists($this, $method)) {
     355                                return new IXR_Error(-32601, 'server error. requested class method "'.
     356                                        $method.'" does not exist.');
     357                        }
     358                        // Call the method
     359                        $result = $this->$method($args);
     360                } else {
     361                        // It's a function - does it exist?
     362                        if (is_array($method)) {
     363                                if (!method_exists($method[0], $method[1])) {
     364                                        return new IXR_Error(-32601, 'server error. requested object method "'.
     365                                                $method[1].'" does not exist.');
     366                                }
     367                        } else if (!function_exists($method)) {
     368                                return new IXR_Error(-32601, 'server error. requested function "'.
     369                                        $method.'" does not exist.');
     370                        }
     371                        // Call the function
     372                        $result = call_user_func($method, $args);
     373                }
     374                return $result;
     375        }
    376376
    377     function error($error, $message = false) {
    378         // Accepts either an error object or an error code and message
    379         if ($message && !is_object($error)) {
    380             $error = new IXR_Error($error, $message);
    381         }
    382         $this->output($error->getXml());
    383     }
    384     function output($xml) {
    385         $xml = '<?xml version="1.0"?>'."\n".$xml;
    386         $length = strlen($xml);
    387         header('Connection: close');
    388         header('Content-Length: '.$length);
    389         header('Content-Type: text/xml');
    390         header('Date: '.date('r'));
    391         echo $xml;
    392         exit;
    393     }
    394     function hasMethod($method) {
    395         return in_array($method, array_keys($this->callbacks));
    396     }
    397     function setCapabilities() {
    398         // Initialises capabilities array
    399         $this->capabilities = array(
    400             'xmlrpc' => array(
    401                 'specUrl' => 'http://www.xmlrpc.com/spec',
    402                 'specVersion' => 1
    403             ),
    404             'faults_interop' => array(
    405                 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',
    406                 'specVersion' => 20010516
    407             ),
    408             'system.multicall' => array(
    409                 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',
    410                 'specVersion' => 1
    411             ),
    412         );
    413     }
    414     function getCapabilities($args) {
    415         return $this->capabilities;
    416     }
    417     function setCallbacks() {
    418         $this->callbacks['system.getCapabilities'] = 'this:getCapabilities';
    419         $this->callbacks['system.listMethods'] = 'this:listMethods';
    420         $this->callbacks['system.multicall'] = 'this:multiCall';
    421     }
    422     function listMethods($args) {
    423         // Returns a list of methods - uses array_reverse to ensure user defined
    424         // methods are listed before server defined methods
    425         return array_reverse(array_keys($this->callbacks));
    426     }
    427     function multiCall($methodcalls) {
    428         // See http://www.xmlrpc.com/discuss/msgReader$1208
    429         $return = array();
    430         foreach ($methodcalls as $call) {
    431             $method = $call['methodName'];
    432             $params = $call['params'];
    433             if ($method == 'system.multicall') {
    434                 $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden');
    435             } else {
    436                 $result = $this->call($method, $params);
    437             }
    438             if (is_a($result, 'IXR_Error')) {
    439                 $return[] = array(
    440                     'faultCode' => $result->code,
    441                     'faultString' => $result->message
    442                 );
    443             } else {
    444                 $return[] = array($result);
    445             }
    446         }
    447         return $return;
    448     }
     377        function error($error, $message = false) {
     378                // Accepts either an error object or an error code and message
     379                if ($message && !is_object($error)) {
     380                        $error = new IXR_Error($error, $message);
     381                }
     382                $this->output($error->getXml());
     383        }
     384        function output($xml) {
     385                $xml = '<?xml version="1.0"?>'."\n".$xml;
     386                $length = strlen($xml);
     387                header('Connection: close');
     388                header('Content-Length: '.$length);
     389                header('Content-Type: text/xml');
     390                header('Date: '.date('r'));
     391                echo $xml;
     392                exit;
     393        }
     394        function hasMethod($method) {
     395                return in_array($method, array_keys($this->callbacks));
     396        }
     397        function setCapabilities() {
     398                // Initialises capabilities array
     399                $this->capabilities = array(
     400                        'xmlrpc' => array(
     401                                'specUrl' => 'http://www.xmlrpc.com/spec',
     402                                'specVersion' => 1
     403                        ),
     404                        'faults_interop' => array(
     405                                'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',
     406                                'specVersion' => 20010516
     407                        ),
     408                        'system.multicall' => array(
     409                                'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',
     410                                'specVersion' => 1
     411                        ),
     412                );
     413        }
     414        function getCapabilities($args) {
     415                return $this->capabilities;
     416        }
     417        function setCallbacks() {
     418                $this->callbacks['system.getCapabilities'] = 'this:getCapabilities';
     419                $this->callbacks['system.listMethods'] = 'this:listMethods';
     420                $this->callbacks['system.multicall'] = 'this:multiCall';
     421        }
     422        function listMethods($args) {
     423                // Returns a list of methods - uses array_reverse to ensure user defined
     424                // methods are listed before server defined methods
     425                return array_reverse(array_keys($this->callbacks));
     426        }
     427        function multiCall($methodcalls) {
     428                // See http://www.xmlrpc.com/discuss/msgReader$1208
     429                $return = array();
     430                foreach ($methodcalls as $call) {
     431                        $method = $call['methodName'];
     432                        $params = $call['params'];
     433                        if ($method == 'system.multicall') {
     434                                $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden');
     435                        } else {
     436                                $result = $this->call($method, $params);
     437                        }
     438                        if (is_a($result, 'IXR_Error')) {
     439                                $return[] = array(
     440                                        'faultCode' => $result->code,
     441                                        'faultString' => $result->message
     442                                );
     443                        } else {
     444                                $return[] = array($result);
     445                        }
     446                }
     447                return $return;
     448        }
    449449}
    450450
    451451/**
     
    455455 * @since 1.5
    456456 */
    457457class IXR_Request {
    458     var $method;
    459     var $args;
    460     var $xml;
    461     function IXR_Request($method, $args) {
    462         $this->method = $method;
    463         $this->args = $args;
    464         $this->xml = <<<EOD
     458        var $method;
     459        var $args;
     460        var $xml;
     461        function IXR_Request($method, $args) {
     462                $this->method = $method;
     463                $this->args = $args;
     464                $this->xml = <<<EOD
    465465<?xml version="1.0"?>
    466466<methodCall>
    467467<methodName>{$this->method}</methodName>
    468468<params>
    469469
    470470EOD;
    471         foreach ($this->args as $arg) {
    472             $this->xml .= '<param><value>';
    473             $v = new IXR_Value($arg);
    474             $this->xml .= $v->getXml();
    475             $this->xml .= "</value></param>\n";
    476         }
    477         $this->xml .= '</params></methodCall>';
    478     }
    479     function getLength() {
    480         return strlen($this->xml);
    481     }
    482     function getXml() {
    483         return $this->xml;
    484     }
     471                foreach ($this->args as $arg) {
     472                        $this->xml .= '<param><value>';
     473                        $v = new IXR_Value($arg);
     474                        $this->xml .= $v->getXml();
     475                        $this->xml .= "</value></param>\n";
     476                }
     477                $this->xml .= '</params></methodCall>';
     478        }
     479        function getLength() {
     480                return strlen($this->xml);
     481        }
     482        function getXml() {
     483                return $this->xml;
     484        }
    485485}
    486486
    487487/**
     
    491491 * @since 1.5
    492492 */
    493493class IXR_Client {
    494     var $server;
    495     var $port;
    496     var $path;
    497     var $useragent;
    498     var $response;
    499     var $message = false;
    500     var $debug = false;
    501     var $timeout;
    502     // Storage place for an error message
    503     var $error = false;
    504     function IXR_Client($server, $path = false, $port = 80, $timeout = false) {
    505         if (!$path) {
    506             // Assume we have been given a URL instead
    507             $bits = parse_url($server);
    508             $this->server = $bits['host'];
    509             $this->port = isset($bits['port']) ? $bits['port'] : 80;
    510             $this->path = isset($bits['path']) ? $bits['path'] : '/';
    511             // Make absolutely sure we have a path
    512             if (!$this->path) {
    513                 $this->path = '/';
    514             }
    515         } else {
    516             $this->server = $server;
    517             $this->path = $path;
    518             $this->port = $port;
    519         }
    520         $this->useragent = 'The Incutio XML-RPC PHP Library';
    521         $this->timeout = $timeout;
    522     }
    523     function query() {
    524         $args = func_get_args();
    525         $method = array_shift($args);
    526         $request = new IXR_Request($method, $args);
    527         $length = $request->getLength();
    528         $xml = $request->getXml();
    529         $r = "\r\n";
    530         $request  = "POST {$this->path} HTTP/1.0$r";
    531         $request .= "Host: {$this->server}$r";
    532         $request .= "Content-Type: text/xml$r";
    533         $request .= "User-Agent: {$this->useragent}$r";
    534         $request .= "Content-length: {$length}$r$r";
    535         $request .= $xml;
    536         // Now send the request
    537         if ($this->debug) {
    538             echo '<pre>'.htmlspecialchars($request)."\n</pre>\n\n";
    539         }
    540         if ($this->timeout) {
    541             $fp = @fsockopen($this->server, $this->port, $errno, $errstr, $this->timeout);
    542         } else {
    543             $fp = @fsockopen($this->server, $this->port, $errno, $errstr);
    544         }
    545         if (!$fp) {
    546             $this->error = new IXR_Error(-32300, "transport error - could not open socket: $errno $errstr");
    547             return false;
    548         }
    549         fputs($fp, $request);
    550         $contents = '';
    551         $gotFirstLine = false;
    552         $gettingHeaders = true;
    553         while (!feof($fp)) {
    554             $line = fgets($fp, 4096);
    555             if (!$gotFirstLine) {
    556                 // Check line for '200'
    557                 if (strstr($line, '200') === false) {
    558                     $this->error = new IXR_Error(-32300, 'transport error - HTTP status code was not 200');
    559                     return false;
    560                 }
    561                 $gotFirstLine = true;
    562             }
    563             if (trim($line) == '') {
    564                 $gettingHeaders = false;
    565             }
    566             if (!$gettingHeaders) {
    567                 $contents .= trim($line);
    568             }
    569         }
    570         if ($this->debug) {
    571             echo '<pre>'.htmlspecialchars($contents)."\n</pre>\n\n";
    572         }
    573         // Now parse what we've got back
    574         $this->message = new IXR_Message($contents);
    575         if (!$this->message->parse()) {
    576             // XML error
    577             $this->error = new IXR_Error(-32700, 'parse error. not well formed');
    578             return false;
    579         }
    580         // Is the message a fault?
    581         if ($this->message->messageType == 'fault') {
    582             $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString);
    583             return false;
    584         }
    585         // Message must be OK
    586         return true;
    587     }
    588     function getResponse() {
    589         // methodResponses can only have one param - return that
    590         return $this->message->params[0];
    591     }
    592     function isError() {
    593         return (is_object($this->error));
    594     }
    595     function getErrorCode() {
    596         return $this->error->code;
    597     }
    598     function getErrorMessage() {
    599         return $this->error->message;
    600     }
     494        var $server;
     495        var $port;
     496        var $path;
     497        var $useragent;
     498        var $response;
     499        var $message = false;
     500        var $debug = false;
     501        var $timeout;
     502        // Storage place for an error message
     503        var $error = false;
     504        function IXR_Client($server, $path = false, $port = 80, $timeout = false) {
     505                if (!$path) {
     506                        // Assume we have been given a URL instead
     507                        $bits = parse_url($server);
     508                        $this->server = $bits['host'];
     509                        $this->port = isset($bits['port']) ? $bits['port'] : 80;
     510                        $this->path = isset($bits['path']) ? $bits['path'] : '/';
     511                        // Make absolutely sure we have a path
     512                        if (!$this->path) {
     513                                $this->path = '/';
     514                        }
     515                } else {
     516                        $this->server = $server;
     517                        $this->path = $path;
     518                        $this->port = $port;
     519                }
     520                $this->useragent = 'The Incutio XML-RPC PHP Library';
     521                $this->timeout = $timeout;
     522        }
     523        function query() {
     524                $args = func_get_args();
     525                $method = array_shift($args);
     526                $request = new IXR_Request($method, $args);
     527                $length = $request->getLength();
     528                $xml = $request->getXml();
     529                $r = "\r\n";
     530                $request  = "POST {$this->path} HTTP/1.0$r";
     531                $request .= "Host: {$this->server}$r";
     532                $request .= "Content-Type: text/xml$r";
     533                $request .= "User-Agent: {$this->useragent}$r";
     534                $request .= "Content-length: {$length}$r$r";
     535                $request .= $xml;
     536                // Now send the request
     537                if ($this->debug) {
     538                        echo '<pre>'.htmlspecialchars($request)."\n</pre>\n\n";
     539                }
     540                if ($this->timeout) {
     541                        $fp = @fsockopen($this->server, $this->port, $errno, $errstr, $this->timeout);
     542                } else {
     543                        $fp = @fsockopen($this->server, $this->port, $errno, $errstr);
     544                }
     545                if (!$fp) {
     546                        $this->error = new IXR_Error(-32300, "transport error - could not open socket: $errno $errstr");
     547                        return false;
     548                }
     549                fputs($fp, $request);
     550                $contents = '';
     551                $gotFirstLine = false;
     552                $gettingHeaders = true;
     553                while (!feof($fp)) {
     554                        $line = fgets($fp, 4096);
     555                        if (!$gotFirstLine) {
     556                                // Check line for '200'
     557                                if (strstr($line, '200') === false) {
     558                                        $this->error = new IXR_Error(-32300, 'transport error - HTTP status code was not 200');
     559                                        return false;
     560                                }
     561                                $gotFirstLine = true;
     562                        }
     563                        if (trim($line) == '') {
     564                                $gettingHeaders = false;
     565                        }
     566                        if (!$gettingHeaders) {
     567                                $contents .= trim($line);
     568                        }
     569                }
     570                if ($this->debug) {
     571                        echo '<pre>'.htmlspecialchars($contents)."\n</pre>\n\n";
     572                }
     573                // Now parse what we've got back
     574                $this->message = new IXR_Message($contents);
     575                if (!$this->message->parse()) {
     576                        // XML error
     577                        $this->error = new IXR_Error(-32700, 'parse error. not well formed');
     578                        return false;
     579                }
     580                // Is the message a fault?
     581                if ($this->message->messageType == 'fault') {
     582                        $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString);
     583                        return false;
     584                }
     585                // Message must be OK
     586                return true;
     587        }
     588        function getResponse() {
     589                // methodResponses can only have one param - return that
     590                return $this->message->params[0];
     591        }
     592        function isError() {
     593                return (is_object($this->error));
     594        }
     595        function getErrorCode() {
     596                return $this->error->code;
     597        }
     598        function getErrorMessage() {
     599                return $this->error->message;
     600        }
    601601}
    602602
    603603/**
     
    607607 * @since 1.5
    608608 */
    609609class IXR_Error {
    610     var $code;
    611     var $message;
    612     function IXR_Error($code, $message) {
    613         $this->code = $code;
    614         // WP adds htmlspecialchars(). See #5666
    615         $this->message = htmlspecialchars($message);
    616     }
    617     function getXml() {
    618         $xml = <<<EOD
     610        var $code;
     611        var $message;
     612        function IXR_Error($code, $message) {
     613                $this->code = $code;
     614                // WP adds htmlspecialchars(). See #5666
     615                $this->message = htmlspecialchars($message);
     616        }
     617        function getXml() {
     618                $xml = <<<EOD
    619619<methodResponse>
    620620  <fault>
    621     <value>
    622       <struct>
    623         <member>
    624           <name>faultCode</name>
    625           <value><int>{$this->code}</int></value>
    626         </member>
    627         <member>
    628           <name>faultString</name>
    629           <value><string>{$this->message}</string></value>
    630         </member>
    631       </struct>
    632     </value>
     621        <value>
     622          <struct>
     623                <member>
     624                  <name>faultCode</name>
     625                  <value><int>{$this->code}</int></value>
     626                </member>
     627                <member>
     628                  <name>faultString</name>
     629                  <value><string>{$this->message}</string></value>
     630                </member>
     631          </struct>
     632        </value>
    633633  </fault>
    634634</methodResponse>
    635635
    636636EOD;
    637         return $xml;
    638     }
     637                return $xml;
     638        }
    639639}
    640640
    641641/**
     
    645645 * @since 1.5
    646646 */
    647647class IXR_Date {
    648     var $year;
    649     var $month;
    650     var $day;
    651     var $hour;
    652     var $minute;
    653     var $second;
    654     var $timezone;
    655     function IXR_Date($time) {
    656         // $time can be a PHP timestamp or an ISO one
    657         if (is_numeric($time)) {
    658             $this->parseTimestamp($time);
    659         } else {
    660             $this->parseIso($time);
    661         }
    662     }
    663     function parseTimestamp($timestamp) {
    664         $this->year = date('Y', $timestamp);
    665         $this->month = date('m', $timestamp);
    666         $this->day = date('d', $timestamp);
    667         $this->hour = date('H', $timestamp);
    668         $this->minute = date('i', $timestamp);
    669         $this->second = date('s', $timestamp);
    670         // WP adds timezone. See #2036
    671         $this->timezone = '';
    672     }
    673     function parseIso($iso) {
    674         $this->year = substr($iso, 0, 4);
    675         $this->month = substr($iso, 4, 2);
    676         $this->day = substr($iso, 6, 2);
    677         $this->hour = substr($iso, 9, 2);
    678         $this->minute = substr($iso, 12, 2);
    679         $this->second = substr($iso, 15, 2);
    680         // WP adds timezone. See #2036
    681         $this->timezone = substr($iso, 17);
    682     }
    683     function getIso() {
    684         // WP adds timezone. See #2036
    685         return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second.$this->timezone;
    686     }
    687     function getXml() {
    688         return '<dateTime.iso8601>'.$this->getIso().'</dateTime.iso8601>';
    689     }
    690     function getTimestamp() {
    691         return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year);
    692     }
     648        var $year;
     649        var $month;
     650        var $day;
     651        var $hour;
     652        var $minute;
     653        var $second;
     654        var $timezone;
     655        function IXR_Date($time) {
     656                // $time can be a PHP timestamp or an ISO one
     657                if (is_numeric($time)) {
     658                        $this->parseTimestamp($time);
     659                } else {
     660                        $this->parseIso($time);
     661                }
     662        }
     663        function parseTimestamp($timestamp) {
     664                $this->year = date('Y', $timestamp);
     665                $this->month = date('m', $timestamp);
     666                $this->day = date('d', $timestamp);
     667                $this->hour = date('H', $timestamp);
     668                $this->minute = date('i', $timestamp);
     669                $this->second = date('s', $timestamp);
     670                // WP adds timezone. See #2036
     671                $this->timezone = '';
     672        }
     673        function parseIso($iso) {
     674                $this->year = substr($iso, 0, 4);
     675                $this->month = substr($iso, 4, 2);
     676                $this->day = substr($iso, 6, 2);
     677                $this->hour = substr($iso, 9, 2);
     678                $this->minute = substr($iso, 12, 2);
     679                $this->second = substr($iso, 15, 2);
     680                // WP adds timezone. See #2036
     681                $this->timezone = substr($iso, 17);
     682        }
     683        function getIso() {
     684                // WP adds timezone. See #2036
     685                return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second.$this->timezone;
     686        }
     687        function getXml() {
     688                return '<dateTime.iso8601>'.$this->getIso().'</dateTime.iso8601>';
     689        }
     690        function getTimestamp() {
     691                return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year);
     692        }
    693693}
    694694
    695695/**
     
    699699 * @since 1.5
    700700 */
    701701class IXR_Base64 {
    702     var $data;
    703     function IXR_Base64($data) {
    704         $this->data = $data;
    705     }
    706     function getXml() {
    707         return '<base64>'.base64_encode($this->data).'</base64>';
    708     }
     702        var $data;
     703        function IXR_Base64($data) {
     704                $this->data = $data;
     705        }
     706        function getXml() {
     707                return '<base64>'.base64_encode($this->data).'</base64>';
     708        }
    709709}
    710710
    711711/**
     
    715715 * @since 1.5
    716716 */
    717717class IXR_IntrospectionServer extends IXR_Server {
    718     var $signatures;
    719     var $help;
    720     function IXR_IntrospectionServer() {
    721         $this->setCallbacks();
    722         $this->setCapabilities();
    723         $this->capabilities['introspection'] = array(
    724             'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html',
    725             'specVersion' => 1
    726         );
    727         $this->addCallback(
    728             'system.methodSignature',
    729             'this:methodSignature',
    730             array('array', 'string'),
    731             'Returns an array describing the return type and required parameters of a method'
    732         );
    733         $this->addCallback(
    734             'system.getCapabilities',
    735             'this:getCapabilities',
    736             array('struct'),
    737             'Returns a struct describing the XML-RPC specifications supported by this server'
    738         );
    739         $this->addCallback(
    740             'system.listMethods',
    741             'this:listMethods',
    742             array('array'),
    743             'Returns an array of available methods on this server'
    744         );
    745         $this->addCallback(
    746             'system.methodHelp',
    747             'this:methodHelp',
    748             array('string', 'string'),
    749             'Returns a documentation string for the specified method'
    750         );
    751     }
    752     function addCallback($method, $callback, $args, $help) {
    753         $this->callbacks[$method] = $callback;
    754         $this->signatures[$method] = $args;
    755         $this->help[$method] = $help;
    756     }
    757     function call($methodname, $args) {
    758         // Make sure it's in an array
    759         if ($args && !is_array($args)) {
    760             $args = array($args);
    761         }
    762         // Over-rides default call method, adds signature check
    763         if (!$this->hasMethod($methodname)) {
    764             return new IXR_Error(-32601, 'server error. requested method "'.$this->message->methodName.'" not specified.');
    765         }
    766         $method = $this->callbacks[$methodname];
    767         $signature = $this->signatures[$methodname];
    768         $returnType = array_shift($signature);
    769         // Check the number of arguments
    770         if (count($args) != count($signature)) {
    771             return new IXR_Error(-32602, 'server error. wrong number of method parameters');
    772         }
    773         // Check the argument types
    774         $ok = true;
    775         $argsbackup = $args;
    776         for ($i = 0, $j = count($args); $i < $j; $i++) {
    777             $arg = array_shift($args);
    778             $type = array_shift($signature);
    779             switch ($type) {
    780                 case 'int':
    781                 case 'i4':
    782                     if (is_array($arg) || !is_int($arg)) {
    783                         $ok = false;
    784                     }
    785                     break;
    786                 case 'base64':
    787                 case 'string':
    788                     if (!is_string($arg)) {
    789                         $ok = false;
    790                     }
    791                     break;
    792                 case 'boolean':
    793                     if ($arg !== false && $arg !== true) {
    794                         $ok = false;
    795                     }
    796                     break;
    797                 case 'float':
    798                 case 'double':
    799                     if (!is_float($arg)) {
    800                         $ok = false;
    801                     }
    802                     break;
    803                 case 'date':
    804                 case 'dateTime.iso8601':
    805                     if (!is_a($arg, 'IXR_Date')) {
    806                         $ok = false;
    807                     }
    808                     break;
    809             }
    810             if (!$ok) {
    811                 return new IXR_Error(-32602, 'server error. invalid method parameters');
    812             }
    813         }
    814         // It passed the test - run the "real" method call
    815         return parent::call($methodname, $argsbackup);
    816     }
    817     function methodSignature($method) {
    818         if (!$this->hasMethod($method)) {
    819             return new IXR_Error(-32601, 'server error. requested method "'.$method.'" not specified.');
    820         }
    821         // We should be returning an array of types
    822         $types = $this->signatures[$method];
    823         $return = array();
    824         foreach ($types as $type) {
    825             switch ($type) {
    826                 case 'string':
    827                     $return[] = 'string';
    828                     break;
    829                 case 'int':
    830                 case 'i4':
    831                     $return[] = 42;
    832                     break;
    833                 case 'double':
    834                     $return[] = 3.1415;
    835                     break;
    836                 case 'dateTime.iso8601':
    837                     $return[] = new IXR_Date(time());
    838                     break;
    839                 case 'boolean':
    840                     $return[] = true;
    841                     break;
    842                 case 'base64':
    843                     $return[] = new IXR_Base64('base64');
    844                     break;
    845                 case 'array':
    846                     $return[] = array('array');
    847                     break;
    848                 case 'struct':
    849                     $return[] = array('struct' => 'struct');
    850                     break;
    851             }
    852         }
    853         return $return;
    854     }
    855     function methodHelp($method) {
    856         return $this->help[$method];
    857     }
     718        var $signatures;
     719        var $help;
     720        function IXR_IntrospectionServer() {
     721                $this->setCallbacks();
     722                $this->setCapabilities();
     723                $this->capabilities['introspection'] = array(
     724                        'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html',
     725                        'specVersion' => 1
     726                );
     727                $this->addCallback(
     728                        'system.methodSignature',
     729                        'this:methodSignature',
     730                        array('array', 'string'),
     731                        'Returns an array describing the return type and required parameters of a method'
     732                );
     733                $this->addCallback(
     734                        'system.getCapabilities',
     735                        'this:getCapabilities',
     736                        array('struct'),
     737                        'Returns a struct describing the XML-RPC specifications supported by this server'
     738                );
     739                $this->addCallback(
     740                        'system.listMethods',
     741                        'this:listMethods',
     742                        array('array'),
     743                        'Returns an array of available methods on this server'
     744                );
     745                $this->addCallback(
     746                        'system.methodHelp',
     747                        'this:methodHelp',
     748                        array('string', 'string'),
     749                        'Returns a documentation string for the specified method'
     750                );
     751        }
     752        function addCallback($method, $callback, $args, $help) {
     753                $this->callbacks[$method] = $callback;
     754                $this->signatures[$method] = $args;
     755                $this->help[$method] = $help;
     756        }
     757        function call($methodname, $args) {
     758                // Make sure it's in an array
     759                if ($args && !is_array($args)) {
     760                        $args = array($args);
     761                }
     762                // Over-rides default call method, adds signature check
     763                if (!$this->hasMethod($methodname)) {
     764                        return new IXR_Error(-32601, 'server error. requested method "'.$this->message->methodName.'" not specified.');
     765                }
     766                $method = $this->callbacks[$methodname];
     767                $signature = $this->signatures[$methodname];
     768                $returnType = array_shift($signature);
     769                // Check the number of arguments
     770                if (count($args) != count($signature)) {
     771                        return new IXR_Error(-32602, 'server error. wrong number of method parameters');
     772                }
     773                // Check the argument types
     774                $ok = true;
     775                $argsbackup = $args;
     776                for ($i = 0, $j = count($args); $i < $j; $i++) {
     777                        $arg = array_shift($args);
     778                        $type = array_shift($signature);
     779                        switch ($type) {
     780                                case 'int':
     781                                case 'i4':
     782                                        if (is_array($arg) || !is_int($arg)) {
     783                                                $ok = false;
     784                                        }
     785                                        break;
     786                                case 'base64':
     787                                case 'string':
     788                                        if (!is_string($arg)) {
     789                                                $ok = false;
     790                                        }
     791                                        break;
     792                                case 'boolean':
     793                                        if ($arg !== false && $arg !== true) {
     794                                                $ok = false;
     795                                        }
     796                                        break;
     797                                case 'float':
     798                                case 'double':
     799                                        if (!is_float($arg)) {
     800                                                $ok = false;
     801                                        }
     802                                        break;
     803                                case 'date':
     804                                case 'dateTime.iso8601':
     805                                        if (!is_a($arg, 'IXR_Date')) {
     806                                                $ok = false;
     807                                        }
     808                                        break;
     809                        }
     810                        if (!$ok) {
     811                                return new IXR_Error(-32602, 'server error. invalid method parameters');
     812                        }
     813                }
     814                // It passed the test - run the "real" method call
     815                return parent::call($methodname, $argsbackup);
     816        }
     817        function methodSignature($method) {
     818                if (!$this->hasMethod($method)) {
     819                        return new IXR_Error(-32601, 'server error. requested method "'.$method.'" not specified.');
     820                }
     821                // We should be returning an array of types
     822                $types = $this->signatures[$method];
     823                $return = array();
     824                foreach ($types as $type) {
     825                        switch ($type) {
     826                                case 'string':
     827                                        $return[] = 'string';
     828                                        break;
     829                                case 'int':
     830                                case 'i4':
     831                                        $return[] = 42;
     832                                        break;
     833                                case 'double':
     834                                        $return[] = 3.1415;
     835                                        break;
     836                                case 'dateTime.iso8601':
     837                                        $return[] = new IXR_Date(time());
     838                                        break;
     839                                case 'boolean':
     840                                        $return[] = true;
     841                                        break;
     842                                case 'base64':
     843                                        $return[] = new IXR_Base64('base64');
     844                                        break;
     845                                case 'array':
     846                                        $return[] = array('array');
     847                                        break;
     848                                case 'struct':
     849                                        $return[] = array('struct' => 'struct');
     850                                        break;
     851                        }
     852                }
     853                return $return;
     854        }
     855        function methodHelp($method) {
     856                return $this->help[$method];
     857        }
    858858}
    859859
    860860/**
     
    864864 * @since 1.5
    865865 */
    866866class IXR_ClientMulticall extends IXR_Client {
    867     var $calls = array();
    868     function IXR_ClientMulticall($server, $path = false, $port = 80) {
    869         parent::IXR_Client($server, $path, $port);
    870         $this->useragent = 'The Incutio XML-RPC PHP Library (multicall client)';
    871     }
    872     function addCall() {
    873         $args = func_get_args();
    874         $methodName = array_shift($args);
    875         $struct = array(
    876             'methodName' => $methodName,
    877             'params' => $args
    878         );
    879         $this->calls[] = $struct;
    880     }
    881     function query() {
    882         // Prepare multicall, then call the parent::query() method
    883         return parent::query('system.multicall', $this->calls);
    884     }
     867        var $calls = array();
     868        function IXR_ClientMulticall($server, $path = false, $port = 80) {
     869                parent::IXR_Client($server, $path, $port);
     870                $this->useragent = 'The Incutio XML-RPC PHP Library (multicall client)';
     871        }
     872        function addCall() {
     873                $args = func_get_args();
     874                $methodName = array_shift($args);
     875                $struct = array(
     876                        'methodName' => $methodName,
     877                        'params' => $args
     878                );
     879                $this->calls[] = $struct;
     880        }
     881        function query() {
     882                // Prepare multicall, then call the parent::query() method
     883                return parent::query('system.multicall', $this->calls);
     884        }
    885885}
    886886
    887887?>
  • class-phpass.php

     
    6565                        $output = '';
    6666                        for ($i = 0; $i < $count; $i += 16) {
    6767                                $this->random_state =
    68                                     md5(microtime() . $this->random_state);
     68                                        md5(microtime() . $this->random_state);
    6969                                $output .=
    70                                     pack('H*', md5($this->random_state));
     70                                        pack('H*', md5($this->random_state));
    7171                        }
    7272                        $output = substr($output, 0, $count);
    7373                }
     
    217217                if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
    218218                        $random = $this->get_random_bytes(16);
    219219                        $hash =
    220                             crypt($password, $this->gensalt_blowfish($random));
     220                                crypt($password, $this->gensalt_blowfish($random));
    221221                        if (strlen($hash) == 60)
    222222                                return $hash;
    223223                }
     
    226226                        if (strlen($random) < 3)
    227227                                $random = $this->get_random_bytes(3);
    228228                        $hash =
    229                             crypt($password, $this->gensalt_extended($random));
     229                                crypt($password, $this->gensalt_extended($random));
    230230                        if (strlen($hash) == 20)
    231231                                return $hash;
    232232                }
     
    234234                if (strlen($random) < 6)
    235235                        $random = $this->get_random_bytes(6);
    236236                $hash =
    237                     $this->crypt_private($password,
    238                     $this->gensalt_private($random));
     237                        $this->crypt_private($password,
     238                        $this->gensalt_private($random));
    239239                if (strlen($hash) == 34)
    240240                        return $hash;
    241241
  • class-snoopy.php

     
    213213                                if(!$this->curl_path)
    214214                                        return false;
    215215                                if(function_exists("is_executable"))
    216                                     if (!is_executable($this->curl_path))
    217                                         return false;
     216                                        if (!is_executable($this->curl_path))
     217                                                return false;
    218218                                $this->host = $URI_PARTS["host"];
    219219                                if(!empty($URI_PARTS["port"]))
    220220                                        $this->port = $URI_PARTS["port"];
     
    372372                                if(!$this->curl_path)
    373373                                        return false;
    374374                                if(function_exists("is_executable"))
    375                                     if (!is_executable($this->curl_path))
    376                                         return false;
     375                                        if (!is_executable($this->curl_path))
     376                                                return false;
    377377                                $this->host = $URI_PARTS["host"];
    378378                                if(!empty($URI_PARTS["port"]))
    379379                                        $this->port = $URI_PARTS["port"];
     
    893893
    894894                        if(preg_match("|^HTTP/|",$currentHeader))
    895895                        {
    896                 if(preg_match("|^HTTP/[^\s]*\s(.*?)\s|",$currentHeader, $status))
     896                                if(preg_match("|^HTTP/[^\s]*\s(.*?)\s|",$currentHeader, $status))
    897897                                {
    898898                                        $this->status= $status[1];
    899                 }
     899                                }
    900900                                $this->response_code = $currentHeader;
    901901                        }
    902902
     
    905905
    906906                $results = '';
    907907                do {
    908                 $_data = fread($fp, $this->maxlength);
    909                 if (strlen($_data) == 0) {
    910                         break;
    911                 }
    912                 $results .= $_data;
     908                        $_data = fread($fp, $this->maxlength);
     909                        if (strlen($_data) == 0) {
     910                                break;
     911                        }
     912                        $results .= $_data;
    913913                } while(true);
    914914
    915915                if ($this->read_timeout > 0 && $this->_check_timeout($fp))
  • streams.php

     
    66 * @subpackage PHP-gettext
    77 *
    88 * @internal
    9   Copyright (c) 2003, 2005 Danilo Segan <danilo@kvota.net>.
     9        Copyright (c) 2003, 2005 Danilo Segan <danilo@kvota.net>.
    1010
    11   This file is part of PHP-gettext.
     11        This file is part of PHP-gettext.
    1212
    13   PHP-gettext is free software; you can redistribute it and/or modify
    14   it under the terms of the GNU General Public License as published by
    15   the Free Software Foundation; either version 2 of the License, or
    16   (at your option) any later version.
     13        PHP-gettext is free software; you can redistribute it and/or modify
     14        it under the terms of the GNU General Public License as published by
     15        the Free Software Foundation; either version 2 of the License, or
     16        (at your option) any later version.
    1717
    18   PHP-gettext is distributed in the hope that it will be useful,
    19   but WITHOUT ANY WARRANTY; without even the implied warranty of
    20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    21   GNU General Public License for more details.
     18        PHP-gettext is distributed in the hope that it will be useful,
     19        but WITHOUT ANY WARRANTY; without even the implied warranty of
     20         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
     21        GNU General Public License for more details.
    2222
    23   You should have received a copy of the GNU General Public License
    24   along with PHP-gettext; if not, write to the Free Software
    25    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     23        You should have received a copy of the GNU General Public License
     24        along with PHP-gettext; if not, write to the Free Software
     25         Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA       02111-1307      USA
    2626
    2727 */
    2828
     
    3030// Simple class to wrap file streams, string streams, etc.
    3131// seek is essential, and it should be byte stream
    3232class StreamReader {
    33   // should return a string [FIXME: perhaps return array of bytes?]
    34   function read($bytes) {
    35     return false;
    36   }
     33        // should return a string [FIXME: perhaps return array of bytes?]
     34        function read($bytes) {
     35                return false;
     36        }
    3737
    38   // should return new position
    39   function seekto($position) {
    40     return false;
    41   }
     38        // should return new position
     39        function seekto($position) {
     40                return false;
     41        }
    4242
    43   // returns current position
    44   function currentpos() {
    45     return false;
    46   }
     43        // returns current position
     44        function currentpos() {
     45                return false;
     46        }
    4747
    48   // returns length of entire stream (limit for seekto()s)
    49   function length() {
    50     return false;
    51   }
     48        // returns length of entire stream (limit for seekto()s)
     49        function length() {
     50                return false;
     51        }
    5252}
    5353
    5454class StringReader {
    55   var $_pos;
    56   var $_str;
     55        var $_pos;
     56        var $_str;
    5757
    58   function StringReader($str='') {
    59     $this->_str = $str;
    60     $this->_pos = 0;
    61     // If string functions are overloaded, we need to use the mb versions
    62     $this->is_overloaded = ((ini_get("mbstring.func_overload") & 2) != 0) && function_exists('mb_substr');
    63   }
     58        function StringReader($str='') {
     59                $this->_str = $str;
     60                $this->_pos = 0;
     61                // If string functions are overloaded, we need to use the mb versions
     62                $this->is_overloaded = ((ini_get("mbstring.func_overload") & 2) != 0) && function_exists('mb_substr');
     63        }
    6464
    65   function _substr($string, $start, $length) {
     65        function _substr($string, $start, $length) {
    6666        if ($this->is_overloaded) {
    6767                return mb_substr($string,$start,$length,'ascii');
    6868        } else {
    6969                return substr($string,$start,$length);
    7070        }
    71   }
     71        }
    7272
    73   function _strlen($string) {
     73        function _strlen($string) {
    7474        if ($this->is_overloaded) {
    7575                return mb_strlen($string,'ascii');
    7676        } else {
    7777                return strlen($string);
    7878        }
    79   }
     79        }
    8080
    81   function read($bytes) {
    82           $data = $this->_substr($this->_str, $this->_pos, $bytes);
    83     $this->_pos += $bytes;
    84     if ($this->_strlen($this->_str)<$this->_pos)
    85       $this->_pos = $this->_strlen($this->_str);
     81        function read($bytes) {
     82                $data = $this->_substr($this->_str, $this->_pos, $bytes);
     83                $this->_pos += $bytes;
     84                if ($this->_strlen($this->_str)<$this->_pos)
     85                        $this->_pos = $this->_strlen($this->_str);
    8686
    87     return $data;
    88   }
     87                return $data;
     88        }
    8989
    90   function seekto($pos) {
    91     $this->_pos = $pos;
    92     if ($this->_strlen($this->_str)<$this->_pos)
    93       $this->_pos = $this->_strlen($this->_str);
    94     return $this->_pos;
    95   }
     90        function seekto($pos) {
     91                $this->_pos = $pos;
     92                if ($this->_strlen($this->_str)<$this->_pos)
     93                        $this->_pos = $this->_strlen($this->_str);
     94                return $this->_pos;
     95        }
    9696
    97   function currentpos() {
    98     return $this->_pos;
    99   }
     97        function currentpos() {
     98                return $this->_pos;
     99        }
    100100
    101   function length() {
    102     return $this->_strlen($this->_str);
    103   }
     101        function length() {
     102                return $this->_strlen($this->_str);
     103        }
    104104}
    105105
    106106
    107107class FileReader {
    108   var $_pos;
    109   var $_fd;
    110   var $_length;
     108        var $_pos;
     109        var $_fd;
     110        var $_length;
    111111
    112   function FileReader($filename) {
    113     if (file_exists($filename)) {
     112        function FileReader($filename) {
     113                if (file_exists($filename)) {
    114114
    115       $this->_length=filesize($filename);
    116       $this->_pos = 0;
    117       $this->_fd = fopen($filename,'rb');
    118       if (!$this->_fd) {
     115                        $this->_length=filesize($filename);
     116                        $this->_pos = 0;
     117                        $this->_fd = fopen($filename,'rb');
     118                        if (!$this->_fd) {
    119119        $this->error = 3; // Cannot read file, probably permissions
    120120        return false;
    121       }
    122     } else {
    123       $this->error = 2; // File doesn't exist
    124       return false;
    125     }
    126   }
     121                        }
     122                } else {
     123                        $this->error = 2; // File doesn't exist
     124                        return false;
     125                }
     126        }
    127127
    128   function read($bytes) {
    129     if ($bytes) {
    130       fseek($this->_fd, $this->_pos);
     128        function read($bytes) {
     129                if ($bytes) {
     130                        fseek($this->_fd, $this->_pos);
    131131
    132       // PHP 5.1.1 does not read more than 8192 bytes in one fread()
    133       // the discussions at PHP Bugs suggest it's the intended behaviour
    134       while ($bytes > 0) {
    135         $chunk  = fread($this->_fd, $bytes);
    136         $data  .= $chunk;
    137         $bytes -= strlen($chunk);
    138       }
    139       $this->_pos = ftell($this->_fd);
     132                        // PHP 5.1.1 does not read more than 8192 bytes in one fread()
     133                        // the discussions at PHP Bugs suggest it's the intended behaviour
     134                        while ($bytes > 0) {
     135                                $chunk  = fread($this->_fd, $bytes);
     136                                $data   .= $chunk;
     137                                $bytes -= strlen($chunk);
     138                        }
     139                        $this->_pos = ftell($this->_fd);
    140140
    141       return $data;
    142     } else return '';
    143   }
     141                        return $data;
     142                } else return '';
     143        }
    144144
    145   function seekto($pos) {
    146     fseek($this->_fd, $pos);
    147     $this->_pos = ftell($this->_fd);
    148     return $this->_pos;
    149   }
     145        function seekto($pos) {
     146                fseek($this->_fd, $pos);
     147                $this->_pos = ftell($this->_fd);
     148                return $this->_pos;
     149        }
    150150
    151   function currentpos() {
    152     return $this->_pos;
    153   }
     151        function currentpos() {
     152                return $this->_pos;
     153        }
    154154
    155   function length() {
    156     return $this->_length;
    157   }
     155        function length() {
     156                return $this->_length;
     157        }
    158158
    159   function close() {
    160     fclose($this->_fd);
    161   }
     159        function close() {
     160                fclose($this->_fd);
     161        }
    162162
    163163}
    164164
    165165// Preloads entire file in memory first, then creates a StringReader
    166166// over it (it assumes knowledge of StringReader internals)
    167167class CachedFileReader extends StringReader {
    168   function CachedFileReader($filename) {
    169     parent::StringReader();
     168        function CachedFileReader($filename) {
     169                parent::StringReader();
    170170
    171     if (file_exists($filename)) {
     171                if (file_exists($filename)) {
    172172
    173       $length=filesize($filename);
    174       $fd = fopen($filename,'rb');
     173                        $length=filesize($filename);
     174                        $fd = fopen($filename,'rb');
    175175
    176       if (!$fd) {
    177         $this->error = 3; // Cannot read file, probably permissions
    178         return false;
    179       }
    180       $this->_str = fread($fd, $length);
    181           fclose($fd);
     176                        if (!$fd) {
     177                                $this->error = 3; // Cannot read file, probably permissions
     178                                return false;
     179                        }
     180                        $this->_str = fread($fd, $length);
     181                fclose($fd);
    182182
    183     } else {
    184       $this->error = 2; // File doesn't exist
    185       return false;
    186     }
    187   }
     183                } else {
     184                        $this->error = 2; // File doesn't exist
     185                        return false;
     186                }
     187        }
    188188}
    189189
    190190
  • Text/Diff.php

     
    1919 */
    2020class Text_Diff {
    2121
    22     /**
    23     * Array of changes.
    24     *
    25     * @var array
    26     */
    27     var $_edits;
     22        /**
     23        * Array of changes.
     24        *
     25        * @var array
     26        */
     27        var $_edits;
    2828
    29     /**
    30     * Computes diffs between sequences of strings.
    31     *
    32      * @param string $engine    Name of the diffing engine to use.  'auto'
    33      *                           will automatically select the best.
    34      * @param array $params      Parameters to pass to the diffing engine.
    35      *                           Normally an array of two arrays, each
    36      *                           containing the lines from a file.
    37     */
    38     function Text_Diff($engine, $params)
    39     {
    40         // Backward compatibility workaround.
    41         if (!is_string($engine)) {
    42             $params = array($engine, $params);
    43             $engine = 'auto';
    44         }
     29        /**
     30        * Computes diffs between sequences of strings.
     31        *
     32         * @param string $engine        Name of the diffing engine to use.  'auto'
     33         *                                                 will automatically select the best.
     34         * @param array $params   Parameters to pass to the diffing engine.
     35         *                                                 Normally an array of two arrays, each
     36         *                                                 containing the lines from a file.
     37        */
     38        function Text_Diff($engine, $params)
     39        {
     40                // Backward compatibility workaround.
     41                if (!is_string($engine)) {
     42                        $params = array($engine, $params);
     43                        $engine = 'auto';
     44                }
    4545
    46         if ($engine == 'auto') {
    47             $engine = extension_loaded('xdiff') ? 'xdiff' : 'native';
    48         } else {
    49             $engine = basename($engine);
    50         }
     46                if ($engine == 'auto') {
     47                        $engine = extension_loaded('xdiff') ? 'xdiff' : 'native';
     48                } else {
     49                        $engine = basename($engine);
     50                }
    5151
    52         // WP #7391
    53         require_once dirname(__FILE__).'/Diff/Engine/' . $engine . '.php';
    54         $class = 'Text_Diff_Engine_' . $engine;
    55         $diff_engine = new $class();
     52                // WP #7391
     53                require_once dirname(__FILE__).'/Diff/Engine/' . $engine . '.php';
     54                $class = 'Text_Diff_Engine_' . $engine;
     55                $diff_engine = new $class();
    5656
    57         $this->_edits = call_user_func_array(array($diff_engine, 'diff'), $params);
    58     }
     57                $this->_edits = call_user_func_array(array($diff_engine, 'diff'), $params);
     58        }
    5959
    60     /**
    61     * Returns the array of differences.
    62     */
    63     function getDiff()
    64     {
    65         return $this->_edits;
    66     }
     60        /**
     61        * Returns the array of differences.
     62        */
     63        function getDiff()
     64        {
     65                return $this->_edits;
     66        }
    6767
    68     /**
    69     * Computes a reversed diff.
    70     *
    71     * Example:
    72     * <code>
    73     * $diff = new Text_Diff($lines1, $lines2);
    74     * $rev = $diff->reverse();
    75     * </code>
    76     *
    77     * @return Text_Diff  A Diff object representing the inverse of the
    78      *                    original diff.  Note that we purposely don't return a
    79      *                    reference here, since this essentially is a clone()
    80      *                    method.
    81     */
    82     function reverse()
    83     {
    84         if (version_compare(zend_version(), '2', '>')) {
    85             $rev = clone($this);
    86         } else {
    87             $rev = $this;
    88         }
    89         $rev->_edits = array();
    90         foreach ($this->_edits as $edit) {
    91             $rev->_edits[] = $edit->reverse();
    92         }
    93         return $rev;
    94     }
     68        /**
     69        * Computes a reversed diff.
     70        *
     71        * Example:
     72        * <code>
     73        * $diff = new Text_Diff($lines1, $lines2);
     74        * $rev = $diff->reverse();
     75        * </code>
     76        *
     77        * @return Text_Diff  A Diff object representing the inverse of the
     78         *                                      original diff.  Note that we purposely don't return a
     79         *                                      reference here, since this essentially is a clone()
     80         *                                      method.
     81        */
     82        function reverse()
     83        {
     84                if (version_compare(zend_version(), '2', '>')) {
     85                        $rev = clone($this);
     86                } else {
     87                        $rev = $this;
     88                }
     89                $rev->_edits = array();
     90                foreach ($this->_edits as $edit) {
     91                        $rev->_edits[] = $edit->reverse();
     92                }
     93                return $rev;
     94        }
    9595
    96     /**
    97     * Checks for an empty diff.
    98     *
    99     * @return boolean  True if two sequences were identical.
    100     */
    101     function isEmpty()
    102     {
    103         foreach ($this->_edits as $edit) {
    104             if (!is_a($edit, 'Text_Diff_Op_copy')) {
    105                 return false;
    106             }
    107         }
    108         return true;
    109     }
     96        /**
     97        * Checks for an empty diff.
     98        *
     99        * @return boolean  True if two sequences were identical.
     100        */
     101        function isEmpty()
     102        {
     103                foreach ($this->_edits as $edit) {
     104                        if (!is_a($edit, 'Text_Diff_Op_copy')) {
     105                                return false;
     106                        }
     107                }
     108                return true;
     109        }
    110110
    111     /**
    112     * Computes the length of the Longest Common Subsequence (LCS).
    113     *
    114     * This is mostly for diagnostic purposes.
    115     *
    116     * @return integer  The length of the LCS.
    117     */
    118     function lcs()
    119     {
    120         $lcs = 0;
    121         foreach ($this->_edits as $edit) {
    122             if (is_a($edit, 'Text_Diff_Op_copy')) {
    123                 $lcs += count($edit->orig);
    124             }
    125         }
    126         return $lcs;
    127     }
     111        /**
     112        * Computes the length of the Longest Common Subsequence (LCS).
     113        *
     114        * This is mostly for diagnostic purposes.
     115        *
     116        * @return integer  The length of the LCS.
     117        */
     118        function lcs()
     119        {
     120                $lcs = 0;
     121                foreach ($this->_edits as $edit) {
     122                        if (is_a($edit, 'Text_Diff_Op_copy')) {
     123                                $lcs += count($edit->orig);
     124                        }
     125                }
     126                return $lcs;
     127        }
    128128
    129     /**
    130     * Gets the original set of lines.
    131     *
    132     * This reconstructs the $from_lines parameter passed to the constructor.
    133     *
    134     * @return array  The original sequence of strings.
    135     */
    136     function getOriginal()
    137     {
    138         $lines = array();
    139         foreach ($this->_edits as $edit) {
    140             if ($edit->orig) {
    141                 array_splice($lines, count($lines), 0, $edit->orig);
    142             }
    143         }
    144         return $lines;
    145     }
     129        /**
     130        * Gets the original set of lines.
     131        *
     132        * This reconstructs the $from_lines parameter passed to the constructor.
     133        *
     134        * @return array  The original sequence of strings.
     135        */
     136        function getOriginal()
     137        {
     138                $lines = array();
     139                foreach ($this->_edits as $edit) {
     140                        if ($edit->orig) {
     141                                array_splice($lines, count($lines), 0, $edit->orig);
     142                        }
     143                }
     144                return $lines;
     145        }
    146146
    147     /**
    148     * Gets the final set of lines.
    149     *
    150     * This reconstructs the $to_lines parameter passed to the constructor.
    151     *
    152     * @return array  The sequence of strings.
    153     */
    154     function getFinal()
    155     {
    156         $lines = array();
    157         foreach ($this->_edits as $edit) {
    158             if ($edit->final) {
    159                 array_splice($lines, count($lines), 0, $edit->final);
    160             }
    161         }
    162         return $lines;
    163     }
     147        /**
     148        * Gets the final set of lines.
     149        *
     150        * This reconstructs the $to_lines parameter passed to the constructor.
     151        *
     152        * @return array  The sequence of strings.
     153        */
     154        function getFinal()
     155        {
     156                $lines = array();
     157                foreach ($this->_edits as $edit) {
     158                        if ($edit->final) {
     159                                array_splice($lines, count($lines), 0, $edit->final);
     160                        }
     161                }
     162                return $lines;
     163        }
    164164
    165     /**
    166     * Removes trailing newlines from a line of text. This is meant to be used
    167     * with array_walk().
    168     *
    169     * @param string $line  The line to trim.
    170     * @param integer $key  The index of the line in the array. Not used.
    171     */
    172     function trimNewlines(&$line, $key)
    173     {
    174         $line = str_replace(array("\n", "\r"), '', $line);
    175     }
     165        /**
     166        * Removes trailing newlines from a line of text. This is meant to be used
     167        * with array_walk().
     168        *
     169        * @param string $line  The line to trim.
     170        * @param integer $key  The index of the line in the array. Not used.
     171        */
     172        function trimNewlines(&$line, $key)
     173        {
     174                $line = str_replace(array("\n", "\r"), '', $line);
     175        }
    176176
    177     /**
    178     * Determines the location of the system temporary directory.
    179     *
    180     * @static
    181     *
    182     * @access protected
    183     *
    184     * @return string  A directory name which can be used for temp files.
    185      *                Returns false if one could not be found.
    186     */
    187     function _getTempDir()
    188     {
    189         $tmp_locations = array('/tmp', '/var/tmp', 'c:\WUTemp', 'c:\temp',
    190                                'c:\windows\temp', 'c:\winnt\temp');
     177        /**
     178        * Determines the location of the system temporary directory.
     179        *
     180        * @static
     181        *
     182        * @access protected
     183        *
     184        * @return string  A directory name which can be used for temp files.
     185         *                              Returns false if one could not be found.
     186        */
     187        function _getTempDir()
     188        {
     189                $tmp_locations = array('/tmp', '/var/tmp', 'c:\WUTemp', 'c:\temp',
     190                                                           'c:\windows\temp', 'c:\winnt\temp');
    191191
    192         /* Try PHP's upload_tmp_dir directive. */
    193         $tmp = ini_get('upload_tmp_dir');
     192                /* Try PHP's upload_tmp_dir directive. */
     193                $tmp = ini_get('upload_tmp_dir');
    194194
    195         /* Otherwise, try to determine the TMPDIR environment variable. */
    196         if (!strlen($tmp)) {
    197             $tmp = getenv('TMPDIR');
    198         }
     195                /* Otherwise, try to determine the TMPDIR environment variable. */
     196                if (!strlen($tmp)) {
     197                        $tmp = getenv('TMPDIR');
     198                }
    199199
    200         /* If we still cannot determine a value, then cycle through a list of
    201         * preset possibilities. */
    202         while (!strlen($tmp) && count($tmp_locations)) {
    203             $tmp_check = array_shift($tmp_locations);
    204             if (@is_dir($tmp_check)) {
    205                 $tmp = $tmp_check;
    206             }
    207         }
     200                /* If we still cannot determine a value, then cycle through a list of
     201                * preset possibilities. */
     202                while (!strlen($tmp) && count($tmp_locations)) {
     203                        $tmp_check = array_shift($tmp_locations);
     204                        if (@is_dir($tmp_check)) {
     205                                $tmp = $tmp_check;
     206                        }
     207                }
    208208
    209         /* If it is still empty, we have failed, so return false; otherwise
    210         * return the directory determined. */
    211         return strlen($tmp) ? $tmp : false;
    212     }
     209                /* If it is still empty, we have failed, so return false; otherwise
     210                * return the directory determined. */
     211                return strlen($tmp) ? $tmp : false;
     212        }
    213213
    214     /**
    215     * Checks a diff for validity.
    216     *
    217     * This is here only for debugging purposes.
    218     */
    219     function _check($from_lines, $to_lines)
    220     {
    221         if (serialize($from_lines) != serialize($this->getOriginal())) {
    222             trigger_error("Reconstructed original doesn't match", E_USER_ERROR);
    223         }
    224         if (serialize($to_lines) != serialize($this->getFinal())) {
    225             trigger_error("Reconstructed final doesn't match", E_USER_ERROR);
    226         }
     214        /**
     215        * Checks a diff for validity.
     216        *
     217        * This is here only for debugging purposes.
     218        */
     219        function _check($from_lines, $to_lines)
     220        {
     221                if (serialize($from_lines) != serialize($this->getOriginal())) {
     222                        trigger_error("Reconstructed original doesn't match", E_USER_ERROR);
     223                }
     224                if (serialize($to_lines) != serialize($this->getFinal())) {
     225                        trigger_error("Reconstructed final doesn't match", E_USER_ERROR);
     226                }
    227227
    228         $rev = $this->reverse();
    229         if (serialize($to_lines) != serialize($rev->getOriginal())) {
    230             trigger_error("Reversed original doesn't match", E_USER_ERROR);
    231         }
    232         if (serialize($from_lines) != serialize($rev->getFinal())) {
    233             trigger_error("Reversed final doesn't match", E_USER_ERROR);
    234         }
     228                $rev = $this->reverse();
     229                if (serialize($to_lines) != serialize($rev->getOriginal())) {
     230                        trigger_error("Reversed original doesn't match", E_USER_ERROR);
     231                }
     232                if (serialize($from_lines) != serialize($rev->getFinal())) {
     233                        trigger_error("Reversed final doesn't match", E_USER_ERROR);
     234                }
    235235
    236         $prevtype = null;
    237         foreach ($this->_edits as $edit) {
    238             if ($prevtype == get_class($edit)) {
    239                 trigger_error("Edit sequence is non-optimal", E_USER_ERROR);
    240             }
    241             $prevtype = get_class($edit);
    242         }
     236                $prevtype = null;
     237                foreach ($this->_edits as $edit) {
     238                        if ($prevtype == get_class($edit)) {
     239                                trigger_error("Edit sequence is non-optimal", E_USER_ERROR);
     240                        }
     241                        $prevtype = get_class($edit);
     242                }
    243243
    244         return true;
    245     }
     244                return true;
     245        }
    246246
    247247}
    248248
     
    252252 */
    253253class Text_MappedDiff extends Text_Diff {
    254254
    255     /**
    256     * Computes a diff between sequences of strings.
    257     *
    258     * This can be used to compute things like case-insensitve diffs, or diffs
    259     * which ignore changes in white-space.
    260     *
    261      * @param array $from_lines        An array of strings.
    262      * @param array $to_lines           An array of strings.
    263     * @param array $mapped_from_lines  This array should have the same size
    264      *                                  number of elements as $from_lines.  The
    265      *                                  elements in $mapped_from_lines and
    266      *                                  $mapped_to_lines are what is actually
    267      *                                  compared when computing the diff.
    268      * @param array $mapped_to_lines    This array should have the same number
    269      *                                  of elements as $to_lines.
    270     */
    271     function Text_MappedDiff($from_lines, $to_lines,
    272                             $mapped_from_lines, $mapped_to_lines)
    273     {
    274         assert(count($from_lines) == count($mapped_from_lines));
    275         assert(count($to_lines) == count($mapped_to_lines));
     255        /**
     256        * Computes a diff between sequences of strings.
     257        *
     258        * This can be used to compute things like case-insensitve diffs, or diffs
     259        * which ignore changes in white-space.
     260        *
     261         * @param array $from_lines              An array of strings.
     262         * @param array $to_lines                  An array of strings.
     263        * @param array $mapped_from_lines  This array should have the same size
     264         *                                                                number of elements as $from_lines.  The
     265         *                                                                elements in $mapped_from_lines and
     266         *                                                                $mapped_to_lines are what is actually
     267         *                                                                compared when computing the diff.
     268         * @param array $mapped_to_lines        This array should have the same number
     269         *                                                                of elements as $to_lines.
     270        */
     271        function Text_MappedDiff($from_lines, $to_lines,
     272                                                        $mapped_from_lines, $mapped_to_lines)
     273        {
     274                assert(count($from_lines) == count($mapped_from_lines));
     275                assert(count($to_lines) == count($mapped_to_lines));
    276276
    277         parent::Text_Diff($mapped_from_lines, $mapped_to_lines);
     277                parent::Text_Diff($mapped_from_lines, $mapped_to_lines);
    278278
    279         $xi = $yi = 0;
    280         for ($i = 0; $i < count($this->_edits); $i++) {
    281             $orig = &$this->_edits[$i]->orig;
    282             if (is_array($orig)) {
    283                 $orig = array_slice($from_lines, $xi, count($orig));
    284                 $xi += count($orig);
    285             }
     279                $xi = $yi = 0;
     280                for ($i = 0; $i < count($this->_edits); $i++) {
     281                        $orig = &$this->_edits[$i]->orig;
     282                        if (is_array($orig)) {
     283                                $orig = array_slice($from_lines, $xi, count($orig));
     284                                $xi += count($orig);
     285                        }
    286286
    287             $final = &$this->_edits[$i]->final;
    288             if (is_array($final)) {
    289                 $final = array_slice($to_lines, $yi, count($final));
    290                 $yi += count($final);
    291             }
    292         }
    293     }
     287                        $final = &$this->_edits[$i]->final;
     288                        if (is_array($final)) {
     289                                $final = array_slice($to_lines, $yi, count($final));
     290                                $yi += count($final);
     291                        }
     292                }
     293        }
    294294
    295295}
    296296
     
    302302 */
    303303class Text_Diff_Op {
    304304
    305     var $orig;
    306     var $final;
     305        var $orig;
     306        var $final;
    307307
    308     function &reverse()
    309     {
    310         trigger_error('Abstract method', E_USER_ERROR);
    311     }
     308        function &reverse()
     309        {
     310                trigger_error('Abstract method', E_USER_ERROR);
     311        }
    312312
    313     function norig()
    314     {
    315         return $this->orig ? count($this->orig) : 0;
    316     }
     313        function norig()
     314        {
     315                return $this->orig ? count($this->orig) : 0;
     316        }
    317317
    318     function nfinal()
    319     {
    320         return $this->final ? count($this->final) : 0;
    321     }
     318        function nfinal()
     319        {
     320                return $this->final ? count($this->final) : 0;
     321        }
    322322
    323323}
    324324
     
    330330 */
    331331class Text_Diff_Op_copy extends Text_Diff_Op {
    332332
    333     function Text_Diff_Op_copy($orig, $final = false)
    334     {
    335         if (!is_array($final)) {
    336             $final = $orig;
    337         }
    338         $this->orig = $orig;
    339         $this->final = $final;
    340     }
     333        function Text_Diff_Op_copy($orig, $final = false)
     334        {
     335                if (!is_array($final)) {
     336                        $final = $orig;
     337                }
     338                $this->orig = $orig;
     339                $this->final = $final;
     340        }
    341341
    342     function &reverse()
    343     {
    344         $reverse = &new Text_Diff_Op_copy($this->final, $this->orig);
    345         return $reverse;
    346     }
     342        function &reverse()
     343        {
     344                $reverse = &new Text_Diff_Op_copy($this->final, $this->orig);
     345                return $reverse;
     346        }
    347347
    348348}
    349349
     
    355355 */
    356356class Text_Diff_Op_delete extends Text_Diff_Op {
    357357
    358     function Text_Diff_Op_delete($lines)
    359     {
    360         $this->orig = $lines;
    361         $this->final = false;
    362     }
     358        function Text_Diff_Op_delete($lines)
     359        {
     360                $this->orig = $lines;
     361                $this->final = false;
     362        }
    363363
    364     function &reverse()
    365     {
    366         $reverse = &new Text_Diff_Op_add($this->orig);
    367         return $reverse;
    368     }
     364        function &reverse()
     365        {
     366                $reverse = &new Text_Diff_Op_add($this->orig);
     367                return $reverse;
     368        }
    369369
    370370}
    371371
     
    377377 */
    378378class Text_Diff_Op_add extends Text_Diff_Op {
    379379
    380     function Text_Diff_Op_add($lines)
    381     {
    382         $this->final = $lines;
    383         $this->orig = false;
    384     }
     380        function Text_Diff_Op_add($lines)
     381        {
     382                $this->final = $lines;
     383                $this->orig = false;
     384        }
    385385
    386     function &reverse()
    387     {
    388         $reverse = &new Text_Diff_Op_delete($this->final);
    389         return $reverse;
    390     }
     386        function &reverse()
     387        {
     388                $reverse = &new Text_Diff_Op_delete($this->final);
     389                return $reverse;
     390        }
    391391
    392392}
    393393
     
    399399 */
    400400class Text_Diff_Op_change extends Text_Diff_Op {
    401401
    402     function Text_Diff_Op_change($orig, $final)
    403     {
    404         $this->orig = $orig;
    405         $this->final = $final;
    406     }
     402        function Text_Diff_Op_change($orig, $final)
     403        {
     404                $this->orig = $orig;
     405                $this->final = $final;
     406        }
    407407
    408     function &reverse()
    409     {
    410         $reverse = &new Text_Diff_Op_change($this->final, $this->orig);
    411         return $reverse;
    412     }
     408        function &reverse()
     409        {
     410                $reverse = &new Text_Diff_Op_change($this->final, $this->orig);
     411                return $reverse;
     412        }
    413413
    414414}
  • Text/Diff/Engine/native.php

     
    2929 */
    3030class Text_Diff_Engine_native {
    3131
    32     function diff($from_lines, $to_lines)
    33     {
    34         array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
    35         array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
     32        function diff($from_lines, $to_lines)
     33        {
     34                array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
     35                array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
    3636
    37         $n_from = count($from_lines);
    38         $n_to = count($to_lines);
     37                $n_from = count($from_lines);
     38                $n_to = count($to_lines);
    3939
    40         $this->xchanged = $this->ychanged = array();
    41         $this->xv = $this->yv = array();
    42         $this->xind = $this->yind = array();
    43         unset($this->seq);
    44         unset($this->in_seq);
    45         unset($this->lcs);
     40                $this->xchanged = $this->ychanged = array();
     41                $this->xv = $this->yv = array();
     42                $this->xind = $this->yind = array();
     43                unset($this->seq);
     44                unset($this->in_seq);
     45                unset($this->lcs);
    4646
    47         // Skip leading common lines.
    48         for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) {
    49             if ($from_lines[$skip] !== $to_lines[$skip]) {
    50                 break;
    51             }
    52             $this->xchanged[$skip] = $this->ychanged[$skip] = false;
    53         }
     47                // Skip leading common lines.
     48                for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) {
     49                        if ($from_lines[$skip] !== $to_lines[$skip]) {
     50                                break;
     51                        }
     52                        $this->xchanged[$skip] = $this->ychanged[$skip] = false;
     53                }
    5454
    55         // Skip trailing common lines.
    56         $xi = $n_from; $yi = $n_to;
    57         for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) {
    58             if ($from_lines[$xi] !== $to_lines[$yi]) {
    59                 break;
    60             }
    61             $this->xchanged[$xi] = $this->ychanged[$yi] = false;
    62         }
     55                // Skip trailing common lines.
     56                $xi = $n_from; $yi = $n_to;
     57                for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) {
     58                        if ($from_lines[$xi] !== $to_lines[$yi]) {
     59                                break;
     60                        }
     61                        $this->xchanged[$xi] = $this->ychanged[$yi] = false;
     62                }
    6363
    64         // Ignore lines which do not exist in both files.
    65         for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
    66             $xhash[$from_lines[$xi]] = 1;
    67         }
    68         for ($yi = $skip; $yi < $n_to - $endskip; $yi++) {
    69             $line = $to_lines[$yi];
    70             if (($this->ychanged[$yi] = empty($xhash[$line]))) {
    71                 continue;
    72             }
    73             $yhash[$line] = 1;
    74             $this->yv[] = $line;
    75             $this->yind[] = $yi;
    76         }
    77         for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
    78             $line = $from_lines[$xi];
    79             if (($this->xchanged[$xi] = empty($yhash[$line]))) {
    80                 continue;
    81             }
    82             $this->xv[] = $line;
    83             $this->xind[] = $xi;
    84         }
     64                // Ignore lines which do not exist in both files.
     65                for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
     66                        $xhash[$from_lines[$xi]] = 1;
     67                }
     68                for ($yi = $skip; $yi < $n_to - $endskip; $yi++) {
     69                        $line = $to_lines[$yi];
     70                        if (($this->ychanged[$yi] = empty($xhash[$line]))) {
     71                                continue;
     72                        }
     73                        $yhash[$line] = 1;
     74                        $this->yv[] = $line;
     75                        $this->yind[] = $yi;
     76                }
     77                for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
     78                        $line = $from_lines[$xi];
     79                        if (($this->xchanged[$xi] = empty($yhash[$line]))) {
     80                                continue;
     81                        }
     82                        $this->xv[] = $line;
     83                        $this->xind[] = $xi;
     84                }
    8585
    86         // Find the LCS.
    87         $this->_compareseq(0, count($this->xv), 0, count($this->yv));
     86                // Find the LCS.
     87                $this->_compareseq(0, count($this->xv), 0, count($this->yv));
    8888
    89         // Merge edits when possible.
    90         $this->_shiftBoundaries($from_lines, $this->xchanged, $this->ychanged);
    91         $this->_shiftBoundaries($to_lines, $this->ychanged, $this->xchanged);
     89                // Merge edits when possible.
     90                $this->_shiftBoundaries($from_lines, $this->xchanged, $this->ychanged);
     91                $this->_shiftBoundaries($to_lines, $this->ychanged, $this->xchanged);
    9292
    93         // Compute the edit operations.
    94         $edits = array();
    95         $xi = $yi = 0;
    96         while ($xi < $n_from || $yi < $n_to) {
    97             assert($yi < $n_to || $this->xchanged[$xi]);
    98             assert($xi < $n_from || $this->ychanged[$yi]);
     93                // Compute the edit operations.
     94                $edits = array();
     95                $xi = $yi = 0;
     96                while ($xi < $n_from || $yi < $n_to) {
     97                        assert($yi < $n_to || $this->xchanged[$xi]);
     98                        assert($xi < $n_from || $this->ychanged[$yi]);
    9999
    100             // Skip matching "snake".
    101             $copy = array();
    102             while ($xi < $n_from && $yi < $n_to
    103                    && !$this->xchanged[$xi] && !$this->ychanged[$yi]) {
    104                 $copy[] = $from_lines[$xi++];
    105                 ++$yi;
    106             }
    107             if ($copy) {
    108                 $edits[] = &new Text_Diff_Op_copy($copy);
    109             }
     100                        // Skip matching "snake".
     101                        $copy = array();
     102                        while ($xi < $n_from && $yi < $n_to
     103                                   && !$this->xchanged[$xi] && !$this->ychanged[$yi]) {
     104                                $copy[] = $from_lines[$xi++];
     105                                ++$yi;
     106                        }
     107                        if ($copy) {
     108                                $edits[] = &new Text_Diff_Op_copy($copy);
     109                        }
    110110
    111             // Find deletes & adds.
    112             $delete = array();
    113             while ($xi < $n_from && $this->xchanged[$xi]) {
    114                 $delete[] = $from_lines[$xi++];
    115             }
     111                        // Find deletes & adds.
     112                        $delete = array();
     113                        while ($xi < $n_from && $this->xchanged[$xi]) {
     114                                $delete[] = $from_lines[$xi++];
     115                        }
    116116
    117             $add = array();
    118             while ($yi < $n_to && $this->ychanged[$yi]) {
    119                 $add[] = $to_lines[$yi++];
    120             }
     117                        $add = array();
     118                        while ($yi < $n_to && $this->ychanged[$yi]) {
     119                                $add[] = $to_lines[$yi++];
     120                        }
    121121
    122             if ($delete && $add) {
    123                 $edits[] = &new Text_Diff_Op_change($delete, $add);
    124             } elseif ($delete) {
    125                 $edits[] = &new Text_Diff_Op_delete($delete);
    126             } elseif ($add) {
    127                 $edits[] = &new Text_Diff_Op_add($add);
    128             }
    129         }
     122                        if ($delete && $add) {
     123                                $edits[] = &new Text_Diff_Op_change($delete, $add);
     124                        } elseif ($delete) {
     125                                $edits[] = &new Text_Diff_Op_delete($delete);
     126                        } elseif ($add) {
     127                                $edits[] = &new Text_Diff_Op_add($add);
     128                        }
     129                }
    130130
    131         return $edits;
    132     }
     131                return $edits;
     132        }
    133133
    134     /**
    135     * Divides the Largest Common Subsequence (LCS) of the sequences (XOFF,
    136     * XLIM) and (YOFF, YLIM) into NCHUNKS approximately equally sized
    137     * segments.
    138     *
    139     * Returns (LCS, PTS).  LCS is the length of the LCS. PTS is an array of
    140     * NCHUNKS+1 (X, Y) indexes giving the diving points between sub
    141     * sequences.  The first sub-sequence is contained in (X0, X1), (Y0, Y1),
    142     * the second in (X1, X2), (Y1, Y2) and so on.  Note that (X0, Y0) ==
    143     * (XOFF, YOFF) and (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM).
    144     *
    145     * This function assumes that the first lines of the specified portions of
    146     * the two files do not match, and likewise that the last lines do not
    147     * match.  The caller must trim matching lines from the beginning and end
    148     * of the portions it is going to specify.
    149     */
    150     function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks)
    151     {
    152         $flip = false;
     134        /**
     135        * Divides the Largest Common Subsequence (LCS) of the sequences (XOFF,
     136        * XLIM) and (YOFF, YLIM) into NCHUNKS approximately equally sized
     137        * segments.
     138        *
     139        * Returns (LCS, PTS).  LCS is the length of the LCS. PTS is an array of
     140        * NCHUNKS+1 (X, Y) indexes giving the diving points between sub
     141        * sequences.  The first sub-sequence is contained in (X0, X1), (Y0, Y1),
     142        * the second in (X1, X2), (Y1, Y2) and so on.  Note that (X0, Y0) ==
     143        * (XOFF, YOFF) and (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM).
     144        *
     145        * This function assumes that the first lines of the specified portions of
     146        * the two files do not match, and likewise that the last lines do not
     147        * match.  The caller must trim matching lines from the beginning and end
     148        * of the portions it is going to specify.
     149        */
     150        function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks)
     151        {
     152                $flip = false;
    153153
    154         if ($xlim - $xoff > $ylim - $yoff) {
    155             /* Things seems faster (I'm not sure I understand why) when the
    156             * shortest sequence is in X. */
    157             $flip = true;
    158             list ($xoff, $xlim, $yoff, $ylim)
    159                 = array($yoff, $ylim, $xoff, $xlim);
    160         }
     154                if ($xlim - $xoff > $ylim - $yoff) {
     155                        /* Things seems faster (I'm not sure I understand why) when the
     156                        * shortest sequence is in X. */
     157                        $flip = true;
     158                        list ($xoff, $xlim, $yoff, $ylim)
     159                                = array($yoff, $ylim, $xoff, $xlim);
     160                }
    161161
    162         if ($flip) {
    163             for ($i = $ylim - 1; $i >= $yoff; $i--) {
    164                 $ymatches[$this->xv[$i]][] = $i;
    165             }
    166         } else {
    167             for ($i = $ylim - 1; $i >= $yoff; $i--) {
    168                 $ymatches[$this->yv[$i]][] = $i;
    169             }
    170         }
     162                if ($flip) {
     163                        for ($i = $ylim - 1; $i >= $yoff; $i--) {
     164                                $ymatches[$this->xv[$i]][] = $i;
     165                        }
     166                } else {
     167                        for ($i = $ylim - 1; $i >= $yoff; $i--) {
     168                                $ymatches[$this->yv[$i]][] = $i;
     169                        }
     170                }
    171171
    172         $this->lcs = 0;
    173         $this->seq[0]= $yoff - 1;
    174         $this->in_seq = array();
    175         $ymids[0] = array();
     172                $this->lcs = 0;
     173                $this->seq[0]= $yoff - 1;
     174                $this->in_seq = array();
     175                $ymids[0] = array();
    176176
    177         $numer = $xlim - $xoff + $nchunks - 1;
    178         $x = $xoff;
    179         for ($chunk = 0; $chunk < $nchunks; $chunk++) {
    180             if ($chunk > 0) {
    181                 for ($i = 0; $i <= $this->lcs; $i++) {
    182                     $ymids[$i][$chunk - 1] = $this->seq[$i];
    183                 }
    184             }
     177                $numer = $xlim - $xoff + $nchunks - 1;
     178                $x = $xoff;
     179                for ($chunk = 0; $chunk < $nchunks; $chunk++) {
     180                        if ($chunk > 0) {
     181                                for ($i = 0; $i <= $this->lcs; $i++) {
     182                                        $ymids[$i][$chunk - 1] = $this->seq[$i];
     183                                }
     184                        }
    185185
    186             $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $chunk) / $nchunks);
    187             for (; $x < $x1; $x++) {
    188                 $line = $flip ? $this->yv[$x] : $this->xv[$x];
    189                 if (empty($ymatches[$line])) {
    190                     continue;
    191                 }
    192                 $matches = $ymatches[$line];
    193                 reset($matches);
    194                 while (list(, $y) = each($matches)) {
    195                     if (empty($this->in_seq[$y])) {
    196                         $k = $this->_lcsPos($y);
    197                         assert($k > 0);
    198                         $ymids[$k] = $ymids[$k - 1];
    199                         break;
    200                     }
    201                 }
    202                 while (list(, $y) = each($matches)) {
    203                     if ($y > $this->seq[$k - 1]) {
    204                         assert($y <= $this->seq[$k]);
    205                         /* Optimization: this is a common case: next match is
    206                         * just replacing previous match. */
    207                         $this->in_seq[$this->seq[$k]] = false;
    208                         $this->seq[$k] = $y;
    209                         $this->in_seq[$y] = 1;
    210                     } elseif (empty($this->in_seq[$y])) {
    211                         $k = $this->_lcsPos($y);
    212                         assert($k > 0);
    213                         $ymids[$k] = $ymids[$k - 1];
    214                     }
    215                 }
    216             }
    217         }
     186                        $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $chunk) / $nchunks);
     187                        for (; $x < $x1; $x++) {
     188                                $line = $flip ? $this->yv[$x] : $this->xv[$x];
     189                                if (empty($ymatches[$line])) {
     190                                        continue;
     191                                }
     192                                $matches = $ymatches[$line];
     193                                reset($matches);
     194                                while (list(, $y) = each($matches)) {
     195                                        if (empty($this->in_seq[$y])) {
     196                                                $k = $this->_lcsPos($y);
     197                                                assert($k > 0);
     198                                                $ymids[$k] = $ymids[$k - 1];
     199                                                break;
     200                                        }
     201                                }
     202                                while (list(, $y) = each($matches)) {
     203                                        if ($y > $this->seq[$k - 1]) {
     204                                                assert($y <= $this->seq[$k]);
     205                                                /* Optimization: this is a common case: next match is
     206                                                * just replacing previous match. */
     207                                                $this->in_seq[$this->seq[$k]] = false;
     208                                                $this->seq[$k] = $y;
     209                                                $this->in_seq[$y] = 1;
     210                                        } elseif (empty($this->in_seq[$y])) {
     211                                                $k = $this->_lcsPos($y);
     212                                                assert($k > 0);
     213                                                $ymids[$k] = $ymids[$k - 1];
     214                                        }
     215                                }
     216                        }
     217                }
    218218
    219         $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff);
    220         $ymid = $ymids[$this->lcs];
    221         for ($n = 0; $n < $nchunks - 1; $n++) {
    222             $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks);
    223             $y1 = $ymid[$n] + 1;
    224             $seps[] = $flip ? array($y1, $x1) : array($x1, $y1);
    225         }
    226         $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim);
     219                $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff);
     220                $ymid = $ymids[$this->lcs];
     221                for ($n = 0; $n < $nchunks - 1; $n++) {
     222                        $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks);
     223                        $y1 = $ymid[$n] + 1;
     224                        $seps[] = $flip ? array($y1, $x1) : array($x1, $y1);
     225                }
     226                $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim);
    227227
    228         return array($this->lcs, $seps);
    229     }
     228                return array($this->lcs, $seps);
     229        }
    230230
    231     function _lcsPos($ypos)
    232     {
    233         $end = $this->lcs;
    234         if ($end == 0 || $ypos > $this->seq[$end]) {
    235             $this->seq[++$this->lcs] = $ypos;
    236             $this->in_seq[$ypos] = 1;
    237             return $this->lcs;
    238         }
     231        function _lcsPos($ypos)
     232        {
     233                $end = $this->lcs;
     234                if ($end == 0 || $ypos > $this->seq[$end]) {
     235                        $this->seq[++$this->lcs] = $ypos;
     236                        $this->in_seq[$ypos] = 1;
     237                        return $this->lcs;
     238                }
    239239
    240         $beg = 1;
    241         while ($beg < $end) {
    242             $mid = (int)(($beg + $end) / 2);
    243             if ($ypos > $this->seq[$mid]) {
    244                 $beg = $mid + 1;
    245             } else {
    246                 $end = $mid;
    247             }
    248         }
     240                $beg = 1;
     241                while ($beg < $end) {
     242                        $mid = (int)(($beg + $end) / 2);
     243                        if ($ypos > $this->seq[$mid]) {
     244                                $beg = $mid + 1;
     245                        } else {
     246                                $end = $mid;
     247                        }
     248                }
    249249
    250         assert($ypos != $this->seq[$end]);
     250                assert($ypos != $this->seq[$end]);
    251251
    252         $this->in_seq[$this->seq[$end]] = false;
    253         $this->seq[$end] = $ypos;
    254         $this->in_seq[$ypos] = 1;
    255         return $end;
    256     }
     252                $this->in_seq[$this->seq[$end]] = false;
     253                $this->seq[$end] = $ypos;
     254                $this->in_seq[$ypos] = 1;
     255                return $end;
     256        }
    257257
    258     /**
    259     * Finds LCS of two sequences.
    260     *
    261     * The results are recorded in the vectors $this->{x,y}changed[], by
    262     * storing a 1 in the element for each line that is an insertion or
    263     * deletion (ie. is not in the LCS).
    264     *
    265     * The subsequence of file 0 is (XOFF, XLIM) and likewise for file 1.
    266     *
    267     * Note that XLIM, YLIM are exclusive bounds.  All line numbers are
    268     * origin-0 and discarded lines are not counted.
    269     */
    270     function _compareseq ($xoff, $xlim, $yoff, $ylim)
    271     {
    272         /* Slide down the bottom initial diagonal. */
    273         while ($xoff < $xlim && $yoff < $ylim
    274                && $this->xv[$xoff] == $this->yv[$yoff]) {
    275             ++$xoff;
    276             ++$yoff;
    277         }
     258        /**
     259        * Finds LCS of two sequences.
     260        *
     261        * The results are recorded in the vectors $this->{x,y}changed[], by
     262        * storing a 1 in the element for each line that is an insertion or
     263        * deletion (ie. is not in the LCS).
     264        *
     265        * The subsequence of file 0 is (XOFF, XLIM) and likewise for file 1.
     266        *
     267        * Note that XLIM, YLIM are exclusive bounds.  All line numbers are
     268        * origin-0 and discarded lines are not counted.
     269        */
     270        function _compareseq ($xoff, $xlim, $yoff, $ylim)
     271        {
     272                /* Slide down the bottom initial diagonal. */
     273                while ($xoff < $xlim && $yoff < $ylim
     274                           && $this->xv[$xoff] == $this->yv[$yoff]) {
     275                        ++$xoff;
     276                        ++$yoff;
     277                }
    278278
    279         /* Slide up the top initial diagonal. */
    280         while ($xlim > $xoff && $ylim > $yoff
    281                && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) {
    282             --$xlim;
    283             --$ylim;
    284         }
     279                /* Slide up the top initial diagonal. */
     280                while ($xlim > $xoff && $ylim > $yoff
     281                           && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) {
     282                        --$xlim;
     283                        --$ylim;
     284                }
    285285
    286         if ($xoff == $xlim || $yoff == $ylim) {
    287             $lcs = 0;
    288         } else {
    289             /* This is ad hoc but seems to work well.  $nchunks =
    290             * sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5); $nchunks =
    291             * max(2,min(8,(int)$nchunks)); */
    292             $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1;
    293             list($lcs, $seps)
    294                 = $this->_diag($xoff, $xlim, $yoff, $ylim, $nchunks);
    295         }
     286                if ($xoff == $xlim || $yoff == $ylim) {
     287                        $lcs = 0;
     288                } else {
     289                        /* This is ad hoc but seems to work well.  $nchunks =
     290                        * sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5); $nchunks =
     291                        * max(2,min(8,(int)$nchunks)); */
     292                        $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1;
     293                        list($lcs, $seps)
     294                                = $this->_diag($xoff, $xlim, $yoff, $ylim, $nchunks);
     295                }
    296296
    297         if ($lcs == 0) {
    298             /* X and Y sequences have no common subsequence: mark all
    299             * changed. */
    300             while ($yoff < $ylim) {
    301                 $this->ychanged[$this->yind[$yoff++]] = 1;
    302             }
    303             while ($xoff < $xlim) {
    304                 $this->xchanged[$this->xind[$xoff++]] = 1;
    305             }
    306         } else {
    307             /* Use the partitions to split this problem into subproblems. */
    308             reset($seps);
    309             $pt1 = $seps[0];
    310             while ($pt2 = next($seps)) {
    311                 $this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]);
    312                 $pt1 = $pt2;
    313             }
    314         }
    315     }
     297                if ($lcs == 0) {
     298                        /* X and Y sequences have no common subsequence: mark all
     299                        * changed. */
     300                        while ($yoff < $ylim) {
     301                                $this->ychanged[$this->yind[$yoff++]] = 1;
     302                        }
     303                        while ($xoff < $xlim) {
     304                                $this->xchanged[$this->xind[$xoff++]] = 1;
     305                        }
     306                } else {
     307                        /* Use the partitions to split this problem into subproblems. */
     308                        reset($seps);
     309                        $pt1 = $seps[0];
     310                        while ($pt2 = next($seps)) {
     311                                $this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]);
     312                                $pt1 = $pt2;
     313                        }
     314                }
     315        }
    316316
    317     /**
    318     * Adjusts inserts/deletes of identical lines to join changes as much as
    319     * possible.
    320     *
    321     * We do something when a run of changed lines include a line at one end
    322     * and has an excluded, identical line at the other.  We are free to
    323     * choose which identical line is included.  `compareseq' usually chooses
    324     * the one at the beginning, but usually it is cleaner to consider the
    325     * following identical line to be the "change".
    326     *
    327     * This is extracted verbatim from analyze.c (GNU diffutils-2.7).
    328     */
    329     function _shiftBoundaries($lines, &$changed, $other_changed)
    330     {
    331         $i = 0;
    332         $j = 0;
     317        /**
     318        * Adjusts inserts/deletes of identical lines to join changes as much as
     319        * possible.
     320        *
     321        * We do something when a run of changed lines include a line at one end
     322        * and has an excluded, identical line at the other.  We are free to
     323        * choose which identical line is included.  `compareseq' usually chooses
     324        * the one at the beginning, but usually it is cleaner to consider the
     325        * following identical line to be the "change".
     326        *
     327        * This is extracted verbatim from analyze.c (GNU diffutils-2.7).
     328        */
     329        function _shiftBoundaries($lines, &$changed, $other_changed)
     330        {
     331                $i = 0;
     332                $j = 0;
    333333
    334         assert('count($lines) == count($changed)');
    335         $len = count($lines);
    336         $other_len = count($other_changed);
     334                assert('count($lines) == count($changed)');
     335                $len = count($lines);
     336                $other_len = count($other_changed);
    337337
    338         while (1) {
    339             /* Scan forward to find the beginning of another run of
    340             * changes. Also keep track of the corresponding point in the
    341             * other file.
    342             *
    343             * Throughout this code, $i and $j are adjusted together so that
    344             * the first $i elements of $changed and the first $j elements of
    345             * $other_changed both contain the same number of zeros (unchanged
    346             * lines).
    347             *
    348             * Furthermore, $j is always kept so that $j == $other_len or
    349             * $other_changed[$j] == false. */
    350             while ($j < $other_len && $other_changed[$j]) {
    351                 $j++;
    352             }
     338                while (1) {
     339                        /* Scan forward to find the beginning of another run of
     340                        * changes. Also keep track of the corresponding point in the
     341                        * other file.
     342                        *
     343                        * Throughout this code, $i and $j are adjusted together so that
     344                        * the first $i elements of $changed and the first $j elements of
     345                        * $other_changed both contain the same number of zeros (unchanged
     346                        * lines).
     347                        *
     348                        * Furthermore, $j is always kept so that $j == $other_len or
     349                        * $other_changed[$j] == false. */
     350                        while ($j < $other_len && $other_changed[$j]) {
     351                                $j++;
     352                        }
    353353
    354             while ($i < $len && ! $changed[$i]) {
    355                 assert('$j < $other_len && ! $other_changed[$j]');
    356                 $i++; $j++;
    357                 while ($j < $other_len && $other_changed[$j]) {
    358                     $j++;
    359                 }
    360             }
     354                        while ($i < $len && ! $changed[$i]) {
     355                                assert('$j < $other_len && ! $other_changed[$j]');
     356                                $i++; $j++;
     357                                while ($j < $other_len && $other_changed[$j]) {
     358                                        $j++;
     359                                }
     360                        }
    361361
    362             if ($i == $len) {
    363                 break;
    364             }
     362                        if ($i == $len) {
     363                                break;
     364                        }
    365365
    366             $start = $i;
     366                        $start = $i;
    367367
    368             /* Find the end of this run of changes. */
    369             while (++$i < $len && $changed[$i]) {
    370                 continue;
    371             }
     368                        /* Find the end of this run of changes. */
     369                        while (++$i < $len && $changed[$i]) {
     370                                continue;
     371                        }
    372372
    373             do {
    374                 /* Record the length of this run of changes, so that we can
    375                 * later determine whether the run has grown. */
    376                 $runlength = $i - $start;
     373                        do {
     374                                /* Record the length of this run of changes, so that we can
     375                                * later determine whether the run has grown. */
     376                                $runlength = $i - $start;
    377377
    378                 /* Move the changed region back, so long as the previous
    379                 * unchanged line matches the last changed one.  This merges
    380                 * with previous changed regions. */
    381                 while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) {
    382                     $changed[--$start] = 1;
    383                     $changed[--$i] = false;
    384                     while ($start > 0 && $changed[$start - 1]) {
    385                         $start--;
    386                     }
    387                     assert('$j > 0');
    388                     while ($other_changed[--$j]) {
    389                         continue;
    390                     }
    391                     assert('$j >= 0 && !$other_changed[$j]');
    392                 }
     378                                /* Move the changed region back, so long as the previous
     379                                * unchanged line matches the last changed one.  This merges
     380                                * with previous changed regions. */
     381                                while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) {
     382                                        $changed[--$start] = 1;
     383                                        $changed[--$i] = false;
     384                                        while ($start > 0 && $changed[$start - 1]) {
     385                                                $start--;
     386                                        }
     387                                        assert('$j > 0');
     388                                        while ($other_changed[--$j]) {
     389                                                continue;
     390                                        }
     391                                        assert('$j >= 0 && !$other_changed[$j]');
     392                                }
    393393
    394                 /* Set CORRESPONDING to the end of the changed run, at the
    395                 * last point where it corresponds to a changed run in the
    396                 * other file. CORRESPONDING == LEN means no such point has
    397                 * been found. */
    398                 $corresponding = $j < $other_len ? $i : $len;
     394                                /* Set CORRESPONDING to the end of the changed run, at the
     395                                * last point where it corresponds to a changed run in the
     396                                * other file. CORRESPONDING == LEN means no such point has
     397                                * been found. */
     398                                $corresponding = $j < $other_len ? $i : $len;
    399399
    400                 /* Move the changed region forward, so long as the first
    401                 * changed line matches the following unchanged one.  This
    402                 * merges with following changed regions.  Do this second, so
    403                 * that if there are no merges, the changed region is moved
    404                 * forward as far as possible. */
    405                 while ($i < $len && $lines[$start] == $lines[$i]) {
    406                     $changed[$start++] = false;
    407                     $changed[$i++] = 1;
    408                     while ($i < $len && $changed[$i]) {
    409                         $i++;
    410                     }
     400                                /* Move the changed region forward, so long as the first
     401                                * changed line matches the following unchanged one.  This
     402                                * merges with following changed regions.  Do this second, so
     403                                * that if there are no merges, the changed region is moved
     404                                * forward as far as possible. */
     405                                while ($i < $len && $lines[$start] == $lines[$i]) {
     406                                        $changed[$start++] = false;
     407                                        $changed[$i++] = 1;
     408                                        while ($i < $len && $changed[$i]) {
     409                                                $i++;
     410                                        }
    411411
    412                     assert('$j < $other_len && ! $other_changed[$j]');
    413                     $j++;
    414                     if ($j < $other_len && $other_changed[$j]) {
    415                         $corresponding = $i;
    416                         while ($j < $other_len && $other_changed[$j]) {
    417                             $j++;
    418                         }
    419                     }
    420                 }
    421             } while ($runlength != $i - $start);
     412                                        assert('$j < $other_len && ! $other_changed[$j]');
     413                                        $j++;
     414                                        if ($j < $other_len && $other_changed[$j]) {
     415                                                $corresponding = $i;
     416                                                while ($j < $other_len && $other_changed[$j]) {
     417                                                        $j++;
     418                                                }
     419                                        }
     420                                }
     421                        } while ($runlength != $i - $start);
    422422
    423             /* If possible, move the fully-merged run of changes back to a
    424             * corresponding run in the other file. */
    425             while ($corresponding < $i) {
    426                 $changed[--$start] = 1;
    427                 $changed[--$i] = 0;
    428                 assert('$j > 0');
    429                 while ($other_changed[--$j]) {
    430                     continue;
    431                 }
    432                 assert('$j >= 0 && !$other_changed[$j]');
    433             }
    434         }
    435     }
     423                        /* If possible, move the fully-merged run of changes back to a
     424                        * corresponding run in the other file. */
     425                        while ($corresponding < $i) {
     426                                $changed[--$start] = 1;
     427                                $changed[--$i] = 0;
     428                                assert('$j > 0');
     429                                while ($other_changed[--$j]) {
     430                                        continue;
     431                                }
     432                                assert('$j >= 0 && !$other_changed[$j]');
     433                        }
     434                }
     435        }
    436436
    437437}
  • Text/Diff/Engine/shell.php

     
    1818 */
    1919class Text_Diff_Engine_shell {
    2020
    21     /**
    22     * Path to the diff executable
    23     *
    24     * @var string
    25     */
    26     var $_diffCommand = 'diff';
     21        /**
     22        * Path to the diff executable
     23        *
     24        * @var string
     25        */
     26        var $_diffCommand = 'diff';
    2727
    28     /**
    29     * Returns the array of differences.
    30     *
    31     * @param array $from_lines lines of text from old file
    32     * @param array $to_lines   lines of text from new file
    33     *
    34     * @return array all changes made (array with Text_Diff_Op_* objects)
    35     */
    36     function diff($from_lines, $to_lines)
    37     {
    38         array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
    39         array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
     28        /**
     29        * Returns the array of differences.
     30        *
     31        * @param array $from_lines lines of text from old file
     32        * @param array $to_lines   lines of text from new file
     33        *
     34        * @return array all changes made (array with Text_Diff_Op_* objects)
     35        */
     36        function diff($from_lines, $to_lines)
     37        {
     38                array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
     39                array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
    4040
    41         $temp_dir = Text_Diff::_getTempDir();
     41                $temp_dir = Text_Diff::_getTempDir();
    4242
    43         // Execute gnu diff or similar to get a standard diff file.
    44         $from_file = tempnam($temp_dir, 'Text_Diff');
    45         $to_file = tempnam($temp_dir, 'Text_Diff');
    46         $fp = fopen($from_file, 'w');
    47         fwrite($fp, implode("\n", $from_lines));
    48         fclose($fp);
    49         $fp = fopen($to_file, 'w');
    50         fwrite($fp, implode("\n", $to_lines));
    51         fclose($fp);
    52         $diff = shell_exec($this->_diffCommand . ' ' . $from_file . ' ' . $to_file);
    53         unlink($from_file);
    54         unlink($to_file);
     43                // Execute gnu diff or similar to get a standard diff file.
     44                $from_file = tempnam($temp_dir, 'Text_Diff');
     45                $to_file = tempnam($temp_dir, 'Text_Diff');
     46                $fp = fopen($from_file, 'w');
     47                fwrite($fp, implode("\n", $from_lines));
     48                fclose($fp);
     49                $fp = fopen($to_file, 'w');
     50                fwrite($fp, implode("\n", $to_lines));
     51                fclose($fp);
     52                $diff = shell_exec($this->_diffCommand . ' ' . $from_file . ' ' . $to_file);
     53                unlink($from_file);
     54                unlink($to_file);
    5555
    56         if (is_null($diff)) {
    57             // No changes were made
    58             return array(new Text_Diff_Op_copy($from_lines));
    59         }
     56                if (is_null($diff)) {
     57                        // No changes were made
     58                        return array(new Text_Diff_Op_copy($from_lines));
     59                }
    6060
    61         $from_line_no = 1;
    62         $to_line_no = 1;
    63         $edits = array();
     61                $from_line_no = 1;
     62                $to_line_no = 1;
     63                $edits = array();
    6464
    65         // Get changed lines by parsing something like:
    66         // 0a1,2
    67         // 1,2c4,6
    68         // 1,5d6
    69         preg_match_all('#^(\d+)(?:,(\d+))?([adc])(\d+)(?:,(\d+))?$#m', $diff,
    70             $matches, PREG_SET_ORDER);
     65                // Get changed lines by parsing something like:
     66                // 0a1,2
     67                // 1,2c4,6
     68                // 1,5d6
     69                preg_match_all('#^(\d+)(?:,(\d+))?([adc])(\d+)(?:,(\d+))?$#m', $diff,
     70                        $matches, PREG_SET_ORDER);
    7171
    72         foreach ($matches as $match) {
    73             if (!isset($match[5])) {
    74                 // This paren is not set every time (see regex).
    75                 $match[5] = false;
    76             }
     72                foreach ($matches as $match) {
     73                        if (!isset($match[5])) {
     74                                // This paren is not set every time (see regex).
     75                                $match[5] = false;
     76                        }
    7777
    78             if ($match[3] == 'a') {
    79                 $from_line_no--;
    80             }
     78                        if ($match[3] == 'a') {
     79                                $from_line_no--;
     80                        }
    8181
    82             if ($match[3] == 'd') {
    83                 $to_line_no--;
    84             }
     82                        if ($match[3] == 'd') {
     83                                $to_line_no--;
     84                        }
    8585
    86             if ($from_line_no < $match[1] || $to_line_no < $match[4]) {
    87                 // copied lines
    88                 assert('$match[1] - $from_line_no == $match[4] - $to_line_no');
    89                 array_push($edits,
    90                     new Text_Diff_Op_copy(
    91                         $this->_getLines($from_lines, $from_line_no, $match[1] - 1),
    92                         $this->_getLines($to_lines, $to_line_no, $match[4] - 1)));
    93             }
     86                        if ($from_line_no < $match[1] || $to_line_no < $match[4]) {
     87                                // copied lines
     88                                assert('$match[1] - $from_line_no == $match[4] - $to_line_no');
     89                                array_push($edits,
     90                                        new Text_Diff_Op_copy(
     91                                                $this->_getLines($from_lines, $from_line_no, $match[1] - 1),
     92                                                $this->_getLines($to_lines, $to_line_no, $match[4] - 1)));
     93                        }
    9494
    95             switch ($match[3]) {
    96             case 'd':
    97                 // deleted lines
    98                 array_push($edits,
    99                     new Text_Diff_Op_delete(
    100                         $this->_getLines($from_lines, $from_line_no, $match[2])));
    101                 $to_line_no++;
    102                 break;
     95                        switch ($match[3]) {
     96                        case 'd':
     97                                // deleted lines
     98                                array_push($edits,
     99                                        new Text_Diff_Op_delete(
     100                                                $this->_getLines($from_lines, $from_line_no, $match[2])));
     101                                $to_line_no++;
     102                                break;
    103103
    104             case 'c':
    105                 // changed lines
    106                 array_push($edits,
    107                     new Text_Diff_Op_change(
    108                         $this->_getLines($from_lines, $from_line_no, $match[2]),
    109                         $this->_getLines($to_lines, $to_line_no, $match[5])));
    110                 break;
     104                        case 'c':
     105                                // changed lines
     106                                array_push($edits,
     107                                        new Text_Diff_Op_change(
     108                                                $this->_getLines($from_lines, $from_line_no, $match[2]),
     109                                                $this->_getLines($to_lines, $to_line_no, $match[5])));
     110                                break;
    111111
    112             case 'a':
    113                 // added lines
    114                 array_push($edits,
    115                     new Text_Diff_Op_add(
    116                         $this->_getLines($to_lines, $to_line_no, $match[5])));
    117                 $from_line_no++;
    118                 break;
    119             }
    120         }
     112                        case 'a':
     113                                // added lines
     114                                array_push($edits,
     115                                        new Text_Diff_Op_add(
     116                                                $this->_getLines($to_lines, $to_line_no, $match[5])));
     117                                $from_line_no++;
     118                                break;
     119                        }
     120                }
    121121
    122         if (!empty($from_lines)) {
    123             // Some lines might still be pending. Add them as copied
    124             array_push($edits,
    125                 new Text_Diff_Op_copy(
    126                     $this->_getLines($from_lines, $from_line_no,
    127                                     $from_line_no + count($from_lines) - 1),
    128                     $this->_getLines($to_lines, $to_line_no,
    129                                     $to_line_no + count($to_lines) - 1)));
    130         }
     122                if (!empty($from_lines)) {
     123                        // Some lines might still be pending. Add them as copied
     124                        array_push($edits,
     125                                new Text_Diff_Op_copy(
     126                                        $this->_getLines($from_lines, $from_line_no,
     127                                                                        $from_line_no + count($from_lines) - 1),
     128                                        $this->_getLines($to_lines, $to_line_no,
     129                                                                        $to_line_no + count($to_lines) - 1)));
     130                }
    131131
    132         return $edits;
    133     }
     132                return $edits;
     133        }
    134134
    135     /**
    136     * Get lines from either the old or new text
    137     *
    138     * @access private
    139     *
    140     * @param array &$text_lines Either $from_lines or $to_lines
    141      * @param int   &$line_no    Current line number
    142      * @param int   $end        Optional end line, when we want to chop more
    143      *                           than one line.
    144     *
    145     * @return array The chopped lines
    146     */
    147     function _getLines(&$text_lines, &$line_no, $end = false)
    148     {
    149         if (!empty($end)) {
    150             $lines = array();
    151             // We can shift even more
    152             while ($line_no <= $end) {
    153                 array_push($lines, array_shift($text_lines));
    154                 $line_no++;
    155             }
    156         } else {
    157             $lines = array(array_shift($text_lines));
    158             $line_no++;
    159         }
     135        /**
     136        * Get lines from either the old or new text
     137        *
     138        * @access private
     139        *
     140        * @param array &$text_lines Either $from_lines or $to_lines
     141         * @param int   &$line_no       Current line number
     142         * @param int   $end            Optional end line, when we want to chop more
     143         *                                                 than one line.
     144        *
     145        * @return array The chopped lines
     146        */
     147        function _getLines(&$text_lines, &$line_no, $end = false)
     148        {
     149                if (!empty($end)) {
     150                        $lines = array();
     151                        // We can shift even more
     152                        while ($line_no <= $end) {
     153                                array_push($lines, array_shift($text_lines));
     154                                $line_no++;
     155                        }
     156                } else {
     157                        $lines = array(array_shift($text_lines));
     158                        $line_no++;
     159                }
    160160
    161         return $lines;
    162     }
     161                return $lines;
     162        }
    163163
    164164}
  • Text/Diff/Engine/string.php

     
    2424 */
    2525class Text_Diff_Engine_string {
    2626
    27     /**
    28     * Parses a unified or context diff.
    29     *
    30     * First param contains the whole diff and the second can be used to force
    31     * a specific diff type. If the second parameter is 'autodetect', the
    32     * diff will be examined to find out which type of diff this is.
    33     *
    34     * @param string $diff  The diff content.
    35     * @param string $mode  The diff mode of the content in $diff. One of
    36      *                      'context', 'unified', or 'autodetect'.
    37     *
    38     * @return array  List of all diff operations.
    39     */
    40     function diff($diff, $mode = 'autodetect')
    41     {
    42         if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') {
    43             return PEAR::raiseError('Type of diff is unsupported');
    44         }
     27        /**
     28        * Parses a unified or context diff.
     29        *
     30        * First param contains the whole diff and the second can be used to force
     31        * a specific diff type. If the second parameter is 'autodetect', the
     32        * diff will be examined to find out which type of diff this is.
     33        *
     34        * @param string $diff  The diff content.
     35        * @param string $mode  The diff mode of the content in $diff. One of
     36         *                                        'context', 'unified', or 'autodetect'.
     37        *
     38        * @return array  List of all diff operations.
     39        */
     40        function diff($diff, $mode = 'autodetect')
     41        {
     42                if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') {
     43                        return PEAR::raiseError('Type of diff is unsupported');
     44                }
    4545
    46         if ($mode == 'autodetect') {
    47             $context = strpos($diff, '***');
    48             $unified = strpos($diff, '---');
    49             if ($context === $unified) {
    50                 return PEAR::raiseError('Type of diff could not be detected');
    51             } elseif ($context === false || $context === false) {
    52                 $mode = $context !== false ? 'context' : 'unified';
    53             } else {
    54                 $mode = $context < $unified ? 'context' : 'unified';
    55             }
    56         }
     46                if ($mode == 'autodetect') {
     47                        $context = strpos($diff, '***');
     48                        $unified = strpos($diff, '---');
     49                        if ($context === $unified) {
     50                                return PEAR::raiseError('Type of diff could not be detected');
     51                        } elseif ($context === false || $context === false) {
     52                                $mode = $context !== false ? 'context' : 'unified';
     53                        } else {
     54                                $mode = $context < $unified ? 'context' : 'unified';
     55                        }
     56                }
    5757
    58         // split by new line and remove the diff header
    59         $diff = explode("\n", $diff);
    60         array_shift($diff);
    61         array_shift($diff);
     58                // split by new line and remove the diff header
     59                $diff = explode("\n", $diff);
     60                array_shift($diff);
     61                array_shift($diff);
    6262
    63         if ($mode == 'context') {
    64             return $this->parseContextDiff($diff);
    65         } else {
    66             return $this->parseUnifiedDiff($diff);
    67         }
    68     }
     63                if ($mode == 'context') {
     64                        return $this->parseContextDiff($diff);
     65                } else {
     66                        return $this->parseUnifiedDiff($diff);
     67                }
     68        }
    6969
    70     /**
    71     * Parses an array containing the unified diff.
    72     *
    73     * @param array $diff  Array of lines.
    74     *
    75     * @return array  List of all diff operations.
    76     */
    77     function parseUnifiedDiff($diff)
    78     {
    79         $edits = array();
    80         $end = count($diff) - 1;
    81         for ($i = 0; $i < $end;) {
    82             $diff1 = array();
    83             switch (substr($diff[$i], 0, 1)) {
    84             case ' ':
    85                 do {
    86                     $diff1[] = substr($diff[$i], 1);
    87                 } while (++$i < $end && substr($diff[$i], 0, 1) == ' ');
    88                 $edits[] = &new Text_Diff_Op_copy($diff1);
    89                 break;
     70        /**
     71        * Parses an array containing the unified diff.
     72        *
     73        * @param array $diff  Array of lines.
     74        *
     75        * @return array  List of all diff operations.
     76        */
     77        function parseUnifiedDiff($diff)
     78        {
     79                $edits = array();
     80                $end = count($diff) - 1;
     81                for ($i = 0; $i < $end;) {
     82                        $diff1 = array();
     83                        switch (substr($diff[$i], 0, 1)) {
     84                        case ' ':
     85                                do {
     86                                        $diff1[] = substr($diff[$i], 1);
     87                                } while (++$i < $end && substr($diff[$i], 0, 1) == ' ');
     88                                $edits[] = &new Text_Diff_Op_copy($diff1);
     89                                break;
    9090
    91             case '+':
    92                 // get all new lines
    93                 do {
    94                     $diff1[] = substr($diff[$i], 1);
    95                 } while (++$i < $end && substr($diff[$i], 0, 1) == '+');
    96                 $edits[] = &new Text_Diff_Op_add($diff1);
    97                 break;
     91                        case '+':
     92                                // get all new lines
     93                                do {
     94                                        $diff1[] = substr($diff[$i], 1);
     95                                } while (++$i < $end && substr($diff[$i], 0, 1) == '+');
     96                                $edits[] = &new Text_Diff_Op_add($diff1);
     97                                break;
    9898
    99             case '-':
    100                 // get changed or removed lines
    101                 $diff2 = array();
    102                 do {
    103                     $diff1[] = substr($diff[$i], 1);
    104                 } while (++$i < $end && substr($diff[$i], 0, 1) == '-');
     99                        case '-':
     100                                // get changed or removed lines
     101                                $diff2 = array();
     102                                do {
     103                                        $diff1[] = substr($diff[$i], 1);
     104                                } while (++$i < $end && substr($diff[$i], 0, 1) == '-');
    105105
    106                 while ($i < $end && substr($diff[$i], 0, 1) == '+') {
    107                     $diff2[] = substr($diff[$i++], 1);
    108                 }
    109                 if (count($diff2) == 0) {
    110                     $edits[] = &new Text_Diff_Op_delete($diff1);
    111                 } else {
    112                     $edits[] = &new Text_Diff_Op_change($diff1, $diff2);
    113                 }
    114                 break;
     106                                while ($i < $end && substr($diff[$i], 0, 1) == '+') {
     107                                        $diff2[] = substr($diff[$i++], 1);
     108                                }
     109                                if (count($diff2) == 0) {
     110                                        $edits[] = &new Text_Diff_Op_delete($diff1);
     111                                } else {
     112                                        $edits[] = &new Text_Diff_Op_change($diff1, $diff2);
     113                                }
     114                                break;
    115115
    116             default:
    117                 $i++;
    118                 break;
    119             }
    120         }
     116                        default:
     117                                $i++;
     118                                break;
     119                        }
     120                }
    121121
    122         return $edits;
    123     }
     122                return $edits;
     123        }
    124124
    125     /**
    126     * Parses an array containing the context diff.
    127     *
    128     * @param array $diff  Array of lines.
    129     *
    130     * @return array  List of all diff operations.
    131     */
    132     function parseContextDiff(&$diff)
    133     {
    134         $edits = array();
    135         $i = $max_i = $j = $max_j = 0;
    136         $end = count($diff) - 1;
    137         while ($i < $end && $j < $end) {
    138             while ($i >= $max_i && $j >= $max_j) {
    139                 // Find the boundaries of the diff output of the two files
    140                 for ($i = $j;
    141                     $i < $end && substr($diff[$i], 0, 3) == '***';
    142                     $i++);
    143                 for ($max_i = $i;
    144                     $max_i < $end && substr($diff[$max_i], 0, 3) != '---';
    145                     $max_i++);
    146                 for ($j = $max_i;
    147                     $j < $end && substr($diff[$j], 0, 3) == '---';
    148                     $j++);
    149                 for ($max_j = $j;
    150                     $max_j < $end && substr($diff[$max_j], 0, 3) != '***';
    151                     $max_j++);
    152             }
     125        /**
     126        * Parses an array containing the context diff.
     127        *
     128        * @param array $diff  Array of lines.
     129        *
     130        * @return array  List of all diff operations.
     131        */
     132        function parseContextDiff(&$diff)
     133        {
     134                $edits = array();
     135                $i = $max_i = $j = $max_j = 0;
     136                $end = count($diff) - 1;
     137                while ($i < $end && $j < $end) {
     138                        while ($i >= $max_i && $j >= $max_j) {
     139                                // Find the boundaries of the diff output of the two files
     140                                for ($i = $j;
     141                                        $i < $end && substr($diff[$i], 0, 3) == '***';
     142                                        $i++);
     143                                for ($max_i = $i;
     144                                        $max_i < $end && substr($diff[$max_i], 0, 3) != '---';
     145                                        $max_i++);
     146                                for ($j = $max_i;
     147                                        $j < $end && substr($diff[$j], 0, 3) == '---';
     148                                        $j++);
     149                                for ($max_j = $j;
     150                                        $max_j < $end && substr($diff[$max_j], 0, 3) != '***';
     151                                        $max_j++);
     152                        }
    153153
    154             // find what hasn't been changed
    155             $array = array();
    156             while ($i < $max_i &&
    157                    $j < $max_j &&
    158                    strcmp($diff[$i], $diff[$j]) == 0) {
    159                 $array[] = substr($diff[$i], 2);
    160                 $i++;
    161                 $j++;
    162             }
     154                        // find what hasn't been changed
     155                        $array = array();
     156                        while ($i < $max_i &&
     157                                   $j < $max_j &&
     158                                   strcmp($diff[$i], $diff[$j]) == 0) {
     159                                $array[] = substr($diff[$i], 2);
     160                                $i++;
     161                                $j++;
     162                        }
    163163
    164             while ($i < $max_i && ($max_j-$j) <= 1) {
    165                 if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') {
    166                     break;
    167                 }
    168                 $array[] = substr($diff[$i++], 2);
    169             }
     164                        while ($i < $max_i && ($max_j-$j) <= 1) {
     165                                if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') {
     166                                        break;
     167                                }
     168                                $array[] = substr($diff[$i++], 2);
     169                        }
    170170
    171             while ($j < $max_j && ($max_i-$i) <= 1) {
    172                 if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') {
    173                     break;
    174                 }
    175                 $array[] = substr($diff[$j++], 2);
    176             }
    177             if (count($array) > 0) {
    178                 $edits[] = &new Text_Diff_Op_copy($array);
    179             }
     171                        while ($j < $max_j && ($max_i-$i) <= 1) {
     172                                if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') {
     173                                        break;
     174                                }
     175                                $array[] = substr($diff[$j++], 2);
     176                        }
     177                        if (count($array) > 0) {
     178                                $edits[] = &new Text_Diff_Op_copy($array);
     179                        }
    180180
    181             if ($i < $max_i) {
    182                 $diff1 = array();
    183                 switch (substr($diff[$i], 0, 1)) {
    184                 case '!':
    185                     $diff2 = array();
    186                     do {
    187                         $diff1[] = substr($diff[$i], 2);
    188                         if ($j < $max_j && substr($diff[$j], 0, 1) == '!') {
    189                             $diff2[] = substr($diff[$j++], 2);
    190                         }
    191                     } while (++$i < $max_i && substr($diff[$i], 0, 1) == '!');
    192                     $edits[] = &new Text_Diff_Op_change($diff1, $diff2);
    193                     break;
     181                        if ($i < $max_i) {
     182                                $diff1 = array();
     183                                switch (substr($diff[$i], 0, 1)) {
     184                                case '!':
     185                                        $diff2 = array();
     186                                        do {
     187                                                $diff1[] = substr($diff[$i], 2);
     188                                                if ($j < $max_j && substr($diff[$j], 0, 1) == '!') {
     189                                                        $diff2[] = substr($diff[$j++], 2);
     190                                                }
     191                                        } while (++$i < $max_i && substr($diff[$i], 0, 1) == '!');
     192                                        $edits[] = &new Text_Diff_Op_change($diff1, $diff2);
     193                                        break;
    194194
    195                 case '+':
    196                     do {
    197                         $diff1[] = substr($diff[$i], 2);
    198                     } while (++$i < $max_i && substr($diff[$i], 0, 1) == '+');
    199                     $edits[] = &new Text_Diff_Op_add($diff1);
    200                     break;
     195                                case '+':
     196                                        do {
     197                                                $diff1[] = substr($diff[$i], 2);
     198                                        } while (++$i < $max_i && substr($diff[$i], 0, 1) == '+');
     199                                        $edits[] = &new Text_Diff_Op_add($diff1);
     200                                        break;
    201201
    202                 case '-':
    203                     do {
    204                         $diff1[] = substr($diff[$i], 2);
    205                     } while (++$i < $max_i && substr($diff[$i], 0, 1) == '-');
    206                     $edits[] = &new Text_Diff_Op_delete($diff1);
    207                     break;
    208                 }
    209             }
     202                                case '-':
     203                                        do {
     204                                                $diff1[] = substr($diff[$i], 2);
     205                                        } while (++$i < $max_i && substr($diff[$i], 0, 1) == '-');
     206                                        $edits[] = &new Text_Diff_Op_delete($diff1);
     207                                        break;
     208                                }
     209                        }
    210210
    211             if ($j < $max_j) {
    212                 $diff2 = array();
    213                 switch (substr($diff[$j], 0, 1)) {
    214                 case '+':
    215                     do {
    216                         $diff2[] = substr($diff[$j++], 2);
    217                     } while ($j < $max_j && substr($diff[$j], 0, 1) == '+');
    218                     $edits[] = &new Text_Diff_Op_add($diff2);
    219                     break;
     211                        if ($j < $max_j) {
     212                                $diff2 = array();
     213                                switch (substr($diff[$j], 0, 1)) {
     214                                case '+':
     215                                        do {
     216                                                $diff2[] = substr($diff[$j++], 2);
     217                                        } while ($j < $max_j && substr($diff[$j], 0, 1) == '+');
     218                                        $edits[] = &new Text_Diff_Op_add($diff2);
     219                                        break;
    220220
    221                 case '-':
    222                     do {
    223                         $diff2[] = substr($diff[$j++], 2);
    224                     } while ($j < $max_j && substr($diff[$j], 0, 1) == '-');
    225                     $edits[] = &new Text_Diff_Op_delete($diff2);
    226                     break;
    227                 }
    228             }
    229         }
     221                                case '-':
     222                                        do {
     223                                                $diff2[] = substr($diff[$j++], 2);
     224                                        } while ($j < $max_j && substr($diff[$j], 0, 1) == '-');
     225                                        $edits[] = &new Text_Diff_Op_delete($diff2);
     226                                        break;
     227                                }
     228                        }
     229                }
    230230
    231         return $edits;
    232     }
     231                return $edits;
     232        }
    233233
    234234}
  • Text/Diff/Engine/xdiff.php

     
    1717 */
    1818class Text_Diff_Engine_xdiff {
    1919
    20     /**
    21     */
    22     function diff($from_lines, $to_lines)
    23     {
    24         array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
    25         array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
     20        /**
     21        */
     22        function diff($from_lines, $to_lines)
     23        {
     24                array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
     25                array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
    2626
    27         /* Convert the two input arrays into strings for xdiff processing. */
    28         $from_string = implode("\n", $from_lines);
    29         $to_string = implode("\n", $to_lines);
     27                /* Convert the two input arrays into strings for xdiff processing. */
     28                $from_string = implode("\n", $from_lines);
     29                $to_string = implode("\n", $to_lines);
    3030
    31         /* Diff the two strings and convert the result to an array. */
    32         $diff = xdiff_string_diff($from_string, $to_string, count($to_lines));
    33         $diff = explode("\n", $diff);
     31                /* Diff the two strings and convert the result to an array. */
     32                $diff = xdiff_string_diff($from_string, $to_string, count($to_lines));
     33                $diff = explode("\n", $diff);
    3434
    35         /* Walk through the diff one line at a time.  We build the $edits
    36         * array of diff operations by reading the first character of the
    37         * xdiff output (which is in the "unified diff" format).
    38         *
    39         * Note that we don't have enough information to detect "changed"
    40         * lines using this approach, so we can't add Text_Diff_Op_changed
    41         * instances to the $edits array.  The result is still perfectly
    42         * valid, albeit a little less descriptive and efficient. */
    43         $edits = array();
    44         foreach ($diff as $line) {
    45             switch ($line[0]) {
    46             case ' ':
    47                 $edits[] = &new Text_Diff_Op_copy(array(substr($line, 1)));
    48                 break;
     35                /* Walk through the diff one line at a time.  We build the $edits
     36                * array of diff operations by reading the first character of the
     37                * xdiff output (which is in the "unified diff" format).
     38                *
     39                * Note that we don't have enough information to detect "changed"
     40                * lines using this approach, so we can't add Text_Diff_Op_changed
     41                * instances to the $edits array.  The result is still perfectly
     42                * valid, albeit a little less descriptive and efficient. */
     43                $edits = array();
     44                foreach ($diff as $line) {
     45                        switch ($line[0]) {
     46                        case ' ':
     47                                $edits[] = &new Text_Diff_Op_copy(array(substr($line, 1)));
     48                                break;
    4949
    50             case '+':
    51                 $edits[] = &new Text_Diff_Op_add(array(substr($line, 1)));
    52                 break;
     50                        case '+':
     51                                $edits[] = &new Text_Diff_Op_add(array(substr($line, 1)));
     52                                break;
    5353
    54             case '-':
    55                 $edits[] = &new Text_Diff_Op_delete(array(substr($line, 1)));
    56                 break;
    57             }
    58         }
     54                        case '-':
     55                                $edits[] = &new Text_Diff_Op_delete(array(substr($line, 1)));
     56                                break;
     57                        }
     58                }
    5959
    60         return $edits;
    61     }
     60                return $edits;
     61        }
    6262
    6363}
  • Text/Diff/Renderer.php

     
    1616 */
    1717class Text_Diff_Renderer {
    1818
    19     /**
    20     * Number of leading context "lines" to preserve.
    21     *
    22     * This should be left at zero for this class, but subclasses may want to
    23     * set this to other values.
    24     */
    25     var $_leading_context_lines = 0;
     19        /**
     20        * Number of leading context "lines" to preserve.
     21        *
     22        * This should be left at zero for this class, but subclasses may want to
     23        * set this to other values.
     24        */
     25        var $_leading_context_lines = 0;
    2626
    27     /**
    28     * Number of trailing context "lines" to preserve.
    29     *
    30     * This should be left at zero for this class, but subclasses may want to
    31     * set this to other values.
    32     */
    33     var $_trailing_context_lines = 0;
     27        /**
     28        * Number of trailing context "lines" to preserve.
     29        *
     30        * This should be left at zero for this class, but subclasses may want to
     31        * set this to other values.
     32        */
     33        var $_trailing_context_lines = 0;
    3434
    35     /**
    36     * Constructor.
    37     */
    38     function Text_Diff_Renderer($params = array())
    39     {
    40         foreach ($params as $param => $value) {
    41             $v = '_' . $param;
    42             if (isset($this->$v)) {
    43                 $this->$v = $value;
    44             }
    45         }
    46     }
     35        /**
     36        * Constructor.
     37        */
     38        function Text_Diff_Renderer($params = array())
     39        {
     40                foreach ($params as $param => $value) {
     41                        $v = '_' . $param;
     42                        if (isset($this->$v)) {
     43                                $this->$v = $value;
     44                        }
     45                }
     46        }
    4747
    48     /**
    49     * Get any renderer parameters.
    50     *
    51     * @return array  All parameters of this renderer object.
    52     */
    53     function getParams()
    54     {
    55         $params = array();
    56         foreach (get_object_vars($this) as $k => $v) {
    57             if ($k[0] == '_') {
    58                 $params[substr($k, 1)] = $v;
    59             }
    60         }
     48        /**
     49        * Get any renderer parameters.
     50        *
     51        * @return array  All parameters of this renderer object.
     52        */
     53        function getParams()
     54        {
     55                $params = array();
     56                foreach (get_object_vars($this) as $k => $v) {
     57                        if ($k[0] == '_') {
     58                                $params[substr($k, 1)] = $v;
     59                        }
     60                }
    6161
    62         return $params;
    63     }
     62                return $params;
     63        }
    6464
    65     /**
    66     * Renders a diff.
    67     *
    68     * @param Text_Diff $diff  A Text_Diff object.
    69     *
    70     * @return string  The formatted output.
    71     */
    72     function render($diff)
    73     {
    74         $xi = $yi = 1;
    75         $block = false;
    76         $context = array();
     65        /**
     66        * Renders a diff.
     67        *
     68        * @param Text_Diff $diff  A Text_Diff object.
     69        *
     70        * @return string  The formatted output.
     71        */
     72        function render($diff)
     73        {
     74                $xi = $yi = 1;
     75                $block = false;
     76                $context = array();
    7777
    78         $nlead = $this->_leading_context_lines;
    79         $ntrail = $this->_trailing_context_lines;
     78                $nlead = $this->_leading_context_lines;
     79                $ntrail = $this->_trailing_context_lines;
    8080
    81         $output = $this->_startDiff();
     81                $output = $this->_startDiff();
    8282
    83         $diffs = $diff->getDiff();
    84         foreach ($diffs as $i => $edit) {
    85             /* If these are unchanged (copied) lines, and we want to keep
    86             * leading or trailing context lines, extract them from the copy
    87             * block. */
    88             if (is_a($edit, 'Text_Diff_Op_copy')) {
    89                 /* Do we have any diff blocks yet? */
    90                 if (is_array($block)) {
    91                     /* How many lines to keep as context from the copy
    92                     * block. */
    93                     $keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail;
    94                     if (count($edit->orig) <= $keep) {
    95                         /* We have less lines in the block than we want for
    96                         * context => keep the whole block. */
    97                         $block[] = $edit;
    98                     } else {
    99                         if ($ntrail) {
    100                             /* Create a new block with as many lines as we need
    101                             * for the trailing context. */
    102                             $context = array_slice($edit->orig, 0, $ntrail);
    103                             $block[] = &new Text_Diff_Op_copy($context);
    104                         }
    105                         /* @todo */
    106                         $output .= $this->_block($x0, $ntrail + $xi - $x0,
    107                                                 $y0, $ntrail + $yi - $y0,
    108                                                 $block);
    109                         $block = false;
    110                     }
    111                 }
    112                 /* Keep the copy block as the context for the next block. */
    113                 $context = $edit->orig;
    114             } else {
    115                 /* Don't we have any diff blocks yet? */
    116                 if (!is_array($block)) {
    117                     /* Extract context lines from the preceding copy block. */
    118                     $context = array_slice($context, count($context) - $nlead);
    119                     $x0 = $xi - count($context);
    120                     $y0 = $yi - count($context);
    121                     $block = array();
    122                     if ($context) {
    123                         $block[] = &new Text_Diff_Op_copy($context);
    124                     }
    125                 }
    126                 $block[] = $edit;
    127             }
     83                $diffs = $diff->getDiff();
     84                foreach ($diffs as $i => $edit) {
     85                        /* If these are unchanged (copied) lines, and we want to keep
     86                        * leading or trailing context lines, extract them from the copy
     87                        * block. */
     88                        if (is_a($edit, 'Text_Diff_Op_copy')) {
     89                                /* Do we have any diff blocks yet? */
     90                                if (is_array($block)) {
     91                                        /* How many lines to keep as context from the copy
     92                                        * block. */
     93                                        $keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail;
     94                                        if (count($edit->orig) <= $keep) {
     95                                                /* We have less lines in the block than we want for
     96                                                * context => keep the whole block. */
     97                                                $block[] = $edit;
     98                                        } else {
     99                                                if ($ntrail) {
     100                                                        /* Create a new block with as many lines as we need
     101                                                        * for the trailing context. */
     102                                                        $context = array_slice($edit->orig, 0, $ntrail);
     103                                                        $block[] = &new Text_Diff_Op_copy($context);
     104                                                }
     105                                                /* @todo */
     106                                                $output .= $this->_block($x0, $ntrail + $xi - $x0,
     107                                                                                                $y0, $ntrail + $yi - $y0,
     108                                                                                                $block);
     109                                                $block = false;
     110                                        }
     111                                }
     112                                /* Keep the copy block as the context for the next block. */
     113                                $context = $edit->orig;
     114                        } else {
     115                                /* Don't we have any diff blocks yet? */
     116                                if (!is_array($block)) {
     117                                        /* Extract context lines from the preceding copy block. */
     118                                        $context = array_slice($context, count($context) - $nlead);
     119