Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
92.68% covered (success)
92.68%
38 / 41
80.00% covered (warning)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
Plugin_Locator
92.68% covered (success)
92.68%
38 / 41
80.00% covered (warning)
80.00%
4 / 5
22.19
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 find_current_plugin
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 find_using_option
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 find_using_request_action
84.21% covered (warning)
84.21%
16 / 19
0.00% covered (danger)
0.00%
0 / 1
10.39
 convert_plugins_to_paths
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
6
1<?php
2/* HEADER */ // phpcs:ignore
3
4/**
5 * This class scans the WordPress installation to find active plugins.
6 */
7class Plugin_Locator {
8
9    /**
10     * The path processor for finding plugin paths.
11     *
12     * @var Path_Processor
13     */
14    private $path_processor;
15
16    /**
17     * The constructor.
18     *
19     * @param Path_Processor $path_processor The Path_Processor instance.
20     */
21    public function __construct( $path_processor ) {
22        $this->path_processor = $path_processor;
23    }
24
25    /**
26     * Finds the path to the current plugin.
27     *
28     * @return string $path The path to the current plugin.
29     *
30     * @throws \RuntimeException If the current plugin does not have an autoloader.
31     */
32    public function find_current_plugin() {
33        // Escape from `vendor/__DIR__` to root plugin directory.
34        $plugin_directory = dirname( __DIR__, 2 );
35
36        // Use the path processor to ensure that this is an autoloader we're referencing.
37        $path = $this->path_processor->find_directory_with_autoloader( $plugin_directory, array() );
38        if ( false === $path ) {
39            throw new \RuntimeException( 'Failed to locate plugin ' . $plugin_directory );
40        }
41
42        return $path;
43    }
44
45    /**
46     * Checks a given option for plugin paths.
47     *
48     * @param string $option_name  The option that we want to check for plugin information.
49     * @param bool   $site_option  Indicates whether or not we want to check the site option.
50     *
51     * @return array $plugin_paths The list of absolute paths we've found.
52     */
53    public function find_using_option( $option_name, $site_option = false ) {
54        $raw = $site_option ? get_site_option( $option_name ) : get_option( $option_name );
55        if ( false === $raw ) {
56            return array();
57        }
58
59        return $this->convert_plugins_to_paths( $raw );
60    }
61
62    /**
63     * Checks for plugins in the `action` request parameter.
64     *
65     * @param string[] $allowed_actions The actions that we're allowed to return plugins for.
66     *
67     * @return array $plugin_paths The list of absolute paths we've found.
68     */
69    public function find_using_request_action( $allowed_actions ) {
70        /**
71         * Note: we're not actually checking the nonce here because it's too early
72         * in the execution. The pluggable functions are not yet loaded to give
73         * plugins a chance to plug their versions. Therefore we're doing the bare
74         * minimum: checking whether the nonce exists and it's in the right place.
75         * The request will fail later if the nonce doesn't pass the check.
76         */
77        if ( empty( $_REQUEST['_wpnonce'] ) ) {
78            return array();
79        }
80
81        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated just below.
82        $action = isset( $_REQUEST['action'] ) ? wp_unslash( $_REQUEST['action'] ) : false;
83        if ( ! in_array( $action, $allowed_actions, true ) ) {
84            return array();
85        }
86
87        $plugin_slugs = array();
88        switch ( $action ) {
89            case 'activate':
90            case 'deactivate':
91                if ( empty( $_REQUEST['plugin'] ) ) {
92                    break;
93                }
94
95                // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated by convert_plugins_to_paths.
96                $plugin_slugs[] = wp_unslash( $_REQUEST['plugin'] );
97                break;
98
99            case 'activate-selected':
100            case 'deactivate-selected':
101                if ( empty( $_REQUEST['checked'] ) ) {
102                    break;
103                }
104
105                // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated by convert_plugins_to_paths.
106                $plugin_slugs = wp_unslash( $_REQUEST['checked'] );
107                break;
108        }
109
110        return $this->convert_plugins_to_paths( $plugin_slugs );
111    }
112
113    /**
114     * Given an array of plugin slugs or paths, this will convert them to absolute paths and filter
115     * out the plugins that are not directory plugins. Note that array keys will also be included
116     * if they are plugin paths!
117     *
118     * @param string[] $plugins Plugin paths or slugs to filter.
119     *
120     * @return string[]
121     */
122    private function convert_plugins_to_paths( $plugins ) {
123        if ( ! is_array( $plugins ) || empty( $plugins ) ) {
124            return array();
125        }
126
127        // We're going to look for plugins in the standard directories.
128        $path_constants = array( WP_PLUGIN_DIR, WPMU_PLUGIN_DIR );
129
130        $plugin_paths = array();
131        foreach ( $plugins as $key => $value ) {
132            $path = $this->path_processor->find_directory_with_autoloader( $key, $path_constants );
133            if ( $path ) {
134                $plugin_paths[] = $path;
135            }
136
137            $path = $this->path_processor->find_directory_with_autoloader( $value, $path_constants );
138            if ( $path ) {
139                $plugin_paths[] = $path;
140            }
141        }
142
143        return $plugin_paths;
144    }
145}