@@ -10,32 +10,198 @@ class Unpoly
10
10
{
11
11
/**
12
12
* Response header to echo request's URL.
13
- *
14
- * @var string
15
13
*/
16
14
const LOCATION_RESPONSE_HEADER = 'X-Up-Location ' ;
17
15
18
16
/**
19
17
* Response header to echo request's method.
20
- *
21
- * @var string
22
18
*/
23
19
const METHOD_RESPONSE_HEADER = 'X-Up-Method ' ;
24
20
25
21
/**
26
22
* Cookie name to echo request's method.
27
- *
28
- * @var string
29
23
*/
30
24
const METHOD_COOKIE_NAME = '_up_method ' ;
31
25
26
+ /**
27
+ * @see Webstronauts\Unpoly\Unpoly::isUnpolyRequest()
28
+ */
29
+ public static function isUpRequest (Request $ request ): bool
30
+ {
31
+ return static ::isUnpolyRequest ($ request );
32
+ }
33
+
34
+ /**
35
+ * Returns whether the current request is a [page fragment update](https://unpoly.com/up.replace)
36
+ * triggered by an Unpoly frontend.
37
+ *
38
+ * This will eventually just check for the `X-Up-Version header`.
39
+ * Just in case a user still has an older version of Unpoly running on the frontend,
40
+ * we also check for the X-Up-Target header.
41
+ */
42
+ public static function isUnpolyRequest (Request $ request ): bool
43
+ {
44
+ return static ::getVersion ($ request ) !== null || static ::getTarget ($ request ) !== null ;
45
+ }
46
+
47
+ /**
48
+ * Returns the current Unpoly version.
49
+ *
50
+ * The version is guaranteed to be set for all Unpoly requests.
51
+ */
52
+ public static function getVersion (Request $ request ): ?string
53
+ {
54
+ return $ request ->headers ->get ('X-Up-Version ' );
55
+ }
56
+
57
+ /**
58
+ * Returns the mode of the targeted layer.
59
+ *
60
+ * Server-side code is free to render different HTML for different modes.
61
+ * For example, you might prefer to not render a site navigation for overlays.
62
+ */
63
+ public static function getMode (Request $ request ): ?string
64
+ {
65
+ return $ request ->headers ->get ('X-Up-Mode ' );
66
+ }
67
+
68
+ /**
69
+ * Returns the mode of the layer targeted for a failed fragment update.
70
+ *
71
+ * A fragment update is considered failed if the server responds with
72
+ * a status code other than 2xx, but still renders HTML.
73
+ * Server-side code is free to render different HTML for different modes.
74
+ * For example, you might prefer to not render a site navigation for overlays.
75
+ */
76
+ public static function getFailMode (Request $ request ): ?string
77
+ {
78
+ return $ request ->headers ->get ('X-Up-Fail-Mode ' );
79
+ }
80
+
81
+ /**
82
+ * Returns the CSS selector for a fragment that Unpoly will update in
83
+ * case of a successful response (200 status code).
84
+ *
85
+ * The Unpoly frontend will expect an HTML response containing an element
86
+ * that matches this selector.
87
+ *
88
+ * Server-side code is free to optimize its successful response by only returning HTML
89
+ * that matches this selector.
90
+ */
91
+ public static function getTarget (Request $ request ): ?string
92
+ {
93
+ return $ request ->headers ->get ('X-Up-Target ' );
94
+ }
95
+
96
+ /**
97
+ * Returns the CSS selector for a fragment that Unpoly will update in
98
+ * case of an failed response. Server errors or validation failures are
99
+ * all examples for a failed response (non-200 status code).
100
+ *
101
+ * The Unpoly frontend will expect an HTML response containing an element
102
+ * that matches this selector.
103
+ *
104
+ * Server-side code is free to optimize its response by only returning HTML
105
+ * that matches this selector.
106
+ */
107
+ public static function getFailTarget (Request $ request ): ?string
108
+ {
109
+ return $ request ->headers ->get ('X-Up-Fail-Target ' );
110
+ }
111
+
112
+ /**
113
+ * Returns whether the given CSS selector is targeted by the current fragment
114
+ * update in case of a successful response (200 status code).
115
+ *
116
+ * Note that the matching logic is very simplistic and does not actually know
117
+ * how your page layout is structured. It will return `true` if
118
+ * the tested selector and the requested CSS selector matches exactly, or if the
119
+ * requested selector is `body` or `html`.
120
+ *
121
+ * Always returns `true` if the current request is not an Unpoly fragment update.
122
+ */
123
+ public static function isTarget (Request $ request , string $ target ): bool
124
+ {
125
+ return static ::queryTarget (static ::getTarget ($ request ), $ target );
126
+ }
127
+
128
+ /**
129
+ * Returns whether the given CSS selector is targeted by the current fragment
130
+ * update in case of a failed response (non-200 status code).
131
+ *
132
+ * Note that the matching logic is very simplistic and does not actually know
133
+ * how your page layout is structured. It will return `true` if
134
+ * the tested selector and the requested CSS selector matches exactly, or if the
135
+ * requested selector is `body` or `html`.
136
+ *
137
+ * Always returns `true` if the current request is not an Unpoly fragment update.
138
+ */
139
+ public static function isFailTarget (Request $ request , string $ target ): bool
140
+ {
141
+ return static ::queryTarget (static ::getFailTarget ($ request ), $ target );
142
+ }
143
+
144
+ /**
145
+ * Returns whether the given CSS selector is targeted by the current fragment
146
+ * update for either a success or a failed response.
147
+ *
148
+ * Note that the matching logic is very simplistic and does not actually know
149
+ * how your page layout is structured. It will return `true` if
150
+ * the tested selector and the requested CSS selector matches exactly, or if the
151
+ * requested selector is `body` or `html`.
152
+ *
153
+ * Always returns `true` if the current request is not an Unpoly fragment update.
154
+ */
155
+ public static function isAnyTarget (Request $ request , string $ target ): bool
156
+ {
157
+ return static ::isTarget ($ request , $ target ) || static ::isFailTarget ($ request , $ target );
158
+ }
159
+
160
+ /**
161
+ * Returns whether the current form submission should be
162
+ * [validated](https://unpoly.com/input-up-validate) (and not be saved to the database).
163
+ */
164
+ public static function isValidationRequest (Request $ request ): bool
165
+ {
166
+ return static ::getValidateNames ($ request ) !== null ;
167
+ }
168
+
169
+ /**
170
+ * If the current form submission is a [validation](https://unpoly.com/input-up-validate),
171
+ * this returns the name attributes of the form field that has triggered
172
+ * the validation.
173
+ *
174
+ * Note that multiple validating form fields may be batched into a single request.
175
+ */
176
+ public static function getValidateNames (Request $ request ): ?string
177
+ {
178
+ return $ request ->headers ->get ('X-Up-Validate ' );
179
+ }
180
+
181
+ protected static function queryTarget (string $ actualTarget , string $ testedTarget ): bool
182
+ {
183
+ if (! static ::isUnpolyRequest ($ request )) {
184
+ return true ;
185
+ }
186
+
187
+ if ($ actualTarget === $ testedTarget ) {
188
+ return true ;
189
+ }
190
+
191
+ if ($ actualTarget === 'html ' ) {
192
+ return true ;
193
+ }
194
+
195
+ if ($ actualTarget === 'body ' && ! in_array ($ testedTarget , ['head ' , 'title ' , 'meta ' ])) {
196
+ return true ;
197
+ }
198
+
199
+ return false ;
200
+ }
201
+
32
202
/**
33
203
* Modifies the HTTP headers and cookies of the response so that it can be
34
204
* properly handled by the Unpoly javascript.
35
- *
36
- * @param \Symfony\Component\HttpFoundation\Request $request
37
- * @param \Symfony\Component\HttpFoundation\Response $response
38
- * @return void
39
205
*/
40
206
public function decorateResponse (Request $ request , Response $ response ): void
41
207
{
@@ -46,10 +212,6 @@ public function decorateResponse(Request $request, Response $response): void
46
212
/**
47
213
* Unpoly requires these headers to detect redirects,
48
214
* which are otherwise undetectable for an AJAX client.
49
- *
50
- * @param \Symfony\Component\HttpFoundation\Request $request
51
- * @param \Symfony\Component\HttpFoundation\Response $response
52
- * @return void
53
215
*/
54
216
protected function echoRequestHeaders (Request $ request , Response $ response ): void
55
217
{
@@ -67,14 +229,10 @@ protected function echoRequestHeaders(Request $request, Response $response): voi
67
229
*
68
230
* @see https://github.com/rails/turbolinks/search?q=request_method&ref=cmdform
69
231
* @see https://github.com/rails/turbolinks/blob/83d4b3d2c52a681f07900c28adb28bc8da604733/README.md#initialization
70
- *
71
- * @param \Symfony\Component\HttpFoundation\Request $request
72
- * @param \Symfony\Component\HttpFoundation\Response $response
73
- * @return void
74
232
*/
75
233
protected function appendMethodCookie (Request $ request , Response $ response ): void
76
234
{
77
- if (! $ request ->isMethod ('GET ' ) && ! $ request-> headers -> has ( ' X-Up-Target ' )) {
235
+ if (! $ request ->isMethod ('GET ' ) && ! static :: isUpRequest ( $ request )) {
78
236
$ response ->headers ->setCookie (new Cookie (self ::METHOD_COOKIE_NAME , $ request ->getMethod ()));
79
237
} else {
80
238
$ response ->headers ->removeCookie (self ::METHOD_COOKIE_NAME );
0 commit comments