Completed
Push — instant-search-master ( e67c60...e24956 )
by
unknown
11:28 queued 04:53
created

AutoloadGenerator   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 318
Duplicated Lines 22.64 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
dl 72
loc 318
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
A dump() 0 35 1
F parseAutoloadsType() 36 60 32
B getClassMap() 24 59 10
A getAutoloadClassmapPackagesFile() 0 14 1
A getFileMap() 12 17 2
A getAutoloadFilesPackagesFile() 0 14 1
A getAutoloadPackageFile() 0 37 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
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.NotHyphenatedLowercase
15
// phpcs:disable WordPress.Files.FileName.InvalidClassFileName
16
// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_var_export
17
// phpcs:disable WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents
18
// phpcs:disable WordPress.WP.AlternativeFunctions.file_system_read_fopen
19
// phpcs:disable WordPress.WP.AlternativeFunctions.file_system_read_fwrite
20
// phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
21
// phpcs:disable WordPress.NamingConventions.ValidVariableName.InterpolatedVariableNotSnakeCase
22
// phpcs:disable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
23
// phpcs:disable WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase
24
25
26
namespace Automattic\Jetpack\Autoloader;
27
28
use Composer\Autoload\AutoloadGenerator as BaseGenerator;
29
use Composer\Autoload\ClassMapGenerator;
30
use Composer\Config;
31
use Composer\Installer\InstallationManager;
32
use Composer\IO\IOInterface;
33
use Composer\Package\PackageInterface;
34
use Composer\Repository\InstalledRepositoryInterface;
35
use Composer\Util\Filesystem;
36
37
/**
38
 * Class AutoloadGenerator.
39
 */
40
class AutoloadGenerator extends BaseGenerator {
41
42
	/**
43
	 * Instantiate an AutoloadGenerator object.
44
	 *
45
	 * @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...
46
	 */
47
	public function __construct( IOInterface $io = null ) {
48
		$this->io = $io;
49
	}
50
51
	/**
52
	 * Dump the autoloader.
53
	 *
54
	 * @param Config                       $config Config object.
55
	 * @param InstalledRepositoryInterface $localRepo Installed Reposetories object.
56
	 * @param PackageInterface             $mainPackage Main Package object.
57
	 * @param InstallationManager          $installationManager Manager for installing packages.
58
	 * @param string                       $targetDir Path to the current target directory.
59
	 * @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...
60
	 * @param string                       $suffix The autoloader suffix, ignored since we want our autoloader to only be included once.
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...
61
	 */
62
	public function dump(
63
		Config $config,
64
		InstalledRepositoryInterface $localRepo,
65
		PackageInterface $mainPackage,
66
		InstallationManager $installationManager,
67
		$targetDir,
68
		$scanPsr0Packages = null, // Not used we always optimize.
69
		$suffix = null
70
	) {
71
72
		$filesystem = new Filesystem();
73
		$filesystem->ensureDirectoryExists( $config->get( 'vendor-dir' ) );
74
75
		$basePath   = $filesystem->normalizePath( realpath( getcwd() ) );
76
		$vendorPath = $filesystem->normalizePath( realpath( $config->get( 'vendor-dir' ) ) );
77
		$targetDir  = $vendorPath . '/' . $targetDir;
78
		$filesystem->ensureDirectoryExists( $targetDir );
79
80
		$packageMap = $this->buildPackageMap( $installationManager, $mainPackage, $localRepo->getCanonicalPackages() );
81
		$autoloads  = $this->parseAutoloads( $packageMap, $mainPackage );
82
83
		$classMap = $this->getClassMap( $autoloads, $filesystem, $vendorPath, $basePath );
84
		$fileMap  = $this->getFileMap( $autoloads, $filesystem, $vendorPath, $basePath );
85
86
		// Generate the files.
87
		file_put_contents( $targetDir . '/autoload_classmap_package.php', $this->getAutoloadClassmapPackagesFile( $classMap ) );
88
		$this->io->writeError( '<info>Generated ' . $targetDir . '/autoload_classmap_package.php</info>', true );
89
90
		file_put_contents( $targetDir . '/autoload_files_package.php', $this->getAutoloadFilesPackagesFile( $fileMap ) );
91
		$this->io->writeError( '<info>Generated ' . $targetDir . '/autoload_files_package.php</info>', true );
92
93
		file_put_contents( $vendorPath . '/autoload_packages.php', $this->getAutoloadPackageFile( $suffix ) );
94
		$this->io->writeError( '<info>Generated ' . $vendorPath . '/autoload_packages.php</info>', true );
95
96
	}
97
98
	/**
99
	 * This function differs from the composer parseAutoloadsType in that beside returning the path.
100
	 * It also return the path and the version of a package.
101
	 *
102
	 * Currently supports only psr-4 and clasmap parsing.
103
	 *
104
	 * @param array            $packageMap Map of all the packages.
105
	 * @param string           $type Type of autoloader to use, currently not used, since we only support psr-4.
106
	 * @param PackageInterface $mainPackage Instance of the Package Object.
107
	 *
108
	 * @return array
109
	 */
110
	protected function parseAutoloadsType( array $packageMap, $type, PackageInterface $mainPackage ) {
111
		$autoloads = array();
112
113
		if ( 'psr-4' !== $type && 'classmap' !== $type && 'files' !== $type ) {
114
			return parent::parseAutoloadsType( $packageMap, $type, $mainPackage );
115
		}
116
117
		foreach ( $packageMap as $item ) {
118
			list($package, $installPath) = $item;
119
			$autoload                    = $package->getAutoload();
120
121
			if ( $package === $mainPackage ) {
122
				$autoload = array_merge_recursive( $autoload, $package->getDevAutoload() );
123
			}
124
125
			if ( null !== $package->getTargetDir() && $package !== $mainPackage ) {
126
				$installPath = substr( $installPath, 0, -strlen( '/' . $package->getTargetDir() ) );
127
			}
128
129 View Code Duplication
			if ( 'psr-4' === $type && isset( $autoload['psr-4'] ) && is_array( $autoload['psr-4'] ) ) {
130
				foreach ( $autoload['psr-4'] as $namespace => $paths ) {
131
					$paths = is_array( $paths ) ? $paths : array( $paths );
132
					foreach ( $paths as $path ) {
133
						$relativePath              = empty( $installPath ) ? ( empty( $path ) ? '.' : $path ) : $installPath . '/' . $path;
134
						$autoloads[ $namespace ][] = array(
135
							'path'    => $relativePath,
136
							'version' => $package->getVersion(), // Version of the class comes from the package - should we try to parse it?
137
						);
138
					}
139
				}
140
			}
141
142 View Code Duplication
			if ( 'classmap' === $type && isset( $autoload['classmap'] ) && is_array( $autoload['classmap'] ) ) {
143
				foreach ( $autoload['classmap'] as $paths ) {
144
					$paths = is_array( $paths ) ? $paths : array( $paths );
145
					foreach ( $paths as $path ) {
146
						$relativePath = empty( $installPath ) ? ( empty( $path ) ? '.' : $path ) : $installPath . '/' . $path;
147
						$autoloads[]  = array(
148
							'path'    => $relativePath,
149
							'version' => $package->getVersion(), // Version of the class comes from the package - should we try to parse it?
150
						);
151
					}
152
				}
153
			}
154 View Code Duplication
			if ( 'files' === $type && isset( $autoload['files'] ) && is_array( $autoload['files'] ) ) {
155
				foreach ( $autoload['files'] as $file_id => $paths ) {
156
					$paths = is_array( $paths ) ? $paths : array( $paths );
157
					foreach ( $paths as $path ) {
158
						$relativePath = empty( $installPath ) ? ( empty( $path ) ? '.' : $path ) : $installPath . '/' . $path;
159
						$autoloads[ $this->getFileIdentifier( $package, $path ) ]  = array(
160
							'path'    => $relativePath,
161
							'version' => $package->getVersion(), // Version of the file comes from the package - should we try to parse it?
162
						);
163
					}
164
				}
165
			}
166
		}
167
168
		return $autoloads;
169
	}
170
171
	/**
172
	 * Take the autoloads array and return the classMap that contains the path and the version for each namespace.
173
	 *
174
	 * @param array      $autoloads Array of autoload settings defined defined by the packages.
175
	 * @param Filesystem $filesystem Filesystem class instance.
176
	 * @param string     $vendorPath Path to the vendor directory.
177
	 * @param string     $basePath Base Path.
178
	 *
179
	 * @return string $classMap
180
	 */
181
	private function getClassMap( array $autoloads, Filesystem $filesystem, $vendorPath, $basePath ) {
182
		$blacklist = null;
183
184
		if ( ! empty( $autoloads['exclude-from-classmap'] ) ) {
185
			$blacklist = '{(' . implode( '|', $autoloads['exclude-from-classmap'] ) . ')}';
186
		}
187
188
		$classmapString = '';
189
190
		// Scan the PSR-4 and classmap directories for class files, and add them to the class map.
191
		foreach ( $autoloads['psr-4'] as $namespace => $packages_info ) {
192
			foreach ( $packages_info as $package ) {
193
				$dir       = $filesystem->normalizePath(
194
					$filesystem->isAbsolutePath( $package['path'] )
195
						? $package['path']
196
						: $basePath . '/' . $package['path']
197
				);
198
				$namespace = empty( $namespace ) ? null : $namespace;
199
				$map       = ClassMapGenerator::createMap( $dir, $blacklist, $this->io, $namespace );
200
201 View Code Duplication
				foreach ( $map as $class => $path ) {
202
					$classCode       = var_export( $class, true );
203
					$pathCode        = $this->getPathCode( $filesystem, $basePath, $vendorPath, $path );
204
					$versionCode     = var_export( $package['version'], true );
205
					$classmapString .= <<<CLASS_CODE
206
	$classCode => array(
207
		'version' => $versionCode,
208
		'path'    => $pathCode
209
	),
210
CLASS_CODE;
211
					$classmapString .= PHP_EOL;
212
				}
213
			}
214
		}
215
216
		foreach ( $autoloads['classmap'] as $package ) {
217
			$dir = $filesystem->normalizePath(
218
				$filesystem->isAbsolutePath( $package['path'] )
219
					? $package['path']
220
					: $basePath . '/' . $package['path']
221
			);
222
			$map = ClassMapGenerator::createMap( $dir, $blacklist, $this->io, null );
223
224 View Code Duplication
			foreach ( $map as $class => $path ) {
225
				$classCode       = var_export( $class, true );
226
				$pathCode        = $this->getPathCode( $filesystem, $basePath, $vendorPath, $path );
227
				$versionCode     = var_export( $package['version'], true );
228
				$classmapString .= <<<CLASS_CODE
229
	$classCode => array(
230
		'version' => $versionCode,
231
		'path'    => $pathCode
232
	),
233
CLASS_CODE;
234
				$classmapString .= PHP_EOL;
235
			}
236
		}
237
238
		return 'array( ' . PHP_EOL . $classmapString . ');' . PHP_EOL;
239
	}
240
241
	/**
242
	 * Generate the PHP that will be used in the autoload_classmap_package.php files.
243
	 *
244
	 * @param string $classMap class map array string that is to be written out to the file.
245
	 *
246
	 * @return string
247
	 */
248
	private function getAutoloadClassmapPackagesFile( $classMap ) {
249
250
		return <<<INCLUDE_CLASSMAP
251
<?php
252
253
// This file `autoload_classmap_packages.php` was auto generated by automattic/jetpack-autoloader.
254
255
\$vendorDir = dirname(__DIR__);
256
\$baseDir   = dirname(\$vendorDir);
257
258
return $classMap
259
260
INCLUDE_CLASSMAP;
261
	}
262
263
	/**
264
	 * Take the autoloads array and return the fileMap that contains the path and the version for each namespace.
265
	 *
266
	 * @param array      $autoloads Array of autoload settings defined defined by the packages.
267
	 * @param Filesystem $filesystem Filesystem class instance.
268
	 * @param string     $vendorPath Path to the vendor directory.
269
	 * @param string     $basePath Base Path.
270
	 *
271
	 * @return string $fileMap
272
	 */
273
	private function getFileMap( array $autoloads, Filesystem $filesystem, $vendorPath, $basePath ) {
274
		$fileMapString = '';
275 View Code Duplication
		foreach ( $autoloads['files'] as $file_id => $package ) {
276
			$key            = var_export( $file_id, true );
277
			$pathCode       = $this->getPathCode( $filesystem, $basePath, $vendorPath, $package['path'] );
278
			$versionCode    = var_export( $package['version'], true );
279
			$fileMapString .= <<<FILE_CODE
280
	$key => array(
281
		'version' => $versionCode,
282
		'path'    => $pathCode
283
	),
284
FILE_CODE;
285
			$fileMapString .= PHP_EOL;
286
		}
287
288
		return 'array( ' . PHP_EOL . $fileMapString . ');' . PHP_EOL;
289
	}
290
291
	/**
292
	 * Generate the PHP that will be used in the autoload_files_package.php files.
293
	 *
294
	 * @param string $filesMap files array as string that is to be written out to the file.
295
	 *
296
	 * @return string
297
	 */
298
	private function getAutoloadFilesPackagesFile( $filesMap ) {
299
300
		return <<<INCLUDE_FILEMAP
301
<?php
302
303
// This file `autoload_files_packages.php` was auto generated by automattic/jetpack-autoloader.
304
305
\$vendorDir = dirname(__DIR__);
306
\$baseDir   = dirname(\$vendorDir);
307
308
return $filesMap
309
310
INCLUDE_FILEMAP;
311
	}
312
313
	/**
314
	 * Generate the PHP that will be used in the autoload_packages.php files.
315
	 *
316
	 * @param string $suffix  Unique suffix added to the jetpack_enqueue_packages function.
317
	 *
318
	 * @return string
319
	 */
320
	private function getAutoloadPackageFile( $suffix ) {
321
		$sourceLoader   = fopen( __DIR__ . '/autoload.php', 'r' );
322
		$file_contents  = stream_get_contents( $sourceLoader );
323
		$file_contents .= <<<INCLUDE_FILES
324
/**
325
 * Prepare all the classes for autoloading.
326
 */
327
function enqueue_packages_$suffix() {
328
	\$class_map = require_once dirname( __FILE__ ) . '/composer/autoload_classmap_package.php';
329
	foreach ( \$class_map as \$class_name => \$class_info ) {
330
		enqueue_package_class( \$class_name, \$class_info['version'], \$class_info['path'] );
331
	}
332
333
	\$autoload_file = __DIR__ . '/composer/autoload_files_package.php';
334
335
	\$includeFiles = file_exists( \$autoload_file )
336
		? require \$autoload_file
337
		: array();
338
339
	foreach ( \$includeFiles as \$fileIdentifier => \$file_data ) {
340
		enqueue_package_file( \$fileIdentifier, \$file_data[ 'version' ], \$file_data[ 'path' ] );
341
	}
342
343
	if ( function_exists( 'has_action') && function_exists( 'did_action' ) && ! did_action( 'plugins_loaded' ) && false === has_action( 'plugins_loaded', __NAMESPACE__ . '\\file_loader' ) ) {
344
		// Add action if it has not been added and has not happened yet.
345
		// Priority -10 to load files as early as possible in case plugins try to use them during `plugins_loaded`.
346
		add_action( 'plugins_loaded', __NAMESPACE__ . '\\file_loader', 0, -10 );
347
	} elseif( ! function_exists( 'did_action' ) || did_action( 'plugins_loaded' ) ) {
348
		file_loader(); // Either WordPress is not loaded or plugin is doing it wrong. Either way we'll load the files so nothing breaks.
349
	}
350
}
351
enqueue_packages_$suffix();
352
353
INCLUDE_FILES;
354
355
		return $file_contents;
356
	}
357
}
358