Вход Регистрация
Файл: vendor/fruitcake/php-cors/src/CorsService.php
Строк: 353
<?php

/*
 * This file is part of fruitcake/php-cors and was originally part of asm89/stack-cors
 *
 * (c) Alexander <iam.asm89@gmail.com>
 * (c) Barryvdh <barryvdh@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace FruitcakeCors;

use 
FruitcakeCorsExceptionsInvalidOptionException;
use 
SymfonyComponentHttpFoundationRequest;
use 
SymfonyComponentHttpFoundationResponse;

/**
 * @phpstan-type CorsInputOptions array{
 *  'allowedOrigins'?: string[],
 *  'allowedOriginsPatterns'?: string[],
 *  'supportsCredentials'?: bool,
 *  'allowedHeaders'?: string[],
 *  'allowedMethods'?: string[],
 *  'exposedHeaders'?: string[]|false,
 *  'maxAge'?: int|bool|null,
 *  'allowed_origins'?: string[],
 *  'allowed_origins_patterns'?: string[],
 *  'supports_credentials'?: bool,
 *  'allowed_headers'?: string[],
 *  'allowed_methods'?: string[],
 *  'exposed_headers'?: string[]|false,
 *  'max_age'?: int|bool|null
 * }
 *
 */
class CorsService
{
    
/** @var string[]  */
    
private array $allowedOrigins = [];
    
/** @var string[] */
    
private array $allowedOriginsPatterns = [];
    
/** @var string[] */
    
private array $allowedMethods = [];
    
/** @var string[] */
    
private array $allowedHeaders = [];
    
/** @var string[] */
    
private array $exposedHeaders = [];
    private 
bool $supportsCredentials false;
    private ?
int $maxAge 0;

    private 
bool $allowAllOrigins false;
    private 
bool $allowAllMethods false;
    private 
bool $allowAllHeaders false;

    
/**
     * @param CorsInputOptions $options
     */
    
public function __construct(array $options = [])
    {
        if (
$options) {
            
$this->setOptions($options);
        }
    }

    
/**
     * @param CorsInputOptions $options
     */
    
public function setOptions(array $options): void
    
{
        
$this->allowedOrigins $options['allowedOrigins'] ?? $options['allowed_origins'] ?? $this->allowedOrigins;
        
$this->allowedOriginsPatterns =
            
$options['allowedOriginsPatterns'] ?? $options['allowed_origins_patterns'] ?? $this->allowedOriginsPatterns;
        
$this->allowedMethods $options['allowedMethods'] ?? $options['allowed_methods'] ?? $this->allowedMethods;
        
$this->allowedHeaders $options['allowedHeaders'] ?? $options['allowed_headers'] ?? $this->allowedHeaders;
        
$this->supportsCredentials =
            
$options['supportsCredentials'] ?? $options['supports_credentials'] ?? $this->supportsCredentials;

        
$maxAge $this->maxAge;
        if (
array_key_exists('maxAge'$options)) {
            
$maxAge $options['maxAge'];
        } elseif (
array_key_exists('max_age'$options)) {
            
$maxAge $options['max_age'];
        }
        
$this->maxAge $maxAge === null null : (int)$maxAge;

        
$exposedHeaders $options['exposedHeaders'] ?? $options['exposed_headers'] ?? $this->exposedHeaders;
        
$this->exposedHeaders $exposedHeaders === false ? [] : $exposedHeaders;

        
$this->normalizeOptions();
    }

    private function 
normalizeOptions(): void
    
{
        
// Normalize case
        
$this->allowedHeaders array_map('strtolower'$this->allowedHeaders);
        
$this->allowedMethods array_map('strtoupper'$this->allowedMethods);

        
// Normalize ['*'] to true
        
$this->allowAllOrigins in_array('*'$this->allowedOrigins);
        
$this->allowAllHeaders in_array('*'$this->allowedHeaders);
        
$this->allowAllMethods in_array('*'$this->allowedMethods);

        
// Transform wildcard pattern
        
if (!$this->allowAllOrigins) {
            foreach (
$this->allowedOrigins as $origin) {
                if (
strpos($origin'*') !== false) {
                    
$this->allowedOriginsPatterns[] = $this->convertWildcardToPattern($origin);
                }
            }
        }
    }

    
/**
     * Create a pattern for a wildcard, based on Str::is() from Laravel
     *
     * @see https://github.com/laravel/framework/blob/5.5/src/Illuminate/Support/Str.php
     * @param string $pattern
     * @return string
     */
    
private function convertWildcardToPattern($pattern)
    {
        
$pattern preg_quote($pattern'#');

        
// Asterisks are translated into zero-or-more regular expression wildcards
        // to make it convenient to check if the strings starts with the given
        // pattern such as "*.example.com", making any string check convenient.
        
$pattern str_replace('*''.*'$pattern);

        return 
'#^' $pattern 'z#u';
    }

    public function 
isCorsRequest(Request $request): bool
    
{
        return 
$request->headers->has('Origin');
    }

    public function 
isPreflightRequest(Request $request): bool
    
{
        return 
$request->getMethod() === 'OPTIONS' && $request->headers->has('Access-Control-Request-Method');
    }

    public function 
handlePreflightRequest(Request $request): Response
    
{
        
$response = new Response();

        
$response->setStatusCode(204);

        return 
$this->addPreflightRequestHeaders($response$request);
    }

    public function 
addPreflightRequestHeaders(Response $responseRequest $request): Response
    
{
        
$this->configureAllowedOrigin($response$request);

        if (
$response->headers->has('Access-Control-Allow-Origin')) {
            
$this->configureAllowCredentials($response$request);

            
$this->configureAllowedMethods($response$request);

            
$this->configureAllowedHeaders($response$request);

            
$this->configureMaxAge($response$request);
        }

        return 
$response;
    }

    public function 
isOriginAllowed(Request $request): bool
    
{
        if (
$this->allowAllOrigins === true) {
            return 
true;
        }

        
$origin = (string) $request->headers->get('Origin');

        if (
in_array($origin$this->allowedOrigins)) {
            return 
true;
        }

        foreach (
$this->allowedOriginsPatterns as $pattern) {
            if (
preg_match($pattern$origin)) {
                return 
true;
            }
        }

        return 
false;
    }

    public function 
addActualRequestHeaders(Response $responseRequest $request): Response
    
{
        
$this->configureAllowedOrigin($response$request);

        if (
$response->headers->has('Access-Control-Allow-Origin')) {
            
$this->configureAllowCredentials($response$request);

            
$this->configureExposedHeaders($response$request);
        }

        return 
$response;
    }

    private function 
configureAllowedOrigin(Response $responseRequest $request): void
    
{
        if (
$this->allowAllOrigins === true && !$this->supportsCredentials) {
            
// Safe+cacheable, allow everything
            
$response->headers->set('Access-Control-Allow-Origin''*');
        } elseif (
$this->isSingleOriginAllowed()) {
            
// Single origins can be safely set
            
$response->headers->set('Access-Control-Allow-Origin'array_values($this->allowedOrigins)[0]);
        } else {
            
// For dynamic headers, set the requested Origin header when set and allowed
            
if ($this->isCorsRequest($request) && $this->isOriginAllowed($request)) {
                
$response->headers->set('Access-Control-Allow-Origin', (string) $request->headers->get('Origin'));
            }

            
$this->varyHeader($response'Origin');
        }
    }

    private function 
isSingleOriginAllowed(): bool
    
{
        if (
$this->allowAllOrigins === true || count($this->allowedOriginsPatterns) > 0) {
            return 
false;
        }

        return 
count($this->allowedOrigins) === 1;
    }

    private function 
configureAllowedMethods(Response $responseRequest $request): void
    
{
        if (
$this->allowAllMethods === true) {
            
$allowMethods strtoupper((string) $request->headers->get('Access-Control-Request-Method'));
            
$this->varyHeader($response'Access-Control-Request-Method');
        } else {
            
$allowMethods implode(', '$this->allowedMethods);
        }

        
$response->headers->set('Access-Control-Allow-Methods'$allowMethods);
    }

    private function 
configureAllowedHeaders(Response $responseRequest $request): void
    
{
        if (
$this->allowAllHeaders === true) {
            
$allowHeaders = (string) $request->headers->get('Access-Control-Request-Headers');
            
$this->varyHeader($response'Access-Control-Request-Headers');
        } else {
            
$allowHeaders implode(', '$this->allowedHeaders);
        }
        
$response->headers->set('Access-Control-Allow-Headers'$allowHeaders);
    }

    private function 
configureAllowCredentials(Response $responseRequest $request): void
    
{
        if (
$this->supportsCredentials) {
            
$response->headers->set('Access-Control-Allow-Credentials''true');
        }
    }

    private function 
configureExposedHeaders(Response $responseRequest $request): void
    
{
        if (
$this->exposedHeaders) {
            
$response->headers->set('Access-Control-Expose-Headers'implode(', '$this->exposedHeaders));
        }
    }

    private function 
configureMaxAge(Response $responseRequest $request): void
    
{
        if (
$this->maxAge !== null) {
            
$response->headers->set('Access-Control-Max-Age', (string) $this->maxAge);
        }
    }

    public function 
varyHeader(Response $responsestring $header): Response
    
{
        if (!
$response->headers->has('Vary')) {
            
$response->headers->set('Vary'$header);
        } elseif (!
in_array($headerexplode(', ', (string) $response->headers->get('Vary')))) {
            
$response->headers->set('Vary', ((string) $response->headers->get('Vary')) . ', ' $header);
        }

        return 
$response;
    }
}
Онлайн: 1
Реклама