s InvalidOptionsException If an option doesn't fulfill the * specified validation rules * @throws MissingOptionsException If a required option is missing * @throws OptionDefinitionException If there is a cyclic dependency between * lazy options and/or normalizers * @throws NoSuchOptionException If a lazy option reads an unavailable option * @throws AccessException If called from a lazy option or normalizer */ public function resolve(array $options = []): array { if ($this->locked) { throw new AccessException('Options cannot be resolved from a lazy option or normalizer.'); } // Allow this method to be called multiple times $clone = clone $this; // Make sure that no unknown options are passed $diff = $this->ignoreUndefined ? [] : array_diff_key($options, $clone->defined); if (\count($diff) > 0) { ksort($clone->defined); ksort($diff); throw new UndefinedOptionsException(\sprintf((\count($diff) > 1 ? 'The options "%s" do not exist.' : 'The option "%s" does not exist.').' Defined options are: "%s".', $this->formatOptions(array_keys($diff)), implode('", "', array_keys($clone->defined)))); } // Override options set by the user foreach ($options as $option => $value) { if ($this->ignoreUndefined && !isset($clone->defined[$option])) { continue; } $clone->given[$option] = true; $clone->defaults[$option] = $value; unset($clone->resolved[$option], $clone->lazy[$option]); } // Check whether any required option is missing $diff = array_diff_key($clone->required, $clone->defaults); if (\count($diff) > 0) { ksort($diff); throw new MissingOptionsException(\sprintf(\count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.', $this->formatOptions(array_keys($diff)))); } // Lock the container $clone->locked = true; // Now process the individual options. Use offsetGet(), which resolves // the option itself and any options that the option depends on foreach ($clone->defaults as $option => $_) { $clone->offsetGet($option); } return $clone->resolved; } /** * Returns the resolved value of an option. * * @param bool $triggerDeprecation Whether to trigger the deprecation or not (true by default) * * @throws AccessException If accessing this method outside of * {@link resolve()} * @throws NoSuchOptionException If the option is not set * @throws InvalidOptionsException If the option doesn't fulfill the * specified validation rules * @throws OptionDefinitionException If there is a cyclic dependency between * lazy options and/or normalizers */ public function offsetGet(mixed $option, bool $triggerDeprecation = true): mixed { if (!$this->locked) { throw new AccessException('Array access is only supported within closures of lazy options and normalizers.'); } // Shortcut for resolved options if (isset($this->resolved[$option]) || \array_key_exists($option, $this->resolved)) { if ($triggerDeprecation && isset($this->deprecated[$option]) && (isset($this->given[$option]) || $this->calling) && \is_string($this->deprecated[$option]['message'])) { trigger_deprecation($this->deprecated[$option]['package'], $this->deprecated[$option]['version'], strtr($this->deprecated[$option]['message'], ['%name%' => $option])); } return $this->resolved[$option]; } // Check whether the option is set at all if (!isset($this->defaults[$option]) && !\array_key_exists($option, $this->defaults)) { if (!isset($this->defined[$option])) { throw new NoSuchOptionException(\sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined)))); } throw new NoSuchOptionException(\sprintf('The optional option "%s" has no value set. You should make sure it is set with "isset" before reading it.', $this->formatOptions([$option]))); } $value = $this->defaults[$option]; // Resolve the option if the default value is lazily evaluated if (isset($this->lazy[$option])) { // If the closure is already being called, we have a cyclic dependency if (isset($this->calling[$option])) { throw new OptionDefinitionException(\sprintf('The options "%s" have a cyclic dependency.', $this->formatOptions(array_keys($this->calling)))); } $this->calling[$option] = true; try { foreach ($this->lazy[$option] as $closure) { $value = $closure($this, $value); } } finally { unset($this->calling[$option]); } } // Resolve the option if it is a nested definition if (isset($this->nested[$option])) { // If the closure is already being called, we have a cyclic dependency if (isset($this->calling[$option])) { throw new OptionDefinitionException(\sprintf('The options "%s" have a cyclic dependency.', $this->formatOptions(array_keys($this->calling)))); } if (!\is_array($value)) { throw new InvalidOptionsException(\sprintf('The nested option "%s" with value %s is expected to be of type array, but is of type "%s".', $this->formatOptions([$option]), $this->formatValue($value), get_debug_type($value))); } $this->calling[$option] = true; try { $resolver = new self(); $resolver->prototype = false; $resolver->parentsOptions = $this->parentsOptions; if ($this->prototype && null !== $this->prototypeIndex && null !== ($parentOptionKey = array_key_last($resolver->parentsOptions))) { $resolver->parentsOptions[$parentOptionKey] .= \sprintf('[%s]', $this->prototypeIndex); } $resolver->parentsOptions[] = $option; foreach ($this->nested[$option] as $closure) { $closure($resolver, $this); } if ($resolver->prototype) { $values = []; foreach ($value as $index => $prototypeValue) { if (!\is_array($prototypeValue)) { throw new InvalidOptionsException(\sprintf('The value of the option "%s" is expected to be of type array of array, but is of type array of "%s".', $this->formatOptions([$option]), get_debug_type($prototypeValue))); } $resolver->prototypeIndex = $index; $values[$index] = $resolver->resolve($prototypeValue); } $value = $values; } else { $value = $resolver->resolve($value); } } finally { $resolver->prototypeIndex = null; unset($this->calling[$option]); } } // Validate the type of the resolved option if (isset($this->allowedTypes[$option])) { $valid = true; $invalidTypes = []; foreach ($this->allowedTypes[$option] as $type) { if ($valid = $this->verifyTypes($type, $value, $invalidTypes)) { break; } } if (!$valid) { $fmtActualValue = $this->formatValue($value); $fmtAllowedTypes = implode('" or "', $this->allowedTypes[$option]); $fmtProvidedTypes = implode('|', array_keys($invalidTypes)); $allowedContainsArrayType = \count(array_filter($this->allowedTypes[$option], static fn ($item) => str_ends_with($item, '[]'))) > 0; if (\is_array($value) && $allowedContainsArrayType) { throw new InvalidOptionsException(\sprintf('The option "%s" with value %s is expected to be of type "%s", but one of the elements is of type "%s".', $this->formatOptions([$option]), $fmtActualValue, $fmtAllowedTypes, $fmtProvidedTypes)); } throw new InvalidOptionsException(\sprintf('The option "%s" with value %s is expected to be of type "%s", but is of type "%s".', $this->formatOptions([$option]), $fmtActualValue, $fmtAllowedTypes, $fmtProvidedTypes)); } } // Validate the value of the resolved option if (isset($this->allowedValues[$option])) { $success = false; $printableAllowedValues = []; foreach ($this->allowedValues[$option] as $allowedValue) { if ($allowedValue instanceof \Closure) { if ($allowedValue($value)) { $success = true; break; } // Don't include closures in the exception message continue; } if ($value === $allowedValue) { $success = true; break; } $printableAllowedValues[] = $allowedValue; } if (!$success) { $message = \sprintf( 'The option "%s" with value %s is invalid.', $this->formatOptions([$option]), $this->formatValue($value) ); if (\count($printableAllowedValues) > 0) { $message .= \sprintf( ' Accepted values are: %s.', $this->formatValues($printableAllowedValues) ); } if (isset($this->info[$option])) { $message .= \sprintf(' Info: %s.', $this->info[$option]); } throw new InvalidOptionsException($message); } } // Check whether the option is deprecated // and it is provided by the user or is being called from a lazy evaluation if ($triggerDeprecation && isset($this->deprecated[$option]) && (isset($this->given[$option]) || ($this->calling && \is_string($this->deprecated[$option]['message'])))) { $deprecation = $this->deprecated[$option]; $message = $this->deprecated[$option]['message']; if ($message instanceof \Closure) { // If the closure is already being called, we have a cyclic dependency if (isset($this->calling[$option])) { throw new OptionDefinitionException(\sprintf('The options "%s" have a cyclic dependency.', $this->formatOptions(array_keys($this->calling)))); } $this->calling[$option] = true; try { if (!\is_string($message = $message($this, $value))) { throw new InvalidOptionsException(\sprintf('Invalid type for deprecation message, expected string but got "%s", return an empty string to ignore.', get_debug_type($message))); } } finally { unset($this->calling[$option]); } } if ('' !== $message) { trigger_deprecation($deprecation['package'], $deprecation['version'], strtr($message, ['%name%' => $option])); } } // Normalize the validated option if (isset($this->normalizers[$option])) { // If the closure is already being called, we have a cyclic // dependency if (isset($this->calling[$option])) { throw new OptionDefinitionException(\sprintf('The options "%s" have a cyclic dependency.', $this->formatOptions(array_keys($this->calling)))); } // The following section must be protected from cyclic // calls. Set $calling for the current $option to detect a cyclic // dependency // BEGIN $this->calling[$option] = true; try { foreach ($this->normalizers[$option] as $normalizer) { $value = $normalizer($this, $value); } } finally { unset($this->calling[$option]); } // END } // Mark as resolved $this->resolved[$option] = $value; return $value; } private function verifyTypes(string $type, mixed $value, ?array &$invalidTypes = null, int $level = 0): bool { $type = trim($type); $allowedTypes = $this->splitOutsideParenthesis($type); if (\count($allowedTypes) > 1) { foreach ($allowedTypes as $allowedType) { if ($this->verifyTypes($allowedType, $value)) { return true; } } if (\is_array($invalidTypes) && (!$invalidTypes || $level > 0)) { $invalidTypes[get_debug_type($value)] = true; } return false; } $type = $allowedTypes[0]; if (str_starts_with($type, '(') && str_ends_with($type, ')')) { return $this->verifyTypes(substr($type, 1, -1), $value, $invalidTypes, $level); } if (\is_array($value) && str_ends_with($type, '[]')) { $type = substr($type, 0, -2); $valid = true; foreach ($value as $val) { if (!$this->verifyTypes($type, $val, $invalidTypes, $level + 1)) { $valid = false; } } return $valid; } if (('null' === $type && null === $value) || (isset(self::VALIDATION_FUNCTIONS[$type]) ? self::VALIDATION_FUNCTIONS[$type]($value) : $value instanceof $type)) { return true; } if (\is_array($invalidTypes) && (!$invalidTypes || $level > 0)) { $invalidTypes[get_debug_type($value)] = true; } return false; } /** * @return list */ private function splitOutsideParenthesis(string $type): array { return preg_split(<<<'EOF' / # Define a recursive subroutine for matching balanced parentheses (?(DEFINE) (? \( # Match an opening parenthesis (?: # Start a non-capturing group for the contents [^()] # Match any character that is not a parenthesis | # OR (?&balanced) # Recursively match a nested balanced group )* # Repeat the group for all contents \) # Match the final closing parenthesis ) ) # Match any balanced parenthetical group, then skip it (?&balanced)(*SKIP)(*FAIL) # Use the defined subroutine and discard the match | # OR \| # Match the pipe delimiter (only if not inside a skipped group) /x EOF, $type ); } /** * Returns whether a resolved option with the given name exists. * * @throws AccessException If accessing this method outside of {@link resolve()} * * @see \ArrayAccess::offsetExists() */ public function offsetExists(mixed $option): bool { if (!$this->locked) { throw new AccessException('Array access is only supported within closures of lazy options and normalizers.'); } return \array_key_exists($option, $this->defaults); } /** * Not supported. * * @throws AccessException */ public function offsetSet(mixed $option, mixed $value): void { throw new AccessException('Setting options via array access is not supported. Use setDefault() instead.'); } /** * Not supported. * * @throws AccessException */ public function offsetUnset(mixed $option): void { throw new AccessException('Removing options via array access is not supported. Use remove() instead.'); } /** * Returns the number of set options. * * This may be only a subset of the defined options. * * @throws AccessException If accessing this method outside of {@link resolve()} * * @see \Countable::count() */ public function count(): int { if (!$this->locked) { throw new AccessException('Counting is only supported within closures of lazy options and normalizers.'); } return \count($this->defaults); } /** * Sets whether ignore undefined options. * * @return $this */ public function setIgnoreUndefined(bool $ignore = true): static { $this->ignoreUndefined = $ignore; return $this; } /** * Returns a string representation of the value. * * This method returns the equivalent PHP tokens for most scalar types * (i.e. "false" for false, "1" for 1 etc.). Strings are always wrapped * in double quotes ("). */ private function formatValue(mixed $value): string { if (\is_object($value)) { return $value::class; } if (\is_array($value)) { return 'array'; } if (\is_string($value)) { return '"'.$value.'"'; } if (\is_resource($value)) { return 'resource'; } if (null === $value) { return 'null'; } if (false === $value) { return 'false'; } if (true === $value) { return 'true'; } return (string) $value; } /** * Returns a string representation of a list of values. * * Each of the values is converted to a string using * {@link formatValue()}. The values are then concatenated with commas. * * @see formatValue() */ private function formatValues(array $values): string { foreach ($values as $key => $value) { $values[$key] = $this->formatValue($value); } return implode(', ', $values); } private function formatOptions(array $options): string { if ($this->parentsOptions) { $prefix = array_shift($this->parentsOptions); if ($this->parentsOptions) { $prefix .= \sprintf('[%s]', implode('][', $this->parentsOptions)); } if ($this->prototype && null !== $this->prototypeIndex) { $prefix .= \sprintf('[%s]', $this->prototypeIndex); } $options = array_map(static fn (string $option): string => \sprintf('%s[%s]', $prefix, $option), $options); } return implode('", "', $options); } private function getParameterClassName(\ReflectionParameter $parameter): ?string { if (!($type = $parameter->getType()) instanceof \ReflectionNamedType || $type->isBuiltin()) { return null; } return $type->getName(); } } An Error Occurred: Whoops, looks like something went wrong.

Sorry, there was a problem we could not recover from.

The server returned a "500 - Whoops, looks like something went wrong."

Help me resolve this