Total Complexity | 67 |
Total Lines | 532 |
Duplicated Lines | 0 % |
Changes | 8 | ||
Bugs | 1 | Features | 1 |
Complex classes like Localization_Editor 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 Localization_Editor, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
26 | class Localization_Editor implements Interface_Optionable |
||
|
|||
27 | { |
||
28 | use Traits_Optionable; |
||
29 | |||
30 | const MESSAGE_INFO = 'info'; |
||
31 | const MESSAGE_ERROR = 'danger'; |
||
32 | const MESSAGE_WARNING = 'warning'; |
||
33 | const MESSAGE_SUCCESS = 'success'; |
||
34 | |||
35 | const ERROR_NO_SOURCES_AVAILABLE = 40001; |
||
36 | const ERROR_LOCAL_PATH_NOT_FOUND = 40002; |
||
37 | const ERROR_STRING_HASH_WITHOUT_TEXT = 40003; |
||
38 | const VARIABLE_STRINGS = 'strings'; |
||
39 | const VARIABLE_SAVE = 'save'; |
||
40 | const VARIABLE_SCAN = 'scan'; |
||
41 | const VARIABLE_WARNINGS = 'warnings'; |
||
42 | |||
43 | /** |
||
44 | * @var string |
||
45 | */ |
||
46 | protected $installPath; |
||
47 | |||
48 | /** |
||
49 | * @var Localization_Source[] |
||
50 | */ |
||
51 | protected $sources; |
||
52 | |||
53 | /** |
||
54 | * @var Request |
||
55 | */ |
||
56 | protected $request; |
||
57 | |||
58 | /** |
||
59 | * @var Localization_Source |
||
60 | */ |
||
61 | protected $activeSource; |
||
62 | |||
63 | /** |
||
64 | * @var Localization_Scanner |
||
65 | */ |
||
66 | protected $scanner; |
||
67 | |||
68 | /** |
||
69 | * @var Localization_Locale[] |
||
70 | */ |
||
71 | protected array $appLocales = array(); |
||
72 | |||
73 | /** |
||
74 | * @var Localization_Locale |
||
75 | */ |
||
76 | protected $activeAppLocale; |
||
77 | |||
78 | /** |
||
79 | * @var Localization_Editor_Filters |
||
80 | */ |
||
81 | protected $filters; |
||
82 | |||
83 | /** |
||
84 | * @var array<string,string> |
||
85 | */ |
||
86 | protected $requestParams = array(); |
||
87 | |||
88 | /** |
||
89 | * @var string |
||
90 | */ |
||
91 | protected $varPrefix = 'applocalize_'; |
||
92 | |||
93 | /** |
||
94 | * @var int |
||
95 | */ |
||
96 | protected $perPage = 20; |
||
97 | |||
98 | /** |
||
99 | * @throws Localization_Exception |
||
100 | * @see \AppLocalize\Localization_Editor::ERROR_LOCAL_PATH_NOT_FOUND |
||
101 | */ |
||
102 | public function __construct() |
||
103 | { |
||
104 | $path = realpath(__DIR__.'/../'); |
||
105 | if($path === false) |
||
106 | { |
||
107 | throw new Localization_Exception( |
||
108 | 'Local path not found', |
||
109 | sprintf( |
||
110 | 'Could not get the parent folder\'s real path from [%s].', |
||
111 | __DIR__ |
||
112 | ), |
||
113 | self::ERROR_LOCAL_PATH_NOT_FOUND |
||
114 | ); |
||
115 | } |
||
116 | |||
117 | $this->installPath = $path; |
||
118 | $this->request = new Request(); |
||
119 | $this->scanner = Localization::createScanner(); |
||
120 | $this->scanner->load(); |
||
121 | |||
122 | $this->initSession(); |
||
123 | $this->initAppLocales(); |
||
124 | } |
||
125 | |||
126 | public function getRequest() : Request |
||
127 | { |
||
128 | return $this->request; |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | * Adds a request parameter that will be persisted in all URLs |
||
133 | * within the editor. This can be used when integrating the |
||
134 | * editor in an existing page that needs specific request params. |
||
135 | * |
||
136 | * @param string $name |
||
137 | * @param string $value |
||
138 | * @return Localization_Editor |
||
139 | */ |
||
140 | public function addRequestParam(string $name, string $value) : Localization_Editor |
||
141 | { |
||
142 | $this->requestParams[$name] = $value; |
||
143 | return $this; |
||
144 | } |
||
145 | |||
146 | public function getActiveSource() : Localization_Source |
||
147 | { |
||
148 | return $this->activeSource; |
||
149 | } |
||
150 | |||
151 | protected function initSession() : void |
||
152 | { |
||
153 | if(session_status() != PHP_SESSION_ACTIVE) { |
||
154 | session_start(); |
||
155 | } |
||
156 | |||
157 | if(!isset($_SESSION['localization_messages'])) { |
||
158 | $_SESSION['localization_messages'] = array(); |
||
159 | } |
||
160 | } |
||
161 | |||
162 | public function getVarName(string $name) : string |
||
163 | { |
||
164 | return $this->varPrefix.$name; |
||
165 | } |
||
166 | |||
167 | /** |
||
168 | * @throws Localization_Exception |
||
169 | */ |
||
170 | protected function initSources() : void |
||
171 | { |
||
172 | $this->sources = Localization::getSources(); |
||
173 | |||
174 | if(empty($this->sources)) |
||
175 | { |
||
176 | throw new Localization_Exception( |
||
177 | 'Cannot start editor: no sources defined.', |
||
178 | null, |
||
179 | self::ERROR_NO_SOURCES_AVAILABLE |
||
180 | ); |
||
181 | } |
||
182 | |||
183 | $activeID = $this->request->registerParam($this->getVarName('source'))->setEnum(Localization::getSourceIDs())->get(); |
||
184 | if(empty($activeID)) { |
||
185 | $activeID = $this->getDefaultSourceID(); |
||
186 | } |
||
187 | |||
188 | $this->activeSource = Localization::getSourceByID($activeID); |
||
189 | } |
||
190 | |||
191 | protected function getDefaultSourceID() : string |
||
192 | { |
||
193 | $default = $this->getOption('default-source'); |
||
194 | if(!empty($default) && Localization::sourceAliasExists($default)) { |
||
195 | return Localization::getSourceByAlias($default)->getID(); |
||
196 | } |
||
197 | |||
198 | return $this->sources[0]->getID(); |
||
199 | } |
||
200 | |||
201 | protected function initAppLocales() : void |
||
202 | { |
||
203 | $names = array(); |
||
204 | |||
205 | $locales = Localization::getAppLocales(); |
||
206 | foreach($locales as $locale) { |
||
207 | if(!$locale->isNative()) { |
||
208 | $this->appLocales[] = $locale; |
||
209 | $names[] = $locale->getName(); |
||
210 | } |
||
211 | } |
||
212 | |||
213 | // use the default locale if no other is available. |
||
214 | if(empty($names)) { |
||
215 | $this->activeAppLocale = Localization::getAppLocale(); |
||
216 | return; |
||
217 | } |
||
218 | |||
219 | $activeID = $this->request->registerParam($this->getVarName('locale'))->setEnum($names)->get(); |
||
220 | if(empty($activeID)) { |
||
221 | $activeID = $this->appLocales[0]->getName(); |
||
222 | } |
||
223 | |||
224 | $this->activeAppLocale = Localization::getAppLocaleByName($activeID); |
||
225 | |||
226 | Localization::selectAppLocale($activeID); |
||
227 | } |
||
228 | |||
229 | /** |
||
230 | * @return Localization_Locale[] |
||
231 | */ |
||
232 | public function getAppLocales() : array |
||
233 | { |
||
234 | return $this->appLocales; |
||
235 | } |
||
236 | |||
237 | /** |
||
238 | * @return Localization_Source[] |
||
239 | */ |
||
240 | public function getSources() : array |
||
241 | { |
||
242 | return $this->sources; |
||
243 | } |
||
244 | |||
245 | public function getBackURL() : string |
||
246 | { |
||
247 | return strval($this->getOption('back-url')); |
||
248 | } |
||
249 | |||
250 | public function getBackButtonLabel() : string |
||
251 | { |
||
252 | return strval($this->getOption('back-label')); |
||
253 | } |
||
254 | |||
255 | public function getSaveVariableName() : string |
||
256 | { |
||
257 | return $this->getVarName(self::VARIABLE_SAVE); |
||
258 | } |
||
259 | |||
260 | public function getStringsVariableName() : string |
||
261 | { |
||
262 | return $this->getVarName(self::VARIABLE_STRINGS); |
||
263 | } |
||
264 | |||
265 | protected function handleActions() : void |
||
266 | { |
||
267 | $this->initSources(); |
||
268 | |||
269 | $this->filters = new Localization_Editor_Filters($this); |
||
270 | |||
271 | if($this->request->getBool($this->getVarName(self::VARIABLE_SCAN))) |
||
272 | { |
||
273 | $this->executeScan(); |
||
274 | } |
||
275 | else if($this->request->getBool($this->getSaveVariableName())) |
||
276 | { |
||
277 | $this->executeSave(); |
||
278 | } |
||
279 | } |
||
280 | |||
281 | public function getScanner() : Localization_Scanner |
||
282 | { |
||
283 | return $this->scanner; |
||
284 | } |
||
285 | |||
286 | /** |
||
287 | * @return string |
||
288 | * @throws OutputBuffering_Exception |
||
289 | */ |
||
290 | public function render() : string |
||
291 | { |
||
292 | $this->handleActions(); |
||
293 | |||
294 | return (new Localization_Editor_Template_PageScaffold($this))->render(); |
||
295 | } |
||
296 | |||
297 | /** |
||
298 | * @return Localization_Scanner_StringsCollection_Warning[] |
||
299 | */ |
||
300 | public function getScannerWarnings() : array |
||
301 | { |
||
302 | return $this->scanner->getWarnings(); |
||
303 | } |
||
304 | |||
305 | public function hasAppLocales() : bool |
||
308 | } |
||
309 | |||
310 | public function isShowWarningsEnabled() : bool |
||
311 | { |
||
312 | return $this->request->getBool($this->getVarName(self::VARIABLE_WARNINGS)); |
||
313 | } |
||
314 | |||
315 | public function getFilters() : Localization_Editor_Filters |
||
316 | { |
||
317 | return $this->filters; |
||
318 | } |
||
319 | |||
320 | /** |
||
321 | * @return Localization_Scanner_StringHash[] |
||
322 | */ |
||
323 | public function getFilteredStrings() : array |
||
324 | { |
||
325 | $strings = $this->activeSource->getSourceScanner($this->scanner)->getHashes(); |
||
326 | |||
327 | $result = array(); |
||
328 | |||
329 | foreach($strings as $string) |
||
330 | { |
||
331 | if($this->filters->isStringMatch($string)) { |
||
332 | $result[] = $string; |
||
333 | } |
||
334 | } |
||
335 | |||
336 | return $result; |
||
337 | } |
||
338 | |||
339 | public function getRequestParams() : array |
||
340 | { |
||
341 | $params = $this->requestParams; |
||
342 | $params[$this->getVarName('locale')] = $this->activeAppLocale->getName(); |
||
343 | $params[$this->getVarName('source')] = $this->activeSource->getID(); |
||
344 | $params[$this->getVarName('page')] = $this->getPageNumber(); |
||
345 | |||
346 | return $params; |
||
347 | } |
||
348 | |||
349 | public function getAmountPerPage() : int |
||
350 | { |
||
351 | return $this->perPage; |
||
352 | } |
||
353 | |||
354 | public function getPageNumber() : int |
||
355 | { |
||
356 | return intval($this->request |
||
357 | ->registerParam($this->getVarName('page')) |
||
358 | ->setInteger() |
||
359 | ->get(0) |
||
360 | ); |
||
361 | } |
||
362 | |||
363 | public function getActiveLocale() : Localization_Locale |
||
364 | { |
||
365 | return $this->activeAppLocale; |
||
366 | } |
||
367 | |||
368 | public function getPaginationURL(int $page, array $params=array()) : string |
||
373 | } |
||
374 | |||
375 | public function detectVariables(string $string) : array |
||
376 | { |
||
377 | $result = array(); |
||
378 | preg_match_all('/%[0-9]+d|%s|%[0-9]+\$s/i', $string, $result, PREG_PATTERN_ORDER); |
||
379 | |||
380 | if(isset($result[0]) && !empty($result[0])) { |
||
381 | return $result[0]; |
||
382 | } |
||
383 | |||
384 | return array(); |
||
385 | } |
||
386 | |||
387 | public function display() : void |
||
388 | { |
||
389 | echo $this->render(); |
||
390 | } |
||
391 | |||
392 | public function getSourceURL(Localization_Source $source, array $params=array()) : string |
||
393 | { |
||
394 | $params[$this->getVarName('source')] = $source->getID(); |
||
395 | |||
396 | return $this->getURL($params); |
||
397 | } |
||
398 | |||
399 | public function getLocaleURL(Localization_Locale $locale, array $params=array()) : string |
||
400 | { |
||
401 | $params[$this->getVarName('locale')] = $locale->getName(); |
||
402 | |||
403 | return $this->getURL($params); |
||
404 | } |
||
405 | |||
406 | public function getScanURL() : string |
||
407 | { |
||
408 | return $this->getSourceURL($this->activeSource, array($this->getVarName(self::VARIABLE_SCAN) => 'yes')); |
||
409 | } |
||
410 | |||
411 | public function getWarningsURL() : string |
||
412 | { |
||
413 | return $this->getSourceURL($this->activeSource, array($this->getVarName(self::VARIABLE_WARNINGS) => 'yes')); |
||
414 | } |
||
415 | |||
416 | public function getURL(array $params=array()) : string |
||
417 | { |
||
418 | $persist = $this->getRequestParams(); |
||
419 | |||
420 | foreach($persist as $name => $value) { |
||
421 | if(!isset($params[$name])) { |
||
422 | $params[$name] = $value; |
||
423 | } |
||
424 | } |
||
425 | |||
426 | return '?'.http_build_query($params); |
||
427 | } |
||
428 | |||
429 | /** |
||
430 | * @param string $url |
||
431 | * @return never-returns |
||
432 | */ |
||
433 | public function redirect(string $url) : void |
||
434 | { |
||
435 | header('Location:'.$url); |
||
436 | exit; |
||
437 | } |
||
438 | |||
439 | protected function executeScan() : void |
||
440 | { |
||
441 | $this->scanner->scan(); |
||
442 | |||
443 | $this->addMessage( |
||
444 | t('The source files have been analyzed successfully at %1$s.', date('H:i:s')), |
||
445 | self::MESSAGE_SUCCESS |
||
446 | ); |
||
447 | |||
448 | $this->redirect($this->getSourceURL($this->activeSource)); |
||
449 | } |
||
450 | |||
451 | protected function executeSave() : void |
||
452 | { |
||
453 | $data = $_POST; |
||
454 | |||
455 | $translator = Localization::getTranslator($this->activeAppLocale); |
||
456 | |||
457 | $strings = $data[$this->getVarName(self::VARIABLE_STRINGS)]; |
||
458 | foreach($strings as $hash => $text) |
||
459 | { |
||
460 | $text = trim($text); |
||
461 | |||
462 | if(empty($text)) { |
||
463 | continue; |
||
464 | } |
||
465 | |||
466 | $translator->setTranslation($hash, $text); |
||
467 | } |
||
468 | |||
469 | $translator->save($this->activeSource, $this->scanner->getCollection()); |
||
470 | |||
471 | // refresh all the client files |
||
472 | Localization::writeClientFiles(true); |
||
473 | |||
474 | $this->addMessage( |
||
475 | t('The texts have been updated successfully at %1$s.', date('H:i:s')), |
||
476 | self::MESSAGE_SUCCESS |
||
477 | ); |
||
478 | |||
479 | $this->redirect($this->getURL()); |
||
480 | } |
||
481 | |||
482 | protected function addMessage(string $message, string $type=self::MESSAGE_INFO) : void |
||
487 | ); |
||
488 | } |
||
489 | |||
490 | /** |
||
491 | * @return array<string,string> |
||
492 | */ |
||
493 | public function getDefaultOptions() : array |
||
494 | { |
||
495 | return array( |
||
496 | 'appname' => '', |
||
497 | 'default-source' => '', |
||
498 | 'back-url' => '', |
||
499 | 'back-label' => '' |
||
500 | ); |
||
501 | } |
||
502 | |||
503 | /** |
||
504 | * Sets the application name shown in the main navigation |
||
505 | * in the user interface. |
||
506 | * |
||
507 | * @param string $name |
||
508 | * @return Localization_Editor |
||
509 | */ |
||
510 | public function setAppName(string $name) : Localization_Editor |
||
511 | { |
||
512 | $this->setOption('appname', $name); |
||
513 | return $this; |
||
514 | } |
||
515 | |||
516 | public function getAppName() : string |
||
517 | { |
||
518 | $name = $this->getOption('appname'); |
||
519 | if(!empty($name)) { |
||
520 | return $name; |
||
521 | } |
||
522 | |||
523 | return t('Localization editor'); |
||
524 | } |
||
525 | |||
526 | /** |
||
527 | * Selects the default source to use if none has been |
||
528 | * explicitly selected. |
||
529 | * |
||
530 | * @param string $sourceID |
||
531 | * @return Localization_Editor |
||
532 | */ |
||
533 | public function selectDefaultSource(string $sourceID) : Localization_Editor |
||
534 | { |
||
535 | $this->setOption('default-source', $sourceID); |
||
536 | return $this; |
||
537 | } |
||
538 | |||
539 | /** |
||
540 | * Sets an URL that the translators can use to go back to |
||
541 | * the main application, for example if it is integrated into |
||
542 | * an existing application. |
||
543 | * |
||
544 | * @param string $url The URL to use for the link |
||
545 | * @param string $label Label of the link |
||
546 | * @return Localization_Editor |
||
547 | */ |
||
548 | public function setBackURL(string $url, string $label) : Localization_Editor |
||
553 | } |
||
554 | |||
555 | public function getInstallPath() : string |
||
556 | { |
||
557 | return $this->installPath; |
||
558 | } |
||
559 | } |
||
560 |
This interface has been deprecated. The supplier of the interface has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the interface will be removed and what other interface to use instead.