Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Templating
    • Utils
  • NetteModule
  • none
  • Tracy
    • Bridges
      • Nette

Classes

  • ArrayHash
  • ArrayList
  • Arrays
  • Callback
  • DateTime
  • FileSystem
  • Finder
  • Html
  • Image
  • Json
  • LimitedScope
  • MimeTypeDetector
  • ObjectMixin
  • Paginator
  • Random
  • Strings
  • TokenIterator
  • Tokenizer
  • Validators

Interfaces

  • IHtmlString

Exceptions

  • AssertionException
  • ImageException
  • JsonException
  • RegexpException
  • TokenizerException
  • UnknownImageFileException
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (http://nette.org)
  5:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  6:  */
  7: 
  8: namespace Nette\Utils;
  9: 
 10: use Nette,
 11:     FilesystemIterator,
 12:     RecursiveIteratorIterator;
 13: 
 14: 
 15: /**
 16:  * Finder allows searching through directory trees using iterator.
 17:  *
 18:  * <code>
 19:  * Finder::findFiles('*.php')
 20:  *     ->size('> 10kB')
 21:  *     ->from('.')
 22:  *     ->exclude('temp');
 23:  * </code>
 24:  *
 25:  * @author     David Grudl
 26:  */
 27: class Finder extends Nette\Object implements \IteratorAggregate, \Countable
 28: {
 29:     /** @var array */
 30:     private $paths = array();
 31: 
 32:     /** @var array of filters */
 33:     private $groups;
 34: 
 35:     /** @var filter for recursive traversing */
 36:     private $exclude = array();
 37: 
 38:     /** @var int */
 39:     private $order = RecursiveIteratorIterator::SELF_FIRST;
 40: 
 41:     /** @var int */
 42:     private $maxDepth = -1;
 43: 
 44:     /** @var array */
 45:     private $cursor;
 46: 
 47: 
 48:     /**
 49:      * Begins search for files matching mask and all directories.
 50:      * @param  mixed
 51:      * @return Finder
 52:      */
 53:     public static function find($mask)
 54:     {
 55:         if (!is_array($mask)) {
 56:             $mask = func_get_args();
 57:         }
 58:         $finder = new static;
 59:         return $finder->select($mask, 'isDir')->select($mask, 'isFile');
 60:     }
 61: 
 62: 
 63:     /**
 64:      * Begins search for files matching mask.
 65:      * @param  mixed
 66:      * @return Finder
 67:      */
 68:     public static function findFiles($mask)
 69:     {
 70:         if (!is_array($mask)) {
 71:             $mask = func_get_args();
 72:         }
 73:         $finder = new static;
 74:         return $finder->select($mask, 'isFile');
 75:     }
 76: 
 77: 
 78:     /**
 79:      * Begins search for directories matching mask.
 80:      * @param  mixed
 81:      * @return Finder
 82:      */
 83:     public static function findDirectories($mask)
 84:     {
 85:         if (!is_array($mask)) {
 86:             $mask = func_get_args();
 87:         }
 88:         $finder = new static;
 89:         return $finder->select($mask, 'isDir');
 90:     }
 91: 
 92: 
 93:     /**
 94:      * Creates filtering group by mask & type selector.
 95:      * @param  array
 96:      * @param  string
 97:      * @return self
 98:      */
 99:     private function select($masks, $type)
100:     {
101:         $this->cursor = & $this->groups[];
102:         $pattern = self::buildPattern($masks);
103:         if ($type || $pattern) {
104:             $this->filter(function(FilesystemIterator $file) use ($type, $pattern) {
105:                 return !$file->isDot()
106:                     && (!$type || $file->$type())
107:                     && (!$pattern || preg_match($pattern, '/' . strtr($file->getSubPathName(), '\\', '/')));
108:             });
109:         }
110:         return $this;
111:     }
112: 
113: 
114:     /**
115:      * Searchs in the given folder(s).
116:      * @param  string|array
117:      * @return self
118:      */
119:     public function in($path)
120:     {
121:         if (!is_array($path)) {
122:             $path = func_get_args();
123:         }
124:         $this->maxDepth = 0;
125:         return $this->from($path);
126:     }
127: 
128: 
129:     /**
130:      * Searchs recursively from the given folder(s).
131:      * @param  string|array
132:      * @return self
133:      */
134:     public function from($path)
135:     {
136:         if ($this->paths) {
137:             throw new Nette\InvalidStateException('Directory to search has already been specified.');
138:         }
139:         if (!is_array($path)) {
140:             $path = func_get_args();
141:         }
142:         $this->paths = $path;
143:         $this->cursor = & $this->exclude;
144:         return $this;
145:     }
146: 
147: 
148:     /**
149:      * Shows folder content prior to the folder.
150:      * @return self
151:      */
152:     public function childFirst()
153:     {
154:         $this->order = RecursiveIteratorIterator::CHILD_FIRST;
155:         return $this;
156:     }
157: 
158: 
159:     /**
160:      * Converts Finder pattern to regular expression.
161:      * @param  array
162:      * @return string
163:      */
164:     private static function buildPattern($masks)
165:     {
166:         $pattern = array();
167:         foreach ($masks as $mask) {
168:             $mask = rtrim(strtr($mask, '\\', '/'), '/');
169:             $prefix = '';
170:             if ($mask === '') {
171:                 continue;
172: 
173:             } elseif ($mask === '*') {
174:                 return NULL;
175: 
176:             } elseif ($mask[0] === '/') { // absolute fixing
177:                 $mask = ltrim($mask, '/');
178:                 $prefix = '(?<=^/)';
179:             }
180:             $pattern[] = $prefix . strtr(preg_quote($mask, '#'),
181:                 array('\*\*' => '.*', '\*' => '[^/]*', '\?' => '[^/]', '\[\!' => '[^', '\[' => '[', '\]' => ']', '\-' => '-'));
182:         }
183:         return $pattern ? '#/(' . implode('|', $pattern) . ')\z#i' : NULL;
184:     }
185: 
186: 
187:     /********************* iterator generator ****************d*g**/
188: 
189: 
190:     /**
191:      * Get the number of found files and/or directories.
192:      * @return int
193:      */
194:     public function count()
195:     {
196:         return iterator_count($this->getIterator());
197:     }
198: 
199: 
200:     /**
201:      * Returns iterator.
202:      * @return \Iterator
203:      */
204:     public function getIterator()
205:     {
206:         if (!$this->paths) {
207:             throw new Nette\InvalidStateException('Call in() or from() to specify directory to search.');
208: 
209:         } elseif (count($this->paths) === 1) {
210:             return $this->buildIterator($this->paths[0]);
211: 
212:         } else {
213:             $iterator = new \AppendIterator();
214:             $iterator->append($workaround = new \ArrayIterator(array('workaround PHP bugs #49104, #63077')));
215:             foreach ($this->paths as $path) {
216:                 $iterator->append($this->buildIterator($path));
217:             }
218:             unset($workaround[0]);
219:             return $iterator;
220:         }
221:     }
222: 
223: 
224:     /**
225:      * Returns per-path iterator.
226:      * @param  string
227:      * @return \Iterator
228:      */
229:     private function buildIterator($path)
230:     {
231:         $iterator = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS);
232: 
233:         if ($this->exclude) {
234:             $filters = $this->exclude;
235:             $iterator = new RecursiveCallbackFilterIterator($iterator, function($foo, $bar, RecursiveCallbackFilterIterator $iterator) use ($filters) {
236:                 $file = $iterator->getInnerIterator();
237:                 if (!$file->isDot() && !$file->isFile()) {
238:                     foreach ($filters as $filter) {
239:                         if (!call_user_func($filter, $file)) {
240:                             return FALSE;
241:                         }
242:                     }
243:                 }
244:                 return TRUE;
245:             });
246:         }
247: 
248:         if ($this->maxDepth !== 0) {
249:             $iterator = new RecursiveIteratorIterator($iterator, $this->order);
250:             $iterator->setMaxDepth($this->maxDepth);
251:         }
252: 
253:         if ($this->groups) {
254:             $groups = $this->groups;
255:             $iterator = new CallbackFilterIterator($iterator, function($foo, $bar, CallbackFilterIterator $file) use ($groups) {
256:                 do {
257:                     $file = $file->getInnerIterator();
258:                 } while (!$file instanceof FilesystemIterator);
259: 
260:                 foreach ($groups as $filters) {
261:                     foreach ($filters as $filter) {
262:                         if (!call_user_func($filter, $file)) {
263:                             continue 2;
264:                         }
265:                     }
266:                     return TRUE;
267:                 }
268:                 return FALSE;
269:             });
270:         }
271: 
272:         return $iterator;
273:     }
274: 
275: 
276:     /********************* filtering ****************d*g**/
277: 
278: 
279:     /**
280:      * Restricts the search using mask.
281:      * Excludes directories from recursive traversing.
282:      * @param  mixed
283:      * @return self
284:      */
285:     public function exclude($masks)
286:     {
287:         if (!is_array($masks)) {
288:             $masks = func_get_args();
289:         }
290:         $pattern = self::buildPattern($masks);
291:         if ($pattern) {
292:             $this->filter(function(FilesystemIterator $file) use ($pattern) {
293:                 return !preg_match($pattern, '/' . strtr($file->getSubPathName(), '\\', '/'));
294:             });
295:         }
296:         return $this;
297:     }
298: 
299: 
300:     /**
301:      * Restricts the search using callback.
302:      * @param  callable  function(FilesystemIterator $file)
303:      * @return self
304:      */
305:     public function filter($callback)
306:     {
307:         $this->cursor[] = $callback;
308:         return $this;
309:     }
310: 
311: 
312:     /**
313:      * Limits recursion level.
314:      * @param  int
315:      * @return self
316:      */
317:     public function limitDepth($depth)
318:     {
319:         $this->maxDepth = $depth;
320:         return $this;
321:     }
322: 
323: 
324:     /**
325:      * Restricts the search by size.
326:      * @param  string  "[operator] [size] [unit]" example: >=10kB
327:      * @param  int
328:      * @return self
329:      */
330:     public function size($operator, $size = NULL)
331:     {
332:         if (func_num_args() === 1) { // in $operator is predicate
333:             if (!preg_match('#^(?:([=<>!]=?|<>)\s*)?((?:\d*\.)?\d+)\s*(K|M|G|)B?\z#i', $operator, $matches)) {
334:                 throw new Nette\InvalidArgumentException('Invalid size predicate format.');
335:             }
336:             list(, $operator, $size, $unit) = $matches;
337:             static $units = array('' => 1, 'k' => 1e3, 'm' => 1e6, 'g' => 1e9);
338:             $size *= $units[strtolower($unit)];
339:             $operator = $operator ? $operator : '=';
340:         }
341:         return $this->filter(function(FilesystemIterator $file) use ($operator, $size) {
342:             return Finder::compare($file->getSize(), $operator, $size);
343:         });
344:     }
345: 
346: 
347:     /**
348:      * Restricts the search by modified time.
349:      * @param  string  "[operator] [date]" example: >1978-01-23
350:      * @param  mixed
351:      * @return self
352:      */
353:     public function date($operator, $date = NULL)
354:     {
355:         if (func_num_args() === 1) { // in $operator is predicate
356:             if (!preg_match('#^(?:([=<>!]=?|<>)\s*)?(.+)\z#i', $operator, $matches)) {
357:                 throw new Nette\InvalidArgumentException('Invalid date predicate format.');
358:             }
359:             list(, $operator, $date) = $matches;
360:             $operator = $operator ? $operator : '=';
361:         }
362:         $date = DateTime::from($date)->format('U');
363:         return $this->filter(function(FilesystemIterator $file) use ($operator, $date) {
364:             return Finder::compare($file->getMTime(), $operator, $date);
365:         });
366:     }
367: 
368: 
369:     /**
370:      * Compares two values.
371:      * @param  mixed
372:      * @param  mixed
373:      * @return bool
374:      */
375:     public static function compare($l, $operator, $r)
376:     {
377:         switch ($operator) {
378:             case '>':
379:                 return $l > $r;
380:             case '>=':
381:                 return $l >= $r;
382:             case '<':
383:                 return $l < $r;
384:             case '<=':
385:                 return $l <= $r;
386:             case '=':
387:             case '==':
388:                 return $l == $r;
389:             case '!':
390:             case '!=':
391:             case '<>':
392:                 return $l != $r;
393:             default:
394:                 throw new Nette\InvalidArgumentException("Unknown operator $operator.");
395:         }
396:     }
397: 
398: }
399: 
Nette 2.3.1 API API documentation generated by ApiGen 2.8.0