Skip to content

Commit c73d628

Browse files
authored
[6.x] Tolerate a folded X-Statamic-Pagination header when warming (#14882)
1 parent a9b09b0 commit c73d628

4 files changed

Lines changed: 83 additions & 4 deletions

File tree

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Statamic\Console\Commands\Concerns;
4+
5+
use Psr\Http\Message\ResponseInterface;
6+
7+
trait NormalizesPaginationHeader
8+
{
9+
/**
10+
* @return array{0: int, 1: int, 2: string}
11+
*/
12+
protected function paginationHeader(ResponseInterface $response): array
13+
{
14+
// A proxy or CDN may fold the repeated X-Statamic-Pagination header into a single
15+
// comma-joined line, so we normalize both framings before destructuring. The limit
16+
// keeps a page name that itself contains a comma intact.
17+
[$current, $total, $name] = array_map(
18+
'trim',
19+
explode(',', implode(',', $response->getHeader('X-Statamic-Pagination')), 3)
20+
);
21+
22+
return [(int) $current, (int) $total, $name];
23+
}
24+
}

src/Console/Commands/StaticWarm.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Illuminate\Http\Request as HttpRequest;
1313
use Illuminate\Routing\Route;
1414
use Illuminate\Support\Collection;
15+
use Statamic\Console\Commands\Concerns\NormalizesPaginationHeader;
1516
use Statamic\Console\EnhancesCommands;
1617
use Statamic\Console\RunsInPlease;
1718
use Statamic\Entries\Collection as EntriesCollection;
@@ -31,6 +32,7 @@ class StaticWarm extends Command
3132
{
3233
use EnhancesCommands;
3334
use Hookable;
35+
use NormalizesPaginationHeader;
3436
use RunsInPlease;
3537

3638
protected $signature = 'statamic:static:warm
@@ -173,7 +175,7 @@ public function outputSuccessLine(Response $response, $index): void
173175
$this->components->twoColumnDetail($this->getRelativeUri($this->uris()->get($index)), '<info>✓ Cached</info>');
174176

175177
if ($response->hasHeader('X-Statamic-Pagination')) {
176-
[$currentPage, $totalPages, $pageName] = $response->getHeader('X-Statamic-Pagination');
178+
[$currentPage, $totalPages, $pageName] = $this->paginationHeader($response);
177179

178180
$this->warmPaginatedPages($this->uris()->get($index), $currentPage, $totalPages, $pageName);
179181
}

src/Console/Commands/StaticWarmJob.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
use Illuminate\Foundation\Bus\Dispatchable;
1111
use Illuminate\Queue\InteractsWithQueue;
1212
use Psr\Http\Message\ResponseInterface;
13+
use Statamic\Console\Commands\Concerns\NormalizesPaginationHeader;
1314

1415
class StaticWarmJob implements ShouldBeUnique, ShouldQueue
1516
{
16-
use Dispatchable, InteractsWithQueue, Queueable;
17+
use Dispatchable, InteractsWithQueue, NormalizesPaginationHeader, Queueable;
1718

1819
public $uniqueId;
1920
public $tries = 1;
@@ -28,7 +29,7 @@ public function handle()
2829
$response = (new Client($this->clientConfig))->send($this->request);
2930

3031
if ($this->shouldWarmPaginatedPages($response)) {
31-
[$currentPage, $totalPages, $pageName] = $response->getHeader('X-Statamic-Pagination');
32+
[$currentPage, $totalPages, $pageName] = $this->paginationHeader($response);
3233

3334
collect(range($currentPage, $totalPages))
3435
->map(function (int $page) use ($pageName): string {
@@ -53,7 +54,7 @@ private function shouldWarmPaginatedPages(ResponseInterface $response): bool
5354
return false;
5455
}
5556

56-
[$currentPage, $totalPages, $pageName] = $response->getHeader('X-Statamic-Pagination');
57+
[$currentPage, $totalPages, $pageName] = $this->paginationHeader($response);
5758

5859
return ! str_contains($this->request->getUri()->getQuery(), "{$pageName}=");
5960
}

tests/Console/Commands/StaticWarmJobTest.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use GuzzleHttp\Psr7\Response;
99
use Illuminate\Support\Facades\Queue;
1010
use PHPUnit\Framework\Attributes\Test;
11+
use Statamic\Console\Commands\Concerns\NormalizesPaginationHeader;
1112
use Statamic\Console\Commands\StaticWarmJob;
1213
use Tests\TestCase;
1314

@@ -90,4 +91,55 @@ public function subsequent_paginated_pages_dont_dispatch_static_warm_jobs()
9091
// The first page is responsible for dispatchin jobs. Not subsequent pages.
9192
Queue::assertNothingPushed();
9293
}
94+
95+
#[Test]
96+
public function it_dispatches_paginated_jobs_when_the_pagination_header_is_folded_into_one_line()
97+
{
98+
Queue::fake();
99+
100+
// A proxy or CDN may coalesce the repeated header into a single comma-joined line.
101+
$mock = new MockHandler([
102+
(new Response(200))->withHeader('X-Statamic-Pagination', '1, 3, page'),
103+
]);
104+
105+
$handlerStack = HandlerStack::create($mock);
106+
107+
$job = new StaticWarmJob(new Request('GET', '/blog'), ['handler' => $handlerStack]);
108+
109+
$job->handle();
110+
111+
Queue::assertPushed(StaticWarmJob::class, function (StaticWarmJob $job) {
112+
return $job->request->getUri()->getQuery() === 'page=1';
113+
});
114+
115+
Queue::assertPushed(StaticWarmJob::class, function (StaticWarmJob $job) {
116+
return $job->request->getUri()->getQuery() === 'page=2';
117+
});
118+
119+
Queue::assertPushed(StaticWarmJob::class, function (StaticWarmJob $job) {
120+
return $job->request->getUri()->getQuery() === 'page=3';
121+
});
122+
}
123+
124+
#[Test]
125+
public function it_keeps_a_page_name_that_contains_a_comma()
126+
{
127+
$parser = new class
128+
{
129+
use NormalizesPaginationHeader;
130+
131+
public function parse($response)
132+
{
133+
return $this->paginationHeader($response);
134+
}
135+
};
136+
137+
// Three separate header values, as set by the static caching middleware.
138+
$separate = (new Response(200))->withHeader('X-Statamic-Pagination', ['current' => 1, 'total' => 3, 'name' => 'pa,ge']);
139+
$this->assertSame([1, 3, 'pa,ge'], $parser->parse($separate));
140+
141+
// The same header folded into one comma-joined line by a proxy.
142+
$folded = (new Response(200))->withHeader('X-Statamic-Pagination', '1, 3, pa,ge');
143+
$this->assertSame([1, 3, 'pa,ge'], $parser->parse($folded));
144+
}
93145
}

0 commit comments

Comments
 (0)