Completed
Push — update/dna-jetpack-autoloader-... ( 5fc821...2e5b65 )
by Mike
113:09 queued 103:54
created

AutoloadGenerator   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 233
Duplicated Lines 28.33 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
dl 66
loc 233
rs 9.52
c 0
b 0
f 0
wmc 36
lcom 1
cbo 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A dump() 0 31 1
D parseAutoloadsType() 24 48 23
B getClassMap() 42 58 9
A getAutoloadClassmapPackagesFile() 0 14 1
A getAutoloadPackageFile() 0 19 1

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
 * 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
85
		// Generate the files.
86
		file_put_contents( $targetDir . '/autoload_classmap_package.php', $this->getAutoloadClassmapPackagesFile( $classMap ) );
87
		$this->io->writeError( '<info>Generated ' . $targetDir . '/autoload_classmap_package.php</info>', true );
88
89
		file_put_contents( $vendorPath . '/autoload_packages.php', $this->getAutoloadPackageFile( $suffix ) );
90
		$this->io->writeError( '<info>Generated ' . $vendorPath . '/autoload_packages.php</info>', true );
91
92
	}
93
94
	/**
95
	 * This function differs from the composer parseAutoloadsType in that beside returning the path.
96
	 * It also return the path and the version of a package.
97
	 *
98
	 * Currently supports only psr-4 and clasmap parsing.
99
	 *
100
	 * @param array            $packageMap Map of all the packages.
101
	 * @param string           $type Type of autoloader to use, currently not used, since we only support psr-4.
102
	 * @param PackageInterface $mainPackage Instance of the Package Object.
103
	 *
104
	 * @return array
105
	 */
106
	protected function parseAutoloadsType( array $packageMap, $type, PackageInterface $mainPackage ) {
107
		$autoloads = array();
108
109
		if ( 'psr-4' !== $type && 'classmap' !== $type ) {
110
			return parent::parseAutoloadsType( $packageMap, $type, $mainPackage );
111
		}
112
113
		foreach ( $packageMap as $item ) {
114
			list($package, $installPath) = $item;
115
			$autoload                    = $package->getAutoload();
116
117
			if ( $package === $mainPackage ) {
118
				$autoload = array_merge_recursive( $autoload, $package->getDevAutoload() );
119
			}
120
121
			if ( null !== $package->getTargetDir() && $package !== $mainPackage ) {
122
				$installPath = substr( $installPath, 0, -strlen( '/' . $package->getTargetDir() ) );
123
			}
124
125 View Code Duplication
			if ( 'psr-4' == $type && isset( $autoload['psr-4'] ) && is_array( $autoload['psr-4'] ) ) {
126
				foreach ( $autoload['psr-4'] as $namespace => $paths ) {
127
					$paths = is_array( $paths ) ? $paths : array( $paths );
128
					foreach ( $paths as $path ) {
129
						$relativePath              = empty( $installPath ) ? ( empty( $path ) ? '.' : $path ) : $installPath . '/' . $path;
130
						$autoloads[ $namespace ][] = array(
131
							'path'    => $relativePath,
132
							'version' => $package->getVersion(), // Version of the class comes from the package - should we try to parse it?
133
						);
134
					}
135
				}
136
			}
137
138 View Code Duplication
			if ( 'classmap' == $type && isset( $autoload['classmap'] ) && is_array( $autoload['classmap'] ) ) {
139
				foreach ( $autoload['classmap'] as $paths ) {
140
					$paths = is_array( $paths ) ? $paths : array( $paths );
141
					foreach ( $paths as $path ) {
142
						$relativePath              = empty( $installPath ) ? ( empty( $path ) ? '.' : $path ) : $installPath . '/' . $path;
143
						$autoloads[] = array(
144
							'path'    => $relativePath,
145
							'version' => $package->getVersion(), // Version of the class comes from the package - should we try to parse it?
146
						);
147
					}
148
				}
149
			}
150
		}
151
152
		return $autoloads;
153
	}
154
155
	/**
156
	 * Take the autoloads array and return the classMap that contains the path and the version for each namespace.
157
	 *
158
	 * @param array      $autoloads Array of autoload settings defined defined by the packages.
159
	 * @param Filesystem $filesystem Filesystem class instance.
160
	 * @param string     $vendorPath Path to the vendor directory.
161
	 * @param string     $basePath Base Path.
162
	 *
163
	 * @return array $classMap
164
	 */
165
	private function getClassMap( array $autoloads, Filesystem $filesystem, $vendorPath, $basePath ) {
166
		$blacklist = null;
167
168
        if ( ! empty( $autoloads['exclude-from-classmap'] ) ) {
169
            $blacklist = '{(' . implode( '|', $autoloads['exclude-from-classmap'] ) . ')}';
170
		}
171
172
		$classmapString = '';
173
174
		// Scan the PSR-4 and classmap directories for class files, and add them to the class map.
175
		foreach ( $autoloads['psr-4'] as $namespace => $packages_info ) {
176 View Code Duplication
			foreach ( $packages_info as $package ) {
177
				$dir = $filesystem->normalizePath(
178
					$filesystem->isAbsolutePath( $package['path'] )
179
					? $package['path']
180
					: $basePath . '/' . $package['path']
181
				);
182
				$map = ClassMapGenerator::createMap( $dir, $blacklist, $this->io, $namespace );
183
184
				foreach ( $map as $class => $path ) {
185
					$classCode       = var_export( $class, true );
186
					$pathCode        = $this->getPathCode( $filesystem, $basePath, $vendorPath, $path );
187
					$versionCode     = var_export( $package['version'], true );
188
					$classmapString .= <<<CLASS_CODE
189
	$classCode => array(
190
		'version' => $versionCode,
191
		'path'    => $pathCode
192
	),
193
CLASS_CODE;
194
					$classmapString .= PHP_EOL;
195
				}
196
			}
197
		}
198
199 View Code Duplication
		foreach ( $autoloads['classmap'] as $package ) {
200
			$dir = $filesystem->normalizePath(
201
				$filesystem->isAbsolutePath( $package['path'] )
202
				? $package['path']
203
				: $basePath . '/' . $package['path']
204
			);
205
			$map = ClassMapGenerator::createMap( $dir, $blacklist, $this->io, null );
206
207
			foreach ( $map as $class => $path ) {
208
				$classCode       = var_export( $class, true );
209
				$pathCode        = $this->getPathCode( $filesystem, $basePath, $vendorPath, $path );
210
				$versionCode     = var_export( $package['version'], true );
211
				$classmapString .= <<<CLASS_CODE
212
	$classCode => array(
213
		'version' => $versionCode,
214
		'path'    => $pathCode
215
	),
216
CLASS_CODE;
217
				$classmapString .= PHP_EOL;
218
			}
219
		}
220
221
		return 'array( ' . PHP_EOL . $classmapString . ');' . PHP_EOL;
222
	}
223
224
	/**
225
	 * Generate the PHP that will be used in the autoload_classmap_package.php files.
226
	 *
227
	 * @param srting $classMap class map array string that is to be written out to the file.
228
	 *
229
	 * @return string
230
	 */
231
	private function getAutoloadClassmapPackagesFile( $classMap ) {
232
233
		return <<<INCLUDE_CLASSMAP
234
<?php
235
236
// This file `autoload_classmap_packages.php` was auto generated by automattic/jetpack-autoloader.
237
238
\$vendorDir = dirname(__DIR__);
239
\$baseDir   = dirname(\$vendorDir);
240
241
return $classMap
242
243
INCLUDE_CLASSMAP;
244
	}
245
246
	/**
247
	 * Generate the PHP that will be used in the autoload_packages.php files.
248
	 *
249
	 * @param string $suffix  Unique suffix added to the jetpack_enqueue_packages function.
250
	 *
251
	 * @return string
252
	 */
253
	private function getAutoloadPackageFile( $suffix ) {
254
		$sourceLoader   = fopen( __DIR__ . '/autoload.php', 'r' );
255
		$file_contents  = stream_get_contents( $sourceLoader );
256
		$file_contents .= <<<INCLUDE_FILES
257
/**
258
 * Prepare all the classes for autoloading.
259
 */
260
function enqueue_packages_$suffix() {
261
	\$class_map = require_once dirname( __FILE__ ) . '/composer/autoload_classmap_package.php';
262
	foreach ( \$class_map as \$class_name => \$class_info ) {
263
		enqueue_package_class( \$class_name, \$class_info['version'], \$class_info['path'] );
264
	}
265
}
266
enqueue_packages_$suffix();
267
268
INCLUDE_FILES;
269
270
		return $file_contents;
271
	}
272
}
273