Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
26.47% covered (danger)
26.47%
9 / 34
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
WordPress_Updater
26.47% covered (danger)
26.47%
9 / 34
0.00% covered (danger)
0.00%
0 / 4
59.10
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 detect_force_update
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
5.03
 add_update_information
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 convert_to_array
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * @see wp_update_plugins()
4 *
5 * @package brianhenryie/bh-wp-plugin-updater
6 */
7
8namespace BrianHenryIE\WP_Plugin_Updater\WP_Includes;
9
10use BrianHenryIE\WP_Plugin_Updater\API_Interface;
11use BrianHenryIE\WP_Plugin_Updater\Model\Plugin_Update_Interface;
12use BrianHenryIE\WP_Plugin_Updater\Settings_Interface;
13use Psr\Log\LoggerAwareTrait;
14use Psr\Log\LoggerInterface;
15use stdClass;
16
17/**
18 * @phpstan-type Plugin_Update_Array array{}
19 * @phpstan-type Plugin_Data_Array array{}
20 */
21class WordPress_Updater {
22    use LoggerAwareTrait;
23
24    /**
25     * Generally we will not refresh plugin update information synchronously, but when the update_plugins transient is
26     * deleted, we infer that to mean the site admin wants to force a check for updates.
27     */
28    protected bool $force_refresh = false;
29
30    /**
31     * Constructor.
32     *
33     * @param API_Interface      $api
34     * @param Settings_Interface $settings
35     * @param LoggerInterface    $logger A PSR-3 logger.
36     */
37    public function __construct(
38        protected API_Interface $api,
39        protected Settings_Interface $settings,
40        LoggerInterface $logger,
41    ) {
42        $this->setLogger( $logger );
43    }
44
45    /**
46     * Determine should the cached plugin information be used, or should a synchronous request be made.
47     *
48     * When a site admin deleted the `update_plugins` transient, i.e. `wp transient delete update_plugins --network`,
49     * that means they want to force a check for updates. In those cases, i.e. when this plugin does not already have
50     * an entry in the stored value, a synchronous HTTP request will be performed.
51     *
52     * Where the plugin is already in the transient, the value will be updated with the saved information, which
53     * itself is updated with the cron job.
54     *
55     * `wp transient delete update_plugins --network`
56     *
57     * @hooked pre_set_site_transient_update_plugins
58     * @see wp_update_plugins()
59     *
60     * @param false|stdClass $value
61     * @param string         $transient Always 'update_plugins'.
62     *
63     * @return false|stdClass Always the unchanged input value.
64     */
65    public function detect_force_update( $value, string $transient_name ) {
66
67        // Probably only happens on a fresh installation of WordPress.
68        if ( false === $value ) {
69            return $value;
70        }
71
72        // This evaluates to true if the cron job has never run.
73
74        // Do a synchronous refresh if the plugin is not already in the `update_plugins` transient.
75        $force_refresh = ! isset( $value->response[ $this->settings->get_plugin_basename() ] )
76                            && ! isset( $value->no_update[ $this->settings->get_plugin_basename() ] );
77
78        // If we're in the admin area and haven't got plugin update information, schedule an immediate background job.
79        if ( $force_refresh && is_admin() ) {
80            $force_refresh = false;
81            $this->api->schedule_immediate_background_update();
82        }
83        $this->force_refresh = $force_refresh;
84
85        /**
86         * The `pre_set_site_transient_update_plugins` filter gets called twice in {@see wp_update_plugins()}. We don't
87         * need it on the later run.
88         */
89        remove_filter( 'pre_set_site_transient_update_plugins', array( $this, 'detect_force_update' ) );
90
91        return $value;
92    }
93
94    /**
95     * Add the plugin's update information to the `update_plugins` transient. To be used later on plugins.php.
96     *
97     * @hooked update_plugins_{$hostname}
98     * @see wp-includes/update.php:513
99     * @see wp_update_plugins()
100     *
101     * @param false|Plugin_Update_Array $plugin_update_array Should always be false, but there could be another filter added to `update_plugins_{$hostname}`.
102     * @param Plugin_Data_Array         $plugin_data
103     * @param string                    $plugin_file The plugin basename.
104     * @param array                     $locales
105     *
106     * @return false|Plugin_Update_Array
107     */
108    public function add_update_information( $plugin_update_array, $plugin_data, $plugin_file, $locales ) {
109
110        if ( $this->settings->get_plugin_basename() !== $plugin_file ) {
111            return $plugin_update_array;
112        }
113
114        try {
115            /** @var ?Plugin_Update_Interface $plugin_information */
116            $plugin_information = $this->api->get_check_update( $this->force_refresh );
117        } catch ( \BrianHenryIE\WP_Plugin_Updater\Exception\Licence_Does_Not_Exist_Exception $exception ) {
118            $this->logger->debug( 'Licence does not exist no server.' );
119            return $plugin_update_array;
120        }
121
122        return is_null( $plugin_information )
123            ? $plugin_update_array
124            : $this->convert_to_array( $plugin_information );
125    }
126
127    /**
128     * Convert the Plugin_Update_Interface object to an array for use in the `update_plugins` transient.
129     *
130     * Not the most elegant solution, but it's the simplest.
131     *
132     * TODO use serialize / get object vars
133     *
134     * @param Plugin_Update_Interface $plugin_update
135     *
136     * @return Plugin_Update_Array
137     */
138    protected function convert_to_array( Plugin_Update_Interface $plugin_update ): array {
139        return array(
140            'id'           => $plugin_update->get_id(),
141            'slug'         => $plugin_update->get_slug(),
142            'version'      => $plugin_update->get_version(),
143            'url'          => $plugin_update->get_url(),
144            'package'      => $plugin_update->get_package(),
145            'tested'       => $plugin_update->get_tested(),
146            'requires_php' => $plugin_update->get_requires_php(),
147            'autoupdate'   => $plugin_update->get_autoupdate(),
148            'icons'        => $plugin_update->get_icons(),
149            'banners'      => $plugin_update->get_banners(),
150            'banners_rtl'  => $plugin_update->get_banners_rtl(),
151            'translations' => $plugin_update->get_translations(),
152        );
153    }
154}