<?php declare(strict_types=1); 
 
namespace Shopware\Core; 
 
use Composer\Autoload\ClassLoader; 
use Composer\InstalledVersions; 
use Doctrine\DBAL\Connection; 
use Doctrine\DBAL\DBALException; 
use Shopware\Core\Framework\Adapter\Cache\CacheIdLoader; 
use Shopware\Core\Framework\Adapter\Database\MySQLFactory; 
use Shopware\Core\Framework\Event\BeforeSendRedirectResponseEvent; 
use Shopware\Core\Framework\Event\BeforeSendResponseEvent; 
use Shopware\Core\Framework\Feature; 
use Shopware\Core\Framework\Plugin\KernelPluginLoader\DbalKernelPluginLoader; 
use Shopware\Core\Framework\Plugin\KernelPluginLoader\KernelPluginLoader; 
use Shopware\Core\Framework\Routing\CanonicalRedirectService; 
use Shopware\Core\Framework\Routing\RequestTransformerInterface; 
use Shopware\Core\Profiling\Doctrine\DebugStack; 
use Shopware\Storefront\Framework\Cache\CacheStore; 
use Symfony\Component\HttpFoundation\RedirectResponse; 
use Symfony\Component\HttpFoundation\Request; 
use Symfony\Component\HttpFoundation\Response; 
use Symfony\Component\HttpKernel\HttpCache\HttpCache; 
use Symfony\Component\HttpKernel\HttpKernelInterface; 
use Symfony\Component\HttpKernel\KernelInterface; 
use Symfony\Component\HttpKernel\TerminableInterface; 
 
class HttpKernel 
{ 
    protected static ?Connection $connection = null; 
 
    /** 
     * @var class-string<Kernel> 
     */ 
    protected static string $kernelClass = Kernel::class; 
 
    /** 
     * @var class-string<HttpCache> 
     */ 
    protected static string $httpCacheClass = HttpCache::class; 
 
    protected ?ClassLoader $classLoader; 
 
    protected string $environment; 
 
    protected bool $debug; 
 
    protected ?string $projectDir = null; 
 
    protected ?KernelPluginLoader $pluginLoader = null; 
 
    protected ?KernelInterface $kernel = null; 
 
    public function __construct(string $environment, bool $debug, ?ClassLoader $classLoader = null) 
    { 
        $this->classLoader = $classLoader; 
        $this->environment = $environment; 
        $this->debug = $debug; 
    } 
 
    /** 
     * @deprecated tag:v6.5.0 - parameter `$type` will be typed to `int` and parameter `$catch` will be typed to `bool` 
     */ 
    public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true): HttpKernelResult 
    { 
        if (!\is_int($type)) { 
            Feature::triggerDeprecationOrThrow('v6.5.0.0', 'The second parameter `$type` of `HttpKernel->handle()` will be typed to `int`'); 
        } 
 
        if (!\is_bool($catch)) { 
            Feature::triggerDeprecationOrThrow('v6.5.0.0', 'The third parameter `$catch` of `HttpKernel->handle()` will be typed to `bool`'); 
        } 
 
        try { 
            return $this->doHandle($request, (int) $type, (bool) $catch); 
        } catch (DBALException $e) { 
            $connectionParams = self::getConnection()->getParams(); 
 
            $message = str_replace([$connectionParams['url'] ?? null, $connectionParams['password'] ?? null, $connectionParams['user'] ?? null], '******', $e->getMessage()); 
 
            throw new \RuntimeException(sprintf('Could not connect to database. Message from SQL Server: %s', $message)); 
        } 
    } 
 
    public function getKernel(): KernelInterface 
    { 
        return $this->createKernel(); 
    } 
 
    /** 
     * Allows to switch the plugin loading. 
     */ 
    public function setPluginLoader(KernelPluginLoader $pluginLoader): void 
    { 
        $this->pluginLoader = $pluginLoader; 
    } 
 
    public static function getConnection(): Connection 
    { 
        if (self::$connection) { 
            return self::$connection; 
        } 
 
        self::$connection = MySQLFactory::create(); 
 
        return self::$connection; 
    } 
 
    public function terminate(Request $request, Response $response): void 
    { 
        if (!$this->kernel instanceof TerminableInterface) { 
            return; 
        } 
 
        $this->kernel->terminate($request, $response); 
    } 
 
    private function doHandle(Request $request, int $type, bool $catch): HttpKernelResult 
    { 
        // create core kernel which contains bootstrapping for plugins etc. 
        $kernel = $this->createKernel(); 
        $kernel->boot(); 
 
        $container = $kernel->getContainer(); 
 
        // transform request to resolve seo urls and detect sales channel 
        $transformed = $container 
            ->get(RequestTransformerInterface::class) 
            ->transform($request); 
 
        $redirect = $container 
            ->get(CanonicalRedirectService::class) 
            ->getRedirect($transformed); 
 
        if ($redirect instanceof RedirectResponse) { 
            $event = new BeforeSendRedirectResponseEvent($transformed, $redirect); 
            $container->get('event_dispatcher')->dispatch($event); 
 
            return new HttpKernelResult($transformed, $event->getResponse()); 
        } 
 
        // check for http caching 
        $enabled = $container->hasParameter('shopware.http.cache.enabled') 
            && $container->getParameter('shopware.http.cache.enabled'); 
        if ($enabled && $container->has(CacheStore::class)) { 
            $kernel = new static::$httpCacheClass($kernel, $container->get(CacheStore::class), null, ['debug' => $this->debug]); 
        } 
 
        $response = $kernel->handle($transformed, $type, $catch); 
 
        // fire event to trigger runtime events like seo url headers 
        $event = new BeforeSendResponseEvent($transformed, $response); 
        $container->get('event_dispatcher')->dispatch($event); 
 
        return new HttpKernelResult($transformed, $event->getResponse()); 
    } 
 
    private function createKernel(): KernelInterface 
    { 
        if ($this->kernel !== null) { 
            return $this->kernel; 
        } 
 
        if (InstalledVersions::isInstalled('shopware/platform')) { 
            $shopwareVersion = InstalledVersions::getVersion('shopware/platform') 
                . '@' . InstalledVersions::getReference('shopware/platform'); 
        } else { 
            $shopwareVersion = InstalledVersions::getVersion('shopware/core') 
                . '@' . InstalledVersions::getReference('shopware/core'); 
        } 
 
        $connection = self::getConnection(); 
 
        if ($this->environment !== 'prod') { 
            $connection->getConfiguration()->setSQLLogger(new DebugStack()); 
        } 
 
        $pluginLoader = $this->createPluginLoader($connection); 
 
        $cacheId = (new CacheIdLoader($connection))->load(); 
 
        return $this->kernel = new static::$kernelClass( 
            $this->environment, 
            $this->debug, 
            $pluginLoader, 
            $cacheId, 
            $shopwareVersion, 
            $connection, 
            $this->getProjectDir() 
        ); 
    } 
 
    private function getProjectDir(): string 
    { 
        if ($this->projectDir === null) { 
            if ($dir = $_ENV['PROJECT_ROOT'] ?? $_SERVER['PROJECT_ROOT'] ?? false) { 
                return $this->projectDir = $dir; 
            } 
 
            $r = new \ReflectionObject($this); 
 
            /** @var string $dir */ 
            $dir = $r->getFileName(); 
            if (!file_exists($dir)) { 
                throw new \LogicException(sprintf('Cannot auto-detect project dir for kernel of class "%s".', $r->name)); 
            } 
 
            $dir = $rootDir = \dirname($dir); 
            while (!file_exists($dir . '/vendor')) { 
                if ($dir === \dirname($dir)) { 
                    return $this->projectDir = $rootDir; 
                } 
                $dir = \dirname($dir); 
            } 
            $this->projectDir = $dir; 
        } 
 
        return $this->projectDir; 
    } 
 
    private function createPluginLoader(Connection $connection): KernelPluginLoader 
    { 
        if ($this->pluginLoader) { 
            return $this->pluginLoader; 
        } 
 
        if (!$this->classLoader) { 
            throw new \RuntimeException('No plugin loader and no class loader provided'); 
        } 
 
        $this->pluginLoader = new DbalKernelPluginLoader($this->classLoader, null, $connection); 
 
        return $this->pluginLoader; 
    } 
}