{"id":20900687,"url":"https://github.com/aivec/wordpress-router","last_synced_at":"2025-05-13T01:32:33.684Z","repository":{"id":37548725,"uuid":"216331030","full_name":"aivec/wordpress-router","owner":"aivec","description":"WordPress request router. Middleware handling and nonce checks included.","archived":false,"fork":false,"pushed_at":"2023-04-19T19:55:11.000Z","size":356,"stargazers_count":5,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-19T23:02:37.319Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aivec.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-10-20T08:33:51.000Z","updated_at":"2024-11-13T08:44:45.000Z","dependencies_parsed_at":"2024-11-19T04:15:37.144Z","dependency_job_id":"c5cbe61d-6e05-4e4d-853c-af46198d31ed","html_url":"https://github.com/aivec/wordpress-router","commit_stats":{"total_commits":46,"total_committers":2,"mean_commits":23.0,"dds":"0.10869565217391308","last_synced_commit":"3e0c1b2b66c6a2ed84f177677b789d444b158c36"},"previous_names":[],"tags_count":37,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aivec%2Fwordpress-router","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aivec%2Fwordpress-router/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aivec%2Fwordpress-router/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aivec%2Fwordpress-router/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aivec","download_url":"https://codeload.github.com/aivec/wordpress-router/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253854094,"owners_count":21974223,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-18T11:21:38.887Z","updated_at":"2025-05-13T01:32:33.382Z","avatar_url":"https://github.com/aivec.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# WordPress REST Router\n\nThis package provides a routing library for WordPress with WordPress specific wrappers such as nonce verification and user role checking. The backbone of this package uses [FastRoute](https://github.com/nikic/FastRoute), a small and succinct route resolver. `FastRoute` is also the route resolver used by the popular micro-framework [Slim](http://www.slimframework.com/).\n\n## The Problem\n\nRouting in WordPress is a pain for plugin authors. It relies solely on `$_POST` object keys to resolve routes if you go with WordPress' traditional way of registering AJAX handlers via `admin-ajax.php`. You could use WordPress' [REST API](https://developer.wordpress.org/rest-api/), but you don't have control of _when_ routes are resolved. This is important to developers who create extensions for other plugins where the load order is out of their control. This package also differs from WordPress' implementation in that it doesn't provide `validate` and `sanitize` callbacks, opting instead for generic middlewares.\n\n## Features\n\nThis library provides many features to streamline the provisioning of routes, as well as some optional default middlewares. The main features are as follows:\n\n- Role based route registration (editor, administrator, etc.)\n- Automatic nonce verification\n- URL parameters (**NOT REGEX** :grin:)\n- Passthru routing (non-AJAX routes)\n- Helpers for generating HTML forms\n- JWT route registration\n- JWT settings page for automatic key pair generation\n\n## Installation\n\nInstall with [composer](https://getcomposer.org/):\n\n```sh\n$ composer require aivec/wordpress-router\n```\n\nIf you plan on using this package in a plugin, we _highly_ recommend namespacing it with [mozart](https://github.com/coenjacobs/mozart). If you don't, things may break in an impossible to debug way. [You have been warned](https://wptavern.com/a-narrative-of-using-composer-in-a-wordpress-plugin).\n\n## Usage Guide\n\n- [Public Route](#public-route)\n  - [Calling the Public Route](#calling-the-public-route)\n- [Private Route](#private-route)\n  - [Calling the Private Route](#calling-the-private-route)\n- [URL Parameters](#url-parameters)\n- [Form Data](#form-data)\n- [Making Everything Easier](#making-everything-easier)\n\n## Public Route\n\nA public route refers to a route without nonce verification. A public route is accessible by anyone, from anywhere.\n\n```php\nuse Aivec\\WordPress\\Routing\\Router;\nuse Aivec\\WordPress\\Routing\\WordPressRouteCollector;\n\n// First, we declare our routes by extending the `Router` class:\nclass Routes extends Router {\n\n    /**\n     * This is where we define each route\n     */\n    public function declareRoutes(WordPressRouteCollector $r) {\n        $r-\u003eaddPublicRoute('GET', '/hamburger', function () {\n            return 'Here is a public hamburger.';\n        });\n    }\n}\n\n// Next, we instantiate the `Routes` class with a unique namespace and listen for requests\n$routes = new Routes('/mynamespace');\n$routes-\u003edispatcher-\u003elisten();\n```\n\n### Calling the Public Route\n\nYou can test the route from the command line, like so:\n\n```sh\n$ curl -X GET http://www.my-site.com/mynamespace/hamburger\n'Here is a public hamburger.'\n```\n\nOr, you can use `jQuery`'s `ajax` function to send a request from a script loaded into a WordPress page:\n\n```js\njQuery.ajax(\"http://www.my-site.com/mynamespace/hamburger\", {\n  success(data) {\n    var response = JSON.parse(data);\n\n    console.log(response); // Here is a public hamburger.\n  },\n});\n```\n\n## Private Route\n\nA private route refers to a route with nonce verification.\n\n```php\nuse Aivec\\WordPress\\Routing\\Router;\nuse Aivec\\WordPress\\Routing\\WordPressRouteCollector;\n\n// First, extend the `Router` class:\nclass Routes extends Router {\n\n    /**\n     * This is where we define each route\n     */\n    public function declareRoutes(WordPressRouteCollector $r) {\n        /**\n         * `add` is the default way to register a route with nonce verification\n         */\n        $r-\u003eadd('POST', '/hamburger', function () {\n            return 'Here is a private hamburger.';\n        });\n    }\n}\n\n```\n\nAfter declaring our routes, we instantiate the `Routes` class with a unique namespace.\n\nThis time, we pass in a nonce key and nonce name as the second and\nthird argument, respectively.\n\nSince nonce handling requires WordPress core functions, we must instantiate the `Routes`\nclass after core functions have been loaded. You can use the `init` hook, or any other\nappropriate hook to ensure core functions are loaded.\n\n```php\n$routes = null;\nadd_action('init', function () use ($routes) {\n    $routes = new Routes('/mynamespace', 'nonce-key', 'nonce-name');\n    $routes-\u003edispatcher-\u003elisten();\n});\n```\n\n### Calling the Private Route\n\nIn general, private routes are called via AJAX from a JavaScript file on the WordPress site. To do this, we must make the nonce available to the script in which we want to call the route.\n\nLeveraging `wp_localize_script`, we can use a helper method from the `Routes` class to inject the nonce variables:\n\n```php\n\nadd_action('wp_enqueue_scripts', function () use ($routes) {\n    wp_enqueue_script(\n        'my-script',\n        site_url() . '/wp-content/plugins/my-plugin/my-script.js',\n        [],\n        '1.0.0',\n        false\n    );\n\n    wp_localize_script('my-script', 'myvars', $routes-\u003egetScriptInjectionVariables());\n});\n```\n\nNow, `my-script.js` will have the nonce variables we need to make the call.\n\n```js\n// my-script.js\njQuery.ajax(`${myvars.endpoint}/hamburger`, {\n  method: \"POST\",\n  data: {\n    [myvars.nonceKey]: myvars.nonce,\n  },\n  success(data) {\n    var response = JSON.parse(data);\n\n    console.log(response); // Here is a private hamburger.\n  },\n});\n```\n\n## URL Parameters\n\nCurly braces are used to define a URL parameter.\n\nURL parameters are parsed and then inserted into an `$args` variable, which is always the _first_ parameter given to the handler function.\n\n```php\n$r-\u003eadd('POST', '/hamburger/{burgername}', function (array $args) {\n    return 'Here is a ' . $args['burgername'] . ' hamburger.';\n});\n```\n\n```js\n// my-script.js\njQuery.ajax(`${myvars.endpoint}/hamburger/mushroom`, {\n  method: \"POST\",\n  data: {\n    [myvars.nonceKey]: myvars.nonce,\n  },\n  success(data) {\n    var response = JSON.parse(data);\n\n    console.log(response); // Here is a mushroom hamburger.\n  },\n});\n```\n\nYou can define as many parameters as you want.\n\n```php\n$r-\u003eadd('POST', '/hamburger/{burgername}/{count}', function (array $args) {\n    return 'Here are ' . $args['count'] . ' ' . $args['burgername'] . ' hamburgers.';\n});\n```\n\nYou can also limit the type of parameter accepted, as well as provide your own patterns for more granular control.\n\n```php\n// Matches /user/42, but not /user/xyz\n$r-\u003eadd('POST', '/user/{id:\\d+}', .....);\n\n// Matches /user/foobar, but not /user/foo/bar\n$r-\u003eadd('GET', '/user/{name}', .....);\n\n// Matches /user/foo/bar as well\n$r-\u003eadd('GET', '/user/{name:.+}', .....);\n```\n\nThere are many possibilities for route definitions. For detailed information about how routes are resolved, refer [here](https://github.com/nikic/FastRoute#defining-routes).\n\n## Form Data\n\nThe router expects `POST` requests to be sent with a content type of `application/x-www-form-urlencoded`. Form data is sent as a JSON encoded string as the value of a `payload` key in the body of the request.\n\n```php\n// $payload contains the decoded JSON key-value array\n$r-\u003eadd('POST', '/hamburger', function (array $args, array $payload) {\n    $ingredients = join(' and ', $payload['ingredients']);\n    return 'I want ' . $ingredients . ' on my hamburger.';\n});\n```\n\n```js\n// my-script.js\njQuery.ajax(`${myvars.endpoint}/hamburger`, {\n  method: \"POST\",\n  data: {\n    [myvars.nonceKey]: myvars.nonce,\n    payload: JSON.stringify({\n      ingredients: [\"pickles\", \"onion\"],\n    }),\n  },\n  success(data) {\n    var response = JSON.parse(data);\n\n    console.log(response); // I want pickles and onion on my hamburger.\n  },\n});\n```\n\n## Making Everything Easier\n\nAs we've seen above, private routes require a nonce key-value pair to be present in the body of a `POST` request. You may have noticed that we excluded `GET` requests in those examples. This is because `GET` requests don't have body content, which means that the nonce variables must be set as URL query parameters. This whole process is tedious, and we can do better.\n\nFor people transpiling their JavaScript, we recommend using [axios](https://github.com/axios/axios) with our [helper library](https://github.com/aivec/reqres-utils). This completely abstracts nonce handling and JSON encoding, as well as automatically setting nonce variables in the request regardless of the request method (`GET`, `POST`, `PUT`, etc.).\n\nThe following is the [Form Data](#form-data) example, rewritten using these libraries:\n\n```js\n// my-script.js\nimport axios from \"axios\";\nimport { createRequestBody } from \"@aivec/reqres-utils\";\n\naxios\n  .post(\n    `${myvars.endpoint}/hamburger`,\n    createRequestBody(myvars, {\n      ingredients: [\"pickles\", \"onion\"],\n    })\n  )\n  .then(({ data }) =\u003e {\n    console.log(data);\n  });\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faivec%2Fwordpress-router","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faivec%2Fwordpress-router","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faivec%2Fwordpress-router/lists"}