Skip to content

Commit 0fc4b10

Browse files
committed
Initial
1 parent 222e9a2 commit 0fc4b10

20 files changed

+1647
-0
lines changed

.gitignore

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Composer
2+
/vendor/
3+
composer.lock
4+
5+
# PHP
6+
*.log
7+
*.cache
8+
.php_cs.cache
9+
.phpunit.result.cache
10+
11+
# IDE and Editor files
12+
.idea/
13+
.vscode/
14+
*.swp
15+
*.swo
16+
*~
17+
.DS_Store
18+
19+
# WordPress specific
20+
*.sql
21+
*.sql.gz
22+
wp-config.php
23+
wp-content/advanced-cache.php
24+
wp-content/backup-db/
25+
wp-content/backups/
26+
wp-content/blogs.dir/
27+
wp-content/cache/
28+
wp-content/upgrade/
29+
wp-content/uploads/
30+
wp-content/wp-cache-config.php
31+
wp-content/plugins/hello.php
32+
33+
# Environment files
34+
.env
35+
.env.*
36+
!.env.example
37+
38+
# Node modules (if using build tools)
39+
/node_modules/
40+
npm-debug.log
41+
yarn-debug.log
42+
yarn-error.log
43+
44+
# Build and dist directories
45+
/dist/
46+
/build/
47+
.sass-cache/
48+
49+
# Testing
50+
/coverage/
51+
/tests/coverage/
52+
.phpunit.cache/
53+
54+
# Operating System
55+
Thumbs.db
56+
ehthumbs.db
57+
Desktop.ini

README.md

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# WP CoderPress Endpoints
2+
3+
This package intends to provide a more straightforward way to add custom endpoints to Wordpress Rest API (added to mu-plugins with no hooks, as a suggestion), also using a Facade pattern which allows:
4+
* To attach PSR-16 compliant cache drivers of any nature
5+
* To attach as many middlewares as you want to modify the request or provide a particular response
6+
* Interfere in the way cached responses are stored and retrieved (JSON, serialize(), igbinary_serialize())
7+
* Provide a port to add multiple custom endpoints without repeating code and sharing cache, middleware and variables within, including a dynamic endpoint generation
8+
* Offer common middlewares to handle common scenarios in REST API development
9+
10+
# Basic Example
11+
12+
The following example creates a very simple example endpoint to the rest API that comes with two middlewares attached (as \Closure objects). The first one modifies any passed parameter to "id = 123", which triggers the second middleware and returns a WP_REST_Response - this also prevents the cache to be done.
13+
14+
If the second middleware is removed, the endpoint is no longer short-circuited, so the results are now serialized and cached in a file within wp-content/cache/. In the example, the cache is managed by FileCache, but this package also includes usable drivers for Redis (using redis-php extension) and MySQL cache, using a custom DB table.
15+
16+
```php
17+
use CoderPress\Rest\{RestEndpointFacade, AbstractRestEndpoint};
18+
use CoderPress\Cache\{RedisCache, MySqlCache, FileCache};
19+
20+
require __DIR__ . '/vendor/autoload.php';
21+
22+
$cacheInstance = new FileCache(WP_CONTENT_DIR . '/cache/');
23+
24+
$custom = RestEndpointFacade::createEndpoint(
25+
/** The five first arguments stand as they are for register_rest_route()
26+
* but with callbacks now accepting \Closure functions instead.
27+
*
28+
* The middlewares arguments accepts an array of closures which will be
29+
* sequentially executed and MUST have a $request parameter.
30+
*
31+
* The cache mechanism is automatically applied to the endpoint and accepts
32+
* any PSR-16 compliant object. Optionally, the expire time and the type
33+
* of serialization can be changed. Expires accepts any value in seconds as
34+
* integer or a Datetime object (and then the time in seconds between that and
35+
* the current time will be automatically extracted)
36+
*
37+
*/
38+
namespace: 'testing/v1',
39+
route: 'custom/',
40+
args: [
41+
'id' => [
42+
'validate_callback' => function($param, $request, $key) {
43+
return is_numeric($param);
44+
}
45+
],
46+
],
47+
callback: function (\WP_REST_Request $request) {
48+
$postId = $request->get_param('id');
49+
$postCategoryIds = wp_get_object_terms($postId, ['category'], ['fields' => 'ids']);
50+
51+
return get_posts([
52+
'post_status' => 'publish',
53+
'category__in' => $postCategoryIds,
54+
'order' => 'relevance'
55+
]);
56+
},
57+
permissionCallback: function () {
58+
return is_user_logged_in();
59+
},
60+
/**
61+
* Accepts any number of /Closure middleware functions - to each one of them,
62+
* the $request object must be passed. For changes made to the WP_REST_Request object,
63+
* the closure must return void(). However, the middleware can also return a response
64+
* to the request easily by return a new WP_REST_Response object instead.
65+
*/
66+
middlewares: [
67+
function (\WP_REST_Request $request) {
68+
$request->set_param('id', 123);
69+
},
70+
function (\WP_REST_Request $request) {
71+
if ($request->get_param('id') == 123) {
72+
return new \WP_REST_Response(['message' => 'Invalid value'], 201);
73+
}
74+
}
75+
],
76+
/**
77+
* Accepts any instance PSR-16 compliant (CacheInterface contract),
78+
* this package comes with 3 usable examples (file, Redis and custom MySQL
79+
* table caching drivers).
80+
*/
81+
cache: $cacheInstance,
82+
/**
83+
* Accepts 3 different serialize/unserialize methods - defaults to serialize(),
84+
* but can be using JSON - AbstractRestEndpoint::SERIALIZE_JSON - or igbinary PHP
85+
* extension which offers a more efficient serializing version
86+
* - AbstractRestEndpoint::SERIALIZE_IGBINARY.
87+
*/
88+
cacheSerializingMethod: AbstractRestEndpoint::SERIALIZE_PHP,
89+
/**
90+
* Accepts seconds as integer value, but also DateTime objects - for the latter,
91+
* the system will get the interval between NOW and the passed DateTime object time,
92+
* so the cache will work as scheduled.
93+
*/
94+
cacheExpires: (new \DateTime())->modify('+1 day')
95+
);
96+
```
97+
98+
# Middlewares
99+
100+
The package includes several built-in middlewares to handle common scenarios in REST API development:
101+
102+
## CORS Middleware
103+
104+
The CORS (Cross-Origin Resource Sharing) middleware allows you to configure cross-origin requests to your API endpoints. Here's how to use it:
105+
106+
```php
107+
use CoderPress\Rest\RestEndpointFacade;
108+
use CoderPress\Rest\Middleware\MiddlewareFactory;
109+
110+
// Create an endpoint with CORS support
111+
RestEndpointFacade::createEndpoint(
112+
namespace: 'api/v1',
113+
route: 'posts',
114+
callback: function($request) {
115+
return ['data' => 'Your API response'];
116+
},
117+
permissionCallback: function() {
118+
return true;
119+
},
120+
args: [],
121+
methods: ['GET', 'POST', 'OPTIONS'],
122+
middlewares: [
123+
MiddlewareFactory::cors(
124+
allowedOrigins: ['https://your-domain.com'],
125+
allowedMethods: ['GET', 'POST'],
126+
allowedHeaders: ['Content-Type', 'X-Custom-Header'],
127+
maxAge: 7200
128+
)
129+
]
130+
);
131+
```
132+
133+
The CORS middleware provides:
134+
- Origin validation against a whitelist
135+
- Configurable HTTP methods
136+
- Customizable allowed headers
137+
- Preflight request handling
138+
- Configurable cache duration for preflight responses
139+
- Credentials support
140+
141+
All parameters are optional and come with sensible defaults for typical API usage.
142+
143+
## Sanitization Middleware
144+
145+
The Sanitization middleware provides comprehensive input sanitization and validation for your API endpoints. It helps prevent XSS attacks, SQL injection, and ensures data consistency:
146+
147+
```php
148+
use CoderPress\Rest\RestEndpointFacade;
149+
use CoderPress\Rest\Middleware\MiddlewareFactory;
150+
151+
RestEndpointFacade::createEndpoint(
152+
namespace: 'api/v1',
153+
route: 'posts',
154+
callback: function($request) {
155+
return ['data' => $request->get_params()];
156+
},
157+
permissionCallback: function() {
158+
return true;
159+
},
160+
args: [
161+
'title' => [
162+
'required' => true,
163+
'type' => 'string'
164+
],
165+
'content' => [
166+
'required' => true,
167+
'type' => 'string'
168+
]
169+
],
170+
methods: ['POST'],
171+
middlewares: [
172+
MiddlewareFactory::sanitization(
173+
rules: [
174+
// Custom sanitization rules for specific fields
175+
'title' => fn($value) => sanitize_title($value),
176+
'content' => fn($value) => wp_kses_post($value)
177+
],
178+
stripTags: true,
179+
encodeSpecialChars: true,
180+
allowedHtmlTags: [
181+
'p' => [],
182+
'a' => ['href' => [], 'title' => []],
183+
'b' => [],
184+
'i' => []
185+
]
186+
)
187+
]
188+
);
189+
```
190+
191+
The middleware provides:
192+
- Field-specific sanitization rules using callbacks
193+
- HTML tag stripping with configurable allowed tags
194+
- Special character encoding
195+
- UTF-8 validation
196+
- Recursive array sanitization
197+
- WordPress-specific sanitization functions integration
198+
- XSS and SQL injection protection
199+
200+
All parameters are optional and come with secure defaults. Use the `rules` parameter to define custom sanitization logic for specific fields.

composer.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "cmatosbc/wp-coderpress-endpoints",
3+
"description": "Endpoints builder and manager for CoderPress",
4+
"license": "gpl-3.0-or-later",
5+
"type": "library",
6+
"authors": [
7+
{
8+
"name": "Carlos Matos",
9+
"email": "carlosarturmatos1977@gmail.com"
10+
}
11+
],
12+
"config": {
13+
"sort-packages": true
14+
},
15+
"autoload": {
16+
"psr-4": {
17+
"CoderPress\\": "src/"
18+
}
19+
},
20+
"autoload-dev": {
21+
"psr-4": {
22+
"CoderPress\\Tests\\": "tests/"
23+
}
24+
},
25+
"require": {
26+
"php": ">=8.0",
27+
"psr/simple-cache": "^3.0"
28+
},
29+
"require-dev": {
30+
"phpunit/phpunit": "^9.0",
31+
"brain/monkey": "^2.6",
32+
"psr/http-server-handler": "^1.0"
33+
}
34+
}

phpunit.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
4+
bootstrap="tests/bootstrap.php"
5+
colors="true"
6+
verbose="true">
7+
<testsuites>
8+
<testsuite name="CoderPress Endpoints Test Suite">
9+
<directory>tests</directory>
10+
</testsuite>
11+
</testsuites>
12+
<coverage>
13+
<include>
14+
<directory suffix=".php">src</directory>
15+
</include>
16+
</coverage>
17+
</phpunit>

0 commit comments

Comments
 (0)