Skip to content

Commit

Permalink
Autoloader: refactor functions.php (#15111)
Browse files Browse the repository at this point in the history
* Autoloader: refactor functions.php into classes

* Autoloader: update unit tests

Update the unit tests to use the new Classes_Handler and Files_Handler classes.

* Autoloader: update is_current_plugin_active()

Update is_current_plugin_active() to check both active and activating
plugins when determining whether the current plugin is active. The
active and activating plugins can be obtained using the
Plugins_Handler::get_active_plugins() method.

* Added newly generated files to gitignore.

* Autoloader: improve Plugins_Handler

Improve the Plugins_Handler class by:
 - Giving some methods more descriptive names.
 - Adding a $jetpack_autoloader_activating_plugins global variable that
   contains the plugins that are activating via a non-request method.
 - Refactoring a few methods to improve clarity.

* Autoloader: update the autoloader chain after autoloader reset

Add a new function, Autoloader_Handler::update_autoloader_chain(), which
handles updates of the autoloader chain as follows:
 - Registers the latest autoloader function.
 - Moves the v1 autoloader function to the end of the chain.
 - Removes any other v2 autoloader functions. This is needed for situations where
   a plugin activates using a non-request method, so the activating plugin was not
   known when the autoloader first ran. If the activating plugin has an autoloader
   with a later version, that autoloader will be registered and the previously
   registered autoloader function should be removed.

Finally, use the Autoloader_Handler::update_autoloader_chain() method in
set_up_autoloader().

* Autoloader: update update_autoloader_chain()

In Autoloader_Handler::update_autoloader_chain(), check whether the autoloader
object is a string. If not, just continue. We're only looking for Jetpack
autoloader functions, and they're registered as strings.

Co-authored-by: Igor Zinovyev <zinigor@gmail.com>
  • Loading branch information
2 people authored and kraftbj committed Jun 22, 2020
1 parent 75ed0be commit dd1a3e3
Show file tree
Hide file tree
Showing 10 changed files with 532 additions and 300 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,7 @@ _inc/jetpack-strings.php
/vendor/autoload.php
/vendor/autoload_packages.php
/vendor/autoload_functions.php
/vendor/class-autoloader-handler.php
/vendor/class-classes-handler.php
/vendor/class-files-handler.php
/vendor/class-plugins-handler.php
14 changes: 13 additions & 1 deletion packages/autoloader/src/AutoloadGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@ public function dump(

file_put_contents( $vendorPath . '/autoload_functions.php', $this->getAutoloadPackageFile( 'functions.php', $suffix ) );
$this->io->writeError( '<info>Generated ' . $vendorPath . '/autoload_functions.php</info>', true );

file_put_contents( $vendorPath . '/class-autoloader-handler.php', $this->getAutoloadPackageFile( 'class-autoloader-handler.php', $suffix ) );
$this->io->writeError( '<info>Generated ' . $vendorPath . '/class-autoloader-handler.php</info>', true );

file_put_contents( $vendorPath . '/class-classes-handler.php', $this->getAutoloadPackageFile( 'class-classes-handler.php', $suffix ) );
$this->io->writeError( '<info>Generated ' . $vendorPath . '/class-classes-handler.php</info>', true );

file_put_contents( $vendorPath . '/class-files-handler.php', $this->getAutoloadPackageFile( 'class-files-handler.php', $suffix ) );
$this->io->writeError( '<info>Generated ' . $vendorPath . '/class-files-handler.php</info>', true );

file_put_contents( $vendorPath . '/class-plugins-handler.php', $this->getAutoloadPackageFile( 'class-plugins-handler.php', $suffix ) );
$this->io->writeError( '<info>Generated ' . $vendorPath . '/class-plugins-handler.php</info>', true );
}

/**
Expand Down Expand Up @@ -330,7 +342,7 @@ private function getAutoloadFilesPackagesFile( $filesMap ) {
*/
private function getAutoloadPackageFile( $filename, $suffix ) {
$header = self::COMMENT;
$header .= PHP_EOL . PHP_EOL;
$header .= PHP_EOL;
$header .= 'namespace Automattic\Jetpack\Autoloader\jp' . $suffix . ';';
$header .= PHP_EOL . PHP_EOL;

Expand Down
120 changes: 120 additions & 0 deletions packages/autoloader/src/class-autoloader-handler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php
/* HEADER */ // phpcs:ignore

/**
* This class selects the package version for the autoloader.
*/
class Autoloader_Handler {

// The name of the autoloader function registered by v1.* autoloaders.
const V1_AUTOLOADER_NAME = 'Automattic\Jetpack\Autoloader\autoloader';

/*
* The autoloader function for v2.* autoloaders is named __NAMESPACE__ . \autoloader.
* The namespace is defined in AutoloadGenerator as
* 'Automattic\Jetpack\Autoloader\jp' plus a unique suffix.
*/
const V2_AUTOLOADER_BASE = 'Automattic\Jetpack\Autoloader\jp';

/**
* The Plugins_Handler object.
*
* @var Plugins_Handler
*/
private $plugins_handler = null;

/**
* The constructor.
*
* @param Plugins_Handler $plugins_handler The plugins_handler object.
*/
public function __construct( $plugins_handler ) {
$this->plugins_handler = $plugins_handler;
}

/**
* Finds the latest installed autoloader.
*/
public function find_latest_autoloader() {
global $jetpack_autoloader_latest_version;

$current_autoloader_path = trailingslashit( dirname( __FILE__ ) ) . 'autoload_packages.php';

$selected_autoloader_version = null;
$selected_autoloader_path = null;

$active_plugins = $this->plugins_handler->get_all_active_plugins();

foreach ( $active_plugins as $plugin ) {
$plugin_path = plugin_dir_path( trailingslashit( WP_PLUGIN_DIR ) . $plugin );
$classmap_path = trailingslashit( $plugin_path ) . 'vendor/composer/jetpack_autoload_classmap.php';

if ( file_exists( $classmap_path ) ) {
$packages = require $classmap_path;

$compare_version = $packages['Automattic\\Jetpack\\Autoloader\\AutoloadGenerator']['version'];
$compare_path = trailingslashit( $plugin_path ) . 'vendor/autoload_packages.php';

// TODO: This comparison needs to properly handle dev versions.
if ( version_compare( $selected_autoloader_version, $compare_version, '<' ) ) {
$selected_autoloader_version = $compare_version;
$selected_autoloader_path = $compare_path;
}
}
}

$jetpack_autoloader_latest_version = $selected_autoloader_version;
if ( $current_autoloader_path !== $selected_autoloader_path ) {
require $selected_autoloader_path;
}
}

/**
* Get this autoloader's package version.
*
* @return String The autoloader's package version.
*/
public function get_current_autoloader_version() {
$classmap_file = trailingslashit( dirname( __FILE__ ) ) . 'composer/jetpack_autoload_classmap.php';
$autoloader_packages = require $classmap_file;

return $autoloader_packages['Automattic\\Jetpack\\Autoloader\\AutoloadGenerator']['version'];
}


/**
* Updates the spl autoloader chain:
* - Registers this namespace's autoloader function.
* - If a v1 autoloader function is registered, moves it to the end of the chain.
* - Removes any other v2 autoloader functions that have already been registered. This
* can occur when the autoloader is being reset by an activating plugin.
*/
public function update_autoloader_chain() {
spl_autoload_register( __NAMESPACE__ . '\autoloader' );

$autoload_chain = spl_autoload_functions();

foreach ( $autoload_chain as $autoloader ) {
if ( ! is_string( $autoloader ) ) {
/*
* The Jetpack Autoloader functions are registered as strings, so
* just continue if $autoloader isn't a string.
*/
continue;
}

if ( self::V1_AUTOLOADER_NAME === $autoloader ) {
// Move the v1.* autoloader function to the end of the spl autoloader chain.
spl_autoload_unregister( $autoloader );
spl_autoload_register( $autoloader );

} elseif (
self::V2_AUTOLOADER_BASE === substr( $autoloader, 0, strlen( self::V2_AUTOLOADER_BASE ) )
&& __NAMESPACE__ !== substr( $autoloader, 0, strlen( __NAMESPACE__ ) )
) {
// Unregister any other v2.* autoloader functions if they're in the chain.
spl_autoload_unregister( $autoloader );
}
}
}
}
86 changes: 86 additions & 0 deletions packages/autoloader/src/class-classes-handler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php
/* HEADER */ // phpcs:ignore

/**
* This class selects the package versions for the package classes.
*/
class Classes_Handler {

/**
* The Plugins_Handler object.
*
* @var Plugins_Handler
*/
private $plugins_handler = null;

/**
* The constructor.
*
* @param Plugins_Handler $plugins_handler The plugins_handler object.
*/
public function __construct( $plugins_handler ) {
$this->plugins_handler = $plugins_handler;
}

/**
* Adds the version of a package to the $jetpack_packages_classmap global
* array so that the autoloader is able to find it.
*
* @param string $class_name Name of the class that you want to autoload.
* @param string $version Version of the class.
* @param string $path Absolute path to the class so that we can load it.
*/
public function enqueue_package_class( $class_name, $version, $path ) {
global $jetpack_packages_classmap;

if ( ! isset( $jetpack_packages_classmap[ $class_name ] ) ) {
$jetpack_packages_classmap[ $class_name ] = array(
'version' => $version,
'path' => $path,
);

return;
}
// If we have a @dev version set always use that one!
if ( 'dev-' === substr( $jetpack_packages_classmap[ $class_name ]['version'], 0, 4 ) ) {
return;
}

// Always favour the @dev version. Since that version is the same as bleeding edge.
// We need to make sure that we don't do this in production!
if ( 'dev-' === substr( $version, 0, 4 ) ) {
$jetpack_packages_classmap[ $class_name ] = array(
'version' => $version,
'path' => $path,
);

return;
}
// Set the latest version!
if ( version_compare( $jetpack_packages_classmap[ $class_name ]['version'], $version, '<' ) ) {
$jetpack_packages_classmap[ $class_name ] = array(
'version' => $version,
'path' => $path,
);
}
}

/**
* Initializes the classmap.
*/
public function set_class_paths() {
$paths = $this->plugins_handler->get_active_plugins_paths();

foreach ( $paths as $path ) {
if ( is_readable( $path['class'] ) ) {
$class_map = require $path['class'];

if ( is_array( $class_map ) ) {
foreach ( $class_map as $class_name => $class_info ) {
$this->enqueue_package_class( $class_name, $class_info['version'], $class_info['path'] );
}
}
}
}
}
}
100 changes: 100 additions & 0 deletions packages/autoloader/src/class-files-handler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php
/* HEADER */ // phpcs:ignore

/**
* This class selects the package versions for the package files.
*/
class Files_Handler {

/**
* The Plugins_Handler object.
*
* @var Plugins_Handler
*/
private $plugins_handler = null;

/**
* The constructor.
*
* @param Plugins_Handler $plugins_handler The plugins_handler object.
*/
public function __construct( $plugins_handler ) {
$this->plugins_handler = $plugins_handler;
}

/**
* Adds the version of a package file to the $jetpack_packages_filemap global
* array so that we can load the most recent version.
*
* @param string $file_identifier Unique id to file assigned by composer based on package name and filename.
* @param string $version Version of the file.
* @param string $path Absolute path to the file so that we can load it.
*/
public function enqueue_package_file( $file_identifier, $version, $path ) {
global $jetpack_packages_filemap;

if ( ! isset( $jetpack_packages_filemap[ $file_identifier ] ) ) {
$jetpack_packages_filemap[ $file_identifier ] = array(
'version' => $version,
'path' => $path,
);

return;
}
// If we have a @dev version set always use that one!
if ( 'dev-' === substr( $jetpack_packages_filemap[ $file_identifier ]['version'], 0, 4 ) ) {
return;
}

// Always favour the @dev version. Since that version is the same as bleeding edge.
// We need to make sure that we don't do this in production!
if ( 'dev-' === substr( $version, 0, 4 ) ) {
$jetpack_packages_filemap[ $file_identifier ] = array(
'version' => $version,
'path' => $path,
);

return;
}
// Set the latest version!
if ( version_compare( $jetpack_packages_filemap[ $file_identifier ]['version'], $version, '<' ) ) {
$jetpack_packages_filemap[ $file_identifier ] = array(
'version' => $version,
'path' => $path,
);
}
}

/**
* Initializes the filemap.
*/
public function set_file_paths() {
$paths = $this->plugins_handler->get_active_plugins_paths();

foreach ( $paths as $path ) {
if ( is_readable( $path['file'] ) ) {
$file_map = require $path['file'];

if ( is_array( $file_map ) ) {
foreach ( $file_map as $file_identifier => $file_data ) {
$this->enqueue_package_file( $file_identifier, $file_data['version'], $file_data['path'] );
}
}
}
}
}

/**
* Include latest version of all enqueued files.
*/
public function file_loader() {
global $jetpack_packages_filemap;
foreach ( $jetpack_packages_filemap as $file_identifier => $file_data ) {
if ( empty( $GLOBALS['__composer_autoload_files'][ $file_identifier ] ) ) {
require_once $file_data['path'];

$GLOBALS['__composer_autoload_files'][ $file_identifier ] = true;
}
}
}
}
Loading

0 comments on commit dd1a3e3

Please sign in to comment.