Completed
Push — master ( 61c24d...075a0d )
by recca
07:28
created

elFinderVolumeGoogleDrive::init()   F

Complexity

Conditions 43
Paths > 20000

Size

Total Lines 150
Code Lines 95

Duplication

Lines 6
Ratio 4 %

Importance

Changes 0
Metric Value
cc 43
eloc 95
nc 429496.7295
nop 0
dl 6
loc 150
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 12 and the first side effect is on line 3.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
elFinder::$netDrivers['googledrive'] = 'GoogleDrive';
4
5
/**
6
 * Simple elFinder driver for GoogleDrive
7
 * google-api-php-client-2.x or above.
8
 *
9
 * @author Dmitry (dio) Levashov
10
 * @author Cem (discofever)
11
 **/
12
class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
13
{
14
    /**
15
     * MIME tyoe of directory.
16
     *
17
     * @var string
18
     */
19
    const DIRMIME = 'application/vnd.google-apps.folder';
20
21
    /**
22
     * Fetch fields for list.
23
     *
24
     * @var string
25
     */
26
    const FETCHFIELDS_LIST = 'files(id,name,mimeType,modifiedTime,parents,permissions,size,imageMediaMetadata(height,width),thumbnailLink,webContentLink,webViewLink),nextPageToken';
27
28
    /**
29
     * Fetch fields for get.
30
     *
31
     * @var string
32
     */
33
    const FETCHFIELDS_GET = 'id,name,mimeType,modifiedTime,parents,permissions,size,imageMediaMetadata(height,width),thumbnailLink,webContentLink,webViewLink';
34
35
    /**
36
     * Net mount key.
37
     *
38
     * @var string
39
     **/
40
    public $netMountKey = '';
41
    /**
42
     * Driver id
43
     * Must be started from letter and contains [a-z0-9]
44
     * Used as part of volume id.
45
     *
46
     * @var string
47
     **/
48
    protected $driverId = 'gd';
49
50
    /**
51
     * Google API client object.
52
     *
53
     * @var object
54
     **/
55
    protected $client = null;
56
57
    /**
58
     * GoogleDrive service object.
59
     *
60
     * @var object
61
     **/
62
    protected $service = null;
63
64
    /**
65
     * Cache of parents of each directories.
66
     *
67
     * @var array
68
     */
69
    protected $parents = [];
70
71
    /**
72
     * Cache of chiled directories of each directories.
73
     *
74
     * @var array
75
     */
76
    protected $directories = null;
77
78
    /**
79
     * Cache of itemID => name of each items.
80
     *
81
     * @var array
82
     */
83
    protected $names = [];
84
85
    /**
86
     * Directory for tmp files
87
     * If not set driver will try to use tmbDir as tmpDir.
88
     *
89
     * @var string
90
     **/
91
    protected $tmp = '';
92
93
    /**
94
     * Constructor
95
     * Extend options with required fields.
96
     *
97
     * @author Dmitry (dio) Levashov
98
     * @author Cem (DiscoFever)
99
     **/
100
    public function __construct()
101
    {
102
        $opts = [
103
            'client_id' => '',
104
            'client_secret' => '',
105
            'access_token' => [],
106
            'refresh_token' => '',
107
            'serviceAccountConfigFile' => '',
108
            'root' => 'My Drive',
109
            'gdAlias' => '%s@GDrive',
110
            'googleApiClient' => '',
111
            'path' => '/',
112
            'tmbPath' => '',
113
            'separator' => '/',
114
            'useGoogleTmb' => true,
115
            'acceptedName' => '#^[^/\\?*:|"<>]*[^./\\?*:|"<>]$#',
116
            'rootCssClass' => 'elfinder-navbar-root-googledrive',
117
            'publishPermission' => [
118
                'type' => 'anyone',
119
                'role' => 'reader',
120
                'withLink' => true,
121
            ],
122
            'appsExportMap' => [
123
                'application/vnd.google-apps.document' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
124
                'application/vnd.google-apps.spreadsheet' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
125
                'application/vnd.google-apps.drawing' => 'application/pdf',
126
                'application/vnd.google-apps.presentation' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
127
                'application/vnd.google-apps.script' => 'application/vnd.google-apps.script+json',
128
                'default' => 'application/pdf',
129
            ],
130
        ];
131
        $this->options = array_merge($this->options, $opts);
132
        $this->options['mimeDetect'] = 'internal';
133
    }
134
135
    /*********************************************************************/
136
    /*                        EXTENDED FUNCTIONS                         */
137
    /*********************************************************************/
138
139
    /**
140
     * Prepare
141
     * Call from elFinder::netmout() before volume->mount().
142
     *
143
     * @return array
144
     *
145
     * @author Naoki Sawada
146
     * @author Raja Sharma updating for GoogleDrive
147
     **/
148
    public function netmountPrepare($options)
0 ignored issues
show
Coding Style introduced by
netmountPrepare uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
149
    {
150
        if (empty($options['client_id']) && defined('ELFINDER_GOOGLEDRIVE_CLIENTID')) {
151
            $options['client_id'] = ELFINDER_GOOGLEDRIVE_CLIENTID;
152
        }
153
        if (empty($options['client_secret']) && defined('ELFINDER_GOOGLEDRIVE_CLIENTSECRET')) {
154
            $options['client_secret'] = ELFINDER_GOOGLEDRIVE_CLIENTSECRET;
155
        }
156
        if (empty($options['googleApiClient']) && defined('ELFINDER_GOOGLEDRIVE_GOOGLEAPICLIENT')) {
157
            $options['googleApiClient'] = ELFINDER_GOOGLEDRIVE_GOOGLEAPICLIENT;
158
            include_once $options['googleApiClient'];
159
        }
160
161
        if (! isset($options['pass'])) {
162
            $options['pass'] = '';
163
        }
164
165
        try {
166
            $client = new \Google_Client();
167
            $client->setClientId($options['client_id']);
168
            $client->setClientSecret($options['client_secret']);
169
170 View Code Duplication
            if ($options['pass'] === 'reauth') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
171
                $options['pass'] = '';
172
                $this->session->set('GoogleDriveAuthParams', [])->set('GoogleDriveTokens', []);
173
            } elseif ($options['pass'] === 'googledrive') {
174
                $options['pass'] = '';
175
            }
176
177
            $options = array_merge($this->session->get('GoogleDriveAuthParams', []), $options);
178
179 View Code Duplication
            if (! isset($options['access_token'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
180
                $options['access_token'] = $this->session->get('GoogleDriveTokens', []);
181
                $this->session->remove('GoogleDriveTokens');
182
            }
183
            $aToken = $options['access_token'];
184
185
            $rootObj = $service = null;
186 View Code Duplication
            if ($aToken) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
187
                try {
188
                    $client->setAccessToken($aToken);
189
                    if ($client->isAccessTokenExpired()) {
190
                        $aToken = array_merge($aToken, $client->fetchAccessTokenWithRefreshToken());
191
                        $client->setAccessToken($aToken);
192
                    }
193
                    $service = new \Google_Service_Drive($client);
194
                    $rootObj = $service->files->get('root');
195
196
                    $options['access_token'] = $aToken;
197
                    $this->session->set('GoogleDriveAuthParams', $options);
198
                } catch (Exception $e) {
199
                    $aToken = [];
200
                    $options['access_token'] = [];
201
                    if ($options['user'] !== 'init') {
202
                        $this->session->set('GoogleDriveAuthParams', $options);
203
204
                        return ['exit' => true, 'error' => elFinder::ERROR_REAUTH_REQUIRE];
205
                    }
206
                }
207
            }
208
209
            if ($options['user'] === 'init') {
210
                if (empty($options['url'])) {
211
                    $options['url'] = elFinder::getConnectorUrl();
212
                }
213
214
                $callback = $options['url']
215
                           .'?cmd=netmount&protocol=googledrive&host=1';
216
                $client->setRedirectUri($callback);
217
218
                if (! $aToken && empty($_GET['code'])) {
219
                    $client->setScopes([Google_Service_Drive::DRIVE]);
220
                    if (! empty($options['offline'])) {
221
                        $client->setApprovalPrompt('force');
222
                        $client->setAccessType('offline');
223
                    }
224
                    $url = $client->createAuthUrl();
225
226
                    $html = '<input id="elf-volumedriver-googledrive-host-btn" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" value="{msg:btnApprove}" type="button" onclick="window.open(\''.$url.'\')">';
227
                    $html .= '<script>
228
                        $("#'.$options['id'].'").elfinder("instance").trigger("netmount", {protocol: "googledrive", mode: "makebtn"});
229
                    </script>';
230 View Code Duplication
                    if (empty($options['pass']) && $options['host'] !== '1') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
231
                        $options['pass'] = 'return';
232
                        $this->session->set('GoogleDriveAuthParams', $options);
233
234
                        return ['exit' => true, 'body' => $html];
235
                    } else {
236
                        $out = [
237
                            'node' => $options['id'],
238
                            'json' => '{"protocol": "googledrive", "mode": "makebtn", "body" : "'.str_replace($html, '"', '\\"').'", "error" : "'.elFinder::ERROR_ACCESS_DENIED.'"}',
239
                            'bind' => 'netmount',
240
                        ];
241
242
                        return ['exit' => 'callback', 'out' => $out];
243
                    }
244
                } else {
245 View Code Duplication
                    if (! empty($_GET['code'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
246
                        $aToken = $client->fetchAccessTokenWithAuthCode($_GET['code']);
247
                        $options['access_token'] = $aToken;
248
                        $this->session->set('GoogleDriveTokens', $aToken)->set('GoogleDriveAuthParams', $options);
249
                        $out = [
250
                            'node' => $options['id'],
251
                            'json' => '{"protocol": "googledrive", "mode": "done", "reset": 1}',
252
                            'bind' => 'netmount',
253
                        ];
254
255
                        return ['exit' => 'callback', 'out' => $out];
256
                    }
257
                    $path = $options['path'];
258
                    if ($path === '/') {
259
                        $path = 'root';
260
                    }
261
                    $folders = [];
262 View Code Duplication
                    foreach ($service->files->listFiles([
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
263
                        'pageSize' => 1000,
264
                        'q' => sprintf('trashed = false and "%s" in parents and mimeType = "application/vnd.google-apps.folder"', $path),
265
                    ]) as $f) {
266
                        $folders[$f->getId()] = $f->getName();
267
                    }
268
                    natcasesort($folders);
269
270
                    if ($options['pass'] === 'folders') {
271
                        return ['exit' => true, 'folders' => $folders];
272
                    }
273
274
                    $folders = ['root' => $rootObj->getName()] + $folders;
275
                    $folders = json_encode($folders);
276
                    $expires = empty($aToken['refresh_token']) ? $aToken['created'] + $aToken['expires_in'] - 30 : 0;
277
                    $json = '{"protocol": "googledrive", "mode": "done", "folders": '.$folders.', "expires": '.$expires.'}';
278
                    $options['pass'] = 'return';
279
                    $html = 'Google.com';
280
                    $html .= '<script>
281
                        $("#'.$options['id'].'").elfinder("instance").trigger("netmount", '.$json.');
282
                    </script>';
283
                    $this->session->set('GoogleDriveAuthParams', $options);
284
285
                    return ['exit' => true, 'body' => $html];
286
                }
287
            }
288
        } catch (Exception $e) {
289
            $this->session->remove('GoogleDriveAuthParams')->remove('GoogleDriveTokens');
290 View Code Duplication
            if (empty($options['pass'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
291
                return ['exit' => true, 'body' => '{msg:'.elFinder::ERROR_ACCESS_DENIED.'}'.' '.$e->getMessage()];
292
            } else {
293
                return ['exit' => true, 'error' => [elFinder::ERROR_ACCESS_DENIED, $e->getMessage()]];
294
            }
295
        }
296
297
        if (! $aToken) {
298
            return ['exit' => true, 'error' => elFinder::ERROR_REAUTH_REQUIRE];
299
        }
300
301
        if ($options['path'] === '/') {
302
            $options['path'] = 'root';
303
        }
304
305
        try {
306
            $file = $service->files->get($options['path']);
307
            $options['alias'] = sprintf($this->options['gdAlias'], $file->getName());
308
        } catch (Google_Service_Exception $e) {
0 ignored issues
show
Bug introduced by
The class Google_Service_Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
309
            $err = json_decode($e->getMessage(), true);
310 View Code Duplication
            if (isset($err['error']) && $err['error']['code'] == 404) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
311
                return ['exit' => true, 'error' => [elFinder::ERROR_TRGDIR_NOT_FOUND, $options['path']]];
312
            } else {
313
                return ['exit' => true, 'error' => $e->getMessage()];
314
            }
315
        } catch (Exception $e) {
316
            return ['exit' => true, 'error' => $e->getMessage()];
317
        }
318
319 View Code Duplication
        foreach (['host', 'user', 'pass', 'id', 'offline'] as $key) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
320
            unset($options[$key]);
321
        }
322
323
        return $options;
324
    }
325
326
    /**
327
     * process of on netunmount
328
     * Drop `googledrive` & rm thumbs.
329
     *
330
     * @param array $options
0 ignored issues
show
Bug introduced by
There is no parameter named $options. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
331
     *
332
     * @return bool
333
     */
334
    public function netunmount($netVolumes, $key)
0 ignored issues
show
Unused Code introduced by
The parameter $netVolumes is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
335
    {
336
        if (! $this->options['useGoogleTmb']) {
337
            if ($tmbs = glob(rtrim($this->options['tmbPath'], '\\/').DIRECTORY_SEPARATOR.$this->netMountKey.'*.png')) {
338
                foreach ($tmbs as $file) {
339
                    unlink($file);
340
                }
341
            }
342
        }
343
        $this->session->remove($this->id.$this->netMountKey);
344
345
        return true;
346
    }
347
348
    /*********************************************************************/
349
    /*                               FS API                              */
350
    /*********************************************************************/
351
352
    /**
353
     * Close opened connection.
354
     *
355
     * @author Dmitry (dio) Levashov
356
     **/
357
    public function umount()
358
    {
359
    }
360
361
    /**
362
     * Return content URL (for netmout volume driver)
363
     * If file.url == 1 requests from JavaScript client with XHR.
364
     *
365
     * @param string $hash    file hash
366
     * @param array  $options options array
367
     *
368
     * @return bool|string
369
     *
370
     * @author Naoki Sawada
371
     */
372
    public function getContentUrl($hash, $options = [])
373
    {
374 View Code Duplication
        if (! empty($options['temporary'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
375
            // try make temporary file
376
            $url = parent::getContentUrl($hash, $options);
377
            if ($url) {
378
                return $url;
379
            }
380
        }
381
        if (($file = $this->file($hash)) == false || ! $file['url'] || $file['url'] == 1) {
382
            $path = $this->decode($hash);
383
384
            if ($this->_gd_publish($path)) {
385
                if ($raw = $this->_gd_getFile($path)) {
386
                    return $this->_gd_getLink($raw);
0 ignored issues
show
Bug introduced by
It seems like $raw defined by $this->_gd_getFile($path) on line 385 can also be of type array; however, elFinderVolumeGoogleDrive::_gd_getLink() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
387
                }
388
            }
389
        }
390
391
        return false;
392
    }
393
394
    /**
395
     * Return debug info for client.
396
     *
397
     * @return array
398
     **/
399
    public function debug()
400
    {
401
        $res = parent::debug();
402 View Code Duplication
        if (empty($this->options['refresh_token']) && $this->options['access_token'] && isset($this->options['access_token']['refresh_token'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
403
            $res['refresh_token'] = $this->options['access_token']['refresh_token'];
404
        }
405
406
        return $res;
407
    }
408
409
    /*********************************************************************/
410
    /*                        ORIGINAL FUNCTIONS                         */
411
    /*********************************************************************/
412
413
    /**
414
     * Get Parent ID, Item ID, Parent Path as an array from path.
415
     *
416
     * @param string $path
417
     *
418
     * @return array
419
     */
420
    protected function _gd_splitPath($path)
421
    {
422
        $path = trim($path, '/');
423
        $pid = '';
424
        if ($path === '') {
425
            $id = 'root';
426
            $parent = '';
427
        } else {
428
            $paths = explode('/', $path);
429
            $id = array_pop($paths);
430
            if ($paths) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $paths of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
431
                $parent = '/'.implode('/', $paths);
432
                $pid = array_pop($paths);
433
            } else {
434
                $rootid = ($this->root === '/') ? 'root' : trim($this->root, '/');
435
                if ($id === $rootid) {
436
                    $parent = '';
437
                } else {
438
                    $parent = $this->root;
439
                    $pid = $rootid;
440
                }
441
            }
442
        }
443
444
        return [$pid, $id, $parent];
445
    }
446
447
    /**
448
     * Parse line from googledrive metadata output and return file stat (array).
449
     *
450
     * @param string $raw line from ftp_rawlist() output
451
     *
452
     * @return array
453
     *
454
     * @author Dmitry Levashov
455
     **/
456
    protected function _gd_parseRaw($raw)
457
    {
458
        $stat = [];
459
460
        $stat['iid'] = isset($raw['id']) ? $raw['id'] : 'root';
461
        $stat['name'] = isset($raw['name']) ? $raw['name'] : '';
462
        if (isset($raw['modifiedTime'])) {
463
            $stat['ts'] = strtotime($raw['modifiedTime']);
464
        }
465
466
        if ($raw['mimeType'] === self::DIRMIME) {
467
            $stat['mime'] = 'directory';
468
            $stat['size'] = 0;
469
        } else {
470
            $stat['mime'] = $raw['mimeType'] == 'image/bmp' ? 'image/x-ms-bmp' : $raw['mimeType'];
471
            $stat['size'] = (int) $raw['size'];
472
            if ($size = $raw->getImageMediaMetadata()) {
0 ignored issues
show
Bug introduced by
The method getImageMediaMetadata cannot be called on $raw (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
473
                $stat['width'] = $size['width'];
474
                $stat['height'] = $size['height'];
475
            }
476
477
            $published = $this->_gd_isPublished($raw);
0 ignored issues
show
Documentation introduced by
$raw is of type string, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
478
479
            if ($this->options['useGoogleTmb']) {
480
                if (isset($raw['thumbnailLink'])) {
481
                    if ($published) {
482
                        $stat['tmb'] = 'drive.google.com/thumbnail?authuser=0&sz=s'.$this->options['tmbSize'].'&id='.$raw['id'];
483
                    } else {
484
                        $stat['tmb'] = substr($raw['thumbnailLink'], 8); // remove "https://"
485
                    }
486
                } else {
487
                    $stat['tmb'] = '';
488
                }
489
            }
490
491
            if ($published) {
492
                $stat['url'] = $this->_gd_getLink($raw);
0 ignored issues
show
Documentation introduced by
$raw is of type string, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
493
            } elseif (! $this->disabledGetUrl) {
494
                $stat['url'] = '1';
495
            }
496
        }
497
498
        return $stat;
499
    }
500
501
    /**
502
     * Make cache of $parents, $names and $directories.
503
     *
504
     * @param string $usecache
505
     */
506
    protected function _gd_getDirectoryData($usecache = true)
507
    {
508
        if ($usecache) {
509
            $cache = $this->session->get($this->id.$this->netMountKey, []);
510
            if ($cache) {
511
                $this->parents = $cache['parents'];
512
                $this->names = $cache['names'];
513
                $this->directories = $cache['directories'];
514
515
                return;
516
            }
517
        }
518
519
        $root = '';
520
        if ($this->root === '/') {
521
            // get root id
522
            if ($res = $this->_gd_getFile('/', 'id')) {
523
                $root = $res->getId();
524
            }
525
        }
526
527
        $data = [];
528
        $opts = [
529
                'fields' => 'files(id, name, parents)',
530
                'q' => sprintf('trashed=false and mimeType="%s"', self::DIRMIME),
531
        ];
532
        $res = $this->_gd_query($opts);
533
        foreach ($res as $raw) {
0 ignored issues
show
Bug introduced by
The expression $res of type boolean|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
534
            if ($parents = $raw->getParents()) {
535
                $id = $raw->getId();
536
                $this->parents[$id] = $parents;
537
                $this->names[$id] = $raw->getName();
538
                foreach ($parents as $p) {
539
                    if (isset($data[$p])) {
540
                        $data[$p][] = $id;
541
                    } else {
542
                        $data[$p] = [$id];
543
                    }
544
                }
545
            }
546
        }
547
        if ($root && isset($data[$root])) {
548
            $data['root'] = $data[$root];
549
        }
550
        $this->directories = $data;
551
        $this->session->set($this->id.$this->netMountKey, [
552
                'parents' => $this->parents,
553
                'names' => $this->names,
554
                'directories' => $this->directories,
555
        ]);
556
    }
557
558
    /**
559
     * Get descendants directories.
560
     *
561
     * @param string $itemId
562
     *
563
     * @return array
564
     */
565
    protected function _gd_getDirectories($itemId)
566
    {
567
        $ret = [];
568
        if ($this->directories === null) {
569
            $this->_gd_getDirectoryData();
570
        }
571
        $data = $this->directories;
572
        if (isset($data[$itemId])) {
573
            $ret = $data[$itemId];
574
            foreach ($data[$itemId] as $cid) {
575
                $ret = array_merge($ret, $this->_gd_getDirectories($cid));
576
            }
577
        }
578
579
        return $ret;
580
    }
581
582
    /**
583
     * Get ID based path from item ID.
584
     *
585
     * @param string $path
0 ignored issues
show
Bug introduced by
There is no parameter named $path. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
586
     */
587
    protected function _gd_getMountPaths($id)
588
    {
589
        $root = false;
590
        if ($this->directories === null) {
591
            $this->_gd_getDirectoryData();
592
        }
593
        list($pid) = explode('/', $id, 2);
594
        $path = $id;
595
        if ('/'.$pid === $this->root) {
596
            $root = true;
597
        } elseif (! isset($this->parents[$pid])) {
598
            $root = true;
599
            $path = ltrim(substr($path, strlen($pid)), '/');
600
        }
601
        $res = [];
602
        if ($root) {
603
            if ($this->root === '/' || strpos('/'.$path, $this->root) === 0) {
604
                $res = [(strpos($path, '/') === false) ? '/' : ('/'.$path)];
605
            }
606
        } else {
607
            foreach ($this->parents[$pid] as $p) {
608
                $_p = $p.'/'.$path;
609
                $res = array_merge($res, $this->_gd_getMountPaths($_p));
610
            }
611
        }
612
613
        return $res;
614
    }
615
616
    /**
617
     * Return is published.
618
     *
619
     * @param object $file
620
     *
621
     * @return bool
622
     */
623
    protected function _gd_isPublished($file)
624
    {
625
        $res = false;
626
        $pType = $this->options['publishPermission']['type'];
627
        $pRole = $this->options['publishPermission']['role'];
628
        if ($permissions = $file->getPermissions()) {
629
            foreach ($permissions as $permission) {
630
                if ($permission->type === $pType && $permission->role === $pRole) {
631
                    $res = true;
632
                    break;
633
                }
634
            }
635
        }
636
637
        return $res;
638
    }
639
640
    /**
641
     * return item URL link.
642
     *
643
     * @param object $file
644
     *
645
     * @return string
646
     */
647
    protected function _gd_getLink($file)
648
    {
649
        if ($url = $file->getWebContentLink()) {
650
            return str_replace('export=download', 'export=media', $url);
651
        }
652
        if ($url = $file->getWebViewLink()) {
653
            return $url;
654
        }
655
656
        return '';
657
    }
658
659
    /**
660
     * Get download url.
661
     *
662
     * @param Google_Service_Drive_DriveFile $file
663
     *
664
     * @return string|false
665
     */
666
    protected function _gd_getDownloadUrl($file)
667
    {
668
        if (strpos($file->mimeType, 'application/vnd.google-apps') !== 0) {
669
            return 'https://www.googleapis.com/drive/v3/files/'.$file->getId().'?alt=media';
670
        } else {
671
            $mimeMap = $this->options['appsExportMap'];
672
            if (isset($mimeMap[$file->getMimeType()])) {
673
                $mime = $mimeMap[$file->getMimeType()];
674
            } else {
675
                $mime = $mimeMap['default'];
676
            }
677
            $mime = rawurlencode($mime);
678
679
            return 'https://www.googleapis.com/drive/v3/files/'.$file->getId().'/export?mimeType='.$mime;
680
        }
681
682
        return false;
0 ignored issues
show
Unused Code introduced by
return false; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
683
    }
684
685
    /**
686
     * Get thumbnail from GoogleDrive.com.
687
     *
688
     * @param string $path
689
     * @param string $size
0 ignored issues
show
Bug introduced by
There is no parameter named $size. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
690
     *
691
     * @return string | boolean
692
     */
693
    protected function _gd_getThumbnail($path)
694
    {
695
        list(, $itemId) = $this->_gd_splitPath($path);
696
697
        try {
698
            $contents = $this->service->files->get($itemId, [
699
                    'alt' => 'media',
700
            ]);
701
            $contents = $contents->getBody()->detach();
702
            rewind($contents);
703
704
            return $contents;
705
        } catch (Exception $e) {
706
            return false;
707
        }
708
    }
709
710
    /**
711
     * Publish permissions specified path item.
712
     *
713
     * @param string $path
714
     *
715
     * @return bool
716
     */
717
    protected function _gd_publish($path)
718
    {
719
        if ($file = $this->_gd_getFile($path)) {
720
            if ($this->_gd_isPublished($file)) {
0 ignored issues
show
Bug introduced by
It seems like $file defined by $this->_gd_getFile($path) on line 719 can also be of type array; however, elFinderVolumeGoogleDrive::_gd_isPublished() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
721
                return true;
722
            }
723
            try {
724
                if ($this->service->permissions->create($file->getId(), new \Google_Service_Drive_Permission($this->options['publishPermission']))) {
725
                    return true;
726
                }
727
            } catch (Exception $e) {
728
                return false;
729
            }
730
        }
731
732
        return false;
733
    }
734
735
    /**
736
     * unPublish permissions specified path.
737
     *
738
     * @param string $path
739
     *
740
     * @return bool
741
     */
742
    protected function _gd_unPublish($path)
743
    {
744
        if ($file = $this->_gd_getFile($path)) {
745
            if (! $this->_gd_isPublished($file)) {
0 ignored issues
show
Bug introduced by
It seems like $file defined by $this->_gd_getFile($path) on line 744 can also be of type array; however, elFinderVolumeGoogleDrive::_gd_isPublished() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
746
                return true;
747
            }
748
            $permissions = $file->getPermissions();
749
            $pType = $this->options['publishPermission']['type'];
750
            $pRole = $this->options['publishPermission']['role'];
751
            try {
752
                foreach ($permissions as $permission) {
753
                    if ($permission->type === $pType && $permission->role === $pRole) {
754
                        $this->service->permissions->delete($file->getId(), $permission->getId());
755
756
                        return true;
757
                        break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
758
                    }
759
                }
760
            } catch (Exception $e) {
761
                return false;
762
            }
763
        }
764
765
        return false;
766
    }
767
768
    /**
769
     * Read file chunk.
770
     *
771
     * @param resource $handle
772
     * @param int      $chunkSize
773
     *
774
     * @return string
775
     */
776
    protected function _gd_readFileChunk($handle, $chunkSize)
777
    {
778
        $byteCount = 0;
779
        $giantChunk = '';
780
        while (! feof($handle)) {
781
            // fread will never return more than 8192 bytes if the stream is read buffered and it does not represent a plain file
782
            $chunk = fread($handle, 8192);
783
            $byteCount += strlen($chunk);
784
            $giantChunk .= $chunk;
785
            if ($byteCount >= $chunkSize) {
786
                return $giantChunk;
787
            }
788
        }
789
790
        return $giantChunk;
791
    }
792
793
    /**
794
     * Return fileinfo based on filename
795
     * For item ID based path file system
796
     * Please override if needed on each drivers.
797
     *
798
     * @param string $path file cache
799
     *
800
     * @return array
801
     */
802
    protected function isNameExists($path)
803
    {
804
        list($parentId, $name) = $this->_gd_splitPath($path);
805
        $opts = [
806
                'q' => sprintf('trashed=false and "%s" in parents and name="%s"', $parentId, $name),
807
                'fields' => self::FETCHFIELDS_LIST,
808
            ];
809
        $srcFile = $this->_gd_query($opts);
810
811
        return empty($srcFile) ? false : $this->_gd_parseRaw($srcFile[0]);
812
    }
813
814
    /*********************************************************************/
815
    /*                        INIT AND CONFIGURE                         */
816
    /*********************************************************************/
817
818
    /**
819
     * Prepare FTP connection
820
     * Connect to remote server and check if credentials are correct, if so, store the connection id in $ftp_conn.
821
     *
822
     * @return bool
823
     *
824
     * @author Dmitry (dio) Levashov
825
     * @author Cem (DiscoFever)
826
     **/
827
    protected function init()
828
    {
829
        $serviceAccountConfig = '';
830
        if (empty($this->options['serviceAccountConfigFile'])) {
831
            if (empty($options['client_id'])) {
0 ignored issues
show
Bug introduced by
The variable $options seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
832
                if (defined('ELFINDER_GOOGLEDRIVE_CLIENTID') && ELFINDER_GOOGLEDRIVE_CLIENTID) {
833
                    $this->options['client_id'] = ELFINDER_GOOGLEDRIVE_CLIENTID;
834
                } else {
835
                    return $this->setError('Required option "client_id" is undefined.');
836
                }
837
            }
838
            if (empty($options['client_secret'])) {
839
                if (defined('ELFINDER_GOOGLEDRIVE_CLIENTSECRET') && ELFINDER_GOOGLEDRIVE_CLIENTSECRET) {
840
                    $this->options['client_secret'] = ELFINDER_GOOGLEDRIVE_CLIENTSECRET;
841
                } else {
842
                    return $this->setError('Required option "client_secret" is undefined.');
843
                }
844
            }
845
            if (! $this->options['access_token'] && ! $this->options['refresh_token']) {
846
                return $this->setError('Required option "access_token" or "refresh_token" is undefined.');
847
            }
848
        } else {
849
            if (! is_readable($this->options['serviceAccountConfigFile'])) {
850
                return $this->setError('Option "serviceAccountConfigFile" file is not readable.');
851
            }
852
            $serviceAccountConfig = $this->options['serviceAccountConfigFile'];
853
        }
854
855
        try {
856
            if (! $serviceAccountConfig) {
857
                $aTokenFile = '';
858
                if ($this->options['refresh_token']) {
859
                    // permanent mount
860
                    $aToken = $this->options['refresh_token'];
861
                    $this->options['access_token'] = '';
862
                    $tmp = elFinder::getStaticVar('commonTempPath');
863
                    if (! $tmp) {
864
                        $tmp = $this->getTempPath();
865
                    }
866
                    if ($tmp) {
867
                        $aTokenFile = $tmp.DIRECTORY_SEPARATOR.md5($this->options['client_id'].$this->options['refresh_token']).'.gtoken';
868
                        if (is_file($aTokenFile)) {
869
                            $this->options['access_token'] = json_decode(file_get_contents($aTokenFile), true);
870
                        }
871
                    }
872
                } else {
873
                    // make net mount key for network mount
874
                    if (is_array($this->options['access_token'])) {
875
                        $aToken = ! empty($this->options['access_token']['refresh_token'])
876
                            ? $this->options['access_token']['refresh_token']
877
                            : $this->options['access_token']['access_token'];
878
                    } else {
879
                        return $this->setError('Required option "access_token" is not Array or empty.');
880
                    }
881
                }
882
            }
883
884
            $errors = [];
885
            if (! $this->service) {
886
                if ($this->options['googleApiClient'] && ! class_exists('Google_Client')) {
887
                    include_once $this->options['googleApiClient'];
888
                }
889
                if (! class_exists('Google_Client')) {
890
                    return $this->setError('Class Google_Client not found.');
891
                }
892
893
                $this->client = new \Google_Client();
894
895
                $client = $this->client;
896
897
                if (! $serviceAccountConfig) {
898
                    if ($this->options['access_token']) {
899
                        $client->setAccessToken($this->options['access_token']);
900
                    }
901
                    if ($client->isAccessTokenExpired()) {
902
                        $client->setClientId($this->options['client_id']);
903
                        $client->setClientSecret($this->options['client_secret']);
904
                        $access_token = $client->fetchAccessTokenWithRefreshToken($this->options['refresh_token'] ?: null);
905
                        $client->setAccessToken($access_token);
906
                        if ($aTokenFile) {
907
                            file_put_contents($aTokenFile, json_encode($access_token));
0 ignored issues
show
Bug introduced by
The variable $aTokenFile does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
908
                        } else {
909
                            $access_token['refresh_token'] = $this->options['access_token']['refresh_token'];
910
                        }
911 View Code Duplication
                        if (! empty($this->options['netkey'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
912
                            elFinder::$instance->updateNetVolumeOption($this->options['netkey'], 'access_token', $access_token);
913
                        }
914
                        $this->options['access_token'] = $access_token;
915
                    }
916
                } else {
917
                    $client->setAuthConfigFile($serviceAccountConfig);
918
                    $client->setScopes([Google_Service_Drive::DRIVE]);
919
                    $aToken = $client->getClientId();
920
                }
921
                $this->service = new \Google_Service_Drive($client);
922
            }
923
924
            $this->netMountKey = md5($aToken.'-'.$this->options['path']);
0 ignored issues
show
Bug introduced by
The variable $aToken does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
925
        } catch (InvalidArgumentException $e) {
926
            $errors[] = $e->getMessage();
0 ignored issues
show
Bug introduced by
The variable $errors does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
927
        } catch (Google_Service_Exception $e) {
0 ignored issues
show
Bug introduced by
The class Google_Service_Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
928
            $errors[] = $e->getMessage();
929
        }
930
931
        if (! $this->service) {
932
            $this->session->remove($this->id.$this->netMountKey);
933
            if ($aTokenFile) {
934
                unlink($aTokenFile);
935
            }
936
            $errors[] = 'Google Drive Service could not be loaded.';
937
938
            return $this->setError($errors);
939
        }
940
941
        // normalize root path
942
        if ($this->options['path'] == 'root') {
943
            $this->options['path'] = '/';
944
        }
945
        $this->root = $this->options['path'] = $this->_normpath($this->options['path']);
946
947
        $this->options['root'] == '' ? $this->options['root'] = $this->_gd_getNameByPath('root') : $this->options['root'];
948
949 View Code Duplication
        if (empty($this->options['alias'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
950
            $this->options['alias'] = ($this->options['path'] === '/') ? $this->options['root'] : sprintf($this->options['gdAlias'], $this->_gd_getNameByPath($this->options['path']));
951
        }
952
953
        $this->rootName = $this->options['alias'];
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->options['alias'] can also be of type array. However, the property $rootName is declared as type string. 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...
954
955
        if (! empty($this->options['tmpPath'])) {
956
            if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'])) && is_writable($this->options['tmpPath'])) {
957
                $this->tmp = $this->options['tmpPath'];
958
            }
959
        }
960
961
        if (! $this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
962
            $this->tmp = $tmp;
963
        }
964
965
        // This driver dose not support `syncChkAsTs`
966
        $this->options['syncChkAsTs'] = false;
967
968
        // 'lsPlSleep' minmum 10 sec
969
        $this->options['lsPlSleep'] = max(10, $this->options['lsPlSleep']);
970
971
        if ($this->options['useGoogleTmb']) {
972
            $this->options['tmbURL'] = 'https://';
973
        }
974
975
        return true;
976
    }
977
978
    /**
979
     * Configure after successfull mount.
980
     *
981
     * @author Dmitry (dio) Levashov
982
     **/
983 View Code Duplication
    protected function configure()
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...
984
    {
985
        parent::configure();
986
987
        // fallback of $this->tmp
988
        if (! $this->tmp && $this->tmbPathWritable) {
989
            $this->tmp = $this->tmbPath;
990
        }
991
992
        $this->disabled[] = 'archive';
993
        $this->disabled[] = 'extract';
994
995
        if ($this->isMyReload()) {
996
            $this->_gd_getDirectoryData(false);
997
        }
998
    }
999
1000
    /**
1001
     * Cache dir contents.
1002
     *
1003
     * @param string $path dir path
1004
     *
1005
     * @author Dmitry Levashov
1006
     **/
1007
    protected function cacheDir($path)
1008
    {
1009
        $this->dirsCache[$path] = [];
1010
        $hasDir = false;
1011
1012
        list(, $pid) = $this->_gd_splitPath($path);
1013
1014
        $opts = [
1015
            'fields' => self::FETCHFIELDS_LIST,
1016
            'q' => sprintf('trashed=false and "%s" in parents', $pid),
1017
        ];
1018
1019
        $res = $this->_gd_query($opts);
1020
1021
        $mountPath = $this->_normpath($path.'/');
1022
1023
        if ($res) {
1024
            foreach ($res as $raw) {
0 ignored issues
show
Bug introduced by
The expression $res of type boolean|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1025
                if ($stat = $this->_gd_parseRaw($raw)) {
1026
                    $stat = $this->updateCache($mountPath.$raw->id, $stat);
1027
                    if (empty($stat['hidden']) && $path !== $mountPath.$raw->id) {
1028
                        if (! $hasDir && $stat['mime'] === 'directory') {
1029
                            $hasDir = true;
1030
                        }
1031
                        $this->dirsCache[$path][] = $mountPath.$raw->id;
1032
                    }
1033
                }
1034
            }
1035
        }
1036
1037
        if (isset($this->sessionCache['subdirs'])) {
1038
            $this->sessionCache['subdirs'][$path] = $hasDir;
1039
        }
1040
1041
        return $this->dirsCache[$path];
1042
    }
1043
1044
    /**
1045
     * Recursive files search.
1046
     *
1047
     * @param string $path  dir path
1048
     * @param string $q     search string
1049
     * @param array  $mimes
1050
     *
1051
     * @return array
1052
     *
1053
     * @author Naoki Sawada
1054
     **/
1055
    protected function doSearch($path, $q, $mimes)
1056
    {
1057
        list(, $itemId) = $this->_gd_splitPath($path);
1058
1059
        $path = $this->_normpath($path.'/');
1060
        $result = [];
1061
        $query = '';
1062
1063
        if ($itemId !== 'root') {
1064
            $dirs = array_merge([$itemId], $this->_gd_getDirectories($itemId));
1065
            $query = '(\''.implode('\' in parents or \'', $dirs).'\' in parents)';
1066
        }
1067
1068
        $tmp = [];
1069
        if (! $mimes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $mimes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1070
            foreach (explode(' ', $q) as $_v) {
1071
                $tmp[] = 'fullText contains \''.str_replace('\'', '\\\'', $_v).'\'';
1072
            }
1073
            $query .= ($query ? ' and ' : '').implode(' and ', $tmp);
1074
        } else {
1075
            foreach ($mimes as $_v) {
1076
                $tmp[] = 'mimeType contains \''.str_replace('\'', '\\\'', $_v).'\'';
1077
            }
1078
            $query .= ($query ? ' and ' : '').'('.implode(' or ', $tmp).')';
1079
        }
1080
1081
        $opts = [
1082
            'q' => sprintf('trashed=false and (%s)', $query),
1083
        ];
1084
1085
        $res = $this->_gd_query($opts);
1086
1087
        $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
1088
        foreach ($res as $raw) {
0 ignored issues
show
Bug introduced by
The expression $res of type boolean|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1089
            if ($timeout && $timeout < time()) {
1090
                $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->_path($path));
1091
                break;
1092
            }
1093
            if ($stat = $this->_gd_parseRaw($raw)) {
1094
                if ($parents = $raw->getParents()) {
1095
                    foreach ($parents as $parent) {
1096
                        $paths = $this->_gd_getMountPaths($parent);
1097
                        foreach ($paths as $path) {
1098
                            $path = ($path === '') ? '/' : (rtrim($path, '/').'/');
1099
                            if (! isset($this->cache[$path.$raw->id])) {
1100
                                $stat = $this->updateCache($path.$raw->id, $stat);
1101
                            } else {
1102
                                $stat = $this->cache[$path.$raw->id];
1103
                            }
1104
                            if (empty($stat['hidden'])) {
1105
                                $stat['path'] = $this->_path($path).$stat['name'];
1106
                                $result[] = $stat;
1107
                            }
1108
                        }
1109
                    }
1110
                }
1111
            }
1112
        }
1113
1114
        return $result;
1115
    }
1116
1117
    /**
1118
     * Copy file/recursive copy dir only in current volume.
1119
     * Return new file path or false.
1120
     *
1121
     * @param string $src  source path
1122
     * @param string $dst  destination dir path
1123
     * @param string $name new file name (optionaly)
1124
     *
1125
     * @return string|false
1126
     *
1127
     * @author Dmitry (dio) Levashov
1128
     * @author Naoki Sawada
1129
     **/
1130
    protected function copy($src, $dst, $name)
1131
    {
1132
        $this->clearcache();
1133
        $res = $this->_gd_getFile($src);
1134
        if ($res['mimeType'] == self::DIRMIME) {
1135
            $newDir = $this->_mkdir($dst, $name);
1136
            if ($newDir) {
1137
                list(, $itemId) = $this->_gd_splitPath($newDir);
0 ignored issues
show
Bug introduced by
It seems like $newDir can also be of type boolean; however, elFinderVolumeGoogleDrive::_gd_splitPath() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1138
                list(, $srcId) = $this->_gd_splitPath($src);
1139
                $path = $this->_joinPath($dst, $itemId);
1140
                $opts = [
1141
                    'q' => sprintf('trashed=false and "%s" in parents', $srcId),
1142
                ];
1143
1144
                $res = $this->_gd_query($opts);
1145
                foreach ($res as $raw) {
0 ignored issues
show
Bug introduced by
The expression $res of type boolean|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1146
                    $raw['mimeType'] == self::DIRMIME ? $this->copy($src.'/'.$raw['id'], $path, $raw['name']) : $this->_copy($src.'/'.$raw['id'], $path, $raw['name']);
1147
                }
1148
1149
                return $this->_joinPath($dst, $itemId);
1150
            } else {
1151
                $this->setError(elFinder::ERROR_COPY, $this->_path($src));
1152
            }
1153 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1154
            $itemId = $this->_copy($src, $dst, $name);
1155
1156
            return $itemId
1157
            ? $this->_joinPath($dst, $itemId)
0 ignored issues
show
Documentation introduced by
$itemId is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1158
            : $this->setError(elFinder::ERROR_COPY, $this->_path($src));
1159
        }
1160
    }
1161
1162
    /**
1163
     * Remove file/ recursive remove dir.
1164
     *
1165
     * @param string $path  file path
1166
     * @param bool   $force try to remove even if file locked
1167
     *
1168
     * @return bool
1169
     *
1170
     * @author Dmitry (dio) Levashov
1171
     * @author Naoki Sawada
1172
     **/
1173 View Code Duplication
    protected function remove($path, $force = false, $recursive = false)
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...
1174
    {
1175
        $stat = $this->stat($path);
1176
        $stat['realpath'] = $path;
1177
        $this->rmTmb($stat);
0 ignored issues
show
Documentation introduced by
$stat is of type array<string,string,{"realpath":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1178
        $this->clearcache();
1179
1180
        if (empty($stat)) {
1181
            return $this->setError(elFinder::ERROR_RM, $this->_path($path), elFinder::ERROR_FILE_NOT_FOUND);
1182
        }
1183
1184
        if (! $force && ! empty($stat['locked'])) {
1185
            return $this->setError(elFinder::ERROR_LOCKED, $this->_path($path));
1186
        }
1187
1188
        if ($stat['mime'] == 'directory') {
1189
            if (! $recursive && ! $this->_rmdir($path)) {
1190
                return $this->setError(elFinder::ERROR_RM, $this->_path($path));
1191
            }
1192
        } else {
1193
            if (! $recursive && ! $this->_unlink($path)) {
1194
                return $this->setError(elFinder::ERROR_RM, $this->_path($path));
1195
            }
1196
        }
1197
1198
        $this->removed[] = $stat;
1199
1200
        return true;
1201
    }
1202
1203
    /**
1204
     * Create thumnbnail and return it's URL on success.
1205
     *
1206
     * @param string $path file path
1207
     * @param string $mime file mime type
0 ignored issues
show
Bug introduced by
There is no parameter named $mime. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1208
     *
1209
     * @return string|false
1210
     *
1211
     * @author Dmitry (dio) Levashov
1212
     * @author Naoki Sawada
1213
     **/
1214 View Code Duplication
    protected function createTmb($path, $stat)
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...
1215
    {
1216
        if (! $stat || ! $this->canCreateTmb($path, $stat)) {
1217
            return false;
1218
        }
1219
1220
        $name = $this->tmbname($stat);
1221
        $tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$name;
1222
1223
        // copy image into tmbPath so some drivers does not store files on local fs
1224
        if (! $data = $this->_gd_getThumbnail($path)) {
1225
            return false;
1226
        }
1227
        if (! file_put_contents($tmb, $data)) {
1228
            return false;
1229
        }
1230
1231
        $result = false;
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1232
1233
        $tmbSize = $this->tmbSize;
1234
1235
        if (($s = getimagesize($tmb)) == false) {
1236
            return false;
1237
        }
1238
1239
        /* If image smaller or equal thumbnail size - just fitting to thumbnail square */
1240
        if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) {
1241
            $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
1242
        } else {
1243
            if ($this->options['tmbCrop']) {
1244
1245
                /* Resize and crop if image bigger than thumbnail */
1246
                if (! (($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize)) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) {
1247
                    $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1248
                }
1249
1250
                if (($s = getimagesize($tmb)) != false) {
1251
                    $x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize) / 2) : 0;
1252
                    $y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize) / 2) : 0;
1253
                    $result = $this->imgCrop($tmb, $tmbSize, $tmbSize, $x, $y, 'png');
0 ignored issues
show
Documentation introduced by
$x is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$y is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1254
                }
1255
            } else {
1256
                $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1257
            }
1258
1259
            $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
1260
        }
1261
1262
        if (! $result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1263
            unlink($tmb);
1264
1265
            return false;
1266
        }
1267
1268
        return $name;
1269
    }
1270
1271
    /**
1272
     * Return thumbnail file name for required file.
1273
     *
1274
     * @param array $stat file stat
1275
     *
1276
     * @return string
1277
     *
1278
     * @author Dmitry (dio) Levashov
1279
     **/
1280
    protected function tmbname($stat)
1281
    {
1282
        return $this->netMountKey.$stat['iid'].$stat['ts'].'.png';
1283
    }
1284
1285
    /*********************** paths/urls *************************/
1286
1287
    /**
1288
     * Return parent directory path.
1289
     *
1290
     * @param string $path file path
1291
     *
1292
     * @return string
1293
     *
1294
     * @author Dmitry (dio) Levashov
1295
     **/
1296
    protected function _dirname($path)
1297
    {
1298
        list(, , $parent) = $this->_gd_splitPath($path);
1299
1300
        return $this->_normpath($parent);
1301
    }
1302
1303
    /**
1304
     * Return file name.
1305
     *
1306
     * @param string $path file path
1307
     *
1308
     * @return string
1309
     *
1310
     * @author Dmitry (dio) Levashov
1311
     **/
1312
    protected function _basename($path)
1313
    {
1314
        list(, $basename) = $this->_gd_splitPath($path);
1315
1316
        return $basename;
1317
    }
1318
1319
    /**
1320
     * Join dir name and file name and retur full path.
1321
     *
1322
     * @param string $dir
1323
     * @param string $name
1324
     *
1325
     * @return string
1326
     *
1327
     * @author Dmitry (dio) Levashov
1328
     **/
1329
    protected function _joinPath($dir, $name)
1330
    {
1331
        return $this->_normpath($dir.'/'.$name);
1332
    }
1333
1334
    /**
1335
     * Return normalized path, this works the same as os.path.normpath() in Python.
1336
     *
1337
     * @param string $path path
1338
     *
1339
     * @return string
1340
     *
1341
     * @author Troex Nevelin
1342
     **/
1343 View Code Duplication
    protected function _normpath($path)
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...
1344
    {
1345
        if (DIRECTORY_SEPARATOR !== '/') {
1346
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
1347
        }
1348
        $path = '/'.ltrim($path, '/');
1349
1350
        return $path;
1351
    }
1352
1353
    /**
1354
     * Return file path related to root dir.
1355
     *
1356
     * @param string $path file path
1357
     *
1358
     * @return string
1359
     *
1360
     * @author Dmitry (dio) Levashov
1361
     **/
1362
    protected function _relpath($path)
1363
    {
1364
        return $path;
1365
    }
1366
1367
    /**
1368
     * Convert path related to root dir into real path.
1369
     *
1370
     * @param string $path file path
1371
     *
1372
     * @return string
1373
     *
1374
     * @author Dmitry (dio) Levashov
1375
     **/
1376
    protected function _abspath($path)
1377
    {
1378
        return $path;
1379
    }
1380
1381
    /**
1382
     * Return fake path started from root dir.
1383
     *
1384
     * @param string $path file path
1385
     *
1386
     * @return string
1387
     *
1388
     * @author Dmitry (dio) Levashov
1389
     **/
1390
    protected function _path($path)
1391
    {
1392
        if (! $this->names) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->names of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1393
            $this->_gd_getDirectoryData();
1394
        }
1395
        $path = $this->_normpath(substr($path, strlen($this->root)));
1396
        $names = [];
1397
        $paths = explode('/', $path);
1398
        foreach ($paths as $_p) {
1399
            $names[] = isset($this->names[$_p]) ? $this->names[$_p] : $_p;
1400
        }
1401
1402
        return $this->rootName.implode('/', $names);
1403
    }
1404
1405
    /**
1406
     * Return true if $path is children of $parent.
1407
     *
1408
     * @param string $path   path to check
1409
     * @param string $parent parent path
1410
     *
1411
     * @return bool
1412
     *
1413
     * @author Dmitry (dio) Levashov
1414
     **/
1415
    protected function _inpath($path, $parent)
1416
    {
1417
        return $path == $parent || strpos($path, $parent.'/') === 0;
1418
    }
1419
1420
    /***************** file stat ********************/
1421
1422
    /**
1423
     * Return stat for given path.
1424
     * Stat contains following fields:
1425
     * - (int)    size    file size in b. required
1426
     * - (int)    ts      file modification time in unix time. required
1427
     * - (string) mime    mimetype. required for folders, others - optionally
1428
     * - (bool)   read    read permissions. required
1429
     * - (bool)   write   write permissions. required
1430
     * - (bool)   locked  is object locked. optionally
1431
     * - (bool)   hidden  is object hidden. optionally
1432
     * - (string) alias   for symlinks - link target path relative to root path. optionally
1433
     * - (string) target  for symlinks - link target path. optionally.
1434
     *
1435
     * If file does not exists - returns empty array or false.
1436
     *
1437
     * @param string $path file path
1438
     *
1439
     * @return array|false
1440
     *
1441
     * @author Dmitry (dio) Levashov
1442
     **/
1443
    protected function _stat($path)
1444
    {
1445
        if ($raw = $this->_gd_getFile($path)) {
1446
            return $this->_gd_parseRaw($raw);
0 ignored issues
show
Bug introduced by
It seems like $raw defined by $this->_gd_getFile($path) on line 1445 can also be of type array; however, elFinderVolumeGoogleDrive::_gd_parseRaw() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1447
        }
1448
1449
        return false;
1450
    }
1451
1452
    /**
1453
     * Return true if path is dir and has at least one childs directory.
1454
     *
1455
     * @param string $path dir path
1456
     *
1457
     * @return bool
1458
     *
1459
     * @author Dmitry (dio) Levashov
1460
     **/
1461
    protected function _subdirs($path)
1462
    {
1463
        if ($this->directories === null) {
1464
            $this->_gd_getDirectoryData();
1465
        }
1466
        list(, $itemId) = $this->_gd_splitPath($path);
1467
1468
        return isset($this->directories[$itemId]);
1469
    }
1470
1471
    /**
1472
     * Return object width and height
1473
     * Ususaly used for images, but can be realize for video etc...
1474
     *
1475
     * @param string $path file path
1476
     * @param string $mime file mime type
1477
     *
1478
     * @return string
1479
     *
1480
     * @author Dmitry (dio) Levashov
1481
     **/
1482
    protected function _dimensions($path, $mime)
1483
    {
1484
        if (strpos($mime, 'image') !== 0) {
1485
            return '';
1486
        }
1487
        $ret = '';
1488
1489 View Code Duplication
        if ($file = $this->_gd_getFile($path)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1490
            if (isset($file['imageMediaMetadata'])) {
1491
                return $file['imageMediaMetadata']['width'].'x'.$file['imageMediaMetadata']['height'];
1492
            }
1493
        }
1494
1495
        return $ret;
1496
    }
1497
1498
    /******************** file/dir content *********************/
1499
1500
    /**
1501
     * Return files list in directory.
1502
     *
1503
     * @param string $path dir path
1504
     *
1505
     * @return array
1506
     *
1507
     * @author Dmitry (dio) Levashov
1508
     * @author Cem (DiscoFever)
1509
     **/
1510
    protected function _scandir($path)
1511
    {
1512
        return isset($this->dirsCache[$path])
1513
            ? $this->dirsCache[$path]
1514
            : $this->cacheDir($path);
1515
    }
1516
1517
    /**
1518
     * Open file and return file pointer.
1519
     *
1520
     * @param string $path  file path
1521
     * @param bool   $write open file for writing
0 ignored issues
show
Bug introduced by
There is no parameter named $write. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1522
     *
1523
     * @return resource|false
1524
     *
1525
     * @author Dmitry (dio) Levashov
1526
     **/
1527
    protected function _fopen($path, $mode = 'rb')
1528
    {
1529
        if ($mode === 'rb' || $mode === 'r') {
1530
            if ($file = $this->_gd_getFile($path)) {
1531
                if ($dlurl = $this->_gd_getDownloadUrl($file)) {
0 ignored issues
show
Bug introduced by
It seems like $file defined by $this->_gd_getFile($path) on line 1530 can also be of type array; however, elFinderVolumeGoogleDrive::_gd_getDownloadUrl() does only seem to accept object<Google_Service_Drive_DriveFile>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1532
                    $token = $this->client->getAccessToken();
1533
                    if (! $token && $this->client->isUsingApplicationDefaultCredentials()) {
1534
                        $this->client->fetchAccessTokenWithAssertion();
1535
                        $token = $this->client->getAccessToken();
1536
                    }
1537
                    $access_token = '';
1538
                    if (is_array($token)) {
1539
                        $access_token = $token['access_token'];
1540
                    } else {
1541
                        if ($token = json_decode($this->client->getAccessToken())) {
1542
                            $access_token = $token->access_token;
1543
                        }
1544
                    }
1545
                    if ($access_token) {
1546
                        $data = [
1547
                                'target' => $dlurl,
1548
                                'headers' => ['Authorization: Bearer '.$access_token],
1549
                        ];
1550
1551
                        return elFinder::getStreamByUrl($data);
1552
                    }
1553
                }
1554
            }
1555
        }
1556
1557
        return false;
1558
    }
1559
1560
    /**
1561
     * Close opened file.
1562
     *
1563
     * @param resource $fp file pointer
1564
     *
1565
     * @return bool
1566
     *
1567
     * @author Dmitry (dio) Levashov
1568
     **/
1569
    protected function _fclose($fp, $path = '')
1570
    {
1571
        fclose($fp);
1572
        if ($path) {
1573
            unlink($this->getTempFile($path));
1574
        }
1575
    }
1576
1577
    /********************  file/dir manipulations *************************/
1578
1579
    /**
1580
     * Create dir and return created dir path or false on failed.
1581
     *
1582
     * @param string $path parent dir path
1583
     * @param string $name new directory name
1584
     *
1585
     * @return string|bool
1586
     *
1587
     * @author Dmitry (dio) Levashov
1588
     **/
1589
    protected function _mkdir($path, $name)
1590
    {
1591
        $path = $this->_joinPath($path, $name);
1592
        list($parentId, , $parent) = $this->_gd_splitPath($path);
1593
1594
        try {
1595
            $file = new \Google_Service_Drive_DriveFile();
1596
1597
            $file->setName($name);
1598
            $file->setMimeType(self::DIRMIME);
1599
            $file->setParents([$parentId]);
1600
1601
            //create the Folder in the Parent
1602
            $obj = $this->service->files->create($file);
1603
1604
            if ($obj instanceof Google_Service_Drive_DriveFile) {
0 ignored issues
show
Bug introduced by
The class Google_Service_Drive_DriveFile does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1605
                $path = $this->_joinPath($parent, $obj['id']);
1606
                $this->_gd_getDirectoryData(false);
1607
1608
                return $path;
1609
            } else {
1610
                return false;
1611
            }
1612
        } catch (Exception $e) {
1613
            return $this->setError('GoogleDrive error: '.$e->getMessage());
1614
        }
1615
    }
1616
1617
    /**
1618
     * Create file and return it's path or false on failed.
1619
     *
1620
     * @param string $path parent dir path
1621
     * @param string $name new file name
1622
     *
1623
     * @return string|bool
1624
     *
1625
     * @author Dmitry (dio) Levashov
1626
     **/
1627
    protected function _mkfile($path, $name)
1628
    {
1629
        return $this->_save(tmpfile(), $path, $name, []);
1630
    }
1631
1632
    /**
1633
     * Create symlink. FTP driver does not support symlinks.
1634
     *
1635
     * @param string $target link target
1636
     * @param string $path   symlink path
1637
     *
1638
     * @return bool
1639
     *
1640
     * @author Dmitry (dio) Levashov
1641
     **/
1642
    protected function _symlink($target, $path, $name)
1643
    {
1644
        return false;
1645
    }
1646
1647
    /**
1648
     * Copy file into another file.
1649
     *
1650
     * @param string $source    source file path
1651
     * @param string $targetDir target directory path
1652
     * @param string $name      new file name
1653
     *
1654
     * @return bool
1655
     *
1656
     * @author Dmitry (dio) Levashov
1657
     **/
1658
    protected function _copy($source, $targetDir, $name)
1659
    {
1660
        $source = $this->_normpath($source);
1661
        $targetDir = $this->_normpath($targetDir);
1662
1663
        try {
1664
            $file = new \Google_Service_Drive_DriveFile();
1665
            $file->setName($name);
1666
1667
            //Set the Parent id
1668
            list(, $parentId) = $this->_gd_splitPath($targetDir);
1669
            $file->setParents([$parentId]);
1670
1671
            list(, $srcId) = $this->_gd_splitPath($source);
1672
            $file = $this->service->files->copy($srcId, $file, ['fields' => self::FETCHFIELDS_GET]);
1673
            $itemId = $file->id;
1674
1675
            return $itemId;
1676
        } catch (Exception $e) {
1677
            return $this->setError('GoogleDrive error: '.$e->getMessage());
1678
        }
1679
1680
        return true;
0 ignored issues
show
Unused Code introduced by
return true; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1681
    }
1682
1683
    /**
1684
     * Move file into another parent dir.
1685
     * Return new file path or false.
1686
     *
1687
     * @param string $source source file path
1688
     * @param string $target target dir path
0 ignored issues
show
Documentation introduced by
There is no parameter named $target. Did you maybe mean $targetDir?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
1689
     * @param string $name   file name
1690
     *
1691
     * @return string|bool
1692
     *
1693
     * @author Dmitry (dio) Levashov
1694
     **/
1695
    protected function _move($source, $targetDir, $name)
1696
    {
1697
        list($removeParents, $itemId) = $this->_gd_splitPath($source);
1698
        $target = $this->_normpath($targetDir.'/'.$itemId);
1699
        try {
1700
            //moving and renaming a file or directory
1701
            $files = new \Google_Service_Drive_DriveFile();
1702
            $files->setName($name);
1703
1704
            //Set new Parent and remove old parent
1705
            list(, $addParents) = $this->_gd_splitPath($targetDir);
1706
            $opts = ['addParents' => $addParents, 'removeParents' => $removeParents];
1707
1708
            $file = $this->service->files->update($itemId, $files, $opts);
1709
1710
            if ($file->getMimeType() === self::DIRMIME) {
1711
                $this->_gd_getDirectoryData(false);
1712
            }
1713
        } catch (Exception $e) {
1714
            return $this->setError('GoogleDrive error: '.$e->getMessage());
1715
        }
1716
1717
        return $target;
1718
    }
1719
1720
    /**
1721
     * Remove file.
1722
     *
1723
     * @param string $path file path
1724
     *
1725
     * @return bool
1726
     *
1727
     * @author Dmitry (dio) Levashov
1728
     **/
1729
    protected function _unlink($path)
1730
    {
1731
        try {
1732
            $files = new \Google_Service_Drive_DriveFile();
1733
            $files->setTrashed(true);
1734
1735
            list($pid, $itemId) = $this->_gd_splitPath($path);
1736
            $opts = ['removeParents' => $pid];
1737
            $this->service->files->update($itemId, $files, $opts);
1738
        } catch (Exception $e) {
1739
            return $this->setError('GoogleDrive error: '.$e->getMessage());
1740
        }
1741
1742
        return true;
1743
    }
1744
1745
    /**
1746
     * Remove dir.
1747
     *
1748
     * @param string $path dir path
1749
     *
1750
     * @return bool
1751
     *
1752
     * @author Dmitry (dio) Levashov
1753
     **/
1754
    protected function _rmdir($path)
1755
    {
1756
        $res = $this->_unlink($path);
1757
        $res && $this->_gd_getDirectoryData(false);
1758
1759
        return $res;
1760
    }
1761
1762
    /**
1763
     * Create new file and write into it from file pointer.
1764
     * Return new file path or false on error.
1765
     *
1766
     * @param resource $fp   file pointer
1767
     * @param string   $dir  target dir path
0 ignored issues
show
Bug introduced by
There is no parameter named $dir. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1768
     * @param string   $name file name
1769
     * @param array    $stat file stat (required by some virtual fs)
1770
     *
1771
     * @return bool|string
1772
     *
1773
     * @author Dmitry (dio) Levashov
1774
     **/
1775
    protected function _save($fp, $path, $name, $stat)
1776
    {
1777
        if ($name !== '') {
1778
            $path .= '/'.$name;
1779
        }
1780
        list($parentId, $itemId, $parent) = $this->_gd_splitPath($path);
1781
        if ($name === '') {
1782
            $stat['iid'] = $itemId;
1783
        }
1784
1785
        if (! $stat || empty($stat['iid'])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $stat of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1786
            $opts = [
1787
                'q' => sprintf('trashed=false and "%s" in parents and name="%s"', $parentId, $name),
1788
                'fields' => self::FETCHFIELDS_LIST,
1789
            ];
1790
            $srcFile = $this->_gd_query($opts);
1791
            $srcFile = empty($srcFile) ? null : $srcFile[0];
1792
        } else {
1793
            $srcFile = $this->_gd_getFile($path);
1794
        }
1795
1796
        try {
1797
            $mode = 'update';
1798
            $mime = isset($stat['mime']) ? $stat['mime'] : '';
1799
1800
            $file = new Google_Service_Drive_DriveFile();
1801
            if ($srcFile) {
1802
                $mime = $srcFile->getMimeType();
1803
            } else {
1804
                $mode = 'insert';
1805
                $file->setName($name);
1806
                $file->setParents([
1807
                    $parentId,
1808
                ]);
1809
            }
1810
1811
            if (! $mime) {
1812
                $mime = self::mimetypeInternalDetect($name);
1813
            }
1814
            if ($mime === 'unknown') {
1815
                $mime = 'application/octet-stream';
1816
            }
1817
            $file->setMimeType($mime);
1818
1819
            $size = 0;
1820
            if (isset($stat['size'])) {
1821
                $size = $stat['size'];
1822
            } else {
1823
                $fstat = fstat($fp);
1824
                if (! empty($fstat['size'])) {
1825
                    $size = $fstat['size'];
1826
                }
1827
            }
1828
1829
            // set chunk size (max: 100MB)
1830
            $chunkSizeBytes = 100 * 1024 * 1024;
1831
            if ($size > 0) {
1832
                $memory = elFinder::getIniBytes('memory_limit');
1833
                if ($memory) {
1834
                    $chunkSizeBytes = min([$chunkSizeBytes, (intval($memory / 4 / 256) * 256)]);
1835
                }
1836
            }
1837
1838
            if ($size > $chunkSizeBytes) {
1839
                $client = $this->client;
1840
                // Call the API with the media upload, defer so it doesn't immediately return.
1841
                $client->setDefer(true);
1842
                if ($mode === 'insert') {
1843
                    $request = $this->service->files->create($file, [
1844
                        'fields' => self::FETCHFIELDS_GET,
1845
                    ]);
1846
                } else {
1847
                    $request = $this->service->files->update($srcFile->getId(), $file, [
1848
                        'fields' => self::FETCHFIELDS_GET,
1849
                    ]);
1850
                }
1851
1852
                // Create a media file upload to represent our upload process.
1853
                $media = new Google_Http_MediaFileUpload($client, $request, $mime, null, true, $chunkSizeBytes);
1854
                $media->setFileSize($size);
1855
                // Upload the various chunks. $status will be false until the process is
1856
                // complete.
1857
                $status = false;
1858
                while (! $status && ! feof($fp)) {
1859
                    elFinder::extendTimeLimit();
1860
                    // read until you get $chunkSizeBytes from TESTFILE
1861
                    // fread will never return more than 8192 bytes if the stream is read buffered and it does not represent a plain file
1862
                    // An example of a read buffered file is when reading from a URL
1863
                    $chunk = $this->_gd_readFileChunk($fp, $chunkSizeBytes);
1864
                    $status = $media->nextChunk($chunk);
1865
                }
1866
                // The final value of $status will be the data from the API for the object
1867
                // that has been uploaded.
1868
                if ($status !== false) {
1869
                    $obj = $status;
1870
                }
1871
1872
                $client->setDefer(false);
1873
            } else {
1874
                $params = [
1875
                    'data' => stream_get_contents($fp),
1876
                    'uploadType' => 'media',
1877
                    'fields' => self::FETCHFIELDS_GET,
1878
                ];
1879
                if ($mode === 'insert') {
1880
                    $obj = $this->service->files->create($file, $params);
1881
                } else {
1882
                    $obj = $this->service->files->update($srcFile->getId(), $file, $params);
1883
                }
1884
            }
1885
            if ($obj instanceof Google_Service_Drive_DriveFile) {
0 ignored issues
show
Bug introduced by
The class Google_Service_Drive_DriveFile does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1886
                return $this->_joinPath($parent, $obj->getId());
0 ignored issues
show
Bug introduced by
The variable $obj does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1887
            } else {
1888
                return false;
1889
            }
1890
        } catch (Exception $e) {
1891
            return $this->setError('GoogleDrive error: '.$e->getMessage());
1892
        }
1893
    }
1894
1895
    /**
1896
     * Get file contents.
1897
     *
1898
     * @param string $path file path
1899
     *
1900
     * @return string|false
1901
     *
1902
     * @author Dmitry (dio) Levashov
1903
     **/
1904
    protected function _getContents($path)
1905
    {
1906
        $contents = '';
1907
1908
        try {
1909
            list(, $itemId) = $this->_gd_splitPath($path);
1910
1911
            $contents = $this->service->files->get($itemId, [
1912
                'alt' => 'media',
1913
            ]);
1914
            $contents = (string) $contents->getBody();
1915
        } catch (Exception $e) {
1916
            return $this->setError('GoogleDrive error: '.$e->getMessage());
1917
        }
1918
1919
        return $contents;
1920
    }
1921
1922
    /**
1923
     * Write a string to a file.
1924
     *
1925
     * @param string $path    file path
1926
     * @param string $content new file content
1927
     *
1928
     * @return bool
1929
     *
1930
     * @author Dmitry (dio) Levashov
1931
     **/
1932 View Code Duplication
    protected function _filePutContents($path, $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...
1933
    {
1934
        $res = false;
1935
1936
        if ($local = $this->getTempFile($path)) {
1937
            if (file_put_contents($local, $content, LOCK_EX) !== false
1938
            && ($fp = fopen($local, 'rb'))) {
1939
                clearstatcache();
1940
                $res = $this->_save($fp, $path, '', []);
1941
                fclose($fp);
1942
            }
1943
            file_exists($local) && unlink($local);
1944
        }
1945
1946
        return $res;
1947
    }
1948
1949
    /**
1950
     * Detect available archivers.
1951
     **/
1952
    protected function _checkArchivers()
1953
    {
1954
        // die('Not yet implemented. (_checkArchivers)');
0 ignored issues
show
Unused Code Comprehensibility introduced by
84% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1955
        return [];
1956
    }
1957
1958
    /**
1959
     * chmod implementation.
1960
     *
1961
     * @return bool
1962
     **/
1963
    protected function _chmod($path, $mode)
1964
    {
1965
        return false;
1966
    }
1967
1968
    /**
1969
     * Unpack archive.
1970
     *
1971
     * @param string $path archive path
1972
     * @param array  $arc  archiver command and arguments (same as in $this->archivers)
1973
     *
1974
     * @return true
1975
     *
1976
     * @author Dmitry (dio) Levashov
1977
     * @author Alexey Sukhotin
1978
     **/
1979
    protected function _unpack($path, $arc)
0 ignored issues
show
Unused Code introduced by
The parameter $path is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $arc is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1980
    {
1981
        die('Not yet implemented. (_unpack)');
0 ignored issues
show
Coding Style Compatibility introduced by
The method _unpack() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1982
        //return false;
1983
    }
1984
1985
    /**
1986
     * Recursive symlinks search.
1987
     *
1988
     * @param string $path file/dir path
1989
     *
1990
     * @return bool
1991
     *
1992
     * @author Dmitry (dio) Levashov
1993
     **/
1994
    protected function _findSymlinks($path)
0 ignored issues
show
Unused Code introduced by
The parameter $path is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1995
    {
1996
        die('Not yet implemented. (_findSymlinks)');
0 ignored issues
show
Coding Style Compatibility introduced by
The method _findSymlinks() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1997
    }
1998
1999
    /**
2000
     * Extract files from archive.
2001
     *
2002
     * @param string $path archive path
2003
     * @param array  $arc  archiver command and arguments (same as in $this->archivers)
2004
     *
2005
     * @return true
2006
     *
2007
     * @author Dmitry (dio) Levashov,
2008
     * @author Alexey Sukhotin
2009
     **/
2010
    protected function _extract($path, $arc)
2011
    {
2012
        die('Not yet implemented. (_extract)');
0 ignored issues
show
Coding Style Compatibility introduced by
The method _extract() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2013
    }
2014
2015
    /**
2016
     * Create archive and return its path.
2017
     *
2018
     * @param string $dir   target dir
2019
     * @param array  $files files names list
2020
     * @param string $name  archive name
2021
     * @param array  $arc   archiver options
2022
     *
2023
     * @return string|bool
2024
     *
2025
     * @author Dmitry (dio) Levashov,
2026
     * @author Alexey Sukhotin
2027
     **/
2028
    protected function _archive($dir, $files, $name, $arc)
2029
    {
2030
        die('Not yet implemented. (_archive)');
0 ignored issues
show
Coding Style Compatibility introduced by
The method _archive() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2031
    }
2032
2033
    /**
2034
     * Drive query and fetchAll.
2035
     *
2036
     * @param string $sql
0 ignored issues
show
Bug introduced by
There is no parameter named $sql. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
2037
     *
2038
     * @return bool|array
2039
     */
2040
    private function _gd_query($opts)
2041
    {
2042
        $result = [];
2043
        $pageToken = null;
2044
        $parameters = [
2045
                'fields' => self::FETCHFIELDS_LIST,
2046
                'pageSize' => 1000,
2047
                'spaces' => 'drive',
2048
        ];
2049
2050
        if (is_array($opts)) {
2051
            $parameters = array_merge($parameters, $opts);
2052
        }
2053
        do {
2054
            try {
2055
                if ($pageToken) {
2056
                    $parameters['pageToken'] = $pageToken;
2057
                }
2058
                $files = $this->service->files->listFiles($parameters);
2059
2060
                $result = array_merge($result, $files->getFiles());
2061
                $pageToken = $files->getNextPageToken();
2062
            } catch (Exception $e) {
2063
                $pageToken = null;
2064
            }
2065
        } while ($pageToken);
2066
2067
        return $result;
2068
    }
2069
2070
    /**
2071
     * Get dat(googledrive metadata) from GoogleDrive.
2072
     *
2073
     * @param string $path
2074
     *
2075
     * @return array googledrive metadata
2076
     */
2077
    private function _gd_getFile($path, $fields = '')
2078
    {
2079
        list(, $itemId) = $this->_gd_splitPath($path);
2080
        if (! $fields) {
2081
            $fields = self::FETCHFIELDS_GET;
2082
        }
2083
        try {
2084
            $file = $this->service->files->get($itemId, ['fields' => $fields]);
2085
            if ($file instanceof Google_Service_Drive_DriveFile) {
0 ignored issues
show
Bug introduced by
The class Google_Service_Drive_DriveFile does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
2086
                return $file;
2087
            } else {
2088
                return [];
2089
            }
2090
        } catch (Exception $e) {
2091
            return [];
2092
        }
2093
    }
2094
2095
    /**
2096
     * Get dat(googledrive metadata) from GoogleDrive.
2097
     *
2098
     * @param string $path
2099
     *
2100
     * @return array googledrive metadata
2101
     */
2102
    private function _gd_getNameByPath($path)
2103
    {
2104
        list(, $itemId) = $this->_gd_splitPath($path);
2105
        if (! $this->names) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->names of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2106
            $this->_gd_getDirectoryData();
2107
        }
2108
2109
        return isset($this->names[$itemId]) ? $this->names[$itemId] : '';
2110
    }
2111
} // END class
2112