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