Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
26.42% covered (danger)
26.42%
14 / 53
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
Latest_Autoloader_Guard
26.42% covered (danger)
26.42%
14 / 53
66.67% covered (warning)
66.67%
2 / 3
364.09
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 should_stop_init
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
6
 check_for_conflicting_autoloaders
4.88% covered (danger)
4.88%
2 / 41
0.00% covered (danger)
0.00%
0 / 1
438.57
1<?php
2/* HEADER */ // phpcs:ignore
3
4/**
5 * This class ensures that we're only executing the latest autoloader.
6 */
7class Latest_Autoloader_Guard {
8
9    /**
10     * The Plugins_Handler instance.
11     *
12     * @var Plugins_Handler
13     */
14    private $plugins_handler;
15
16    /**
17     * The Autoloader_Handler instance.
18     *
19     * @var Autoloader_Handler
20     */
21    private $autoloader_handler;
22
23    /**
24     * The Autoloader_locator instance.
25     *
26     * @var Autoloader_Locator
27     */
28    private $autoloader_locator;
29
30    /**
31     * The constructor.
32     *
33     * @param Plugins_Handler    $plugins_handler    The Plugins_Handler instance.
34     * @param Autoloader_Handler $autoloader_handler The Autoloader_Handler instance.
35     * @param Autoloader_Locator $autoloader_locator The Autoloader_Locator instance.
36     */
37    public function __construct( $plugins_handler, $autoloader_handler, $autoloader_locator ) {
38        $this->plugins_handler    = $plugins_handler;
39        $this->autoloader_handler = $autoloader_handler;
40        $this->autoloader_locator = $autoloader_locator;
41    }
42
43    /**
44     * Indicates whether or not the autoloader should be initialized. Note that this function
45     * has the side-effect of actually loading the latest autoloader in the event that this
46     * is not it.
47     *
48     * @param string   $current_plugin             The current plugin we're checking.
49     * @param string[] $plugins                    The active plugins to check for autoloaders in.
50     * @param bool     $was_included_by_autoloader Indicates whether or not this autoloader was included by another.
51     *
52     * @return bool True if we should stop initialization, otherwise false.
53     */
54    public function should_stop_init( $current_plugin, $plugins, $was_included_by_autoloader ) {
55        global $jetpack_autoloader_latest_version;
56
57        // We need to reset the autoloader when the plugins change because
58        // that means the autoloader was generated with a different list.
59        if ( $this->plugins_handler->have_plugins_changed( $plugins ) ) {
60            $this->autoloader_handler->reset_autoloader();
61        }
62
63        // When the latest autoloader has already been found we don't need to search for it again.
64        // We should take care however because this will also trigger if the autoloader has been
65        // included by an older one.
66        if ( isset( $jetpack_autoloader_latest_version ) && ! $was_included_by_autoloader ) {
67            return true;
68        }
69
70        $latest_plugin = $this->autoloader_locator->find_latest_autoloader( $plugins, $jetpack_autoloader_latest_version );
71        if ( isset( $latest_plugin ) && $latest_plugin !== $current_plugin ) {
72            require $this->autoloader_locator->get_autoloader_path( $latest_plugin );
73            return true;
74        }
75
76        return false;
77    }
78
79    /**
80     * Check for conflicting autoloaders.
81     *
82     * A common source of strange and confusing problems is when another plugin
83     * registers a Composer autoloader at a higher priority that us. If enabled,
84     * check for this problem and warn about it.
85     *
86     * Called from the plugins_loaded hook.
87     *
88     * @since 3.1.0
89     * @return void
90     */
91    public function check_for_conflicting_autoloaders() {
92        if ( ! defined( 'JETPACK_AUTOLOAD_DEBUG_CONFLICTING_LOADERS' ) || ! JETPACK_AUTOLOAD_DEBUG_CONFLICTING_LOADERS ) {
93            return;
94        }
95
96        global $jetpack_autoloader_loader;
97        if ( ! isset( $jetpack_autoloader_loader ) ) {
98            return;
99        }
100        $prefixes = array();
101        foreach ( ( $jetpack_autoloader_loader->get_class_map() ?? array() ) as $classname => $data ) {
102            $parts = explode( '\\', trim( $classname, '\\' ) );
103            array_pop( $parts );
104            while ( $parts ) {
105                $prefixes[ implode( '\\', $parts ) . '\\' ] = true;
106                array_pop( $parts );
107            }
108        }
109        foreach ( ( $jetpack_autoloader_loader->get_psr4_map() ?? array() ) as $prefix => $data ) {
110            $parts = explode( '\\', trim( $prefix, '\\' ) );
111            while ( $parts ) {
112                $prefixes[ implode( '\\', $parts ) . '\\' ] = true;
113                array_pop( $parts );
114            }
115        }
116
117        $autoload_chain = spl_autoload_functions();
118        if ( ! $autoload_chain ) {
119            return;
120        }
121
122        foreach ( $autoload_chain as $autoloader ) {
123            // No need to check anything after us.
124            if ( is_array( $autoloader ) && is_string( $autoloader[0] ) && substr( $autoloader[0], 0, strlen( __NAMESPACE__ ) + 1 ) === __NAMESPACE__ . '\\' ) {
125                break;
126            }
127
128            // We can check Composer autoloaders easily enough.
129            if ( is_array( $autoloader ) && $autoloader[0] instanceof \Composer\Autoload\ClassLoader && is_callable( array( $autoloader[0], 'getPrefixesPsr4' ) ) ) {
130                $composer_autoloader = $autoloader[0];
131                foreach ( $composer_autoloader->getClassMap() as $classname => $path ) {
132                    if ( $jetpack_autoloader_loader->find_class_file( $classname ) ) {
133                        $msg = "A Composer autoloader is registered with a higher priority than the Jetpack Autoloader and would also handle some of the classes we handle (e.g. $classname => $path). This may cause strange and confusing problems.";
134                        wp_trigger_error( '', $msg );
135                        continue 2;
136                    }
137                }
138                foreach ( $composer_autoloader->getPrefixesPsr4() as $prefix => $paths ) {
139                    if ( isset( $prefixes[ $prefix ] ) ) {
140                        $path = array_pop( $paths );
141                        $msg  = "A Composer autoloader is registered with a higher priority than the Jetpack Autoloader and would also handle some of the namespaces we handle (e.g. $prefix => $path). This may cause strange and confusing problems.";
142                        wp_trigger_error( '', $msg );
143                        continue 2;
144                    }
145                }
146                foreach ( $composer_autoloader->getPrefixes() as $prefix => $paths ) {
147                    if ( isset( $prefixes[ $prefix ] ) ) {
148                        $path = array_pop( $paths );
149                        $msg  = "A Composer autoloader is registered with a higher priority than the Jetpack Autoloader and would also handle some of the namespaces we handle (e.g. $prefix => $path). This may cause strange and confusing problems.";
150                        wp_trigger_error( '', $msg );
151                        continue 2;
152                    }
153                }
154            }
155        }
156    }
157}