| 1 |
<?php |
| 2 |
/** |
| 3 |
* WP-CLI command - the sanctioned unattended path for huge sites. |
| 4 |
* |
| 5 |
* Runs the same tick engine as the admin UI, but from the shell where no |
| 6 |
* gateway timeout exists, with generous per-slice budgets. |
| 7 |
* |
| 8 |
* @package media-sweep |
| 9 |
*/ |
| 10 |
|
| 11 |
namespace Media_Sweep\CLI; |
| 12 |
|
| 13 |
use Media_Sweep\Models\Scan_Model; |
| 14 |
use WP_CLI; |
| 15 |
|
| 16 |
/** |
| 17 |
* Scan the media library for unused files from the command line. |
| 18 |
*/ |
| 19 |
class CLI_Command { |
| 20 |
|
| 21 |
/** |
| 22 |
* Run a media library scan to completion. |
| 23 |
* |
| 24 |
* ## OPTIONS |
| 25 |
* |
| 26 |
* [--deep] |
| 27 |
* : Also scan non-core database tables for media usage. |
| 28 |
* |
| 29 |
* [--no-thumbnails] |
| 30 |
* : Skip thumbnail files. |
| 31 |
* |
| 32 |
* ## EXAMPLES |
| 33 |
* |
| 34 |
* wp media-sweep scan |
| 35 |
* wp media-sweep scan --deep |
| 36 |
* |
| 37 |
* @param array $args Positional args. |
| 38 |
* @param array $assoc_args Named args. |
| 39 |
*/ |
| 40 |
public function scan( $args, $assoc_args ) { |
| 41 |
$container = \media_sweep()->services(); |
| 42 |
|
| 43 |
$media_scanner = $container->get( 'media_scanner_service' ); |
| 44 |
$runner = $container->get( 'scan_runner_service' ); |
| 45 |
|
| 46 |
$options = array( |
| 47 |
'deep_scan' => ! empty( $assoc_args['deep'] ), |
| 48 |
'include_thumbnails' => ! isset( $assoc_args['no-thumbnails'] ), |
| 49 |
); |
| 50 |
|
| 51 |
$scan = $media_scanner->start_scan( $options ); |
| 52 |
if ( ! $scan ) { |
| 53 |
WP_CLI::error( 'Failed to start scan.' ); |
| 54 |
} |
| 55 |
|
| 56 |
WP_CLI::log( sprintf( 'Scan #%d started.', $scan->id ) ); |
| 57 |
|
| 58 |
$last_phase = ''; |
| 59 |
do { |
| 60 |
// 60s slices: no gateway exists on the CLI, but bounded slices |
| 61 |
// keep checkpoints fresh so Ctrl+C is always resumable. |
| 62 |
$result = $runner->run_tick( $scan->id, 60 ); |
| 63 |
|
| 64 |
if ( is_wp_error( $result ) ) { |
| 65 |
WP_CLI::error( $result->get_error_message() ); |
| 66 |
} |
| 67 |
|
| 68 |
$progress = $result['progress']; |
| 69 |
if ( $result['phase'] !== $last_phase ) { |
| 70 |
$last_phase = $result['phase']; |
| 71 |
WP_CLI::log( 'Phase: ' . $last_phase ); |
| 72 |
} |
| 73 |
|
| 74 |
if ( $progress['att_total'] > 0 ) { |
| 75 |
WP_CLI::log( sprintf( ' attachments %d / %d', $progress['att_done'], $progress['att_total'] ) ); |
| 76 |
} elseif ( $progress['posts_total'] > 0 ) { |
| 77 |
WP_CLI::log( sprintf( ' content items %d / %d', $progress['posts_done'], $progress['posts_total'] ) ); |
| 78 |
} |
| 79 |
} while ( empty( $result['done'] ) ); |
| 80 |
|
| 81 |
$results = $media_scanner->finish_scan( $scan->id ); |
| 82 |
|
| 83 |
WP_CLI::success( |
| 84 |
sprintf( |
| 85 |
'Scan complete. Files: %d | In use: %d | Unused: %d | Orphaned: %d', |
| 86 |
$results['total_files'], |
| 87 |
$results['in_use'], |
| 88 |
$results['unused'], |
| 89 |
$results['orphaned'] |
| 90 |
) |
| 91 |
); |
| 92 |
} |
| 93 |
|
| 94 |
/** |
| 95 |
* Show the status of the most recent scans. |
| 96 |
* |
| 97 |
* ## EXAMPLES |
| 98 |
* |
| 99 |
* wp media-sweep status |
| 100 |
* |
| 101 |
* @param array $args Positional args. |
| 102 |
* @param array $assoc_args Named args. |
| 103 |
*/ |
| 104 |
public function status( $args, $assoc_args ) { |
| 105 |
$scans = Scan_Model::query()->order_by( 'id', 'DESC' )->limit( 5 )->get(); |
| 106 |
|
| 107 |
if ( empty( $scans ) ) { |
| 108 |
WP_CLI::log( 'No scans yet.' ); |
| 109 |
return; |
| 110 |
} |
| 111 |
|
| 112 |
foreach ( $scans as $scan ) { |
| 113 |
WP_CLI::log( |
| 114 |
sprintf( |
| 115 |
'#%d %s %s phase=%s started=%s finished=%s', |
| 116 |
$scan->id, |
| 117 |
$scan->mode, |
| 118 |
$scan->status ? $scan->status : '-', |
| 119 |
$scan->phase ? $scan->phase : '-', |
| 120 |
$scan->started_at, |
| 121 |
$scan->finished_at ? $scan->finished_at : '-' |
| 122 |
) |
| 123 |
); |
| 124 |
} |
| 125 |
} |
| 126 |
} |
| 127 |
|