Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
Plugins_Handler
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
5 / 5
18
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 get_active_plugins
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
1 / 1
11
 get_cached_plugins
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 cache_plugins
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 have_plugins_changed
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/* HEADER */ // phpcs:ignore
3
4/**
5 * This class handles locating and caching all of the active plugins.
6 */
7class Plugins_Handler {
8    /**
9     * The transient key for plugin paths.
10     */
11    const TRANSIENT_KEY = 'jetpack_autoloader_plugin_paths';
12
13    /**
14     * The locator for finding plugins in different locations.
15     *
16     * @var Plugin_Locator
17     */
18    private $plugin_locator;
19
20    /**
21     * The processor for transforming cached paths.
22     *
23     * @var Path_Processor
24     */
25    private $path_processor;
26
27    /**
28     * The constructor.
29     *
30     * @param Plugin_Locator $plugin_locator The locator for finding active plugins.
31     * @param Path_Processor $path_processor The processor for transforming cached paths.
32     */
33    public function __construct( $plugin_locator, $path_processor ) {
34        $this->plugin_locator = $plugin_locator;
35        $this->path_processor = $path_processor;
36    }
37
38    /**
39     * Gets all of the active plugins we can find.
40     *
41     * @param bool $include_deactivating When true, plugins deactivating this request will be considered active.
42     * @param bool $record_unknown When true, the current plugin will be marked as active and recorded when unknown.
43     *
44     * @return string[]
45     */
46    public function get_active_plugins( $include_deactivating, $record_unknown ) {
47        global $jetpack_autoloader_activating_plugins_paths;
48
49        // We're going to build a unique list of plugins from a few different sources
50        // to find all of our "active" plugins. While we need to return an integer
51        // array, we're going to use an associative array internally to reduce
52        // the amount of time that we're going to spend checking uniqueness
53        // and merging different arrays together to form the output.
54        $active_plugins = array();
55
56        // Make sure that plugins which have activated this request are considered as "active" even though
57        // they probably won't be present in any option.
58        if ( is_array( $jetpack_autoloader_activating_plugins_paths ) ) {
59            foreach ( $jetpack_autoloader_activating_plugins_paths as $path ) {
60                $active_plugins[ $path ] = $path;
61            }
62        }
63
64        // This option contains all of the plugins that have been activated.
65        $plugins = $this->plugin_locator->find_using_option( 'active_plugins' );
66        foreach ( $plugins as $path ) {
67            $active_plugins[ $path ] = $path;
68        }
69
70        // This option contains all of the multisite plugins that have been activated.
71        if ( is_multisite() ) {
72            $plugins = $this->plugin_locator->find_using_option( 'active_sitewide_plugins', true );
73            foreach ( $plugins as $path ) {
74                $active_plugins[ $path ] = $path;
75            }
76        }
77
78        // These actions contain plugins that are being activated/deactivated during this request.
79        $plugins = $this->plugin_locator->find_using_request_action( array( 'activate', 'activate-selected', 'deactivate', 'deactivate-selected' ) );
80        foreach ( $plugins as $path ) {
81            $active_plugins[ $path ] = $path;
82        }
83
84        // When the current plugin isn't considered "active" there's a problem.
85        // Since we're here, the plugin is active and currently being loaded.
86        // We can support this case (mu-plugins and non-standard activation)
87        // by adding the current plugin to the active list and marking it
88        // as an unknown (activating) plugin. This also has the benefit
89        // of causing a reset because the active plugins list has
90        // been changed since it was saved in the global.
91        $current_plugin = $this->plugin_locator->find_current_plugin();
92        if ( $record_unknown && ! in_array( $current_plugin, $active_plugins, true ) ) {
93            $active_plugins[ $current_plugin ]             = $current_plugin;
94            $jetpack_autoloader_activating_plugins_paths[] = $current_plugin;
95        }
96
97        // When deactivating plugins aren't desired we should entirely remove them from the active list.
98        if ( ! $include_deactivating ) {
99            // These actions contain plugins that are being deactivated during this request.
100            $plugins = $this->plugin_locator->find_using_request_action( array( 'deactivate', 'deactivate-selected' ) );
101            foreach ( $plugins as $path ) {
102                unset( $active_plugins[ $path ] );
103            }
104        }
105
106        // Transform the array so that we don't have to worry about the keys interacting with other array types later.
107        return array_values( $active_plugins );
108    }
109
110    /**
111     * Gets all of the cached plugins if there are any.
112     *
113     * @return string[]
114     */
115    public function get_cached_plugins() {
116        $cached = get_transient( self::TRANSIENT_KEY );
117        if ( ! is_array( $cached ) || empty( $cached ) ) {
118            return array();
119        }
120
121        // We need to expand the tokens to an absolute path for this webserver.
122        return array_map( array( $this->path_processor, 'untokenize_path_constants' ), $cached );
123    }
124
125    /**
126     * Saves the plugin list to the cache.
127     *
128     * @param array $plugins The plugin list to save to the cache.
129     */
130    public function cache_plugins( $plugins ) {
131        // We store the paths in a tokenized form so that that webservers with different absolute paths don't break.
132        $plugins = array_map( array( $this->path_processor, 'tokenize_path_constants' ), $plugins );
133
134        set_transient( self::TRANSIENT_KEY, $plugins );
135    }
136
137    /**
138     * Checks to see whether or not the plugin list given has changed when compared to the
139     * shared `$jetpack_autoloader_cached_plugin_paths` global. This allows us to deal
140     * with cases where the active list may change due to filtering..
141     *
142     * @param string[] $plugins The plugins list to check against the global cache.
143     *
144     * @return bool True if the plugins have changed, otherwise false.
145     */
146    public function have_plugins_changed( $plugins ) {
147        global $jetpack_autoloader_cached_plugin_paths;
148
149        if ( $jetpack_autoloader_cached_plugin_paths !== $plugins ) {
150            $jetpack_autoloader_cached_plugin_paths = $plugins;
151            return true;
152        }
153
154        return false;
155    }
156}