Passed
Push — master ( 9c6499...c22bc5 )
by Jens
04:52 queued 02:21
created

FormComponent   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 323
Duplicated Lines 13 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 42
loc 323
rs 8.3999
c 0
b 0
f 0
wmc 46
lcom 1
cbo 4

23 Methods

Rating   Name   Duplication   Size   Complexity  
A run() 0 9 1
A render() 0 9 1
A checkParameters() 0 9 1
A initialize() 0 10 1
A checkSubmit() 0 11 3
A postSubmit() 0 2 1
A setFormId() 0 10 2
B isFormSubmitted() 0 4 5
A getPostValues() 0 9 1
A setUserSessionBackup() 0 7 2
A restoreUserSessionBackup() 0 8 2
A setSubmitToSession() 0 4 1
A isSubmitAllowed() 0 8 3
A checkDocumentTypeParameter() 7 7 2
A checkResponseFolderParameter() 7 7 2
A checkSubTemplateParameter() 7 7 2
A checkFormParameterNameParameter() 7 7 2
A checkThankYouMessageParameter() 7 7 2
A checkSubmitOncePerSessionParameter() 7 7 2
A checkRequiredParameters() 0 6 3
A setPathBackup() 0 10 2
A resetPathBackup() 0 8 2
A setFormParameter() 0 8 3

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 FormComponent 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 FormComponent, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace CloudControl\Cms\components;
3
4
5
use cc\Application;
6
use storage\Storage;
7
8
class FormComponent Extends BaseComponent
9
{
10
	const GET_PARAMETER_PATH = 'path';
11
12
	const PARAMETER_CMS_PREFIX = 'cmsPrefix';
13
	const PARAMETER_DOCUMENT_TYPE = 'documentType';
14
	const PARAMETER_DOCUMENT_TYPES = 'documentTypes';
15
	const PARAMETER_FORM_ID = 'formId';
16
	const PARAMETER_FORM_PARAMETER_NAME = 'formParameterName';
17
	const PARAMETER_HIDE_TITLE_AND_STATE = 'hideTitleAndState';
18
	const PARAMETER_RESPONSE_FOLDER = 'responseFolder';
19
	const PARAMETER_SMALLEST_IMAGE = 'smallestImage';
20
	const PARAMETER_SUBMIT_ONCE_PER_SESSION = 'submitOncePerSession';
21
	const PARAMETER_SUB_TEMPLATE = 'subTemplate';
22
	const PARAMETER_THANK_YOU_MESSAGE = 'thankYouMessage';
23
24
	const SESSION_PARAMETER_CLOUDCONTROL = 'cloudcontrol';
25
	const SESSION_PARAMETER_FORM_COMPONENT = 'FormComponent';
26
	/**
27
	 * @var null|string
28
	 */
29
	protected $documentType = null;
30
	/**
31
	 * @var null|string
32
	 */
33
	protected $responseFolder = null;
34
	/**
35
	 * @var string
36
	 */
37
	protected $subTemplate = 'cms/documents/document-form-form';
38
	/**
39
	 * @var string
40
	 */
41
	protected $formParameterName = 'form';
42
	/**
43
	 * @var string
44
	 */
45
	protected $thankYouMessage = 'Thank you for sending us your response.';
46
47
	/**
48
	 * @var bool
49
	 */
50
	protected $submitOncePerSession = false;
51
52
	/**
53
	 * @var string
54
	 */
55
	private $formId;
56
	/**
57
	 * @var null|string
58
	 */
59
	private $getPathBackup = null;
60
61
	/**
62
	 * @var null|\stdClass
63
	 */
64
	private $userSessionBackup = null;
65
66
	/**
67
	 * @param Storage $storage
68
	 *
69
	 * @return void
70
	 * @throws \Exception
71
	 */
72
	public function run(Storage $storage)
73
	{
74
		parent::run($storage);
75
		$this->checkParameters();
76
		$this->checkRequiredParameters();
77
		$this->setFormId();
78
		$this->initialize($storage);
79
		$this->checkSubmit($storage);
80
	}
81
82
	/**
83
	 * @param null|Application $application
84
	 *
85
	 * @throws \Exception
86
	 */
87
	public function render($application = null)
88
	{
89
		$request = $this->setPathBackup();
90
		$form = $this->renderTemplate($this->subTemplate);
91
		$this->resetPathBackup($request);
0 ignored issues
show
Documentation introduced by
$request is of type object<CloudControl\Cms\cc\Request>, but the function expects a object<cc\Request>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
92
		$this->setFormParameter($form);
93
94
		parent::render($application);
95
	}
96
97
	/**
98
	 * Checks if parameters were given in the CMS configuration and
99
	 * sets them to their respective fields
100
	 */
101
	private function checkParameters()
102
	{
103
		$this->checkDocumentTypeParameter();
104
		$this->checkResponseFolderParameter();
105
		$this->checkSubTemplateParameter();
106
		$this->checkFormParameterNameParameter();
107
		$this->checkThankYouMessageParameter();
108
		$this->checkSubmitOncePerSessionParameter();
109
	}
110
111
	/**
112
	 * Sets variables needed for rendering the form template
113
	 *
114
	 * @param Storage $storage
115
	 */
116
	private function initialize($storage)
117
	{
118
		$this->parameters[self::PARAMETER_SMALLEST_IMAGE] = $storage->getImageSet()->getSmallestImageSet();
119
		$this->parameters[self::PARAMETER_CMS_PREFIX] = '';
120
121
		$this->parameters[self::PARAMETER_DOCUMENT_TYPE] = $this->storage->getDocumentTypes()->getDocumentTypeBySlug($this->documentType, true);
122
		$this->parameters[self::PARAMETER_DOCUMENT_TYPES] = $this->storage->getDocumentTypes()->getDocumentTypes();
123
		$this->parameters[self::PARAMETER_HIDE_TITLE_AND_STATE] = true;
124
		$this->parameters[self::PARAMETER_FORM_ID] = $this->formId;
125
	}
126
127
	/**
128
	 * If the form has been submitted, save the document
129
	 * Calls $this->postSubmit() afterwards
130
	 *
131
	 * @param Storage $storage
132
	 */
133
	private function checkSubmit($storage)
134
	{
135
		if ($this->isFormSubmitted($this->request) && $this->isSubmitAllowed()) {
136
			$postValues = $this->getPostValues($this->request);
0 ignored issues
show
Documentation introduced by
$this->request is of type object<CloudControl\Cms\cc\Request>, but the function expects a object<cc\Request>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
137
			$this->setUserSessionBackup();
138
			$storage->getDocuments()->addDocument($postValues);
139
			$this->restoreUserSessionBackup();
140
			$this->setSubmitToSession();
141
			$this->postSubmit($postValues, $storage);
142
		}
143
	}
144
145
	/**
146
	 * Hook for derived classes to take actions after
147
	 * submitting the form
148
	 *
149
	 * @param $postValues
150
	 * @param $storage
151
	 */
152
	protected function postSubmit($postValues, $storage)
0 ignored issues
show
Unused Code introduced by
The parameter $postValues is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $storage is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
153
	{}
154
155
	/**
156
	 * Sets a unique id for this particular form, so it can recognize
157
	 * it when a submit occurs
158
	 */
159
	private function setFormId()
160
	{
161
		if (isset($_SESSION[self::SESSION_PARAMETER_FORM_COMPONENT][$this->formParameterName][self::PARAMETER_FORM_ID])) {
162
			$this->formId = $_SESSION[self::SESSION_PARAMETER_FORM_COMPONENT][$this->formParameterName][self::PARAMETER_FORM_ID];
163
		} else {
164
			$_SESSION[self::SESSION_PARAMETER_FORM_COMPONENT][$this->formParameterName][self::PARAMETER_FORM_ID] = (string)microtime(true);
165
			$_SESSION[self::SESSION_PARAMETER_FORM_COMPONENT][$this->formParameterName]['submitted'] = false;
166
			$this->formId = $_SESSION[self::SESSION_PARAMETER_FORM_COMPONENT][$this->formParameterName][self::PARAMETER_FORM_ID];
167
		}
168
	}
169
170
	/**
171
	 * Checks if this form has been submitted
172
	 *
173
	 * @param \CloudControl\Cms\cc\Request $request
174
	 *
175
	 * @return bool
176
	 */
177
	private function isFormSubmitted($request)
178
	{
179
		return !empty($request::$post) && isset($request::$post[self::PARAMETER_FORM_ID]) && $request::$post[self::PARAMETER_FORM_ID] === $this->formId && isset($_SESSION[self::SESSION_PARAMETER_FORM_COMPONENT][$this->formParameterName][self::PARAMETER_FORM_ID]) && $_SESSION[self::SESSION_PARAMETER_FORM_COMPONENT][$this->formParameterName][self::PARAMETER_FORM_ID] === $this->formId;
180
	}
181
182
	/**
183
	 * @param \cc\Request $request
184
	 * @return array
185
	 */
186
	private function getPostValues($request)
187
	{
188
		$postValues = $request::$post;
189
		$postValues[self::PARAMETER_DOCUMENT_TYPE] = $this->documentType;
190
		$postValues[self::GET_PARAMETER_PATH] = $this->responseFolder;
191
		$postValues['title'] = date('r') . ' - From: ' . $request::$requestUri;
192
193
		return $postValues;
194
	}
195
196
	/**
197
	 * Temporarily stores the current user session in a backup variable
198
	 * and sets a fake user instead
199
	 */
200
	private function setUserSessionBackup()
201
	{
202
		$this->userSessionBackup = isset($_SESSION[self::SESSION_PARAMETER_CLOUDCONTROL]) ? $_SESSION[self::SESSION_PARAMETER_CLOUDCONTROL] : null;
203
		$fakeUser = new \stdClass();
204
		$fakeUser->username = self::SESSION_PARAMETER_FORM_COMPONENT;
205
		$_SESSION[self::SESSION_PARAMETER_CLOUDCONTROL] = $fakeUser;
206
	}
207
208
	/**
209
	 * Removes the fake user and restores the existing user
210
	 * session if it was there
211
	 */
212
	private function restoreUserSessionBackup()
213
	{
214
		if ($this->userSessionBackup === null) {
215
			unset($_SESSION[self::SESSION_PARAMETER_CLOUDCONTROL]);
216
		} else {
217
			$_SESSION[self::SESSION_PARAMETER_CLOUDCONTROL] = $this->userSessionBackup;
218
		}
219
	}
220
221
	private function setSubmitToSession()
222
	{
223
		$_SESSION[self::SESSION_PARAMETER_FORM_COMPONENT][$this->formParameterName]['submitted'] = true;
224
	}
225
226
	private function isSubmitAllowed()
227
	{
228
		if ($this->submitOncePerSession === true && $_SESSION[self::SESSION_PARAMETER_FORM_COMPONENT][$this->formParameterName]['submitted'] === true) {
229
			return false;
230
		} else {
231
			return true;
232
		}
233
	}
234
235 View Code Duplication
	private function checkDocumentTypeParameter()
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...
236
	{
237
		if (isset($this->parameters[self::PARAMETER_DOCUMENT_TYPE])) {
238
			$this->documentType = $this->parameters[self::PARAMETER_DOCUMENT_TYPE];
239
			unset($this->parameters[self::PARAMETER_DOCUMENT_TYPE]);
240
		}
241
	}
242
243 View Code Duplication
	private function checkResponseFolderParameter()
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...
244
	{
245
		if (isset($this->parameters[self::PARAMETER_RESPONSE_FOLDER])) {
246
			$this->responseFolder = $this->parameters[self::PARAMETER_RESPONSE_FOLDER];
247
			unset($this->parameters[self::PARAMETER_RESPONSE_FOLDER]);
248
		}
249
	}
250
251 View Code Duplication
	private function checkSubTemplateParameter()
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...
252
	{
253
		if (isset($this->parameters[self::PARAMETER_SUB_TEMPLATE])) {
254
			$this->subTemplate = $this->parameters[self::PARAMETER_SUB_TEMPLATE];
255
			unset($this->parameters[self::PARAMETER_SUB_TEMPLATE]);
256
		}
257
	}
258
259 View Code Duplication
	private function checkFormParameterNameParameter()
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...
260
	{
261
		if (isset($this->parameters[self::PARAMETER_FORM_PARAMETER_NAME])) {
262
			$this->formParameterName = $this->parameters[self::PARAMETER_FORM_PARAMETER_NAME];
263
			unset($this->parameters[self::PARAMETER_FORM_PARAMETER_NAME]);
264
		}
265
	}
266
267 View Code Duplication
	private function checkThankYouMessageParameter()
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...
268
	{
269
		if (isset($this->parameters[self::PARAMETER_THANK_YOU_MESSAGE])) {
270
			$this->thankYouMessage = $this->parameters[self::PARAMETER_THANK_YOU_MESSAGE];
271
			unset($this->parameters[self::PARAMETER_THANK_YOU_MESSAGE]);
272
		}
273
	}
274
275 View Code Duplication
	private function checkSubmitOncePerSessionParameter()
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...
276
	{
277
		if (isset($this->parameters[self::PARAMETER_SUBMIT_ONCE_PER_SESSION])) {
278
			$this->submitOncePerSession = $this->parameters[self::PARAMETER_SUBMIT_ONCE_PER_SESSION] === 'true';
279
			unset($this->parameters[self::PARAMETER_SUBMIT_ONCE_PER_SESSION]);
280
		}
281
	}
282
283
	/**
284
	 * @throws \Exception
285
	 */
286
	private function checkRequiredParameters()
287
	{
288
		if ($this->documentType === null || $this->responseFolder === null) {
289
			throw new \Exception('Parameters `documentType` and `responseFolder` are required for usage with this form');
290
		}
291
	}
292
293
	/**
294
	 * @return \cc\Request
295
	 */
296
	private function setPathBackup()
297
	{
298
		$request = $this->request;
299
		if (isset($request::$get[self::GET_PARAMETER_PATH])) {
300
			$this->getPathBackup = $request::$get[self::GET_PARAMETER_PATH];
301
		}
302
		$request::$get[self::GET_PARAMETER_PATH] = $this->responseFolder;
303
304
		return $request;
305
	}
306
307
	/**
308
	 * @param \cc\Request $request
309
	 */
310
	private function resetPathBackup($request)
311
	{
312
		if ($this->getPathBackup !== null) {
313
			$request::$get[self::GET_PARAMETER_PATH] = $this->getPathBackup;
314
		} else {
315
			unset($request::$get[self::GET_PARAMETER_PATH]);
316
		}
317
	}
318
319
	/**
320
	 * @param $form
321
	 */
322
	private function setFormParameter($form)
323
	{
324
		if ($this->isFormSubmitted($this->request) || $this->isSubmitAllowed() === false) {
325
			$this->parameters[$this->formParameterName] = '<a name="' . $this->formId . '"></a>' . $this->thankYouMessage;
326
		} else {
327
			$this->parameters[$this->formParameterName] = $form;
328
		}
329
	}
330
}