Skip to content

Commit 5c547fa

Browse files
committed
Implemented support for absolute purge urls and split up target_dir into target_dir and document_root
1 parent 888f2ed commit 5c547fa

File tree

2 files changed

+165
-40
lines changed

2 files changed

+165
-40
lines changed

src/ProxyClient/LiteSpeed.php

Lines changed: 73 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class LiteSpeed extends HttpProxyClient implements PurgeCapable, TagCapable, Cle
2929
*/
3030
public function clear()
3131
{
32-
$this->headerLines[] = 'X-LiteSpeed-Purge: *';
32+
$this->addHeaderLine('X-LiteSpeed-Purge', '*');
3333

3434
return $this;
3535
}
@@ -39,7 +39,15 @@ public function clear()
3939
*/
4040
public function purge($url, array $headers = [])
4141
{
42-
$this->headerLines[] = 'X-LiteSpeed-Purge: '.$url;
42+
$urlParts = parse_url($url);
43+
$host = null;
44+
45+
if (isset($urlParts['host'])) {
46+
$host = $urlParts['host'];
47+
$url = $urlParts['path'];
48+
}
49+
50+
$this->addHeaderLine('X-LiteSpeed-Purge', $url, $host);
4351

4452
return $this;
4553
}
@@ -51,8 +59,15 @@ protected function configureOptions()
5159
{
5260
$resolver = parent::configureOptions();
5361

54-
$resolver->setRequired(['target_dir']);
62+
$resolver->setRequired(['document_root']);
63+
$resolver->setDefaults([
64+
'target_dir' => '',
65+
'base_uri' => '/',
66+
]);
67+
68+
$resolver->setAllowedTypes('document_root', 'string');
5569
$resolver->setAllowedTypes('target_dir', 'string');
70+
$resolver->setAllowedTypes('base_uri', 'string');
5671

5772
return $resolver;
5873
}
@@ -62,7 +77,7 @@ protected function configureOptions()
6277
*/
6378
public function invalidateTags(array $tags)
6479
{
65-
$this->headerLines[] = 'X-LiteSpeed-Purge: '.implode(', ', preg_filter('/^/', 'tag=', $tags));
80+
$this->addHeaderLine('X-LiteSpeed-Purge', implode(', ', preg_filter('/^/', 'tag=', $tags)));
6681

6782
return $this;
6883
}
@@ -72,39 +87,81 @@ public function invalidateTags(array $tags)
7287
*/
7388
public function flush()
7489
{
75-
$filename = $this->createFile();
90+
$filenames = [];
91+
92+
$url = '/';
93+
94+
if ($this->options['target_dir']) {
95+
$url .= $this->options['target_dir'].'/';
96+
}
7697

77-
$url = '/'.$filename;
98+
foreach ($this->headerLines as $host => $lines) {
99+
$filename = $this->createFileForHost($host);
78100

79-
$this->queueRequest('GET', $url, []);
101+
$this->queueRequest('GET', $url.$filename, []);
102+
103+
$filenames[] = $filename;
104+
}
105+
106+
try {
107+
return parent::flush();
108+
} finally {
109+
// Reset
110+
$this->headerLines = [];
111+
112+
foreach ($filenames as $filename) {
113+
unlink($this->getFilePath().'/'.$filename);
114+
}
115+
}
116+
}
80117

81-
$result = parent::flush();
118+
private function addHeaderLine($header, $value, $host = null)
119+
{
120+
if (null === $host) {
121+
$host = $this->options['base_uri'];
122+
}
82123

83-
// Reset
84-
$this->headerLines = [];
85-
unlink($this->options['target_dir'].'/'.$filename);
124+
if (!isset($this->headerLines[$host])) {
125+
$this->headerLines[$host] = [];
126+
}
86127

87-
return $result;
128+
$this->headerLines[$host][] = $header.': '.$value;
88129
}
89130

90131
/**
91132
* Creates the file and returns the file name.
92133
*
134+
* @param string $host
135+
*
93136
* @return string
94137
*/
95-
private function createFile()
138+
private function createFileForHost($host)
96139
{
97140
$content = '<?php'."\n\n";
98141

99-
foreach ($this->headerLines as $header) {
142+
foreach ($this->headerLines[$host] as $header) {
100143
$content .= sprintf('header(\'%s\');', addslashes($header))."\n";
101144
}
102145

103146
// Generate a reasonably random file name, no need to be cryptographically safe here
104-
$filename = 'fos_cache_litespeed_purger_'.substr(sha1(uniqid('', true).mt_rand()), 0, mt_rand(10, 40)) . '.php';
147+
$filename = 'fos_cache_litespeed_purger_'.substr(sha1(uniqid('', true).mt_rand()), 0, mt_rand(10, 40)).'.php';
105148

106-
file_put_contents($this->options['target_dir'].'/'.$filename, $content);
149+
file_put_contents($this->getFilePath().'/'.$filename, $content);
107150

108151
return $filename;
109152
}
153+
154+
/**
155+
* @return string
156+
*/
157+
private function getFilePath()
158+
{
159+
$path = $this->options['document_root'];
160+
161+
if ($this->options['target_dir']) {
162+
$path .= '/'.$this->options['target_dir'];
163+
}
164+
165+
return $path;
166+
}
110167
}

tests/Unit/ProxyClient/LiteSpeedTest.php

Lines changed: 92 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,25 +30,40 @@ class LiteSpeedTest extends TestCase
3030
/**
3131
* @var string
3232
*/
33-
private $targetDir;
33+
private $documentRoot;
3434

3535
protected function setUp()
3636
{
3737
$this->httpDispatcher = \Mockery::mock(HttpDispatcher::class);
3838

39-
$targetDir = sys_get_temp_dir().'/fos_ls_tests';
40-
if (is_dir($targetDir)) {
41-
array_map('unlink', glob($targetDir.'/*'));
42-
rmdir($targetDir);
39+
$deleteFilesAndFolders = function ($path) use (&$deleteFilesAndFolders) {
40+
$files = glob($path.'/*');
41+
foreach ($files as $file) {
42+
is_dir($file) ? $deleteFilesAndFolders($file) : unlink($file);
43+
}
44+
rmdir($path);
45+
46+
return;
47+
};
48+
49+
$documentRoot = sys_get_temp_dir().'/fos_ls_tests';
50+
if (is_dir($documentRoot)) {
51+
$deleteFilesAndFolders($documentRoot);
4352
}
44-
mkdir($targetDir);
53+
mkdir($documentRoot);
4554

46-
$this->targetDir = $targetDir;
55+
$this->documentRoot = $documentRoot;
4756
}
4857

4958
public function testPurge()
5059
{
51-
$ls = new LiteSpeed($this->httpDispatcher, ['target_dir' => $this->targetDir]);
60+
$ls = new LiteSpeed($this->httpDispatcher, [
61+
'document_root' => $this->documentRoot,
62+
'target_dir' => 'subfolder',
63+
]);
64+
65+
// We're also testing target_dir here so we have to create the subfolder
66+
mkdir($this->documentRoot.'/subfolder');
5267

5368
$expectedContent = <<<'EOT'
5469
<?php
@@ -59,7 +74,7 @@ public function testPurge()
5974
header('X-LiteSpeed-Purge: foo\'); exec(\\\'rm -rf /\\\');//');
6075

6176
EOT;
62-
$this->assertLiteSpeedPurger($expectedContent);
77+
$this->assertLiteSpeedPurger([$expectedContent], 'subfolder');
6378

6479
$ls->purge('/url');
6580
$ls->purge('/another/url');
@@ -68,12 +83,50 @@ public function testPurge()
6883
$ls->flush();
6984

7085
// Assert file has been deleted again
71-
$this->assertDirectoryEmpty($this->targetDir);
86+
$this->assertDirectoryEmpty($this->documentRoot.'/subfolder');
87+
}
88+
89+
public function testPurgeWithAbsoluteUrls()
90+
{
91+
$ls = new LiteSpeed($this->httpDispatcher, [
92+
'document_root' => $this->documentRoot,
93+
]);
94+
95+
$expectedContents = [];
96+
$expectedContents[] = <<<'EOT'
97+
<?php
98+
99+
header('X-LiteSpeed-Purge: /url');
100+
101+
EOT;
102+
$expectedContents[] = <<<'EOT'
103+
<?php
104+
105+
header('X-LiteSpeed-Purge: /foobar');
106+
107+
EOT;
108+
$expectedContents[] = <<<'EOT'
109+
<?php
110+
111+
header('X-LiteSpeed-Purge: /foobar');
112+
113+
EOT;
114+
$this->assertLiteSpeedPurger($expectedContents);
115+
116+
$ls->purge('/url');
117+
$ls->purge('https://www.domain.com/foobar');
118+
$ls->purge('https://www.domain.ch/foobar');
119+
$ls->flush();
120+
121+
// Assert file has been deleted again
122+
$this->assertDirectoryEmpty($this->documentRoot);
72123
}
73124

74125
public function testInvalidateTags()
75126
{
76-
$ls = new LiteSpeed($this->httpDispatcher, ['target_dir' => $this->targetDir]);
127+
$ls = new LiteSpeed($this->httpDispatcher, [
128+
'document_root' => $this->documentRoot,
129+
]);
77130

78131
$expectedContent = <<<'EOT'
79132
<?php
@@ -82,55 +135,70 @@ public function testInvalidateTags()
82135
header('X-LiteSpeed-Purge: tag=more, tag=tags');
83136

84137
EOT;
85-
$this->assertLiteSpeedPurger($expectedContent);
138+
$this->assertLiteSpeedPurger([$expectedContent]);
86139

87140
$ls->invalidateTags(['foobar', 'tag']);
88141
$ls->invalidateTags(['more', 'tags']);
89142
$ls->flush();
90143

91144
// Assert file has been deleted again
92-
$this->assertDirectoryEmpty($this->targetDir);
145+
$this->assertDirectoryEmpty($this->documentRoot);
93146
}
94147

95148
public function testClear()
96149
{
97-
$ls = new LiteSpeed($this->httpDispatcher, ['target_dir' => $this->targetDir]);
150+
$ls = new LiteSpeed($this->httpDispatcher, [
151+
'document_root' => $this->documentRoot,
152+
]);
98153

99154
$expectedContent = <<<'EOT'
100155
<?php
101156
102157
header('X-LiteSpeed-Purge: *');
103158

104159
EOT;
105-
$this->assertLiteSpeedPurger($expectedContent);
160+
$this->assertLiteSpeedPurger([$expectedContent]);
106161

107162
$ls->clear();
108163
$ls->flush();
109164

110165
// Assert file has been deleted again
111-
$this->assertDirectoryEmpty($this->targetDir);
166+
$this->assertDirectoryEmpty($this->documentRoot);
112167
}
113168

114-
private function assertLiteSpeedPurger($expectedContent)
169+
private function assertLiteSpeedPurger(array $expectedContents, $targetDir = '')
115170
{
116-
$this->httpDispatcher->shouldReceive('invalidate')->once()->with(
117-
\Mockery::on(
118-
function (RequestInterface $request) use ($expectedContent) {
171+
$methodCallCount = 0;
172+
173+
$this->httpDispatcher->shouldReceive('invalidate')
174+
->times(count($expectedContents))
175+
->with(\Mockery::on(
176+
function (RequestInterface $request) use ($expectedContents, $targetDir, &$methodCallCount) {
119177
$this->assertEquals('GET', $request->getMethod());
120178

121-
$filename = ltrim($request->getRequestTarget(), '/');
179+
$cutOff = $targetDir ? (strlen($targetDir) + 2) : 1;
180+
$filename = substr_replace($request->getRequestTarget(), '', 0, $cutOff);
181+
182+
$path = $this->documentRoot;
183+
184+
if ($targetDir) {
185+
$path .= '/'.$targetDir;
186+
}
122187

123188
// Assert file has been generated
124-
$this->assertFileExists($this->targetDir.'/'.$filename);
189+
$this->assertFileExists($path.'/'.$filename);
125190

126191
// Assert file contents
127-
$this->assertSame($expectedContent, file_get_contents($this->targetDir.'/'.$filename));
192+
$this->assertSame($expectedContents[$methodCallCount], file_get_contents($path.'/'.$filename));
193+
194+
++$methodCallCount;
128195

129196
return true;
130197
}
131198
),
132199
true
133-
);
200+
);
201+
134202
$this->httpDispatcher->shouldReceive('flush')->once();
135203
}
136204

0 commit comments

Comments
 (0)