v1.0.0
This commit is contained in:
parent
ac2c7a8014
commit
f7702f2872
25 changed files with 6453 additions and 0 deletions
337
includes/class-robotstxt-og-cli.php
Normal file
337
includes/class-robotstxt-og-cli.php
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
<?php
|
||||
/**
|
||||
* WP-CLI Commands Class
|
||||
*
|
||||
* Provides WP-CLI commands for managing OG image fallbacks.
|
||||
*
|
||||
* @package ROBOTSTXT_OG
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Class Robotstxt_OG_CLI
|
||||
*
|
||||
* WP-CLI commands for OpenGraph (by ROBOTSTXT) plugin.
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* # Resolve fallback images for all posts
|
||||
* $ wp og-fallback resolve --all
|
||||
* Success: Resolved 45 posts. Failed: 2.
|
||||
*
|
||||
* # Resolve a single post
|
||||
* $ wp og-fallback resolve 42
|
||||
* Success: Post 42 resolved to https://example.com/image.jpg
|
||||
*
|
||||
* # Clear all cached fallback URLs
|
||||
* $ wp og-fallback clear-cache --all
|
||||
* Success: Cleared 45 cached fallback URLs.
|
||||
*
|
||||
* # Preview without making changes
|
||||
* $ wp og-fallback resolve --all --dry-run
|
||||
* Found 47 posts with featured images. (dry-run, no changes made)
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Robotstxt_OG_CLI extends WP_CLI_Command {
|
||||
|
||||
/**
|
||||
* Image resolver instance.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @var Robotstxt_OG_Image_Resolver
|
||||
*/
|
||||
private Robotstxt_OG_Image_Resolver $resolver;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param Robotstxt_OG_Image_Resolver $resolver Image resolver instance.
|
||||
*/
|
||||
public function __construct( Robotstxt_OG_Image_Resolver $resolver ) {
|
||||
$this->resolver = $resolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve fallback images for posts.
|
||||
*
|
||||
* Clears cached fallback URL and re-resolves it for the specified post(s).
|
||||
* Use --all to process all posts with featured images.
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* [<post_id>]
|
||||
* : The ID of a single post to resolve.
|
||||
*
|
||||
* [--all]
|
||||
* : Resolve fallback images for all posts with featured images.
|
||||
*
|
||||
* [--dry-run]
|
||||
* : Preview what would be done without making changes.
|
||||
*
|
||||
* [--post-type=<type>]
|
||||
* : Limit resolution to a specific post type. Default: any.
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* wp og-fallback resolve --all
|
||||
* wp og-fallback resolve --all --dry-run
|
||||
* wp og-fallback resolve 42
|
||||
* wp og-fallback resolve --all --post-type=post
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $args Positional arguments.
|
||||
* @param array $assoc_args Associative arguments.
|
||||
* @return void
|
||||
*/
|
||||
public function resolve( array $args, array $assoc_args ): void {
|
||||
$all = isset( $assoc_args['all'] );
|
||||
$dry_run = isset( $assoc_args['dry-run'] );
|
||||
$post_type = isset( $assoc_args['post-type'] ) ? $assoc_args['post-type'] : 'any';
|
||||
|
||||
if ( $all ) {
|
||||
$this->resolve_all( $dry_run, $post_type );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! empty( $args[0] ) ) {
|
||||
$post_id = absint( $args[0] );
|
||||
$this->resolve_single( $post_id, $dry_run );
|
||||
return;
|
||||
}
|
||||
|
||||
WP_CLI::error( 'Please specify a post ID or use --all flag.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear cached fallback URLs.
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* [<post_id>]
|
||||
* : The ID of a single post to clear cache for.
|
||||
*
|
||||
* [--all]
|
||||
* : Clear all cached fallback URLs.
|
||||
*
|
||||
* [--dry-run]
|
||||
* : Preview what would be done without making changes.
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* wp og-fallback clear-cache --all
|
||||
* wp og-fallback clear-cache 42
|
||||
* wp og-fallback clear-cache --all --dry-run
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $args Positional arguments.
|
||||
* @param array $assoc_args Associative arguments.
|
||||
* @return void
|
||||
*/
|
||||
public function clear_cache( array $args, array $assoc_args ): void {
|
||||
$all = isset( $assoc_args['all'] );
|
||||
$dry_run = isset( $assoc_args['dry-run'] );
|
||||
|
||||
if ( $all ) {
|
||||
$this->clear_all_caches( $dry_run );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! empty( $args[0] ) ) {
|
||||
$post_id = absint( $args[0] );
|
||||
$this->clear_single_cache( $post_id, $dry_run );
|
||||
return;
|
||||
}
|
||||
|
||||
WP_CLI::error( 'Please specify a post ID or use --all flag.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve fallback images for all posts.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param bool $dry_run Whether to run without making changes.
|
||||
* @param string $post_type Post type to limit to.
|
||||
* @return void
|
||||
*/
|
||||
private function resolve_all( bool $dry_run, string $post_type ): void {
|
||||
$posts = get_posts(
|
||||
array(
|
||||
'post_type' => $post_type,
|
||||
'post_status' => 'any',
|
||||
'posts_per_page' => -1,
|
||||
'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
||||
array(
|
||||
'key' => '_thumbnail_id',
|
||||
'compare' => 'EXISTS',
|
||||
),
|
||||
),
|
||||
'fields' => 'ids',
|
||||
)
|
||||
);
|
||||
|
||||
$count = count( $posts );
|
||||
|
||||
if ( $dry_run ) {
|
||||
WP_CLI::line(
|
||||
/* translators: %d: number of posts found */
|
||||
sprintf( __( 'Found %d posts with featured images. (dry-run, no changes made)', 'robotstxt-og' ), $count )
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 0 === $count ) {
|
||||
WP_CLI::warning( __( 'No posts with featured images found.', 'robotstxt-og' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
$success = 0;
|
||||
$failed = 0;
|
||||
$progress = WP_CLI\Utils\make_progress_bar(
|
||||
/* translators: %d: number of posts to process */
|
||||
sprintf( __( 'Resolving %d posts', 'robotstxt-og' ), $count ),
|
||||
$count
|
||||
);
|
||||
|
||||
foreach ( $posts as $post_id ) {
|
||||
$this->resolver->clear_cache( $post_id );
|
||||
$url = $this->resolver->resolve_image( $post_id );
|
||||
|
||||
if ( ! empty( $url ) ) {
|
||||
++$success;
|
||||
} else {
|
||||
++$failed;
|
||||
}
|
||||
|
||||
$progress->tick();
|
||||
}
|
||||
|
||||
$progress->finish();
|
||||
|
||||
WP_CLI::success(
|
||||
/* translators: 1: successful count, 2: failed count */
|
||||
sprintf( __( 'Resolved %1$d posts. Failed: %2$d.', 'robotstxt-og' ), $success, $failed )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve fallback image for a single post.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @param bool $dry_run Whether to run without making changes.
|
||||
* @return void
|
||||
*/
|
||||
private function resolve_single( int $post_id, bool $dry_run ): void {
|
||||
$post = get_post( $post_id );
|
||||
|
||||
if ( ! $post ) {
|
||||
/* translators: %d: post ID */
|
||||
WP_CLI::error( sprintf( __( 'Post %d not found.', 'robotstxt-og' ), $post_id ) );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $dry_run ) {
|
||||
$thumb_id = get_post_thumbnail_id( $post_id );
|
||||
$thumb = $thumb_id ? wp_get_attachment_url( $thumb_id ) : '';
|
||||
/* translators: 1: post ID, 2: image URL */
|
||||
WP_CLI::line( sprintf( __( 'Post %1$d has featured image: %2$s (dry-run, no changes made)', 'robotstxt-og' ), $post_id, ! empty( $thumb ) ? $thumb : 'none' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
$this->resolver->clear_cache( $post_id );
|
||||
$url = $this->resolver->resolve_image( $post_id );
|
||||
|
||||
if ( ! empty( $url ) ) {
|
||||
/* translators: 1: post ID, 2: resolved URL */
|
||||
WP_CLI::success( sprintf( __( 'Post %1$d resolved to %2$s', 'robotstxt-og' ), $post_id, $url ) );
|
||||
} else {
|
||||
/* translators: %d: post ID */
|
||||
WP_CLI::warning( sprintf( __( 'Post %d: no compatible image found.', 'robotstxt-og' ), $post_id ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all cached fallback URLs.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param bool $dry_run Whether to run without making changes.
|
||||
* @return void
|
||||
*/
|
||||
private function clear_all_caches( bool $dry_run ): void {
|
||||
global $wpdb;
|
||||
|
||||
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
||||
// WP-CLI single-invocation context; no persistent cache layer is appropriate here.
|
||||
$count = (int) $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$wpdb->postmeta} WHERE meta_key = %s",
|
||||
'_og_image_fallback_url'
|
||||
)
|
||||
);
|
||||
// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
||||
|
||||
if ( $dry_run ) {
|
||||
/* translators: %d: number of cache entries found */
|
||||
WP_CLI::line( sprintf( __( 'Found %d cached fallback URLs. (dry-run, no changes made)', 'robotstxt-og' ), $count ) );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 0 === $count ) {
|
||||
WP_CLI::warning( __( 'No cached fallback URLs found.', 'robotstxt-og' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
||||
// Bulk delete of all cached postmeta rows — no WP API equivalent for this operation.
|
||||
$deleted = $wpdb->delete(
|
||||
$wpdb->postmeta,
|
||||
array( 'meta_key' => '_og_image_fallback_url' ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
|
||||
array( '%s' )
|
||||
);
|
||||
// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
||||
|
||||
/* translators: %d: number of cleared entries */
|
||||
WP_CLI::success( sprintf( __( 'Cleared %d cached fallback URLs.', 'robotstxt-og' ), (int) $deleted ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear cached fallback URL for a single post.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @param bool $dry_run Whether to run without making changes.
|
||||
* @return void
|
||||
*/
|
||||
private function clear_single_cache( int $post_id, bool $dry_run ): void {
|
||||
$cached = get_post_meta( $post_id, '_og_image_fallback_url', true );
|
||||
|
||||
if ( $dry_run ) {
|
||||
/* translators: 1: post ID, 2: cached URL or 'none' */
|
||||
WP_CLI::line( sprintf( __( 'Post %1$d cached URL: %2$s (dry-run, no changes made)', 'robotstxt-og' ), $post_id, ! empty( $cached ) ? $cached : 'none' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( empty( $cached ) ) {
|
||||
/* translators: %d: post ID */
|
||||
WP_CLI::warning( sprintf( __( 'Post %d has no cached fallback URL.', 'robotstxt-og' ), $post_id ) );
|
||||
return;
|
||||
}
|
||||
|
||||
$this->resolver->clear_cache( $post_id );
|
||||
/* translators: %d: post ID */
|
||||
WP_CLI::success( sprintf( __( 'Cleared cached fallback URL for post %d.', 'robotstxt-og' ), $post_id ) );
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue