> $args Argument definitions. * @return array> Definitions with validators attached. */ public static function enforce(array $args): array { foreach ($args as $name => $definition) { if (!is_array($definition) || !empty($definition['validate_callback']) || !isset($definition['type']) ) { continue; } foreach (self::CONSTRAINTS as $keyword) { if (array_key_exists($keyword, $definition)) { $args[$name]['validate_callback'] = 'rest_validate_request_arg'; break; } } } return $args; } /** * Attach validators across every route in the plugin's namespace. * * Hooked to `rest_endpoints`, which hands over the whole route map after * registration — so a constraint declared anywhere is enforced, including * on routes added by an add-on, and nobody has to remember to wrap an * argument array. * * @since 2.0.1 * * @param array $endpoints Registered routes. * @return array Routes with validators attached. */ public static function enforce_namespace(array $endpoints): array { foreach ($endpoints as $route => $handlers) { if (0 !== strpos((string) $route, '/' . self::NAMESPACE . '/')) { continue; } foreach ($handlers as $index => $handler) { if (!is_array($handler) || empty($handler['args']) || !is_array($handler['args'])) { continue; } $endpoints[ $route ][ $index ]['args'] = self::enforce($handler['args']); } } return $endpoints; } /** * Argument names whose declared constraints would not be enforced. * * Used by the test that walks the namespace so this class of defect cannot * come back one route at a time. * * @since 2.0.1 * * @param array> $args Argument definitions. * @return string[] Names of arguments declaring a constraint with no validator. */ public static function unenforced(array $args): array { $unenforced = []; foreach ($args as $name => $definition) { if (!is_array($definition) || !empty($definition['validate_callback'])) { continue; } foreach (self::CONSTRAINTS as $keyword) { if (array_key_exists($keyword, $definition)) { $unenforced[] = (string) $name; break; } } } return $unenforced; } /** * Argument names declaring a constraint with no `type` to validate against. * * Core cannot validate such an argument without emitting warnings, so * `enforce()` skips it and the constraint stays inert. Every one of these * is a bug at the point of registration. * * @since 2.0.1 * * @param array> $args Argument definitions. * @return string[] Names of constrained arguments missing a `type`. */ public static function typeless(array $args): array { $typeless = []; foreach ($args as $name => $definition) { if (!is_array($definition) || isset($definition['type'])) { continue; } foreach (self::CONSTRAINTS as $keyword) { if (array_key_exists($keyword, $definition)) { $typeless[] = (string) $name; break; } } } return $typeless; } /** * Constrained arguments missing a `type`, across a whole route map. * * @since 2.0.1 * * @param array $endpoints Registered routes. * @return string[] "route: arg" for each offender in this namespace. */ public static function typeless_in_namespace(array $endpoints): array { $offenders = []; foreach ($endpoints as $route => $handlers) { if (0 !== strpos((string) $route, '/' . self::NAMESPACE . '/')) { continue; } foreach ($handlers as $handler) { if (!is_array($handler) || empty($handler['args']) || !is_array($handler['args'])) { continue; } foreach (self::typeless($handler['args']) as $name) { $offenders[] = $route . ': ' . $name; } } } return $offenders; } }