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 classes like Application 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 Application, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
60 | class Application |
||
61 | { |
||
62 | private $commands = array(); |
||
63 | private $wantHelps = false; |
||
64 | private $runningCommand; |
||
65 | private $name; |
||
66 | private $version; |
||
67 | private $catchExceptions = true; |
||
68 | private $autoExit = true; |
||
69 | private $definition; |
||
70 | private $helperSet; |
||
71 | private $dispatcher; |
||
72 | private $terminalDimensions; |
||
73 | private $defaultCommand; |
||
74 | |||
75 | /** |
||
76 | * Constructor. |
||
77 | * |
||
78 | * @param string $name The name of the application |
||
79 | * @param string $version The version of the application |
||
80 | * |
||
81 | * @api |
||
82 | */ |
||
83 | public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') |
||
95 | |||
96 | public function setDispatcher(EventDispatcherInterface $dispatcher) |
||
97 | { |
||
98 | $this->dispatcher = $dispatcher; |
||
99 | } |
||
100 | |||
101 | /** |
||
102 | * Runs the current application. |
||
103 | * |
||
104 | * @param InputInterface $input An Input instance |
||
105 | * @param OutputInterface $output An Output instance |
||
106 | * |
||
107 | * @return int 0 if everything went fine, or an error code |
||
108 | * |
||
109 | * @throws \Exception When doRun returns Exception |
||
110 | * |
||
111 | * @api |
||
112 | */ |
||
113 | public function run(InputInterface $input = null, OutputInterface $output = null) |
||
159 | |||
160 | /** |
||
161 | * Runs the current application. |
||
162 | * |
||
163 | * @param InputInterface $input An Input instance |
||
164 | * @param OutputInterface $output An Output instance |
||
165 | * |
||
166 | * @return int 0 if everything went fine, or an error code |
||
167 | */ |
||
168 | public function doRun(InputInterface $input, OutputInterface $output) |
||
200 | |||
201 | /** |
||
202 | * Set a helper set to be used with the command. |
||
203 | * |
||
204 | * @param HelperSet $helperSet The helper set |
||
205 | * |
||
206 | * @api |
||
207 | */ |
||
208 | public function setHelperSet(HelperSet $helperSet) |
||
212 | |||
213 | /** |
||
214 | * Get the helper set associated with the command. |
||
215 | * |
||
216 | * @return HelperSet The HelperSet instance associated with this command |
||
217 | * |
||
218 | * @api |
||
219 | */ |
||
220 | public function getHelperSet() |
||
224 | |||
225 | /** |
||
226 | * Set an input definition set to be used with this application. |
||
227 | * |
||
228 | * @param InputDefinition $definition The input definition |
||
229 | * |
||
230 | * @api |
||
231 | */ |
||
232 | public function setDefinition(InputDefinition $definition) |
||
233 | { |
||
234 | $this->definition = $definition; |
||
235 | } |
||
236 | |||
237 | /** |
||
238 | * Gets the InputDefinition related to this Application. |
||
239 | * |
||
240 | * @return InputDefinition The InputDefinition instance |
||
241 | */ |
||
242 | public function getDefinition() |
||
246 | |||
247 | /** |
||
248 | * Gets the help message. |
||
249 | * |
||
250 | * @return string A help message. |
||
251 | */ |
||
252 | public function getHelp() |
||
256 | |||
257 | /** |
||
258 | * Sets whether to catch exceptions or not during commands execution. |
||
259 | * |
||
260 | * @param bool $boolean Whether to catch exceptions or not during commands execution |
||
261 | * |
||
262 | * @api |
||
263 | */ |
||
264 | public function setCatchExceptions($boolean) |
||
268 | |||
269 | /** |
||
270 | * Sets whether to automatically exit after a command execution or not. |
||
271 | * |
||
272 | * @param bool $boolean Whether to automatically exit after a command execution or not |
||
273 | * |
||
274 | * @api |
||
275 | */ |
||
276 | public function setAutoExit($boolean) |
||
280 | |||
281 | /** |
||
282 | * Gets the name of the application. |
||
283 | * |
||
284 | * @return string The application name |
||
285 | * |
||
286 | * @api |
||
287 | */ |
||
288 | public function getName() |
||
292 | |||
293 | /** |
||
294 | * Sets the application name. |
||
295 | * |
||
296 | * @param string $name The application name |
||
297 | * |
||
298 | * @api |
||
299 | */ |
||
300 | public function setName($name) |
||
304 | |||
305 | /** |
||
306 | * Gets the application version. |
||
307 | * |
||
308 | * @return string The application version |
||
309 | * |
||
310 | * @api |
||
311 | */ |
||
312 | public function getVersion() |
||
316 | |||
317 | /** |
||
318 | * Sets the application version. |
||
319 | * |
||
320 | * @param string $version The application version |
||
321 | * |
||
322 | * @api |
||
323 | */ |
||
324 | public function setVersion($version) |
||
328 | |||
329 | /** |
||
330 | * Returns the long version of the application. |
||
331 | * |
||
332 | * @return string The long application version |
||
333 | * |
||
334 | * @api |
||
335 | */ |
||
336 | public function getLongVersion() |
||
344 | |||
345 | /** |
||
346 | * Registers a new command. |
||
347 | * |
||
348 | * @param string $name The command name |
||
349 | * |
||
350 | * @return Command The newly created command |
||
351 | * |
||
352 | * @api |
||
353 | */ |
||
354 | public function register($name) |
||
358 | |||
359 | /** |
||
360 | * Adds an array of command objects. |
||
361 | * |
||
362 | * @param Command[] $commands An array of commands |
||
363 | * |
||
364 | * @api |
||
365 | */ |
||
366 | public function addCommands(array $commands) |
||
372 | |||
373 | /** |
||
374 | * Adds a command object. |
||
375 | * |
||
376 | * If a command with the same name already exists, it will be overridden. |
||
377 | * |
||
378 | * @param Command $command A Command object |
||
379 | * |
||
380 | * @return Command The registered command |
||
381 | * |
||
382 | * @api |
||
383 | */ |
||
384 | public function add(Command $command) |
||
406 | |||
407 | /** |
||
408 | * Returns a registered command by name or alias. |
||
409 | * |
||
410 | * @param string $name The command name or alias |
||
411 | * |
||
412 | * @return Command A Command object |
||
413 | * |
||
414 | * @throws \InvalidArgumentException When command name given does not exist |
||
415 | * |
||
416 | * @api |
||
417 | */ |
||
418 | public function get($name) |
||
437 | |||
438 | /** |
||
439 | * Returns true if the command exists, false otherwise. |
||
440 | * |
||
441 | * @param string $name The command name or alias |
||
442 | * |
||
443 | * @return bool true if the command exists, false otherwise |
||
444 | * |
||
445 | * @api |
||
446 | */ |
||
447 | public function has($name) |
||
451 | |||
452 | /** |
||
453 | * Returns an array of all unique namespaces used by currently registered commands. |
||
454 | * |
||
455 | * It does not returns the global namespace which always exists. |
||
456 | * |
||
457 | * @return array An array of namespaces |
||
458 | */ |
||
459 | public function getNamespaces() |
||
472 | |||
473 | /** |
||
474 | * Finds a registered namespace by a name or an abbreviation. |
||
475 | * |
||
476 | * @param string $namespace A namespace or abbreviation to search for |
||
477 | * |
||
478 | * @return string A registered namespace |
||
479 | * |
||
480 | * @throws \InvalidArgumentException When namespace is incorrect or ambiguous |
||
481 | */ |
||
482 | public function findNamespace($namespace) |
||
511 | |||
512 | /** |
||
513 | * Finds a command by name or alias. |
||
514 | * |
||
515 | * Contrary to get, this command tries to find the best |
||
516 | * match if you give it an abbreviation of a name or alias. |
||
517 | * |
||
518 | * @param string $name A command name or a command alias |
||
519 | * |
||
520 | * @return Command A Command instance |
||
521 | * |
||
522 | * @throws \InvalidArgumentException When command name is incorrect or ambiguous |
||
523 | * |
||
524 | * @api |
||
525 | */ |
||
526 | public function find($name) |
||
571 | |||
572 | /** |
||
573 | * Gets the commands (registered in the given namespace if provided). |
||
574 | * |
||
575 | * The array keys are the full names and the values the command instances. |
||
576 | * |
||
577 | * @param string $namespace A namespace name |
||
578 | * |
||
579 | * @return Command[] An array of Command instances |
||
580 | * |
||
581 | * @api |
||
582 | */ |
||
583 | public function all($namespace = null) |
||
598 | |||
599 | /** |
||
600 | * Returns an array of possible abbreviations given a set of names. |
||
601 | * |
||
602 | * @param array $names An array of names |
||
603 | * |
||
604 | * @return array An array of abbreviations |
||
605 | */ |
||
606 | public static function getAbbreviations($names) |
||
618 | |||
619 | /** |
||
620 | * Returns a text representation of the Application. |
||
621 | * |
||
622 | * @param string $namespace An optional namespace name |
||
623 | * @param bool $raw Whether to return raw command list |
||
624 | * |
||
625 | * @return string A string representing the Application |
||
626 | * |
||
627 | * @deprecated Deprecated since version 2.3, to be removed in 3.0. |
||
628 | */ |
||
629 | View Code Duplication | public function asText($namespace = null, $raw = false) |
|
637 | |||
638 | /** |
||
639 | * Returns an XML representation of the Application. |
||
640 | * |
||
641 | * @param string $namespace An optional namespace name |
||
642 | * @param bool $asDom Whether to return a DOM or an XML string |
||
643 | * |
||
644 | * @return string|\DOMDocument An XML string representing the Application |
||
645 | * |
||
646 | * @deprecated Deprecated since version 2.3, to be removed in 3.0. |
||
647 | */ |
||
648 | View Code Duplication | public function asXml($namespace = null, $asDom = false) |
|
661 | |||
662 | /** |
||
663 | * Renders a caught exception. |
||
664 | * |
||
665 | * @param \Exception $e An exception instance |
||
666 | * @param OutputInterface $output An OutputInterface instance |
||
667 | */ |
||
668 | public function renderException($e, $output) |
||
737 | |||
738 | /** |
||
739 | * Tries to figure out the terminal width in which this application runs. |
||
740 | * |
||
741 | * @return int|null |
||
742 | */ |
||
743 | protected function getTerminalWidth() |
||
744 | { |
||
745 | $dimensions = $this->getTerminalDimensions(); |
||
746 | |||
747 | return $dimensions[0]; |
||
748 | } |
||
749 | |||
750 | /** |
||
751 | * Tries to figure out the terminal height in which this application runs. |
||
752 | * |
||
753 | * @return int|null |
||
754 | */ |
||
755 | protected function getTerminalHeight() |
||
756 | { |
||
757 | $dimensions = $this->getTerminalDimensions(); |
||
758 | |||
759 | return $dimensions[1]; |
||
760 | } |
||
761 | |||
762 | /** |
||
763 | * Tries to figure out the terminal dimensions based on the current environment. |
||
764 | * |
||
765 | * @return array Array containing width and height |
||
766 | */ |
||
767 | public function getTerminalDimensions() |
||
768 | { |
||
769 | if ($this->terminalDimensions) { |
||
770 | return $this->terminalDimensions; |
||
771 | } |
||
772 | |||
773 | if ('\\' === DIRECTORY_SEPARATOR) { |
||
774 | // extract [w, H] from "wxh (WxH)" |
||
775 | if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) { |
||
776 | return array((int) $matches[1], (int) $matches[2]); |
||
777 | } |
||
778 | // extract [w, h] from "wxh" |
||
779 | if (preg_match('/^(\d+)x(\d+)$/', $this->getConsoleMode(), $matches)) { |
||
780 | return array((int) $matches[1], (int) $matches[2]); |
||
781 | } |
||
782 | } |
||
783 | |||
784 | if ($sttyString = $this->getSttyColumns()) { |
||
785 | // extract [w, h] from "rows h; columns w;" |
||
786 | View Code Duplication | if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) { |
|
787 | return array((int) $matches[2], (int) $matches[1]); |
||
788 | } |
||
789 | // extract [w, h] from "; h rows; w columns" |
||
790 | View Code Duplication | if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) { |
|
791 | return array((int) $matches[2], (int) $matches[1]); |
||
792 | } |
||
793 | } |
||
794 | |||
795 | return array(null, null); |
||
796 | } |
||
797 | |||
798 | /** |
||
799 | * Sets terminal dimensions. |
||
800 | * |
||
801 | * Can be useful to force terminal dimensions for functional tests. |
||
802 | * |
||
803 | * @param int $width The width |
||
804 | * @param int $height The height |
||
805 | * |
||
806 | * @return Application The current application |
||
807 | */ |
||
808 | public function setTerminalDimensions($width, $height) |
||
809 | { |
||
810 | $this->terminalDimensions = array($width, $height); |
||
811 | |||
812 | return $this; |
||
813 | } |
||
814 | |||
815 | /** |
||
816 | * Configures the input and output instances based on the user arguments and options. |
||
817 | * |
||
818 | * @param InputInterface $input An InputInterface instance |
||
819 | * @param OutputInterface $output An OutputInterface instance |
||
820 | */ |
||
821 | protected function configureIO(InputInterface $input, OutputInterface $output) |
||
822 | { |
||
823 | if (true === $input->hasParameterOption(array('--ansi'))) { |
||
824 | $output->setDecorated(true); |
||
825 | } elseif (true === $input->hasParameterOption(array('--no-ansi'))) { |
||
826 | $output->setDecorated(false); |
||
827 | } |
||
828 | |||
829 | if (true === $input->hasParameterOption(array('--no-interaction', '-n'))) { |
||
830 | $input->setInteractive(false); |
||
831 | } elseif (function_exists('posix_isatty') && $this->getHelperSet()->has('question')) { |
||
832 | $inputStream = $this->getHelperSet()->get('question')->getInputStream(); |
||
833 | if (!@posix_isatty($inputStream)) { |
||
834 | $input->setInteractive(false); |
||
835 | } |
||
836 | } |
||
837 | |||
838 | if (true === $input->hasParameterOption(array('--quiet', '-q'))) { |
||
839 | $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); |
||
840 | } else { |
||
841 | if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) { |
||
842 | $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); |
||
843 | } elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) { |
||
844 | $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); |
||
845 | } elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) { |
||
846 | $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); |
||
847 | } |
||
848 | } |
||
849 | } |
||
850 | |||
851 | /** |
||
852 | * Runs the current command. |
||
853 | * |
||
854 | * If an event dispatcher has been attached to the application, |
||
855 | * events are also dispatched during the life-cycle of the command. |
||
856 | * |
||
857 | * @param Command $command A Command instance |
||
858 | * @param InputInterface $input An Input instance |
||
859 | * @param OutputInterface $output An Output instance |
||
860 | * |
||
861 | * @return int 0 if everything went fine, or an error code |
||
862 | * |
||
863 | * @throws \Exception when the command being run threw an exception |
||
864 | */ |
||
865 | protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output) |
||
866 | { |
||
867 | foreach ($command->getHelperSet() as $helper) { |
||
868 | if ($helper instanceof InputAwareInterface) { |
||
869 | $helper->setInput($input); |
||
870 | } |
||
871 | } |
||
872 | |||
873 | if (null === $this->dispatcher) { |
||
874 | return $command->run($input, $output); |
||
875 | } |
||
876 | |||
877 | $event = new ConsoleCommandEvent($command, $input, $output); |
||
878 | $this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event); |
||
879 | |||
880 | if ($event->commandShouldRun()) { |
||
881 | try { |
||
882 | $exitCode = $command->run($input, $output); |
||
883 | } catch (\Exception $e) { |
||
884 | $event = new ConsoleTerminateEvent($command, $input, $output, $e->getCode()); |
||
885 | $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); |
||
886 | |||
887 | $event = new ConsoleExceptionEvent($command, $input, $output, $e, $event->getExitCode()); |
||
888 | $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event); |
||
889 | |||
890 | throw $event->getException(); |
||
891 | } |
||
892 | } else { |
||
893 | $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED; |
||
894 | } |
||
895 | |||
896 | $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode); |
||
897 | $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); |
||
898 | |||
899 | return $event->getExitCode(); |
||
900 | } |
||
901 | |||
902 | /** |
||
903 | * Gets the name of the command based on input. |
||
904 | * |
||
905 | * @param InputInterface $input The input interface |
||
906 | * |
||
907 | * @return string The command name |
||
908 | */ |
||
909 | protected function getCommandName(InputInterface $input) |
||
913 | |||
914 | /** |
||
915 | * Gets the default input definition. |
||
916 | * |
||
917 | * @return InputDefinition An InputDefinition instance |
||
918 | */ |
||
919 | protected function getDefaultInputDefinition() |
||
920 | { |
||
921 | return new InputDefinition(array( |
||
922 | new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), |
||
923 | |||
924 | new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'), |
||
925 | new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'), |
||
926 | new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'), |
||
927 | new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'), |
||
928 | new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'), |
||
929 | new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'), |
||
930 | new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'), |
||
931 | )); |
||
932 | } |
||
933 | |||
934 | /** |
||
935 | * Gets the default commands that should always be available. |
||
936 | * |
||
937 | * @return Command[] An array of default Command instances |
||
938 | */ |
||
939 | protected function getDefaultCommands() |
||
940 | { |
||
941 | return array(new HelpCommand(), new ListCommand()); |
||
942 | } |
||
943 | |||
944 | /** |
||
945 | * Gets the default helper set with the helpers that should always be available. |
||
946 | * |
||
947 | * @return HelperSet A HelperSet instance |
||
948 | */ |
||
949 | protected function getDefaultHelperSet() |
||
950 | { |
||
951 | return new HelperSet(array( |
||
952 | new FormatterHelper(), |
||
953 | new DialogHelper(), |
||
954 | new ProgressHelper(), |
||
955 | new TableHelper(), |
||
956 | new DebugFormatterHelper(), |
||
957 | new ProcessHelper(), |
||
958 | new QuestionHelper(), |
||
959 | )); |
||
960 | } |
||
961 | |||
962 | /** |
||
963 | * Runs and parses stty -a if it's available, suppressing any error output. |
||
964 | * |
||
965 | * @return string |
||
966 | */ |
||
967 | private function getSttyColumns() |
||
968 | { |
||
969 | if (!function_exists('proc_open')) { |
||
970 | return; |
||
971 | } |
||
972 | |||
973 | $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')); |
||
974 | $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, array('suppress_errors' => true)); |
||
975 | if (is_resource($process)) { |
||
976 | $info = stream_get_contents($pipes[1]); |
||
977 | fclose($pipes[1]); |
||
978 | fclose($pipes[2]); |
||
979 | proc_close($process); |
||
980 | |||
981 | return $info; |
||
982 | } |
||
983 | } |
||
984 | |||
985 | /** |
||
986 | * Runs and parses mode CON if it's available, suppressing any error output. |
||
987 | * |
||
988 | * @return string <width>x<height> or null if it could not be parsed |
||
989 | */ |
||
990 | private function getConsoleMode() |
||
991 | { |
||
992 | if (!function_exists('proc_open')) { |
||
993 | return; |
||
994 | } |
||
995 | |||
996 | $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')); |
||
997 | $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, array('suppress_errors' => true)); |
||
998 | if (is_resource($process)) { |
||
999 | $info = stream_get_contents($pipes[1]); |
||
1000 | fclose($pipes[1]); |
||
1001 | fclose($pipes[2]); |
||
1002 | proc_close($process); |
||
1003 | |||
1004 | if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { |
||
1005 | return $matches[2].'x'.$matches[1]; |
||
1006 | } |
||
1007 | } |
||
1008 | } |
||
1009 | |||
1010 | /** |
||
1011 | * Returns abbreviated suggestions in string format. |
||
1012 | * |
||
1013 | * @param array $abbrevs Abbreviated suggestions to convert |
||
1014 | * |
||
1015 | * @return string A formatted string of abbreviated suggestions |
||
1016 | */ |
||
1017 | private function getAbbreviationSuggestions($abbrevs) |
||
1021 | |||
1022 | /** |
||
1023 | * Returns the namespace part of the command name. |
||
1024 | * |
||
1025 | * This method is not part of public API and should not be used directly. |
||
1026 | * |
||
1027 | * @param string $name The full name of the command |
||
1028 | * @param string $limit The maximum number of parts of the namespace |
||
1029 | * |
||
1030 | * @return string The namespace of the command |
||
1031 | */ |
||
1032 | public function extractNamespace($name, $limit = null) |
||
1039 | |||
1040 | /** |
||
1041 | * Finds alternative of $name among $collection, |
||
1042 | * if nothing is found in $collection, try in $abbrevs. |
||
1043 | * |
||
1044 | * @param string $name The string |
||
1045 | * @param array|\Traversable $collection The collection |
||
1046 | * |
||
1047 | * @return array A sorted array of similar string |
||
1048 | */ |
||
1049 | private function findAlternatives($name, $collection) |
||
1050 | { |
||
1051 | $threshold = 1e3; |
||
1052 | $alternatives = array(); |
||
1053 | |||
1054 | $collectionParts = array(); |
||
1055 | foreach ($collection as $item) { |
||
1056 | $collectionParts[$item] = explode(':', $item); |
||
1057 | } |
||
1058 | |||
1059 | foreach (explode(':', $name) as $i => $subname) { |
||
1060 | foreach ($collectionParts as $collectionName => $parts) { |
||
1061 | $exists = isset($alternatives[$collectionName]); |
||
1062 | if (!isset($parts[$i]) && $exists) { |
||
1063 | $alternatives[$collectionName] += $threshold; |
||
1064 | continue; |
||
1065 | } elseif (!isset($parts[$i])) { |
||
1066 | continue; |
||
1067 | } |
||
1068 | |||
1069 | $lev = levenshtein($subname, $parts[$i]); |
||
1070 | if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) { |
||
1071 | $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev; |
||
1072 | } elseif ($exists) { |
||
1073 | $alternatives[$collectionName] += $threshold; |
||
1074 | } |
||
1075 | } |
||
1076 | } |
||
1077 | |||
1078 | foreach ($collection as $item) { |
||
1079 | $lev = levenshtein($name, $item); |
||
1080 | if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { |
||
1081 | $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev; |
||
1082 | } |
||
1083 | } |
||
1084 | |||
1085 | $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2*$threshold; }); |
||
1086 | asort($alternatives); |
||
1087 | |||
1088 | return array_keys($alternatives); |
||
1089 | } |
||
1090 | |||
1091 | /** |
||
1092 | * Sets the default Command name. |
||
1093 | * |
||
1094 | * @param string $commandName The Command name |
||
1095 | */ |
||
1096 | public function setDefaultCommand($commandName) |
||
1097 | { |
||
1098 | $this->defaultCommand = $commandName; |
||
1099 | } |
||
1100 | |||
1101 | View Code Duplication | private function stringWidth($string) |
|
1102 | { |
||
1103 | if (!function_exists('mb_strwidth')) { |
||
1104 | return strlen($string); |
||
1105 | } |
||
1106 | |||
1107 | if (false === $encoding = mb_detect_encoding($string)) { |
||
1108 | return strlen($string); |
||
1109 | } |
||
1110 | |||
1111 | return mb_strwidth($string, $encoding); |
||
1112 | } |
||
1113 | |||
1114 | private function splitStringByWidth($string, $width) |
||
1115 | { |
||
1116 | // str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly. |
||
1117 | // additionally, array_slice() is not enough as some character has doubled width. |
||
1118 | // we need a function to split string not by character count but by string width |
||
1119 | |||
1120 | if (!function_exists('mb_strwidth')) { |
||
1121 | return str_split($string, $width); |
||
1122 | } |
||
1123 | |||
1124 | if (false === $encoding = mb_detect_encoding($string)) { |
||
1125 | return str_split($string, $width); |
||
1126 | } |
||
1127 | |||
1128 | $utf8String = mb_convert_encoding($string, 'utf8', $encoding); |
||
1129 | $lines = array(); |
||
1130 | $line = ''; |
||
1131 | foreach (preg_split('//u', $utf8String) as $char) { |
||
1132 | // test if $char could be appended to current line |
||
1133 | if (mb_strwidth($line.$char, 'utf8') <= $width) { |
||
1134 | $line .= $char; |
||
1135 | continue; |
||
1136 | } |
||
1137 | // if not, push current line to array and make new line |
||
1138 | $lines[] = str_pad($line, $width); |
||
1139 | $line = $char; |
||
1140 | } |
||
1141 | if (strlen($line)) { |
||
1142 | $lines[] = count($lines) ? str_pad($line, $width) : $line; |
||
1143 | } |
||
1144 | |||
1145 | mb_convert_variables($encoding, 'utf8', $lines); |
||
1146 | |||
1147 | return $lines; |
||
1148 | } |
||
1149 | |||
1150 | /** |
||
1151 | * Returns all namespaces of the command name. |
||
1152 | * |
||
1153 | * @param string $name The full name of the command |
||
1154 | * |
||
1155 | * @return array The namespaces of the command |
||
1156 | */ |
||
1157 | private function extractAllNamespaces($name) |
||
1158 | { |
||
1159 | // -1 as third argument is needed to skip the command short name when exploding |
||
1160 | $parts = explode(':', $name, -1); |
||
1161 | $namespaces = array(); |
||
1162 | |||
1163 | foreach ($parts as $part) { |
||
1164 | if (count($namespaces)) { |
||
1165 | $namespaces[] = end($namespaces).':'.$part; |
||
1166 | } else { |
||
1167 | $namespaces[] = $part; |
||
1168 | } |
||
1169 | } |
||
1170 | |||
1171 | return $namespaces; |
||
1172 | } |
||
1173 | } |
||
1174 |
An exit expression should only be used in rare cases. For example, if you write a short command line script.
In most cases however, using an
exit
expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.