File
[ class tree: File ] [ index: File ] [ all elements ]

Source for file Find.php

Documentation is available at Find.php

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2005 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license,      |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Author: Sterling Hughes <sterling@php.net>                           |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // $Id$
  20. //
  21.  
  22. require_once 'PEAR.php';
  23.  
  24. define('FILE_FIND_VERSION''@package_version@');
  25.  
  26. // to debug uncomment this string
  27. // define('FILE_FIND_DEBUG', '');
  28.  
  29. /**
  30. *  Commonly needed functions searching directory trees
  31. *
  32. @access public
  33. @version $Id$
  34. @package File
  35. @author Sterling Hughes <sterling@php.net>
  36. */
  37. class File_Find
  38. {
  39.     /**
  40.      * internal dir-list
  41.      * @var array 
  42.      */
  43.     var $_dirs = array();
  44.  
  45.     /**
  46.      * directory separator
  47.      * @var string 
  48.      */
  49.     var $dirsep = "/";
  50.  
  51.     /**
  52.      * found files
  53.      * @var array 
  54.      */
  55.     var $files = array();
  56.  
  57.     /**
  58.      * found dirs
  59.      * @var array 
  60.      */
  61.     var $directories = array();
  62.  
  63.     /**
  64.      * Search specified directory to find matches for specified pattern
  65.      *
  66.      * @param string $pattern a string containing the pattern to search
  67.      *  the directory for.
  68.      *
  69.      * @param string $dirpath a string containing the directory path
  70.      *  to search.
  71.      *
  72.      * @param string $pattern_type a string containing the type of
  73.      *  pattern matching functions to use (can either be 'php',
  74.      *  'perl' or 'shell').
  75.      *
  76.      * @return array containing all of the files and directories
  77.      *  matching the pattern or null if no matches
  78.      *
  79.      * @author Sterling Hughes <sterling@php.net>
  80.      * @access public
  81.      * @static
  82.      */
  83.     function &glob($pattern$dirpath$pattern_type 'php')
  84.     {
  85.         $dh @opendir($dirpath);
  86.  
  87.         if (!$dh{
  88.             $pe = PEAR::raiseError("Cannot open directory $dirpath");
  89.             return $pe;
  90.         }
  91.  
  92.         $match_function File_Find::_determineRegex($pattern$pattern_type);
  93.         $matches = array();
  94.  
  95.         // empty string cannot be specified for 'php' and 'perl' pattern
  96.         if ($pattern || ($pattern_type != 'php' && $pattern_type != 'perl')) {
  97.             while (false !== ($entry @readdir($dh))) {
  98.                 if ($match_function($pattern$entry&&
  99.                     $entry != '.' && $entry != '..'{
  100.                     $matches[$entry;
  101.                 }
  102.             }
  103.         }
  104.  
  105.         @closedir($dh);
  106.  
  107.         if (0 == count($matches)) {
  108.             $matches = null;
  109.         }
  110.  
  111.         sort($matches);
  112.  
  113.         return $matches ;
  114.     }
  115.  
  116.     /**
  117.      * Map the directory tree given by the directory_path parameter.
  118.      *
  119.      * @param string $directory contains the directory path that you
  120.      *  want to map.
  121.      *
  122.      * @return array a two element array, the first element containing a list
  123.      *  of all the directories, the second element containing a list of all the
  124.      *  files.
  125.      *
  126.      * @author Sterling Hughes <sterling@php.net>
  127.      * @access public
  128.      */
  129.     function &maptree($directory)
  130.     {
  131.  
  132.         /* if called statically */
  133.         if (!isset($this)  || !is_a($this"File_Find")) {
  134.             $obj = new File_Find();
  135.             return $obj->maptree($directory);
  136.         }
  137.       
  138.         /* clear the results just in case */
  139.         $this->files       = array();
  140.         $this->directories = array();
  141.  
  142.         /* strip out trailing slashes */
  143.         $directory preg_replace('![\\\\/]+$!'''$directory);
  144.  
  145.         $this->_dirs = array($directory);
  146.  
  147.         while (count($this->_dirs)) {
  148.             $dir array_pop($this->_dirs);
  149.             File_Find::_build($dir$this->dirsep);
  150.             array_push($this->directories$dir);
  151.         }
  152.  
  153.         sort($this->directories);
  154.         sort($this->files);
  155.  
  156.         $retval = array($this->directories$this->files);
  157.         return $retval;
  158.  
  159.     }
  160.  
  161.     /**
  162.      * Map the directory tree given by the directory parameter.
  163.      *
  164.      * @param string $directory contains the directory path that you
  165.      *  want to map.
  166.      * @param integer $maxrecursion maximun number of folders to recursive
  167.      *  map
  168.      *
  169.      * @return array a multidimensional array containing all subdirectories
  170.      *  and their files. For example:
  171.      *
  172.      *  Array
  173.      *  (
  174.      *     [0] => file_1.php
  175.      *     [1] => file_2.php
  176.      *     [subdirname] => Array
  177.      *        (
  178.      *           [0] => file_1.php
  179.      *        )
  180.      *  )
  181.      *
  182.      * @author Mika Tuupola <tuupola@appelsiini.net>
  183.      * @access public
  184.      * @static
  185.      */
  186.     function &mapTreeMultiple($directory$maxrecursion = 0$count = 0)
  187.     {   
  188.         $retval = array();
  189.  
  190.         $count++;
  191.  
  192.         /* strip trailing slashes */
  193.         $directory preg_replace('![\\\\/]+$!'''$directory);
  194.         
  195.         if (is_readable($directory)) {
  196.             $dh opendir($directory);
  197.             while (false !== ($entry @readdir($dh))) {
  198.                 if ($entry != '.' && $entry != '..'{
  199.                      array_push($retval$entry);
  200.                 }
  201.             }
  202.             closedir($dh);
  203.         }
  204.  
  205.         sort($retval);
  206.  
  207.         while (list($key$valeach($retval)) {
  208.       
  209.             if (!is_array($val)) {
  210.                 $path $directory "/" $val;
  211.                 if (is_dir($path)) {
  212.                     unset($retval[$key]);
  213.                     if ($maxrecursion == 0 || $count $maxrecursion{
  214.                         $retval[$val&File_Find::mapTreeMultiple($path
  215.                                         $maxrecursion$count);
  216.                     }
  217.                 }
  218.             }
  219.         }
  220.  
  221.         return $retval;
  222.     }
  223.  
  224.     /**
  225.      * Search the specified directory tree with the specified pattern.  Return
  226.      * an array containing all matching files (no directories included).
  227.      *
  228.      * @param string $pattern the pattern to match every file with.
  229.      *
  230.      * @param string $directory the directory tree to search in.
  231.      *
  232.      * @param string $type the type of regular expression support to use, either
  233.      *  'php', 'perl' or 'shell'.
  234.      *
  235.      * @param bool $fullpath whether the regex should be matched against the
  236.      *  full path or only against the filename
  237.      *
  238.      * @param string $match can be either 'files', 'dirs' or 'both' to specify
  239.      *  the kind of list to return
  240.      *
  241.      * @return array a list of files matching the pattern parameter in the the
  242.      *  directory path specified by the directory parameter
  243.      *
  244.      * @author Sterling Hughes <sterling@php.net>
  245.      * @access public
  246.      * @static
  247.      */
  248.     function &search($pattern$directory$type 'php'$fullpath = true$match 'files')
  249.     {
  250.  
  251.         $matches = array();
  252.         list ($directories,$files)  File_Find::maptree($directory);
  253.         switch($match{
  254.             case 'directories'
  255.                 $data $directories
  256.                 break;
  257.             case 'both'
  258.                 $data array_merge($directories$files)
  259.                 break;
  260.             case 'files':
  261.             default:
  262.                 $data $files;
  263.         }
  264.         unset($files$directories);
  265.  
  266.         $match_function File_Find::_determineRegex($pattern$type);
  267.  
  268.         reset($data);
  269.         // check if empty string given (ok for 'shell' method, but bad for others)
  270.         if ($pattern || ($type != 'php' && $type != 'perl')) {
  271.             while (list(,$entryeach($data)) {
  272.                 if ($match_function($pattern
  273.                                     $fullpath $entry basename($entry))) {
  274.                     $matches[$entry;
  275.                 
  276.             }
  277.         }
  278.  
  279.         sort($matches);
  280.  
  281.         return $matches;
  282.     }
  283.  
  284.     /**
  285.      * Determine whether or not a variable is a PEAR error
  286.      *
  287.      * @param object PEAR_Error $var the variable to test.
  288.      *
  289.      * @return boolean returns true if the variable is a PEAR error, otherwise
  290.      *  it returns false.
  291.      * @access public
  292.      */
  293.     function isError(&$var)
  294.     {
  295.         return PEAR::isError($var);
  296.     }
  297.  
  298.     /**
  299.      * internal function to build singular directory trees, used by
  300.      * File_Find::maptree()
  301.      *
  302.      * @param string $directory name of the directory to read
  303.      * @param string $separator directory separator
  304.      * @return void 
  305.      */
  306.     function _build($directory$separator "/")
  307.     {
  308.  
  309.         $dh @opendir($directory);
  310.  
  311.         if (!$dh{
  312.             $pe = PEAR::raiseError("Cannot open directory");
  313.             return $pe;
  314.         }
  315.  
  316.         while (false !== ($entry @readdir($dh))) {
  317.             if ($entry != '.' && $entry != '..'{
  318.  
  319.                 $entry $directory.$separator.$entry;
  320.  
  321.                 if (is_dir($entry)) {
  322.                     array_push($this->_dirs$entry);
  323.                 else {
  324.                     array_push($this->files$entry);
  325.                 }
  326.             }
  327.         }
  328.  
  329.         @closedir($dh);
  330.     }
  331.  
  332.     /**
  333.      * internal function to determine the type of regular expression to
  334.      * use, implemented by File_Find::glob() and File_Find::search()
  335.      *
  336.      * @param string $type given RegExp type
  337.      * @return string kind of function ( "eregi", "ereg" or "preg_match") ;
  338.      *
  339.      */
  340.     function _determineRegex($pattern$type)
  341.     {
  342.         if (!strcasecmp($type'shell')) {
  343.             $match_function 'File_Find_match_shell';
  344.         else if (!strcasecmp($type'perl')) {
  345.             $match_function 'preg_match';
  346.         else if (!strcasecmp(substr($pattern-2)'/i')) {
  347.             $match_function 'eregi';
  348.         else {
  349.             $match_function 'ereg';
  350.         }
  351.         return $match_function;
  352.     }
  353.  
  354. }
  355.  
  356. /**
  357. * Package method to match via 'shell' pattern. Provided in global
  358. * scope, because it should be called like 'preg_match' and 'eregi'
  359. * and can be easily copied into other packages
  360. *
  361. @author techtonik <techtonik@php.net>
  362. @return mixed bool on success and PEAR_Error on failure
  363. */ 
  364. function File_Find_match_shell($pattern$filename)
  365. {
  366.     // {{{ convert pattern to positive and negative regexps
  367.         $positive $pattern;
  368.         $negation substr_count($pattern"|");
  369.  
  370.         if ($negation > 1{
  371.             PEAR::raiseError("Mask string contains errors!");
  372.             return FALSE;
  373.         elseif ($negation{
  374.             list($positive$negativeexplode("|"$pattern);
  375.             if (strlen($negative== 0{
  376.                 PEAR::raiseError("File-mask string contains errors!");
  377.                 return FALSE;
  378.             }
  379.         }
  380.  
  381.        $positive _File_Find_match_shell_get_pattern($positive);
  382.        if ($negation{
  383.            $negative _File_Find_match_shell_get_pattern($negative);
  384.        }
  385.     // }}} convert end 
  386.  
  387.  
  388.     if (defined("FILE_FIND_DEBUG")) {
  389.         print("Method: $type\nPattern: $pattern\n Converted pattern:");
  390.         print_r($positive);
  391.         if (isset($negative)) print_r($negative);
  392.     }
  393.  
  394.     if (!preg_match($positive$filename)) {
  395.         return FALSE;
  396.     else {
  397.         if (isset($negative
  398.               && preg_match($negative$filename)) {
  399.             return FALSE;
  400.         else {
  401.             return TRUE;
  402.         }
  403.     }
  404. }
  405.  
  406. /**
  407. * function used by File_Find_match_shell to convert 'shell' mask
  408. * into pcre regexp. Some of the rules (see testcases for more):
  409. *  escaping all special chars and replacing
  410. *    . with \.
  411. *    * with .*
  412. *    ? with .{1}
  413. *    also adding ^ and $ as the pattern matches whole filename
  414. *
  415. @author techtonik <techtonik@php.net>
  416. @return string pcre regexp for preg_match
  417. */ 
  418.     // get array of several masks (if any) delimited by comma
  419.     // do not touch commas in char class
  420.     $premasks preg_split("|(\[[^\]]+\])|"$mask-1PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
  421.     if (defined("FILE_FIND_DEBUG")) {
  422.         print("\nPremask: ");
  423.         print_r($premasks);
  424.     }
  425.     $pi = 0;
  426.     foreach($premasks as $pm{
  427.         if (!isset($masks[$pi])) $masks[$pi"";
  428.         if ($pm{0== '[' && $pm{strlen($pm)-1== ']'{
  429.             // strip commas from character class
  430.             $masks[$pi.= str_replace(","""$pm);
  431.         else {
  432.             $tarr explode(","$pm);
  433.             if (sizeof($tarr== 1{
  434.                 $masks[$pi.= $pm;
  435.             else {
  436.                 foreach ($tarr as $te{
  437.                     $masks[$pi++.= $te;
  438.                     $masks[$pi"";
  439.                 }
  440.                 unset($masks[$pi--]);
  441.             }
  442.         }
  443.     }
  444.  
  445.     // if empty string given return *.* pattern
  446.     if (strlen($mask== 0return "!^.*$!";
  447.  
  448.     // convert to preg regexp
  449.     $regexmask implode("|"$masks);
  450.     if (defined("FILE_FIND_DEBUG")) {
  451.         print("regexMask step one(implode): $regexmask");
  452.     }
  453.     $regexmask addcslashes($regexmask'^$}!{)(\/.+');
  454.     if (defined("FILE_FIND_DEBUG")) {
  455.         print("\nregexMask step two(addcslashes): $regexmask");
  456.     }
  457.     $regexmask preg_replace("!(\*|\?)!"".$1"$regexmask);
  458.     if (defined("FILE_FIND_DEBUG")) {
  459.         print("\nregexMask step three(* ? -> .* .?): $regexmask");
  460.     }
  461.     // a special case '*.' at the end means that there is no extension
  462.     $regexmask preg_replace("!\.\*\\\.(\||$)!""[^\.]*$1"$regexmask);
  463.     // it is impossible to have dot at the end of filename
  464.     $regexmask preg_replace("!\\\.(\||$)!""$1"$regexmask);
  465.     // and .* at the end also means that there could be nothing at all
  466.     //   (i.e. no dot at the end also)
  467.     $regexmask preg_replace("!\\\.\.\*(\||$)!""(\\\\..*)?$1"$regexmask);
  468.     if (defined("FILE_FIND_DEBUG")) {
  469.         print("\nregexMask step two and half(*.$ \\..*$ .$ -> [^.]*$ .?.* $): $regexmask");
  470.     }
  471.     // if no extension supplied - add .* to match partially from filename start
  472.     if (strpos($regexmask"\\."=== FALSE$regexmask .= ".*";
  473.  
  474.     // file mask match whole name - adding restrictions
  475.     $regexmask preg_replace("!(\|)!"'^'."$1".'$'$regexmask);
  476.     $regexmask '^'.$regexmask.'$';
  477.     if (defined("FILE_FIND_DEBUG")) {
  478.         print("\nregexMask step three(^ and $ to match whole name): $regexmask");
  479.     }
  480.     // wrap regex into ! since all ! are already escaped
  481.     $regexmask = "!$regexmask!i";
  482.     if (defined("FILE_FIND_DEBUG")) {
  483.         print("\nWrapped regex: $regexmask\n");
  484.     }
  485.     return $regexmask;
  486. }
  487.  
  488. /*
  489.  * Local variables:
  490.  * tab-width: 4
  491.  * c-basic-offset: 4
  492.  * End:
  493.  */
  494.  
  495. ?>

Documentation generated on Mon, 11 Mar 2019 15:56:37 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.