Passed
Pull Request — master (#70)
by Daniel
02:11
created

FilesystemPublisher::publishURL()   C

Complexity

Conditions 7
Paths 13

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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

184
        return /** @scrutinizer ignore-call */ URLtoPath($url, BASE_URL, FilesystemPublisher::config()->get('domain_based_caching'));
Loading history...
185
    }
186
187
    protected function pathToURL($path)
188
    {
189
        if (strpos($path, $this->getDestPath()) === 0) {
190
            //Strip off the full path of the cache dir from the front
191
            $path = substr($path, strlen($this->getDestPath()));
192
        }
193
        $path = ltrim($path, '/');
194
        // Strip off the file extension
195
        $relativeURL = substr($path, 0, (strrpos($path, ".")));
196
197
        return $relativeURL == 'index' ? '/' : $relativeURL;
198
    }
199
200
    public function getPublishedURLs($dir = null, &$result = [])
201
    {
202
        if ($dir == null) {
203
            $dir = $this->getDestPath();
204
        }
205
206
        $root = scandir($dir);
207
        foreach ($root as $fileOrDir) {
208
            if (strpos($fileOrDir, '.') === 0) {
209
                continue;
210
            }
211
            $fullPath = $dir . DIRECTORY_SEPARATOR . $fileOrDir;
212
            // we know html will always be generated, this prevents double ups
213
            if (is_file($fullPath) && pathinfo($fullPath)['extension'] == 'html') {
214
                $result[] = $this->pathToURL($fullPath);
215
                continue;
216
            }
217
218
            if (is_dir($fullPath)) {
219
                $this->getPublishedURLs($fullPath, $result);
220
            }
221
        }
222
        return $result;
223
    }
224
}
225