$dataset ) { $path = LWAC_Store::path( $key ); $rows[] = array( 'key' => $key, 'post_types' => implode( ',', $dataset['post_types'] ), 'categories' => empty( $dataset['cats'] ) ? '(all)' : implode( ',', $dataset['cats'] ), 'file' => is_readable( $path ) ? size_format( (int) filesize( $path ) ) : 'missing', 'built' => isset( $state['built'][ $key ] ) ? wp_date( 'Y-m-d H:i:s', (int) $state['built'][ $key ] ) : '-', 'error' => isset( $state['error'][ $key ] ) ? $state['error'][ $key ] : '', ); } if ( empty( $rows ) ) { WP_CLI::log( 'No calendar widgets yet.' ); return; } WP_CLI\Utils\format_items( 'table', $rows, array_keys( $rows[0] ) ); WP_CLI::log( 'Directory: ' . LWAC_Store::dir() ); WP_CLI::log( 'Retry pending: ' . ( $state['dirty'] ? 'yes' : 'no' ) ); } /** * 全データセットを作り直す. * * ## EXAMPLES * * wp coyomi rebuild * * @param array $args 位置引数. * @param array $assoc_args 名前付き引数. */ public function rebuild( $args, $assoc_args ) { $start = microtime( true ); $ok = LWAC_Store::rebuild_all(); $ms = (int) round( ( microtime( true ) - $start ) * 1000 ); if ( $ok ) { WP_CLI::success( sprintf( 'Rebuilt in %d ms.', $ms ) ); } else { WP_CLI::warning( sprintf( 'Rebuilt in %d ms, but some files could not be written (stored in the database instead). Check permissions of %s', $ms, LWAC_Store::dir() ) ); } } /** * 旧プラグインと同じクエリで日付を集計し、このプラグインのデータと突き合わせる. * * 旧プラグインがページ表示のたびに投げていた「月ごとに 1 本」のクエリを 1 回だけ流す. * 本番では空いている時間帯に、--sleep で間隔を空けて実行すること. * * ## OPTIONS * * * : 突き合わせるウィジェット (lwac_calendar-N). 設定 (投稿タイプ・カテゴリ) はこのウィジェットのものを使う. * * [--sleep=] * : 月ごとのクエリの間隔 (秒). * --- * default: 0.2 * --- * * [--yes] * : 確認を省く. * * ## EXAMPLES * * wp coyomi compare lwac_calendar-2 --sleep=0.5 * * @param array $args 位置引数. * @param array $assoc_args 名前付き引数. */ public function compare( $args, $assoc_args ) { if ( ! preg_match( '/^' . LWAC_Widget::ID_BASE . '-(\d+)$/', $args[0], $m ) ) { WP_CLI::error( 'The widget ID must look like ' . LWAC_Widget::ID_BASE . '-2.' ); } $instances = get_option( 'widget_' . LWAC_Widget::ID_BASE, array() ); if ( ! isset( $instances[ (int) $m[1] ] ) ) { WP_CLI::error( $args[0] . ' was not found.' ); } $dataset = LWAC_Widget::dataset_of( $instances[ (int) $m[1] ] ); $sleep = max( 0.0, (float) $assoc_args['sleep'] ); $months = self::legacy_months( $dataset['post_types'], $dataset['cats'] ); WP_CLI::confirm( sprintf( 'Run %d legacy queries (one per month) with %.1f s between them?', count( $months ) + 1, $sleep ), $assoc_args ); $legacy = array(); foreach ( $months as $row ) { $days = self::legacy_days( $dataset['post_types'], $dataset['cats'], (int) $row->year, (int) $row->month ); $days = array_map( 'intval', $days ); sort( $days ); $legacy[ sprintf( '%04d-%02d', $row->year, $row->month ) ] = $days; if ( $sleep > 0 ) { usleep( (int) ( $sleep * 1000000 ) ); } } $mine = LWAC_Store::build( $dataset ); $mine = $mine['days']; $all = array_unique( array_merge( array_keys( $legacy ), array_keys( $mine ) ) ); $mismatch = 0; $day_sum = 0; sort( $all ); foreach ( $all as $ym ) { $a = isset( $legacy[ $ym ] ) ? $legacy[ $ym ] : array(); $b = isset( $mine[ $ym ] ) ? $mine[ $ym ] : array(); $day_sum += count( $a ); if ( $a !== $b ) { $mismatch++; WP_CLI::log( sprintf( '%s legacy only: [%s] new only: [%s]', $ym, implode( ',', array_diff( $a, $b ) ), implode( ',', array_diff( $b, $a ) ) ) ); } } if ( 0 === $mismatch ) { WP_CLI::success( sprintf( 'All %d months match (%d days with posts).', count( $all ), $day_sum ) ); } else { WP_CLI::error( sprintf( '%d of %d months differ.', $mismatch, count( $all ) ) ); } } /* * 旧プラグイン (archives_view / archives_month_view) と同じ条件の問い合わせ. * 投稿タイプとカテゴリの数だけプレースホルダを並べ、値はすべて prepare() に渡す. * 突き合わせのために 1 回だけ流すもので、結果は残さない. */ /** * 記事のある年月 (新しい順). * * @param string[] $types 投稿タイプ. * @param int[] $cats カテゴリの term_id (空ならカテゴリで絞らない). * @return object[] year / month を持つ行. */ private static function legacy_months( array $types, array $cats ) { global $wpdb; if ( empty( $cats ) ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching return $wpdb->get_results( $wpdb->prepare( "SELECT DISTINCT YEAR(post_date) AS year, MONTH(post_date) AS month FROM {$wpdb->posts} wpposts " . 'WHERE post_type IN (' . implode( ',', array_fill( 0, count( $types ), '%s' ) ) . ") AND post_status IN ('publish') AND post_password='' " . 'ORDER BY year DESC, month DESC', $types ) ); } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching return $wpdb->get_results( $wpdb->prepare( "SELECT DISTINCT YEAR(post_date) AS year, MONTH(post_date) AS month FROM {$wpdb->posts} wpposts " . "JOIN {$wpdb->term_relationships} tr ON ( wpposts.ID = tr.object_id ) " . "JOIN {$wpdb->term_taxonomy} tt ON ( tr.term_taxonomy_id = tt.term_taxonomy_id " . 'AND tt.term_id IN(' . implode( ',', array_fill( 0, count( $cats ), '%d' ) ) . ") AND tt.taxonomy = 'category' ) " . 'WHERE post_type IN (' . implode( ',', array_fill( 0, count( $types ), '%s' ) ) . ") AND post_status IN ('publish') AND post_password='' " . 'ORDER BY year DESC, month DESC', array_merge( $cats, $types ) ) ); } /** * 1 か月のうち記事のある日. * * @param string[] $types 投稿タイプ. * @param int[] $cats カテゴリの term_id (空ならカテゴリで絞らない). * @param int $year 年. * @param int $month 月. * @return string[] */ private static function legacy_days( array $types, array $cats, $year, $month ) { global $wpdb; if ( empty( $cats ) ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching return $wpdb->get_col( $wpdb->prepare( "SELECT DAY(post_date) AS day FROM {$wpdb->posts} wpposts " . 'WHERE post_type IN (' . implode( ',', array_fill( 0, count( $types ), '%s' ) ) . ") AND post_status IN ('publish') AND post_password='' " . 'AND YEAR(post_date) = %d AND MONTH(post_date) = %d GROUP BY day', array_merge( $types, array( $year, $month ) ) ) ); } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching return $wpdb->get_col( $wpdb->prepare( "SELECT DAY(post_date) AS day FROM {$wpdb->posts} wpposts " . "JOIN {$wpdb->term_relationships} tr ON ( wpposts.ID = tr.object_id ) " . "JOIN {$wpdb->term_taxonomy} tt ON ( tr.term_taxonomy_id = tt.term_taxonomy_id " . 'AND tt.term_id IN(' . implode( ',', array_fill( 0, count( $cats ), '%d' ) ) . ") AND tt.taxonomy = 'category' ) " . 'WHERE post_type IN (' . implode( ',', array_fill( 0, count( $types ), '%s' ) ) . ") AND post_status IN ('publish') AND post_password='' " . 'AND YEAR(post_date) = %d AND MONTH(post_date) = %d GROUP BY day', array_merge( $cats, $types, array( $year, $month ) ) ) ); } }