From 2127b1bdd73ecd2423e443992da4b442f5a3c1a3 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Wed, 04 Feb 2026 21:19:25 +0000
Subject: [PATCH] =Major overhaul of MetaManager.php -> Meta.php and RestRouteManager.php -> Rest.php. Seems to work for JakeVan

---
 inc/rest/Route.php |  242 +++++++++++++++++-------------------------------
 1 files changed, 87 insertions(+), 155 deletions(-)

diff --git a/inc/rest/Route.php b/inc/rest/Route.php
index 2350eea..4deb44a 100644
--- a/inc/rest/Route.php
+++ b/inc/rest/Route.php
@@ -12,8 +12,15 @@
  * Fluent REST Route Builder
  *
  * Usage:
- *   Route::get('queue', [$this, 'getQueue'])->auth('user')->args(['status' => 'string']);
- *   Route::resource('content')->get(...)->post(...)->delete(false);
+ *   // Single-method routes
+ *   Route::for('queue')->get([$this, 'getQueue'])->auth('user');
+ *   Route::for('content')->post([$this, 'create'])->auth('verified');
+ *
+ *   // Multi-method resources
+ *   Route::for('uploads')
+ *       ->get([$this, 'list'])->auth('user')
+ *       ->post([$this, 'upload'])->auth('user')
+ *       ->delete(false);
  */
 class Route
 {
@@ -23,57 +30,21 @@
 	private bool $registered = false;
 
 	private static string $namespace = 'jvb/v1';
-	private static ?RateLimiter $rateLimiter = null;
+	private static ?RateLimits $rateLimiter = null;
+
+	// =========================================================================
+	// ENTRY POINTS
+	// =========================================================================
 
 	/**
-	 * Create a resource route (supports multiple methods)
+	 * Create a new route builder for the given path
 	 */
-	public static function resource(string $path): self
+	public static function for(string $path): self
 	{
 		return new self($path);
 	}
 
 	/**
-	 * Create a GET route
-	 */
-	public static function get(string $path, callable|array $callback): self
-	{
-		return (new self($path))->addMethod('GET', $callback);
-	}
-
-	/**
-	 * Create a POST route
-	 */
-	public static function post(string $path, callable|array $callback): self
-	{
-		return (new self($path))->addMethod('POST', $callback);
-	}
-
-	/**
-	 * Create a PUT route
-	 */
-	public static function put(string $path, callable|array $callback): self
-	{
-		return (new self($path))->addMethod('PUT', $callback);
-	}
-
-	/**
-	 * Create a PATCH route
-	 */
-	public static function patch(string $path, callable|array $callback): self
-	{
-		return (new self($path))->addMethod('PATCH', $callback);
-	}
-
-	/**
-	 * Create a DELETE route
-	 */
-	public static function delete(string $path, callable|array $callback): self
-	{
-		return (new self($path))->addMethod('DELETE', $callback);
-	}
-
-	/**
 	 * Set custom namespace (defaults to 'jvb/v1')
 	 */
 	public static function setNamespace(string $namespace): void
@@ -89,13 +60,40 @@
 		return self::$namespace;
 	}
 
+	/**
+	 * Convert readable pattern to WordPress regex
+	 * Example: 'queue/{id}' becomes 'queue/(?P<id>[a-zA-Z0-9_-]+)'
+	 */
+	public static function pattern(string $path, array $patterns = []): string
+	{
+		$defaults = [
+			'id' => '[a-zA-Z0-9_-]+',
+			'slug' => '[a-zA-Z0-9_-]+',
+			'type' => '[a-zA-Z_]+',
+			'int' => '[0-9]+',
+		];
+
+		$patterns = array_merge($defaults, $patterns);
+
+		return preg_replace_callback('/\{(\w+)(?::(\w+))?\}/', function($matches) use ($patterns) {
+			$name = $matches[1];
+			$type = $matches[2] ?? $name;
+			$pattern = $patterns[$type] ?? $patterns['id'];
+			return "(?P<{$name}>{$pattern})";
+		}, $path);
+	}
+
 	private function __construct(string $path)
 	{
 		$this->path = '/' . ltrim($path, '/');
 	}
 
+	// =========================================================================
+	// HTTP METHODS
+	// =========================================================================
+
 	/**
-	 * Add GET method to resource
+	 * Add GET handler
 	 */
 	public function get(callable|array $callback): self
 	{
@@ -103,7 +101,7 @@
 	}
 
 	/**
-	 * Add POST method to resource
+	 * Add POST handler
 	 */
 	public function post(callable|array $callback): self
 	{
@@ -111,7 +109,7 @@
 	}
 
 	/**
-	 * Add PUT method to resource
+	 * Add PUT handler
 	 */
 	public function put(callable|array $callback): self
 	{
@@ -119,7 +117,7 @@
 	}
 
 	/**
-	 * Add PATCH method to resource
+	 * Add PATCH handler
 	 */
 	public function patch(callable|array $callback): self
 	{
@@ -127,19 +125,16 @@
 	}
 
 	/**
-	 * Add DELETE method to resource (pass false to explicitly disable)
+	 * Add DELETE handler (pass false to explicitly disable)
 	 */
 	public function delete(callable|array|false $callback): self
 	{
 		if ($callback === false) {
-			return $this; // Explicitly disabled
+			return $this;
 		}
 		return $this->addMethod('DELETE', $callback);
 	}
 
-	/**
-	 * Internal method to add HTTP method
-	 */
 	private function addMethod(string $method, callable|array $callback): self
 	{
 		// Finalize previous method if exists
@@ -157,17 +152,21 @@
 		return $this;
 	}
 
+	// =========================================================================
+	// AUTHENTICATION
+	// =========================================================================
+
 	/**
-	 * Set authentication/permission requirement
+	 * Set authentication requirement
 	 *
-	 * @param string|array|false $auth
+	 * @param string|array|callable|false $auth
 	 *   - 'public' or false: Anyone can access
 	 *   - 'user': Logged-in user must match 'user' param in request
 	 *   - 'logged_in': Any logged-in user
 	 *   - 'admin': Users with manage_options capability
 	 *   - 'verified': Users with skip_moderation capability
-	 *   - ['capability' => 'edit_posts']: Specific capability check
-	 *   - ['role' => 'artist']: Specific role check
+	 *   - ['capability' => 'edit_posts']: Specific capability
+	 *   - ['role' => 'artist']: Specific role
 	 *   - ['roles' => ['artist', 'admin']]: Multiple roles (OR)
 	 *   - callable: Custom permission callback
 	 */
@@ -180,31 +179,23 @@
 		$this->currentMethod['permission_callback'] = match (true) {
 			$auth === false || $auth === 'public'
 			=> '__return_true',
-
 			$auth === 'logged_in'
 			=> 'is_user_logged_in',
-
 			$auth === 'user'
 			=> [PermissionHandler::class, 'userMatch'],
-
 			$auth === 'admin'
 			=> [PermissionHandler::class, 'isAdmin'],
-
 			$auth === 'verified'
 			=> [PermissionHandler::class, 'isVerified'],
-
+			$auth === 'nonce' => [PermissionHandler::class, 'nonce'],
 			is_callable($auth)
 			=> $auth,
-
 			is_array($auth) && isset($auth['capability'])
 			=> fn(WP_REST_Request $req) => current_user_can($auth['capability']),
-
 			is_array($auth) && isset($auth['role'])
 			=> fn(WP_REST_Request $req) => PermissionHandler::hasRole($req, $auth['role']),
-
 			is_array($auth) && isset($auth['roles'])
 			=> fn(WP_REST_Request $req) => PermissionHandler::hasAnyRole($req, $auth['roles']),
-
 			default
 			=> '__return_true',
 		};
@@ -213,10 +204,7 @@
 	}
 
 	/**
-	 * Add rate limiting to the route
-	 *
-	 * @param int $limit Maximum requests
-	 * @param int $window Time window in seconds
+	 * Add rate limiting
 	 */
 	public function rateLimit(int $limit = 60, int $window = 60): self
 	{
@@ -227,12 +215,10 @@
 		$originalCallback = $this->currentMethod['permission_callback'];
 
 		$this->currentMethod['permission_callback'] = function(WP_REST_Request $request) use ($originalCallback, $limit, $window) {
-			// Initialize rate limiter if needed
 			if (self::$rateLimiter === null) {
-				self::$rateLimiter = new RateLimiter();
+				self::$rateLimiter = new RateLimits();
 			}
 
-			// Check rate limit first
 			if (!self::$rateLimiter->checkLimit($request, $limit, $window)) {
 				return new WP_Error(
 					'rate_limit',
@@ -241,16 +227,13 @@
 				);
 			}
 
-			// Then check original permission
 			if ($originalCallback === '__return_true') {
 				return true;
 			}
 
-			if (is_callable($originalCallback)) {
-				return call_user_func($originalCallback, $request);
-			}
-
-			return true;
+			return is_callable($originalCallback)
+				? call_user_func($originalCallback, $request)
+				: true;
 		};
 
 		return $this;
@@ -258,9 +241,6 @@
 
 	/**
 	 * Require nonce verification
-	 *
-	 * @param string $action Nonce action name (default: 'wp_rest')
-	 * @param string $header Header name containing nonce (default: 'X-WP-Nonce')
 	 */
 	public function nonce(string $action = 'wp_rest', string $header = 'X-WP-Nonce'): self
 	{
@@ -281,35 +261,31 @@
 				);
 			}
 
-			// Then check original permission
 			if ($originalCallback === '__return_true') {
 				return true;
 			}
 
-			if (is_callable($originalCallback)) {
-				return call_user_func($originalCallback, $request);
-			}
-
-			return true;
+			return is_callable($originalCallback)
+				? call_user_func($originalCallback, $request)
+				: true;
 		};
 
 		return $this;
 	}
 
+	// =========================================================================
+	// ARGUMENTS
+	// =========================================================================
+
 	/**
 	 * Define route arguments with shorthand syntax
 	 *
-	 * @param array $args Argument definitions
-	 *   Shorthand: ['name' => 'type|required|default:value|enum:a,b,c']
-	 *   Full: ['name' => ['type' => 'string', 'required' => true, ...]]
-	 *
 	 * Examples:
 	 *   'status' => 'string'
 	 *   'status' => 'string|required'
 	 *   'status' => 'string|default:all'
 	 *   'status' => 'string|enum:pending,completed,failed'
 	 *   'limit' => 'integer|default:50|min:1|max:100'
-	 *   'ids' => 'array|required'
 	 */
 	public function args(array $args): self
 	{
@@ -337,12 +313,8 @@
 		return $this;
 	}
 
-	/**
-	 * Parse shorthand argument definition into WP REST format
-	 */
 	private function parseArgDefinition(string|array $definition): array
 	{
-		// Already full format
 		if (is_array($definition)) {
 			return $definition;
 		}
@@ -351,11 +323,11 @@
 		$type = trim($parts[0]);
 
 		$arg = [
-			'type' => $type,
+			'type' => $type === 'int' ? 'integer' : ($type === 'bool' ? 'boolean' : $type),
 			'required' => false,
 		];
 
-		// Add sanitize callback based on type
+		// Sanitize callback based on type
 		$arg['sanitize_callback'] = match ($type) {
 			'integer', 'int' => 'absint',
 			'string' => 'sanitize_text_field',
@@ -365,36 +337,22 @@
 			default => null,
 		};
 
-		// Normalize type for WP
-		if ($type === 'int') {
-			$arg['type'] = 'integer';
-		} elseif ($type === 'bool') {
-			$arg['type'] = 'boolean';
-		}
-
 		// Parse modifiers
 		foreach (array_slice($parts, 1) as $part) {
 			$part = trim($part);
 
-			if ($part === 'required') {
-				$arg['required'] = true;
-			} elseif (str_starts_with($part, 'default:')) {
-				$value = substr($part, 8);
-				$arg['default'] = $this->castValue($value, $type);
-			} elseif (str_starts_with($part, 'enum:')) {
-				$arg['enum'] = array_map('trim', explode(',', substr($part, 5)));
-			} elseif (str_starts_with($part, 'min:')) {
-				$arg['minimum'] = (int) substr($part, 4);
-			} elseif (str_starts_with($part, 'max:')) {
-				$arg['maximum'] = (int) substr($part, 4);
-			} elseif (str_starts_with($part, 'desc:')) {
-				$arg['description'] = substr($part, 5);
-			} elseif (str_starts_with($part, 'pattern:')) {
-				$arg['pattern'] = substr($part, 8);
-			}
+			match (true) {
+				$part === 'required' => $arg['required'] = true,
+				str_starts_with($part, 'default:') => $arg['default'] = $this->castValue(substr($part, 8), $type),
+				str_starts_with($part, 'enum:') => $arg['enum'] = array_map('trim', explode(',', substr($part, 5))),
+				str_starts_with($part, 'min:') => $arg['minimum'] = (int) substr($part, 4),
+				str_starts_with($part, 'max:') => $arg['maximum'] = (int) substr($part, 4),
+				str_starts_with($part, 'desc:') => $arg['description'] = substr($part, 5),
+				str_starts_with($part, 'pattern:') => $arg['pattern'] = substr($part, 8),
+				default => null,
+			};
 		}
 
-		// Remove null sanitize callback
 		if ($arg['sanitize_callback'] === null) {
 			unset($arg['sanitize_callback']);
 		}
@@ -402,9 +360,6 @@
 		return $arg;
 	}
 
-	/**
-	 * Cast value to appropriate type
-	 */
 	private function castValue(string $value, string $type): mixed
 	{
 		return match ($type) {
@@ -416,6 +371,10 @@
 		};
 	}
 
+	// =========================================================================
+	// REGISTRATION
+	// =========================================================================
+
 	/**
 	 * Register the route with WordPress
 	 */
@@ -425,7 +384,6 @@
 			return $this;
 		}
 
-		// Add current method if not empty
 		if (!empty($this->currentMethod)) {
 			$this->methods[] = $this->currentMethod;
 			$this->currentMethod = [];
@@ -435,13 +393,10 @@
 			return $this;
 		}
 
-		// Register single method or array of methods
 		$config = count($this->methods) === 1 ? $this->methods[0] : $this->methods;
-
 		register_rest_route(self::$namespace, $this->path, $config);
 
 		$this->registered = true;
-
 		return $this;
 	}
 
@@ -450,31 +405,8 @@
 	 */
 	public function __destruct()
 	{
-		if (!$this->registered && !empty($this->methods) || !empty($this->currentMethod)) {
+		if (!$this->registered && (!empty($this->methods) || !empty($this->currentMethod))) {
 			$this->register();
 		}
 	}
-
-	/**
-	 * Convert WordPress route pattern to more readable format
-	 * Converts: queue/{id} to queue/(?P<id>[a-zA-Z0-9_-]+)
-	 */
-	public static function pattern(string $path, array $patterns = []): string
-	{
-		$defaults = [
-			'id' => '[a-zA-Z0-9_-]+',
-			'slug' => '[a-zA-Z0-9_-]+',
-			'type' => '[a-zA-Z_]+',
-			'int' => '[0-9]+',
-		];
-
-		$patterns = array_merge($defaults, $patterns);
-
-		return preg_replace_callback('/\{(\w+)(?::(\w+))?\}/', function($matches) use ($patterns) {
-			$name = $matches[1];
-			$type = $matches[2] ?? $name;
-			$pattern = $patterns[$type] ?? $patterns['id'];
-			return "(?P<{$name}>{$pattern})";
-		}, $path);
-	}
 }

--
Gitblit v1.10.0