Skip to content

Commit 9c38fa9

Browse files
authored
Merge pull request #354 from cloudinary/fix/cli-caching
free up memory in CLI
2 parents b068e09 + 47ec96e commit 9c38fa9

File tree

4 files changed

+362
-268
lines changed

4 files changed

+362
-268
lines changed

instance.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,20 @@ function get_plugin_instance() {
2626
return $cloudinary_plugin;
2727
}
2828

29+
/**
30+
* Get an instance of the CLI Class.
31+
*/
32+
function cloudinary_cli_instance() {
33+
if ( class_exists( '\WPCOM_VIP_CLI_Command' ) ) {
34+
return new CLI_VIP();
35+
}
36+
37+
return new CLI();
38+
}
39+
2940
if ( defined( 'WP_CLI' ) && WP_CLI ) {
30-
$instance = new CLI( get_plugin_instance() );
31-
\WP_CLI::add_command( 'cloudinary', $instance );
41+
$plugin_instance = get_plugin_instance();
42+
$cli = cloudinary_cli_instance();
43+
$cli->setup_cloudinary( $plugin_instance );
44+
\WP_CLI::add_command( 'cloudinary', $cli );
3245
}

php/class-cli-vip.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
/**
3+
* Cloudinary CLI for VIP.
4+
*
5+
* @package Cloudinary
6+
*/
7+
8+
namespace Cloudinary;
9+
10+
use Cloudinary\Traits\CLI_Trait;
11+
12+
/**
13+
* CLI VIP class.
14+
*
15+
* @since 2.5.1
16+
*/
17+
class CLI_VIP extends \WPCOM_VIP_CLI_Command {
18+
19+
use CLI_Trait;
20+
21+
/**
22+
* Output the Intro.
23+
*
24+
* @since 2.5.1
25+
* @link http://patorjk.com/software/taag/#p=display&c=echo&f=Calvin%20S&t=Cloudinary%20CLI
26+
*/
27+
public function do_intro() {
28+
static $intro;
29+
if ( ! $intro ) {
30+
\WP_CLI::log( '' );
31+
\WP_CLI::log( '╔═╗┬ ┌─┐┬ ┬┌┬┐┬┌┐┌┌─┐┬─┐┬ ┬ ╔═╗╦ ╦ ╦ ╦╦╔═╗' );
32+
\WP_CLI::log( '║ │ │ ││ │ ││││││├─┤├┬┘└┬┘ ║ ║ ║ ╚╗╔╝║╠═╝' );
33+
\WP_CLI::log( '╚═╝┴─┘└─┘└─┘─┴┘┴┘└┘┴ ┴┴└─ ┴ ╚═╝╩═╝╩ ╚╝ ╩╩ ' );
34+
$intro = true;
35+
}
36+
}
37+
}

php/class-cli.php

Lines changed: 14 additions & 266 deletions
Original file line numberDiff line numberDiff line change
@@ -7,283 +7,31 @@
77

88
namespace Cloudinary;
99

10-
use Cloudinary\Plugin;
10+
use Cloudinary\Traits\CLI_Trait;
1111

1212
/**
1313
* CLI class.
1414
*
1515
* @since 2.5.1
1616
*/
17-
class CLI {
17+
class CLI extends \WP_CLI_Command { // phpcs:ignore WordPressVIPMinimum.Classes.RestrictedExtendClasses.wp_cli
1818

19-
/**
20-
* Holds the plugin instance.
21-
*
22-
* @since 2.5.1
23-
*
24-
* @var Plugin Instance of the global plugin.
25-
*/
26-
public $plugin;
27-
28-
/**
29-
* Holds the base query args.
30-
*
31-
* @since 2.5.1
32-
*
33-
* @var array
34-
*/
35-
protected $base_query_args = array(
36-
'post_type' => 'attachment',
37-
'post_status' => 'inherit',
38-
'fields' => 'ids',
39-
'posts_per_page' => 100,
40-
'update_post_term_cache' => false,
41-
'update_post_meta_cache' => false,
42-
'paged' => 1,
43-
);
44-
45-
/**
46-
* CLI constructor.
47-
*
48-
* @since 2.5.1
49-
*
50-
* @param Plugin $plugin The plugin instance.
51-
*/
52-
public function __construct( $plugin ) {
53-
$this->plugin = $plugin;
54-
}
55-
56-
/**
57-
* Output the Intro.
58-
*
59-
* @since 2.5.1
60-
*/
61-
public function do_intro() {
62-
static $intro;
63-
if ( ! $intro ) {
64-
\WP_CLI::log( '' );
65-
\WP_CLI::log( '╔═╗┬ ┌─┐┬ ┬┌┬┐┬┌┐┌┌─┐┬─┐┬ ┬ ╔═╗╦ ╦' );
66-
\WP_CLI::log( '║ │ │ ││ │ ││││││├─┤├┬┘└┬┘ ║ ║ ║' );
67-
\WP_CLI::log( '╚═╝┴─┘└─┘└─┘─┴┘┴┘└┘┴ ┴┴└─ ┴ ╚═╝╩═╝╩' );
68-
$intro = true;
69-
}
70-
}
71-
72-
/**
73-
* Syncs assets with Cloudinary.
74-
* ## EXAMPLES
75-
*
76-
* wp cloudinary sync
77-
*
78-
* @when after_wp_load
79-
* @since 2.5.1
80-
*
81-
* @param array $args Ignored.
82-
* @param array $assoc_args Ignored.
83-
*
84-
* @return void
85-
*/
86-
public function sync( $args, $assoc_args ) {
87-
88-
// Check if analyzed first.
89-
if ( empty( get_option( '_cld_cli_analyzed' ) ) ) {
90-
$this->analyze();
91-
}
92-
93-
// Initial Query.
94-
$query_args = $this->base_query_args;
95-
// phpcs:ignore WordPress.DB.SlowDBQuery
96-
$query_args['meta_query'] = array(
97-
'AND',
98-
array(
99-
'key' => '_cld_unsynced',
100-
'compare' => 'EXISTS',
101-
),
102-
);
103-
104-
// Get assets that need to be synced.
105-
$query = new \WP_Query( $query_args );
106-
$this->do_process( $query, 'sync' );
107-
if ( ! $query->have_posts() ) {
108-
\WP_CLI::log( \WP_CLI::colorize( '%gAll assets synced.%n' ) );
109-
}
110-
111-
}
19+
use CLI_Trait;
11220

11321
/**
114-
* Analyze assets with Cloudinary.
115-
* ## EXAMPLES
116-
*
117-
* wp cloudinary analyze
118-
*
119-
* @when after_wp_load
120-
* @since 2.5.1
121-
*
122-
* @return void
22+
* Workaround to prevent memory leaks from growing variables
12323
*/
124-
public function analyze() {
125-
126-
// Initial query.
127-
$query_args = $this->base_query_args;
128-
$query = new \WP_Query( $query_args );
129-
130-
// Kill all _cld_ related meta.
131-
delete_post_meta_by_key( '_cld_unsynced' );
132-
delete_option( '_cld_cli_analyzed' );
133-
134-
// Do process.
135-
$this->do_process( $query, 'analyze' );
136-
}
137-
138-
/**
139-
* Do a process on the query.
140-
*
141-
* @since 2.5.1
142-
*
143-
* @param \WP_Query $query The initial query object.
144-
* @param string $process The process to do.
145-
*/
146-
protected function do_process( &$query, $process ) {
147-
$this->do_intro();
148-
149-
// Bail early.
150-
if ( ! method_exists( $this, "process_{$process}" ) ) {
151-
\WP_CLI::log( \WP_CLI::colorize( "%Invalid Process: {$process}.%n" ) );
152-
153-
return;
154-
}
155-
if ( method_exists( $this, $process ) ) {
156-
// Setup process.
157-
$total = $query->found_posts;
158-
$process = "process_{$process}";
159-
do {
160-
$posts = $query->get_posts();
161-
$this->{$process}( $posts, $total );
162-
163-
// Free up memory.
164-
if ( method_exists( $this, 'stop_the_insanity' ) ) {
165-
$this->stop_the_insanity();
166-
}
167-
168-
// Paginate.
169-
$query_args = $query->query_vars;
170-
$query_args['paged'] ++;
171-
$query = new \WP_Query( $query_args );
172-
} while ( $query->have_posts() );
173-
}
174-
\WP_CLI::line( '' );
175-
}
176-
177-
/**
178-
* Sync Assets.
179-
*
180-
* @param array $posts Array of Post IDs to process.
181-
* @param int $total Count of total posts to process.
182-
*/
183-
protected function process_sync( $posts, $total ) {
184-
static $bar, $done;
185-
if ( ! $bar && ! empty( $posts ) ) {
186-
\WP_CLI::log( \WP_CLI::colorize( '%gSyncing assets%n' ) );
187-
$bar = \WP_CLI\Utils\make_progress_bar( 'Syncing ' . $total . ' assets', $total, 10 );
188-
$done = 0;
189-
}
190-
foreach ( $posts as $index => $asset ) {
191-
$done ++; // Set $done early to not show 0 of x.
192-
$file = get_attached_file( $asset );
193-
$filename = self::pad_name( basename( $file ), 20, ' ', '*' );
194-
$bar->tick( 0, 'Syncing (' . ( $done ) . ' of ' . $total . ') : ' . $filename );
195-
if ( ! $this->plugin->get_component( 'sync' )->is_synced( $asset ) ) {
196-
$this->plugin->get_component( 'sync' )->managers['push']->process_assets( $asset, $bar );
24+
protected function stop_the_insanity() {
25+
global $wpdb, $wp_object_cache;
26+
$wpdb->queries = array();
27+
if ( is_object( $wp_object_cache ) ) {
28+
$wp_object_cache->group_ops = array();
29+
$wp_object_cache->stats = array();
30+
$wp_object_cache->memcache_debug = array();
31+
$wp_object_cache->cache = array();
32+
if ( method_exists( $wp_object_cache, '__remoteset' ) ) {
33+
$wp_object_cache->__remoteset();
19734
}
198-
delete_post_meta( $asset, '_cld_unsynced', true );
199-
$bar->tick();
20035
}
201-
// Done message - reanalyze.
202-
if ( $done === $total ) {
203-
$bar->tick( 0, 'Sync Completed.' );
204-
$bar->finish();
205-
$bar = null;
206-
\WP_CLI::line( '' );
207-
$this->analyze();
208-
delete_option( '_cld_cli_analyzed' );
209-
}
210-
}
211-
212-
/**
213-
* Analyze and mark assets that need to be synced.
214-
*
215-
* @since 2.5.1
216-
*
217-
* @param array $posts Array of Post IDs to process.
218-
* @param int $total Count of total posts to process.
219-
*/
220-
protected function process_analyze( $posts, $total ) {
221-
static $bar, $done, $info;
222-
223-
if ( ! $bar ) {
224-
\WP_CLI::log( \WP_CLI::colorize( '%gAnalyzing ' . $total . ' assets:%n' ) );
225-
$bar = \WP_CLI\Utils\make_progress_bar( '', $total, 10 );
226-
$done = 0;
227-
$info = array(
228-
'_cld_unsupported' => 0,
229-
'_cld_synced' => 0,
230-
'_cld_unsynced' => 0,
231-
);
232-
}
233-
foreach ( $posts as $index => $asset ) {
234-
$done ++;
235-
$key = '_cld_unsupported';
236-
if ( $this->plugin->get_component( 'media' )->is_media( $asset ) ) {
237-
// Add a key.
238-
$key = '_cld_synced';
239-
if ( ! $this->plugin->get_component( 'sync' )->is_synced( $asset ) ) {
240-
$key = '_cld_unsynced';
241-
add_post_meta( $asset, $key, true, true );
242-
}
243-
}
244-
$info[ $key ] ++;
245-
$bar->tick( 1, $done . ' of ' . $total . ' |' );
246-
}
247-
// Done message.
248-
if ( $done === $total ) {
249-
$bar->tick( 0, $total . ' Analyzed |' );
250-
$bar->finish();
251-
$bar = null;
252-
\WP_CLI::log( '' );
253-
\WP_CLI::log( \WP_CLI::colorize( '%gSynced%n :' ) . ' ' . $info['_cld_synced'] );
254-
\WP_CLI::log( \WP_CLI::colorize( '%yUn-synced%n :' ) . ' ' . $info['_cld_unsynced'] );
255-
\WP_CLI::log( \WP_CLI::colorize( '%rUnsupported%n :' ) . ' ' . $info['_cld_unsupported'] );
256-
update_option( '_cld_cli_analyzed', true, false );
257-
}
258-
}
259-
260-
/**
261-
* Pad a file name to fit within max chars.
262-
*
263-
* @param string $name The name to pad.
264-
* @param int $max_length The max length of the filename.
265-
* @param string $pad_char The pad char to use when name is less of the max.
266-
* @param string $concat_char The char to use when shortening names to fit.
267-
*
268-
* @return string
269-
*/
270-
protected static function pad_name( $name, $max_length, $pad_char = '.', $concat_char = '*' ) {
271-
$name_length = strlen( $name );
272-
$prefix = null;
273-
if ( $name_length > $max_length ) {
274-
$diff = $name_length - $max_length;
275-
$concat_length = $diff > 3 ? 3 : $diff;
276-
$usable_length = $max_length - $concat_length;
277-
$front = substr( $name, 0, floor( $usable_length / 2 ) );
278-
$back = substr( $name, strlen( $name ) - ceil( $usable_length / 2 ) );
279-
$name = $front . implode( array_fill( 0, $concat_length, $concat_char ) ) . $back;
280-
}
281-
$used_length = $max_length - strlen( $name );
282-
if ( 0 < $used_length ) {
283-
$prefix = implode( array_fill( 0, $used_length, $pad_char ) );
284-
}
285-
$out = $prefix . $name;
286-
287-
return $out;
28836
}
28937
}

0 commit comments

Comments
 (0)