Test Failed
Push — master ( 191cbb...97bed7 )
by Joe
02:13
created

Plugin   B

Complexity

Total Complexity 38

Size/Duplication

Total Lines 271
Duplicated Lines 11.44 %

Coupling/Cohesion

Components 2
Dependencies 1

Test Coverage

Coverage 0%

Importance

Changes 18
Bugs 3 Features 0
Metric Value
c 18
b 3
f 0
dl 31
loc 271
ccs 0
cts 123
cp 0
rs 8.3999
wmc 38
lcom 2
cbo 1

15 Methods

Rating   Name   Duplication   Size   Complexity  
A activate() 0 7 1
A getCapabilities() 0 5 1
A getSubscribedEvents() 0 7 1
A onPreFileDownload() 0 8 1
A setPermissions() 0 20 4
A getWritableDirs() 8 8 3
A getWritableFiles() 8 8 3
A setPermissionsSetfacl() 8 8 3
A setPermissionsChmod() 7 7 3
A getHttpdUser() 0 9 3
A SetfaclPermissionsSetter() 0 8 2
A ChmodPermissionsSetter() 0 8 2
A EnsureDirExists() 0 9 4
A EnsureFileExists() 0 10 4
A runProcess() 0 8 3

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * Plugins Management
4
 * @author Joe Huss <[email protected]>
5
 * @copyright 2017
6
 * @package MyAdmin
7
 * @category Plugins
8
 */
9
10
/**
11
 * MyAdmin Installer Plugin
12
 * @link https://github.com/composer/composer/blob/master/src/Composer/Plugin/PluginInterface.php
13
 */
14
15
namespace MyAdmin\Plugins;
16
17
use Composer\Composer;
18
use Composer\EventDispatcher\EventSubscriberInterface;
19
use Composer\IO\IOInterface;
20
use Composer\Plugin\PluginInterface;
21
use Composer\Plugin\Capable;
22
use Composer\Plugin\PluginEvents;
23
use Composer\Plugin\PreFileDownloadEvent;
24
use Composer\Script\Event;
25
26
/**
27
 * MyAdmin Installer Plugin
28
 */
29
class Plugin implements PluginInterface, EventSubscriberInterface, Capable {
30
	protected $composer;
31
	protected $io;
32
33
	/**
34
	 * Apply plugin modifications to Composer
35
	 *
36
	 * @param Composer	$composer
37
	 * @param IOInterface $io
38
	 */
39
	public function activate(Composer $composer, IOInterface $io) {
40
		$this->composer = $composer;
41
		$this->io = $io;
42
		print 'Hello peoples...';
43
		$installer = new Installer($this->io, $this->composer);
44
		$this->composer->getInstallationManager()->addInstaller($installer);
45
	}
46
47
	/**
48
	 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
49
	 */
50
	public function getCapabilities() {
51
		return [
52
			'Composer\Plugin\Capability\CommandProvider' => 'MyAdmin\Plugins\CommandProvider'
53
		];
54
	}
55
56
	/**
57
	 *
58
	 *	Events
59
	 *
60
	 *		Command Events					Composer\Script\Event
61
	 *
62
	 * 			pre-install-cmd				occurs before the install command is executed with a lock file present.
63
	 * 			post-install-cmd			occurs after the install command has been executed with a lock file present.
64
	 * 			pre-update-cmd				occurs before the update command is executed, or before the install command is executed without a lock file present.
65
	 * 			post-update-cmd				occurs after the update command has been executed, or after the install command has been executed without a lock file present.
66
	 * 			post-status-cmd				occurs after the status command has been executed.
67
	 * 			pre-archive-cmd				occurs before the archive command is executed.
68
	 * 			post-archive-cmd			occurs after the archive command has been executed.
69
	 * 			pre-autoload-dump			occurs before the autoloader is dumped, either during install/update, or via the dump-autoload command.
70
	 * 			post-autoload-dump			occurs after the autoloader has been dumped, either during install/update, or via the dump-autoload command.
71
	 * 			post-root-package-install	occurs after the root package has been installed, during the create-project command.
72
	 *			post-create-project-cmd		occurs after the create-project command has been executed.
73
	 *
74
	 *		Installer Events				Composer\Installer\InstallerEvent
75
	 *
76
	 * 			pre-dependencies-solving	occurs before the dependencies are resolved.
77
	 * 			post-dependencies-solving	occurs after the dependencies have been resolved.
78
	 *
79
	 *		Package Events					Composer\Installer\PackageEvent
80
	 *
81
	 * 			pre-package-install			occurs before a package is installed.
82
	 * 			post-package-install		occurs after a package has been installed.
83
	 * 			pre-package-update			occurs before a package is updated.
84
	 * 			post-package-update			occurs after a package has been updated.
85
	 * 			pre-package-uninstall		occurs before a package is uninstalled.
86
	 * 			post-package-uninstall		occurs after a package has been uninstalled.
87
	 *
88
	 * 		Plugin Events					Composer\Plugin\PluginEvents
89
	 *
90
	 * 			init						occurs after a Composer instance is done being initialized.
91
	 * 			command						occurs before any Composer Command is executed on the CLI. It provides you with access to the input and output objects of the program.
92
	 * 			pre-file-download			occurs before files are downloaded and allows you to manipulate the RemoteFilesystem object prior to downloading files based on the URL to be downloaded.
93
	 */
94
	public static function getSubscribedEvents() {
95
		return [
96
			PluginEvents::PRE_FILE_DOWNLOAD => [
97
				['onPreFileDownload', 0]
98
			]
99
		];
100
	}
101
102
	/**
103
	* @param PreFileDownloadEvent $event
104
	*/
105
	public function onPreFileDownload(PreFileDownloadEvent $event) {
106
		/*$protocol = parse_url($event->getProcessedUrl(), PHP_URL_SCHEME);
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% 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...
107
		if ($protocol === 's3') {
108
			$awsClient = new AwsClient($this->io, $this->composer->getConfig());
109
			$s3RemoteFilesystem = new S3RemoteFilesystem($this->io, $event->getRemoteFilesystem()->getOptions(), $awsClient);
110
			$event->setRemoteFilesystem($s3RemoteFilesystem);
111
		}*/
112
	}
113
114
	/**
115
	 * An event that triggers setting writable permissions on any directories specified in the writable-dirs composer extra options
116
	 *
117
	 * @param Event $event
118
	 * @return void
119
	 */
120
	public static function setPermissions(Event $event) {
121
		if ('WIN' === strtoupper(substr(PHP_OS, 0, 3))) {
122
			$event->getIO()->write('<info>No permissions setup is required on Windows.</info>');
123
			return;
124
		}
125
		$event->getIO()->write('Setting up permissions.');
126
		try {
127
			self::setPermissionsSetfacl($event);
128
			return;
129
		} catch (\Exception $setfaclException) {
130
			$event->getIO()->write(sprintf('<error>%s</error>', $setfaclException->getMessage()));
131
			$event->getIO()->write('<info>Trying chmod...</info>');
132
		}
133
		try {
134
			self::setPermissionsChmod($event);
135
			return;
136
		} catch (\Exception $chmodException) {
137
			$event->getIO()->write(sprintf('<error>%s</error>', $chmodException->getMessage()));
138
		}
139
	}
140
141
	/**
142
	 * returns a list of writeable directories specified in the writeable-dirs composer extra options
143
	 *
144
	 * @param Event $event
145
	 * @return array an array of directory paths
146
	 */
147 View Code Duplication
	public static function getWritableDirs(Event $event) {
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...
148
		$configuration = $event->getComposer()->getPackage()->getExtra();
149
		if (!isset($configuration['writable-dirs']))
150
			throw new \Exception('The writable-dirs must be specified in composer arbitrary extra data.');
151
		if (!is_array($configuration['writable-dirs']))
152
			throw new \Exception('The writable-dirs must be an array.');
153
		return $configuration['writable-dirs'];
154
	}
155
156
	/**
157
	 * returns a list of writeable files specified in the writeable-files composer extra options
158
	 *
159
	 * @param Event $event
160
	 * @return array an array of file paths
161
	 */
162 View Code Duplication
	public static function getWritableFiles(Event $event) {
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...
163
		$configuration = $event->getComposer()->getPackage()->getExtra();
164
		if (!isset($configuration['writable-files']))
165
			throw new \Exception('The writable-files must be specified in composer arbitrary extra data.');
166
		if (!is_array($configuration['writable-files']))
167
			throw new \Exception('The writable-files must be an array.');
168
		return $configuration['writable-files'];
169
	}
170
171
	/**
172
	 * Sets Writrable Directory permissions for any directories listed in the writeable-dirs option using setfacl
173
	 *
174
	 * @param Event $event
175
	 */
176 View Code Duplication
	public static function setPermissionsSetfacl(Event $event) {
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...
177
		$http_user = self::getHttpdUser($event);
178
		foreach (self::getWritableDirs($event) as $path)
179
			self::SetfaclPermissionsSetter($event, $http_user, $path, 'dir');
180
		foreach (self::getWritableFiles($event) as $path)
181
			//self::SetfaclPermissionsSetter($event, $http_user, $path, 'file');
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% 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...
182
			self::ChmodPermissionsSetter($event, $http_user, $path, 'file');
183
	}
184
185
	/**
186
	 * Sets Writrable Directory permissions for any directories listed in the writeable-dirs option using chmod
187
	 *
188
	 * @param Event $event
189
	 */
190 View Code Duplication
	public static function setPermissionsChmod(Event $event) {
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...
191
		$http_user = self::getHttpdUser($event);
192
		foreach (self::getWritableDirs($event) as $path)
193
			self::ChmodPermissionsSetter($event, $http_user, $path, 'dir');
194
		foreach (self::getWritableFiles($event) as $path)
195
			self::ChmodPermissionsSetter($event, $http_user, $path, 'file');
196
	}
197
198
	/**
199
	 * returns the user the webserver is running as
200
	 *
201
	 * @param Event $event
202
	 * @return string the webserver username
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
203
	 */
204
	public static function getHttpdUser(Event $event) {
205
		$ps = self::runProcess($event, 'ps aux');
206
		preg_match_all('/^.*([a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx)$/m', $ps, $matches);
207
		foreach ($matches[0] as $match) {
208
			$user = substr($match, 0, strpos($match, ' '));
209
			if ($user != 'root')
210
				return $user;
211
		}
212
	}
213
214
	/**
215
	 * sets the needed permissions for the $http_user and the running user on $path using setfacl
216
	 *
217
	 * @param Event $event
218
	 * @param string $http_user the webserver username
219
	 * @param string $path the directory to set permissions on
220
	 * @param string $type optional type of entry, defaults to dir, can be dir or file
221
	 */
222
	public static function SetfaclPermissionsSetter(Event $event, $http_user, $path, $type = 'dir') {
0 ignored issues
show
Coding Style introduced by
SetfaclPermissionsSetter uses the super-global variable $_SERVER 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...
Coding Style Naming introduced by
The parameter $http_user is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
223
		if ($type == 'dir')
224
			self::EnsureDirExists($event, $path);
225
		else
226
			self::EnsureFileExists($event, $path);
227
		self::runProcess($event, 'setfacl -m u:"'.$http_user.'":rwX -m u:'.$_SERVER['USER'].':rwX '.$path);
228
		self::runProcess($event, 'setfacl -d -m u:"'.$http_user.'":rwX -m u:'.$_SERVER['USER'].':rwX '.$path);
229
	}
230
231
	/**
232
	 * sets the needed permissions for the $http_user and the running user on $path using chmod
233
	 *
234
	 * @param Event $event
235
	 * @param string $http_user the webserver username
236
	 * @param string $path the directory to set permissions on
237
	 * @param string $type optional type of entry, defaults to dir, can be dir or file
238
	 */
239
	public static function ChmodPermissionsSetter(Event $event, $http_user, $path, $type = 'dir') {
0 ignored issues
show
Coding Style introduced by
ChmodPermissionsSetter uses the super-global variable $_SERVER 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...
Coding Style Naming introduced by
The parameter $http_user is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
240
		if ($type == 'dir')
241
			self::EnsureDirExists($event, $path);
242
		else
243
			self::EnsureFileExists($event, $path);
244
		self::runProcess($event, 'chmod +a "'.$http_user.' allow delete,write,append,file_inherit,directory_inherit" '.$path);
245
		self::runProcess($event, 'chmod +a "'.$_SERVER['USER'].' allow delete,write,append,file_inherit,directory_inherit" '.$path);
246
	}
247
248
	/**
249
	 * checks if the given directory exists and if not tries to create it.
250
	 *
251
	 * @param Event $event
252
	 * @param string $path the directory
253
	 * @throws \Exception
254
	 */
255
	public static function EnsureDirExists(Event $event, $path) {
256
		if (!is_dir($path)) {
257
			mkdir($path, 0777, true);
258
			if (!is_dir($path))
259
				throw new \Exception('Path Not Found: '.$path);
260
			if ($event->getIO()->isVerbose() === TRUE)
261
				$event->getIO()->write(sprintf('Created Directory <info>%s</info>', $path));
262
		}
263
	}
264
265
	/**
266
	 * checks if the given file exists and if not tries to create it.
267
	 *
268
	 * @param Event $event
269
	 * @param string $path the directory
270
	 * @throws \Exception
271
	 */
272
	public static function EnsureFileExists(Event $event, $path) {
273
		if (!is_dir(dirname($path))) {
274
			mkdir(dirname($path), 0777, true);
275
			touch($path);
276
			if (!file_exists($path))
277
				throw new \Exception('File Not Found: '.$path);
278
			if ($event->getIO()->isVerbose() === TRUE)
279
				$event->getIO()->write(sprintf('Created File <info>%s</info>', $path));
280
		}
281
	}
282
283
	/**
284
	 * runs a command process returning the output and checking return code
285
	 *
286
	 * @param Event $event
287
	 * @param string $commandline the command line to run
288
	 * @return string the output
289
	 * @throws \Exception
290
	 */
291
	public static function runProcess(Event $event, $commandline) {
292
		if ($event->getIO()->isVerbose() === TRUE)
293
			$event->getIO()->write(sprintf('Running <info>%s</info>', $commandline));
294
		exec($commandline, $output, $return);
295
		if ($return != 0)
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $return of type integer|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
296
			throw new \Exception('Returned Error Code '.$return);
297
		return implode(PHP_EOL, $output);
298
	}
299
}
300