src/Core/Framework/Update/Services/UpdateHtaccess.php line 38

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\Update\Services;
  3. use Shopware\Core\Framework\Log\Package;
  4. use Shopware\Core\Framework\Update\Event\UpdatePostFinishEvent;
  5. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  6. /**
  7.  * @internal
  8.  */
  9. #[Package('system-settings')]
  10. class UpdateHtaccess implements EventSubscriberInterface
  11. {
  12.     private const MARKER_START '# BEGIN Shopware';
  13.     private const MARKER_STOP '# END Shopware';
  14.     private const INSTRUCTIONS '# The directives (lines) between "# BEGIN Shopware" and "# END Shopware" are dynamically generated. Any changes to the directives between these markers will be overwritten.';
  15.     private const OLD_FILES = [
  16.         '9ab5be8c4bbff3490f3ae367af8a30d7'// https://github.com/shopware/production/commit/bebf9adc90bf5d7b0d53a149cc5bdba328696086
  17.         'ba812f2a64b337b032b10685ca6e2308'// https://github.com/shopware/production/commit/18ce6ffc904b8d2d237dc4ee6654c1fa9a6df719
  18.     ];
  19.     /**
  20.      * @internal
  21.      */
  22.     public function __construct(private readonly string $htaccessPath)
  23.     {
  24.     }
  25.     public static function getSubscribedEvents(): array
  26.     {
  27.         return [
  28.             UpdatePostFinishEvent::class => 'update',
  29.         ];
  30.     }
  31.     public function update(): void
  32.     {
  33.         if (!file_exists($this->htaccessPath) || !file_exists($this->htaccessPath '.dist')) {
  34.             return;
  35.         }
  36.         if (\in_array(md5_file($this->htaccessPath), self::OLD_FILEStrue)) {
  37.             $this->replaceFile($this->htaccessPath);
  38.             return;
  39.         }
  40.         $content file_get_contents($this->htaccessPath);
  41.         // User has deleted the markers. So we will ignore the update process
  42.         if (!$content || !str_contains($contentself::MARKER_START) || !str_contains($contentself::MARKER_STOP)) {
  43.             return;
  44.         }
  45.         $this->updateByMarkers($this->htaccessPath);
  46.     }
  47.     /**
  48.      * Replace entire .htaccess from dist
  49.      */
  50.     private function replaceFile(string $path): void
  51.     {
  52.         $dist $path '.dist';
  53.         $perms fileperms($dist);
  54.         copy($dist$path);
  55.         if ($perms) {
  56.             chmod($path$perms 0644);
  57.         }
  58.     }
  59.     private function updateByMarkers(string $path): void
  60.     {
  61.         [$pre$_$post] = $this->getLinesFromMarkedFile($path);
  62.         [$_$existing$_] = $this->getLinesFromMarkedFile($path '.dist');
  63.         if (!\in_array(self::INSTRUCTIONS$existingtrue)) {
  64.             array_unshift($existingself::INSTRUCTIONS);
  65.         }
  66.         array_unshift($existingself::MARKER_START);
  67.         $existing[] = self::MARKER_STOP;
  68.         $newFile implode("\n", [...$pre, ...$existing, ...$post]);
  69.         $perms fileperms($path);
  70.         file_put_contents($path$newFile);
  71.         if ($perms) {
  72.             chmod($path$perms 0644);
  73.         }
  74.     }
  75.     /**
  76.      * @return array{0: list<string>, 1: list<string>, 2: list<string>}
  77.      */
  78.     private function getLinesFromMarkedFile(string $path): array
  79.     {
  80.         $fp fopen($path'rb+');
  81.         if (!$fp) {
  82.             // @codeCoverageIgnoreStart
  83.             return [[], [], []];
  84.             // @codeCoverageIgnoreEnd
  85.         }
  86.         $lines = [];
  87.         while (!feof($fp)) {
  88.             if ($line fgets($fp)) {
  89.                 $lines[] = rtrim($line"\r\n");
  90.             }
  91.         }
  92.         $foundStart false;
  93.         $foundStop false;
  94.         $preLines = [];
  95.         $postLines = [];
  96.         $existingLines = [];
  97.         foreach ($lines as $line) {
  98.             if (!$foundStart && str_starts_with($lineself::MARKER_START)) {
  99.                 $foundStart true;
  100.                 continue;
  101.             }
  102.             if (!$foundStop && str_starts_with($lineself::MARKER_STOP)) {
  103.                 $foundStop true;
  104.                 continue;
  105.             }
  106.             if (!$foundStart) {
  107.                 $preLines[] = $line;
  108.             } elseif ($foundStop) {
  109.                 $postLines[] = $line;
  110.             } else {
  111.                 $existingLines[] = $line;
  112.             }
  113.         }
  114.         return [$preLines$existingLines$postLines];
  115.     }
  116. }