public static function iterate(string $dir, array $options = []) : array {
		$result = [];
		$relative_path = realpath($dir);
		if (empty($options['recursive'])) {
			$iterator = new \DirectoryIterator($dir);
		} else {
			$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir));
		}
		foreach ($iterator as $v) {
			if (method_exists($v, 'isDot')) {
				if ($v->isDot()) {
					continue;
				}
			} else {
				$filename = $v->getFilename();
				if ($filename === '.' || $filename === '..') {
					continue;
				}
			}
			if (!empty($options['only_extensions']) && !in_array($v->getExtension(), $options['only_extensions'])) {
				continue;
			}
			if (!empty($options['only_files']) && !in_array($filename, $options['only_files'])) {
				continue;
			}
			if (empty($options['extended'])) {
				$result[] = $v->getPathname();
			} else {
				$pathname = $v->getPathname();
				$result[$pathname] = [
					'pathname' => $pathname,
					'access' => $v->getATime(),
					'modified' => $v->getMTime(),
					'permissions' => $v->getPerms(),
					'size' => $v->getSize(),
					'type' => $v->getType(),
					'directory' => $v->getPath(),
					'basename' => $v->getBasename(),
					'filename' => $v->getFilename(),
					'relative_directory' => self::iterateProcessPathInnerHelper($v->getPath(), $relative_path),
				];
			}
		}
		return $result;
	}