diff --git a/functions.php b/functions.php index 8716c197..6560a9d7 100644 --- a/functions.php +++ b/functions.php @@ -1,13 +1,19 @@ , format?: class-string, writer?: class-string, writer_args?: mixed} $args + */ +function wp_export( $args = array() ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Renaming breaks Phar compat. + $defaults = array( 'filters' => array(), 'format' => 'WP_Export_WXR_Formatter', 'writer' => 'WP_Export_Returner', 'writer_args' => null, ); + + /** + * @var array{filters: array, format: class-string, writer: class-string, writer_args: mixed} $args + */ $args = wp_parse_args( $args, $defaults ); $export_query = new WP_Export_Query( $args['filters'] ); $formatter = new $args['format']( $export_query ); diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 00000000..25183173 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,16 @@ +parameters: + level: 9 + paths: + - src + - export-command.php + - functions.php + scanDirectories: + - vendor/wp-cli/wp-cli/php + scanFiles: + - vendor/php-stubs/wordpress-stubs/wordpress-stubs.php + treatPhpDocTypesAsCertain: false + ignoreErrors: + - identifier: missingType.iterableValue + - identifier: missingType.property + - identifier: missingType.parameter + - identifier: missingType.return diff --git a/src/Export_Command.php b/src/Export_Command.php index 0e27a27d..ea25ca74 100644 --- a/src/Export_Command.php +++ b/src/Export_Command.php @@ -181,7 +181,7 @@ public function __invoke( $_, $assoc_args ) { 'wp_export_new_file', static function ( $file_path ) { WP_CLI::log( sprintf( 'Writing to file %s', $file_path ) ); - Utils\wp_clear_object_cache(); + Utils\wp_clear_object_cache(); // phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.wp_clear_object_cacheDeprecatedRemoved @phpstan-ignore-line } ); @@ -190,7 +190,7 @@ static function ( $file_path ) { wp_export( [ 'filters' => $this->export_args, - 'writer' => 'WP_Export_File_Writer', + 'writer' => WP_Export_File_Writer::class, 'writer_args' => 'php://output', ] ); @@ -198,7 +198,7 @@ static function ( $file_path ) { wp_export( [ 'filters' => $this->export_args, - 'writer' => 'WP_Export_Split_Files_Writer', + 'writer' => WP_Export_Split_Files_Writer::class, 'writer_args' => [ 'max_file_size' => $this->max_file_size, 'destination_directory' => $this->wxr_path, @@ -234,6 +234,7 @@ private function validate_args( $args ) { foreach ( $args as $key => $value ) { if ( is_callable( [ $this, 'check_' . $key ] ) ) { + /** @phpstan-ignore argument.type */ $result = call_user_func( [ $this, 'check_' . $key ], $value ); if ( false === $result ) { $has_errors = true; @@ -251,9 +252,14 @@ private function validate_args( $args ) { } } + /** + * @param string $path + * + * @phpstan-ignore method.unused + */ private function check_dir( $path ) { if ( empty( $path ) ) { - $path = getcwd(); + $path = (string) getcwd(); } elseif ( ! is_dir( $path ) ) { WP_CLI::error( sprintf( "The directory '%s' does not exist.", $path ) ); } elseif ( ! is_writable( $path ) ) { @@ -265,34 +271,49 @@ private function check_dir( $path ) { return true; } + /** + * @param string $date + * + * @phpstan-ignore method.unused + */ private function check_start_date( $date ) { if ( null === $date ) { return true; } $time = strtotime( $date ); - if ( ! empty( $date ) && ! $time ) { + if ( ! empty( $date ) && false === $time ) { WP_CLI::warning( sprintf( 'The start_date %s is invalid.', $date ) ); return false; } - $this->export_args['start_date'] = date( 'Y-m-d', $time ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date + $this->export_args['start_date'] = date( 'Y-m-d', (int) $time ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date return true; } + /** + * @param string $date + * + * @phpstan-ignore method.unused + */ private function check_end_date( $date ) { if ( null === $date ) { return true; } $time = strtotime( $date ); - if ( ! empty( $date ) && ! $time ) { + if ( ! empty( $date ) && false === $time ) { WP_CLI::warning( sprintf( 'The end_date %s is invalid.', $date ) ); return false; } - $this->export_args['end_date'] = date( 'Y-m-d', $time ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date + $this->export_args['end_date'] = date( 'Y-m-d', (int) $time ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date return true; } + /** + * @param string $post_type + * + * @phpstan-ignore method.unused + */ private function check_post_type( $post_type ) { if ( null === $post_type || 'any' === $post_type ) { return true; @@ -317,6 +338,11 @@ private function check_post_type( $post_type ) { return true; } + /** + * @param string $post_type + * + * @phpstan-ignore method.unused + */ private function check_post_type__not_in( $post_type ) { if ( null === $post_type ) { return true; @@ -341,6 +367,11 @@ private function check_post_type__not_in( $post_type ) { return true; } + /** + * @param string $post__in + * + * @phpstan-ignore method.unused + */ private function check_post__in( $post__in ) { if ( null === $post__in ) { return true; @@ -357,6 +388,11 @@ private function check_post__in( $post__in ) { return true; } + /** + * @param string $start_id + * + * @phpstan-ignore method.unused + */ private function check_start_id( $start_id ) { if ( null === $start_id ) { return true; @@ -374,14 +410,19 @@ private function check_start_id( $start_id ) { return true; } + /** + * @param string $author + * + * @phpstan-ignore method.unused + */ private function check_author( $author ) { if ( null === $author ) { return true; } // phpcs:ignore WordPress.WP.DeprecatedFunctions.get_users_of_blogFound -- Fallback. - $authors = function_exists( 'get_users' ) ? get_users() : get_users_of_blog(); - if ( empty( $authors ) || is_wp_error( $authors ) ) { + $authors = function_exists( 'get_users' ) ? get_users() : get_users_of_blog(); // @phpstan-ignore-line + if ( empty( $authors ) ) { WP_CLI::warning( 'Could not find any authors in this blog.' ); return false; } @@ -407,6 +448,9 @@ private function check_author( $author ) { return true; } + /** + * @phpstan-ignore method.unused + */ private function check_max_num_posts( $num ) { if ( null !== $num && ( ! is_numeric( $num ) || $num <= 0 ) ) { WP_CLI::warning( 'max_num_posts should be a positive integer.' ); @@ -418,6 +462,11 @@ private function check_max_num_posts( $num ) { return true; } + /** + * @param string $category + * + * @phpstan-ignore method.unused + */ private function check_category( $category ) { if ( null === $category ) { return true; @@ -428,7 +477,7 @@ private function check_category( $category ) { } $term = category_exists( $category ); - if ( empty( $term ) || is_wp_error( $term ) ) { + if ( empty( $term ) ) { WP_CLI::warning( sprintf( 'Could not find a category matching %s.', $category ) ); return false; } @@ -436,6 +485,11 @@ private function check_category( $category ) { return true; } + /** + * @param string $status + * + * @phpstan-ignore method.unused + */ private function check_post_status( $status ) { if ( null === $status ) { return true; @@ -456,6 +510,11 @@ private function check_post_status( $status ) { return true; } + /** + * @param string|null $skip + * + * @phpstan-ignore method.unused + */ private function check_skip_comments( $skip ) { if ( null === $skip ) { return true; @@ -469,6 +528,11 @@ private function check_skip_comments( $skip ) { return true; } + /** + * @param string $size + * + * @phpstan-ignore method.unused + */ private function check_max_file_size( $size ) { if ( ! is_numeric( $size ) ) { WP_CLI::warning( 'max_file_size should be numeric.' ); @@ -480,6 +544,11 @@ private function check_max_file_size( $size ) { return true; } + /** + * @param string $once + * + * @phpstan-ignore method.unused + */ private function check_include_once( $once ) { if ( null === $once ) { return true; @@ -498,6 +567,11 @@ private function check_include_once( $once ) { return true; } + /** + * @param string $allow_orphan_terms + * + * @phpstan-ignore method.unused + */ private function check_allow_orphan_terms( $allow_orphan_terms ) { if ( null === $allow_orphan_terms ) { return true; diff --git a/src/WP_Export_Oxymel.php b/src/WP_Export_Oxymel.php index 5c5b7804..d985cc41 100644 --- a/src/WP_Export_Oxymel.php +++ b/src/WP_Export_Oxymel.php @@ -21,9 +21,9 @@ public function cdata( $text ) { if ( ! wp_is_valid_utf8( $text ) ) { $text = mb_convert_encoding( $text, 'UTF-8' ); } + // @phpstan-ignore function.deprecated } elseif ( ! seems_utf8( $text ) ) { // phpcs:ignore WordPress.WP.DeprecatedFunctions.seems_utf8Found $text = mb_convert_encoding( $text, 'UTF-8' ); - } } return parent::cdata( $text ); diff --git a/src/WP_Export_Query.php b/src/WP_Export_Query.php index 334e6e98..1ca3ea93 100644 --- a/src/WP_Export_Query.php +++ b/src/WP_Export_Query.php @@ -138,8 +138,13 @@ public function nav_menu_terms() { } public function exportify_post( $post ) { - $GLOBALS['wp_query']->in_the_loop = true; - $previous_global_post = Utils\get_flag_value( $GLOBALS, 'post' ); + /** + * @var \WP_Query $wp_query + */ + global $wp_query; + + $wp_query->in_the_loop = true; + $previous_global_post = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Temporary override. $GLOBALS['post'] = $post; setup_postdata( $post ); @@ -208,6 +213,9 @@ private function post_type_where() { if ( isset( $post_types_filters['name'] ) && is_array( $post_types_filters['name'] ) ) { $post_types = []; foreach ( $post_types_filters['name'] as $post_type ) { + /** + * @var string $post_type + */ if ( post_type_exists( $post_type ) ) { $post_types[] = $post_type; } @@ -327,7 +335,7 @@ private function bloginfo_rss( $section ) { private function find_user_from_any_object( $user ) { if ( is_numeric( $user ) ) { - return get_user_by( 'id', $user ); + return get_user_by( 'id', (int) $user ); } elseif ( is_string( $user ) ) { return get_user_by( 'login', $user ); } elseif ( isset( $user->ID ) ) { @@ -338,10 +346,10 @@ private function find_user_from_any_object( $user ) { private function find_category_from_any_object( $category ) { if ( is_numeric( $category ) ) { - return get_term( $category, 'category' ); + return get_term( (int) $category, 'category' ); } elseif ( is_string( $category ) ) { $term = term_exists( $category, 'category' ); - return isset( $term['term_id'] ) ? get_term( $term['term_id'], 'category' ) : false; + return isset( $term['term_id'] ) ? get_term( (int) $term['term_id'], 'category' ) : false; } elseif ( isset( $category->term_id ) ) { return get_term( $category->term_id, 'category' ); } diff --git a/src/WP_Export_Returner.php b/src/WP_Export_Returner.php index 6327e135..234e1b5b 100644 --- a/src/WP_Export_Returner.php +++ b/src/WP_Export_Returner.php @@ -4,7 +4,6 @@ class WP_Export_Returner extends WP_Export_Base_Writer { private $result = ''; public function export() { - $this->private = ''; try { parent::export(); } catch ( WP_Export_Exception $e ) { diff --git a/src/WP_Export_WXR_Formatter.php b/src/WP_Export_WXR_Formatter.php index e22c34e3..19361f84 100644 --- a/src/WP_Export_WXR_Formatter.php +++ b/src/WP_Export_WXR_Formatter.php @@ -12,7 +12,14 @@ * Responsible for formatting the data in WP_Export_Query to WXR */ class WP_Export_WXR_Formatter { + /** + * @var WP_Export_Query + */ private $export; + + /** + * @var string + */ private $wxr_version; public function __construct( $export ) { @@ -20,6 +27,9 @@ public function __construct( $export ) { $this->wxr_version = WXR_VERSION; } + /** + * @param array $requested_sections + */ public function before_posts( $requested_sections = [] ) { $available_sections = [ 'header', @@ -74,6 +84,7 @@ public function header() { contained in this file into your site. COMMENT; + // @phpstan-ignore property.private return $oxymel ->xml ->comment( $comment ) @@ -95,6 +106,7 @@ public function header() { public function site_metadata() { $oxymel = new Oxymel(); $metadata = $this->export->site_metadata(); + // @phpstan-ignore method.notFound return $oxymel ->title( $metadata['name'] ) ->link( $metadata['url'] ) @@ -171,16 +183,21 @@ public function rss2_head_action() { } public function post( $post ) { - $oxymel = new WP_Export_Oxymel(); - $GLOBALS['wp_query']->in_the_loop = true; + /** + * @var \WP_Query $wp_query + */ + global $wp_query; + $oxymel = new WP_Export_Oxymel(); + $wp_query->in_the_loop = true; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Intentional. $GLOBALS['post'] = $post; setup_postdata( $post ); + // @phpstan-ignore property.notFound $oxymel->item->contains ->tag( 'title' )->contains->cdata( apply_filters( 'the_title_export', $post->post_title ) )->end // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WordPress native hook. ->link( esc_url( apply_filters( 'the_permalink_rss', get_permalink() ) ) ) // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WordPress native hook. - ->pubDate( mysql2date( 'D, d M Y H:i:s +0000', get_post_time( 'Y-m-d H:i:s', true ), false ) ) + ->pubDate( mysql2date( 'D, d M Y H:i:s +0000', (string) get_post_time( 'Y-m-d H:i:s', true ), false ) ) ->tag( 'dc:creator', get_the_author_meta( 'login' ) ) ->guid( esc_url( get_the_guid() ), [ 'isPermaLink' => 'false' ] ) ->description( '' ) @@ -202,6 +219,7 @@ public function post( $post ) { ->tag( 'wp:is_sticky', $post->is_sticky ) ->optional( 'wp:attachment_url', wp_get_attachment_url( $post->ID ) ); foreach ( $post->terms as $term ) { + // @phpstan-ignore method.notFound $oxymel ->category( [ @@ -235,13 +253,15 @@ public function post( $post ) { ->oxymel( $this->comment_meta( $comment ) ) ->end; } - $oxymel - ->end; + + // @phpstan-ignore property.notFound, expr.resultUnused + $oxymel->end; return $oxymel->to_string(); } public function footer() { $oxymel = new Oxymel(); + // @phpstan-ignore property.notFound return $oxymel->close_channel->close_rss->to_string(); } diff --git a/src/WP_Export_XML_Over_HTTP.php b/src/WP_Export_XML_Over_HTTP.php index f4dee588..5c174721 100644 --- a/src/WP_Export_XML_Over_HTTP.php +++ b/src/WP_Export_XML_Over_HTTP.php @@ -1,9 +1,20 @@ file_name = $file_name; @@ -37,8 +48,12 @@ protected function get_export() { } protected function send_headers() { + /** + * @var string $charset + */ + $charset = get_option( 'blog_charset' ); header( 'Content-Description: File Transfer' ); header( 'Content-Disposition: attachment; filename=' . $this->file_name ); - header( 'Content-Type: text/xml; charset=' . get_option( 'blog_charset' ), true ); + header( 'Content-Type: text/xml; charset=' . $charset, true ); } } diff --git a/src/WP_Map_Iterator.php b/src/WP_Map_Iterator.php index a359ba00..b68a618e 100644 --- a/src/WP_Map_Iterator.php +++ b/src/WP_Map_Iterator.php @@ -1,8 +1,18 @@ > + */ class WP_Map_Iterator extends IteratorIterator { + /** + * @var callable + */ private $callback; + /** + * @param \Iterator $iterator + * @param callable $callback + */ public function __construct( $iterator, $callback ) { $this->callback = $callback; parent::__construct( $iterator ); diff --git a/src/WP_Post_IDs_Iterator.php b/src/WP_Post_IDs_Iterator.php index 054a76ab..83b1279c 100644 --- a/src/WP_Post_IDs_Iterator.php +++ b/src/WP_Post_IDs_Iterator.php @@ -1,16 +1,51 @@ + */ class WP_Post_IDs_Iterator implements Iterator { + /** + * @var \wpdb + */ + private $db; + + /** + * @var int + */ private $limit = 100; + + /** + * @var int[] + */ private $post_ids; + + /** + * @var int[] + */ private $ids_left; - private $results = array(); - private $global_index = 0; - private $index_in_results = 0; - private $db; + + /** + * @var object[] + */ + private $results = array(); + + /** + * @var int + */ + private $index_in_results; + + /** + * @var int + */ + private $global_index; public function __construct( $post_ids, $limit = null ) { - $this->db = $GLOBALS['wpdb']; + /** + * @var \wpdb $wpdb + */ + global $wpdb; + + $this->db = $wpdb; $this->post_ids = $post_ids; $this->ids_left = $post_ids; if ( ! is_null( $limit ) ) { @@ -61,7 +96,8 @@ public function valid() { private function load_next_posts_from_db() { $next_batch_post_ids = array_splice( $this->ids_left, 0, $this->limit ); $in_post_ids_sql = _wp_export_build_IN_condition( 'ID', $next_batch_post_ids ); - $this->results = $this->db->get_results( "SELECT * FROM {$this->db->posts} WHERE {$in_post_ids_sql}" ); + $results = $this->db->get_results( "SELECT * FROM {$this->db->posts} WHERE {$in_post_ids_sql}" ); + $this->results = is_array( $results ) ? $results : array(); if ( ! $this->results ) { if ( $this->db->last_error ) { throw new WP_Iterator_Exception( "Database error: {$this->db->last_error}" );