Completed
Pull Request — master (#62)
by Vladimir
02:32
created

File   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 200
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 2

Test Coverage

Coverage 95.65%

Importance

Changes 5
Bugs 0 Features 0
Metric Value
wmc 19
lcom 2
cbo 2
dl 0
loc 200
ccs 44
cts 46
cp 0.9565
rs 10
c 5
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A createFileForRelativePath() 0 4 1
A exists() 0 4 1
A getBasename() 0 4 1
A getFilename() 0 4 1
A getAbsolutePath() 0 4 1
A getParentFolder() 0 4 1
A getRelativeFilePath() 0 4 1
A getRelativeParentFolder() 0 4 1
A getContents() 0 17 3
A isSafeToRead() 0 12 3
A buildNotFoundException() 0 9 1
A realpath() 0 4 2
A isVFS() 0 4 1
1
<?php
2
3
/**
4
 * @copyright 2017 Vladimir Jimenez
5
 * @license   https://github.com/allejo/stakx/blob/master/LICENSE.md MIT
6
 */
7
8
namespace allejo\stakx\Filesystem;
9
10
use allejo\stakx\Service;
11
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
12
13
/**
14
 * A representation of a file on a given filesystem, virtual or physical.
15
 *
16
 * This class extends \SplFileInfo and adds new methods along with overriding some methods solely because I feel that
17
 * some of the naming can be misleading.
18
 *
19
 * @since 0.2.0
20
 */
21
final class File extends \SplFileInfo
22
{
23
    /** @var string The path relative to the site's working directory. */
24
    private $relativePath;
25
26
    /** @var string The original raw path given to the constructor. */
27
    private $rawPath;
28
29
    /**
30
     * File Constructor.
31
     *
32
     * @param string $filePath An absolute file path or a path relative to the current working directory.
33
     *
34
     * @since 0.2.0
35
     */
36 166
    public function __construct($filePath)
37
    {
38 166
        $this->rawPath = $filePath;
39
40 166
        parent::__construct(self::realpath($filePath));
41
42 166
        $this->relativePath = str_replace(Service::getWorkingDirectory() . DIRECTORY_SEPARATOR, '', $this->getAbsolutePath());
43
44 166
        $this->isSafeToRead();
45 164
    }
46
47
    /**
48
     * Get a new File object for another file relative to this file.
49
     *
50
     * @param string $path
51
     *
52
     * @return File
53
     */
54 11
    public function createFileForRelativePath($path)
55
    {
56 11
        return new File(Service::getWorkingDirectory() . DIRECTORY_SEPARATOR . $path);
57
    }
58
59
    /**
60
     * Whether or not this file exists on the filesystem.
61
     *
62
     * @return bool
63
     */
64 157
    public function exists()
65
    {
66 157
        return file_exists($this->getAbsolutePath());
67
    }
68
69
    /**
70
     * Get the name of the file without an extension.
71
     *
72
     * @param  null $suffix This value will be discarded and is only needed to be able to override the \SplFileInfo
73
     *                      definition.
74
     *
75
     * @since 0.2.0
76
     *
77
     * @return string
78
     */
79 5
    public function getBasename($suffix = null)
80
    {
81 5
        return parent::getBasename('.' . $this->getExtension());
82
    }
83
84
    /**
85
     * Get the name of the with the extension.
86
     *
87
     * @since 0.2.0
88
     *
89
     * @return string
90
     */
91 1
    public function getFilename()
92
    {
93 1
        return parent::getBasename();
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (getBasename() instead of getFilename()). Are you sure this is correct? If so, you might want to change this to $this->getBasename().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
94
    }
95
96
    /**
97
     * Get the absolute path to this file.
98
     *
99
     * @since 0.2.0
100
     *
101
     * @return string
102
     */
103 166
    public function getAbsolutePath()
104
    {
105 166
        return $this->getPathname();
106
    }
107
108
    /**
109
     * Get the path to the parent folder of this file.
110
     *
111
     * @since 0.2.0
112
     *
113
     * @return string
114
     */
115 1
    public function getParentFolder()
116
    {
117 1
        return $this->getPath();
118
    }
119
120
    /**
121
     * Get the file path to this file, relative to where it was created; likely the current working directory.
122
     *
123
     * @since 0.2.0
124
     *
125
     * @return string
126
     */
127 137
    public function getRelativeFilePath()
128
    {
129 137
        return $this->relativePath;
130
    }
131
132
    /**
133
     * Get the path to the parent folder this file, relative to where it was created; likely the current working directory.
134
     *
135
     * @since 0.2.0
136
     *
137
     * @return string
138
     */
139 2
    public function getRelativeParentFolder()
140
    {
141 2
        return dirname($this->getRelativeFilePath());
142
    }
143
144
    /**
145
     * Get the contents of this file.
146
     *
147
     * @since 0.2.0
148
     *
149
     * @throws \RuntimeException When the file could not be read.
150
     *
151
     * @return string
152
     */
153 156
    public function getContents()
154
    {
155 156
        if (!$this->exists())
156
        {
157 1
            throw $this->buildNotFoundException();
158
        }
159
160 156
        $content = file_get_contents($this->getAbsolutePath());
161
162 156
        if ($content === false)
163
        {
164
            $error = error_get_last();
165
            throw new \RuntimeException($error['message']);
166
        }
167
168 156
        return $content;
169
    }
170
171
    /**
172
     * Check if a file is safe to read.
173
     */
174 166
    private function isSafeToRead()
175
    {
176 166
        if (self::isVFS($this->getAbsolutePath()))
177
        {
178 118
            return;
179
        }
180
181 53
        if (strpos($this->getAbsolutePath(), Service::getWorkingDirectory()) !== 0)
182
        {
183 3
            throw $this->buildNotFoundException();
184
        }
185 51
    }
186
187 4
    private function buildNotFoundException()
188
    {
189 4
        return new FileNotFoundException(
190 4
            sprintf('The given path "%s" does not exist or is outside the website working directory', $this->rawPath),
191 4
            0,
192 4
            null,
193 4
            $this->rawPath
194
        );
195
    }
196
197
    /**
198
     * A vfsStream friendly way of getting the realpath() of something.
199
     *
200
     * @param string $path
201
     *
202
     * @return string
203
     */
204 166
    public static function realpath($path)
205
    {
206 166
        return self::isVFS($path) ? $path : realpath($path);
207
    }
208
209
    /**
210
     * Check whether a given path is on the virtual filesystem.
211
     *
212
     * @param string $path
213
     *
214
     * @return bool
215
     */
216 166
    private static function isVFS($path)
217
    {
218 166
        return substr($path, 0, 6) == 'vfs://';
219
    }
220
}
221