Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/Console/Commands/Concerns/NormalizesPaginationHeader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Statamic\Console\Commands\Concerns;

use Psr\Http\Message\ResponseInterface;

trait NormalizesPaginationHeader
{
/**
* @return array{0: int, 1: int, 2: string}
*/
protected function paginationHeader(ResponseInterface $response): array
{
// A proxy or CDN may fold the repeated X-Statamic-Pagination header into a single
// comma-joined line, so we normalize both framings before destructuring. The limit
// keeps a page name that itself contains a comma intact.
[$current, $total, $name] = array_map(
'trim',
explode(',', implode(',', $response->getHeader('X-Statamic-Pagination')), 3)
);

return [(int) $current, (int) $total, $name];
}
}
4 changes: 3 additions & 1 deletion src/Console/Commands/StaticWarm.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Illuminate\Http\Request as HttpRequest;
use Illuminate\Routing\Route;
use Illuminate\Support\Collection;
use Statamic\Console\Commands\Concerns\NormalizesPaginationHeader;
use Statamic\Console\EnhancesCommands;
use Statamic\Console\RunsInPlease;
use Statamic\Entries\Collection as EntriesCollection;
Expand All @@ -31,6 +32,7 @@ class StaticWarm extends Command
{
use EnhancesCommands;
use Hookable;
use NormalizesPaginationHeader;
use RunsInPlease;

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

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

$this->warmPaginatedPages($this->uris()->get($index), $currentPage, $totalPages, $pageName);
}
Expand Down
7 changes: 4 additions & 3 deletions src/Console/Commands/StaticWarmJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Psr\Http\Message\ResponseInterface;
use Statamic\Console\Commands\Concerns\NormalizesPaginationHeader;

class StaticWarmJob implements ShouldBeUnique, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable;
use Dispatchable, InteractsWithQueue, NormalizesPaginationHeader, Queueable;

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

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

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

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

return ! str_contains($this->request->getUri()->getQuery(), "{$pageName}=");
}
Expand Down
52 changes: 52 additions & 0 deletions tests/Console/Commands/StaticWarmJobTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use GuzzleHttp\Psr7\Response;
use Illuminate\Support\Facades\Queue;
use PHPUnit\Framework\Attributes\Test;
use Statamic\Console\Commands\Concerns\NormalizesPaginationHeader;
use Statamic\Console\Commands\StaticWarmJob;
use Tests\TestCase;

Expand Down Expand Up @@ -90,4 +91,55 @@ public function subsequent_paginated_pages_dont_dispatch_static_warm_jobs()
// The first page is responsible for dispatchin jobs. Not subsequent pages.
Queue::assertNothingPushed();
}

#[Test]
public function it_dispatches_paginated_jobs_when_the_pagination_header_is_folded_into_one_line()
{
Queue::fake();

// A proxy or CDN may coalesce the repeated header into a single comma-joined line.
$mock = new MockHandler([
(new Response(200))->withHeader('X-Statamic-Pagination', '1, 3, page'),
]);

$handlerStack = HandlerStack::create($mock);

$job = new StaticWarmJob(new Request('GET', '/blog'), ['handler' => $handlerStack]);

$job->handle();

Queue::assertPushed(StaticWarmJob::class, function (StaticWarmJob $job) {
return $job->request->getUri()->getQuery() === 'page=1';
});

Queue::assertPushed(StaticWarmJob::class, function (StaticWarmJob $job) {
return $job->request->getUri()->getQuery() === 'page=2';
});

Queue::assertPushed(StaticWarmJob::class, function (StaticWarmJob $job) {
return $job->request->getUri()->getQuery() === 'page=3';
});
}

#[Test]
public function it_keeps_a_page_name_that_contains_a_comma()
{
$parser = new class
{
use NormalizesPaginationHeader;

public function parse($response)
{
return $this->paginationHeader($response);
}
};

// Three separate header values, as set by the static caching middleware.
$separate = (new Response(200))->withHeader('X-Statamic-Pagination', ['current' => 1, 'total' => 3, 'name' => 'pa,ge']);
$this->assertSame([1, 3, 'pa,ge'], $parser->parse($separate));

// The same header folded into one comma-joined line by a proxy.
$folded = (new Response(200))->withHeader('X-Statamic-Pagination', '1, 3, pa,ge');
$this->assertSame([1, 3, 'pa,ge'], $parser->parse($folded));
}
}
Loading