34
34
* 'max_age'?: int|bool|null
35
35
* }
36
36
*
37
- * @phpstan-type CorsNormalizedOptions array{
38
- * 'allowedOrigins': string[],
39
- * 'allowedOriginsPatterns': string[],
40
- * 'supportsCredentials': bool,
41
- * 'allowedHeaders': string[],
42
- * 'allowedMethods': string[],
43
- * 'exposedHeaders': string[],
44
- * 'maxAge': int|bool|null,
45
- * 'allowAllOrigins': bool,
46
- * 'allowAllHeaders': bool,
47
- * 'allowAllMethods': bool,
48
- * }
49
37
*/
50
38
class CorsService
51
39
{
52
- /** @var CorsNormalizedOptions */
53
- private $ options ;
40
+ /** @var string[] */
41
+ private $ allowedOrigins = [];
42
+ /** @var string[] */
43
+ private $ allowedOriginsPatterns = [];
44
+ /** @var string[] */
45
+ private $ allowedMethods = [];
46
+ /** @var string[] */
47
+ private $ allowedHeaders = [];
48
+ /** @var string[] */
49
+ private $ exposedHeaders = [];
50
+ /** @var bool */
51
+ private $ supportsCredentials = false ;
52
+ /** @var null|int */
53
+ private $ maxAge = 0 ;
54
+
55
+ /** @var bool */
56
+ private $ allowAllOrigins = false ;
57
+ /** @var bool */
58
+ private $ allowAllMethods = false ;
59
+ /** @var bool */
60
+ private $ allowAllHeaders = false ;
54
61
55
62
/**
56
63
* @param CorsInputOptions $options
57
64
*/
58
65
public function __construct (array $ options = [])
59
66
{
60
- $ this ->options = $ this ->normalizeOptions ($ options );
61
- }
62
-
63
- /**
64
- * @param CorsInputOptions $options
65
- * @return CorsNormalizedOptions
66
- */
67
- private function normalizeOptions (array $ options = []): array
68
- {
69
- $ options ['allowedOrigins ' ] = $ options ['allowedOrigins ' ] ?? $ options ['allowed_origins ' ] ?? [];
70
- $ options ['allowedOriginsPatterns ' ] =
71
- $ options ['allowedOriginsPatterns ' ] ?? $ options ['allowed_origins_patterns ' ] ?? [];
72
- $ options ['allowedMethods ' ] = $ options ['allowedMethods ' ] ?? $ options ['allowed_methods ' ] ?? [];
73
- $ options ['allowedHeaders ' ] = $ options ['allowedHeaders ' ] ?? $ options ['allowed_headers ' ] ?? [];
74
- $ options ['exposedHeaders ' ] = $ options ['exposedHeaders ' ] ?? $ options ['exposed_headers ' ] ?? [];
75
- $ options ['supportsCredentials ' ] = $ options ['supportsCredentials ' ] ?? $ options ['supports_credentials ' ] ?? false ;
76
-
77
- if (!array_key_exists ('maxAge ' , $ options )) {
78
- $ options ['maxAge ' ] = array_key_exists ('max_age ' , $ options ) ? $ options ['max_age ' ] : 0 ;
67
+ $ this ->allowedOrigins = $ options ['allowedOrigins ' ] ?? $ options ['allowed_origins ' ] ?? $ this ->allowedOrigins ;
68
+ $ this ->allowedOriginsPatterns =
69
+ $ options ['allowedOriginsPatterns ' ] ?? $ options ['allowed_origins_patterns ' ] ?? $ this ->allowedOriginsPatterns ;
70
+ $ this ->allowedMethods = $ options ['allowedMethods ' ] ?? $ options ['allowed_methods ' ] ?? $ this ->allowedMethods ;
71
+ $ this ->allowedHeaders = $ options ['allowedHeaders ' ] ?? $ options ['allowed_headers ' ] ?? $ this ->allowedHeaders ;
72
+ $ this ->supportsCredentials =
73
+ $ options ['supportsCredentials ' ] ?? $ options ['supports_credentials ' ] ?? $ this ->supportsCredentials ;
74
+
75
+ $ maxAge = $ this ->maxAge ;
76
+ if (array_key_exists ('maxAge ' , $ options )) {
77
+ $ maxAge = $ options ['maxAge ' ];
78
+ } elseif (array_key_exists ('max_age ' , $ options )) {
79
+ $ maxAge = $ options ['max_age ' ];
79
80
}
81
+ $ this ->maxAge = $ maxAge === null ? null : (int )$ maxAge ;
80
82
81
- if ($ options ['exposedHeaders ' ] === false ) {
82
- $ options ['exposedHeaders ' ] = [];
83
- }
83
+ $ exposedHeaders = $ options ['exposedHeaders ' ] ?? $ options ['exposed_headers ' ] ?? $ this ->exposedHeaders ;
84
+ $ this ->exposedHeaders = $ exposedHeaders === false ? [] : $ exposedHeaders ;
85
+
86
+ $ this ->validateOptions ();
87
+ $ this ->normalizeOptions ();
88
+ }
84
89
90
+ private function validateOptions (): void
91
+ {
85
92
$ arrayHeaders = [
86
93
'allowedOrigins ' ,
87
94
'allowedOriginsPatterns ' ,
@@ -90,28 +97,29 @@ private function normalizeOptions(array $options = []): array
90
97
'exposedHeaders ' ,
91
98
];
92
99
foreach ($ arrayHeaders as $ key ) {
93
- if (!is_array ($ options [ $ key] )) {
100
+ if (!is_array ($ this ->{ $ key} )) {
94
101
throw new InvalidOptionException ("CORS option ` {$ key }` should be an array " );
95
102
}
96
103
}
104
+ }
97
105
106
+ private function normalizeOptions (): void
107
+ {
98
108
// Transform wildcard pattern
99
- foreach ($ options [ ' allowedOrigins ' ] as $ origin ) {
109
+ foreach ($ this -> allowedOrigins as $ origin ) {
100
110
if (strpos ($ origin , '* ' ) !== false ) {
101
- $ options [ ' allowedOriginsPatterns ' ] [] = $ this ->convertWildcardToPattern ($ origin );
111
+ $ this -> allowedOriginsPatterns [] = $ this ->convertWildcardToPattern ($ origin );
102
112
}
103
113
}
104
114
105
115
// Normalize case
106
- $ options [ ' allowedHeaders ' ] = array_map ('strtolower ' , $ options [ ' allowedHeaders ' ] );
107
- $ options [ ' allowedMethods ' ] = array_map ('strtoupper ' , $ options [ ' allowedMethods ' ] );
116
+ $ this -> allowedHeaders = array_map ('strtolower ' , $ this -> allowedHeaders );
117
+ $ this -> allowedMethods = array_map ('strtoupper ' , $ this -> allowedMethods );
108
118
109
119
// Normalize ['*'] to true
110
- $ options ['allowAllOrigins ' ] = in_array ('* ' , $ options ['allowedOrigins ' ]);
111
- $ options ['allowAllHeaders ' ] = in_array ('* ' , $ options ['allowedHeaders ' ]);
112
- $ options ['allowAllMethods ' ] = in_array ('* ' , $ options ['allowedMethods ' ]);
113
-
114
- return $ options ;
120
+ $ this ->allowAllOrigins = in_array ('* ' , $ this ->allowedOrigins );
121
+ $ this ->allowAllHeaders = in_array ('* ' , $ this ->allowedHeaders );
122
+ $ this ->allowAllMethods = in_array ('* ' , $ this ->allowedMethods );
115
123
}
116
124
117
125
/**
@@ -171,17 +179,17 @@ public function addPreflightRequestHeaders(Response $response, Request $request)
171
179
172
180
public function isOriginAllowed (Request $ request ): bool
173
181
{
174
- if ($ this ->options [ ' allowAllOrigins ' ] === true ) {
182
+ if ($ this ->allowAllOrigins === true ) {
175
183
return true ;
176
184
}
177
185
178
186
$ origin = (string ) $ request ->headers ->get ('Origin ' );
179
187
180
- if (in_array ($ origin , $ this ->options [ ' allowedOrigins ' ] )) {
188
+ if (in_array ($ origin , $ this ->allowedOrigins )) {
181
189
return true ;
182
190
}
183
191
184
- foreach ($ this ->options [ ' allowedOriginsPatterns ' ] as $ pattern ) {
192
+ foreach ($ this ->allowedOriginsPatterns as $ pattern ) {
185
193
if (preg_match ($ pattern , $ origin )) {
186
194
return true ;
187
195
}
@@ -205,12 +213,12 @@ public function addActualRequestHeaders(Response $response, Request $request): R
205
213
206
214
private function configureAllowedOrigin (Response $ response , Request $ request ): void
207
215
{
208
- if ($ this ->options [ ' allowAllOrigins ' ] === true && !$ this ->options [ ' supportsCredentials ' ] ) {
216
+ if ($ this ->allowAllOrigins === true && !$ this ->supportsCredentials ) {
209
217
// Safe+cacheable, allow everything
210
218
$ response ->headers ->set ('Access-Control-Allow-Origin ' , '* ' );
211
219
} elseif ($ this ->isSingleOriginAllowed ()) {
212
220
// Single origins can be safely set
213
- $ response ->headers ->set ('Access-Control-Allow-Origin ' , array_values ($ this ->options [ ' allowedOrigins ' ] )[0 ]);
221
+ $ response ->headers ->set ('Access-Control-Allow-Origin ' , array_values ($ this ->allowedOrigins )[0 ]);
214
222
} else {
215
223
// For dynamic headers, set the requested Origin header when set and allowed
216
224
if ($ this ->isCorsRequest ($ request ) && $ this ->isOriginAllowed ($ request )) {
@@ -223,54 +231,54 @@ private function configureAllowedOrigin(Response $response, Request $request): v
223
231
224
232
private function isSingleOriginAllowed (): bool
225
233
{
226
- if ($ this ->options [ ' allowAllOrigins ' ] === true || count ($ this ->options [ ' allowedOriginsPatterns ' ] ) > 0 ) {
234
+ if ($ this ->allowAllOrigins === true || count ($ this ->allowedOriginsPatterns ) > 0 ) {
227
235
return false ;
228
236
}
229
237
230
- return count ($ this ->options [ ' allowedOrigins ' ] ) === 1 ;
238
+ return count ($ this ->allowedOrigins ) === 1 ;
231
239
}
232
240
233
241
private function configureAllowedMethods (Response $ response , Request $ request ): void
234
242
{
235
- if ($ this ->options [ ' allowAllMethods ' ] === true ) {
243
+ if ($ this ->allowAllMethods === true ) {
236
244
$ allowMethods = strtoupper ((string ) $ request ->headers ->get ('Access-Control-Request-Method ' ));
237
245
$ this ->varyHeader ($ response , 'Access-Control-Request-Method ' );
238
246
} else {
239
- $ allowMethods = implode (', ' , $ this ->options [ ' allowedMethods ' ] );
247
+ $ allowMethods = implode (', ' , $ this ->allowedMethods );
240
248
}
241
249
242
250
$ response ->headers ->set ('Access-Control-Allow-Methods ' , $ allowMethods );
243
251
}
244
252
245
253
private function configureAllowedHeaders (Response $ response , Request $ request ): void
246
254
{
247
- if ($ this ->options [ ' allowAllHeaders ' ] === true ) {
255
+ if ($ this ->allowAllHeaders === true ) {
248
256
$ allowHeaders = (string ) $ request ->headers ->get ('Access-Control-Request-Headers ' );
249
257
$ this ->varyHeader ($ response , 'Access-Control-Request-Headers ' );
250
258
} else {
251
- $ allowHeaders = implode (', ' , $ this ->options [ ' allowedHeaders ' ] );
259
+ $ allowHeaders = implode (', ' , $ this ->allowedHeaders );
252
260
}
253
261
$ response ->headers ->set ('Access-Control-Allow-Headers ' , $ allowHeaders );
254
262
}
255
263
256
264
private function configureAllowCredentials (Response $ response , Request $ request ): void
257
265
{
258
- if ($ this ->options [ ' supportsCredentials ' ] ) {
266
+ if ($ this ->supportsCredentials ) {
259
267
$ response ->headers ->set ('Access-Control-Allow-Credentials ' , 'true ' );
260
268
}
261
269
}
262
270
263
271
private function configureExposedHeaders (Response $ response , Request $ request ): void
264
272
{
265
- if ($ this ->options [ ' exposedHeaders ' ] ) {
266
- $ response ->headers ->set ('Access-Control-Expose-Headers ' , implode (', ' , $ this ->options [ ' exposedHeaders ' ] ));
273
+ if ($ this ->exposedHeaders ) {
274
+ $ response ->headers ->set ('Access-Control-Expose-Headers ' , implode (', ' , $ this ->exposedHeaders ));
267
275
}
268
276
}
269
277
270
278
private function configureMaxAge (Response $ response , Request $ request ): void
271
279
{
272
- if ($ this ->options [ ' maxAge ' ] !== null ) {
273
- $ response ->headers ->set ('Access-Control-Max-Age ' , (string ) $ this ->options [ ' maxAge ' ] );
280
+ if ($ this ->maxAge !== null ) {
281
+ $ response ->headers ->set ('Access-Control-Max-Age ' , (string ) $ this ->maxAge );
274
282
}
275
283
}
276
284
0 commit comments