PageRenderTime 52ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/php/commands/core.php

https://gitlab.com/Blueprint-Marketing/wp-cli
PHP | 1034 lines | 697 code | 107 blank | 230 comment | 102 complexity | 481f39a353092291836dd91aae6b2275 MD5 | raw file
  1. <?php
  2. use \WP_CLI\Utils;
  3. /**
  4. * Download, install, update and otherwise manage WordPress proper.
  5. *
  6. * @package wp-cli
  7. */
  8. class Core_Command extends WP_CLI_Command {
  9. /**
  10. * Check for update via Version Check API. Returns latest version if there's an update, or empty if no update available.
  11. *
  12. * ## OPTIONS
  13. *
  14. * [--minor]
  15. * : Compare only the first two parts of the version number.
  16. *
  17. * [--major]
  18. * : Compare only the first part of the version number.
  19. *
  20. * [--field=<field>]
  21. * : Prints the value of a single field for each update.
  22. *
  23. * [--fields=<fields>]
  24. * : Limit the output to specific object fields. Defaults to version,update_type,package_url.
  25. *
  26. * [--format=<format>]
  27. * : Accepted values: table, csv, json. Default: table
  28. *
  29. * @subcommand check-update
  30. */
  31. function check_update( $_, $assoc_args ) {
  32. $versions_path = ABSPATH . 'wp-includes/version.php';
  33. include $versions_path;
  34. $url = 'https://api.wordpress.org/core/stable-check/1.0/';
  35. $options = array(
  36. 'timeout' => 30
  37. );
  38. $headers = array(
  39. 'Accept' => 'application/json'
  40. );
  41. $response = Utils\http_request( 'GET', $url, $headers, $options );
  42. if ( ! $response->success || 200 !== $response->status_code ) {
  43. WP_CLI::error( "Failed to get latest version." );
  44. }
  45. $release_data = json_decode( $response->body );
  46. $release_versions = array_keys( (array) $release_data );
  47. usort( $release_versions, function( $a, $b ){
  48. return 1 === version_compare( $a, $b );
  49. });
  50. $locale = get_locale();
  51. $current_parts = explode( '.', $wp_version );
  52. $updates = array();
  53. foreach ( $release_versions as $release_version ) {
  54. // don't list earliers versions
  55. if ( version_compare( $release_version, $wp_version, '<=' ) )
  56. continue;
  57. $release_parts = explode( '.', $release_version );
  58. $update_type = 'major';
  59. if ( $release_parts[0] === $current_parts[0]
  60. && $release_parts[1] === $current_parts[1] ) {
  61. $update_type = 'minor';
  62. }
  63. if ( ! ( isset( $assoc_args['minor'] ) && 'minor' !== $update_type )
  64. && ! ( isset( $assoc_args['major'] ) && 'major' !== $update_type )
  65. ) {
  66. $updates = $this->remove_same_minor_releases( $release_parts, $updates );
  67. $updates[] = array(
  68. 'version' => $release_version,
  69. 'update_type' => $update_type,
  70. 'package_url' => $this->get_download_url( $release_version, $locale )
  71. );
  72. }
  73. }
  74. if ( $updates ) {
  75. $updates = array_reverse( $updates );
  76. $formatter = new \WP_CLI\Formatter(
  77. $assoc_args,
  78. array( 'version', 'update_type', 'package_url' )
  79. );
  80. $formatter->display_items( $updates );
  81. } else if ( empty( $assoc_args['format'] ) || 'table' == $assoc_args['format'] ) {
  82. WP_CLI::success( "WordPress is at the latest version." );
  83. }
  84. }
  85. /**
  86. * Download core WordPress files.
  87. *
  88. * ## OPTIONS
  89. *
  90. * [--path=<path>]
  91. * : Specify the path in which to install WordPress.
  92. *
  93. * [--locale=<locale>]
  94. * : Select which language you want to download.
  95. *
  96. * [--version=<version>]
  97. * : Select which version you want to download.
  98. *
  99. * [--force]
  100. * : Overwrites existing files, if present.
  101. *
  102. * ## EXAMPLES
  103. *
  104. * wp core download --locale=nl_NL
  105. *
  106. * @when before_wp_load
  107. */
  108. public function download( $args, $assoc_args ) {
  109. if ( !isset( $assoc_args['force'] ) && is_readable( ABSPATH . 'wp-load.php' ) )
  110. WP_CLI::error( 'WordPress files seem to already be present here.' );
  111. if ( !is_dir( ABSPATH ) ) {
  112. WP_CLI::log( sprintf( 'Creating directory %s', ABSPATH ) );
  113. $mkdir = \WP_CLI\Utils\is_windows() ? 'mkdir %s' : 'mkdir -p %s';
  114. WP_CLI::launch( Utils\esc_cmd( $mkdir, ABSPATH ) );
  115. }
  116. $locale = isset( $assoc_args['locale'] ) ? $assoc_args['locale'] : 'en_US';
  117. if ( isset( $assoc_args['version'] ) ) {
  118. $version = $assoc_args['version'];
  119. $download_url = $this->get_download_url($version, $locale, 'tar.gz');
  120. } else {
  121. $offer = $this->get_download_offer( $locale );
  122. if ( !$offer ) {
  123. WP_CLI::error( "The requested locale ($locale) was not found." );
  124. }
  125. $version = $offer['current'];
  126. $download_url = str_replace( '.zip', '.tar.gz', $offer['download'] );
  127. }
  128. WP_CLI::log( sprintf( 'Downloading WordPress %s (%s)...', $version, $locale ) );
  129. $cache = WP_CLI::get_cache();
  130. $cache_key = "core/$locale-$version.tar.gz";
  131. $cache_file = $cache->has($cache_key);
  132. $bad_cache = false;
  133. if ( $cache_file ) {
  134. WP_CLI::log( "Using cached file '$cache_file'..." );
  135. try{
  136. self::_extract( $cache_file, ABSPATH );
  137. } catch ( Exception $e ) {
  138. WP_CLI::warning( "Extraction failed, downloading a new copy..." );
  139. $bad_cache = true;
  140. }
  141. }
  142. if ( ! $cache_file || $bad_cache ) {
  143. // We need to use a temporary file because piping from cURL to tar is flaky
  144. // on MinGW (and probably in other environments too).
  145. $temp = sys_get_temp_dir() . '/' . uniqid('wp_') . '.tar.gz';
  146. $headers = array('Accept' => 'application/json');
  147. $options = array(
  148. 'timeout' => 600, // 10 minutes ought to be enough for everybody
  149. 'filename' => $temp
  150. );
  151. $response = Utils\http_request( 'GET', $download_url, null, $headers, $options );
  152. if ( 404 == $response->status_code ) {
  153. WP_CLI::error( "Release not found. Double-check locale or version." );
  154. } else if ( 20 != substr( $response->status_code, 0, 2 ) ) {
  155. WP_CLI::error( "Couldn't access download URL (HTTP code {$response->status_code})" );
  156. }
  157. self::_extract( $temp, ABSPATH );
  158. $cache->import( $cache_key, $temp );
  159. unlink($temp);
  160. }
  161. WP_CLI::success( 'WordPress downloaded.' );
  162. }
  163. private static function _extract( $tarball, $dest ) {
  164. if ( ! class_exists( 'PharData' ) ) {
  165. $cmd = "tar xz --strip-components=1 --directory=%s -f $tarball";
  166. WP_CLI::launch( Utils\esc_cmd( $cmd, $dest ) );
  167. return;
  168. }
  169. $phar = new PharData( $tarball );
  170. $tempdir = implode( DIRECTORY_SEPARATOR, Array (
  171. dirname( $tarball ),
  172. basename( $tarball, '.tar.gz' ),
  173. $phar->getFileName()
  174. ) );
  175. $phar->extractTo( dirname( $tempdir ), null, true );
  176. self::_copy_overwrite_files( $tempdir, $dest );
  177. self::_rmdir( dirname( $tempdir ) );
  178. }
  179. private static function _copy_overwrite_files( $source, $dest ) {
  180. $iterator = new RecursiveIteratorIterator(
  181. new RecursiveDirectoryIterator( $source, RecursiveDirectoryIterator::SKIP_DOTS ),
  182. RecursiveIteratorIterator::SELF_FIRST);
  183. foreach ( $iterator as $item ) {
  184. if ( $item->isDir() ) {
  185. $dest_path = $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName();
  186. if ( !is_dir( $dest_path ) ) {
  187. mkdir( $dest_path );
  188. }
  189. } else {
  190. copy( $item, $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName() );
  191. }
  192. }
  193. }
  194. private static function _rmdir( $dir ) {
  195. $files = new RecursiveIteratorIterator(
  196. new RecursiveDirectoryIterator( $dir, RecursiveDirectoryIterator::SKIP_DOTS ),
  197. RecursiveIteratorIterator::CHILD_FIRST
  198. );
  199. foreach ( $files as $fileinfo ) {
  200. $todo = $fileinfo->isDir() ? 'rmdir' : 'unlink';
  201. $todo( $fileinfo->getRealPath() );
  202. }
  203. rmdir( $dir );
  204. }
  205. private static function _read( $url ) {
  206. $headers = array('Accept' => 'application/json');
  207. $response = Utils\http_request( 'GET', $url, null, $headers, array( 'timeout' => 30 ) );
  208. if ( 200 === $response->status_code ) {
  209. return $response->body;
  210. } else {
  211. WP_CLI::error( "Couldn't fetch response from {$url} (HTTP code {$response->status_code})" );
  212. }
  213. }
  214. private function get_download_offer( $locale ) {
  215. $out = unserialize( self::_read(
  216. 'https://api.wordpress.org/core/version-check/1.6/?locale=' . $locale ) );
  217. $offer = $out['offers'][0];
  218. if ( $offer['locale'] != $locale ) {
  219. return false;
  220. }
  221. return $offer;
  222. }
  223. private static function get_initial_locale() {
  224. include ABSPATH . '/wp-includes/version.php';
  225. // @codingStandardsIgnoreStart
  226. if ( isset( $wp_local_package ) )
  227. return $wp_local_package;
  228. // @codingStandardsIgnoreEnd
  229. return '';
  230. }
  231. /**
  232. * Generate a wp-config.php file.
  233. *
  234. * ## OPTIONS
  235. *
  236. * --dbname=<dbname>
  237. * : Set the database name.
  238. *
  239. * --dbuser=<dbuser>
  240. * : Set the database user.
  241. *
  242. * [--dbpass=<dbpass>]
  243. * : Set the database user password.
  244. *
  245. * [--dbhost=<dbhost>]
  246. * : Set the database host. Default: 'localhost'
  247. *
  248. * [--dbprefix=<dbprefix>]
  249. * : Set the database table prefix. Default: 'wp_'
  250. *
  251. * [--dbcharset=<dbcharset>]
  252. * : Set the database charset. Default: 'utf8'
  253. *
  254. * [--dbcollate=<dbcollate>]
  255. * : Set the database collation. Default: ''
  256. *
  257. * [--locale=<locale>]
  258. * : Set the WPLANG constant. Defaults to $wp_local_package variable.
  259. *
  260. * [--extra-php]
  261. * : If set, the command copies additional PHP code into wp-config.php from STDIN.
  262. *
  263. * [--skip-salts]
  264. * : If set, keys and salts won't be generated, but should instead be passed via `--extra-php`.
  265. *
  266. * [--skip-check]
  267. * : If set, the database connection is not checked.
  268. *
  269. * ## EXAMPLES
  270. *
  271. * # Standard wp-config.php file
  272. * wp core config --dbname=testing --dbuser=wp --dbpass=securepswd --locale=ro_RO
  273. *
  274. * # Enable WP_DEBUG and WP_DEBUG_LOG
  275. * wp core config --dbname=testing --dbuser=wp --dbpass=securepswd --extra-php <<PHP
  276. * define( 'WP_DEBUG', true );
  277. * define( 'WP_DEBUG_LOG', true );
  278. * PHP
  279. */
  280. public function config( $_, $assoc_args ) {
  281. if ( Utils\locate_wp_config() ) {
  282. WP_CLI::error( "The 'wp-config.php' file already exists." );
  283. }
  284. $versions_path = ABSPATH . 'wp-includes/version.php';
  285. include $versions_path;
  286. $defaults = array(
  287. 'dbhost' => 'localhost',
  288. 'dbpass' => '',
  289. 'dbprefix' => 'wp_',
  290. 'dbcharset' => 'utf8',
  291. 'dbcollate' => '',
  292. 'locale' => self::get_initial_locale()
  293. );
  294. $assoc_args = array_merge( $defaults, $assoc_args );
  295. if ( preg_match( '|[^a-z0-9_]|i', $assoc_args['dbprefix'] ) )
  296. WP_CLI::error( '--dbprefix can only contain numbers, letters, and underscores.' );
  297. // Check DB connection
  298. if ( !isset( $assoc_args['skip-check'] ) ) {
  299. Utils\run_mysql_command( 'mysql --no-defaults', array(
  300. 'execute' => ';',
  301. 'host' => $assoc_args['dbhost'],
  302. 'user' => $assoc_args['dbuser'],
  303. 'pass' => $assoc_args['dbpass'],
  304. ) );
  305. }
  306. if ( isset( $assoc_args['extra-php'] ) && $assoc_args['extra-php'] === true ) {
  307. $assoc_args['extra-php'] = file_get_contents( 'php://stdin' );
  308. }
  309. // TODO: adapt more resilient code from wp-admin/setup-config.php
  310. if ( ! isset( $assoc_args['skip-salts'] ) ) {
  311. $assoc_args['keys-and-salts'] = self::_read(
  312. 'https://api.wordpress.org/secret-key/1.1/salt/' );
  313. }
  314. if ( version_compare( $wp_version, '4.0', '<' ) ) {
  315. $assoc_args['add-wplang'] = true;
  316. } else {
  317. $assoc_args['add-wplang'] = false;
  318. }
  319. $out = Utils\mustache_render( 'wp-config.mustache', $assoc_args );
  320. $bytes_written = file_put_contents( ABSPATH . 'wp-config.php', $out );
  321. if ( ! $bytes_written ) {
  322. WP_CLI::error( 'Could not create new wp-config.php file.' );
  323. } else {
  324. WP_CLI::success( 'Generated wp-config.php file.' );
  325. }
  326. }
  327. /**
  328. * Determine if the WordPress tables are installed.
  329. *
  330. * [--network]
  331. * : Check if this is a multisite install
  332. *
  333. * ## EXAMPLES
  334. *
  335. * if ! $(wp core is-installed); then
  336. * wp core install
  337. * fi
  338. *
  339. * @subcommand is-installed
  340. */
  341. public function is_installed( $_, $assoc_args ) {
  342. if ( isset( $assoc_args['network'] ) ) {
  343. if ( is_blog_installed() && is_multisite() ) {
  344. exit( 0 );
  345. } else {
  346. exit( 1 );
  347. }
  348. } else if ( is_blog_installed() ) {
  349. exit( 0 );
  350. } else {
  351. exit( 1 );
  352. }
  353. }
  354. /**
  355. * Create the WordPress tables in the database.
  356. *
  357. * ## OPTIONS
  358. *
  359. * --url=<url>
  360. * : The address of the new site.
  361. *
  362. * --title=<site-title>
  363. * : The title of the new site.
  364. *
  365. * --admin_user=<username>
  366. * : The name of the admin user.
  367. *
  368. * --admin_password=<password>
  369. * : The password for the admin user.
  370. *
  371. * --admin_email=<email>
  372. * : The email address for the admin user.
  373. */
  374. public function install( $args, $assoc_args ) {
  375. if ( $this->_install( $assoc_args ) ) {
  376. WP_CLI::success( 'WordPress installed successfully.' );
  377. } else {
  378. WP_CLI::log( 'WordPress is already installed.' );
  379. }
  380. }
  381. /**
  382. * Transform a single-site install into a multi-site install.
  383. *
  384. * ## OPTIONS
  385. *
  386. * [--title=<network-title>]
  387. * : The title of the new network.
  388. *
  389. * [--base=<url-path>]
  390. * : Base path after the domain name that each site url will start with.
  391. * Default: '/'
  392. *
  393. * [--subdomains]
  394. * : If passed, the network will use subdomains, instead of subdirectories. Doesn't work with 'localhost'.
  395. *
  396. * @subcommand multisite-convert
  397. * @alias install-network
  398. */
  399. public function multisite_convert( $args, $assoc_args ) {
  400. if ( is_multisite() )
  401. WP_CLI::error( 'This already is a multisite install.' );
  402. $assoc_args = self::_set_multisite_defaults( $assoc_args );
  403. if ( !isset( $assoc_args['title'] ) ) {
  404. $assoc_args['title'] = sprintf( _x('%s Sites', 'Default network name' ), get_option( 'blogname' ) );
  405. }
  406. if ( $this->_multisite_convert( $assoc_args ) ) {
  407. WP_CLI::success( "Network installed. Don't forget to set up rewrite rules." );
  408. }
  409. }
  410. /**
  411. * Install multisite from scratch.
  412. *
  413. * ## OPTIONS
  414. *
  415. * [--url=<url>]
  416. * : The address of the new site.
  417. *
  418. * [--base=<url-path>]
  419. * : Base path after the domain name that each site url in the network will start with.
  420. * Default: '/'
  421. *
  422. * [--subdomains]
  423. * : If passed, the network will use subdomains, instead of subdirectories. Doesn't work with 'localhost'.
  424. *
  425. * --title=<site-title>
  426. * : The title of the new site.
  427. *
  428. * --admin_user=<username>
  429. * : The name of the admin user. Default: 'admin'
  430. *
  431. * --admin_password=<password>
  432. * : The password for the admin user.
  433. *
  434. * --admin_email=<email>
  435. * : The email address for the admin user.
  436. *
  437. * @subcommand multisite-install
  438. */
  439. public function multisite_install( $args, $assoc_args ) {
  440. if ( $this->_install( $assoc_args ) ) {
  441. WP_CLI::log( 'Created single site database tables.' );
  442. } else {
  443. WP_CLI::log( 'Single site database tables already present.' );
  444. }
  445. $assoc_args = self::_set_multisite_defaults( $assoc_args );
  446. $assoc_args['title'] = sprintf( _x('%s Sites', 'Default network name' ), $assoc_args['title'] );
  447. // Overwrite runtime args, to avoid mismatches.
  448. $consts_to_args = array(
  449. 'SUBDOMAIN_INSTALL' => 'subdomains',
  450. 'PATH_CURRENT_SITE' => 'base',
  451. 'SITE_ID_CURRENT_SITE' => 'site_id',
  452. 'BLOG_ID_CURRENT_SITE' => 'blog_id',
  453. );
  454. foreach ( $consts_to_args as $const => $arg ) {
  455. if ( defined( $const ) ) {
  456. $assoc_args[ $arg ] = constant( $const );
  457. }
  458. }
  459. if ( !$this->_multisite_convert( $assoc_args ) ) {
  460. return;
  461. }
  462. // Do the steps that were skipped by populate_network(),
  463. // which checks is_multisite().
  464. if ( is_multisite() ) {
  465. $site_user = get_user_by( 'email', $assoc_args['admin_email'] );
  466. self::add_site_admins( $site_user );
  467. $domain = self::get_clean_basedomain();
  468. self::create_initial_blog(
  469. $assoc_args['site_id'],
  470. $assoc_args['blog_id'],
  471. $domain,
  472. $assoc_args['base'],
  473. $assoc_args['subdomains'],
  474. $site_user
  475. );
  476. }
  477. WP_CLI::success( "Network installed. Don't forget to set up rewrite rules." );
  478. }
  479. private static function _set_multisite_defaults( $assoc_args ) {
  480. $defaults = array(
  481. 'subdomains' => false,
  482. 'base' => '/',
  483. 'site_id' => 1,
  484. 'blog_id' => 1,
  485. );
  486. return array_merge( $defaults, $assoc_args );
  487. }
  488. private function _install( $assoc_args ) {
  489. if ( is_blog_installed() ) {
  490. return false;
  491. }
  492. require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
  493. extract( wp_parse_args( $assoc_args, array(
  494. 'title' => '',
  495. 'admin_user' => '',
  496. 'admin_email' => '',
  497. 'admin_password' => ''
  498. ) ), EXTR_SKIP );
  499. // Support prompting for the `--url=<url>`,
  500. // which is normally a runtime argument
  501. if ( isset( $assoc_args['url'] ) ) {
  502. WP_CLI::set_url( $assoc_args['url'] );
  503. }
  504. $public = true;
  505. // @codingStandardsIgnoreStart
  506. if ( !is_email( $admin_email ) ) {
  507. WP_CLI::error( "The '{$admin_email}' email address is invalid." );
  508. }
  509. $result = wp_install( $title, $admin_user, $admin_email, $public, '', $admin_password );
  510. if ( is_wp_error( $result ) ) {
  511. WP_CLI::error( 'Installation failed (' . WP_CLI::error_to_string($result) . ').' );
  512. }
  513. // @codingStandardsIgnoreEnd
  514. // Confirm the uploads directory exists
  515. $upload_dir = wp_upload_dir();
  516. if ( ! empty( $upload_dir['error'] ) ) {
  517. WP_CLI::warning( $upload_dir['error'] );
  518. }
  519. return true;
  520. }
  521. private function _multisite_convert( $assoc_args ) {
  522. global $wpdb;
  523. require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
  524. $domain = self::get_clean_basedomain();
  525. if ( 'localhost' === $domain && ! empty( $assoc_args['subdomains'] ) ) {
  526. WP_CLI::error( "Multisite with subdomains cannot be configured when domain is 'localhost'." );
  527. }
  528. // need to register the multisite tables manually for some reason
  529. foreach ( $wpdb->tables( 'ms_global' ) as $table => $prefixed_table )
  530. $wpdb->$table = $prefixed_table;
  531. install_network();
  532. $result = populate_network(
  533. $assoc_args['site_id'],
  534. $domain,
  535. get_option( 'admin_email' ),
  536. $assoc_args['title'],
  537. $assoc_args['base'],
  538. $assoc_args['subdomains']
  539. );
  540. if ( true === $result ) {
  541. WP_CLI::log( 'Set up multisite database tables.' );
  542. } else if ( is_wp_error( $result ) ) {
  543. switch ( $result->get_error_code() ) {
  544. case 'siteid_exists':
  545. WP_CLI::log( $result->get_error_message() );
  546. return false;
  547. case 'no_wildcard_dns':
  548. WP_CLI::warning( __( 'Wildcard DNS may not be configured correctly.' ) );
  549. break;
  550. default:
  551. WP_CLI::error( $result );
  552. }
  553. }
  554. if ( !is_multisite() ) {
  555. ob_start();
  556. ?>
  557. define( 'WP_ALLOW_MULTISITE', true );
  558. define('MULTISITE', true);
  559. define('SUBDOMAIN_INSTALL', <?php var_export( $assoc_args['subdomains'] ); ?>);
  560. $base = '<?php echo $assoc_args['base']; ?>';
  561. define('DOMAIN_CURRENT_SITE', '<?php echo $domain; ?>');
  562. define('PATH_CURRENT_SITE', '<?php echo $assoc_args['base']; ?>');
  563. define('SITE_ID_CURRENT_SITE', 1);
  564. define('BLOG_ID_CURRENT_SITE', 1);
  565. <?php
  566. $ms_config = ob_get_clean();
  567. self::modify_wp_config( $ms_config );
  568. WP_CLI::log( 'Added multisite constants to wp-config.php.' );
  569. }
  570. return true;
  571. }
  572. // copied from populate_network()
  573. private static function create_initial_blog( $network_id, $blog_id, $domain, $path,
  574. $subdomain_install, $site_user ) {
  575. global $wpdb, $current_site, $wp_rewrite;
  576. $current_site = new stdClass;
  577. $current_site->domain = $domain;
  578. $current_site->path = $path;
  579. $current_site->site_name = ucfirst( $domain );
  580. $wpdb->insert( $wpdb->blogs, array(
  581. 'site_id' => $network_id,
  582. 'domain' => $domain,
  583. 'path' => $path,
  584. 'registered' => current_time( 'mysql' )
  585. ) );
  586. $current_site->blog_id = $blog_id = $wpdb->insert_id;
  587. update_user_meta( $site_user->ID, 'source_domain', $domain );
  588. update_user_meta( $site_user->ID, 'primary_blog', $blog_id );
  589. if ( $subdomain_install )
  590. $wp_rewrite->set_permalink_structure( '/%year%/%monthnum%/%day%/%postname%/' );
  591. else
  592. $wp_rewrite->set_permalink_structure( '/blog/%year%/%monthnum%/%day%/%postname%/' );
  593. flush_rewrite_rules();
  594. }
  595. // copied from populate_network()
  596. private static function add_site_admins( $site_user ) {
  597. $site_admins = array( $site_user->user_login );
  598. $users = get_users( array( 'fields' => array( 'ID', 'user_login' ) ) );
  599. if ( $users ) {
  600. foreach ( $users as $user ) {
  601. if ( is_super_admin( $user->ID ) && !in_array( $user->user_login, $site_admins ) )
  602. $site_admins[] = $user->user_login;
  603. }
  604. }
  605. update_site_option( 'site_admins', $site_admins );
  606. }
  607. private static function modify_wp_config( $content ) {
  608. $wp_config_path = Utils\locate_wp_config();
  609. $token = "/* That's all, stop editing!";
  610. list( $before, $after ) = explode( $token, file_get_contents( $wp_config_path ) );
  611. file_put_contents( $wp_config_path, $before . $content . $token . $after );
  612. }
  613. private static function get_clean_basedomain() {
  614. $domain = preg_replace( '|https?://|', '', get_option( 'siteurl' ) );
  615. if ( $slash = strpos( $domain, '/' ) )
  616. $domain = substr( $domain, 0, $slash );
  617. return $domain;
  618. }
  619. /**
  620. * Display the WordPress version.
  621. *
  622. * ## OPTIONS
  623. *
  624. * [--extra]
  625. * : Show extended version information.
  626. *
  627. * @when before_wp_load
  628. */
  629. public function version( $args = array(), $assoc_args = array() ) {
  630. $versions_path = ABSPATH . 'wp-includes/version.php';
  631. if ( !is_readable( $versions_path ) ) {
  632. WP_CLI::error(
  633. "This does not seem to be a WordPress install.\n" .
  634. "Pass --path=`path/to/wordpress` or run `wp core download`." );
  635. }
  636. include $versions_path;
  637. // @codingStandardsIgnoreStart
  638. if ( isset( $assoc_args['extra'] ) ) {
  639. if ( preg_match( '/(\d)(\d+)-/', $tinymce_version, $match ) ) {
  640. $human_readable_tiny_mce = $match[1] . '.' . $match[2];
  641. } else {
  642. $human_readable_tiny_mce = '';
  643. }
  644. echo \WP_CLI\Utils\mustache_render( 'versions.mustache', array(
  645. 'wp-version' => $wp_version,
  646. 'db-version' => $wp_db_version,
  647. 'mce-version' => ( $human_readable_tiny_mce ?
  648. "$human_readable_tiny_mce ($tinymce_version)"
  649. : $tinymce_version
  650. )
  651. ) );
  652. } else {
  653. WP_CLI::line( $wp_version );
  654. }
  655. // @codingStandardsIgnoreEnd
  656. }
  657. /**
  658. * Security copy of the core function with Requests - Gets the checksums for the given version of WordPress.
  659. *
  660. * @param string $version Version string to query.
  661. * @param string $locale Locale to query.
  662. * @return bool|array False on failure. An array of checksums on success.
  663. */
  664. private static function get_core_checksums( $version, $locale ) {
  665. $url = $http_url = 'http://api.wordpress.org/core/checksums/1.0/?' . http_build_query( compact( 'version', 'locale' ), null, '&' );
  666. if ( $ssl = wp_http_supports( array( 'ssl' ) ) )
  667. $url = 'https' . substr( $url, 4 );
  668. $options = array(
  669. 'timeout' => 30
  670. );
  671. $headers = array(
  672. 'Accept' => 'application/json'
  673. );
  674. $response = Utils\http_request( 'GET', $url, null, $headers, $options );
  675. if ( $ssl && ! $response->success ) {
  676. WP_CLI::warning( 'wp-cli could not establish a secure connection to WordPress.org. Please contact your server administrator.' );
  677. $response = Utils\http_request( 'GET', $http_url, null, $headers, $options );
  678. }
  679. if ( ! $response->success || 200 != $response->status_code )
  680. return false;
  681. $body = trim( $response->body );
  682. $body = json_decode( $body, true );
  683. if ( ! is_array( $body ) || ! isset( $body['checksums'] ) || ! is_array( $body['checksums'] ) )
  684. return false;
  685. return $body['checksums'];
  686. }
  687. /**
  688. * Verify WordPress files against WordPress.org's checksums.
  689. *
  690. * @subcommand verify-checksums
  691. */
  692. public function verify_checksums( $args, $assoc_args ) {
  693. global $wp_version, $wp_local_package;
  694. $checksums = self::get_core_checksums( $wp_version, isset( $wp_local_package ) ? $wp_local_package : 'en_US' );
  695. if ( ! is_array( $checksums ) ) {
  696. WP_CLI::error( "Couldn't get checksums from WordPress.org." );
  697. }
  698. $has_errors = false;
  699. foreach ( $checksums as $file => $checksum ) {
  700. // Skip files which get updated
  701. if ( 'wp-content' == substr( $file, 0, 10 ) ) {
  702. continue;
  703. }
  704. if ( ! file_exists( ABSPATH . $file ) ) {
  705. WP_CLI::warning( "File doesn't exist: {$file}" );
  706. $has_errors = true;
  707. continue;
  708. }
  709. $md5_file = md5_file( ABSPATH . $file );
  710. if ( $md5_file !== $checksum ) {
  711. WP_CLI::warning( "File doesn't verify against checksum: {$file}" );
  712. $has_errors = true;
  713. }
  714. }
  715. if ( ! $has_errors ) {
  716. WP_CLI::success( "WordPress install verifies against checksums." );
  717. } else {
  718. WP_CLI::error( "WordPress install doesn't verify against checksums." );
  719. }
  720. }
  721. /**
  722. * Update WordPress.
  723. *
  724. * ## OPTIONS
  725. *
  726. * [<zip>]
  727. * : Path to zip file to use, instead of downloading from wordpress.org.
  728. *
  729. * [--version=<version>]
  730. * : Update to this version, instead of to the latest version.
  731. *
  732. * [--force]
  733. * : Update even when installed WP version is greater than the requested version.
  734. *
  735. * [--locale=<locale>]
  736. * : Select which language you want to download.
  737. *
  738. * ## EXAMPLES
  739. *
  740. * wp core update
  741. *
  742. * wp core update --version=3.8 ../latest.zip
  743. *
  744. * wp core update --version=3.1 --force
  745. *
  746. * @alias upgrade
  747. */
  748. function update( $args, $assoc_args ) {
  749. global $wp_version;
  750. $update = $from_api = null;
  751. $upgrader = 'WP_CLI\\CoreUpgrader';
  752. if ( ! empty( $args[0] ) ) {
  753. $upgrader = 'WP_CLI\\NonDestructiveCoreUpgrader';
  754. $version = ! empty( $assoc_args['version'] ) ? $assoc_args['version'] : null;
  755. $update = (object) array(
  756. 'response' => 'upgrade',
  757. 'current' => $version,
  758. 'download' => $args[0],
  759. 'packages' => (object) array (
  760. 'partial' => null,
  761. 'new_bundled' => null,
  762. 'no_content' => null,
  763. 'full' => $args[0],
  764. ),
  765. 'version' => $version,
  766. 'locale' => null
  767. );
  768. } else if ( empty( $assoc_args['version'] ) ) {
  769. wp_version_check();
  770. $from_api = get_site_transient( 'update_core' );
  771. if ( ! empty( $from_api->updates ) ) {
  772. list( $update ) = $from_api->updates;
  773. }
  774. } else if ( version_compare( $wp_version, $assoc_args['version'], '<' )
  775. || isset( $assoc_args['force'] ) ) {
  776. $version = $assoc_args['version'];
  777. $locale = isset( $assoc_args['locale'] ) ? $assoc_args['locale'] : get_locale();
  778. $new_package = $this->get_download_url($version, $locale);
  779. $update = (object) array(
  780. 'response' => 'upgrade',
  781. 'current' => $assoc_args['version'],
  782. 'download' => $new_package,
  783. 'packages' => (object) array (
  784. 'partial' => null,
  785. 'new_bundled' => null,
  786. 'no_content' => null,
  787. 'full' => $new_package,
  788. ),
  789. 'version' => $version,
  790. 'locale' => $locale
  791. );
  792. }
  793. if ( ! empty( $update ) && ( $update->version != $wp_version || isset( $assoc_args['force'] ) ) ) {
  794. require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
  795. if ( $update->version ) {
  796. WP_CLI::log( "Updating to version {$update->version} ({$update->locale})..." );
  797. } else {
  798. WP_CLI::log( "Starting update..." );
  799. }
  800. $GLOBALS['wp_cli_update_obj'] = $update;
  801. $result = Utils\get_upgrader( $upgrader )->upgrade( $update );
  802. unset( $GLOBALS['wp_cli_update_obj'] );
  803. if ( is_wp_error($result) ) {
  804. $msg = WP_CLI::error_to_string( $result );
  805. if ( 'up_to_date' != $result->get_error_code() ) {
  806. WP_CLI::error( $msg );
  807. } else {
  808. WP_CLI::success( $msg );
  809. }
  810. } else {
  811. WP_CLI::success( 'WordPress updated successfully.' );
  812. }
  813. } else {
  814. WP_CLI::success( 'WordPress is up to date.' );
  815. }
  816. }
  817. /**
  818. * Update the WordPress database.
  819. *
  820. * @subcommand update-db
  821. */
  822. function update_db() {
  823. require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
  824. wp_upgrade();
  825. WP_CLI::success( 'WordPress database upgraded successfully.' );
  826. }
  827. /**
  828. * Gets download url based on version, locale and desired file type.
  829. *
  830. * @param $version
  831. * @param string $locale
  832. * @param string $file_type
  833. * @return string
  834. */
  835. private function get_download_url($version, $locale = 'en_US', $file_type = 'zip')
  836. {
  837. if ('en_US' === $locale) {
  838. $url = 'https://wordpress.org/wordpress-' . $version . '.' . $file_type;
  839. return $url;
  840. } else {
  841. $url = sprintf(
  842. 'https://%s.wordpress.org/wordpress-%s-%s.' . $file_type,
  843. substr($locale, 0, 2),
  844. $version,
  845. $locale
  846. );
  847. return $url;
  848. }
  849. }
  850. /**
  851. * Compare processed releases to the current one, and delete older one. Return remaining updates.
  852. *
  853. */
  854. private function remove_same_minor_releases( $release_parts, $updates ) {
  855. if ( empty( $updates ) )
  856. return false;
  857. $difference = array();
  858. foreach ( $updates as $processed ) {
  859. $processed_parts = explode( '.', $processed['version'] );
  860. // later releases are always later in the array
  861. if ( $processed_parts[0] !== $release_parts[0]
  862. || $processed_parts[1] !== $release_parts[1] ) {
  863. $difference[] = $processed;
  864. }
  865. }
  866. return $difference;
  867. }
  868. }
  869. WP_CLI::add_command( 'core', 'Core_Command' );
  870. class Core_Language_Command extends WP_CLI\CommandWithTranslation {
  871. protected $obj_type = 'core';
  872. }
  873. WP_CLI::add_command( 'core language', 'Core_Language_Command', array(
  874. 'before_invoke' => function() {
  875. if ( version_compare( $GLOBALS['wp_version'], '4.0', '<' ) ) {
  876. WP_CLI::error( "Requires WordPress 4.0 or greater." );
  877. }
  878. })
  879. );