Aucune description

Prerequisites.php 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. <?php
  2. /**
  3. * Prerequisite handler.
  4. *
  5. * @package PUM\Core
  6. */
  7. /**
  8. * Prerequisite handler.
  9. *
  10. * @version 1.0.0
  11. */
  12. class PUM_Utils_Prerequisites {
  13. /**
  14. * Cache accessible across instances.
  15. *
  16. * @var array
  17. */
  18. public static $cache = [];
  19. /**
  20. * Array of checks to perform.
  21. *
  22. * @var array
  23. */
  24. protected $checks = [];
  25. /**
  26. * Array of detected failures.
  27. *
  28. * @var array
  29. */
  30. protected $failures = [];
  31. /**
  32. * Instantiate prerequisite checker.
  33. *
  34. * @param array $requirements Array of requirements.
  35. */
  36. public function __construct( $requirements = [] ) {
  37. foreach ( $requirements as $arguments ) {
  38. switch ( $arguments['type'] ) {
  39. case 'php':
  40. $this->checks[] = wp_parse_args(
  41. $arguments,
  42. [
  43. 'type' => 'php',
  44. 'version' => '5.6',
  45. ]
  46. );
  47. break;
  48. case 'plugin':
  49. $this->checks[] = wp_parse_args(
  50. $arguments,
  51. [
  52. 'type' => 'plugin',
  53. 'slug' => '',
  54. 'name' => '',
  55. 'version' => '',
  56. 'check_installed' => false,
  57. 'dep_label' => '',
  58. ]
  59. );
  60. break;
  61. default:
  62. break;
  63. }
  64. }
  65. }
  66. /**
  67. * Check requirements.
  68. *
  69. * @param boolean $return_on_fail Whether it should stop processing if one fails.
  70. *
  71. * @return bool
  72. */
  73. public function check( $return_on_fail = false ) {
  74. $end_result = true;
  75. foreach ( $this->checks as $check ) {
  76. $result = $this->check_handler( $check );
  77. if ( false === $result ) {
  78. if ( true === $return_on_fail ) {
  79. return false;
  80. }
  81. $end_result = false;
  82. }
  83. }
  84. return $end_result;
  85. }
  86. /**
  87. * Render notices when appropriate.
  88. */
  89. public function setup_notices() {
  90. add_action( 'admin_notices', [ $this, 'render_notices' ] );
  91. }
  92. /**
  93. * Handle individual checks by mapping them to methods.
  94. *
  95. * @param array $check Requirement check arguments.
  96. *
  97. * @return bool
  98. */
  99. public function check_handler( $check ) {
  100. return method_exists( $this, 'check_' . $check['type'] ) ? $this->{'check_' . $check['type']}( $check ) : false;
  101. }
  102. /**
  103. * Report failure notice to the queue.
  104. *
  105. * @param array $check_args Array of check arguments.
  106. */
  107. public function report_failure( $check_args ) {
  108. $this->failures[] = $check_args;
  109. }
  110. /**
  111. * Get a list of failures.
  112. *
  113. * @return array
  114. */
  115. public function get_failures() {
  116. return $this->failures;
  117. }
  118. /**
  119. * Check PHP version against args.
  120. *
  121. * @param array $check_args Array of args.
  122. *
  123. * @return bool
  124. */
  125. public function check_php( $check_args ) {
  126. if ( false === version_compare( phpversion(), $check_args['version'], '>=' ) ) {
  127. $this->report_failure( $check_args );
  128. return false;
  129. }
  130. return true;
  131. }
  132. /**
  133. * Check plugin requirements.
  134. *
  135. * @param array $check_args Array of args.
  136. *
  137. * @return bool
  138. */
  139. public function check_plugin( $check_args ) {
  140. $active = $this->plugin_is_active( $check_args['slug'] );
  141. /**
  142. * The following checks are performed in this order for performance reasons.
  143. *
  144. * We start with most cached option, to least in hopes of a hit early.
  145. *
  146. * 1. If active and not checking version.
  147. * 2. If active and outdated.
  148. * 3. If not active and installed.
  149. * 4. If not installed
  150. */
  151. if ( true === $active ) {
  152. // If required version is set & plugin is active, check that first.
  153. if ( isset( $check_args['version'] ) ) {
  154. $version = $this->get_plugin_data( $check_args['slug'], 'Version' );
  155. // If its higher than the required version, we can bail now > true.
  156. if ( version_compare( $version, $check_args['version'], '>=' ) ) {
  157. return true;
  158. } else {
  159. // If not updated, report the failure and bail > false.
  160. $this->report_failure(
  161. array_merge(
  162. $check_args,
  163. [
  164. // Report not_updated status.
  165. 'not_updated' => true,
  166. ]
  167. )
  168. );
  169. return false;
  170. }
  171. } else {
  172. // If the plugin is active, with no required version, were done > true.
  173. return true;
  174. }
  175. }
  176. if ( $check_args['check_installed'] ) {
  177. // Check if installed, if so the plugin is not activated.
  178. if ( $check_args['name'] === $this->get_plugin_data( $check_args['slug'], 'Name' ) ) {
  179. $this->report_failure(
  180. array_merge(
  181. $check_args,
  182. [
  183. // Report not_activated status.
  184. 'not_activated' => true,
  185. ]
  186. )
  187. );
  188. } else {
  189. $this->report_failure(
  190. array_merge(
  191. $check_args,
  192. [
  193. // Report not_installed status.
  194. 'not_installed' => true,
  195. ]
  196. )
  197. );
  198. }
  199. }
  200. return false;
  201. }
  202. /**
  203. * Internally cached get_plugin_data/get_file_data wrapper.
  204. *
  205. * @param string $slug Plugins `folder/file.php` slug.
  206. * @param string $header Specific plugin header needed.
  207. * @return mixed
  208. */
  209. private function get_plugin_data( $slug, $header = null ) {
  210. if ( ! isset( static::$cache['get_plugin_data'][ $slug ] ) ) {
  211. $headers = \get_file_data( WP_PLUGIN_DIR . '/' . $slug, [
  212. 'Name' => 'Plugin Name',
  213. 'Version' => 'Version',
  214. ], 'plugin' );
  215. static::$cache['get_plugin_data'][ $slug ] = $headers;
  216. }
  217. $plugin_data = static::$cache['get_plugin_data'][ $slug ];
  218. if ( empty( $header ) ) {
  219. return $plugin_data;
  220. }
  221. return isset( $plugin_data[ $header ] ) ? $plugin_data[ $header ] : null;
  222. }
  223. /**
  224. * Check if plugin is active.
  225. *
  226. * @param string $slug Slug to check for.
  227. *
  228. * @return bool
  229. */
  230. protected function plugin_is_active( $slug ) {
  231. $active_plugins = get_option( 'active_plugins', [] );
  232. return in_array( $slug, $active_plugins, true );
  233. }
  234. /**
  235. * Get php error message.
  236. *
  237. * @param array $failed_check_args Check arguments.
  238. *
  239. * @return string
  240. */
  241. public function get_php_message( $failed_check_args ) {
  242. /* translators: 1. PHP Version */
  243. $message = __( 'This plugin requires <b>PHP %s</b> or higher in order to run.', 'popup-maker' );
  244. return sprintf( $message, $failed_check_args['version'] );
  245. }
  246. /**
  247. * Get plugin error message.
  248. *
  249. * @param array $failed_check_args Get helpful error message.
  250. *
  251. * @return string
  252. */
  253. public function get_plugin_message( $failed_check_args ) {
  254. $slug = $failed_check_args['slug'];
  255. // Without file path.
  256. $short_slug = explode( '/', $slug );
  257. $short_slug = $short_slug[0];
  258. $name = $failed_check_args['name'];
  259. $dep_label = $failed_check_args['dep_label'];
  260. if ( isset( $failed_check_args['not_activated'] ) ) {
  261. $url = esc_url( wp_nonce_url( admin_url( 'plugins.php?action=activate&plugin=' . $slug ), 'activate-plugin_' . $slug ) );
  262. $link = '<a href="' . $url . '">' . __( 'activate it', 'popup-maker' ) . '</a>';
  263. $text = sprintf(
  264. /* translators: 1. Plugin Name, 2. Required Plugin Name, 4. `activate it` link. */
  265. __( 'The plugin "%1$s" requires %2$s! Please %3$s to continue!', 'popup-maker' ),
  266. $dep_label,
  267. '<strong>' . $name . '</strong>',
  268. $link
  269. );
  270. } elseif ( isset( $failed_check_args['not_updated'] ) ) {
  271. $url = esc_url( wp_nonce_url( admin_url( 'update.php?action=upgrade-plugin&plugin=' . $slug ), 'upgrade-plugin_' . $slug ) );
  272. $link = '<a href="' . $url . '">' . __( 'update it', 'popup-maker' ) . '</a>';
  273. $text = sprintf(
  274. /* translators: 1. Plugin Name, 2. Required Plugin Name, 3. Version number, 4. `update it` link. */
  275. __( 'The plugin "%1$s" requires %2$s v%3$s or higher! Please %4$s to continue!', 'popup-maker' ),
  276. $dep_label,
  277. '<strong>' . $name . '</strong>',
  278. '<strong>' . $failed_check_args['version'] . '</strong>',
  279. $link
  280. );
  281. } else {
  282. $url = esc_url( wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=' . $short_slug ), 'install-plugin_' . $short_slug ) );
  283. $link = '<a href="' . $url . '">' . __( 'install it', 'popup-maker' ) . '</a>';
  284. $text = sprintf(
  285. /* translators: 1. Plugin Name, 2. Required Plugin Name, 3. `install it` link. */
  286. __( 'The plugin "%1$s" requires %2$s! Please %3$s to continue!', 'popup-maker' ),
  287. $dep_label,
  288. '<strong>' . $name . '</strong>',
  289. $link
  290. );
  291. }
  292. return $text;
  293. }
  294. /**
  295. * Render needed admin notices.
  296. *
  297. * @return void
  298. */
  299. public function render_notices() {
  300. foreach ( $this->failures as $failure ) {
  301. $class = 'notice notice-error';
  302. $message = method_exists( $this, 'get_' . $failure['type'] . '_message' ) ? $this->{'get_' . $failure['type'] . '_message'}( $failure ) : false;
  303. /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */
  304. echo sprintf( '<div class="%1$s"><p>%2$s</p></div>', esc_attr( $class ), $message );
  305. }
  306. }
  307. }