home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / File / SearchReplace.php < prev    next >
Encoding:
PHP Script  |  2006-04-07  |  18.0 KB  |  533 lines

  1. <?php
  2. // +-----------------------------------------------------------------------+
  3. // | Copyright (c) 2002-2005, Richard Heyes                                |
  4. // | All rights reserved.                                                  |
  5. // |                                                                       |
  6. // | Redistribution and use in source and binary forms, with or without    |
  7. // | modification, are permitted provided that the following conditions    |
  8. // | are met:                                                              |
  9. // |                                                                       |
  10. // | o Redistributions of source code must retain the above copyright      |
  11. // |   notice, this list of conditions and the following disclaimer.       |
  12. // | o Redistributions in binary form must reproduce the above copyright   |
  13. // |   notice, this list of conditions and the following disclaimer in the |
  14. // |   documentation and/or other materials provided with the distribution.|
  15. // | o The names of the authors may not be used to endorse or promote      |
  16. // |   products derived from this software without specific prior written  |
  17. // |   permission.                                                         |
  18. // |                                                                       |
  19. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
  20. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
  21. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
  22. // | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
  23. // | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
  24. // | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
  25. // | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
  26. // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
  27. // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
  28. // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
  29. // | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
  30. // |                                                                       |
  31. // +-----------------------------------------------------------------------+
  32. // | Author: Richard Heyes <richard@phpguru.org>                           |
  33. // +-----------------------------------------------------------------------+
  34. //
  35. // $Id: SearchReplace.php,v 1.12 2005/03/11 20:32:40 techtonik Exp $
  36. //
  37. // Search and Replace Utility
  38. //
  39.  
  40. /**
  41.  * Search and Replace Utility
  42.  *
  43.  *
  44.  * @author  Richard Heyes <richard@phpguru.org>
  45.  * @version 1.0
  46.  * @package File
  47.  */
  48. class File_SearchReplace
  49. {
  50.     
  51.     // {{{ Properties (All private)
  52.  
  53.     var $find;
  54.     var $replace;
  55.     var $files;
  56.     var $directories;
  57.     var $include_subdir;
  58.     var $ignore_lines;
  59.     var $ignore_sep;
  60.     var $occurences;
  61.     var $search_function;
  62.     var $php5;
  63.     var $last_error;
  64.  
  65.     // }}}
  66.     // {{{ Constructor
  67.  
  68.     /**
  69.      * Sets up the object
  70.      *
  71.      * @access public
  72.      * @param string $find                      The string/regex to find.
  73.      * @param string $replace                   The string/regex to replace $find with.
  74.      * @param array  $files                     The file(s) to perform this operation on.
  75.      * @param array  $directories    (optional) The directories to perform this operation on.
  76.      * @param bool   $include_subdir            If performing on directories, whether to traverse subdirectories.
  77.      * @param array  $ignore_lines              Ignore lines beginning with any of the strings in this array. This
  78.      *                                          feature only works with the "normal" search.
  79.      */
  80.     function File_SearchReplace($find, $replace, $files, $directories = '', $include_subdir = TRUE, $ignore_lines = array())
  81.     {
  82.  
  83.         $this->find            = $find;
  84.         $this->replace         = $replace;
  85.         $this->files           = $files;
  86.         $this->directories     = $directories;
  87.         $this->include_subdir  = $include_subdir;
  88.         $this->ignore_lines    = (array) $ignore_lines;
  89.  
  90.         $this->occurences      = 0;
  91.         $this->search_function = 'search';
  92.         $this->php5            = (substr(PHP_VERSION, 0, 1) == 5) ? TRUE : FALSE;
  93.         $this->last_error      = '';
  94.  
  95.     }
  96.  
  97.     // }}}
  98.     // {{{ getNumOccurences()
  99.  
  100.     /**
  101.      * Accessor to return the number of occurences found.
  102.      *
  103.      * @access public
  104.      * @return int Number of occurences found.
  105.      */
  106.     function getNumOccurences()
  107.     {
  108.         return $this->occurences;
  109.     }
  110.  
  111.     // }}}
  112.     // {{{ getLastError()
  113.  
  114.     /**
  115.      * Accessor for retrieving last error.
  116.      *
  117.      * @access public
  118.      * @return string The last error that occurred, if any.
  119.      */
  120.     function getLastError()
  121.     {
  122.         return $this->last_error;
  123.     }
  124.  
  125.     // }}}
  126.     // {{{ setFind()
  127.  
  128.     /**
  129.      * Accessor for setting find variable.
  130.      *
  131.      * @access public
  132.      * @param string $find The string/regex to find.
  133.      */
  134.     function setFind($find)
  135.     {
  136.         $this->find = $find;
  137.     }
  138.  
  139.     // }}}
  140.     // {{{ setReplace()
  141.  
  142.     /**
  143.      * Accessor for setting replace variable.
  144.      *
  145.      * @access public
  146.      * @param string $replace The string/regex to replace the find string/regex with.
  147.      */
  148.     function setReplace($replace)
  149.     {
  150.         $this->replace = $replace;
  151.     }
  152.  
  153.     // }}}
  154.     // {{{ setFiles()
  155.  
  156.     /**
  157.      * Accessor for setting files variable.
  158.      *
  159.      * @access public
  160.      * @param array $files The file(s) to perform this operation on.
  161.      */
  162.     function setFiles($files)
  163.     {
  164.         $this->files = $files;
  165.     }
  166.  
  167.     // }}}
  168.     // {{{ setDirectories()
  169.  
  170.     /**
  171.      * Accessor for setting directories variable.
  172.      *
  173.      * @access public
  174.      * @param array $directories The directories to perform this operation on.
  175.      */
  176.     function setDirectories($directories)
  177.     {
  178.         $this->directories = $directories;
  179.     }
  180.  
  181.     // }}}
  182.     // {{{ setIncludeSubdir
  183.  
  184.     /**
  185.      * Accessor for setting include_subdir variable.
  186.      *
  187.      * @access public
  188.      * @param bool $include_subdir Whether to traverse subdirectories or not.
  189.      */
  190.     function setIncludeSubdir($include_subdir)
  191.     {
  192.         $this->include_subdir = $include_subdir;
  193.     }
  194.  
  195.     // }}}
  196.     // {{{ setIgnoreLines()
  197.  
  198.     /**
  199.      * Accessor for setting ignore_lines variable.
  200.      *
  201.      * @access public
  202.      * @param array $ignore_lines Ignore lines beginning with any of the strings in this array. This
  203.      *                            feature only works with the "normal" search.
  204.      */
  205.     function setIgnoreLines($ignore_lines)
  206.     {
  207.         $this->ignore_lines = $ignore_lines;
  208.     }
  209.  
  210.     // }}}
  211.     // {{{ setSearchFunction()
  212.  
  213.     /**
  214.      * Function to determine which search function is used.
  215.      *
  216.      * @access public
  217.      * @param string The search function that should be used. Can be any one of:
  218.      *               normal - Default search. Goes line by line. Ignore lines feature only works with this type.
  219.      *               quick  - Uses str_replace for straight replacement throughout file. Quickest of the lot.
  220.      *               preg   - Uses preg_replace(), so any regex valid with this function is valid here.
  221.      *               ereg   - Uses ereg_replace(), so any regex valid with this function is valid here.
  222.      */
  223.     function setSearchFunction($search_function)
  224.     {
  225.         switch($search_function) {
  226.         case 'normal': $this->search_function = 'search';
  227.             return TRUE;
  228.             break;
  229.  
  230.         case 'quick' : $this->search_function = 'quickSearch';
  231.             return TRUE;
  232.             break;
  233.  
  234.         case 'preg'  : $this->search_function = 'pregSearch';
  235.             return TRUE;
  236.             break;
  237.  
  238.         case 'ereg'  : $this->search_function = 'eregSearch';
  239.             return TRUE;
  240.             break;
  241.  
  242.         default      : $this->last_error      = 'Invalid search function specified';
  243.             return FALSE;
  244.             break;
  245.         }
  246.     }
  247.  
  248.     // }}}
  249.     // {{{ search()
  250.  
  251.     /**
  252.      * Default ("normal") search routine.
  253.      *
  254.      * @access private
  255.      * @param string $filename The filename to search and replace upon.
  256.      * @return array Will return an array containing the new file contents and the number of occurences.
  257.      *               Will return FALSE if there are no occurences.
  258.      */
  259.     function search($filename)
  260.     {
  261.         $occurences = 0;
  262.         $file_array = file($filename);
  263.  
  264.         if (empty($this->ignore_lines) && $this->php5) { // PHP5 acceleration
  265.             $file_array = str_replace($this->find, $this->replace, $file_array, $occurences);
  266.  
  267.         } else { // str_replace() doesn't return number of occurences in PHP4
  268.                  // so we need to count them manually and/or filter strings
  269.             $ignore_lines_num = count($this->ignore_lines);
  270.  
  271.             // just for the sake of catching occurences
  272.             $local_find    = array_values((array) $this->find);
  273.             $local_replace = (is_array($this->replace)) ? array_values($this->replace) : $this->replace;
  274.  
  275.             for ($i=0; $i < count($file_array); $i++) {
  276.  
  277.                 if ($ignore_lines_num > 0) {
  278.                     for ($j=0; $j < $ignore_lines_num; $j++) {
  279.                         if (substr($file_array[$i],0,strlen($this->ignore_lines[$j])) == $this->ignore_lines[$j]) continue 2;
  280.                     }
  281.                 }
  282.  
  283.                 if ($this->php5) {
  284.                     $file_array[$i] = str_replace($this->find, $this->replace, $file_array[$i], $counted);
  285.                     $occurences += $counted;
  286.                 } else {
  287.                     foreach ($local_find as $fk => $ff) {
  288.                         $occurences += substr_count($file_array[$i], $ff);
  289.                         if (!is_array($local_replace)) {
  290.                             $fr = $local_replace;
  291.                         } else {
  292.                             $fr = (isset($local_replace[$fk])) ? $local_replace[$fk] : "";
  293.                         }
  294.                         $file_array[$i] = str_replace($ff, $fr, $file_array[$i]);
  295.                     }
  296.                 }
  297.             }
  298.  
  299.         }
  300.         if ($occurences > 0) $return = array($occurences, implode('', $file_array)); else $return = FALSE;
  301.         return $return;
  302.  
  303.     }
  304.  
  305.     // }}}
  306.     // {{{ quickSearch()
  307.  
  308.     /**
  309.      * Quick search routine.
  310.      *
  311.      * @access private
  312.      * @param string $filename The filename to search and replace upon.
  313.      * @return array Will return an array containing the new file contents and the number of occurences.
  314.      *               Will return FALSE if there are no occurences.
  315.      */
  316.     function quickSearch($filename)
  317.     {
  318.  
  319.         clearstatcache();
  320.  
  321.         $file          = fread($fp = fopen($filename, 'r'), filesize($filename)); fclose($fp);
  322.         $local_find    = array_values((array) $this->find);
  323.         $local_replace = (is_array($this->replace)) ? array_values($this->replace) : $this->replace;
  324.  
  325.         $occurences    = 0;
  326.  
  327.         // logic is the same as in str_replace function with one exception:
  328.         //   if <search> is a string and <replacement> is an array - substitution
  329.         //   is done from the first element of array. str_replace in this case
  330.         //   usualy fails with notice and returns "ArrayArrayArray..." string
  331.         // (this exclusive logic of SearchReplace will not work for php5, though,
  332.         // because I haven't decided yet whether it is bug or feature)
  333.  
  334.         if ($this->php5) {
  335.             $file_array[$i] = str_replace($this->find, $this->replace, $file_array[$i], $counted);
  336.             $occurences += $counted;
  337.         } else {
  338.             foreach ($local_find as $fk => $ff) {
  339.                 $occurences += substr_count($file, $ff);
  340.                 if (!is_array($local_replace)) {
  341.                     $fr = $local_replace;
  342.                 } else {
  343.                     $fr = (isset($local_replace[$fk])) ? $local_replace[$fk] : "";
  344.                 }
  345.                 $file = str_replace($ff, $fr, $file);
  346.             }
  347.         }
  348.  
  349.         if ($occurences > 0) $return = array($occurences, $file); else $return = FALSE;
  350.         return $return;
  351.  
  352.     }
  353.  
  354.     // }}}
  355.     // {{{ pregSearch()
  356.  
  357.     /**
  358.      * Preg search routine.
  359.      *
  360.      * @access private
  361.      * @param string $filename The filename to search and replace upon.
  362.      * @return array Will return an array containing the new file contents and the number of occurences.
  363.      *               Will return FALSE if there are no occurences.
  364.      */
  365.     function pregSearch($filename)
  366.     {
  367.  
  368.         clearstatcache();
  369.  
  370.         $file       = fread($fp = fopen($filename, 'r'), filesize($filename)); fclose($fp);
  371.         $local_find    = array_values((array) $this->find);
  372.         $local_replace = (is_array($this->replace)) ? array_values($this->replace) : $this->replace;
  373.  
  374.         $occurences = 0;
  375.  
  376.         foreach($local_find as $fk => $ff) {
  377.             $occurences += preg_match_all($ff, $file, $matches);
  378.             if (!is_array($local_replace)) {
  379.                 $fr = $local_replace;
  380.             } else {
  381.                 $fr = (isset($local_replace[$fk])) ? $local_replace[$fk] : "";
  382.             }
  383.             $file = preg_replace($ff, $fr, $file);
  384.         }
  385.  
  386.         if ($occurences > 0) $return = array($occurences, $file); else $return = FALSE;
  387.         return $return;
  388.  
  389.     }
  390.  
  391.     // }}}
  392.     // {{{ eregSearch()
  393.  
  394.     /**
  395.      * Ereg search routine.
  396.      *
  397.      * @access private
  398.      * @param string $filename The filename to search and replace upon.
  399.      * @return array Will return an array containing the new file contents and the number of occurences.
  400.      *               Will return FALSE if there are no occurences.
  401.      */
  402.     function eregSearch($filename)
  403.     {
  404.  
  405.         clearstatcache();
  406.  
  407.         $file = fread($fp = fopen($filename, 'r'), filesize($filename)); fclose($fp);
  408.         $local_find    = array_values((array) $this->find);
  409.         $local_replace = (is_array($this->replace)) ? array_values($this->replace) : $this->replace;
  410.  
  411.         $occurences = 0;
  412.  
  413.         foreach($local_find as $fk => $ff) {
  414.             $occurences += count(split($ff, $file)) - 1;
  415.             if (!is_array($local_replace)) {
  416.                 $fr = $local_replace;
  417.             } else {
  418.                 $fr = (isset($local_replace[$fk])) ? $local_replace[$fk] : "";
  419.             }
  420.             $file = ereg_replace($ff, $fr, $file);
  421.         }
  422.  
  423.         if ($occurences > 0) $return = array($occurences, $file); else $return = FALSE;
  424.         return $return;
  425.  
  426.     }
  427.  
  428.     // }}}
  429.     // {{{ writeout()
  430.     
  431.     /**
  432.      * Function to writeout the file contents.
  433.      *
  434.      * @access private
  435.      * @param string $filename The filename of the file to write.
  436.      * @param string $contents The contents to write to the file.
  437.      */
  438.     function writeout($filename, $contents)
  439.     {
  440.  
  441.         if ($fp = @fopen($filename, 'w')) {
  442.             flock($fp,2);
  443.             fwrite($fp, $contents);
  444.             flock($fp,3);
  445.             fclose($fp);
  446.         } else {
  447.             $this->last_error = 'Could not open file: '.$filename;
  448.         }
  449.  
  450.     }
  451.  
  452.     // }}}
  453.     // {{{ doFiles()
  454.  
  455.     /**
  456.      * Function called by doSearch() to go through any files that need searching.
  457.      *
  458.      * @access private
  459.      * @param string $ser_func The search function to use.
  460.      */
  461.     function doFiles($ser_func)
  462.     {
  463.         if (!is_array($this->files)) $this->files = explode(',', $this->files);
  464.         for ($i=0; $i<count($this->files); $i++) {
  465.             if ($this->files[$i] == '.' OR $this->files[$i] == '..') continue;
  466.             if (is_dir($this->files[$i]) == TRUE) continue;
  467.             $newfile = $this->$ser_func($this->files[$i]);
  468.             if (is_array($newfile) == TRUE){
  469.                 $this->writeout($this->files[$i], $newfile[1]);
  470.                 $this->occurences += $newfile[0];
  471.             }
  472.         }
  473.     }
  474.  
  475.     // }}}
  476.     // {{{ doDirectories()
  477.  
  478.     /**
  479.      * Function called by doSearch() to go through any directories that need searching.
  480.      *
  481.      * @access private
  482.      * @param string $ser_func The search function to use.
  483.      */
  484.     function doDirectories($ser_func)
  485.     {
  486.         if (!is_array($this->directories)) $this->directories = explode(',', $this->directories);
  487.         for ($i=0; $i<count($this->directories); $i++) {
  488.             $dh = opendir($this->directories[$i]);
  489.             while ($file = readdir($dh)) {
  490.                 if ($file == '.' OR $file == '..') continue;
  491.  
  492.                 if (is_dir($this->directories[$i].$file) == TRUE) {
  493.                     if ($this->include_subdir == TRUE) {
  494.                         $this->directories[] = $this->directories[$i].$file.'/';
  495.                         continue;
  496.                     } else {
  497.                         continue;
  498.                     }
  499.                 }
  500.  
  501.                 $newfile = $this->$ser_func($this->directories[$i].$file);
  502.                 if (is_array($newfile) == TRUE) {
  503.                     $this->writeout($this->directories[$i].$file, $newfile[1]);
  504.                     $this->occurences += $newfile[0];
  505.                 }
  506.             }
  507.         }
  508.     }
  509.  
  510.     // }}}
  511.     // {{{ doSearch()
  512.     
  513.     /**
  514.      * This starts the search/replace off. Call this to do the search.
  515.      * First do whatever files are specified, and/or if directories are specified,
  516.      * do those too.
  517.      *
  518.      * @access public
  519.      */
  520.     function doSearch()
  521.     {
  522.         $this->occurences = 0;
  523.         if ($this->find != '') {
  524.             if ((is_array($this->files) AND count($this->files) > 0) OR $this->files != '') $this->doFiles($this->search_function);
  525.             if ($this->directories != '')                                                   $this->doDirectories($this->search_function);
  526.         }
  527.     }
  528.     
  529.     // }}}
  530.  
  531. }
  532. ?>
  533.