Passed
Pull Request — master (#79)
by Tim
02:01
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',
69
                    $fileExtension,
70
                    static::class,
71
                    __FUNCTION__
72
                )
73
            );
74
        }
75
        $this->fileExtension = $fileExtension;
76
        return $this;
77
    }
78
79
    public function getFileExtension()
80
    {
81
        return $this->fileExtension;
82
    }
83
84
    public function purgeURL($url)
85
    {
86
        if (!$url) {
87
            user_error("Bad url:" . var_export($url, true), E_USER_WARNING);
88
            return;
89
        }
90
        if ($path = $this->URLtoPath($url)) {
91
            $success = $this->deleteFromPath($path . '.html') && $this->deleteFromPath($path . '.php');
92
            return [
93
                'success' => $success,
94
                'url' => $url,
95
                'path' => $this->getDestPath() . DIRECTORY_SEPARATOR . $path,
96
            ];
97
        }
98
        return [
99
            'success' => false,
100
            'url' => $url,
101
            'path' => false,
102
        ];
103
    }
104
105
    /**
106
     * @param string $url
107
     * @return array A result array
108
     */
109
    public function publishURL($url, $forcePublish = false)
110
    {
111
        if (!$url) {
112
            user_error("Bad url:" . var_export($url, true), E_USER_WARNING);
113
            return;
114
        }
115
        $success = false;
116
        $response = $this->generatePageResponse($url);
117
        $statusCode = $response->getStatusCode();
118
        $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...
119
120
        if ($statusCode < 300) {
121
            // publish success response
122
            $success = $this->publishPage($response, $url);
123
        } elseif ($statusCode < 400) {
124
            // publish redirect response
125
            $success = $this->publishRedirect($response, $url);
126
        } elseif ($doPublish) {
127
            // only publish error pages if we are able to send status codes via PHP
128
            $success = $this->publishPage($response, $url);
129
        }
130
        return [
131
            'published' => $doPublish,
132
            'success' => $success,
133
            'responsecode' => $statusCode,
134
            'url' => $url,
135
        ];
136
    }
137
138
    /**
139
     * @param HTTPResponse $response
140
     * @param string       $url
141
     * @return bool
142
     */
143
    protected function publishRedirect($response, $url)
144
    {
145
        $success = true;
146
        if ($path = $this->URLtoPath($url)) {
147
            $location = $response->getHeader('Location');
148
            if ($this->getFileExtension() === 'php') {
149
                $phpContent = $this->generatePHPCacheFile($response);
150
                $success = $this->saveToPath($phpContent, $path . '.php');
151
            }
152
            return $this->saveToPath($this->generateHTMLCacheRedirection($location), $path . '.html') && $success;
153
        }
154
        return false;
155
    }
156
157
    /**
158
     * @param HTTPResponse $response
159
     * @param string       $url
160
     * @return bool
161
     */
162
    protected function publishPage($response, $url)
163
    {
164
        $success = true;
165
        if ($path = $this->URLtoPath($url)) {
166
            if ($this->getFileExtension() === 'php') {
167
                $phpContent = $this->generatePHPCacheFile($response);
168
                $success = $this->saveToPath($phpContent, $path . '.php');
169
            }
170
            $responseBody = $response->getBody();
171
            if ($this->getAddTimestamp() === true) {
172
                $responseBody = str_replace('</html>', "<!-- " . date('c') . " -->\n</html>", $responseBody);
173
            }
174
175
            return $this->saveToPath($responseBody, $path . '.html') && $success;
176
        }
177
        return false;
178
    }
179
180
    /**
181
     * @param string $content
182
     * @param string $filePath
183
     * @return bool
184
     */
185
    protected function saveToPath($content, $filePath)
186
    {
187
        if (empty($content)) {
188
            return false;
189
        }
190
        $publishPath = $this->getDestPath() . DIRECTORY_SEPARATOR . $filePath;
191
        Filesystem::makeFolder(dirname($publishPath));
192
        return file_put_contents($publishPath, $content) !== false;
193
    }
194
195
    protected function deleteFromPath($filePath)
196
    {
197
        $deletePath = $this->getDestPath() . DIRECTORY_SEPARATOR . $filePath;
198
        if (file_exists($deletePath)) {
199
            $success = unlink($deletePath);
200
        } else {
201
            $success = true;
202
        }
203
204
        return $success;
205
    }
206
207
    protected function URLtoPath($url)
208
    {
209
        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

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