Issues (661)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

php/elFinderVolumeS3.class.php (32 issues)

Upgrade to new PHP Analysis Engine

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

1
<?php
2
3
/**
4
 * @file
5
 * 
6
 * elFinder driver for Amazon S3 (SOAP) filesystem.
7
 *
8
 * @author Dmitry (dio) Levashov,
9
 * @author Alexey Sukhotin
10
 * */
11
class elFinderVolumeS3 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...
12
	protected $driverId = 's3s';
13
	
14
	protected $s3;
15
	
16
	public function __construct() {
17
		$opts = array(
18
			'accesskey'          => '',
19
			'secretkey'          => '',
20
			'bucket'          => '',
21
			'tmpPath' => '',
22
		);
23
		$this->options = array_merge($this->options, $opts); 
24
		$this->options['mimeDetect'] = 'internal';
25
26
	}
27
	
28
	
29
	protected function init() {
30
		if (!$this->options['accesskey'] 
31
		||  !$this->options['secretkey'] 
32
		||  !$this->options['bucket']) {
33
			return $this->setError('Required options undefined.');
34
		}
35
		
36
		$this->s3 = new S3SoapClient($this->options['accesskey'], $this->options['secretkey']);
37
		
38
		$this->root = $this->options['path'];
39
		
40
		$this->rootName = 's3';
41
		
42
		return true;
43
	}
44
	
45
	protected function configure() {
46
		parent::configure();
47 View Code Duplication
		if (!empty($this->options['tmpPath'])) {
0 ignored issues
show
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...
48
			if ((is_dir($this->options['tmpPath']) || @mkdir($this->options['tmpPath'])) && is_writable($this->options['tmpPath'])) {
49
				$this->tmpPath = $this->options['tmpPath'];
0 ignored issues
show
The property tmpPath does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
50
			}
51
		}
52
		$this->mimeDetect = 'internal';
53
	}
54
	
55
	/**
56
	 * Return parent directory path
57
	 *
58
	 * @param  string  $path  file path
59
	 * @return string
60
	 * @author Dmitry (dio) Levashov
61
	 **/
62
	protected function _dirname($path) {
63
	
64
		$newpath =  preg_replace("/\/$/", "", $path);
65
		$dn = substr($path, 0, strrpos($newpath, '/')) ;
66
		
67
		if (substr($dn, 0, 1) != '/') {
68
		 $dn = "/$dn";
69
		}
70
		
71
		return $dn;
72
	}
73
74
	/**
75
	 * Return file name
76
	 *
77
	 * @param  string  $path  file path
78
	 * @return string
79
	 * @author Dmitry (dio) Levashov
80
	 **/
81
	protected function _basename($path) {
82
		return basename($path);
83
	}
84
85
	
86
	
87
	/**
88
	 * Join dir name and file name and return full path.
89
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
90
	 *
91
	 * @param  string  $dir   dir path
92
	 * @param  string  $name  file name
93
	 * @return string
94
	 * @author Dmitry (dio) Levashov
95
	 **/
96
		protected function _joinPath($dir, $name) {
97
		return $dir.DIRECTORY_SEPARATOR.$name;
98
	}
99
	
100
	/**
101
	 * Return normalized path, this works the same as os.path.normpath() in Python
102
	 *
103
	 * @param  string  $path  path
104
	 * @return string
105
	 * @author Troex Nevelin
106
	 **/
107
	protected function _normpath($path) {
108
		$tmp =  preg_replace("/^\//", "", $path);
109
		$tmp =  preg_replace("/\/\//", "/", $tmp);
110
		$tmp =  preg_replace("/\/$/", "", $tmp);
111
		return $tmp;
112
	}
113
114
	/**
115
	 * Return file path related to root dir
116
	 *
117
	 * @param  string  $path  file path
118
	 * @return string
119
	 * @author Dmitry (dio) Levashov
120
	 **/
121
	protected function _relpath($path) {
122
	
123
		
124
		$newpath = $path;
125
		
126
		
127
		if (substr($path, 0, 1) != '/') {
128
			$newpath = "/$newpath";
129
		}
130
		
131
		$newpath =  preg_replace("/\/$/", "", $newpath);
132
	
133
		$ret = ($newpath == $this->root) ? '' : substr($newpath, strlen($this->root)+1);
134
		
135
		return $ret;
136
	}
137
	
138
	/**
139
	 * Convert path related to root dir into real path
140
	 *
141
	 * @param  string  $path  file path
142
	 * @return string
143
	 * @author Dmitry (dio) Levashov
144
	 **/
145
	protected function _abspath($path) {
146
		return $path == $this->separator ? $this->root : $this->root.$this->separator.$path;
147
	}
148
	
149
	/**
150
	 * Return fake path started from root dir
151
	 *
152
	 * @param  string  $path  file path
153
	 * @return string
154
	 * @author Dmitry (dio) Levashov
155
	 **/
156
	protected function _path($path) {
157
		return $this->rootName.($path == $this->root ? '' : $this->separator.$this->_relpath($path));
158
	}
159
	
160
	/**
161
	 * Return true if $path is children of $parent
162
	 *
163
	 * @param  string  $path    path to check
164
	 * @param  string  $parent  parent path
165
	 * @return bool
166
	 * @author Dmitry (dio) Levashov
167
	 **/
168
	protected function _inpath($path, $parent) {
169
		return $path == $parent || strpos($path, $parent.'/') === 0;
170
	}
171
172
	
173
	/**
174
	 * Converting array of objects with name and value properties to
175
	 * array[key] = value
176
	 * @param  array  $metadata  source array
177
	 * @return array
178
	 * @author Alexey Sukhotin
179
	 **/
180
	protected function metaobj2array($metadata) {
181
		$arr = array();
182
		
183
		if (is_array($metadata)) {
184
			foreach ($metadata as $meta) {
185
				$arr[$meta->Name] = $meta->Value;
186
			}
187
		} else {
188
			$arr[$metadata->Name] = $metadata->Value;
189
		}
190
		return $arr;
191
	}
192
	
193
	/**
194
	 * Return stat for given path.
195
	 * Stat contains following fields:
196
	 * - (int)    size    file size in b. required
197
	 * - (int)    ts      file modification time in unix time. required
198
	 * - (string) mime    mimetype. required for folders, others - optionally
199
	 * - (bool)   read    read permissions. required
200
	 * - (bool)   write   write permissions. required
201
	 * - (bool)   locked  is object locked. optionally
202
	 * - (bool)   hidden  is object hidden. optionally
203
	 * - (string) alias   for symlinks - link target path relative to root path. optionally
204
	 * - (string) target  for symlinks - link target path. optionally
205
	 *
206
	 * If file does not exists - returns empty array or false.
207
	 *
208
	 * @param  string  $path    file path 
209
	 * @return array|false
210
	 * @author Dmitry (dio) Levashov,
211
	 * @author Alexey Sukhotin
212
	 **/
213
	protected function _stat($path) {
214
	
215
		$stat = array(
216
		 'size' => 0,
217
		 'ts' => time(),
218
		 'read' => true,
219
		 'write' => true,
220
		 'locked' => false,
221
		 'hidden' => false,
222
		 'mime' => 'directory',
223
		);
224
		
225
		
226
		if ($this->root == $path) {
227
			return $stat;
228
		}
229
230
231
		$np = $this->_normpath($path);
232
		
233
		try {
234
			$obj = $this->s3->GetObject(array('Bucket' => $this->options['bucket'], 'Key' => $np , 'GetMetadata' => true, 'InlineData' => false, 'GetData' => false));
0 ignored issues
show
Documentation Bug introduced by
The method GetObject does not exist on object<S3SoapClient>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
235
		} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
236
		
237
		}
238
			
239
		if (!isset($obj) || ($obj->GetObjectResponse->Status->Code != 200)) {
240
			$np .= '/';
241
			try {
242
				$obj = $this->s3->GetObject(array('Bucket' => $this->options['bucket'], 'Key' => $np , 'GetMetadata' => true, 'InlineData' => false, 'GetData' => false));
0 ignored issues
show
Documentation Bug introduced by
The method GetObject does not exist on object<S3SoapClient>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
243
			} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
244
		
245
			}
246
		}
247
			
248
		if (!(isset($obj) && $obj->GetObjectResponse->Status->Code == 200)) {
249
				return array();
250
		}
251
		
252
		$mime = '';
0 ignored issues
show
$mime 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...
253
		
254
		$metadata = $this->metaobj2array($obj->GetObjectResponse->Metadata);
255
			
256
		$mime = $metadata['Content-Type'];
257
		
258
		if (!empty($mime)) {
259
		 $stat['mime'] = ($mime == 'binary/octet-stream') ? 'directory' : $mime;
260
		}
261
		
262
		if (isset($obj->GetObjectResponse->LastModified)) {
263
			$stat['ts'] = strtotime($obj->GetObjectResponse->LastModified);
264
		}
265
			
266
		try {
267
			$files = $this->s3->ListBucket(array('Bucket' => $this->options['bucket'], 'Prefix' => $np, 'Delimiter' => '/'))->ListBucketResponse->Contents;
0 ignored issues
show
Documentation Bug introduced by
The method ListBucket does not exist on object<S3SoapClient>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
268
		} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
269
			
270
		}
271
			
272
		if (!is_array($files)) {
273
			$files = array($files);
274
		}
275
		
276
		foreach ($files as $file) {
277
			if ($file->Key == $np) {
278
				$stat['size'] = $file->Size;
279
			}
280
		}
281
282
		return $stat;
283
	}
284
	
285
	
286
287
	/***************** file stat ********************/
288
289
		
290
	/**
291
	 * Return true if path is dir and has at least one childs directory
292
	 *
293
	 * @param  string  $path  dir path
294
	 * @return bool
295
	 * @author Alexey Sukhotin
296
	 **/
297
	protected function _subdirs($path) {
298
		$stat = $this->_stat($path);
299
		
300
		if ($stat['mime'] == 'directory') {
301
		 $files = $this->_scandir($path);
302
		 foreach ($files as $file) {
303
			$fstat = $this->_stat($file);
304
			if ($fstat['mime'] == 'directory') {
305
				return true;
306
			}
307
		 }
308
		
309
		}
310
		
311
		return false;
312
	}
313
	
314
	/**
315
	 * Return object width and height
316
	 * Ususaly used for images, but can be realize for video etc...
317
	 *
318
	 * @param  string  $path  file path
319
	 * @param  string  $mime  file mime type
320
	 * @return string|false
321
	 * @author Dmitry (dio) Levashov
322
	 * @author Naoki Sawada
323
	 **/
324 View Code Duplication
	protected function _dimensions($path, $mime) {
0 ignored issues
show
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...
325
		$ret = false;
326
		if ($imgsize = $this->getImageSize($path, $mime)) {
327
			$ret = $imgsize['dimensions'];
328
		}
329
		return $ret;
330
	}
331
	
332
	/******************** file/dir content *********************/
333
334
	/**
335
	 * Return files list in directory
336
	 *
337
	 * @param  string  $path  dir path
338
	 * @return array
339
	 * @author Dmitry (dio) Levashov,
340
	 * @author Alexey Sukhotin
341
	 **/
342
	protected function _scandir($path) {
343
		
344
		$s3path = preg_replace("/^\//", "", $path) . '/';
345
		
346
		$files = $this->s3->ListBucket(array('Bucket' => $this->options['bucket'], 'delimiter' => '/', 'Prefix' => $s3path))->ListBucketResponse->Contents;
0 ignored issues
show
Documentation Bug introduced by
The method ListBucket does not exist on object<S3SoapClient>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
347
	
348
		$finalfiles = array();
349
		
350
		foreach ($files as $file) {
351
			if (preg_match("|^" . $s3path . "[^/]*/?$|", $file->Key)) {
352
				$fname = preg_replace("/\/$/", "", $file->Key);
0 ignored issues
show
$fname 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...
353
				$fname = $file->Key;
354
				
355
				if ($fname != preg_replace("/\/$/", "", $s3path)) {
0 ignored issues
show
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
356
					
357
				}
358
				
359
				$finalfiles[] = $fname;
360
			}
361
		}
362
		
363
		sort($finalfiles);
364
		return $finalfiles;
365
	}
366
367
	/**
368
	 * Open file and return file pointer
369
	 *
370
	 * @param  string  $path  file path
371
	 * @param  string  $mode open mode
372
	 * @return resource|false
373
	 * @author Dmitry (dio) Levashov,
374
	 * @author Alexey Sukhotin
375
	 **/
376
	protected function _fopen($path, $mode="rb") {
377
	
378
		$tn = $this->getTempFile($path);
379
	
380
		$fp = $this->tmbPath
381
			? @fopen($tn, 'w+')
382
			: @tmpfile();
383
		
384
385
		if ($fp) {
386
387
			try {
388
				$obj = $this->s3->GetObject(array('Bucket' => $this->options['bucket'], 'Key' => $this->_normpath($path) , 'GetMetadata' => true, 'InlineData' => true, 'GetData' => true));
0 ignored issues
show
Documentation Bug introduced by
The method GetObject does not exist on object<S3SoapClient>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
389
			}	catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
390
		
391
			}
392
				
393
			$mime = '';
0 ignored issues
show
$mime 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...
394
		
395
			$metadata = $this->metaobj2array($obj->GetObjectResponse->Metadata);
0 ignored issues
show
$metadata 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...
396
397
			fwrite($fp, $obj->GetObjectResponse->Data);
398
			rewind($fp);
399
			return $fp;
400
		}
401
		
402
		return false;
403
	}
404
	
405
	/**
406
	 * Close opened file
407
	 * 
408
	 * @param  resource  $fp    file pointer
409
	 * @param  string    $path  file path
410
	 * @return bool
411
	 * @author Dmitry (dio) Levashov
412
	 **/
413
	protected function _fclose($fp, $path='') {
414
		@fclose($fp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
415
		if ($path) {
416
			@unlink($this->getTempFile($path));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
417
		}
418
	}
419
	
420
	/********************  file/dir manipulations *************************/
421
	
422
	/**
423
	 * Create dir and return created dir path or false on failed
424
	 *
425
	 * @param  string  $path  parent dir path
426
	 * @param string  $name  new directory name
427
	 * @return string|bool
428
	 * @author Dmitry (dio) Levashov,
429
	 * @author Alexey Sukhotin
430
	 **/
431
	protected function _mkdir($path, $name) {
432
		
433
		$newkey = $this->_normpath($path);
434
		$newkey = preg_replace("/\/$/", "", $newkey);
435
		$newkey = "$newkey/$name/";
436
437
		try {
438
			$obj = $this->s3->PutObjectInline(array('Bucket' => $this->options['bucket'], 'Key' => $newkey , 'ContentLength' => 0, 'Data' => ''));
0 ignored issues
show
Documentation Bug introduced by
The method PutObjectInline does not exist on object<S3SoapClient>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
439
		} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
440
		
441
		}
442
		
443
		if (isset($obj)) {
444
			return "$path/$name";
445
		}
446
		
447
		return false;
448
	}
449
	
450
	/**
451
	 * Create file and return it's path or false on failed
452
	 *
453
	 * @param  string  $path  parent dir path
454
	 * @param string  $name  new file name
455
	 * @return string|bool
456
	 * @author Dmitry (dio) Levashov,
457
	 * @author Alexey Sukhotin
458
	 **/
459
	 protected function _mkfile($path, $name) {
460
		$newkey = $this->_normpath($path);
461
		$newkey = preg_replace("/\/$/", "", $newkey);
462
		$newkey = "$newkey/$name";
463
464
		try {
465
			$obj = $this->s3->PutObjectInline(array('Bucket' => $this->options['bucket'], 'Key' => $newkey , 'ContentLength' => 0, 'Data' => '', 'Metadata' => array(array('Name' => 'Content-Type', 'Value' => 'text/plain'))));
0 ignored issues
show
Documentation Bug introduced by
The method PutObjectInline does not exist on object<S3SoapClient>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
466
		} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
467
		
468
		}
469
		
470
		if (isset($obj)) {
471
			return "$path/$name";
472
		}
473
		
474
		return false;
475
476
	 }
477
	
478
	/**
479
	 * Create symlink
480
	 *
481
	 * @param  string  $source     file to link to
482
	 * @param  string  $targetDir  folder to create link in
483
	 * @param  string  $name       symlink name
484
	 * @return bool
485
	 * @author Dmitry (dio) Levashov
486
	 **/
487
	 protected function _symlink($source, $targetDir, $name) {
488
		return false;
489
	 }
490
491
	/**
492
	 * Copy file into another file (only inside one volume)
493
	 *
494
	 * @param  string  $source  source file path
495
	 * @param  string  $targetDir  target dir path
496
	 * @param  string  $name    file name
497
	 * @return bool
498
	 * @author Dmitry (dio) Levashov
499
	 **/
500
	 protected function _copy($source, $targetDir, $name) {
501
		return false;
502
	 }
503
	
504
	/**
505
	 * Move file into another parent dir.
506
	 * Return new file path or false.
507
	 *
508
	 * @param  string  $source  source file path
509
	 * @param  string  $targetDir  target dir path
510
	 * @param  string  $name    file name
511
	 * @return string|bool
512
	 * @author Dmitry (dio) Levashov
513
	 **/
514
	protected function _move($source, $targetDir, $name) {
515
		return false;
516
	}
517
	
518
	/**
519
	 * Remove file
520
	 *
521
	 * @param  string  $path  file path
522
	 * @return bool
523
	 * @author Dmitry (dio) Levashov
524
	 **/
525
	protected function _unlink($path) {
526
		
527
		$newkey = $this->_normpath($path);
528
		$newkey = preg_replace("/\/$/", "", $newkey);
529
530
		try {
531
			$obj = $this->s3->DeleteObject(array('Bucket' => $this->options['bucket'], 'Key' => $newkey));
0 ignored issues
show
Documentation Bug introduced by
The method DeleteObject does not exist on object<S3SoapClient>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
532
		} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
533
		
534
		}
535
		
536
		/*$fp = fopen('/tmp/eltest.txt','a+');
0 ignored issues
show
Unused Code Comprehensibility introduced by
61% 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...
537
		
538
		fwrite($fp, 'key='.$newkey);*/
539
		
540
		if (is_object($obj)) {
541
			//fwrite($fp, 'obj='.var_export($obj,true));
0 ignored issues
show
Unused Code Comprehensibility introduced by
74% 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...
542
			
543
			if (isset($obj->DeleteObjectResponse->Code)) {
544
				$rc = $obj->DeleteObjectResponse->Code;
545
				
546
				if (substr($rc, 0, 1) == '2') {
547
					return true;
548
				}
549
			}
550
		}
551
552
			
553
		//fclose($fp);
554
		
555
		return false;
556
	}
557
558
	/**
559
	 * Remove dir
560
	 *
561
	 * @param  string  $path  dir path
562
	 * @return bool
563
	 * @author Dmitry (dio) Levashov
564
	 **/
565
	protected function _rmdir($path) {
566
		return $this->_unlink($path . '/');
567
	}
568
569
	/**
570
	 * Create new file and write into it from file pointer.
571
	 * Return new file path or false on error.
572
	 *
573
	 * @param  resource $fp file pointer
574
	 * @param  string $dir target dir path
575
	 * @param  string $name file name
576
	 * @param  array $stat
577
	 * @return bool|string
578
	 * @author Dmitry (dio) Levashov
579
	 */
580
	protected function _save($fp, $dir, $name, $stat) {
581
		return false;
582
	}
583
	
584
	/**
585
	 * Get file contents
586
	 *
587
	 * @param  string  $path  file path
588
	 * @return string|false
589
	 * @author Dmitry (dio) Levashov
590
	 **/
591
	protected function _getContents($path) {
592
		return false;
593
	}
594
	
595
	/**
596
	 * Write a string to a file
597
	 *
598
	 * @param  string  $path     file path
599
	 * @param  string  $content  new file content
600
	 * @return bool
601
	 * @author Dmitry (dio) Levashov
602
	 **/
603
	protected function _filePutContents($path, $content) {
604
		return false;
605
	}
606
607
	/**
608
	 * Extract files from archive
609
	 *
610
	 * @param  string  $path file path
611
	 * @param  array   $arc  archiver options
612
	 * @return bool
613
	 * @author Dmitry (dio) Levashov, 
614
	 * @author Alexey Sukhotin
615
	 **/
616
	 protected function _extract($path, $arc) {
617
		return false;
618
	 }
619
620
	/**
621
	 * Create archive and return its path
622
	 *
623
	 * @param  string  $dir    target dir
624
	 * @param  array   $files  files names list
625
	 * @param  string  $name   archive name
626
	 * @param  array   $arc    archiver options
627
	 * @return string|bool
628
	 * @author Dmitry (dio) Levashov, 
629
	 * @author Alexey Sukhotin
630
	 **/
631
	protected function _archive($dir, $files, $name, $arc) {
632
		return false;
633
	}
634
635
	/**
636
	 * Detect available archivers
637
	 *
638
	 * @return void
639
	 * @author Dmitry (dio) Levashov, 
640
	 * @author Alexey Sukhotin
641
	 **/
642
	protected function _checkArchivers() {
643
		
644
	}
645
646
	/**
647
	 * chmod implementation
648
	 *
649
	 * @param string $path
650
	 * @param string $mode
651
	 * @return bool
652
	 */
653
	protected function _chmod($path, $mode) {
654
		return false;
655
	}
656
657
}
658
659
/**
660
 * SoapClient extension with Amazon S3 WSDL and request signing support
661
 *
662
 * @author Alexey Sukhotin
663
 **/
664
class S3SoapClient extends SoapClient {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
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...
665
666
	private $accesskey = ''; 
667
	private $secretkey = '';
668
	public $client = NULL;
669
	
670
671
	public function __construct($key = '', $secret = '') {
672
		$this->accesskey = $key;
673
		$this->secretkey = $secret;
674
		parent::__construct('http://s3.amazonaws.com/doc/2006-03-01/AmazonS3.wsdl');
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class SoapClient as the method __construct() does only exist in the following sub-classes of SoapClient: S3SoapClient. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
675
	}
676
677
678
	/**
679
	 * Method call wrapper which adding S3 signature and default arguments to all S3 operations
680
	 *
681
	 * @param string $method
682
	 * @param string $arguments
683
	 * @return mixed
684
	 *
685
	 * @author Alexey Sukhotin
686
	 */
687
	public function __call($method, $arguments) {
688
		
689
		/* Getting list of S3 web service functions which requires signing */
690
		$funcs = $this->__getFunctions();
691
		
692
		$funcnames  = array();
693
		
694
		foreach ($funcs as $func) {
695
			preg_match("/\S+\s+([^\)]+)\(/", $func, $m);
696
	
697
			if (isset($m[1])) {
698
				$funcnames[] = $m[1];
699
			}
700
		}
701
		
702
		/* adding signature to arguments */
703
		if (in_array("{$method}", $funcnames)) {
704
		
705
			if (is_array($arguments[0])) {
706
				$arguments[0] = array_merge($arguments[0], $this->sign("{$method}"));
707
			} else {
708
				$arguments[0] = $this->sign("{$method}");
709
			}
710
711
		}
712
		
713
		/*$fp = fopen('/tmp/s3debug.txt', 'a+');
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% 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...
714
		fwrite($fp, 'method='."{$method}". ' timestamp='.date('Y-m-d H:i:s').' args='.var_export($arguments,true) . "\n");
715
		fclose($fp);*/
716
		return parent::__call($method, $arguments);
717
	}
718
719
	/**
720
	 * Generating signature and timestamp for specified S3 operation
721
	 *
722
	 * @param  string  $operation    S3 operation name
723
	 * @return array
724
	 * @author Alexey Sukhotin
725
	 **/
726
	protected function sign($operation) {
727
	
728
		$params = array(
729
			'AWSAccessKeyId' => $this->accesskey,
730
			'Timestamp' => gmdate('Y-m-d\TH:i:s.000\Z'),
731
		);
732
733
		$sign_str = 'AmazonS3' . $operation . $params['Timestamp'];
734
		
735
		$params['Signature'] = base64_encode(hash_hmac('sha1', $sign_str, $this->secretkey, TRUE));
736
		
737
		return $params;
738
	}
739
	
740
}
741
742