protected function validateFilterOptions(array $options, array $validOptions)
    {
        foreach ($options as $option => $value) {
            // Check if the option is allowed.
            if (!array_key_exists($option, $validOptions)) {
                throw new FilterOptionValidatorException(sprintf(
                    'Option "%s" is not found in valid option list: [%s].',
                    $option, implode(', ', array_keys($validOptions))
                ));
            }

            // Check the type if it is required.
            if (array_key_exists('type', $validOptions[$option])) {
                $types = $validOptions[$option]['type'];

                // Convert single type into array of types.
                if (!is_array($types)) {
                    $types = [$types];
                } elseif (count($types) === 0) {
                    throw new FilterOptionException('Type option must not be empty.');
                }

                $typeCount = count($types);
                $typeCheckedCount = 0;
                $valueType = '';

                foreach ($types as $type) {
                    $typeCheckedCount++;
                    $typeFunction = sprintf('is_%s', $type);

                    if (!function_exists($typeFunction)) {
                        throw new FilterOptionException(sprintf('There is no such function "%s".', $typeFunction));
                    }

                    if (!$typeFunction($value)) {
                        if ($typeCount === $typeCheckedCount) {
                            throw new FilterOptionValidatorException(sprintf(
                                'Option "%s" has invalid value type "%s" (which must be "%s").',
                                $option, gettype($value), $type
                            ));
                        }
                    } else {
                        $valueType = $type;
                        break;
                    }
                }

                // Check class instance if it is required.
                if (
                    $valueType === 'object'
                    && in_array('object', $types, true)
                    && array_key_exists('instance_of', $validOptions[$option])
                    && !($value instanceof $validOptions[$option]['instance_of'])
                ) {
                    throw new FilterOptionValidatorException(sprintf(
                        'Option "%s" must be an instance of %s.', $option, $validOptions[$option]['instance_of']
                    ));
                }
            }

            if (
                array_key_exists('empty', $validOptions[$option])
                && $validOptions[$option]['empty'] === false
                && (string) $value === ''
            ) {
                throw new FilterOptionValidatorException(sprintf(
                    'Option "%s" with value "%s" can not be empty.',
                    $option, is_scalar($value) ? (string) $value : '[...]'
                ));
            }
        }

        return $this;
    }