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

FilesystemPublisher::setAddTimestamp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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

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