Completed
Push — master ( 62075d...fae35c )
by Nicolas
03:27 queued 19s
created

src/Gaufrette/File.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Gaufrette;
4
5
use Gaufrette\Adapter\MetadataSupporter;
6
use Gaufrette\Exception\FileNotFound;
7
8
/**
9
 * Points to a file in a filesystem.
10
 *
11
 * @author Antoine Hérault <[email protected]>
12
 */
13
class File
14
{
15
    protected $key;
16
    protected $filesystem;
17
18
    /**
19
     * Content variable is lazy. It will not be read from filesystem until it's requested first time.
20
     *
21
     * @var mixed content
22
     */
23
    protected $content = null;
24
25
    /**
26
     * @var array metadata in associative array. Only for adapters that support metadata
27
     */
28
    protected $metadata = null;
29
30
    /**
31
     * Human readable filename (usually the end of the key).
32
     *
33
     * @var string name
34
     */
35
    protected $name = null;
36
37
    /**
38
     * File size in bytes.
39
     *
40
     * @var int size
41
     */
42
    protected $size = 0;
43
44
    /**
45
     * File date modified.
46
     *
47
     * @var int mtime
48
     */
49
    protected $mtime = null;
50
51
    /**
52
     * @param string     $key
53
     * @param Filesystem $filesystem
54
     */
55
    public function __construct($key, Filesystem $filesystem)
56
    {
57
        $this->key = $key;
58
        $this->name = $key;
59
        $this->filesystem = $filesystem;
60
    }
61
62
    /**
63
     * Returns the key.
64
     *
65
     * @return string
66
     */
67
    public function getKey()
68
    {
69
        return $this->key;
70
    }
71
72
    /**
73
     * Returns the content.
74
     *
75
     * @throws FileNotFound
76
     *
77
     * @param array $metadata optional metadata which should be set when read
78
     *
79
     * @return string
80
     */
81 View Code Duplication
    public function getContent($metadata = array())
82
    {
83
        if (isset($this->content)) {
84
            return $this->content;
85
        }
86
        $this->setMetadata($metadata);
87
88
        return $this->content = $this->filesystem->read($this->key);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->content = $this->...stem->read($this->key); of type string|boolean adds the type boolean to the return on line 88 which is incompatible with the return type documented by Gaufrette\File::getContent of type string.
Loading history...
89
    }
90
91
    /**
92
     * @return string name of the file
93
     */
94
    public function getName()
95
    {
96
        return $this->name;
97
    }
98
99
    /**
100
     * @return int size of the file
101
     */
102
    public function getSize()
103
    {
104
        if ($this->size) {
105
            return $this->size;
106
        }
107
108
        try {
109
            return $this->size = $this->filesystem->size($this->getKey());
110
        } catch (FileNotFound $exception) {
111
        }
112
113
        return 0;
114
    }
115
116
    /**
117
     * Returns the file modified time.
118
     *
119
     * @return int
120
     */
121
    public function getMtime()
122
    {
123
        return $this->mtime = $this->filesystem->mtime($this->key);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->filesystem->mtime($this->key) can also be of type boolean. However, the property $mtime is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
124
    }
125
126
    /**
127
     * @param int $size size of the file
128
     */
129
    public function setSize($size)
130
    {
131
        $this->size = $size;
132
    }
133
134
    /**
135
     * Sets the content.
136
     *
137
     * @param string $content
138
     * @param array  $metadata optional metadata which should be send when write
139
     *
140
     * @return int The number of bytes that were written into the file, or
141
     *             FALSE on failure
142
     */
143 View Code Duplication
    public function setContent($content, $metadata = array())
144
    {
145
        $this->content = $content;
146
        $this->setMetadata($metadata);
147
148
        return $this->size = $this->filesystem->write($this->key, $this->content, true);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->filesystem->write..., $this->content, true) can also be of type boolean. However, the property $size is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
149
    }
150
151
    /**
152
     * @param string $name name of the file
153
     */
154
    public function setName($name)
155
    {
156
        $this->name = $name;
157
    }
158
159
    /**
160
     * Indicates whether the file exists in the filesystem.
161
     *
162
     * @return bool
163
     */
164
    public function exists()
165
    {
166
        return $this->filesystem->has($this->key);
167
    }
168
169
    /**
170
     * Deletes the file from the filesystem.
171
     *
172
     * @throws FileNotFound
173
     * @throws \RuntimeException when cannot delete file
174
     *
175
     * @param array $metadata optional metadata which should be send when write
176
     *
177
     * @return bool TRUE on success
178
     */
179
    public function delete($metadata = array())
180
    {
181
        $this->setMetadata($metadata);
182
183
        return $this->filesystem->delete($this->key);
184
    }
185
186
    /**
187
     * Creates a new file stream instance of the file.
188
     *
189
     * @return Stream
190
     */
191
    public function createStream()
192
    {
193
        return $this->filesystem->createStream($this->key);
194
    }
195
196
    /**
197
     * Rename the file and move it to its new location.
198
     *
199
     * @param string $newKey
200
     */
201
    public function rename($newKey)
202
    {
203
        $this->filesystem->rename($this->key, $newKey);
204
205
        $this->key = $newKey;
206
    }
207
208
    /**
209
     * Sets the metadata array to be stored in adapters that can support it.
210
     *
211
     * @param array $metadata
212
     *
213
     * @return bool
214
     */
215
    protected function setMetadata(array $metadata)
216
    {
217
        if ($metadata && $this->supportsMetadata()) {
218
            $this->filesystem->getAdapter()->setMetadata($this->key, $metadata);
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Gaufrette\Adapter as the method setMetadata() does only exist in the following implementations of said interface: Gaufrette\Adapter\AclAwareAmazonS3, Gaufrette\Adapter\AmazonS3, Gaufrette\Adapter\AwsS3, Gaufrette\Adapter\AzureBlobStorage, Gaufrette\Adapter\Cache, Gaufrette\Adapter\GoogleCloudStorage, Gaufrette\Adapter\GridFS.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
219
220
            return true;
221
        }
222
223
        return false;
224
    }
225
226
    /**
227
     * @return bool
228
     */
229
    private function supportsMetadata()
230
    {
231
        return $this->filesystem->getAdapter() instanceof MetadataSupporter;
232
    }
233
}
234