Test Failed
Push — master ( 5fcf14...625fb6 )
by Adrian Florin
04:15 queued 01:51
created

File::prependContent()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 8
Ratio 100 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 8
loc 8
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
crap 2
1
<?php
2
/**
3
 * This file is part of the NeedleProject\FileIo package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
namespace NeedleProject\FileIo;
9
10
use NeedleProject\FileIo\Content\ContentInterface;
11
use NeedleProject\FileIo\Exception\FileNotFoundException;
12
use NeedleProject\FileIo\Exception\IOException;
13
use NeedleProject\FileIo\Exception\PermissionDeniedException;
14
use NeedleProject\FileIo\Factory\ContentFactory;
15
use NeedleProject\FileIo\Helper\PathHelper;
16
use NeedleProject\Common\Util\ErrorToExceptionConverter;
17
18
/**
19
 * Class File
20
 *
21
 * @package NeedleProject\FileIo
22
 * @author Adrian Tilita <[email protected]>
23
 * @copyright 2017 Adrian Tilita
24
 * @license https://opensource.org/licenses/MIT MIT Licence
25
 */
26
class File
27
{
28
    /**
29
     * File extension separator
30
     * @const string
31
     */
32
    const EXTENSION_SEPARATOR = '.';
33
34
    /**
35
     * File's name including the path
36
     * @var null|string
37
     */
38
    private $filenameWithPath = null;
39
40
    /**
41
     * File's extension - For no extension a blank string will be used
42
     * @var null|string
43
     */
44
    private $extension = null;
45
46
    /**
47
     * File's name without extension
48
     * @var null|string
49
     */
50
    private $name = null;
51
52
    /**
53
     * Whether the file has an extension or if it is set by us
54
     * @var bool
55
     */
56
    private $hasExtension = false;
57
58
    /**
59
     * @var null|ContentFactory
60
     */
61
    private $contentFactory = null;
62
63
    /**
64
     * @var null|ErrorToExceptionConverter
65
     */
66
    private $errorHandler = null;
67
68
    /**
69
     * File constructor.
70
     *
71
     * @param string $filenameWithPath
72
     */
73 51
    public function __construct(string $filenameWithPath)
74
    {
75 51
        $pathHelper = new PathHelper();
76 51
        $this->filenameWithPath = $pathHelper->normalizePathSeparator($filenameWithPath);
77 51
        $filename = $pathHelper->extractFilenameFromPath($this->filenameWithPath);
78 51
        if (empty($filename) || false === $this->validatePath($this->filenameWithPath)) {
79 2
            throw new \RuntimeException(
80 2
                sprintf('Given path %s does not represents a file!', $filenameWithPath)
81
            );
82
        }
83 49
        list($this->name, $this->extension) = $pathHelper->splitFilename($filename);
84 49
        $this->hasExtension = (bool)$this->extension;
85 49
        $this->errorHandler = new ErrorToExceptionConverter();
86 49
    }
87
88
    /**
89
     * States whether the file actually exists on disk
90
     * @return bool
91
     */
92 50
    public function exists(): bool
0 ignored issues
show
Coding Style introduced by
function exists() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
93
    {
94 50
        return file_exists($this->filenameWithPath);
95
    }
96
97
    /**
98
     * States whether the file is readable
99
     * @return bool
100
     */
101 9
    public function isReadable(): bool
102
    {
103 9
        return is_readable($this->filenameWithPath);
104
    }
105
106
    /**
107
     * @return bool
108
     */
109 17
    public function isWritable(): bool
110
    {
111 17
        if ($this->exists()) {
112 16
            return is_writable($this->filenameWithPath);
113
        }
114 1
        $parts = explode(DIRECTORY_SEPARATOR, $this->filenameWithPath);
115 1
        array_pop($parts);
116 1
        return is_writable(implode(DIRECTORY_SEPARATOR, $parts));
117
    }
118
119
    /**
120
     * Write content to the current file
121
     *
122
     * @param \NeedleProject\FileIo\Content\ContentInterface $content
123
     * @return \NeedleProject\FileIo\File
124
     * @throws \NeedleProject\FileIo\Exception\PermissionDeniedException
125
     */
126 11
    public function write(ContentInterface $content): File
127
    {
128 11
        if ($this->isWritable() === false) {
129 1
            throw new PermissionDeniedException("The current file is not writable!");
130
        }
131 10
        file_put_contents($this->filenameWithPath, $content->get());
132 10
        return $this;
133
    }
134
135
    /**
136
     * @return \NeedleProject\FileIo\Content\ContentInterface
137
     * @throws \NeedleProject\FileIo\Exception\FileNotFoundException
138
     * @throws \NeedleProject\FileIo\Exception\IOException
139
     * @throws \NeedleProject\FileIo\Exception\PermissionDeniedException
140
     */
141 9
    public function getContent(): ContentInterface
142
    {
143 9
        if ($this->exists() === false) {
144 2
            throw new FileNotFoundException(sprintf("%s does not exists!", $this->filenameWithPath));
145
        }
146 7
        if ($this->isReadable() === false) {
147 1
            throw new PermissionDeniedException(
148 1
                sprintf("You do not have permissions to read file %s!", $this->filenameWithPath)
149
            );
150
        }
151 6
        $this->convertErrors();
152 6
        $stringContent = file_get_contents($this->filenameWithPath);
153 5
        $this->resetErrorHandler();
154 5
        if (false === $stringContent) {
155 1
            throw new IOException(
156 1
                sprintf("Could not retrieve content! Error message: %s", error_get_last()['message'])
157
            );
158
        }
159 4
        return $this->getContentFactory()
160 4
            ->create($this->extension, $stringContent);
161
    }
162
163
    /**
164
     * Add content to the begging of the file
165
     * @param string $content
166
     * @return $this
167
     * @throws \NeedleProject\FileIo\Exception\PermissionDeniedException
168
     */
169 4 View Code Duplication
    public function appendContent(string $content)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
170
    {
171 4
        if ($this->isWritable() === false) {
172 1
            throw new PermissionDeniedException("The current file is not writable!");
173
        }
174 3
        file_put_contents($this->filenameWithPath, $content, FILE_APPEND);
175 3
        return $this;
176
    }
177
178
    /**
179
     * Add content to the begging of the file
180
     * @param string $content
181
     * @return $this
182
     * @throws \NeedleProject\FileIo\Exception\PermissionDeniedException
183
     */
184 4 View Code Duplication
    public function prependContent(string $content)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
185
    {
186 4
        if ($this->isWritable() === false) {
187 1
            throw new PermissionDeniedException("The current file is not writable!");
188
        }
189 3
        file_put_contents($this->filenameWithPath, $content . $this->getContent()->get());
190 3
        return $this;
191
    }
192
193
    /**
194
     * Deletes the current file
195
     * @return bool
196
     * @throws \NeedleProject\FileIo\Exception\IOException
197
     */
198 3
    public function delete(): bool
0 ignored issues
show
Coding Style introduced by
function delete() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
199
    {
200 3
        if ($this->exists() === false) {
201 2
            return false;
202
        }
203 1
        $this->convertErrors();
204 1
        $unlinkResult = unlink($this->filenameWithPath);
205 1
        $this->resetErrorHandler();
206 1
        return $unlinkResult;
207
    }
208
209
    /**
210
     * State existence of a file's extension
211
     * @return bool
212
     */
213 10
    public function hasExtension(): bool
214
    {
215 10
        return $this->hasExtension;
216
    }
217
218
    /**
219
     * Get file's extension
220
     * @return string
221
     */
222 4
    public function getExtension(): string
223
    {
224 4
        return $this->extension;
225
    }
226
227
    /**
228
     * Get file's name without extension
229
     * @return string
230
     */
231 4
    public function getName(): string
232
    {
233 4
        return $this->name;
234
    }
235
236
    /**
237
     * Get file's name with extension
238
     * @return string
239
     */
240 5
    public function getBasename(): string
241
    {
242 5
        if (false === $this->hasExtension()) {
243 1
            return $this->name;
244
        }
245 4
        return $this->name . static::EXTENSION_SEPARATOR . $this->extension;
246
    }
247
248
    /**
249
     * Returns a factory responsible for creating appropriate content
250
     * @return \NeedleProject\FileIo\Factory\ContentFactory
251
     */
252 4
    protected function getContentFactory(): ContentFactory
253
    {
254 4
        if (is_null($this->contentFactory)) {
255 4
            $this->contentFactory = new ContentFactory();
256
        }
257 4
        return $this->contentFactory;
258
    }
259
260
    /**
261
     * Validate if the given path is not a directory
262
     * @param string $filenameWithPath
263
     * @return bool
264
     */
265 50
    private function validatePath(string $filenameWithPath): bool
0 ignored issues
show
Coding Style introduced by
function validatePath() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
266
    {
267 50
        return !($this->exists() && is_dir($filenameWithPath));
268
    }
269
270
    /**
271
     * Convert errors to Exception type objects
272
     */
273 7
    protected function convertErrors()
274
    {
275 7
        $this->errorHandler->convertErrorsToExceptions(E_ALL, IOException::class);
276 7
    }
277
278
    /**
279
     * Undo error to exception conversion
280
     */
281 6
    protected function resetErrorHandler()
282
    {
283 6
        $this->errorHandler->restoreErrorHandler();
284 6
    }
285
}
286