Completed
Push — update/base-styles-210 ( 2e278b...ad767b )
by Jeremy
22:25 queued 13:15
created

AutoloadGenerator   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 327
Duplicated Lines 22.02 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
dl 72
loc 327
rs 8.48
c 0
b 0
f 0
wmc 49
lcom 1
cbo 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
F parseAutoloadsType() 36 60 32
B dump() 0 57 1
B getClassMap() 24 59 10
A getAutoloadClassmapPackagesFile() 0 14 1
A getFileMap() 12 17 2
A getAutoloadFilesPackagesFile() 0 14 1
A getAutoloadPackageFile() 0 14 1

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like AutoloadGenerator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AutoloadGenerator, and based on these observations, apply Extract Interface, too.

1
<?php // phpcs:ignore WordPress.Files.FileName
2
/**
3
 * Autoloader Generator.
4
 *
5
 * @package automattic/jetpack-autoloader
6
 */
7
8
// phpcs:disable PHPCompatibility.Keywords.NewKeywords.t_useFound
9
// phpcs:disable PHPCompatibility.LanguageConstructs.NewLanguageConstructs.t_ns_separatorFound
10
// phpcs:disable PHPCompatibility.FunctionDeclarations.NewClosure.Found
11
// phpcs:disable PHPCompatibility.Keywords.NewKeywords.t_namespaceFound
12
// phpcs:disable PHPCompatibility.Keywords.NewKeywords.t_dirFound
13
// phpcs:disable WordPress.Files.FileName.InvalidClassFileName
14
// phpcs:disable WordPress.Files.FileName.InvalidClassFileName
15
// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_var_export
16
// phpcs:disable WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents
17
// phpcs:disable WordPress.WP.AlternativeFunctions.file_system_read_fopen
18
// phpcs:disable WordPress.WP.AlternativeFunctions.file_system_read_fwrite
19
// phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
20
// phpcs:disable WordPress.NamingConventions.ValidVariableName.InterpolatedVariableNotSnakeCase
21
// phpcs:disable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
22
// phpcs:disable WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase
23
24
25
namespace Automattic\Jetpack\Autoloader;
26
27
use Composer\Autoload\AutoloadGenerator as BaseGenerator;
28
use Composer\Autoload\ClassMapGenerator;
29
use Composer\Config;
30
use Composer\Installer\InstallationManager;
31
use Composer\IO\IOInterface;
32
use Composer\Package\PackageInterface;
33
use Composer\Repository\InstalledRepositoryInterface;
34
use Composer\Util\Filesystem;
35
36
/**
37
 * Class AutoloadGenerator.
38
 */
39
class AutoloadGenerator extends BaseGenerator {
40
41
	const COMMENT = <<<AUTOLOADER_COMMENT
42
/**
43
 * This file was automatically generated by automattic/jetpack-autoloader.
44
 *
45
 * @package automattic/jetpack-autoloader
46
 */
47
48
AUTOLOADER_COMMENT;
49
50
	/**
51
	 * Instantiate an AutoloadGenerator object.
52
	 *
53
	 * @param IOInterface $io IO object.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $io not be null|IOInterface?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
54
	 */
55
	public function __construct( IOInterface $io = null ) {
56
		$this->io = $io;
57
	}
58
59
	/**
60
	 * Dump the autoloader.
61
	 *
62
	 * @param Config                       $config Config object.
63
	 * @param InstalledRepositoryInterface $localRepo Installed Reposetories object.
64
	 * @param PackageInterface             $mainPackage Main Package object.
65
	 * @param InstallationManager          $installationManager Manager for installing packages.
66
	 * @param string                       $targetDir Path to the current target directory.
67
	 * @param bool                         $scanPsr0Packages Whether to search for packages. Currently hard coded to always be false.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $scanPsr0Packages not be boolean|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
68
	 * @param string                       $suffix The autoloader suffix.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $suffix not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
69
	 */
70
	public function dump(
71
		Config $config,
72
		InstalledRepositoryInterface $localRepo,
73
		PackageInterface $mainPackage,
74
		InstallationManager $installationManager,
75
		$targetDir,
76
		$scanPsr0Packages = null, // Not used we always optimize.
77
		$suffix = null
78
	) {
79
80
		$filesystem = new Filesystem();
81
		$filesystem->ensureDirectoryExists( $config->get( 'vendor-dir' ) );
82
83
		$basePath   = $filesystem->normalizePath( realpath( getcwd() ) );
84
		$vendorPath = $filesystem->normalizePath( realpath( $config->get( 'vendor-dir' ) ) );
85
		$targetDir  = $vendorPath . '/' . $targetDir;
86
		$filesystem->ensureDirectoryExists( $targetDir );
87
88
		$packageMap = $this->buildPackageMap( $installationManager, $mainPackage, $localRepo->getCanonicalPackages() );
89
		$autoloads  = $this->parseAutoloads( $packageMap, $mainPackage );
90
91
		$classMap = $this->getClassMap( $autoloads, $filesystem, $vendorPath, $basePath );
92
		$fileMap  = $this->getFileMap( $autoloads, $filesystem, $vendorPath, $basePath );
93
94
		// Remove a file that was generated in versions 2.0.0 to 2.1.0.
95
		$filesystem->remove( $vendorPath . '/autoload_functions.php' );
96
97
		// Generate the files.
98
		file_put_contents( $targetDir . '/jetpack_autoload_classmap.php', $this->getAutoloadClassmapPackagesFile( $classMap ) );
99
		$this->io->writeError( '<info>Generated ' . $targetDir . '/jetpack_autoload_classmap.php</info>', true );
100
101
		file_put_contents( $targetDir . '/jetpack_autoload_filemap.php', $this->getAutoloadFilesPackagesFile( $fileMap ) );
102
		$this->io->writeError( '<info>Generated ' . $targetDir . '/jetpack_autoload_filemap.php</info>', true );
103
104
		file_put_contents( $vendorPath . '/autoload_packages.php', $this->getAutoloadPackageFile( 'autoload.php', $suffix ) );
105
		$this->io->writeError( '<info>Generated ' . $vendorPath . '/autoload_packages.php</info>', true );
106
107
		$jetpackAutoloaderDir = $vendorPath . '/jetpack-autoloader';
108
		$filesystem->ensureDirectoryExists( $jetpackAutoloaderDir );
109
		file_put_contents( $jetpackAutoloaderDir . '/autoload_functions.php', $this->getAutoloadPackageFile( 'functions.php', $suffix ) );
110
		$this->io->writeError( '<info>Generated ' . $jetpackAutoloaderDir . '/jetpack-autoloader/autoload_functions.php</info>', true );
111
112
		file_put_contents( $vendorPath . '/class-autoloader-handler.php', $this->getAutoloadPackageFile( 'class-autoloader-handler.php', $suffix ) );
113
		$this->io->writeError( '<info>Generated ' . $vendorPath . '/class-autoloader-handler.php</info>', true );
114
115
		file_put_contents( $vendorPath . '/class-classes-handler.php', $this->getAutoloadPackageFile( 'class-classes-handler.php', $suffix ) );
116
		$this->io->writeError( '<info>Generated ' . $vendorPath . '/class-classes-handler.php</info>', true );
117
118
		file_put_contents( $vendorPath . '/class-files-handler.php', $this->getAutoloadPackageFile( 'class-files-handler.php', $suffix ) );
119
		$this->io->writeError( '<info>Generated ' . $vendorPath . '/class-files-handler.php</info>', true );
120
121
		file_put_contents( $vendorPath . '/class-plugins-handler.php', $this->getAutoloadPackageFile( 'class-plugins-handler.php', $suffix ) );
122
		$this->io->writeError( '<info>Generated ' . $vendorPath . '/class-plugins-handler.php</info>', true );
123
124
		file_put_contents( $vendorPath . '/class-version-selector.php', $this->getAutoloadPackageFile( 'class-version-selector.php', $suffix ) );
125
		$this->io->writeError( '<info>Generated ' . $vendorPath . '/class-version-selector.php</info>', true );
126
	}
127
128
	/**
129
	 * This function differs from the composer parseAutoloadsType in that beside returning the path.
130
	 * It also return the path and the version of a package.
131
	 *
132
	 * Currently supports only psr-4 and clasmap parsing.
133
	 *
134
	 * @param array            $packageMap Map of all the packages.
135
	 * @param string           $type Type of autoloader to use, currently not used, since we only support psr-4.
136
	 * @param PackageInterface $mainPackage Instance of the Package Object.
137
	 *
138
	 * @return array
139
	 */
140
	protected function parseAutoloadsType( array $packageMap, $type, PackageInterface $mainPackage ) {
141
		$autoloads = array();
142
143
		if ( 'psr-4' !== $type && 'classmap' !== $type && 'files' !== $type ) {
144
			return parent::parseAutoloadsType( $packageMap, $type, $mainPackage );
145
		}
146
147
		foreach ( $packageMap as $item ) {
148
			list($package, $installPath) = $item;
149
			$autoload                    = $package->getAutoload();
150
151
			if ( $package === $mainPackage ) {
152
				$autoload = array_merge_recursive( $autoload, $package->getDevAutoload() );
153
			}
154
155
			if ( null !== $package->getTargetDir() && $package !== $mainPackage ) {
156
				$installPath = substr( $installPath, 0, -strlen( '/' . $package->getTargetDir() ) );
157
			}
158
159 View Code Duplication
			if ( 'psr-4' === $type && isset( $autoload['psr-4'] ) && is_array( $autoload['psr-4'] ) ) {
160
				foreach ( $autoload['psr-4'] as $namespace => $paths ) {
161
					$paths = is_array( $paths ) ? $paths : array( $paths );
162
					foreach ( $paths as $path ) {
163
						$relativePath              = empty( $installPath ) ? ( empty( $path ) ? '.' : $path ) : $installPath . '/' . $path;
164
						$autoloads[ $namespace ][] = array(
165
							'path'    => $relativePath,
166
							'version' => $package->getVersion(), // Version of the class comes from the package - should we try to parse it?
167
						);
168
					}
169
				}
170
			}
171
172 View Code Duplication
			if ( 'classmap' === $type && isset( $autoload['classmap'] ) && is_array( $autoload['classmap'] ) ) {
173
				foreach ( $autoload['classmap'] as $paths ) {
174
					$paths = is_array( $paths ) ? $paths : array( $paths );
175
					foreach ( $paths as $path ) {
176
						$relativePath = empty( $installPath ) ? ( empty( $path ) ? '.' : $path ) : $installPath . '/' . $path;
177
						$autoloads[]  = array(
178
							'path'    => $relativePath,
179
							'version' => $package->getVersion(), // Version of the class comes from the package - should we try to parse it?
180
						);
181
					}
182
				}
183
			}
184 View Code Duplication
			if ( 'files' === $type && isset( $autoload['files'] ) && is_array( $autoload['files'] ) ) {
185
				foreach ( $autoload['files'] as $file_id => $paths ) {
186
					$paths = is_array( $paths ) ? $paths : array( $paths );
187
					foreach ( $paths as $path ) {
188
						$relativePath = empty( $installPath ) ? ( empty( $path ) ? '.' : $path ) : $installPath . '/' . $path;
189
						$autoloads[ $this->getFileIdentifier( $package, $path ) ] = array(
190
							'path'    => $relativePath,
191
							'version' => $package->getVersion(), // Version of the file comes from the package - should we try to parse it?
192
						);
193
					}
194
				}
195
			}
196
		}
197
198
		return $autoloads;
199
	}
200
201
	/**
202
	 * Take the autoloads array and return the classMap that contains the path and the version for each namespace.
203
	 *
204
	 * @param array      $autoloads Array of autoload settings defined defined by the packages.
205
	 * @param Filesystem $filesystem Filesystem class instance.
206
	 * @param string     $vendorPath Path to the vendor directory.
207
	 * @param string     $basePath Base Path.
208
	 *
209
	 * @return string $classMap
210
	 */
211
	private function getClassMap( array $autoloads, Filesystem $filesystem, $vendorPath, $basePath ) {
212
		$blacklist = null;
213
214
		if ( ! empty( $autoloads['exclude-from-classmap'] ) ) {
215
			$blacklist = '{(' . implode( '|', $autoloads['exclude-from-classmap'] ) . ')}';
216
		}
217
218
		$classmapString = '';
219
220
		// Scan the PSR-4 and classmap directories for class files, and add them to the class map.
221
		foreach ( $autoloads['psr-4'] as $namespace => $packages_info ) {
222
			foreach ( $packages_info as $package ) {
223
				$dir       = $filesystem->normalizePath(
224
					$filesystem->isAbsolutePath( $package['path'] )
225
						? $package['path']
226
						: $basePath . '/' . $package['path']
227
				);
228
				$namespace = empty( $namespace ) ? null : $namespace;
229
				$map       = ClassMapGenerator::createMap( $dir, $blacklist, $this->io, $namespace );
230
231 View Code Duplication
				foreach ( $map as $class => $path ) {
232
					$classCode       = var_export( $class, true );
233
					$pathCode        = $this->getPathCode( $filesystem, $basePath, $vendorPath, $path );
234
					$versionCode     = var_export( $package['version'], true );
235
					$classmapString .= <<<CLASS_CODE
236
	$classCode => array(
237
		'version' => $versionCode,
238
		'path'    => $pathCode
239
	),
240
CLASS_CODE;
241
					$classmapString .= PHP_EOL;
242
				}
243
			}
244
		}
245
246
		foreach ( $autoloads['classmap'] as $package ) {
247
			$dir = $filesystem->normalizePath(
248
				$filesystem->isAbsolutePath( $package['path'] )
249
					? $package['path']
250
					: $basePath . '/' . $package['path']
251
			);
252
			$map = ClassMapGenerator::createMap( $dir, $blacklist, $this->io, null );
253
254 View Code Duplication
			foreach ( $map as $class => $path ) {
255
				$classCode       = var_export( $class, true );
256
				$pathCode        = $this->getPathCode( $filesystem, $basePath, $vendorPath, $path );
257
				$versionCode     = var_export( $package['version'], true );
258
				$classmapString .= <<<CLASS_CODE
259
	$classCode => array(
260
		'version' => $versionCode,
261
		'path'    => $pathCode
262
	),
263
CLASS_CODE;
264
				$classmapString .= PHP_EOL;
265
			}
266
		}
267
268
		return 'array( ' . PHP_EOL . $classmapString . ');' . PHP_EOL;
269
	}
270
271
	/**
272
	 * Generate the PHP that will be used in the autoload_classmap_package.php files.
273
	 *
274
	 * @param string $classMap class map array string that is to be written out to the file.
275
	 *
276
	 * @return string
277
	 */
278
	private function getAutoloadClassmapPackagesFile( $classMap ) {
279
280
		return <<<INCLUDE_CLASSMAP
281
<?php
282
283
// This file `autoload_classmap_packages.php` was auto generated by automattic/jetpack-autoloader.
284
285
\$vendorDir = dirname(__DIR__);
286
\$baseDir   = dirname(\$vendorDir);
287
288
return $classMap
289
290
INCLUDE_CLASSMAP;
291
	}
292
293
	/**
294
	 * Take the autoloads array and return the fileMap that contains the path and the version for each namespace.
295
	 *
296
	 * @param array      $autoloads Array of autoload settings defined defined by the packages.
297
	 * @param Filesystem $filesystem Filesystem class instance.
298
	 * @param string     $vendorPath Path to the vendor directory.
299
	 * @param string     $basePath Base Path.
300
	 *
301
	 * @return string $fileMap
302
	 */
303
	private function getFileMap( array $autoloads, Filesystem $filesystem, $vendorPath, $basePath ) {
304
		$fileMapString = '';
305 View Code Duplication
		foreach ( $autoloads['files'] as $file_id => $package ) {
306
			$key            = var_export( $file_id, true );
307
			$pathCode       = $this->getPathCode( $filesystem, $basePath, $vendorPath, $package['path'] );
308
			$versionCode    = var_export( $package['version'], true );
309
			$fileMapString .= <<<FILE_CODE
310
	$key => array(
311
		'version' => $versionCode,
312
		'path'    => $pathCode
313
	),
314
FILE_CODE;
315
			$fileMapString .= PHP_EOL;
316
		}
317
318
		return 'array( ' . PHP_EOL . $fileMapString . ');' . PHP_EOL;
319
	}
320
321
	/**
322
	 * Generate the PHP that will be used in the autoload_files_package.php files.
323
	 *
324
	 * @param string $filesMap files array as string that is to be written out to the file.
325
	 *
326
	 * @return string
327
	 */
328
	private function getAutoloadFilesPackagesFile( $filesMap ) {
329
330
		return <<<INCLUDE_FILEMAP
331
<?php
332
333
// This file `autoload_files_packages.php` was auto generated by automattic/jetpack-autoloader.
334
335
\$vendorDir = dirname(__DIR__);
336
\$baseDir   = dirname(\$vendorDir);
337
338
return $filesMap
339
340
INCLUDE_FILEMAP;
341
	}
342
343
	/**
344
	 * Generate the PHP that will be used in the autoload_packages.php files.
345
	 *
346
	 * @param String $filename a file to prepare.
347
	 * @param String $suffix   Unique suffix used in the namespace.
348
	 *
349
	 * @return string
350
	 */
351
	private function getAutoloadPackageFile( $filename, $suffix ) {
352
		$header  = self::COMMENT;
353
		$header .= PHP_EOL;
354
		$header .= 'namespace Automattic\Jetpack\Autoloader\jp' . $suffix . ';';
355
		$header .= PHP_EOL . PHP_EOL;
356
357
		$sourceLoader  = fopen( __DIR__ . '/' . $filename, 'r' );
358
		$file_contents = stream_get_contents( $sourceLoader );
359
		return str_replace(
360
			'/* HEADER */',
361
			$header,
362
			$file_contents
363
		);
364
	}
365
}
366