Completed
Push — master ( 9a18cf...d044fe )
by Sebastian
01:59
created

src/Client.php (3 issues)

Labels
Severity
1
<?php
2
/**
3
 * This file is part of SebastianFeldmann\Ftp.
4
 *
5
 * (c) Sebastian Feldmann <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace SebastianFeldmann\Ftp;
11
12
use RuntimeException;
13
14
/**
15
 * Class Client
16
 *
17
 * @package SebastianFeldmann\Ftp
18
 * @author  Sebastian Feldmann <[email protected]>
19
 * @link    https://github.com/sebastianfeldmann/ftp
20
 * @since   Class available since Release 1.0.0
21
 *
22
 * @method void   cdUp()                   - Changes to the parent directory
23
 * @method void   chDir(string $directory) - Changes the current directory on a FTP server
24
 * @method void   pwd()                    - Returns current working directory
25
 * @method array  nlist(string $path)      - Returns list of files in given dir
26
 * @method int    size(string $file)       - Returns given files sizes in bytes
27
 * @method string mdtm(string $file)       - Returns last modification time from given file
28
 * @method void   pasv(bool $passive)      - Sets the ftp passive mode on or off
29
 */
30
class Client
31
{
32
    /**
33
     * PHP FTP connection resource.
34
     *
35
     * @var resource
36
     */
37
    private $connection;
38
39
    /**
40
     * Host to connect to.
41
     *
42
     * @var string
43
     */
44
    private $host;
45
46
    /**
47
     * Port to connect to.
48
     *
49
     * @var int
50
     */
51
    private $port;
52
53
    /**
54
     * User to login.
55
     *
56
     * @var string
57
     */
58
    private $user;
59
60
    /**
61
     * Password to login.
62
     *
63
     * @var string
64
     */
65
    private $password;
66
67
    /**
68
     * Use passive ftp mode
69
     *
70
     * @var bool
71
     */
72
    private $passive;
73
74
    /**
75
     * Client constructor.
76
     *
77
     * @param string $url
78
     * @param bool   $passive
79
     */
80 1
    public function __construct(string $url, bool $passive = false)
81
    {
82 1
        if (!extension_loaded('ftp')) {
83
            throw new RuntimeException('FTP extension is not loaded.');
84
        }
85 1
        $this->passive = $passive;
86 1
        $this->setup($url);
87 1
        $this->login();
88 1
    }
89
90
    /**
91
     * Determine if file is a directory.
92
     *
93
     * @param  string $name
94
     * @return bool
95
     */
96 1
    public function isDir(string $name)
97
    {
98 1
        $current = $this->pwd();
0 ignored issues
show
Are you sure the assignment to $current is correct as $this->pwd() targeting SebastianFeldmann\Ftp\Client::pwd() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
99
        try {
100 1
            $this->chDir($name);
101 1
            $this->chDir($current);
0 ignored issues
show
$current of type void is incompatible with the type string expected by parameter $directory of SebastianFeldmann\Ftp\Client::chDir(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

101
            $this->chDir(/** @scrutinizer ignore-type */ $current);
Loading history...
102 1
            return true;
103 1
        } catch (\Exception $e) {
104
            // do nothing
105
        }
106 1
        return false;
107
    }
108
109
    /**
110
     * Returns to the home directory.
111
     *
112
     * @return void
113
     */
114
    public function chHome()
115
    {
116
        $this->chDir('');
117
    }
118
119
    /**
120
     * Return list of all files in directory.
121
     *
122
     * @param  string $path
123
     * @return \SebastianFeldmann\Ftp\File[]
124
     * @throws \Exception
125
     */
126 1
    public function ls(string $path = '') : array
127
    {
128 1
        $list = [];
129 1
        foreach ($this->nlist($path) as $name) {
130 1
            $type   = $this->isDir($name) ? 'dir' : 'file';
131 1
            $mtime  = $this->mdtm($name);
132 1
            $size   = $this->size($name);
133 1
            $list[] = new File(['name' => $name, 'modify' => $mtime, 'type' => $type, 'size' => $size]);
134
        }
135 1
        return $list;
136
    }
137
138
    /**
139
     * Return list of directories in given path.
140
     *
141
     * @param  string $path
142
     * @return array
143
     * @throws \Exception
144
     */
145
    public function lsDirs(string $path = '') : array
146
    {
147
        return array_filter(
148
            $this->ls($path),
149
            function(File $file) {
150
                return $file->isDir();
151
            }
152
        );
153
    }
154
155
    /**
156
     * Return list of files in given path.
157
     *
158
     * @param  string $path
159
     * @return array
160
     * @throws \Exception
161
     */
162
    public function lsFiles(string $path = '') : array
163
    {
164
        return array_filter(
165
            $this->ls($path),
166
            function(File $file) {
167
                return $file->isFile();
168
            }
169
        );
170
    }
171
172
    /**
173
     * Upload local file to ftp server.
174
     *
175
     * @param  string $file Path to local file that should be uploaded.
176
     * @param  string $path Path to store the file under.
177
     * @param  string $name Filename on the ftp server.
178
     * @return void
179
     */
180
    public function uploadFile(string $file, string $path, string $name)
181
    {
182
        // to store the file we have to make sure the directory exists
183
        foreach ($this->extractDirectories($path) as $dir) {
184
            // if change to directory fails
185
            // create the dir and change into it afterwards
186
            if (!$this->chDir($dir)) {
0 ignored issues
show
Are you sure the usage of $this->chDir($dir) targeting SebastianFeldmann\Ftp\Client::chDir() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
187
                $this->mkDir($dir);
188
                $this->chDir($dir);
189
            }
190
        }
191
        if (!$this->put($name, $file, FTP_BINARY)) {
192
            $error   = error_get_last();
193
            $message = $error['message'];
194
            throw new RuntimeException(sprintf('error uploading file: %s - %s', $file, $message));
195
        }
196
    }
197
198
    /**
199
     * Setup local member variables by parsing the ftp url.
200
     *
201
     * @param string $url
202
     */
203 1
    private function setup(string $url)
204
    {
205 1
        $parts          = \parse_url($url);
206 1
        $this->host     = $parts['host'] ?? '';
207 1
        $this->port     = $parts['port'] ?? 21;
208 1
        $this->user     = $parts['user'] ?? '';
209 1
        $this->password = $parts['pass'] ?? '';
210 1
    }
211
212
    /**
213
     * Setup ftp connection
214
     *
215
     * @throws \RuntimeException
216
     */
217 1
    private function login()
218
    {
219 1
        if (empty($this->host)) {
220
            throw new RuntimeException('no host to connect to');
221
        }
222
223 1
        $old = error_reporting(0);
224 1
        if (!$this->connection = ftp_connect($this->host, $this->port)) {
225
            error_reporting($old);
226
            throw new RuntimeException(sprintf('unable to connect to ftp server %s', $this->host));
227
        }
228
229 1
        if (!ftp_login($this->connection, $this->user, $this->password)) {
230
            error_reporting($old);
231
            throw new RuntimeException(
232
                sprintf('authentication failed for %s@%s', $this->user, $this->host)
233
            );
234
        }
235
        // set passive mode if needed
236 1
        $this->pasv($this->passive);
237 1
        error_reporting($old);
238 1
    }
239
240
    /**
241
     * Return list of remote directories to travers.
242
     *
243
     * @param  string $path
244
     * @return array
245
     */
246
    private function extractDirectories(string $path) : array
247
    {
248
        $remoteDirs = [];
249
        if (!empty($path)) {
250
            $remoteDirs = explode('/', $path);
251
            // fix empty first array element for absolute path
252
            if (substr($path, 0, 1) === '/') {
253
                $remoteDirs[0] = '/';
254
            }
255
            $remoteDirs = array_filter($remoteDirs);
256
        }
257
        return $remoteDirs;
258
    }
259
260
    /**
261
     * Handle all ftp_* functions.
262
     *
263
     * @param  string $name
264
     * @param  array  $args
265
     * @return mixed
266
     */
267 1
    public function __call($name, $args)
268
    {
269 1
        $function = 'ftp_' . strtolower($name);
270 1
        if (!function_exists($function)) {
271
            throw new RuntimeException(sprintf('invalid method call: %s', $function));
272
        }
273 1
        $old = error_reporting(0);
274 1
        array_unshift($args, $this->connection);
275 1
        if (!$result = call_user_func_array($function, $args)) {
276 1
            $error = error_get_last();
277 1
            error_reporting($old);
278 1
            throw new RuntimeException($error['message']);
279
        }
280 1
        error_reporting($old);
281 1
        return $result;
282
    }
283
}
284