public static function executable($class, array $options = []) {
		$defaults = [
			'self' => true,
			'filter' => true,
			'methods' => [],
			'empty' => [' ', "\t", '}', ')', ';'],
			'pattern' => null,
			'blockOpeners' => ['switch (', 'try {', '} else {', 'do {', '} while']
		];
		$options += $defaults;

		if (empty($options['pattern']) && $options['filter']) {
			$pattern = str_replace(' ', '\s*', join('|', array_map(
				function($str) { return preg_quote($str, '/'); },
				$options['blockOpeners']
			)));
			$pattern = join('|', [
				"({$pattern})",
				"\\$(.+)\($",
				"\s*['\"]\w+['\"]\s*=>\s*.+[\{\(]$",
				"\s*['\"]\w+['\"]\s*=>\s*['\"]*.+['\"]*\s*"
			]);
			$options['pattern'] = "/^({$pattern})/";
		}

		if (!$class instanceof ReflectionClass) {
			$class = new ReflectionClass(is_object($class) ? get_class($class) : $class);
		}
		$options += ['group' => false];
		$result = array_filter(static::methods($class, 'ranges', $options));

		if ($options['filter'] && $class->getFileName() && $result) {
			$lines = static::lines($class->getFileName(), $result);
			$start = key($lines);

			$code = implode("\n", $lines);
			$tokens = token_get_all('<' . '?php' . $code);
			$tmp = [];

			foreach ($tokens as $token) {
				if (is_array($token)) {
					if (!in_array($token[0], [T_COMMENT, T_DOC_COMMENT, T_WHITESPACE])) {
						$tmp[] = $token[2];
					}
				}
			}

			$filteredLines = array_values(array_map(
				function($ln) use ($start) { return $ln + $start - 1; },
				array_unique($tmp))
			);

			$lines = array_intersect_key($lines, array_flip($filteredLines));

			$result = array_keys(array_filter($lines, function($line) use ($options) {
				$line = trim($line);
				$empty = preg_match($options['pattern'], $line);
				return $empty ? false : (str_replace($options['empty'], '', $line) !== '');
			}));
		}
		return $result;
	}