post_type ) { return $num; } if ( self::$writing_post_id === $post->ID ) { return 0 === self::to_keep( $post->ID ) ? 0 : -1; // No WordPress pruning during a sync. } $num = (int) $num; return $num < 0 ? $num : $num + self::count( $post->ID ); } /** * Marks a revision as created by synchronization. * * Uses add_metadata() because add_post_meta() would attach the meta to the parent post. * * @param int $revision_id Revision ID. */ public static function mark( int $revision_id ): void { add_metadata( 'post', $revision_id, self::META_KEY, '1', true ); } /** * Highest revision ID of a table, to recognize revisions created afterwards. * * @param int $post_id Table post ID. * @return int */ public static function max_revision_id( int $post_id ): int { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Must reflect the database state. return (int) $wpdb->get_var( $wpdb->prepare( "SELECT MAX(ID) FROM {$wpdb->posts} WHERE post_parent = %d AND post_type = 'revision'", $post_id ) ); } /** * IDs of sync revisions of a table, newest first. * * @param int $post_id Table post ID. * @param int $after_id Only revisions with a higher ID. * @return int[] */ public static function sync_revision_ids( int $post_id, int $after_id = 0 ): array { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Must reflect the database state. $ids = $wpdb->get_col( $wpdb->prepare( "SELECT p.ID FROM {$wpdb->posts} p INNER JOIN {$wpdb->postmeta} m ON m.post_id = p.ID AND m.meta_key = %s WHERE p.post_parent = %d AND p.post_type = 'revision' AND p.ID > %d ORDER BY p.ID DESC", self::META_KEY, $post_id, $after_id ) ); return array_map( 'intval', $ids ); } /** * Number of sync revisions of a table. * * @param int $post_id Table post ID. * @return int */ public static function count( int $post_id ): int { return count( self::sync_revision_ids( $post_id ) ); } /** * Deletes sync revisions beyond the retention limit. * * @param int $post_id Table post ID. * @return int Number of deleted revisions. */ public static function prune( int $post_id ): int { $keep = self::to_keep( $post_id ); if ( $keep < 0 ) { return 0; } return self::delete( array_slice( self::sync_revision_ids( $post_id ), $keep ) ); } /** * Deletes sync revisions created after a given revision ID (left behind by a failed write). * * @param int $post_id Table post ID. * @param int $after_id Revision ID before the write. * @return int Number of deleted revisions. */ public static function delete_created_after( int $post_id, int $after_id ): int { return self::delete( self::sync_revision_ids( $post_id, $after_id ) ); } /** * Deletes revisions. * * @param int[] $revision_ids Revision IDs. * @return int Number deleted. */ private static function delete( array $revision_ids ): int { $deleted = 0; foreach ( $revision_ids as $revision_id ) { if ( wp_delete_post_revision( $revision_id ) ) { ++$deleted; } } return $deleted; } }