Completed
Push — update/autoloader_check_for_v1 ( acd358...447bca )
by
unknown
07:43
created

get_current_autoloader_version()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
/* HEADER */ // phpcs:ignore
3
4
/**
5
 * This class selects the package version for the autoloader.
6
 */
7
class Autoloader_Handler {
8
9
	// The name of the autoloader function registered by v1.* autoloaders.
10
	const V1_AUTOLOADER_NAME = 'Automattic\Jetpack\Autoloader\autoloader';
11
12
	/*
13
	 * The autoloader function for v2.* autoloaders is named __NAMESPACE__ . \autoloader.
14
	 * The namespace is defined in AutoloadGenerator as
15
	 * 'Automattic\Jetpack\Autoloader\jp' plus a unique suffix.
16
	 */
17
	const V2_AUTOLOADER_BASE = 'Automattic\Jetpack\Autoloader\jp';
18
19
	const AUTOLOAD_GENERATOR_CLASS_NAME = 'Automattic\Jetpack\Autoloader\AutoloadGenerator';
20
21
	/**
22
	 * The Plugins_Handler object.
23
	 *
24
	 * @var Plugins_Handler
25
	 */
26
	private $plugins_handler = null;
27
28
	/**
29
	 * The Version_Selector object.
30
	 *
31
	 * @var Version_Selector
32
	 */
33
	private $version_selector = null;
34
35
	/**
36
	 * The constructor.
37
	 *
38
	 * @param Plugins_Handler  $plugins_handler The Plugins_Handler object.
39
	 * @param Version_Selector $version_selector The Version_Selector object.
40
	 */
41
	public function __construct( $plugins_handler, $version_selector ) {
42
		$this->plugins_handler  = $plugins_handler;
43
		$this->version_selector = $version_selector;
44
	}
45
46
	/**
47
	 * Finds the latest installed autoloader.
48
	 */
49
	public function find_latest_autoloader() {
50
		global $jetpack_autoloader_latest_version;
51
52
		$current_autoloader_path = trailingslashit( dirname( __FILE__ ) ) . 'autoload_packages.php';
53
		$current_autoloader_path = str_replace( '\\', '/', $current_autoloader_path );
54
55
		$selected_autoloader_version = null;
56
		$selected_autoloader_path    = null;
57
58
		$active_plugins_paths = $this->plugins_handler->get_all_active_plugins_paths();
59
60
		foreach ( $active_plugins_paths as $plugin_path ) {
61
			$classmap_path = trailingslashit( $plugin_path ) . 'vendor/composer/jetpack_autoload_classmap.php';
62
63
			if ( file_exists( $classmap_path ) ) {
64
				$packages = require $classmap_path;
65
66
				$compare_version = $packages[ self::AUTOLOAD_GENERATOR_CLASS_NAME ]['version'];
67
				$compare_path    = trailingslashit( $plugin_path ) . 'vendor/autoload_packages.php';
68
69
				if ( $this->version_selector->is_version_update_required( $selected_autoloader_version, $compare_version ) ) {
70
					if ( ! $this->check_for_v1_files( $compare_path ) ) {
71
						$selected_autoloader_version = $compare_version;
72
						$selected_autoloader_path    = $compare_path;
73
					}
74
				}
75
			}
76
		}
77
78
		$jetpack_autoloader_latest_version = $selected_autoloader_version;
79
80
		// $current_autoloader_path is already loaded
81
		if ( $current_autoloader_path !== $selected_autoloader_path ) {
82
			require $selected_autoloader_path;
83
		}
84
	}
85
86
	/**
87
	 * Check the plugins directory for autoloader v1.x files. If the plugin's directory contains
88
	 * v1.x autoloader files, we will ignore this plugins autoloader.
89
	 *
90
	 * This situation can occur if a plugin has a v2.x autoloader and a backup containing a version
91
	 * of the plugin with a v1.x autoloader is restored. The v2.x autolaoder files remain in the
92
	 * plugin directory.
93
	 *
94
	 * @param string $autoload_packages_path The path to the plugin's vendor/autoload_packages.php file.
95
	 *
96
	 * @return boolean True if the plugin directory contains a v1.x autoloader, false otherwise.
97
	 */
98
	private function check_for_v1_files( $autoload_packages_path ) {
99
		$v1_classmap_path = trailingslashit( dirname( $autoload_packages_path ) ) . 'composer/autoload_classmap_package.php';
100
101
		clearstatcache( true, $v1_classmap_path );
102
103
		if ( ! file_exists( $v1_classmap_path ) ) {
104
			return false;
105
		}
106
107
		clearstatcache( true, $autoload_packages_path );
108
109
		/*
110
		 * Use the filesize of autoload_packages.php to determine if it's a v1 or v2 file.
111
		 * The v2.x file is small, less than 1 KB. The v1.x file is much larger, > 6 KB.
112
		 */
113
		if ( 2000 < filesize( $autoload_packages_path ) ) {
114
			// The autoload_package.php file is a v1.x, so try to delete v2.x files.
115
			$vendor_dir = trailingslashit( dirname( $autoload_packages_path ) );
116
117
			$v1_files_to_delete = array(
118
				$vendor_dir . 'autoload_functions.php',
119
				$vendor_dir . 'jetpack-autoloader/autoload_functions.php',
120
				$vendor_dir . 'composer/jetpack_autoload_classmap.php',
121
				$vendor_dir . 'composer/jetpack_autoload_filemap.php',
122
			);
123
124
			foreach ( $v1_files_to_delete as $file ) {
125
				if ( file_exists( $file ) ) {
126
					@unlink( $file ); //phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
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...
127
				}
128
			}
129
130
			return true;
131
		}
132
133
		// Try to delete v1.x classmap file.
134
		@unlink( $v1_classmap_path ); //phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
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...
135
136
		return false;
137
	}
138
139
	/**
140
	 * Get this autoloader's package version.
141
	 *
142
	 * @return String The autoloader's package version.
143
	 */
144
	public function get_current_autoloader_version() {
145
		$classmap_file       = trailingslashit( dirname( __FILE__ ) ) . 'composer/jetpack_autoload_classmap.php';
146
		$autoloader_packages = require $classmap_file;
147
148
		return $autoloader_packages[ self::AUTOLOAD_GENERATOR_CLASS_NAME ]['version'];
149
	}
150
151
152
	/**
153
	 * Updates the spl autoloader chain:
154
	 *  - Registers this namespace's autoloader function.
155
	 *  - If a v1 autoloader function is registered, moves it to the end of the chain.
156
	 *  - Removes any other v2 autoloader functions that have already been registered. This
157
	 *    can occur when the autoloader is being reset by an activating plugin.
158
	 */
159
	public function update_autoloader_chain() {
160
		spl_autoload_register( __NAMESPACE__ . '\autoloader' );
161
162
		$autoload_chain = spl_autoload_functions();
163
164
		foreach ( $autoload_chain as $autoloader ) {
165
			if ( ! is_string( $autoloader ) ) {
166
				/*
167
				 * The Jetpack Autoloader functions are registered as strings, so
168
				 * just continue if $autoloader isn't a string.
169
				 */
170
				continue;
171
			}
172
173
			if ( self::V1_AUTOLOADER_NAME === $autoloader ) {
174
				// Move the v1.* autoloader function to the end of the spl autoloader chain.
175
				spl_autoload_unregister( $autoloader );
176
				spl_autoload_register( $autoloader );
177
178
			} elseif (
179
				self::V2_AUTOLOADER_BASE === substr( $autoloader, 0, strlen( self::V2_AUTOLOADER_BASE ) )
180
				&& __NAMESPACE__ !== substr( $autoloader, 0, strlen( __NAMESPACE__ ) )
181
			) {
182
				// Unregister any other v2.* autoloader functions if they're in the chain.
183
				spl_autoload_unregister( $autoloader );
184
			}
185
		}
186
	}
187
}
188