Passed
Push — master ( e98e24...a358d2 )
by Eugene
03:37
created

copyFileToDefaults()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 20
ccs 0
cts 17
cp 0
rs 8.8571
cc 5
eloc 14
nc 4
nop 1
crap 30
1
<?php
2
namespace Staticus\Resources\Middlewares;
3
4
use League\Flysystem\FilesystemInterface;
5
use Psr\Http\Message\UploadedFileInterface;
6
use Staticus\Config\ConfigInterface;
7
use Staticus\Exceptions\WrongRequestException;
8
use Staticus\Diactoros\Response\FileUploadedResponse;
9
use Staticus\Resources\Commands\BackupResourceCommand;
10
use Staticus\Resources\Commands\CopyResourceCommand;
11
use Staticus\Resources\Commands\DestroyEqualResourceCommand;
12
use Staticus\Resources\File\ResourceDO;
13
use Staticus\Middlewares\MiddlewareAbstract;
14
use Staticus\Diactoros\Response\FileContentResponse;
15
use Staticus\Resources\Exceptions\SaveResourceErrorException;
16
use Staticus\Exceptions\WrongResponseException;
17
use Staticus\Resources\ResourceDOInterface;
18
use Zend\Diactoros\Response\EmptyResponse;
19
use Psr\Http\Message\ResponseInterface;
20
use Psr\Http\Message\ServerRequestInterface;
21
use Zend\Diactoros\Stream;
22
23
abstract class SaveResourceMiddlewareAbstract extends MiddlewareAbstract
24
{
25
    protected $resourceDO;
26
27
    /**
28
     * @var FilesystemInterface
29
     */
30
    protected $filesystem;
31
32
    /**
33
     * @var ConfigInterface
34
     */
35
    protected $config;
36
37
    public function __construct(ResourceDOInterface $resourceDO, FilesystemInterface $filesystem, ConfigInterface $config)
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 122 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
38
    {
39
        $this->resourceDO = $resourceDO;
40
        $this->filesystem = $filesystem;
41
        $this->config = $config;
42
    }
43
44
    /**
45
     * @param ServerRequestInterface $request
46
     * @param ResponseInterface $response
47
     * @param callable|null $next
48
     * @return EmptyResponse
49
     * @throws \Exception
50
     */
51
    public function __invoke(
52
        ServerRequestInterface $request,
53
        ResponseInterface $response,
54
        callable $next = null
55
    )
56
    {
57
        parent::__invoke($request, $response, $next);
58
        if (
59
            $response instanceof FileContentResponse
60
            || $response instanceof FileUploadedResponse
61
        ) {
62
            $resourceDO = $this->resourceDO;
63
            $filePath = $resourceDO->getFilePath();
64
            if (empty($filePath)) {
65
                throw new WrongResponseException('Empty file path. File can\'t be saved.');
66
            }
67
            $resourceStream = $response->getResource();
68
            if (is_resource($resourceStream)) {
69
                $this->save($resourceDO, $resourceStream);
70
            } else {
71
                $body = $response->getContent();
72
                if (!$body) {
73
                    throw new WrongResponseException('Empty body for generated file. Request: ' . $resourceDO->getName());
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 122 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
74
                }
75
                $this->save($resourceDO, $body);
76
            }
77
            if ($this->config->get('staticus.magic_defaults.allow')) {
78
                $this->copyFileToDefaults($resourceDO);
79
            }
80
            $this->response = new EmptyResponse($response->getStatusCode(), [
81
                'Content-Type' => $this->resourceDO->getMimeType(),
82
            ]);
83
        }
84
85
        return $this->next();
86
    }
87
88
    /**
89
     * @param $filePath
90
     * @param $content
91
     */
92
    protected function writeFile($filePath, $content)
93
    {
94
        if (is_resource($content)) {
95
            $result = $this->filesystem->putStream($filePath, $content);
96
        } else {
97
            $result = $this->filesystem->put($filePath, $content);
98
        }
99
        if (!$result) {
100
            throw new SaveResourceErrorException('File cannot be written to the path ' . $filePath);
101
        }
102
    }
103
104
    protected function uploadFile(UploadedFileInterface $content, $mime, $filePath)
105
    {
106
        $uri = $content->getStream()->getMetadata('uri');
107
        if (!$uri) {
108
            throw new SaveResourceErrorException('Unknown error: can\'t get uploaded file uri');
109
        }
110
        $uploadedMime = $this->filesystem->getMimetype($uri);
111
        if ($mime !== $uploadedMime) {
112
            /**
113
             * Try to remove unnecessary file because UploadFile object can be emulated
114
             * @see \Staticus\Middlewares\ActionPostAbstract::download
115
             */
116
            $this->filesystem->delete($uri);
117
            throw new WrongRequestException('Bad request: incorrect mime-type of the uploaded file');
118
        }
119
        $content->moveTo($filePath);
120
    }
121
122
    protected function copyResource(ResourceDOInterface $originResourceDO, ResourceDOInterface $newResourceDO)
123
    {
124
        $command = new CopyResourceCommand($originResourceDO, $newResourceDO, $this->filesystem);
125
126
        return $command();
127
    }
128
129
    /**
130
     * @param $directory
131
     * @throws SaveResourceErrorException
132
     * @see \Staticus\Resources\Middlewares\Image\ImagePostProcessingMiddlewareAbstract::createDirectory
133
     */
134
    protected function createDirectory($directory)
135
    {
136
        if (!$this->filesystem->createDir($directory)) {
137
            throw new SaveResourceErrorException('Can\'t create a directory: ' . $directory);
138
        }
139
    }
140
141
    protected function copyFileToDefaults(ResourceDOInterface $resourceDO)
142
    {
143
        if (
144
            ResourceDO::DEFAULT_VARIANT !== $resourceDO->getVariant()
145
            && $this->config->get('staticus.magic_defaults.variant')
146
        ) {
147
            $defaultDO = clone $resourceDO;
148
            $defaultDO->setVariant();
149
            $defaultDO->setVersion();
150
            $this->copyResource($resourceDO, $defaultDO);
151
        }
152
        if (
153
            ResourceDO::DEFAULT_VERSION !== $resourceDO->getVersion()
154
            && $this->config->get('staticus.magic_defaults.version')
155
        ) {
156
            $defaultDO = clone $resourceDO;
157
            $defaultDO->setVersion();
158
            $this->copyResource($resourceDO, $defaultDO);
159
        }
160
    }
161
162
    /**
163
     * @param ResourceDOInterface $resourceDO
164
     * @param string|resource|Stream $content
165
     * @return ResourceDOInterface
166
     * @throws \RuntimeException if the upload was not successful.
167
     * @throws \InvalidArgumentException if the $path specified is invalid.
168
     * @throws \RuntimeException on any error during the move operation, or on
169
     */
170
    protected function save(ResourceDOInterface $resourceDO, $content)
171
    {
172
        $backupResourceVerDO = null;
173
        $filePath = $resourceDO->getFilePath();
174
        $this->createDirectory(dirname($filePath));
175
        // backups don't needs if this is a 'new creation' command
176
        if ($resourceDO->isRecreate()) {
177
            $backupResourceVerDO = $this->backup($resourceDO);
178
        }
179
        if ($content instanceof UploadedFileInterface) {
180
            $this->uploadFile($content, $resourceDO->getMimeType(), $filePath);
181
        } else {
182
            $this->writeFile($filePath, $content);
183
        }
184
185
        $responseDO = $resourceDO;
186
        if ($backupResourceVerDO instanceof ResourceDOInterface
187
            && $backupResourceVerDO->getVersion() !== ResourceDOInterface::DEFAULT_VERSION) {
188
            // If the newly created file is the same as the previous version, remove backup immediately
189
            $responseDO = $this->destroyEqual($resourceDO, $backupResourceVerDO);
190
        }
191
        if ($responseDO === $resourceDO) {
192
193
            // cleanup postprocessing cache folders
194
            // - if it is a new file creation (remove possible garbage after other operations)
195
            // - or if the basic file is replaced and not equal to the previous version
196
            $this->afterSave($resourceDO);
197
        }
198
199
        return $resourceDO;
200
    }
201
    abstract protected function afterSave(ResourceDOInterface $resourceDO);
202
203
    protected function backup(ResourceDOInterface $resourceDO)
204
    {
205
        $command = new BackupResourceCommand($resourceDO, $this->filesystem);
206
        $backupResourceVerDO = $command();
207
208
        return $backupResourceVerDO;
209
    }
210
211
    /**
212
     * @param ResourceDOInterface $resourceDO
213
     * @param ResourceDOInterface $backupResourceVerDO
214
     * @return mixed
215
     */
216
    protected function destroyEqual(ResourceDOInterface $resourceDO, ResourceDOInterface $backupResourceVerDO)
217
    {
218
        $command = new DestroyEqualResourceCommand($resourceDO, $backupResourceVerDO, $this->filesystem);
219
        $responseDO = $command();
220
221
        return $responseDO;
222
    }
223
}
224