├── .gitignore
├── .php-cs-fixer.dist.php
├── .phpmd-ruleset.xml
├── .phpunit.dist.xml
├── .readme.template.md
├── LICENSE
├── README.md
├── composer.json
├── composer.lock
├── src
├── HtmxResponse.php
├── HtmxServerRequest.php
└── Proxy
│ ├── AbstractMessageInterfaceProxy.php
│ ├── ResponseInterfaceProxy.php
│ └── ServerRequestInterfaceProxy.php
└── tests
├── HtmxResponseTest.php
└── HtmxServerRequestTest.php
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 | *.cache
3 | test.php
4 |
--------------------------------------------------------------------------------
/.php-cs-fixer.dist.php:
--------------------------------------------------------------------------------
1 | setRules([
7 | '@PSR12' => true,
8 | '@PSR12:risky' => true,
9 | '@PhpCsFixer' => true,
10 | '@PhpCsFixer:risky' => true,
11 | '@PHP80Migration' => true,
12 | '@PHP80Migration:risky' => true,
13 | 'final_internal_class' => false,
14 | 'ordered_class_elements' => [
15 | 'sort_algorithm' => 'alpha',
16 | ],
17 | ])
18 | ->setFinder(
19 | PhpCsFixer\Finder::create()
20 | ->in(__DIR__.'/src')
21 | )
22 | ;
23 |
--------------------------------------------------------------------------------
/.phpmd-ruleset.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 | PHPMD ruleset
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.phpunit.dist.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | tests
6 |
7 |
8 |
9 |
10 |
11 | src
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.readme.template.md:
--------------------------------------------------------------------------------
1 | # :package_project - :package_description
2 |
3 | [](https://packagist.org/packages/:package_name) [](https://packagist.org/packages/:package_name) [](https://packagist.org/packages/:package_name)
4 |
5 | :package_extra_intro
6 |
7 | ## Installation
8 | Installation via composer:
9 |
10 | ```bash
11 | composer require :package_name
12 | ```
13 |
14 | ## Usage example
15 | ```php
16 | :package_extra_example
17 | ```
18 |
19 | ## Testing
20 | ```bash
21 | composer test
22 | ```
23 |
24 | ## License
25 | This project is released under the :package_license License (:package_license).
26 | See [LICENSE](LICENSE) for more information.
27 |
28 | ## Documentation
29 | :documentation_toc
30 | :documentation_body
31 |
32 | ***
33 |
34 | _Generated :date using 📚[tomrf/readme-gen](https://packagist.org/packages/tomrf/readme-gen)_
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2021-2022 Tom Rune Flo
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 | of the Software, and to permit persons to whom the Software is furnished to do
8 | so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # htmx-message - PHP PSR-7 proxy classes with convenience methods for htmx requests and responses
2 |
3 | [](https://packagist.org/packages/tomrf/htmx-message) [](https://packagist.org/packages/tomrf/htmx-message) [](https://packagist.org/packages/tomrf/htmx-message)
4 |
5 | PSR-7 `ServerRequestInterface` and `ResponseInterface` proxy classes for [`htmx`](https://htmx.org)
6 | with convenience methods for all special htmx headers (`HX-*`) in request and
7 | response objects.
8 |
9 | `HtmxResponse` is a proxy for PSR-7 response objects implementing `\Psr\Http\Message\ResponseInterface`.
10 |
11 | `HtmxServerRequest` is a proxy for PSR-7 server request objects implementing `\Psr\Http\Message\ServerRequestInterface`.
12 |
13 | You can safely wrap any `ResponseInterface` or `ServerRequestInterface` object, also those not originating from [`htmx`](htmx.org), without affecting your application.
14 |
15 | `HtmxResponse` and `HtmxServerRequest` objects retains immutability in the same fashion as one would expect from a PSR-7 `MessageInterface`, returning a new object wrapping a new `ResponseInterface` instance whenever the `HtmxResponse` is changed.
16 |
17 | Conforms to [`htmx`](https://htmx.org) version 1.9.x (latest) as per 2023-08-01.
18 |
19 | 📔 [Go to documentation](#documentation)
20 |
21 | ## Installation
22 | Installation via composer:
23 |
24 | ```bash
25 | composer require tomrf/htmx-message
26 | ```
27 |
28 | ## Usage
29 | ```php
30 | $request = new HtmxRequest($request); // PSR-7 `ServerRequestInterface`
31 |
32 | if ($request->isHxRequest() && $request->isHxBoosted()) {
33 | $layout = 'ajax.layout';
34 | }
35 |
36 | [...]
37 |
38 | $response = new HtmxResponse($response); // PSR-7 `ResponseInterface`
39 |
40 | $response = $response->withHxTrigger('aSimpleTrigger')
41 | ->withAddedHxTrigger('triggerWithParams', ['arg' => true, 'arg2' => 7])
42 | ->withTriggerAfterSwap('afterSwap', time())
43 | ->withHxPush($newUrl);
44 |
45 | // Emit $response as normal
46 | [...]
47 | ```
48 |
49 | ## Testing
50 | ```bash
51 | composer test
52 | ```
53 |
54 | ## License
55 | This project is released under the MIT License (MIT).
56 | See [LICENSE](LICENSE) for more information.
57 |
58 | ## Documentation
59 | - [Tomrf\HtmxMessage\HtmxResponse](#-tomrfhtmxmessagehtmxresponseclass)
60 | - [getHxPush](#gethxpush)
61 | - [getHxRedirect](#gethxredirect)
62 | - [getHxRetarget](#gethxretarget)
63 | - [getHxTrigger](#gethxtrigger)
64 | - [getHxTriggerAfterSettle](#gethxtriggeraftersettle)
65 | - [getHxTriggerAfterSwap](#gethxtriggerafterswap)
66 | - [hasHxPush](#hashxpush)
67 | - [hasHxRedirect](#hashxredirect)
68 | - [hasHxRefresh](#hashxrefresh)
69 | - [hasHxRetarget](#hashxretarget)
70 | - [hasHxTrigger](#hashxtrigger)
71 | - [hasHxTriggerAfterSettle](#hashxtriggeraftersettle)
72 | - [hasHxTriggerAfterSwap](#hashxtriggerafterswap)
73 | - [withAddedHxTrigger](#withaddedhxtrigger)
74 | - [withAddedHxTriggerAfterSettle](#withaddedhxtriggeraftersettle)
75 | - [withAddedHxTriggerAfterSwap](#withaddedhxtriggerafterswap)
76 | - [withHxPush](#withhxpush)
77 | - [withHxRedirect](#withhxredirect)
78 | - [withHxRefresh](#withhxrefresh)
79 | - [withHxRetarget](#withhxretarget)
80 | - [withHxTrigger](#withhxtrigger)
81 | - [withHxTriggerAfterSettle](#withhxtriggeraftersettle)
82 | - [withHxTriggerAfterSwap](#withhxtriggerafterswap)
83 | - [withoutHxPush](#withouthxpush)
84 | - [withoutHxRedirect](#withouthxredirect)
85 | - [withoutHxRefresh](#withouthxrefresh)
86 | - [withoutHxRetarget](#withouthxretarget)
87 | - [withoutHxTrigger](#withouthxtrigger)
88 | - [withoutHxTriggerAfterSettle](#withouthxtriggeraftersettle)
89 | - [withoutHxTriggerAfterSwap](#withouthxtriggerafterswap)
90 | - [Tomrf\HtmxMessage\HtmxServerRequest](#-tomrfhtmxmessagehtmxserverrequestclass)
91 | - [getHxCurrentUrl](#gethxcurrenturl)
92 | - [getHxPrompt](#gethxprompt)
93 | - [getHxTarget](#gethxtarget)
94 | - [getHxTrigger](#gethxtrigger)
95 | - [getHxTriggerName](#gethxtriggername)
96 | - [hasHxPrompt](#hashxprompt)
97 | - [hasHxTarget](#hashxtarget)
98 | - [hasHxTrigger](#hashxtrigger)
99 | - [hasHxTriggerName](#hashxtriggername)
100 | - [isHxBoosted](#ishxboosted)
101 | - [isHxHistoryRestoreRequest](#ishxhistoryrestorerequest)
102 | - [isHxRequest](#ishxrequest)
103 | - [__construct](#__construct)
104 |
105 |
106 | ***
107 |
108 | ### 📂 Tomrf\HtmxMessage\HtmxResponse::class
109 |
110 | HtmxResponse is a proxy for PSR-7 response objects implementing
111 | \Psr\Http\Message\ResponseInterface.
112 |
113 | You can safely wrap any ResponseInterface object, also those not
114 | responding to a htmx request, without affecting your application.
115 |
116 | HtmxResponse objects retains immutability in the same fashion as
117 | one would expect from a PSR-7 MessageInterface, returning a new object
118 | wrapping a new ResponseInterface instance whenever the HtmxResponse
119 | is changed.
120 |
121 | Example:
122 |
123 | $response = new HtmxResponse($response);
124 | $response = $response->withHxTrigger('myTrigger')
125 | ->withRedirect('/user/redirected')
126 | ->withHxPush(false);
127 |
128 | // ...
129 |
130 | #### getHxPush()
131 |
132 | ```php
133 | public function getHxPush(): string
134 | ```
135 |
136 | #### getHxRedirect()
137 |
138 | ```php
139 | public function getHxRedirect(): string
140 | ```
141 |
142 | #### getHxRetarget()
143 |
144 | ```php
145 | public function getHxRetarget(): string
146 | ```
147 |
148 | #### getHxTrigger()
149 |
150 | ```php
151 | public function getHxTrigger(): array
152 |
153 | @return array
154 | ```
155 |
156 | #### getHxTriggerAfterSettle()
157 |
158 | ```php
159 | public function getHxTriggerAfterSettle(): array
160 |
161 | @return array
162 | ```
163 |
164 | #### getHxTriggerAfterSwap()
165 |
166 | ```php
167 | public function getHxTriggerAfterSwap(): array
168 |
169 | @return array
170 | ```
171 |
172 | #### hasHxPush()
173 |
174 | ```php
175 | public function hasHxPush(): bool
176 | ```
177 |
178 | #### hasHxRedirect()
179 |
180 | ```php
181 | public function hasHxRedirect(): bool
182 | ```
183 |
184 | #### hasHxRefresh()
185 |
186 | ```php
187 | public function hasHxRefresh(): bool
188 | ```
189 |
190 | #### hasHxRetarget()
191 |
192 | ```php
193 | public function hasHxRetarget(): bool
194 | ```
195 |
196 | #### hasHxTrigger()
197 |
198 | ```php
199 | public function hasHxTrigger(): bool
200 | ```
201 |
202 | #### hasHxTriggerAfterSettle()
203 |
204 | ```php
205 | public function hasHxTriggerAfterSettle(): bool
206 | ```
207 |
208 | #### hasHxTriggerAfterSwap()
209 |
210 | ```php
211 | public function hasHxTriggerAfterSwap(): bool
212 | ```
213 |
214 | #### withAddedHxTrigger()
215 |
216 | ```php
217 | public function withAddedHxTrigger(
218 | string $trigger,
219 | mixed $argument = null
220 | ): static
221 | ```
222 |
223 | #### withAddedHxTriggerAfterSettle()
224 |
225 | ```php
226 | public function withAddedHxTriggerAfterSettle(
227 | string $trigger,
228 | mixed $argument = null
229 | ): static
230 | ```
231 |
232 | #### withAddedHxTriggerAfterSwap()
233 |
234 | ```php
235 | public function withAddedHxTriggerAfterSwap(
236 | string $trigger,
237 | mixed $argument = null
238 | ): static
239 | ```
240 |
241 | #### withHxPush()
242 |
243 | ```php
244 | public function withHxPush(
245 | string|bool $url
246 | ): static
247 | ```
248 |
249 | #### withHxRedirect()
250 |
251 | ```php
252 | public function withHxRedirect(
253 | string $url
254 | ): static
255 | ```
256 |
257 | #### withHxRefresh()
258 |
259 | ```php
260 | public function withHxRefresh(): static
261 | ```
262 |
263 | #### withHxRetarget()
264 |
265 | ```php
266 | public function withHxRetarget(
267 | string $selector
268 | ): static
269 | ```
270 |
271 | #### withHxTrigger()
272 |
273 | ```php
274 | public function withHxTrigger(
275 | string $trigger,
276 | mixed $argument = null
277 | ): static
278 | ```
279 |
280 | #### withHxTriggerAfterSettle()
281 |
282 | ```php
283 | public function withHxTriggerAfterSettle(
284 | string $trigger,
285 | mixed $argument = null
286 | ): static
287 | ```
288 |
289 | #### withHxTriggerAfterSwap()
290 |
291 | ```php
292 | public function withHxTriggerAfterSwap(
293 | string $trigger,
294 | mixed $argument = null
295 | ): static
296 | ```
297 |
298 | #### withoutHxPush()
299 |
300 | ```php
301 | public function withoutHxPush(): static
302 | ```
303 |
304 | #### withoutHxRedirect()
305 |
306 | ```php
307 | public function withoutHxRedirect(): static
308 | ```
309 |
310 | #### withoutHxRefresh()
311 |
312 | ```php
313 | public function withoutHxRefresh(): static
314 | ```
315 |
316 | #### withoutHxRetarget()
317 |
318 | ```php
319 | public function withoutHxRetarget(): static
320 | ```
321 |
322 | #### withoutHxTrigger()
323 |
324 | ```php
325 | public function withoutHxTrigger(): static
326 | ```
327 |
328 | #### withoutHxTriggerAfterSettle()
329 |
330 | ```php
331 | public function withoutHxTriggerAfterSettle(): static
332 | ```
333 |
334 | #### withoutHxTriggerAfterSwap()
335 |
336 | ```php
337 | public function withoutHxTriggerAfterSwap(): static
338 | ```
339 |
340 |
341 | ***
342 |
343 | ### 📂 Tomrf\HtmxMessage\HtmxServerRequest::class
344 |
345 | HtmxServerRequest is a proxy for PSR-7 server request objects implementing
346 | \Psr\Http\Message\ServerRequestInterface.
347 |
348 | You can safely wrap any ServerRequestInterface object, also those not
349 | originating from htmx, without affecting your application.
350 |
351 | Check if the request originated from htmx using the isHxRequest() method.
352 |
353 | HtmxServerRequest objects retains immutability in the same fashion as
354 | one would expect from a PSR-7 MessageInterface, returning a new object
355 | wrapping a new ServerRequestInterface instance whenever the HtmxServerRequest
356 | is changed.
357 |
358 | Example:
359 |
360 | $request = new HtmxServerRequest($request);
361 | if ($request->isHxRequest() && $request->isHxBoosted()) {
362 | // htmx request from boosted client, respond accordingly
363 | }
364 |
365 | $userPrompt = $request->getHxPrompt();
366 | // ...
367 |
368 | #### getHxCurrentUrl()
369 |
370 | ```php
371 | public function getHxCurrentUrl(): string
372 | ```
373 |
374 | #### getHxPrompt()
375 |
376 | ```php
377 | public function getHxPrompt(): string
378 | ```
379 |
380 | #### getHxTarget()
381 |
382 | ```php
383 | public function getHxTarget(): string
384 | ```
385 |
386 | #### getHxTrigger()
387 |
388 | ```php
389 | public function getHxTrigger(): string
390 | ```
391 |
392 | #### getHxTriggerName()
393 |
394 | ```php
395 | public function getHxTriggerName(): string
396 | ```
397 |
398 | #### hasHxPrompt()
399 |
400 | ```php
401 | public function hasHxPrompt(): bool
402 | ```
403 |
404 | #### hasHxTarget()
405 |
406 | ```php
407 | public function hasHxTarget(): bool
408 | ```
409 |
410 | #### hasHxTrigger()
411 |
412 | ```php
413 | public function hasHxTrigger(): bool
414 | ```
415 |
416 | #### hasHxTriggerName()
417 |
418 | ```php
419 | public function hasHxTriggerName(): bool
420 | ```
421 |
422 | #### isHxBoosted()
423 |
424 | ```php
425 | public function isHxBoosted(): bool
426 | ```
427 |
428 | #### isHxHistoryRestoreRequest()
429 |
430 | ```php
431 | public function isHxHistoryRestoreRequest(): bool
432 | ```
433 |
434 | #### isHxRequest()
435 |
436 | ```php
437 | public function isHxRequest(): bool
438 | ```
439 |
440 | #### __construct()
441 |
442 | ```php
443 | public function __construct(
444 | Psr\Http\Message\ServerRequestInterface $message
445 | ): void
446 | ```
447 |
448 |
449 |
450 | ***
451 |
452 | _Generated 2023-08-18T12:46:49+00:00 using 📚[tomrf/readme-gen](https://packagist.org/packages/tomrf/readme-gen)_
453 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tomrf/htmx-message",
3 | "type": "library",
4 | "description": "PHP PSR-7 proxy classes with convenience methods for htmx requests and responses",
5 | "keywords": [ "htmx", "http-message", "psr-7", "server-request", "http-request", "http-response" ],
6 | "homepage": "http://github.com/tomrf/htmx-message",
7 | "license": "MIT",
8 | "authors": [
9 | {
10 | "name": "Tom Rune Flo",
11 | "email": "tom@x86.no",
12 | "homepage": "http://github.com/tomrf/"
13 | }
14 | ],
15 | "extra": {
16 | "intro": [
17 | "PSR-7 `ServerRequestInterface` and `ResponseInterface` proxy classes for [`htmx`](https://htmx.org)",
18 | "with convenience methods for all special htmx headers (`HX-*`) in request and",
19 | "response objects.",
20 | "",
21 | "`HtmxResponse` is a proxy for PSR-7 response objects implementing `\\Psr\\Http\\Message\\ResponseInterface`.",
22 | "",
23 | "`HtmxServerRequest` is a proxy for PSR-7 server request objects implementing `\\Psr\\Http\\Message\\ServerRequestInterface`.",
24 | "",
25 | "You can safely wrap any `ResponseInterface` or `ServerRequestInterface` object, also those not originating from [`htmx`](htmx.org), without affecting your application.",
26 | "",
27 | "`HtmxResponse` and `HtmxServerRequest` objects retains immutability in the same fashion as one would expect from a PSR-7 `MessageInterface`, returning a new object wrapping a new `ResponseInterface` instance whenever the `HtmxResponse` is changed.",
28 | "",
29 | "Conforms to [`htmx`](https://htmx.org) version 1.9.x (latest) as per 2023-11-10."
30 | ],
31 | "example": [
32 | "$request = new HtmxRequest($request); // PSR-7 `ServerRequestInterface`",
33 | "",
34 | "if ($request->isHxRequest() && $request->isHxBoosted()) {",
35 | " $layout = 'ajax.layout';",
36 | "}",
37 | "",
38 | "[...]",
39 | "",
40 | "$response = new HtmxResponse($response); // PSR-7 `ResponseInterface`",
41 | "",
42 | "$response = $response->withHxTrigger('aSimpleTrigger')",
43 | " ->withAddedHxTrigger('triggerWithParams', ['arg' => true, 'arg2' => 7])",
44 | " ->withTriggerAfterSwap('afterSwap', time())",
45 | " ->withHxPush($newUrl);",
46 | "",
47 | "// Emit $response as normal",
48 | "[...]"
49 | ]
50 | },
51 | "require": {
52 | "php": "^8.0",
53 | "psr/http-message": "^1.0"
54 | },
55 | "require-dev": {
56 | "phpunit/phpunit": "^10.1",
57 | "phpstan/phpstan": "^1.10",
58 | "friendsofphp/php-cs-fixer": "^3.17",
59 | "phpmd/phpmd": "^2.12",
60 | "bmitch/churn-php": "^1.7",
61 | "roave/security-advisories": "dev-latest",
62 | "nyholm/psr7-server": "^1.0",
63 | "nyholm/psr7": "^1.8",
64 | "tomrf/http-emitter": "^0.0",
65 | "tomrf/readme-gen": "^0.0"
66 | },
67 | "autoload": {
68 | "psr-4": {
69 | "Tomrf\\HtmxMessage\\": "src/"
70 | }
71 | },
72 | "autoload-dev": {
73 | "psr-4": {
74 | "Tomrf\\HtmxMessage\\Test\\": "tests/"
75 | }
76 | },
77 | "scripts": {
78 | "phpunit": [
79 | "phpunit -c .phpunit.dist.xml --colors=auto --no-coverage --stderr"
80 | ],
81 | "cs-fixer": [
82 | "php-cs-fixer fix --allow-risky=yes --diff --verbose src/"
83 | ],
84 | "phpstan": [
85 | "phpstan analyze --level max src/"
86 | ],
87 | "phpmd": [
88 | "phpmd src/ ansi .phpmd-ruleset.xml"
89 | ],
90 | "churn": [
91 | "churn run src/ | tail -n +7"
92 | ],
93 | "test": [
94 | "@phpunit"
95 | ],
96 | "test-coverage": [
97 | "@putenv XDEBUG_MODE=coverage",
98 | "@phpunit --coverage-text"
99 | ],
100 | "test-coverage-html": [
101 | "@putenv XDEBUG_MODE=coverage",
102 | "@phpunit --coverage-html .phpunit.cache/code-coverage/html",
103 | "php -S localhost:0 -t .phpunit.cache/code-coverage/html"
104 | ],
105 | "test-coverage-xml": [
106 | "@putenv XDEBUG_MODE=coverage",
107 | "@phpunit --coverage-xml .phpunit.cache/code-coverage/xml",
108 | "ls -al .phpunit.cache/code-coverage/xml"
109 | ],
110 | "check": [
111 | "@test",
112 | "@phpmd",
113 | "@phpstan",
114 | "@churn"
115 | ],
116 | "clean": [
117 | "rm -rf vendor/ .phpunit.cache .php-cs-fixer.cache"
118 | ],
119 | "readme-gen": [
120 | "readme-gen ./ ./.readme.template.md"
121 | ]
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/HtmxResponse.php:
--------------------------------------------------------------------------------
1 | withHxTrigger('myTrigger')
25 | * ->withRedirect('/user/redirected')
26 | * ->withHxPush(false);
27 | *
28 | * // ...
29 | */
30 | class HtmxResponse extends ResponseInterfaceProxy
31 | {
32 | public function getHxPush(): string
33 | {
34 | return $this->getHeaderLine('HX-Push');
35 | }
36 |
37 | public function getHxRedirect(): string
38 | {
39 | return $this->getHeaderLine('HX-Redirect');
40 | }
41 |
42 | public function getHxRetarget(): string
43 | {
44 | return $this->getHeaderLine('HX-Retarget');
45 | }
46 |
47 | /**
48 | * @return array
49 | */
50 | public function getHxTrigger(): array
51 | {
52 | return (array) json_decode($this->getHeaderLine('HX-Trigger'), true);
53 | }
54 |
55 | /**
56 | * @return array
57 | */
58 | public function getHxTriggerAfterSettle(): array
59 | {
60 | return (array) json_decode($this->getHeaderLine('HX-Trigger-After-Settle'), true);
61 | }
62 |
63 | /**
64 | * @return array
65 | */
66 | public function getHxTriggerAfterSwap(): array
67 | {
68 | return (array) json_decode($this->getHeaderLine('HX-Trigger-After-Swap'), true);
69 | }
70 |
71 | // HX-Push
72 | public function hasHxPush(): bool
73 | {
74 | return 'true' === $this->getHeaderLine('HX-Push');
75 | }
76 |
77 | // HX-Redirect
78 | public function hasHxRedirect(): bool
79 | {
80 | return $this->hasHeader('HX-Redirect');
81 | }
82 |
83 | // HX-Refresh
84 |
85 | public function hasHxRefresh(): bool
86 | {
87 | return 'true' === $this->getHeaderLine('HX-Refresh');
88 | }
89 |
90 | // HX-Retarget
91 | public function hasHxRetarget(): bool
92 | {
93 | return 'true' === $this->getHeaderLine('HX-Retarget');
94 | }
95 |
96 | // HX-Trigger
97 | public function hasHxTrigger(): bool
98 | {
99 | return $this->hasHeader('HX-Trigger');
100 | }
101 |
102 | // HX-Trigger-After-Settle
103 | public function hasHxTriggerAfterSettle(): bool
104 | {
105 | return $this->hasHeader('HX-Trigger-After-Settle');
106 | }
107 |
108 | public function hasHxTriggerAfterSwap(): bool
109 | {
110 | return $this->hasHeader('HX-Trigger-After-Swap');
111 | }
112 |
113 | public function withAddedHxTrigger(string $trigger, mixed $argument = null): static
114 | {
115 | $value = (array) json_decode($this->getHeaderLine('HX-Trigger'), true);
116 | $value[$trigger] = $argument;
117 |
118 | return $this->withHeader('HX-Trigger', (string) json_encode($value));
119 | }
120 |
121 | public function withAddedHxTriggerAfterSettle(string $trigger, mixed $argument = null): static
122 | {
123 | $value = (array) json_decode($this->getHeaderLine('HX-Trigger-After-Settle'), true);
124 | $value[$trigger] = $argument;
125 |
126 | return $this->withHeader('HX-Trigger-After-Settle', (string) json_encode($value));
127 | }
128 |
129 | public function withAddedHxTriggerAfterSwap(string $trigger, mixed $argument = null): static
130 | {
131 | $value = (array) json_decode($this->getHeaderLine('HX-Trigger-After-Settle'), true);
132 | $value[$trigger] = $argument;
133 |
134 | return $this->withHeader('HX-Trigger-After-Settle', (string) json_encode($value));
135 | }
136 |
137 | public function withHxPush(string|bool $url): static
138 | {
139 | return $this->withHeader('HX-Push', (string) $url);
140 | }
141 |
142 | public function withHxRedirect(string $url): static
143 | {
144 | return $this->withHeader('HX-Redirect', $url);
145 | }
146 |
147 | public function withHxRefresh(): static
148 | {
149 | return $this->withHeader('HX-Refresh', 'true');
150 | }
151 |
152 | public function withHxRetarget(string $selector): static
153 | {
154 | return $this->withHeader('HX-Retarget', $selector);
155 | }
156 |
157 | public function withHxTrigger(string $trigger, mixed $argument = null): static
158 | {
159 | return $this->withHeader('HX-Trigger', (string) json_encode([$trigger => $argument]));
160 | }
161 |
162 | public function withHxTriggerAfterSettle(string $trigger, mixed $argument = null): static
163 | {
164 | return $this->withHeader('HX-Trigger-After-Settle', (string) json_encode([$trigger => $argument]));
165 | }
166 |
167 | public function withHxTriggerAfterSwap(string $trigger, mixed $argument = null): static
168 | {
169 | return $this->withHeader('HX-Trigger-After-Settle', (string) json_encode([$trigger => $argument]));
170 | }
171 |
172 | public function withoutHxPush(): static
173 | {
174 | return $this->withoutHeader('HX-Push');
175 | }
176 |
177 | public function withoutHxRedirect(): static
178 | {
179 | return $this->withoutHeader('HX-Redirect');
180 | }
181 |
182 | public function withoutHxRefresh(): static
183 | {
184 | return $this->withoutHeader('HX-Refresh');
185 | }
186 |
187 | public function withoutHxRetarget(): static
188 | {
189 | return $this->withoutHeader('HX-Retarget');
190 | }
191 |
192 | public function withoutHxTrigger(): static
193 | {
194 | return $this->withoutHeader('HX-Trigger');
195 | }
196 |
197 | public function withoutHxTriggerAfterSettle(): static
198 | {
199 | return $this->withoutHeader('HX-Trigger-After-Settle');
200 | }
201 |
202 | public function withoutHxTriggerAfterSwap(): static
203 | {
204 | return $this->withoutHeader('HX-Trigger-After-Swap');
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/src/HtmxServerRequest.php:
--------------------------------------------------------------------------------
1 | isHxRequest() && $request->isHxBoosted()) {
27 | * // htmx request from boosted client, respond accordingly
28 | * }
29 | *
30 | * $userPrompt = $request->getHxPrompt();
31 | * // ...
32 | */
33 | class HtmxServerRequest extends ServerRequestInterfaceProxy
34 | {
35 | public function getHxCurrentUrl(): string
36 | {
37 | return $this->message->getHeaderLine('HX-Current-URL');
38 | }
39 |
40 | public function getHxPrompt(): string
41 | {
42 | return $this->message->getHeaderLine('HX-Prompt');
43 | }
44 |
45 | public function getHxTarget(): string
46 | {
47 | return $this->message->getHeaderLine('HX-Target');
48 | }
49 |
50 | public function getHxTrigger(): string
51 | {
52 | return $this->message->getHeaderLine('HX-Trigger');
53 | }
54 |
55 | public function getHxTriggerName(): string
56 | {
57 | return $this->message->getHeaderLine('HX-Trigger-Name');
58 | }
59 |
60 | public function hasHxPrompt(): bool
61 | {
62 | return $this->message->hasHeader('HX-Prompt');
63 | }
64 |
65 | public function hasHxTarget(): bool
66 | {
67 | return $this->message->hasHeader('HX-Target');
68 | }
69 |
70 | public function hasHxTrigger(): bool
71 | {
72 | return $this->message->hasHeader('HX-Trigger');
73 | }
74 |
75 | public function hasHxTriggerName(): bool
76 | {
77 | return $this->message->hasHeader('HX-Trigger-Name');
78 | }
79 |
80 | public function isHxBoosted(): bool
81 | {
82 | return $this->message->hasHeader('HX-Boosted');
83 | }
84 |
85 | public function isHxHistoryRestoreRequest(): bool
86 | {
87 | return $this->message->hasHeader('HX-History-Restore-Request');
88 | }
89 |
90 | public function isHxRequest(): bool
91 | {
92 | return $this->message->hasHeader('HX-Request');
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/Proxy/AbstractMessageInterfaceProxy.php:
--------------------------------------------------------------------------------
1 | message = $message;
24 | }
25 |
26 | public function getBody()
27 | {
28 | return $this->message->getBody();
29 | }
30 |
31 | /**
32 | * @param string $name
33 | */
34 | public function getHeader($name)
35 | {
36 | return $this->message->getHeader($name);
37 | }
38 |
39 | /**
40 | * @param string $name
41 | */
42 | public function getHeaderLine($name)
43 | {
44 | return $this->message->getHeaderLine($name);
45 | }
46 |
47 | public function getHeaders()
48 | {
49 | return $this->message->getHeaders();
50 | }
51 |
52 | public function getProtocolVersion()
53 | {
54 | return $this->message->getProtocolVersion();
55 | }
56 |
57 | /**
58 | * @param string $name
59 | */
60 | public function hasHeader($name)
61 | {
62 | return $this->message->hasHeader($name);
63 | }
64 |
65 | /**
66 | * @param string $name
67 | * @param array|string $value
68 | */
69 | public function withAddedHeader($name, $value)
70 | {
71 | return new static($this->message->withAddedHeader($name, $value));
72 | }
73 |
74 | public function withBody(StreamInterface $body)
75 | {
76 | return new static($this->message->withBody($body));
77 | }
78 |
79 | /**
80 | * @param string $name
81 | * @param array|string $value
82 | */
83 | public function withHeader($name, $value)
84 | {
85 | return new static($this->message->withHeader($name, $value));
86 | }
87 |
88 | /**
89 | * @param string $name
90 | */
91 | public function withoutHeader($name)
92 | {
93 | return new static($this->message->withoutHeader($name));
94 | }
95 |
96 | /**
97 | * @param string $version
98 | */
99 | public function withProtocolVersion($version)
100 | {
101 | return new static($this->message->withProtocolVersion($version));
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/Proxy/ResponseInterfaceProxy.php:
--------------------------------------------------------------------------------
1 | message = $message;
23 | }
24 |
25 | public function getReasonPhrase()
26 | {
27 | return $this->message->getReasonPhrase();
28 | }
29 |
30 | public function getResponse(): ResponseInterface
31 | {
32 | return $this->message;
33 | }
34 |
35 | public function getStatusCode()
36 | {
37 | return $this->message->getStatusCode();
38 | }
39 |
40 | /**
41 | * @param int $code
42 | * @param string $reasonPhrase
43 | */
44 | public function withStatus($code, $reasonPhrase = '')
45 | {
46 | return new static($this->message->withStatus($code, $reasonPhrase));
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Proxy/ServerRequestInterfaceProxy.php:
--------------------------------------------------------------------------------
1 | message = $message;
25 | }
26 |
27 | /**
28 | * @param string $name
29 | * @param null|mixed $default
30 | */
31 | public function getAttribute($name, $default = null)
32 | {
33 | return $this->message->getAttribute($name, $default);
34 | }
35 |
36 | /**
37 | * @return array
38 | */
39 | public function getAttributes()
40 | {
41 | return $this->message->getAttributes();
42 | }
43 |
44 | /**
45 | * @return array
46 | */
47 | public function getCookieParams()
48 | {
49 | return $this->message->getCookieParams();
50 | }
51 |
52 | public function getMethod()
53 | {
54 | return $this->message->getMethod();
55 | }
56 |
57 | /**
58 | * @return null|array|object
59 | */
60 | public function getParsedBody()
61 | {
62 | return $this->message->getParsedBody();
63 | }
64 |
65 | /**
66 | * @return array
67 | */
68 | public function getQueryParams()
69 | {
70 | return $this->message->getQueryParams();
71 | }
72 |
73 | public function getRequest(): ServerRequestInterface
74 | {
75 | return $this->message;
76 | }
77 |
78 | public function getRequestTarget()
79 | {
80 | return $this->message->getRequestTarget();
81 | }
82 |
83 | /**
84 | * @return array
85 | */
86 | public function getServerParams()
87 | {
88 | return $this->message->getServerParams();
89 | }
90 |
91 | /**
92 | * @return array
93 | */
94 | public function getUploadedFiles()
95 | {
96 | return $this->message->getUploadedFiles();
97 | }
98 |
99 | public function getUri()
100 | {
101 | return $this->message->getUri();
102 | }
103 |
104 | /**
105 | * @param string $name
106 | * @param mixed $value
107 | */
108 | public function withAttribute($name, $value)
109 | {
110 | return new static($this->message->withAttribute($name, $value));
111 | }
112 |
113 | /**
114 | * @param array $cookies
115 | */
116 | public function withCookieParams(array $cookies)
117 | {
118 | return new static($this->message->withCookieParams($cookies));
119 | }
120 |
121 | /**
122 | * @param string $method
123 | */
124 | public function withMethod($method)
125 | {
126 | return new static($this->message->withMethod($method));
127 | }
128 |
129 | /**
130 | * @param string $name
131 | */
132 | public function withoutAttribute($name)
133 | {
134 | return new static($this->message->withoutAttribute($name));
135 | }
136 |
137 | /**
138 | * @param null|array|object $data
139 | */
140 | public function withParsedBody($data)
141 | {
142 | return new static($this->message->withParsedBody($data));
143 | }
144 |
145 | /**
146 | * @param string $version
147 | */
148 | public function withProtocolVersion($version)
149 | {
150 | return new static($this->message->withProtocolVersion($version));
151 | }
152 |
153 | /**
154 | * @param array $query
155 | */
156 | public function withQueryParams(array $query)
157 | {
158 | return new static($this->message->withQueryParams($query));
159 | }
160 |
161 | /**
162 | * @param string $requestTarget
163 | */
164 | public function withRequestTarget($requestTarget)
165 | {
166 | return new static($this->message->withRequestTarget($requestTarget));
167 | }
168 |
169 | /**
170 | * @param array $uploadedFiles
171 | */
172 | public function withUploadedFiles(array $uploadedFiles)
173 | {
174 | return new static($this->message->withUploadedFiles($uploadedFiles));
175 | }
176 |
177 | /**
178 | * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
179 | *
180 | * @param bool $preserveHost
181 | */
182 | public function withUri(UriInterface $uri, $preserveHost = false)
183 | {
184 | return new static($this->message->withUri($uri, $preserveHost));
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/tests/HtmxResponseTest.php:
--------------------------------------------------------------------------------
1 | withHxTrigger('first', 'value-first');
25 | $response = $response->withAddedHxTrigger('added', 'value-added');
26 |
27 | self::assertTrue($response->hasHeader('HX-Trigger'));
28 |
29 | self::assertSame(
30 | json_encode(['first' => 'value-first', 'added' => 'value-added']),
31 | $response->getHeaderLine('HX-Trigger')
32 | );
33 |
34 | self::assertSame(
35 | ['first' => 'value-first', 'added' => 'value-added'],
36 | $response->getHxTrigger()
37 | );
38 | }
39 |
40 | public function testHtmxAddedTriggerAndValueAndReplace(): void
41 | {
42 | $response = new HtmxResponse(
43 | new \Nyholm\Psr7\Response(),
44 | );
45 | $response = $response->withHxTrigger('first', 'value-first');
46 | $response = $response->withAddedHxTrigger('added', 'value-added');
47 | $response = $response->withAddedHxTrigger('added', 'value-added-replace', true);
48 |
49 | self::assertSame(
50 | json_encode(['first' => 'value-first', 'added' => 'value-added-replace']),
51 | $response->getHeaderLine('HX-Trigger')
52 | );
53 | }
54 |
55 | public function testHtmxPush(): void
56 | {
57 | $response = new HtmxResponse(
58 | new \Nyholm\Psr7\Response(),
59 | );
60 | $response = $response->withHxPush('url');
61 |
62 | self::assertTrue($response->hasHeader('HX-Push'));
63 |
64 | self::assertSame(
65 | 'url',
66 | $response->getHeaderLine('HX-Push')
67 | );
68 | self::assertSame('url', $response->getHxPush());
69 | }
70 |
71 | public function testHtmxRefresh(): void
72 | {
73 | $response = new HtmxResponse(
74 | new \Nyholm\Psr7\Response(),
75 | );
76 | $response = $response->withHxRefresh();
77 |
78 | self::assertTrue($response->hasHeader('HX-Refresh'));
79 | self::assertSame('true', $response->getHeaderLine('HX-Refresh'));
80 | }
81 |
82 | public function testHtmxRetargetWithSelector(): void
83 | {
84 | $response = new HtmxResponse(
85 | new \Nyholm\Psr7\Response(),
86 | );
87 | $response = $response->withHxRetarget('selector');
88 |
89 | self::assertTrue($response->hasHeader('HX-Retarget'));
90 | self::assertSame('selector', $response->getHeaderLine('HX-Retarget'));
91 | self::assertSame('selector', $response->getHxRetarget());
92 | }
93 |
94 | public function testHtmxTrigger(): void
95 | {
96 | $response = new HtmxResponse(
97 | new \Nyholm\Psr7\Response(),
98 | );
99 | $response = $response->withHxTrigger('test');
100 |
101 | self::assertTrue($response->hasHeader('HX-Trigger'));
102 |
103 | self::assertSame(
104 | json_encode(['test' => null]),
105 | $response->getHeaderLine('HX-Trigger')
106 | );
107 |
108 | self::assertSame(
109 | ['test' => null],
110 | $response->getHxTrigger()
111 | );
112 | }
113 |
114 | public function testHtmxTriggerWithValues(): void
115 | {
116 | $response = new HtmxResponse(
117 | new \Nyholm\Psr7\Response(),
118 | );
119 | $response = $response->withHxTrigger('test', 'value');
120 |
121 | self::assertTrue($response->hasHeader('HX-Trigger'));
122 |
123 | self::assertSame(
124 | json_encode(['test' => 'value']),
125 | $response->getHeaderLine('HX-Trigger')
126 | );
127 |
128 | self::assertSame(
129 | ['test' => 'value'],
130 | $response->getHxTrigger()
131 | );
132 | }
133 |
134 | public function testHtmxTriggerWithValuesAndAdded(): void
135 | {
136 | $response = new HtmxResponse(
137 | new \Nyholm\Psr7\Response(),
138 | );
139 | $response = $response->withHxTrigger('test', 'value');
140 | $response = $response->withAddedHxTrigger('added', 'value-added');
141 |
142 | self::assertSame(
143 | json_encode(['test' => 'value', 'added' => 'value-added']),
144 | $response->getHeaderLine('HX-Trigger')
145 | );
146 | }
147 |
148 | public function testHtmxTriggerWithValuesAndAddedAndReplace(): void
149 | {
150 | $response = new HtmxResponse(
151 | new \Nyholm\Psr7\Response(),
152 | );
153 | $response = $response->withHxTrigger('test', 'value');
154 | $response = $response->withAddedHxTrigger('added', 'value-added');
155 | $response = $response->withAddedHxTrigger('added', 'value-added-replace', true);
156 |
157 | self::assertSame(
158 | json_encode(['test' => 'value', 'added' => 'value-added-replace']),
159 | $response->getHeaderLine('HX-Trigger')
160 | );
161 | }
162 |
163 | public function testHtmxTriggerWithValuesAndReplace(): void
164 | {
165 | $response = new HtmxResponse(
166 | new \Nyholm\Psr7\Response(),
167 | );
168 | $response = $response->withHxTrigger('test', 'value');
169 | $response = $response->withHxTrigger('test', 'value-replace', true);
170 |
171 | self::assertSame(
172 | json_encode(['test' => 'value-replace']),
173 | $response->getHeaderLine('HX-Trigger')
174 | );
175 | }
176 |
177 | public function testHtmxTriggerWithValuesAndReplaceAndAdded(): void
178 | {
179 | $response = new HtmxResponse(
180 | new \Nyholm\Psr7\Response(),
181 | );
182 | $response = $response->withHxTrigger('test', 'value');
183 | $response = $response->withHxTrigger('test', 'value-replace', true);
184 | $response = $response->withAddedHxTrigger('added', 'value-added');
185 |
186 | self::assertSame(
187 | json_encode(['test' => 'value-replace', 'added' => 'value-added']),
188 | $response->getHeaderLine('HX-Trigger')
189 | );
190 | }
191 |
192 | public function testNewInstance(): void
193 | {
194 | $response = new HtmxResponse(
195 | new \Nyholm\Psr7\Response(),
196 | );
197 | self::assertInstanceOf(HtmxResponse::class, $response);
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/tests/HtmxServerRequestTest.php:
--------------------------------------------------------------------------------
1 | withoutHeader('HX-Request')))
40 | ->isHxRequest()
41 | );
42 | }
43 |
44 | public function testNewInstanceFromServerRequest(): void
45 | {
46 | $serverRequest = self::createServerRequest();
47 | $htmxRequest = new HtmxServerRequest($serverRequest);
48 |
49 | self::assertInstanceOf(ServerRequestInterface::class, $serverRequest);
50 | self::assertInstanceOf(ServerRequestInterface::class, $htmxRequest);
51 |
52 | // assert isHxRequest
53 | self::assertTrue($htmxRequest->isHxRequest());
54 |
55 | // assert request method
56 | self::assertSame('GET', $serverRequest->getMethod());
57 | self::assertSame($serverRequest->getMethod(), $htmxRequest->getMethod());
58 |
59 | // assert request uri path
60 | self::assertSame('/', $serverRequest->getUri()->getPath());
61 | self::assertSame($serverRequest->getUri(), $htmxRequest->getUri());
62 |
63 | // assert protocol version change
64 | self::assertSame('1.1', $htmxRequest->getProtocolVersion());
65 | $newRequest = $htmxRequest->withProtocolVersion('1.0');
66 | self::assertSame('1.0', $newRequest->getProtocolVersion());
67 | self::assertNotSame($htmxRequest, $newRequest);
68 |
69 | // assert adding attribute
70 | $newRequest = $htmxRequest->withAttribute('attr', 123);
71 | self::assertSame(123, $newRequest->getAttribute('attr'));
72 | self::assertNotSame($htmxRequest, $newRequest);
73 |
74 | // assert manipulating body content
75 | $newRequest->getBody()->write('foo');
76 | $newRequest->getBody()->rewind();
77 | self::assertSame('foo', $newRequest->getBody()->getContents());
78 | }
79 |
80 | public function testNewInstanceFromServerRequestWithBody(): void
81 | {
82 | $serverRequest = self::createServerRequest('POST', '/', 'foo');
83 | $htmxRequest = new HtmxServerRequest($serverRequest);
84 |
85 | self::assertInstanceOf(ServerRequestInterface::class, $serverRequest);
86 | self::assertInstanceOf(ServerRequestInterface::class, $htmxRequest);
87 |
88 | // assert isHxRequest
89 | self::assertTrue($htmxRequest->isHxRequest());
90 |
91 | // assert request method
92 | self::assertSame('POST', $serverRequest->getMethod());
93 | self::assertSame($serverRequest->getMethod(), $htmxRequest->getMethod());
94 |
95 | // assert request uri path
96 | self::assertSame('/', $serverRequest->getUri()->getPath());
97 | self::assertSame($serverRequest->getUri(), $htmxRequest->getUri());
98 |
99 | // assert protocol version change
100 | self::assertSame('1.1', $htmxRequest->getProtocolVersion());
101 | $newRequest = $htmxRequest->withProtocolVersion('1.0');
102 | self::assertSame('1.0', $newRequest->getProtocolVersion());
103 | self::assertNotSame($htmxRequest, $newRequest);
104 |
105 | // assert adding attribute
106 | $newRequest = $htmxRequest->withAttribute('attr', 123);
107 | self::assertSame(123, $newRequest->getAttribute('attr'));
108 | self::assertNotSame($htmxRequest, $newRequest);
109 | }
110 |
111 | public function testNewInstanceFromServerRequestWithBodyAndQueryParams(): void
112 | {
113 | $serverRequest = self::createServerRequest('POST', '/?foo=bar', 'foo=bar');
114 | $htmxRequest = new HtmxServerRequest($serverRequest);
115 |
116 | self::assertInstanceOf(ServerRequestInterface::class, $serverRequest);
117 | self::assertInstanceOf(ServerRequestInterface::class, $htmxRequest);
118 |
119 | // assert isHxRequest
120 | self::assertTrue($htmxRequest->isHxRequest());
121 |
122 | // assert request method
123 | self::assertSame('POST', $serverRequest->getMethod());
124 | self::assertSame($serverRequest->getMethod(), $htmxRequest->getMethod());
125 |
126 | // assert request uri path
127 | self::assertSame('/', $serverRequest->getUri()->getPath());
128 | self::assertSame($serverRequest->getUri(), $htmxRequest->getUri());
129 |
130 | // assert protocol version change
131 | self::assertSame('1.1', $htmxRequest->getProtocolVersion());
132 | $newRequest = $htmxRequest->withProtocolVersion('1.0');
133 | self::assertSame('1.0', $newRequest->getProtocolVersion());
134 | self::assertNotSame($htmxRequest, $newRequest);
135 |
136 | // assert adding attribute
137 | $newRequest = $htmxRequest->withAttribute('attr', 123);
138 | self::assertSame(123, $newRequest->getAttribute('attr'));
139 | self::assertNotSame($htmxRequest, $newRequest);
140 | }
141 |
142 | private static function createServerRequest(string $method = 'GET', string $uri = '/', ?string $body = null): ServerRequestInterface
143 | {
144 | return self::$serverRequestCreator->fromArrays(
145 | [
146 | 'REQUEST_METHOD' => $method,
147 | 'REQUEST_URI' => $uri,
148 | 'SERVER_PROTOCOL' => '1.1',
149 | 'SERVER_NAME' => 'localhost',
150 | 'SERVER_PORT' => '80',
151 | 'REMOTE_ADDR' => '::1',
152 | 'REMOTE_PORT' => '58981',
153 | 'SERVER_ADDR' => '::1',
154 | ],
155 | [
156 | 'HX-Request' => 'true',
157 | 'Accept' => 'text/html',
158 | 'Accept-Charset' => 'utf-8',
159 | 'Accept-Language' => 'en-US,en;q=0.9',
160 | 'Cache-Control' => 'no-cache',
161 | 'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36',
162 | ],
163 | $_COOKIE,
164 | $_GET,
165 | $_POST,
166 | $_FILES,
167 | $body
168 | );
169 | }
170 | }
171 |
--------------------------------------------------------------------------------