Total Complexity | 51 |
Total Lines | 337 |
Duplicated Lines | 0 % |
Changes | 24 | ||
Bugs | 6 | Features | 4 |
Complex classes like EmbedHelper 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.
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 EmbedHelper, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
24 | class EmbedHelper |
||
25 | { |
||
26 | /** |
||
27 | * Service container |
||
28 | * |
||
29 | * @var \Symfony\Component\DependencyInjection\Container |
||
30 | */ |
||
31 | protected $container; |
||
32 | |||
33 | /** |
||
34 | * Whether or not to consider exceptions thrown by the handler as formerrors. |
||
35 | * |
||
36 | * @var bool |
||
37 | */ |
||
38 | protected $isMarkExceptionsAsFormErrors; |
||
39 | |||
40 | |||
41 | /** |
||
42 | * Construct the helper with the service container. |
||
43 | * |
||
44 | * @param \Symfony\Component\DependencyInjection\Container $container |
||
45 | * @param bool $markExceptionsAsFormErrors |
||
46 | */ |
||
47 | public function __construct(Container $container, $markExceptionsAsFormErrors = false) |
||
48 | { |
||
49 | $this->container = $container; |
||
50 | $this->isMarkExceptionsAsFormErrors = $markExceptionsAsFormErrors; |
||
51 | } |
||
52 | |||
53 | /** |
||
54 | * Get the top most (root) element of the form view |
||
55 | * |
||
56 | * @param FormView $formView |
||
57 | * @return mixed |
||
58 | */ |
||
59 | public static function getFormRoot($formView) |
||
60 | { |
||
61 | $parent = $formView; |
||
62 | while (isset($parent->parent)) { |
||
63 | $parent = $parent->parent; |
||
64 | } |
||
65 | return $parent; |
||
66 | } |
||
67 | |||
68 | |||
69 | /** |
||
70 | * Generate an embedded url, adding the embedded parameters to the url |
||
71 | * |
||
72 | * @param string $route |
||
73 | * @param array $params |
||
74 | * @return string |
||
75 | */ |
||
76 | public function url($route, $params) |
||
82 | } |
||
83 | |||
84 | |||
85 | /** |
||
86 | * Returns the parameters to add to an embedded url from the current request. |
||
87 | * |
||
88 | * @return array |
||
89 | */ |
||
90 | public function getEmbedParams($checkSafety = true) |
||
106 | } |
||
107 | |||
108 | |||
109 | /** |
||
110 | * Handles a form and executes a callback to do definite handling. |
||
111 | * |
||
112 | * The embed helper utilizes a way to store the form state of a form after submitting, so when including a form |
||
113 | * in an ESI, the form state is kept until the next display of the form. When using the handleForm with |
||
114 | * XmlHttpRequests, the form state is not stored, since it is assumed the response will be used to display the |
||
115 | * data directly. |
||
116 | * |
||
117 | * The return value is either a Response object that can be returned as the result of the action, or it is an |
||
118 | * array which can be used in a template. |
||
119 | * |
||
120 | * @param \Symfony\Component\Form\Form $form |
||
121 | * @param \Symfony\Component\HttpFoundation\Request $request |
||
122 | * @param \callback $handlerCallback |
||
123 | * @param string $formTargetRoute |
||
124 | * @param array $formTargetParams |
||
125 | * @param array $extraViewVars |
||
126 | * @return array|Response |
||
127 | * @throws \Exception |
||
128 | */ |
||
129 | public function handleForm( |
||
130 | Form $form, |
||
131 | Request $request, |
||
132 | $handlerCallback, |
||
133 | $formTargetRoute, |
||
134 | $formTargetParams = array(), |
||
135 | $extraViewVars = array() |
||
136 | ) { |
||
137 | $formId = $this->getFormId($form); |
||
138 | if ($request->hasPreviousSession()) { |
||
139 | // cannot store errors iterator in session because somewhere there is a closure that can't be serialized |
||
140 | // therefore convert the errors iterator to array, on get from session convert to iterator |
||
141 | // see [1] |
||
|
|||
142 | $formState = $request->getSession()->get($formId); |
||
143 | $formState['form_errors'] = is_array($formState['form_errors']) ? $formState['form_errors'] : array(); |
||
144 | $formState['form_errors'] = new FormErrorIterator($form, $formState['form_errors']); |
||
145 | } else { |
||
146 | $formState = null; |
||
147 | } |
||
148 | $formStatus = ''; |
||
149 | |||
150 | |||
151 | // This only binds the form, so the event listeners are triggered, but no actual submit-handling is done. |
||
152 | // This is useful for AJAX requests which need to modify the form based on submitted data. |
||
153 | if ($request->get('_submit_type') === 'bind') { |
||
154 | $form->submit($request); |
||
155 | } elseif ($request->getMethod() == 'POST') { |
||
156 | $form->submit($request); |
||
157 | |||
158 | $returnUrl = $this->container->get(UrlCheckerService::class)->getSafeUrl($request->get('return_url')); |
||
159 | $successUrl = $this->container->get(UrlCheckerService::class)->getSafeUrl($request->get('success_url')); |
||
160 | |||
161 | $handlerResult = false; |
||
162 | |||
163 | |||
164 | // if it is valid, we can use the callback to handle the actual handling |
||
165 | if ($form->isValid()) { |
||
166 | try { |
||
167 | $handlerResult = call_user_func($handlerCallback, $request, $form, $this->container); |
||
168 | } catch (\Exception $e) { |
||
169 | if (!$this->isMarkExceptionsAsFormErrors) { |
||
170 | throw $e; |
||
171 | } else { |
||
172 | $form->addError($this->convertExceptionToFormError($e)); |
||
173 | } |
||
174 | } |
||
175 | |||
176 | if ($handlerResult) { |
||
177 | // any lingering errors may be removed now. |
||
178 | unset($formState['has_errors']); |
||
179 | unset($formState['data']); |
||
180 | unset($formState['form_errors']); |
||
181 | $formStatus = 'ok'; |
||
182 | |||
183 | if ($handlerResult && $handlerResult instanceof Response) { |
||
184 | return $handlerResult; |
||
185 | } elseif (is_array($handlerResult)) { |
||
186 | $extraViewVars = $handlerResult + $extraViewVars; |
||
187 | } |
||
188 | if ($successUrl) { |
||
189 | $returnUrl = $successUrl; |
||
190 | |||
191 | if ($request->isXmlHttpRequest()) { |
||
192 | return new JsonResponse(array('success_url' => $successUrl)); |
||
193 | } |
||
194 | } else { |
||
195 | // we set a convenience flash message if there was no success url, because |
||
196 | // we will probably return to the return url re-displaying the form. |
||
197 | $this->setFlashMessage($form, 'confirmed', $request->getSession()); |
||
198 | } |
||
199 | } else { |
||
200 | $formStatus = 'errors'; |
||
201 | |||
202 | $formState['has_errors'] = true; |
||
203 | $formState['data'] = $request->request->get($form->getName()); |
||
204 | $formState['form_errors'] = $form->getErrors(); |
||
205 | } |
||
206 | } else { |
||
207 | $formStatus = 'errors'; |
||
208 | |||
209 | $formState['has_errors'] = true; |
||
210 | $formState['data'] = $request->request->get($form->getName()); |
||
211 | $formState['form_errors'] = $form->getErrors(); |
||
212 | } |
||
213 | // redirect to the return url, if available |
||
214 | if ($returnUrl && !$request->isXmlHttpRequest()) { |
||
215 | $response = new RedirectResponse($returnUrl); |
||
216 | } |
||
217 | } elseif (!empty($formState['has_errors'])) { |
||
218 | $formStatus = 'errors'; |
||
219 | |||
220 | // see if there were any errors left in the session from a previous post, so we show them |
||
221 | if (!empty($formState['data']) && is_array($formState['data'])) { |
||
222 | $form->submit($formState['data']); |
||
223 | unset($formState['data']); |
||
224 | } |
||
225 | if (!empty($formState['form_errors'])) { |
||
226 | foreach ($formState['form_errors'] as $error) { |
||
227 | $form->addError($error); |
||
228 | } |
||
229 | } |
||
230 | // and we only show them once. |
||
231 | unset($formState['has_errors']); |
||
232 | unset($formState['form_errors']); |
||
233 | } |
||
234 | |||
235 | if ($formState && !$request->isXmlHttpRequest()) { |
||
236 | if (!empty($formState['form_errors'])) { |
||
237 | |||
238 | // 1. You cannot serialize or un-serialize PDO instances |
||
239 | // 2. We do not want to store cause and origin in the session since these can become quite large |
||
240 | foreach ($formState['form_errors'] as $key => $error) { |
||
241 | $refObject = new \ReflectionObject($error); |
||
242 | $refCauseProperty = $refObject->getProperty('cause'); |
||
243 | $refCauseProperty->setAccessible(true); |
||
244 | $refCauseProperty->setValue($error, null); |
||
245 | $refOriginProperty = $refObject->getProperty('origin'); |
||
246 | $refOriginProperty->setAccessible(true); |
||
247 | $refOriginProperty->setValue($error, null); |
||
248 | } |
||
249 | } |
||
250 | |||
251 | // see [1] for explanation |
||
252 | if (!isset($formState['form_errors'])) { |
||
253 | $formState['form_errors'] = []; |
||
254 | } elseif ($formState['form_errors'] instanceof \Traversable) { |
||
255 | $formState['form_errors'] = iterator_to_array($formState['form_errors']); |
||
256 | } |
||
257 | |||
258 | $request->getSession()->set($formId, $formState); |
||
259 | } elseif ($request->hasPreviousSession()) { |
||
260 | $request->getSession()->remove($formId); |
||
261 | } |
||
262 | |||
263 | $viewVars = $extraViewVars; |
||
264 | |||
265 | if (empty($response)) { |
||
266 | if ($request->get('extension')) { |
||
267 | $formTargetParams += array( |
||
268 | 'extension' => $request->get('extension') |
||
269 | ); |
||
270 | } |
||
271 | $viewVars['form_status'] = $formStatus; |
||
272 | |||
273 | $viewVars['form_url'] = $this->url($formTargetRoute, $formTargetParams); |
||
274 | $viewVars['form'] = $form->createView(); |
||
275 | |||
276 | $prefix = ''; |
||
277 | if ($root = self::getFormRoot($viewVars['form'])) { |
||
278 | $prefix = sprintf('form_messages.%s.', strtolower($root->vars['name'])); |
||
279 | } |
||
280 | |||
281 | $viewVars['messages'] = []; |
||
282 | if ($request->hasPreviousSession() && ($messages = $this->container->get('session')->getFlashBag()->get($formId))) { |
||
283 | foreach ($messages as $value) { |
||
284 | $viewVars['messages'][] = $prefix . $value; |
||
285 | } |
||
286 | } |
||
287 | |||
288 | |||
289 | return $viewVars; |
||
290 | } |
||
291 | |||
292 | return $response; |
||
293 | } |
||
294 | |||
295 | |||
296 | /** |
||
297 | * Returns the ID the form's state is stored by in the session |
||
298 | * |
||
299 | * @param \Symfony\Component\Form\FormInterface $form |
||
300 | * @return mixed |
||
301 | */ |
||
302 | public function getFormId(FormInterface $form) |
||
303 | { |
||
304 | if (is_object($form->getData())) { |
||
305 | $ret = preg_replace('/\W/', '_', get_class($form->getData())); |
||
306 | } else { |
||
307 | if ($form->getName()) { |
||
308 | return (string)$form->getName(); |
||
309 | } else { |
||
310 | return preg_replace('/\W/', '_', get_class($form)); |
||
311 | } |
||
312 | } |
||
313 | |||
314 | return $ret; |
||
315 | } |
||
316 | |||
317 | |||
318 | /** |
||
319 | * @param bool $markExceptionsAsFormErrors |
||
320 | */ |
||
321 | public function setMarkExceptionsAsFormErrors($markExceptionsAsFormErrors) |
||
324 | } |
||
325 | |||
326 | |||
327 | /** |
||
328 | * Provides a way of customizing error messages based on type of exception, etc. |
||
329 | * |
||
330 | * @param \Exception $exception |
||
331 | * @return FormError |
||
332 | */ |
||
333 | protected function convertExceptionToFormError($exception) |
||
336 | } |
||
337 | |||
338 | /** |
||
339 | * A generic way to set flash messages. |
||
340 | * |
||
341 | * When using this make sure you set the following parameter in your parameters.yml to avoid stacking of messages |
||
342 | * when they are not shown or rendered in templates |
||
343 | * |
||
344 | * session.flashbag.class: Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag |
||
345 | * |
||
346 | * Messages will be pushed to a viewVars called 'messages' see $this->handleForm |
||
347 | * |
||
348 | * @param Form $form |
||
349 | * @param string $message |
||
350 | */ |
||
351 | public function setFlashMessage(Form $form, $message, SessionInterface $session = null) |
||
363 |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.