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 |
||
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' |
||
|
|||
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
|
|||
60 | $this->settings['parent'] = ''; |
||
61 | } else if ( strpos( $this->settings['parent'], '.php' ) === false ) { |
||
1 ignored issue
–
show
|
|||
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
|
|||
83 | return false; |
||
84 | } else if ( ! isset( $_REQUEST[ $this->get_nonce_name() ] ) || ! wp_verify_nonce( $_REQUEST[ $this->get_nonce_name() ], $this->get_nonce_name() ) ) { |
||
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'] ) { |
||
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
|
|||
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(); |
||
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
|
|||
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
|
|||
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 ) { |
||
280 | |||
281 | /** |
||
282 | * Set the theme options file name of this container. |
||
283 | **/ |
||
284 | public function set_page_file( $file ) { |
||
288 | |||
289 | /** |
||
290 | * Set the permissions necessary to view |
||
291 | * the corresponding theme options page |
||
292 | **/ |
||
293 | public function set_page_permissions( $permissions ) { |
||
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 |