Ticket #7760: replace.patch

File replace.patch, 167.8 KB (added by Simek, 5 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