Application.php 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. <?php
  2. namespace Illuminate\Console;
  3. use Closure;
  4. use Illuminate\Support\ProcessUtils;
  5. use Illuminate\Contracts\Events\Dispatcher;
  6. use Illuminate\Contracts\Container\Container;
  7. use Symfony\Component\Console\Input\ArgvInput;
  8. use Symfony\Component\Console\Input\ArrayInput;
  9. use Symfony\Component\Console\Input\InputOption;
  10. use Symfony\Component\Process\PhpExecutableFinder;
  11. use Symfony\Component\Console\Input\InputInterface;
  12. use Symfony\Component\Console\Output\ConsoleOutput;
  13. use Symfony\Component\Console\Output\BufferedOutput;
  14. use Symfony\Component\Console\Output\OutputInterface;
  15. use Symfony\Component\Console\Application as SymfonyApplication;
  16. use Symfony\Component\Console\Command\Command as SymfonyCommand;
  17. use Symfony\Component\Console\Exception\CommandNotFoundException;
  18. use Illuminate\Contracts\Console\Application as ApplicationContract;
  19. class Application extends SymfonyApplication implements ApplicationContract
  20. {
  21. /**
  22. * The Laravel application instance.
  23. *
  24. * @var \Illuminate\Contracts\Container\Container
  25. */
  26. protected $laravel;
  27. /**
  28. * The output from the previous command.
  29. *
  30. * @var \Symfony\Component\Console\Output\BufferedOutput
  31. */
  32. protected $lastOutput;
  33. /**
  34. * The console application bootstrappers.
  35. *
  36. * @var array
  37. */
  38. protected static $bootstrappers = [];
  39. /**
  40. * The Event Dispatcher.
  41. *
  42. * @var \Illuminate\Contracts\Events\Dispatcher
  43. */
  44. protected $events;
  45. /**
  46. * Create a new Artisan console application.
  47. *
  48. * @param \Illuminate\Contracts\Container\Container $laravel
  49. * @param \Illuminate\Contracts\Events\Dispatcher $events
  50. * @param string $version
  51. * @return void
  52. */
  53. public function __construct(Container $laravel, Dispatcher $events, $version)
  54. {
  55. parent::__construct('Laravel Framework', $version);
  56. $this->laravel = $laravel;
  57. $this->events = $events;
  58. $this->setAutoExit(false);
  59. $this->setCatchExceptions(false);
  60. $this->events->dispatch(new Events\ArtisanStarting($this));
  61. $this->bootstrap();
  62. }
  63. /**
  64. * {@inheritdoc}
  65. */
  66. public function run(InputInterface $input = null, OutputInterface $output = null)
  67. {
  68. $commandName = $this->getCommandName(
  69. $input = $input ?: new ArgvInput
  70. );
  71. $this->events->fire(
  72. new Events\CommandStarting(
  73. $commandName, $input, $output = $output ?: new ConsoleOutput
  74. )
  75. );
  76. $exitCode = parent::run($input, $output);
  77. $this->events->fire(
  78. new Events\CommandFinished($commandName, $input, $output, $exitCode)
  79. );
  80. return $exitCode;
  81. }
  82. /**
  83. * Determine the proper PHP executable.
  84. *
  85. * @return string
  86. */
  87. public static function phpBinary()
  88. {
  89. return ProcessUtils::escapeArgument((new PhpExecutableFinder)->find(false));
  90. }
  91. /**
  92. * Determine the proper Artisan executable.
  93. *
  94. * @return string
  95. */
  96. public static function artisanBinary()
  97. {
  98. return defined('ARTISAN_BINARY') ? ProcessUtils::escapeArgument(ARTISAN_BINARY) : 'artisan';
  99. }
  100. /**
  101. * Format the given command as a fully-qualified executable command.
  102. *
  103. * @param string $string
  104. * @return string
  105. */
  106. public static function formatCommandString($string)
  107. {
  108. return sprintf('%s %s %s', static::phpBinary(), static::artisanBinary(), $string);
  109. }
  110. /**
  111. * Register a console "starting" bootstrapper.
  112. *
  113. * @param \Closure $callback
  114. * @return void
  115. */
  116. public static function starting(Closure $callback)
  117. {
  118. static::$bootstrappers[] = $callback;
  119. }
  120. /**
  121. * Bootstrap the console application.
  122. *
  123. * @return void
  124. */
  125. protected function bootstrap()
  126. {
  127. foreach (static::$bootstrappers as $bootstrapper) {
  128. $bootstrapper($this);
  129. }
  130. }
  131. /**
  132. * Clear the console application bootstrappers.
  133. *
  134. * @return void
  135. */
  136. public static function forgetBootstrappers()
  137. {
  138. static::$bootstrappers = [];
  139. }
  140. /**
  141. * Run an Artisan console command by name.
  142. *
  143. * @param string $command
  144. * @param array $parameters
  145. * @param \Symfony\Component\Console\Output\OutputInterface|null $outputBuffer
  146. * @return int
  147. */
  148. public function call($command, array $parameters = [], $outputBuffer = null)
  149. {
  150. if (is_subclass_of($command, SymfonyCommand::class)) {
  151. $command = $this->laravel->make($command)->getName();
  152. }
  153. if (! $this->has($command)) {
  154. throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $command));
  155. }
  156. array_unshift($parameters, $command);
  157. $this->lastOutput = $outputBuffer ?: new BufferedOutput;
  158. $this->setCatchExceptions(false);
  159. $result = $this->run(new ArrayInput($parameters), $this->lastOutput);
  160. $this->setCatchExceptions(true);
  161. return $result;
  162. }
  163. /**
  164. * Get the output for the last run command.
  165. *
  166. * @return string
  167. */
  168. public function output()
  169. {
  170. return $this->lastOutput && method_exists($this->lastOutput, 'fetch')
  171. ? $this->lastOutput->fetch()
  172. : '';
  173. }
  174. /**
  175. * Add a command to the console.
  176. *
  177. * @param \Symfony\Component\Console\Command\Command $command
  178. * @return \Symfony\Component\Console\Command\Command
  179. */
  180. public function add(SymfonyCommand $command)
  181. {
  182. if ($command instanceof Command) {
  183. $command->setLaravel($this->laravel);
  184. }
  185. return $this->addToParent($command);
  186. }
  187. /**
  188. * Add the command to the parent instance.
  189. *
  190. * @param \Symfony\Component\Console\Command\Command $command
  191. * @return \Symfony\Component\Console\Command\Command
  192. */
  193. protected function addToParent(SymfonyCommand $command)
  194. {
  195. return parent::add($command);
  196. }
  197. /**
  198. * Add a command, resolving through the application.
  199. *
  200. * @param string $command
  201. * @return \Symfony\Component\Console\Command\Command
  202. */
  203. public function resolve($command)
  204. {
  205. return $this->add($this->laravel->make($command));
  206. }
  207. /**
  208. * Resolve an array of commands through the application.
  209. *
  210. * @param array|mixed $commands
  211. * @return $this
  212. */
  213. public function resolveCommands($commands)
  214. {
  215. $commands = is_array($commands) ? $commands : func_get_args();
  216. foreach ($commands as $command) {
  217. $this->resolve($command);
  218. }
  219. return $this;
  220. }
  221. /**
  222. * Get the default input definitions for the applications.
  223. *
  224. * This is used to add the --env option to every available command.
  225. *
  226. * @return \Symfony\Component\Console\Input\InputDefinition
  227. */
  228. protected function getDefaultInputDefinition()
  229. {
  230. return tap(parent::getDefaultInputDefinition(), function ($definition) {
  231. $definition->addOption($this->getEnvironmentOption());
  232. });
  233. }
  234. /**
  235. * Get the global environment option for the definition.
  236. *
  237. * @return \Symfony\Component\Console\Input\InputOption
  238. */
  239. protected function getEnvironmentOption()
  240. {
  241. $message = 'The environment the command should run under';
  242. return new InputOption('--env', null, InputOption::VALUE_OPTIONAL, $message);
  243. }
  244. /**
  245. * Get the Laravel application instance.
  246. *
  247. * @return \Illuminate\Contracts\Foundation\Application
  248. */
  249. public function getLaravel()
  250. {
  251. return $this->laravel;
  252. }
  253. }