[ Index ] |
|
Code source de Symfony 1.0.0 |
1 <?php 2 /* 3 * $Id: DirectoryScanner.php 3076 2006-12-18 08:52:12Z fabien $ 4 * 5 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 6 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 7 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 8 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 9 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 10 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 11 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 12 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 13 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 14 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 15 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 * 17 * This software consists of voluntary contributions made by many individuals 18 * and is licensed under the LGPL. For more information please see 19 * <http://phing.info>. 20 */ 21 22 require_once 'phing/types/selectors/SelectorScanner.php'; 23 include_once 'phing/util/StringHelper.php'; 24 include_once 'phing/types/selectors/SelectorUtils.php'; 25 26 /** 27 * Class for scanning a directory for files/directories that match a certain 28 * criteria. 29 * 30 * These criteria consist of a set of include and exclude patterns. With these 31 * patterns, you can select which files you want to have included, and which 32 * files you want to have excluded. 33 * 34 * The idea is simple. A given directory is recursively scanned for all files 35 * and directories. Each file/directory is matched against a set of include 36 * and exclude patterns. Only files/directories that match at least one 37 * pattern of the include pattern list, and don't match a pattern of the 38 * exclude pattern list will be placed in the list of files/directories found. 39 * 40 * When no list of include patterns is supplied, "**" will be used, which 41 * means that everything will be matched. When no list of exclude patterns is 42 * supplied, an empty list is used, such that nothing will be excluded. 43 * 44 * The pattern matching is done as follows: 45 * The name to be matched is split up in path segments. A path segment is the 46 * name of a directory or file, which is bounded by DIRECTORY_SEPARATOR 47 * ('/' under UNIX, '\' under Windows). 48 * E.g. "abc/def/ghi/xyz.php" is split up in the segments "abc", "def", "ghi" 49 * and "xyz.php". 50 * The same is done for the pattern against which should be matched. 51 * 52 * Then the segments of the name and the pattern will be matched against each 53 * other. When '**' is used for a path segment in the pattern, then it matches 54 * zero or more path segments of the name. 55 * 56 * There are special case regarding the use of DIRECTORY_SEPARATOR at 57 * the beginning of the pattern and the string to match: 58 * When a pattern starts with a DIRECTORY_SEPARATOR, the string 59 * to match must also start with a DIRECTORY_SEPARATOR. 60 * When a pattern does not start with a DIRECTORY_SEPARATOR, the 61 * string to match may not start with a DIRECTORY_SEPARATOR. 62 * When one of these rules is not obeyed, the string will not 63 * match. 64 * 65 * When a name path segment is matched against a pattern path segment, the 66 * following special characters can be used: 67 * '*' matches zero or more characters, 68 * '?' matches one character. 69 * 70 * Examples: 71 * 72 * "**\*.php" matches all .php files/dirs in a directory tree. 73 * 74 * "test\a??.php" matches all files/dirs which start with an 'a', then two 75 * more characters and then ".php", in a directory called test. 76 * 77 * "**" matches everything in a directory tree. 78 * 79 * "**\test\**\XYZ*" matches all files/dirs that start with "XYZ" and where 80 * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123"). 81 * 82 * Case sensitivity may be turned off if necessary. By default, it is 83 * turned on. 84 * 85 * Example of usage: 86 * $ds = new DirectroyScanner(); 87 * $includes = array("**\*.php"); 88 * $excludes = array("modules\*\**"); 89 * $ds->SetIncludes($includes); 90 * $ds->SetExcludes($excludes); 91 * $ds->SetBasedir("test"); 92 * $ds->SetCaseSensitive(true); 93 * $ds->Scan(); 94 * 95 * print("FILES:"); 96 * $files = ds->GetIncludedFiles(); 97 * for ($i = 0; $i < count($files);$i++) { 98 * println("$files[$i]\n"); 99 * } 100 * 101 * This will scan a directory called test for .php files, but excludes all 102 * .php files in all directories under a directory called "modules" 103 * 104 * This class is complete preg/ereg free port of the Java class 105 * org.apache.tools.ant.DirectoryScanner. Even functions that use preg/ereg 106 * internally (like split()) are not used. Only the _fast_ string functions 107 * and comparison operators (=== !=== etc) are used for matching and tokenizing. 108 * 109 * @author Arnout J. Kuiper, ajkuiper@wxs.nl 110 * @author Magesh Umasankar, umagesh@rediffmail.com 111 * @author Andreas Aderhold, andi@binarycloud.com 112 * 113 * @version $Revision: 1.15 $ 114 * @package phing.util 115 */ 116 class DirectoryScanner implements SelectorScanner { 117 118 /** default set of excludes */ 119 protected $DEFAULTEXCLUDES = array( 120 "**/*~", 121 "**/#*#", 122 "**/.#*", 123 "**/%*%", 124 "**/CVS", 125 "**/CVS/**", 126 "**/.cvsignore", 127 "**/SCCS", 128 "**/SCCS/**", 129 "**/vssver.scc", 130 "**/.svn", 131 "**/.svn/**", 132 "**/._*", 133 "**/.DS_Store", 134 ); 135 136 /** The base directory which should be scanned. */ 137 protected $basedir; 138 139 /** The patterns for the files that should be included. */ 140 protected $includes = null; 141 142 /** The patterns for the files that should be excluded. */ 143 protected $excludes = null; 144 145 /** 146 * The files that where found and matched at least one includes, and matched 147 * no excludes. 148 */ 149 protected $filesIncluded; 150 151 /** The files that where found and did not match any includes. Trie */ 152 protected $filesNotIncluded; 153 154 /** 155 * The files that where found and matched at least one includes, and also 156 * matched at least one excludes. Trie object. 157 */ 158 protected $filesExcluded; 159 160 /** 161 * The directories that where found and matched at least one includes, and 162 * matched no excludes. 163 */ 164 protected $dirsIncluded; 165 166 /** The directories that where found and did not match any includes. */ 167 protected $dirsNotIncluded; 168 169 /** 170 * The files that where found and matched at least one includes, and also 171 * matched at least one excludes. 172 */ 173 protected $dirsExcluded; 174 175 /** Have the vars holding our results been built by a slow scan? */ 176 protected $haveSlowResults = false; 177 178 /** Should the file system be treated as a case sensitive one? */ 179 protected $isCaseSensitive = true; 180 181 /** Selectors */ 182 protected $selectors = null; 183 184 protected $filesDeselected; 185 protected $dirsDeselected; 186 187 /** if there are no deselected files */ 188 protected $everythingIncluded = true; 189 190 /** 191 * Does the path match the start of this pattern up to the first "**". 192 * This is a static mehtod and should always be called static 193 * 194 * This is not a general purpose test and should only be used if you 195 * can live with false positives. 196 * 197 * pattern=**\a and str=b will yield true. 198 * 199 * @param pattern the (non-null) pattern to match against 200 * @param str the (non-null) string (path) to match 201 * @param isCaseSensitive must matches be case sensitive? 202 * @return boolean true if matches, otherwise false 203 */ 204 function matchPatternStart($pattern, $str, $isCaseSensitive = true) { 205 return SelectorUtils::matchPatternStart($pattern, $str, $isCaseSensitive); 206 } 207 208 /** 209 * Matches a path against a pattern. Static 210 * 211 * @param pattern the (non-null) pattern to match against 212 * @param str the (non-null) string (path) to match 213 * @param isCaseSensitive must a case sensitive match be done? 214 * 215 * @return true when the pattern matches against the string. 216 * false otherwise. 217 */ 218 function matchPath($pattern, $str, $isCaseSensitive = true) { 219 return SelectorUtils::matchPath($pattern, $str, $isCaseSensitive); 220 } 221 222 /** 223 * Matches a string against a pattern. The pattern contains two special 224 * characters: 225 * '*' which means zero or more characters, 226 * '?' which means one and only one character. 227 * 228 * @param pattern the (non-null) pattern to match against 229 * @param str the (non-null) string that must be matched against the 230 * pattern 231 * 232 * @return boolean true when the string matches against the pattern, 233 * false otherwise. 234 * @access public 235 */ 236 function match($pattern, $str, $isCaseSensitive = true) { 237 return SelectorUtils::match($pattern, $str, $isCaseSensitive); 238 } 239 240 /** 241 * Sets the basedir for scanning. This is the directory that is scanned 242 * recursively. All '/' and '\' characters are replaced by 243 * DIRECTORY_SEPARATOR 244 * 245 * @param basedir the (non-null) basedir for scanning 246 */ 247 function setBasedir($_basedir) { 248 $_basedir = str_replace('\\', DIRECTORY_SEPARATOR, $_basedir); 249 $_basedir = str_replace('/', DIRECTORY_SEPARATOR, $_basedir); 250 $this->basedir = $_basedir; 251 } 252 253 /** 254 * Gets the basedir that is used for scanning. This is the directory that 255 * is scanned recursively. 256 * 257 * @return the basedir that is used for scanning 258 */ 259 function getBasedir() { 260 return $this->basedir; 261 } 262 263 /** 264 * Sets the case sensitivity of the file system 265 * 266 * @param specifies if the filesystem is case sensitive 267 */ 268 function setCaseSensitive($_isCaseSensitive) { 269 $this->isCaseSensitive = ($_isCaseSensitive) ? true : false; 270 } 271 272 /** 273 * Sets the set of include patterns to use. All '/' and '\' characters are 274 * replaced by DIRECTORY_SEPARATOR. So the separator used need 275 * not match DIRECTORY_SEPARATOR. 276 * 277 * When a pattern ends with a '/' or '\', "**" is appended. 278 * 279 * @param includes list of include patterns 280 */ 281 function setIncludes($_includes = array()) { 282 if (empty($_includes) || is_null($_includes)) { 283 $this->includes = null; 284 } else { 285 for ($i = 0; $i < count($_includes); $i++) { 286 $pattern = null; 287 $pattern = str_replace('\\', DIRECTORY_SEPARATOR, $_includes[$i]); 288 $pattern = str_replace('/', DIRECTORY_SEPARATOR, $pattern); 289 if (StringHelper::endsWith(DIRECTORY_SEPARATOR, $pattern)) { 290 $pattern .= "**"; 291 } 292 $this->includes[] = $pattern; 293 } 294 } 295 } 296 297 /** 298 * Sets the set of exclude patterns to use. All '/' and '\' characters are 299 * replaced by <code>File.separatorChar</code>. So the separator used need 300 * not match <code>File.separatorChar</code>. 301 * 302 * When a pattern ends with a '/' or '\', "**" is appended. 303 * 304 * @param excludes list of exclude patterns 305 */ 306 307 function setExcludes($_excludes = array()) { 308 if (empty($_excludes) || is_null($_excludes)) { 309 $this->excludes = null; 310 } else { 311 for ($i = 0; $i < count($_excludes); $i++) { 312 $pattern = null; 313 $pattern = str_replace('\\', DIRECTORY_SEPARATOR, $_excludes[$i]); 314 $pattern = str_replace('/', DIRECTORY_SEPARATOR, $pattern); 315 if (StringHelper::endsWith(DIRECTORY_SEPARATOR, $pattern)) { 316 $pattern .= "**"; 317 } 318 $this->excludes[] = $pattern; 319 } 320 } 321 } 322 323 /** 324 * Scans the base directory for files that match at least one include 325 * pattern, and don't match any exclude patterns. 326 * 327 */ 328 function scan() { 329 330 if ((empty($this->basedir)) || (!@is_dir($this->basedir))) { 331 return false; 332 } 333 334 if ($this->includes === null) { 335 // No includes supplied, so set it to 'matches all' 336 $this->includes = array("**"); 337 } 338 if (is_null($this->excludes)) { 339 $this->excludes = array(); 340 } 341 342 $this->filesIncluded = array(); 343 $this->filesNotIncluded = array(); 344 $this->filesExcluded = array(); 345 $this->dirsIncluded = array(); 346 $this->dirsNotIncluded = array(); 347 $this->dirsExcluded = array(); 348 $this->dirsDeselected = array(); 349 $this->filesDeselected = array(); 350 351 if ($this->isIncluded("")) { 352 if (!$this->isExcluded("")) { 353 if ($this->isSelected("", $this->basedir)) { 354 $this->dirsIncluded[] = ""; 355 } else { 356 $this->dirsDeselected[] = ""; 357 } 358 } else { 359 $this->dirsExcluded[] = ""; 360 } 361 } else { 362 $this->dirsNotIncluded[] = ""; 363 } 364 365 $this->scandir($this->basedir, "", true); 366 return true; 367 } 368 369 /** 370 * Toplevel invocation for the scan. 371 * 372 * Returns immediately if a slow scan has already been requested. 373 */ 374 protected function slowScan() { 375 376 if ($this->haveSlowResults) { 377 return; 378 } 379 380 // copy trie object add CopyInto() method 381 $excl = $this->dirsExcluded; 382 $notIncl = $this->dirsNotIncluded; 383 384 for ($i=0, $_i=count($excl); $i < $_i; $i++) { 385 if (!$this->couldHoldIncluded($excl[$i])) { 386 $this->scandir($this->basedir.$excl[$i], $excl[$i].DIRECTORY_SEPARATOR, false); 387 } 388 } 389 390 for ($i=0, $_i=count($notIncl); $i < $_i; $i++) { 391 if (!$this->couldHoldIncluded($notIncl[$i])) { 392 $this->scandir($this->basedir.$notIncl[$i], $notIncl[$i].DIRECTORY_SEPARATOR, false); 393 } 394 } 395 396 $this->haveSlowResults = true; 397 } 398 399 /** 400 * Lists contens of a given directory and returns array with entries 401 * 402 * @param src String. Source path and name file to copy. 403 * 404 * @access public 405 * @return array directory entries 406 * @author Albert Lash, alash@plateauinnovation.com 407 */ 408 409 function listDir($_dir) { 410 $d = dir($_dir); 411 $list = array(); 412 while($entry = $d->read()) { 413 if ($entry != "." && $entry != "..") { 414 $list[] = $entry; 415 } 416 } 417 $d->close(); 418 return $list; 419 } 420 421 /** 422 * Scans the passed dir for files and directories. Found files and 423 * directories are placed in their respective collections, based on the 424 * matching of includes and excludes. When a directory is found, it is 425 * scanned recursively. 426 * 427 * @param dir the directory to scan 428 * @param vpath the path relative to the basedir (needed to prevent 429 * problems with an absolute path when using dir) 430 * 431 * @access private 432 * @see #filesIncluded 433 * @see #filesNotIncluded 434 * @see #filesExcluded 435 * @see #dirsIncluded 436 * @see #dirsNotIncluded 437 * @see #dirsExcluded 438 */ 439 private function scandir($_rootdir, $_vpath, $_fast) { 440 441 if (!is_readable($_rootdir)) { 442 return; 443 } 444 445 $newfiles = self::listDir($_rootdir); 446 447 for ($i=0,$_i=count($newfiles); $i < $_i; $i++) { 448 449 $file = $_rootdir . DIRECTORY_SEPARATOR . $newfiles[$i]; 450 $name = $_vpath . $newfiles[$i]; 451 452 if (@is_dir($file)) { 453 if ($this->isIncluded($name)) { 454 if (!$this->isExcluded($name)) { 455 if ($this->isSelected($name, $file)) { 456 $this->dirsIncluded[] = $name; 457 if ($_fast) { 458 $this->scandir($file, $name.DIRECTORY_SEPARATOR, $_fast); 459 } 460 } else { 461 $this->everythingIncluded = false; 462 $this->dirsDeselected[] = $name; 463 if ($_fast && $this->couldHoldIncluded($name)) { 464 $this->scandir($file, $name.DIRECTORY_SEPARATOR, $_fast); 465 } 466 } 467 } else { 468 $this->everythingIncluded = false; 469 $this->dirsExcluded[] = $name; 470 if ($_fast && $this->couldHoldIncluded($name)) { 471 $this->scandir($file, $name.DIRECTORY_SEPARATOR, $_fast); 472 } 473 } 474 } else { 475 $this->everythingIncluded = false; 476 $this->dirsNotIncluded[] = $name; 477 if ($_fast && $this->couldHoldIncluded($name)) { 478 $this->scandir($file, $name.DIRECTORY_SEPARATOR, $_fast); 479 } 480 } 481 482 if (!$_fast) { 483 $this->scandir($file, $name.DIRECTORY_SEPARATOR, $_fast); 484 } 485 486 } elseif (@is_file($file)) { 487 if ($this->isIncluded($name)) { 488 if (!$this->isExcluded($name)) { 489 if ($this->isSelected($name, $file)) { 490 $this->filesIncluded[] = $name; 491 } else { 492 $this->everythingIncluded = false; 493 $this->filesDeselected[] = $name; 494 } 495 } else { 496 $this->everythingIncluded = false; 497 $this->filesExcluded[] = $name; 498 } 499 } else { 500 $this->everythingIncluded = false; 501 $this->filesNotIncluded[] = $name; 502 } 503 } 504 } 505 } 506 507 /** 508 * Tests whether a name matches against at least one include pattern. 509 * 510 * @param name the name to match 511 * @return <code>true</code> when the name matches against at least one 512 * include pattern, <code>false</code> otherwise. 513 */ 514 protected function isIncluded($_name) { 515 for ($i=0, $_i=count($this->includes); $i < $_i; $i++) { 516 if (DirectoryScanner::matchPath($this->includes[$i], $_name, $this->isCaseSensitive)) { 517 return true; 518 } 519 } 520 return false; 521 } 522 523 /** 524 * Tests whether a name matches the start of at least one include pattern. 525 * 526 * @param name the name to match 527 * @return <code>true</code> when the name matches against at least one 528 * include pattern, <code>false</code> otherwise. 529 */ 530 protected function couldHoldIncluded($_name) { 531 for ($i = 0; $i < count($this->includes); $i++) { 532 if (DirectoryScanner::matchPatternStart($this->includes[$i], $_name, $this->isCaseSensitive)) { 533 return true; 534 } 535 } 536 return false; 537 } 538 539 /** 540 * Tests whether a name matches against at least one exclude pattern. 541 * 542 * @param name the name to match 543 * @return <code>true</code> when the name matches against at least one 544 * exclude pattern, <code>false</code> otherwise. 545 */ 546 protected function isExcluded($_name) { 547 for ($i = 0; $i < count($this->excludes); $i++) { 548 if (DirectoryScanner::matchPath($this->excludes[$i], $_name, $this->isCaseSensitive)) { 549 return true; 550 } 551 } 552 return false; 553 } 554 555 /** 556 * Get the names of the files that matched at least one of the include 557 * patterns, and matched none of the exclude patterns. 558 * The names are relative to the basedir. 559 * 560 * @return the names of the files 561 */ 562 function getIncludedFiles() { 563 return $this->filesIncluded; 564 } 565 566 /** 567 * Get the names of the files that matched at none of the include patterns. 568 * The names are relative to the basedir. 569 * 570 * @return the names of the files 571 */ 572 function getNotIncludedFiles() { 573 $this->slowScan(); 574 return $this->filesNotIncluded; 575 } 576 577 /** 578 * Get the names of the files that matched at least one of the include 579 * patterns, an matched also at least one of the exclude patterns. 580 * The names are relative to the basedir. 581 * 582 * @return the names of the files 583 */ 584 585 function getExcludedFiles() { 586 $this->slowScan(); 587 return $this->filesExcluded; 588 } 589 590 /** 591 * <p>Returns the names of the files which were selected out and 592 * therefore not ultimately included.</p> 593 * 594 * <p>The names are relative to the base directory. This involves 595 * performing a slow scan if one has not already been completed.</p> 596 * 597 * @return the names of the files which were deselected. 598 * 599 * @see #slowScan 600 */ 601 public function getDeselectedFiles() { 602 $this->slowScan(); 603 return $this->filesDeselected; 604 } 605 606 /** 607 * Get the names of the directories that matched at least one of the include 608 * patterns, an matched none of the exclude patterns. 609 * The names are relative to the basedir. 610 * 611 * @return the names of the directories 612 */ 613 614 function getIncludedDirectories() { 615 return $this->dirsIncluded; 616 } 617 618 /** 619 * Get the names of the directories that matched at none of the include 620 * patterns. 621 * The names are relative to the basedir. 622 * 623 * @return the names of the directories 624 */ 625 function getNotIncludedDirectories() { 626 $this->slowScan(); 627 return $this->dirsNotIncluded; 628 } 629 630 /** 631 * <p>Returns the names of the directories which were selected out and 632 * therefore not ultimately included.</p> 633 * 634 * <p>The names are relative to the base directory. This involves 635 * performing a slow scan if one has not already been completed.</p> 636 * 637 * @return the names of the directories which were deselected. 638 * 639 * @see #slowScan 640 */ 641 public function getDeselectedDirectories() { 642 $this->slowScan(); 643 return $this->dirsDeselected; 644 } 645 646 /** 647 * Get the names of the directories that matched at least one of the include 648 * patterns, an matched also at least one of the exclude patterns. 649 * The names are relative to the basedir. 650 * 651 * @return the names of the directories 652 */ 653 function getExcludedDirectories() { 654 $this->slowScan(); 655 return $this->dirsExcluded; 656 } 657 658 /** 659 * Adds the array with default exclusions to the current exclusions set. 660 * 661 */ 662 function addDefaultExcludes() { 663 //$excludesLength = ($this->excludes == null) ? 0 : count($this->excludes); 664 foreach($this->DEFAULTEXCLUDES as $pattern) { 665 $pattern = str_replace('\\', DIRECTORY_SEPARATOR, $pattern); 666 $pattern = str_replace('/', DIRECTORY_SEPARATOR, $pattern); 667 $this->excludes[] = $pattern; 668 } 669 } 670 671 /** 672 * Sets the selectors that will select the filelist. 673 * 674 * @param selectors specifies the selectors to be invoked on a scan 675 */ 676 public function setSelectors($selectors) { 677 $this->selectors = $selectors; 678 } 679 680 /** 681 * Returns whether or not the scanner has included all the files or 682 * directories it has come across so far. 683 * 684 * @return <code>true</code> if all files and directories which have 685 * been found so far have been included. 686 */ 687 public function isEverythingIncluded() { 688 return $this->everythingIncluded; 689 } 690 691 /** 692 * Tests whether a name should be selected. 693 * 694 * @param string $name The filename to check for selecting. 695 * @param string $file The full file path. 696 * @return boolean False when the selectors says that the file 697 * should not be selected, True otherwise. 698 */ 699 protected function isSelected($name, $file) { 700 if ($this->selectors !== null) { 701 for ($i=0,$size=count($this->selectors); $i < $size; $i++) { 702 if (($this->selectors[$i]->isSelected(new PhingFile($this->basedir), $name, new PhingFile($file))) === false) { 703 return false; 704 } 705 } 706 } 707 return true; 708 } 709 710 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Fri Mar 16 22:42:14 2007 | par Balluche grâce à PHPXref 0.7 |