Completed
Push — master ( 7fba5d...16c9f9 )
by Marin
14:40
created

Theme_Options_Container   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 296
Duplicated Lines 0 %

Coupling/Cohesion

Components 4
Dependencies 3

Importance

Changes 5
Bugs 1 Features 0
Metric Value
wmc 49
c 5
b 1
f 0
lcom 4
cbo 3
dl 0
loc 296
rs 8.5455

18 Methods

Rating   Name   Duplication   Size   Complexity  
A set_icon() 0 4 1
A set_page_file() 0 4 1
A set_page_permissions() 0 4 1
A __construct() 0 7 2
A save() 0 13 3
B init() 0 17 5
B attach() 0 27 2
A is_active() 0 7 3
A detach() 0 8 1
A render() 0 7 3
B verify_unique_page() 0 23 5
C drop_unique_page() 0 22 7
A verify_unique_field_name() 0 13 3
A drop_unique_field_name() 0 8 2
A add_fields() 0 9 2
A set_page_parent() 0 8 2
A clear_string() 0 3 1
B is_valid_save() 0 9 5

How to fix   Complexity   

Complex Class

Complex classes like Theme_Options_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 Theme_Options_Container, and based on these observations, apply Extract Interface, too.

1
<?php 
2
3
namespace Carbon_Fields\Container;
4
5
use Carbon_Fields\Datastore\Theme_Options_Datastore;
6
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
7
8
/**
9
 * Theme options container class. 
10
 */
11
class Theme_Options_Container extends Container {
12
	static protected $registered_pages = array();
13
14
	public $settings = array(
15
		'parent' => 'self',
16
		'file' => '',
17
		'permissions' => 'manage_options'
0 ignored issues
show
introduced by
Each line in an array declaration must end in a comma
Loading history...
18
	);
19
20
	public $icon = '';
21
	
22
	/**
23
	 * Create a new theme options fields container
24
	 *
25
	 * @param string $title Unique title of the container
26
	 **/
27
	public function __construct( $title ) {
28
		parent::__construct( $title );
29
30
		if ( ! $this->get_datastore() ) {
31
			$this->set_datastore( new Theme_Options_Datastore() );
32
		}
33
	}
34
35
	/**
36
	 * Perform save operation after successful is_valid_save() check.
37
	 * The call is propagated to all fields in the container.
38
	 *
39
	 * @param mixed $user_data
40
	 **/
41
	public function save( $user_data = null ) {
42
		try {
43
			parent::save( $user_data );
44
		} catch ( Incorrect_Syntax_Exception $e ) {
45
			$this->errors[] = $e->getMessage();
46
		}
47
48
		do_action( 'carbon_after_save_theme_options', $user_data );
49
50
		if ( ! headers_sent() ) {
51
			wp_redirect( add_query_arg( array( 'settings-updated' => 'true' ) ) );
52
		}
53
	}
54
55
	/**
56
	 * Attach container as a theme options page/subpage.
57
	 **/
58
	public function init() {
59
		if ( ! $this->settings['parent'] || $this->settings['parent'] == 'self' ) {
1 ignored issue
show
introduced by
Found "== '". Use Yoda Condition checks, you must
Loading history...
60
			$this->settings['parent'] = '';
61
		} else if ( strpos( $this->settings['parent'], '.php' ) === false ) {
1 ignored issue
show
introduced by
Found "=== false". Use Yoda Condition checks, you must
Loading history...
62
			$clear_title = $this->clear_string( $this->settings['parent'] );
63
			$this->settings['parent'] = 'crbn-' . $clear_title . '.php';
64
		}
65
66
		if ( ! $this->settings['file'] ) {
67
			$clear_title = $this->clear_string( $this->title );
68
			$this->settings['file'] .= 'crbn-' . $clear_title . '.php';
69
		}
70
71
		$this->verify_unique_page();
72
73
		add_action( 'admin_menu', array( $this, '_attach' ) );
74
	}
75
76
	/**
77
	 * Perform checks whether the current save() request is valid.
78
	 * 
79
	 * @return bool
80
	 **/
81
	public function is_valid_save() {
82
		if ( ! isset( $_SERVER['REQUEST_METHOD'] ) || $_SERVER['REQUEST_METHOD'] != 'POST' ) {
1 ignored issue
show
introduced by
Found "!= '". Use Yoda Condition checks, you must
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_SERVER
Loading history...
83
			return false;
84
		} else if ( ! isset( $_REQUEST[ $this->get_nonce_name() ] ) || ! wp_verify_nonce( $_REQUEST[ $this->get_nonce_name() ], $this->get_nonce_name() ) ) {
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_REQUEST
Loading history...
85
			return false;
86
		}
87
88
		return true;
89
	}
90
91
	/**
92
	 * Add theme options container pages.
93
	 * Hook the container saving action.
94
	 **/
95
	public function attach() {
96
97
		// Add menu page
98
		if ( ! $this->settings['parent'] ) {
99
			add_menu_page(
100
				$this->title, 
101
				$this->title, 
102
				$this->settings['permissions'], 
103
				$this->settings['file'],
104
				array( $this, 'render' ),
105
				$this->icon
106
			);
107
		}
108
109
		add_submenu_page(
110
			$this->settings['parent'],
111
			$this->title, 
112
			$this->title, 
113
			$this->settings['permissions'], 
114
			$this->settings['file'],
115
			array( $this, 'render' ),
116
			$this->icon
117
		);
118
119
		$page_hook = get_plugin_page_hookname( $this->settings['file'], '' );
120
		add_action( 'load-' . $page_hook, array( $this, '_save' ) );
121
	}
122
123
	/**
124
	 * Whether this container is currently viewed.
125
	 **/
126
	public function is_active() {
127
		if ( isset( $_GET['page'] ) && $_GET['page'] === $this->settings['file'] ) {
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_GET
Loading history...
128
			return true;
129
		}
130
131
		return false;
132
	}
133
134
	/**
135
	 * Revert the result of attach()
136
	 **/
137
	public function detach() {
138
		parent::detach();
139
140
		$this->drop_unique_page();
141
142
		$page_hook = get_plugin_page_hookname( $this->settings['file'], '' );
143
		remove_action( 'load-' . $page_hook, array( $this, '_save' ) );
144
	}
145
146
	/**
147
	 * Output the container markup
148
	 **/
149
	public function render() {
150
		if ( isset( $_GET['settings-updated'] ) && $_GET['settings-updated'] == 'true' ) {
1 ignored issue
show
introduced by
Found "== '". Use Yoda Condition checks, you must
Loading history...
151
			$this->notifications[] = __( 'Settings saved.', 'carbon_fields' );
152
		}
153
154
		include \Carbon_Fields\DIR . '/templates/Container/theme_options.php';
155
	}
156
157
	/**
158
	 * Make sure that there are no duplicate containers with the same name.
159
	 **/
160
	public function verify_unique_page() {
161
		$file = $this->settings['file'];
162
		$parent = $this->settings['parent'];
163
164
		// Register top level page
165
		if ( ! $parent ) {
166
			if ( isset( self::$registered_pages[ $file ] ) ) {
167
				throw new Incorrect_Syntax_Exception( 'Page "' . $file . '" already registered' );
168
			}
169
170
			self::$registered_pages[$file] = array();
0 ignored issues
show
introduced by
Array keys should be surrounded by spaces unless they contain a string or an integer.
Loading history...
171
			return;
172
		}
173
174
		// Register sub-page
175
		if ( ! isset( self::$registered_pages[ $parent ] ) ) {
176
			self::$registered_pages[ $parent ] = array( $file );
177
		}  elseif ( in_array( $file, self::$registered_pages[ $parent ] ) ) {
178
			throw new Incorrect_Syntax_Exception( 'Page "' . $file . '" with parent "' . $parent . '" is already registered. Please set a different file name using setup()' );
179
		} else {
180
			self::$registered_pages[ $parent ][] = $file;
181
		}
182
	}
183
184
	/**
185
	 * Unregister the container parent and child pages.
186
	 **/
187
	public function drop_unique_page() {
188
		$file = $this->settings['file'];
189
		$parent = $this->settings['parent'];
190
191
		// Register top level page
192
		if ( ! $parent ) {
193
			if ( isset( self::$registered_pages[ $file ] ) && empty( self::$registered_pages[ $file ] ) ) {
194
				unset( self::$registered_pages[ $file ] );
195
			}
196
197
			return;
198
		}
199
200
		// Register sub-page
201
		if ( isset( self::$registered_pages[ $parent ] ) && in_array( $file, self::$registered_pages[ $parent ] ) ) {
202
203
			$index = array_search( $file, self::$registered_pages[ $parent ] );
204
			if ( $index !== false ) {
1 ignored issue
show
introduced by
Found "!== false". Use Yoda Condition checks, you must
Loading history...
205
				unset( self::$registered_pages[ $parent ][ $index ] );
206
			}
207
		}
208
	}
209
210
	/**
211
	 * Make sure a field certain name can't be registered multiple times.
212
	 **/
213
	public function verify_unique_field_name( $name ) {
214
		$page_id = $this->settings['parent'] . '/' . $this->settings['file'];
215
216
		if ( ! isset( self::$registered_field_names[ $page_id ] ) ) {
217
			self::$registered_field_names[ $page_id ] = array();
218
		}
219
220
		if ( in_array( $name, self::$registered_field_names[ $page_id ] ) ) {
221
			throw new Incorrect_Syntax_Exception( 'Field name "' . $name . '" already registered' );
222
		}
223
224
		self::$registered_field_names[ $page_id ][] = $name;
225
	}
226
227
	/**
228
	 * Remove field name $name from the list of unique field names
229
	 *
230
	 * @param string $name
231
	 **/
232
	public function drop_unique_field_name( $name ) {
233
		$page_id = $this->settings['parent'] . '/' . $this->settings['file'];
234
235
		$index = array_search( $name, self::$registered_field_names[ $page_id ] );
236
		if ( $index !== false ) {
1 ignored issue
show
introduced by
Found "!== false". Use Yoda Condition checks, you must
Loading history...
237
			unset( self::$registered_field_names[ $page_id ][ $index ] );
238
		}
239
	}
240
241
	/**
242
	 * Append array of fields to the current fields set. All items of the array
243
	 * must be instances of Field and their names should be unique for all
244
	 * Carbon containers.
245
	 * If a field does not have DataStore already, the container data store is 
246
	 * assigned to them instead.
247
	 *
248
	 * @param array $fields
249
	 **/
250
	public function add_fields( $fields ) {
251
		parent::add_fields( $fields );
252
253
		foreach ( $this->fields as $field ) {
254
			$field->set_prefix( '' );
255
		}
256
257
		return $this;
258
	}
259
260
	/**
261
	 * Change the parent theme options page of this container
262
	 **/
263
	public function set_page_parent( $parent ) {
264
		if ( is_a( $parent, 'Carbon_Container' ) ) {
265
			$parent = $parent->title;
266
		}
267
		
268
		$this->settings['parent'] = $parent;
269
		return $this;
270
	}
271
272
	/**
273
	 * Set the icon of this theme options page.
274
	 * Applicable only for parent theme option pages.
275
	 **/
276
	public function set_icon( $icon ) {
277
		$this->icon = $icon;
278
		return $this;
279
	}
280
281
	/**
282
	 * Set the theme options file name of this container.
283
	 **/
284
	public function set_page_file( $file ) {
285
		$this->settings['file'] = $file;
286
		return $this;
287
	}
288
289
	/**
290
	 * Set the permissions necessary to view 
291
	 * the corresponding theme options page
292
	 **/
293
	public function set_page_permissions( $permissions ) {
294
		$this->settings['permissions'] = $permissions;
295
		return $this;
296
	}
297
298
	/**
299
	 * Sanitize the container title for use in 
300
	 * the theme options file name.
301
	 **/
302
	protected function clear_string( $string ) {
303
		return preg_replace( array( '~ +~', '~[^\w\d-]+~u', '~-+~' ), array( '-', '-', '-' ), strtolower( remove_accents( $string ) ) );
304
	}
305
306
}
307
308