Completed
Branch 2.0.0 (814c19)
by Jimmy
03:05
created

Data_Class   F

Complexity

Total Complexity 62

Size/Duplication

Total Lines 273
Duplicated Lines 2.93 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 8
loc 273
rs 3.44
c 0
b 0
f 0
wmc 62
lcom 1
cbo 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 Data_Class 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 Data_Class, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Gestion de la construction des données selon les modèles.
4
 *
5
 * @author Eoxia <[email protected]>
6
 * @since 0.1.0
7
 * @version 1.0.0
8
 * @copyright 2015-2018
9
 * @package EO_Framework\EO_Model\Class
10
 */
11
12
namespace eoxia;
13
14
if ( ! defined( 'ABSPATH' ) ) {
15
	exit;
16
}
17
18
if ( ! class_exists( '\eoxia\Data_Class' ) ) {
19
	/**
20
	 * Gestion de la construction des données selon les modèles.
21
	 */
22
	class Data_Class extends Helper_Class {
23
24
		/**
25
		 * La liste des types de base dans PHP acceptés par EO_Framework pour les champs.
26
		 *
27
		 * @var array
28
		 */
29
		public static $built_in_types = array( 'string', 'integer', 'float', 'boolean', 'array' );
30
31
		/**
32
		 * La liste des types de personnalisés acceptés par EO_Framework pour les champs.
33
		 *
34
		 * @var array
35
		 */
36
		public static $custom_types = array( 'wpeo_date' );
37
38
		/**
39
		 * La liste complétes des types de champs acceptés pour les champs dans EO_Framework.
40
		 *
41
		 * @var array
42
		 */
43
		public static $accepted_types = array();
44
45
		/**
46
		 * Variable contenant l'ensemble des erreurs rencontrées lors de la création d'un objet.
47
		 *
48
		 * @var WP_Error
49
		 */
50
		private $wp_errors;
51
52
		/**
53
		 * Variable contenant la méthode actuellement utilisée. Permet de construire l'objet demandé selon la méthode HTTP utilisée.
54
		 *
55
		 * @var string
56
		 */
57
		private $req_method;
58
59
		/**
60
		 * Appelle la méthode pour dispatcher les données.
61
		 *
62
		 * @since 1.0.0
63
		 * @version 1.0.0
64
		 *
65
		 * @param Array  $data       Les données non traité. Peut être null, permet de récupérer le schéma.
66
		 * @param string $req_method La méthode HTTP actuellement utilisée.
67
		 */
68
		public function __construct( $data = null, $req_method ) {
69
			$this->wp_errors  = new \WP_Error();
70
			$this->req_method = ( null !== $req_method ) ? strtoupper( $req_method ) : null;
71
72
			// On construit les types autorisés à partir des listes séparées. Permet de ne pas mettre de type en dur dans le code.
73
			self::$accepted_types = wp_parse_args( self::$custom_types, self::$built_in_types );
74
75
			// Filtre du schéma.
76
			$this->schema = apply_filters( 'eo_model_handle_schema', $this->schema, $this->req_method );
77
78
			if ( null !== $data && null !== $this->req_method ) {
79
				$this->data = $this->handle_data( $data );
80
			}
81
82
			if ( ! empty( $this->wp_errors->errors ) ) {
83
				echo wp_json_encode( $this->wp_errors );
84
				exit;
85
			}
86
		}
87
88
		/**
89
		 * Dispatches les données selon le modèle.
90
		 *
91
		 * @since 1.0.0
92
		 * @version 1.0.0
93
		 *
94
		 * @param array $data   Les données envoyées par l'utilisateur pour construire un objet selon un schéma.
95
		 * @param array $schema Optionnal. La définition des données. Ce paramètre est utilisé uniquement dans le cas d'un schéma récursif.
96
		 *
97
		 * @return object       Les données traitées, typées et converties en l'objet demandé.
98
		 */
99
		private function handle_data( $data, $schema = null ) {
100
			$object = null;
101
			$schema = ( null === $schema ) ? $this->schema : $schema;
102
103
			foreach ( $schema as $field_name => $field_def ) {
104
				// Définie les données  par défaut pour l'élément courant par rapport à "default".
105
				$value = null;
106
				if ( isset( $field_def['default'] ) && in_array( $this->req_method, array( 'GET', 'POST' ), true ) ) {
107
					$value = $field_def['default'];
108
				}
109
110
				// On vérifie si la valeur du champs actuelle est fournie dans les données envoyées pour construction.
111
				if ( isset( $field_def['field'] ) && isset( $data[ $field_def['field'] ] ) ) { // On vérifie si la clé correspondant au champs de la BDD existe. $data['post_date'].
112
					$value = $data[ $field_def['field'] ];
113
				} elseif ( isset( $data[ $field_name ] ) && isset( $field_def ) && ! isset( $field_def['child'] ) ) { // On vérifie si la clé correspondant au schéma défini existe. $data['date'].
114
					$value = $data[ $field_name ];
115
				}
116
117
				$value = apply_filters( 'eo_model_handle_value', $value, $this, $field_def, $this->req_method );
118
119
				// Dans le cas ou la méthode actuelle implique un enregistrement dans la base de données.
120
				// On vérifie que le schéma n'indique pas une valeur obligatoire. Si le champs est vide on retourne une erreur.
121
				if ( 'GET' !== $this->req_method && isset( $field_def['required'] ) && $field_def['required'] && null === $value ) {
122
					$this->wp_errors->add( 'eo_model_is_required', get_class( $this ) . ' => ' . $field_name . ' is required' );
123
				}
124
125
				// Force le typage de $value en requête mode "GET".
126
				if ( 'GET' === $this->req_method ) {
127
					$value = $this->handle_value_type( $value, $field_def );
128
				}
129
130
				// Vérifie le typage de $value.
131
				$this->check_value_type( $value, $field_name, $field_def );
132
133
				// On assigne la valeur "construite" au champs dans l'objet en cours de construction.
134
				if ( null !== $value ) {
135
					$object[ $field_name ] = $value;
136
				}
137
138
				// Dans le cas ou la méthode actuelle implique un enregistrement dans la base de données.
139
				// Si la valeur "construite" est "null" (aucun cas précédent n'a rempli ce champs) et que le champs est requis alors on le supprime pour ne pas supprimer de la BDD.
140
				if ( 'GET' !== $this->req_method ) {
141
					if ( isset( $object[ $field_name ] ) && null === $value && isset( $field_def['required'] ) && $field_def['required'] ) {
142
						unset( $object[ $field_name ] );
143
					}
144
				}
145
146
				// Si le champs traité contient l'entrée "child" il s'agit d'un tableau mutli-dimensionnel alors on lance une récursivité.
147
				if ( isset( $field_def['child'] ) ) {
148
					// On vérifie si des données correspondantes au champs en traitement ont été envoyées.
149
					$values = ! empty( $data[ $field_name ] ) ? $data[ $field_name ] : array();
150
151
					// Récursivité sur les enfants de la définition courante.
152
					$object[ $field_name ] = $this->handle_data( $values, $field_def['child'] );
153
				}
154
			}
155
156
			return $object;
157
		}
158
159
		/**
160
		 * Vérification du type de la valeur d'un champs. Si le type n'est pas correct on rempli la variable $this->wp_errors qui sera retournée en fin de traitement.
161
		 *
162
		 * @param mixed  $value      La valeur du champs à vérifier.
163
		 * @param string $field_name Le nom du champs à vérifier. Utilisé pour le message d'erreur.
164
		 * @param array  $field_def  La définition complète du champs à vérifier.
165
		 *
166
		 * @return void
167
		 */
168
		public function check_value_type( $value, $field_name, $field_def ) {
169
			// Vérifie le type de $value.
170
			if ( null !== $value ) {
171
				if ( empty( $field_def['type'] ) ) {
172
					$this->wp_errors->add( 'eo_model_invalid_type', get_class( $this ) . ' => ' . $field_name . ': ' . $value . '(' . gettype( $value ) . ') no setted in schema. Type accepted: ' . join( ',', self::$accepted_types ) );
173
				} else {
174
					$field_type = true;
175
					switch ( $field_def['type'] ) {
176
						case 'string':
177
							if ( ! is_string( $value ) ) {
178
								$field_type = false;
179
							}
180
							break;
181
						case 'integer':
182
							if ( ! is_int( $value ) ) {
183
								$field_type = false;
184
							}
185
							break;
186
						case 'boolean':
187
							if ( ! is_bool( $value ) ) {
188
								$field_type = false;
189
							}
190
							break;
191
						case 'array':
192
							if ( ! is_array( $value ) ) {
193
								$rendered_value = is_object( $value ) ? 'Object item' : $value;
194
195
								$this->wp_errors->add( 'eo_model_invalid_type', get_class( $this ) . ' => ' . $field_name . ': ' . $rendered_value . '(' . gettype( $value ) . ') is not a ' . $field_def['type'] );
196
							} elseif ( isset( $field_def['array_type'] ) ) {
197
								if ( ! empty( $value ) ) {
198
									foreach ( $value as $key => $sub_value ) {
199
										$field_def['type'] = $field_def['array_type'];
200
										$this->check_value_type( $sub_value, $field_name, $field_def );
201
202
										if ( isset( $field_def['key_type'] ) ) {
203
											$field_def['type'] = $field_def['key_type'];
204
											$this->check_value_type( $key, $field_name, $field_def );
205
										}
206
									}
207
								}
208
							}
209
							break;
210
						default:
211
							if ( ! in_array( $field_def['type'], self::$accepted_types, true ) ) {
212
								$this->wp_errors->add( 'eo_model_invalid_type', get_class( $this ) . ' => ' . $field_name . ': ' . $value . '(' . gettype( $value ) . ') incorrect type: "' . $field_def['type'] . '". Type accepted: ' . join( ',', self::$accepted_types ) );
213
							}
214
							break;
215
					}
216
217
					if ( ! $field_type ) {
218
						// Translators: 1.Current className 2.Field name 3.Given value 4.Field Real type 5.Field expected type.
219
						$this->wp_errors->add( 'eo_model_invalid_type', sprintf( __( '%1$s => %2$s: %3$s ( %4$s ) is not a %5$s', 'eo-framework' ), get_class( $this ), $field_name, $value, gettype( $value ), $field_def['type'] ) );
220
					}
221
				}
222
			}
223
		}
224
225
		/**
226
		 * Forces le typage des données.
227
		 *
228
		 * @since 0.1.0
229
		 * @version 1.0.0
230
		 *
231
		 * @param mixed $value     La valeur courante.
232
		 * @param array $field_def La définition du champ.
233
		 *
234
		 * @return mixed           L'objet avec le typage forcé.
235
		 */
236
		public function handle_value_type( $value, $field_def ) {
237
			// Si le type du champs à vérifier est parmis les types personnalisés (non défini par PHP) alors on retourne simplement la valeur, la fonction risque de corrompre les données.
238
			if ( in_array( $field_def['type'], self::$custom_types, true ) ) {
239
				return $value;
240
			}
241
242
			/**
243
			 * On type la valeur.
244
			 *
245
			 * @see self::$accepted_types
246
			 */
247
			settype( $value, $field_def['type'] );
248
249
			// On force le typage des enfants uniquement si array_type est défini.
250
			if ( ! empty( $field_def['array_type'] ) && is_array( $value ) && ! empty( $value ) ) {
251
				foreach ( $value as $key => $val ) {
252
					/**
253
					 * On type la valeur.
254
					 *
255
					 * @see self::$accepted_types
256
					 */
257
					settype( $value[ $key ], $field_def['array_type'] );
258
				}
259
			}
260
261
			return $value;
262
		}
263
264
		/**
265
		 * Convertis le modèle en un tableau compatible WordPress.
266
		 *
267
		 * @since 1.0.0
268
		 * @version 1.0.0
269
		 *
270
		 * @return array Tableau compatible avec les fonctions WordPress.
271
		 */
272
		public function convert_to_wordpress() {
273
			$data = array();
274
275
			foreach ( $this->schema as $field_name => $field_def ) {
276
277
				if ( ! empty( $field_def['field'] ) ) {
278
					if ( isset( $this->data[ $field_name ] ) ) {
279
						$value = ( ( isset( $this->data[ $field_name ] ) && null !== $this->data[ $field_name ] ) ) ? $this->data[ $field_name ] : null;
280
281
						if ( null !== $value ) {
282
							if ( ! in_array( $field_def['type'], self::$custom_types, true ) || ! isset( $value['raw'] ) ) {
283
								$data[ $field_def['field'] ] = $value;
284
							} elseif ( isset( $value['raw'] ) ) {
285
								$data[ $field_def['field'] ] = $value['raw'];
286
							}
287
						}
288
					}
289
				}
290
			}
291
292
			return $data;
293
		}
294
	}
295
296
} // End if().
297