Passed
Pull Request — master (#79)
by Tim
02:39
created

FilesystemPublisher::getAddTimestamp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
namespace SilverStripe\StaticPublishQueue\Publisher;
3
4
use SilverStripe\Assets\Filesystem;
5
use SilverStripe\Control\Director;
6
use SilverStripe\Control\HTTPResponse;
7
use SilverStripe\StaticPublishQueue\Publisher;
8
use function SilverStripe\StaticPublishQueue\URLtoPath;
0 ignored issues
show
introduced by
The function SilverStripe\StaticPublishQueue\URLtoPath was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
9
10
class FilesystemPublisher extends Publisher
11
{
12
13
    /**
14
     * @var boolean
15
     */
16
    protected $addTimestamp = false;
17
18
    /**
19
     * @var string
20
     */
21
    protected $destFolder = 'cache';
22
23
    /**
24
     * @var string
25
     */
26
    protected $fileExtension = 'php';
27
28
    /**
29
     * @return boolean
30
     */
31
    public function getAddTimestamp()
32
    {
33
        return $this->addTimestamp;
34
    }
35
36
    public function setAddTimestamp($state)
37
    {
38
        $this->addTimestamp = $state;
39
        return $this;
40
    }
41
42
    /**
43
     * @return string
44
     */
45
    public function getDestPath()
46
    {
47
        $base = defined('PUBLIC_PATH') ? PUBLIC_PATH : BASE_PATH;
48
        return $base . DIRECTORY_SEPARATOR . $this->getDestFolder();
49
    }
50
51
    public function setDestFolder($destFolder)
52
    {
53
        $this->destFolder = $destFolder;
54
        return $this;
55
    }
56
57
    public function getDestFolder()
58
    {
59
        return $this->destFolder;
60
    }
61
62
    public function setFileExtension($fileExtension)
63
    {
64
        $fileExtension = strtolower($fileExtension);
65
        if (!in_array($fileExtension, ['html', 'php'])) {
66
            throw new \InvalidArgumentException(
67
            sprintf(
68
                'Bad file extension "%s" passed to %s::%s', $fileExtension, static::class, __FUNCTION__
69
            )
70
            );
71
        }
72
        $this->fileExtension = $fileExtension;
73
        return $this;
74
    }
75
76
    public function getFileExtension()
77
    {
78
        return $this->fileExtension;
79
    }
80
81
    public function purgeURL($url)
82
    {
83
        if (!$url) {
84
            user_error("Bad url:" . var_export($url, true), E_USER_WARNING);
85
            return;
86
        }
87
        if ($path = $this->URLtoPath($url)) {
88
            $success = $this->deleteFromPath($path . '.html') && $this->deleteFromPath($path . '.php');
89
            return [
90
                'success' => $success,
91
                'url' => $url,
92
                'path' => $this->getDestPath() . DIRECTORY_SEPARATOR . $path,
93
            ];
94
        }
95
        return [
96
            'success' => false,
97
            'url' => $url,
98
            'path' => false,
99
        ];
100
    }
101
102
    /**
103
     * @param string $url
104
     * @return array A result array
105
     */
106
    public function publishURL($url, $forcePublish = false)
107
    {
108
        if (!$url) {
109
            user_error("Bad url:" . var_export($url, true), E_USER_WARNING);
110
            return;
111
        }
112
        $success = false;
113
        $response = $this->generatePageResponse($url);
114
        $statusCode = $response->getStatusCode();
115
        $doPublish = ($forcePublish && $this->getFileExtension() == 'php') || $statusCode < 400;
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: $doPublish = ($forcePubl...' || $statusCode < 400), Probably Intended Meaning: $doPublish = $forcePubli...' || $statusCode < 400)
Loading history...
116
117
        if ($statusCode < 300) {
118
            // publish success response
119
            $success = $this->publishPage($response, $url);
120
        } elseif ($statusCode < 400) {
121
            // publish redirect response
122
            $success = $this->publishRedirect($response, $url);
123
        } elseif ($doPublish) {
124
            // only publish error pages if we are able to send status codes via PHP
125
            $success = $this->publishPage($response, $url);
126
        }
127
        return [
128
            'published' => $doPublish,
129
            'success' => $success,
130
            'responsecode' => $statusCode,
131
            'url' => $url,
132
        ];
133
    }
134
135
    /**
136
     * @param HTTPResponse $response
137
     * @param string       $url
138
     * @return bool
139
     */
140
    protected function publishRedirect($response, $url)
141
    {
142
        $success = true;
143
        if ($path = $this->URLtoPath($url)) {
144
            $location = $response->getHeader('Location');
145
            if ($this->getFileExtension() === 'php') {
146
                $phpContent = $this->generatePHPCacheFile($response);
147
                $success = $this->saveToPath($phpContent, $path . '.php');
148
            }
149
            return $this->saveToPath($this->generateHTMLCacheRedirection($location), $path . '.html') && $success;
150
        }
151
        return false;
152
    }
153
154
    /**
155
     * @param HTTPResponse $response
156
     * @param string       $url
157
     * @return bool
158
     */
159
    protected function publishPage($response, $url)
160
    {
161
        $success = true;
162
        if ($path = $this->URLtoPath($url)) {
163
            if ($this->getFileExtension() === 'php') {
164
                $phpContent = $this->generatePHPCacheFile($response);
165
                $success = $this->saveToPath($phpContent, $path . '.php');
166
            }
167
            $responseBody = $response->getBody();
168
            if ($this->getAddTimestamp() === true) {
169
                $responseBody = str_replace('</html>', "<!-- " . date('c') . " -->\n</html>", $responseBody);
170
            }
171
172
            return $this->saveToPath($responseBody, $path . '.html') && $success;
173
        }
174
        return false;
175
    }
176
177
    /**
178
     * @param string $content
179
     * @param string $filePath
180
     * @return bool
181
     */
182
    protected function saveToPath($content, $filePath)
183
    {
184
        if (empty($content)) {
185
            return false;
186
        }
187
        $publishPath = $this->getDestPath() . DIRECTORY_SEPARATOR . $filePath;
188
        Filesystem::makeFolder(dirname($publishPath));
189
        return file_put_contents($publishPath, $content) !== false;
190
    }
191
192
    protected function deleteFromPath($filePath)
193
    {
194
        $deletePath = $this->getDestPath() . DIRECTORY_SEPARATOR . $filePath;
195
        if (file_exists($deletePath)) {
196
            $success = unlink($deletePath);
197
        } else {
198
            $success = true;
199
        }
200
201
        return $success;
202
    }
203
204
    protected function URLtoPath($url)
205
    {
206
        return URLtoPath($url, BASE_URL, FilesystemPublisher::config()->get('domain_based_caching'));
0 ignored issues
show
Bug introduced by
The function URLtoPath was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

206
        return /** @scrutinizer ignore-call */ URLtoPath($url, BASE_URL, FilesystemPublisher::config()->get('domain_based_caching'));
Loading history...
207
    }
208
209
    protected function pathToURL($path)
210
    {
211
        if (strpos($path, $this->getDestPath()) === 0) {
212
            //Strip off the full path of the cache dir from the front
213
            $path = substr($path, strlen($this->getDestPath()));
214
        }
215
216
        // Strip off the file extension and leading /
217
        $relativeURL = substr($path, 0, (strrpos($path, ".")));
218
        $relativeURL = ltrim($relativeURL, '/');
219
220
        if (FilesystemPublisher::config()->get('domain_based_caching')) {
221
            // factor in the domain as the top dir
222
            return Director::protocol() . $relativeURL;
223
        }
224
225
        return $relativeURL == 'index' ? Director::absoluteBaseURL() : Director::absoluteURL($relativeURL);
226
    }
227
228
    public function getPublishedURLs($dir = null, &$result = [])
229
    {
230
        if ($dir == null) {
231
            $dir = $this->getDestPath();
232
        }
233
234
        $root = scandir($dir);
235
        foreach ($root as $fileOrDir) {
236
            if (strpos($fileOrDir, '.') === 0) {
237
                continue;
238
            }
239
            $fullPath = $dir . DIRECTORY_SEPARATOR . $fileOrDir;
240
            // we know html will always be generated, this prevents double ups
241
            if (is_file($fullPath) && pathinfo($fullPath)['extension'] == 'html') {
242
                $result[] = $this->pathToURL($fullPath);
243
                continue;
244
            }
245
246
            if (is_dir($fullPath)) {
247
                $this->getPublishedURLs($fullPath, $result);
248
            }
249
        }
250
        return $result;
251
    }
252
}
253