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
28 changes: 26 additions & 2 deletions src/Snappdf.php
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ public function getKeepTemporaryFiles(): bool
return (bool) $this->keepTemporaryFiles;
}

private function cleanup(string $tempFile, array $content): void
private function cleanup(string $tempFile, array $content, array $existingChromiumProfiles): void
{
if ($this->keepTemporaryFiles) {
return;
Expand All @@ -205,6 +205,28 @@ private function cleanup(string $tempFile, array $content): void
if ($content['type'] === 'html') {
unlink($content['content']);
}

// Chromium writes its profile (BrowserMetrics-*.pma etc.) into a temp directory
// of the form /tmp/.org.chromium.Chromium.<random>. On a clean exit it removes
// the directory itself; on an abnormal exit (crash, timeout, killed process) it
// is left behind. Remove any we created during this run.
$this->removeOrphanedChromiumProfiles($existingChromiumProfiles);
}

/**
* Remove Chromium temp profile directories that did not exist before this run.
*
* @param string[] $existingProfiles Directories present before the browser started.
*/
private function removeOrphanedChromiumProfiles(array $existingProfiles): void
{
$filesystem = new Filesystem();

foreach (glob(sys_get_temp_dir() . DIRECTORY_SEPARATOR . '.org.chromium.Chromium.*') as $profile) {
if (!in_array($profile, $existingProfiles, true)) {
$filesystem->remove($profile);
}
}
}

public function generate(): ?string
Expand Down Expand Up @@ -235,6 +257,8 @@ public function generate(): ?string
$pdf = tempnam(sys_get_temp_dir(), 'pdf_');
rename($pdf, $pdf .= '.pdf');

$existingChromiumProfiles = glob(sys_get_temp_dir() . DIRECTORY_SEPARATOR . '.org.chromium.Chromium.*') ?: [];

$commandInput = [$this->getChromiumPath()];

foreach ($this->getChromiumArguments() as $argument) {
Expand All @@ -261,7 +285,7 @@ public function generate(): ?string

$pdfContent = file_get_contents($pdf);

$this->cleanup($pdf, $content);
$this->cleanup($pdf, $content, $existingChromiumProfiles);

return $pdfContent;
}
Expand Down
27 changes: 27 additions & 0 deletions tests/SnappdfTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Beganovich\Snappdf\Exception\MissingContent;
use Beganovich\Snappdf\Snappdf;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Filesystem\Filesystem;

class SnappdfTest extends TestCase
{
Expand Down Expand Up @@ -155,4 +156,30 @@ public function testArgumentCanBeRemoved()

$this->assertEquals($argumentsCount, count($snappdf->getChromiumArguments()) + 1);
}

public function testOrphanedChromiumProfilesAreRemovedAfterRun()
{
$snappdf = new Snappdf();

$preExisting = sys_get_temp_dir() . DIRECTORY_SEPARATOR . '.org.chromium.Chromium.PREEXISTING';
$orphan = sys_get_temp_dir() . DIRECTORY_SEPARATOR . '.org.chromium.Chromium.ORPHANED';

$filesystem = new Filesystem();
$filesystem->mkdir($preExisting);
$filesystem->touch($preExisting . '/keep.txt');
$filesystem->mkdir($orphan);

try {
// Chromium snapshots the profiles present before the run; an orphan created
// during the run (abnormal exit) is absent from that snapshot and must go.
$removeOrphaned = new \ReflectionMethod($snappdf, 'removeOrphanedChromiumProfiles');
$removeOrphaned->invoke($snappdf, [$preExisting]);

$this->assertTrue(is_dir($preExisting), 'Pre-existing .org.chromium profile must be preserved.');
$this->assertFalse(is_dir($orphan), 'Orphaned profile from this run must be removed.');
} finally {
$filesystem->remove($preExisting);
$filesystem->remove($orphan);
}
}
}
Loading