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

Documentation generated on Mon, 11 Mar 2019 14:36:42 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.