3434 * 'max_age'?: int|bool|null
3535 * }
3636 *
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- * }
4937 */
5038class CorsService
5139{
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 ;
5461
5562 /**
5663 * @param CorsInputOptions $options
5764 */
5865 public function __construct (array $ options = [])
5966 {
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 ' ];
7980 }
81+ $ this ->maxAge = $ maxAge === null ? null : (int )$ maxAge ;
8082
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+ }
8489
90+ private function validateOptions (): void
91+ {
8592 $ arrayHeaders = [
8693 'allowedOrigins ' ,
8794 'allowedOriginsPatterns ' ,
@@ -90,28 +97,29 @@ private function normalizeOptions(array $options = []): array
9097 'exposedHeaders ' ,
9198 ];
9299 foreach ($ arrayHeaders as $ key ) {
93- if (!is_array ($ options [ $ key] )) {
100+ if (!is_array ($ this ->{ $ key} )) {
94101 throw new InvalidOptionException ("CORS option ` {$ key }` should be an array " );
95102 }
96103 }
104+ }
97105
106+ private function normalizeOptions (): void
107+ {
98108 // Transform wildcard pattern
99- foreach ($ options [ ' allowedOrigins ' ] as $ origin ) {
109+ foreach ($ this -> allowedOrigins as $ origin ) {
100110 if (strpos ($ origin , '* ' ) !== false ) {
101- $ options [ ' allowedOriginsPatterns ' ] [] = $ this ->convertWildcardToPattern ($ origin );
111+ $ this -> allowedOriginsPatterns [] = $ this ->convertWildcardToPattern ($ origin );
102112 }
103113 }
104114
105115 // 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 );
108118
109119 // 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 );
115123 }
116124
117125 /**
@@ -171,17 +179,17 @@ public function addPreflightRequestHeaders(Response $response, Request $request)
171179
172180 public function isOriginAllowed (Request $ request ): bool
173181 {
174- if ($ this ->options [ ' allowAllOrigins ' ] === true ) {
182+ if ($ this ->allowAllOrigins === true ) {
175183 return true ;
176184 }
177185
178186 $ origin = (string ) $ request ->headers ->get ('Origin ' );
179187
180- if (in_array ($ origin , $ this ->options [ ' allowedOrigins ' ] )) {
188+ if (in_array ($ origin , $ this ->allowedOrigins )) {
181189 return true ;
182190 }
183191
184- foreach ($ this ->options [ ' allowedOriginsPatterns ' ] as $ pattern ) {
192+ foreach ($ this ->allowedOriginsPatterns as $ pattern ) {
185193 if (preg_match ($ pattern , $ origin )) {
186194 return true ;
187195 }
@@ -205,12 +213,12 @@ public function addActualRequestHeaders(Response $response, Request $request): R
205213
206214 private function configureAllowedOrigin (Response $ response , Request $ request ): void
207215 {
208- if ($ this ->options [ ' allowAllOrigins ' ] === true && !$ this ->options [ ' supportsCredentials ' ] ) {
216+ if ($ this ->allowAllOrigins === true && !$ this ->supportsCredentials ) {
209217 // Safe+cacheable, allow everything
210218 $ response ->headers ->set ('Access-Control-Allow-Origin ' , '* ' );
211219 } elseif ($ this ->isSingleOriginAllowed ()) {
212220 // 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 ]);
214222 } else {
215223 // For dynamic headers, set the requested Origin header when set and allowed
216224 if ($ this ->isCorsRequest ($ request ) && $ this ->isOriginAllowed ($ request )) {
@@ -223,54 +231,54 @@ private function configureAllowedOrigin(Response $response, Request $request): v
223231
224232 private function isSingleOriginAllowed (): bool
225233 {
226- if ($ this ->options [ ' allowAllOrigins ' ] === true || count ($ this ->options [ ' allowedOriginsPatterns ' ] ) > 0 ) {
234+ if ($ this ->allowAllOrigins === true || count ($ this ->allowedOriginsPatterns ) > 0 ) {
227235 return false ;
228236 }
229237
230- return count ($ this ->options [ ' allowedOrigins ' ] ) === 1 ;
238+ return count ($ this ->allowedOrigins ) === 1 ;
231239 }
232240
233241 private function configureAllowedMethods (Response $ response , Request $ request ): void
234242 {
235- if ($ this ->options [ ' allowAllMethods ' ] === true ) {
243+ if ($ this ->allowAllMethods === true ) {
236244 $ allowMethods = strtoupper ((string ) $ request ->headers ->get ('Access-Control-Request-Method ' ));
237245 $ this ->varyHeader ($ response , 'Access-Control-Request-Method ' );
238246 } else {
239- $ allowMethods = implode (', ' , $ this ->options [ ' allowedMethods ' ] );
247+ $ allowMethods = implode (', ' , $ this ->allowedMethods );
240248 }
241249
242250 $ response ->headers ->set ('Access-Control-Allow-Methods ' , $ allowMethods );
243251 }
244252
245253 private function configureAllowedHeaders (Response $ response , Request $ request ): void
246254 {
247- if ($ this ->options [ ' allowAllHeaders ' ] === true ) {
255+ if ($ this ->allowAllHeaders === true ) {
248256 $ allowHeaders = (string ) $ request ->headers ->get ('Access-Control-Request-Headers ' );
249257 $ this ->varyHeader ($ response , 'Access-Control-Request-Headers ' );
250258 } else {
251- $ allowHeaders = implode (', ' , $ this ->options [ ' allowedHeaders ' ] );
259+ $ allowHeaders = implode (', ' , $ this ->allowedHeaders );
252260 }
253261 $ response ->headers ->set ('Access-Control-Allow-Headers ' , $ allowHeaders );
254262 }
255263
256264 private function configureAllowCredentials (Response $ response , Request $ request ): void
257265 {
258- if ($ this ->options [ ' supportsCredentials ' ] ) {
266+ if ($ this ->supportsCredentials ) {
259267 $ response ->headers ->set ('Access-Control-Allow-Credentials ' , 'true ' );
260268 }
261269 }
262270
263271 private function configureExposedHeaders (Response $ response , Request $ request ): void
264272 {
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 ));
267275 }
268276 }
269277
270278 private function configureMaxAge (Response $ response , Request $ request ): void
271279 {
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 );
274282 }
275283 }
276284
0 commit comments