Post_Meta_Container   F
last analyzed

Complexity

Total Complexity 63

Size/Duplication

Total Lines 444
Duplicated Lines 12.39 %

Coupling/Cohesion

Components 4
Dependencies 5

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 63
lcom 4
cbo 5
dl 55
loc 444
ccs 0
cts 139
cp 0
rs 3.36
c 0
b 0
f 0

26 Methods

Rating   Name   Duplication   Size   Complexity  
B is_valid_save() 0 20 8
A is_valid_attach_for_request() 0 14 4
A __construct() 7 7 2
A init() 0 14 3
A save() 13 13 2
B get_environment_for_request() 0 24 8
A get_environment_for_object() 0 16 2
A is_valid_attach_for_object() 9 9 2
A attach() 0 17 2
A add_postbox_classes() 0 4 1
A render() 0 3 1
A set_post_id() 11 11 3
A get_post_type_visibility() 15 15 3
A show_on_page() 0 14 4
A show_on_page_children() 0 6 2
A show_on_template() 0 10 3
A hide_on_template() 0 5 2
A show_on_level() 0 4 1
A show_on_post_format() 0 5 2
A show_on_post_type() 0 5 2
A show_on_category() 0 8 1
A show_on_taxonomy_term() 0 8 1
A set_context() 0 4 1
A set_priority() 0 4 1
A set_revisions_disabled() 0 4 1
A get_revisions_disabled() 0 3 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Post_Meta_Container often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Post_Meta_Container, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Carbon_Fields\Container;
4
5
use Carbon_Fields\Helper\Helper;
6
use Carbon_Fields\Datastore\Datastore;
7
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
8
9
/**
10
 * Field container designed to extend WordPress custom fields functionality,
11
 * providing easier user interface to add, edit and delete text, media files,
12
 * location information and more.
13
 */
14
class Post_Meta_Container extends Container {
15
	/**
16
	 * ID of the post the container is working with
17
	 *
18
	 * @see init()
19
	 * @var int
20
	 */
21
	protected $post_id;
22
23
	/**
24
	 * Determines whether revisions are disabled for this container
25
	 *
26
	 * @var bool
27
	 */
28
	protected $revisions_disabled = false;
29
30
	/**
31
	 * List of default container settings
32
	 *
33
	 * @see init()
34
	 * @var array
35
	 */
36
	public $settings = array(
37
		'meta_box_context' => 'normal',
38
		'meta_box_priority' => 'high',
39
	);
40
41
	/**
42
	 * {@inheritDoc}
43
	 */
44 View Code Duplication
	public function __construct( $id, $title, $type, $condition_collection, $condition_translator ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
45
		parent::__construct( $id, $title, $type, $condition_collection, $condition_translator );
46
47
		if ( ! $this->get_datastore() ) {
48
			$this->set_datastore( Datastore::make( 'post_meta' ), $this->has_default_datastore() );
49
		}
50
	}
51
52
	/**
53
	 * Create DataStore instance, set post ID to operate with (if such exists).
54
	 * Bind attach() and save() to the appropriate WordPress actions.
55
	 */
56
	public function init() {
57
		$input = stripslashes_deep( $_GET );
58
		$request_post_id = isset( $input['post'] ) ? intval( $input['post'] ) : 0;
59
		if ( $request_post_id > 0 ) {
60
			$this->set_post_id( $request_post_id );
61
		}
62
63
		add_action( 'admin_init', array( $this, '_attach' ) );
64
		add_action( 'save_post', array( $this, '_save' ) );
65
66
		// support for attachments
67
		add_action( 'add_attachment', array( $this, '_save' ) );
68
		add_action( 'edit_attachment', array( $this, '_save' ) );
69
	}
70
71
	/**
72
	 * Checks whether the current save request is valid
73
	 * Possible errors are triggering save() for autosave requests
74
	 * or performing post save outside of the post edit page (like Quick Edit)
75
	 *
76
	 * @return bool
77
	 */
78
	public function is_valid_save() {
79
		$params = func_get_args();
80
		$post_id = $params[0];
81
		$post_type = get_post_type( $post_id );
82
83
		$wp_preview = ( ! empty( $_POST['wp-preview'] ) ) ? $_POST['wp-preview'] : '';
84
		$in_preview = $wp_preview === 'dopreview';
85
		$doing_autosave = defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE;
86
		$is_revision = $post_type === 'revision';
87
88
		if ( ( $doing_autosave || $is_revision ) && ( ! $in_preview || $this->revisions_disabled ) ) {
89
			return false;
90
		}
91
92
		if ( ! $this->verified_nonce_in_request() ) {
93
			return false;
94
		}
95
96
		return $this->is_valid_attach_for_object( $post_id );
97
	}
98
99
	/**
100
	 * Perform save operation after successful is_valid_save() check.
101
	 * The call is propagated to all fields in the container.
102
	 *
103
	 * @param int $post_id ID of the post against which save() is ran
104
	 */
105 View Code Duplication
	public function save( $post_id = null ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
106
		// Unhook action to garantee single save
107
		remove_action( 'save_post', array( $this, '_save' ) );
108
109
		$this->set_post_id( $post_id );
110
111
		foreach ( $this->fields as $field ) {
112
			$field->set_value_from_input( Helper::input() );
113
			$field->save();
114
		}
115
116
		do_action( 'carbon_fields_post_meta_container_saved', $post_id, $this );
117
	}
118
119
	/**
120
	 * Get environment array for page request (in admin)
121
	 *
122
	 * @return array
123
	 */
124
	protected function get_environment_for_request() {
125
		global $pagenow;
126
127
		$input = stripslashes_deep( $_GET );
128
		$request_post_type = isset( $input['post_type'] ) ? $input['post_type'] : '';
129
		$post_type = '';
130
131
		if ( $this->post_id ) {
132
			$post_type = get_post_type( $this->post_id );
133
		} elseif ( ! empty( $request_post_type ) ) {
134
			$post_type = $request_post_type;
135
		} elseif ( $pagenow === 'post-new.php' ) {
136
			$post_type = 'post';
137
		}
138
139
		$post = get_post( $this->post_id );
140
		$post = $post ? $post : null;
141
		$environment = array(
142
			'post_id' => $post ? $post->ID : 0,
143
			'post_type' => $post ? $post->post_type : $post_type,
144
			'post' => $post,
145
		);
146
		return $environment;
147
	}
148
149
	/**
150
	 * Perform checks whether the container should be attached during the current request
151
	 *
152
	 * @return bool True if the container is allowed to be attached
153
	 */
154
	public function is_valid_attach_for_request() {
155
		global $pagenow;
156
157
		if ( $pagenow !== 'post.php' && $pagenow !== 'post-new.php' ) {
158
			return false;
159
		}
160
161
		$environment = $this->get_environment_for_request();
162
		if ( ! $environment['post_type'] ) {
163
			return false;
164
		}
165
166
		return $this->static_conditions_pass();
167
	}
168
169
	/**
170
	 * Get environment array for object id
171
	 *
172
	 * @return array
173
	 */
174
	protected function get_environment_for_object( $object_id ) {
175
		$post = get_post( intval( $object_id ) );
176
		$post_type = $post->post_type;
177
178
		if ( wp_is_post_revision( $post ) !== false ) {
179
			$post = get_post( intval( $post->post_parent ) );
180
			$post_type = $post->post_type;
181
		}
182
183
		$environment = array(
184
			'post_id' => $post->ID,
185
			'post' => $post,
186
			'post_type' => $post_type,
187
		);
188
		return $environment;
189
	}
190
191
	/**
192
	 * Check container attachment rules against object id
193
	 *
194
	 * @param int $object_id
195
	 * @return bool
196
	 */
197 View Code Duplication
	public function is_valid_attach_for_object( $object_id = null ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
198
		$post = get_post( intval( $object_id ) );
199
200
		if ( ! $post ) {
201
			return false;
202
		}
203
204
		return $this->all_conditions_pass( intval( $post->ID ) );
205
	}
206
207
	/**
208
	 * Add meta box for each of the container post types
209
	 */
210
	public function attach() {
211
		$this->post_types = $this->get_post_type_visibility();
212
213
		foreach ( $this->post_types as $post_type ) {
214
			add_meta_box(
215
				$this->get_id(),
216
				$this->title,
217
				array( $this, 'render' ),
218
				$post_type,
219
				$this->settings['meta_box_context'],
220
				$this->settings['meta_box_priority']
221
			);
222
223
			$container_id = $this->get_id();
224
			add_filter( "postbox_classes_{$post_type}_{$container_id}", array( $this, 'add_postbox_classes' ) );
225
		}
226
	}
227
228
	/**
229
	 * Classes to add to the post meta box
230
	 */
231
	public function add_postbox_classes( $classes ) {
232
		$classes[] = 'carbon-box';
233
		return $classes;
234
	}
235
236
	/**
237
	 * Output the container markup
238
	 */
239
	public function render() {
240
		include \Carbon_Fields\DIR . '/templates/Container/post_meta.php';
241
	}
242
243
	/**
244
	 * Set the post ID the container will operate with.
245
	 *
246
	 * @param int $post_id
247
	 */
248 View Code Duplication
	public function set_post_id( $post_id ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
249
		$this->post_id = $post_id;
250
		$this->get_datastore()->set_object_id( $post_id );
251
252
		foreach ( $this->fields as $field ) {
253
			$datastore = $field->get_datastore();
254
			if ( $datastore->get_object_id() === 0 ) {
255
				$datastore->set_object_id( $post_id );
256
			}
257
		}
258
	}
259
260
	/**
261
	 * Get array of post types this container can appear on conditionally
262
	 *
263
	 * @return array<string>
264
	 */
265 View Code Duplication
	public function get_post_type_visibility() {
266
		$all_post_types = get_post_types();
267
		$evaluated_collection = $this->condition_collection->evaluate( array( 'post_type' ), true, array(), true );
268
269
		$shown_on = array();
270
		foreach ( $all_post_types as $post_type ) {
271
			$environment = array(
272
				'post_type' => $post_type,
273
			);
274
			if ( $evaluated_collection->is_fulfilled( $environment ) ) {
275
				$shown_on[] = $post_type;
276
			}
277
		}
278
		return $shown_on;
279
	}
280
281
	/**
282
	 * COMMON USAGE METHODS
283
	 */
284
285
	/**
286
	 * Show the container only on particular page referenced by its path.
287
	 *
288
	 * @deprecated
289
	 * @param int|string $page page ID or page path
290
	 * @return object $this
291
	 */
292
	public function show_on_page( $page ) {
293
		$page_id = absint( $page );
294
295
		if ( $page_id && $page_id == $page ) {
296
			$page_obj = get_post( $page_id );
297
		} else {
298
			$page_obj = get_page_by_path( $page );
299
		}
300
		$page_id = ( $page_obj ) ? $page_obj->ID : -1;
301
302
		$this->where( 'post_id', '=', $page_id );
303
304
		return $this;
305
	}
306
307
	/**
308
	 * Show the container only on pages whose parent is referenced by $parent_page_path.
309
	 *
310
	 * @deprecated
311
	 * @param string $parent_page_path
312
	 * @return object $this
313
	 */
314
	public function show_on_page_children( $parent_page_path ) {
315
		$page = get_page_by_path( $parent_page_path );
316
		$page_id = ( $page ) ? $page->ID : -1;
317
		$this->where( 'post_parent_id', '=', $page_id );
318
		return $this;
319
	}
320
321
	/**
322
	 * Show the container only on pages whose template has filename $template_path.
323
	 *
324
	 * @deprecated
325
	 * @param string|array $template_path
326
	 * @return object $this
327
	 */
328
	public function show_on_template( $template_path ) {
329
		// Backwards compatibility where only pages support templates
330
		if ( version_compare( get_bloginfo( 'version' ), '4.7', '<' ) ) {
331
			$this->show_on_post_type( 'page' );
332
		}
333
334
		$template_paths = is_array( $template_path ) ? $template_path : array( $template_path );
335
		$this->where( 'post_template', 'IN', $template_paths );
336
		return $this;
337
	}
338
339
	/**
340
	 * Hide the container from pages whose template has filename $template_path.
341
	 *
342
	 * @deprecated
343
	 * @param string|array $template_path
344
	 * @return object $this
345
	 */
346
	public function hide_on_template( $template_path ) {
347
		$template_paths = is_array( $template_path ) ? $template_path : array( $template_path );
348
		$this->where( 'post_template', 'NOT IN', $template_paths );
349
		return $this;
350
	}
351
352
	/**
353
	 * Show the container only on hierarchical posts of level $level.
354
	 * Levels start from 1 (top level post)
355
	 *
356
	 * @deprecated
357
	 * @param int $level
358
	 * @return object $this
359
	 */
360
	public function show_on_level( $level ) {
361
		$this->where( 'post_level', '=', intval( $level ) );
362
		return $this;
363
	}
364
365
	/**
366
	 * Show the container only on posts from the specified format.
367
	 * Learn more about {@link http://codex.wordpress.org/Post_Formats Post Formats (Codex)}
368
	 *
369
	 * @deprecated
370
	 * @param string|array $post_format Name of the format as listed on Codex
371
	 * @return object $this
372
	 */
373
	public function show_on_post_format( $post_format ) {
374
		$post_formats = is_array( $post_format ) ? $post_format : array( $post_format );
375
		$this->where( 'post_format', 'IN', $post_formats );
376
		return $this;
377
	}
378
379
	/**
380
	 * Show the container only on posts from the specified type(s).
381
	 *
382
	 * @deprecated
383
	 * @param string|array $post_types
384
	 * @return object $this
385
	 */
386
	public function show_on_post_type( $post_types ) {
387
		$post_types = is_array( $post_types ) ? $post_types : array( $post_types );
388
		$this->where( 'post_type', 'IN', $post_types );
389
		return $this;
390
	}
391
392
	/**
393
	 * Show the container only on posts from the specified category.
394
	 *
395
	 * @see show_on_taxonomy_term()
396
	 *
397
	 * @deprecated
398
	 * @param string $category_slug
399
	 * @return object $this
400
	 */
401
	public function show_on_category( $category_slug ) {
402
		$this->where( 'post_term', '=', array(
403
			'value' => $category_slug,
404
			'field' => 'slug',
405
			'taxonomy' => 'category',
406
		) );
407
		return $this;
408
	}
409
410
	/**
411
	 * Show the container only on posts which have term $term_slug from the $taxonomy_slug taxonomy.
412
	 *
413
	 * @deprecated
414
	 * @param string $taxonomy_slug
415
	 * @param string $term_slug
416
	 * @return object $this
417
	 */
418
	public function show_on_taxonomy_term( $term_slug, $taxonomy_slug ) {
419
		$this->where( 'post_term', '=', array(
420
			'value' => $term_slug,
421
			'field' => 'slug',
422
			'taxonomy' => $taxonomy_slug,
423
		) );
424
		return $this;
425
	}
426
427
	/**
428
	 * Sets the meta box container context
429
	 *
430
	 * @see https://codex.wordpress.org/Function_Reference/add_meta_box
431
	 * @param string $context ('normal', 'advanced', 'side' or the custom `carbon_fields_after_title`)
432
	 */
433
	public function set_context( $context ) {
434
		$this->settings['meta_box_context'] = $context;
435
		return $this;
436
	}
437
438
	/**
439
	 * Sets the meta box container priority
440
	 *
441
	 * @see https://codex.wordpress.org/Function_Reference/add_meta_box
442
	 * @param string $priority ('high', 'core', 'default' or 'low')
443
	 */
444
	public function set_priority( $priority ) {
445
		$this->settings['meta_box_priority'] = $priority;
446
		return $this;
447
	}
448
449
	public function set_revisions_disabled( $revisions_disabled ) {
450
		$this->revisions_disabled = $revisions_disabled;
451
		return $this;
452
	}
453
454
	public function get_revisions_disabled() {
455
		return $this->revisions_disabled;
456
	}
457
}
458