Completed
Pull Request — master (#444)
by Yanick
27:35 queued 12:35
created

LiteSpeed::addHeaderLine()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.8666
c 0
b 0
f 0
cc 3
nc 4
nop 3
1
<?php
2
3
/*
4
 * This file is part of the FOSHttpCache package.
5
 *
6
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace FOS\HttpCache\ProxyClient;
13
14
use FOS\HttpCache\ProxyClient\Invalidation\ClearCapable;
15
use FOS\HttpCache\ProxyClient\Invalidation\PurgeCapable;
16
use FOS\HttpCache\ProxyClient\Invalidation\TagCapable;
17
18
/**
19
 * LiteSpeed Web Server (LSWS) invalidator.
20
 *
21
 * @author Yanick Witschi <[email protected]>
22
 */
23
class LiteSpeed extends HttpProxyClient implements PurgeCapable, TagCapable, ClearCapable
24
{
25
    private $headerLines = [];
26
27
    /**
28
     * {@inheritdoc}
29
     */
30
    public function clear()
31
    {
32
        // Litespeed supports purging everything by passing *
33
        $this->addHeaderLine('X-LiteSpeed-Purge', '*');
34
35
        return $this;
36
    }
37
38
    /**
39
     * {@inheritdoc}
40
     */
41
    public function purge($url, array $headers = [])
42
    {
43
        $urlParts = parse_url($url);
44
        $host = array_key_exists('host', $urlParts) ? $urlParts['host'] : null;
45
        $url = array_key_exists('path', $urlParts) ? $urlParts['path'] : '/';
46
47
        $this->addHeaderLine('X-LiteSpeed-Purge', $url, $host);
48
49
        return $this;
50
    }
51
52
    /**
53
     * {@inheritdoc}
54
     *
55
     * You must configure the following options:
56
     *
57
     * - document_root: Must contain the absolute path on your system, where the invalidation file should be created at.
58
     *
59
     * You can configure the following options:
60
     *
61
     * - target_dir: If you don't want to have your invalidation files reside in document_root directly, you can specify
62
     *               a target_dir. It will be appended to both, the document_root when creating the files and the URL
63
     *               when executing the invalidation request.
64
     * - base_uri:   The base_uri is used when you call purge() with passing an URL without any host (e.g. /path). The
65
     *               base_uri will be used as host then.
66
     */
67
    protected function configureOptions()
68
    {
69
        $resolver = parent::configureOptions();
70
71
        $resolver->setRequired(['document_root']);
72
        $resolver->setDefaults([
73
            'target_dir' => '',
74
            'base_uri' => '/',
75
        ]);
76
77
        $resolver->setAllowedTypes('document_root', 'string');
78
        $resolver->setAllowedTypes('target_dir', 'string');
79
        $resolver->setAllowedTypes('base_uri', 'string');
80
81
        return $resolver;
82
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87
    public function invalidateTags(array $tags)
88
    {
89
        $this->addHeaderLine('X-LiteSpeed-Purge', implode(', ', preg_filter('/^/', 'tag=', $tags)));
90
91
        return $this;
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97
    public function flush()
98
    {
99
        $filenames = [];
100
101
        $path = '/';
102
103
        if ($this->options['target_dir']) {
104
            $path .= $this->options['target_dir'].'/';
105
        }
106
107
        foreach ($this->headerLines as $host => $lines) {
108
            $filename = $this->createInvalidationFile($lines);
109
110
            $this->queueRequest('GET', $path.$filename, []);
111
112
            $filenames[] = $filename;
113
        }
114
115
        try {
116
            return parent::flush();
117
        } finally {
118
            // Reset
119
            $this->headerLines = [];
120
121
            foreach ($filenames as $filename) {
122
                unlink($this->getFilePath().'/'.$filename);
123
            }
124
        }
125
    }
126
127
    private function addHeaderLine($header, $value, $host = null)
128
    {
129
        if (null === $host) {
130
            $host = $this->options['base_uri'];
131
        }
132
133
        if (!isset($this->headerLines[$host])) {
134
            $this->headerLines[$host] = [];
135
        }
136
137
        $this->headerLines[$host][] = $header.': '.$value;
138
    }
139
140
    /**
141
     * Creates the file and returns the file name.
142
     *
143
     * @param array $lines
144
     *
145
     * @return string
146
     */
147
    private function createInvalidationFile(array $lines)
148
    {
149
        $content = '<?php'."\n\n";
150
151
        foreach ($lines as $header) {
152
            $content .= sprintf('header(\'%s\');', addslashes($header))."\n";
153
        }
154
155
        // Generate a reasonably random file name, no need to be cryptographically safe here
156
        $filename = 'fos_cache_litespeed_purger_'.substr(sha1(uniqid('', true).mt_rand()), 0, mt_rand(10, 40)).'.php';
157
158
        file_put_contents($this->getFilePath().'/'.$filename, $content);
159
160
        return $filename;
161
    }
162
163
    /**
164
     * @return string
165
     */
166
    private function getFilePath()
167
    {
168
        $path = $this->options['document_root'];
169
170
        if ($this->options['target_dir']) {
171
            $path .= '/'.$this->options['target_dir'];
172
        }
173
174
        return $path;
175
    }
176
}
177