router = $router; parent::__construct(); } /** * Configure the command. * * @return void * * @since 2.0.0 */ protected function configure(): void { $this->setDescription("Displays information about the application's routes"); $this->addOption('show-controllers', null, InputOption::VALUE_NONE, 'Show the controller for a route in the overview'); $this->setHelp(<<<'EOF' The %command.name% command lists all of the application's routes: php %command.full_name% To show the controllers that handle each route, use the --show-controllers option: php %command.full_name% --show-controllers EOF ); } /** * Internal function to execute the command. * * @param InputInterface $input The input to inject into the command. * @param OutputInterface $output The output to inject into the command. * * @return integer The command exit code * * @since 2.0.0 */ protected function doExecute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $showControllers = $input->getOption('show-controllers'); $io->title(sprintf('%s Router Information', $this->getApplication()->getName())); if (empty($this->router->getRoutes())) { $io->warning('The router has no routes.'); return 0; } $tableHeaders = [ 'Methods', 'Pattern', 'Rules', ]; $tableRows = []; if ($showControllers) { $tableHeaders[] = 'Controller'; } foreach ($this->router->getRoutes() as $route) { $row = []; $row[] = $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY'; $row[] = $route->getPattern(); $rules = $route->getRules(); if (empty($rules)) { $row[] = 'N/A'; } else { ksort($rules); $rulesAsString = ''; foreach ($rules as $key => $value) { $rulesAsString .= sprintf("%s: %s\n", $key, $this->formatValue($value)); } $row[] = new TableCell(rtrim($rulesAsString), ['rowspan' => count($rules)]); } if ($showControllers) { $row[] = $this->formatCallable($route->getController()); } $tableRows[] = $row; } $io->table($tableHeaders, $tableRows); return 0; } /** * Formats a callable resource to be displayed in the console output * * @param callable $callable A callable resource to format * * @return string * * @since 2.0.0 * @throws \ReflectionException * @note This method is based on \Symfony\Bundle\FrameworkBundle\Console\Descriptor\TextDescriptor::formatCallable() */ private function formatCallable($callable): string { if (\is_array($callable)) { if (\is_object($callable[0])) { return sprintf('%s::%s()', \get_class($callable[0]), $callable[1]); } return sprintf('%s::%s()', $callable[0], $callable[1]); } if (\is_string($callable)) { return sprintf('%s()', $callable); } if ($callable instanceof \Closure) { $r = new \ReflectionFunction($callable); if (strpos($r->name, '{closure}') !== false) { return 'Closure()'; } if ($class = $r->getClosureScopeClass()) { return sprintf('%s::%s()', $class->name, $r->name); } return $r->name . '()'; } if (method_exists($callable, '__invoke')) { return sprintf('%s::__invoke()', \get_class($callable)); } throw new \InvalidArgumentException('Callable is not describable.'); } /** * Formats a value as string. * * @param mixed $value A value to format * * @return string * * @since 2.0.0 * @note This method is based on \Symfony\Bundle\FrameworkBundle\Console\Descriptor\Descriptor::formatValue() */ private function formatValue($value): string { if (\is_object($value)) { return sprintf('object(%s)', \get_class($value)); } if (\is_string($value)) { return $value; } return preg_replace("/\n\s*/s", '', var_export($value, true)); } }