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

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