public static function getFilesFromDirectory( $dir, array $include = [], array $exclude = [], $extensions = [] ) {
		$filtered_files = [];

		$files = new RecursiveIteratorIterator(
			new RecursiveCallbackFilterIterator(
				new RecursiveDirectoryIterator( $dir, RecursiveDirectoryIterator::SKIP_DOTS | RecursiveDirectoryIterator::UNIX_PATHS ),
				function ( $file, $key, $iterator ) use ( $include, $exclude, $extensions ) {
					/** @var RecursiveCallbackFilterIterator $iterator */
					/** @var SplFileInfo $file */

					// If no $include is passed everything gets the weakest possible matching score.
					$inclusion_score = empty( $include ) ? 0.1 : static::calculateMatchScore( $file, $include );
					$exclusion_score = static::calculateMatchScore( $file, $exclude );

					// Always include directories that aren't excluded.
					if ( 0 === $exclusion_score && $iterator->hasChildren() ) {
						return true;
					}

					if ( 0 === $inclusion_score || $exclusion_score > $inclusion_score ) {
						// Always include directories that may have matching children even if they are excluded.
						return $iterator->hasChildren() && static::containsMatchingChildren( $file, $include );
					}

					return ( $file->isFile() && in_array( $file->getExtension(), $extensions, true ) );
				}
			),
			RecursiveIteratorIterator::CHILD_FIRST
		);

		foreach ( $files as $file ) {
			/** @var SplFileInfo $file */
			if ( ! $file->isFile() || ! in_array( $file->getExtension(), $extensions, true ) ) {
				continue;
			}

			$filtered_files[] = Utils\normalize_path( $file->getPathname() );
		}

		return $filtered_files;
	}