vendor and env first commit
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation;
|
||||
|
||||
class AliasLoader
|
||||
{
|
||||
/**
|
||||
* The array of class aliases.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $aliases;
|
||||
|
||||
/**
|
||||
* Indicates if a loader has been registered.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $registered = false;
|
||||
|
||||
/**
|
||||
* The namespace for all real-time facades.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $facadeNamespace = 'Facades\\';
|
||||
|
||||
/**
|
||||
* The singleton instance of the loader.
|
||||
*
|
||||
* @var \Illuminate\Foundation\AliasLoader
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
/**
|
||||
* Create a new AliasLoader instance.
|
||||
*
|
||||
* @param array $aliases
|
||||
* @return void
|
||||
*/
|
||||
private function __construct($aliases)
|
||||
{
|
||||
$this->aliases = $aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create the singleton alias loader instance.
|
||||
*
|
||||
* @param array $aliases
|
||||
* @return \Illuminate\Foundation\AliasLoader
|
||||
*/
|
||||
public static function getInstance(array $aliases = [])
|
||||
{
|
||||
if (is_null(static::$instance)) {
|
||||
return static::$instance = new static($aliases);
|
||||
}
|
||||
|
||||
$aliases = array_merge(static::$instance->getAliases(), $aliases);
|
||||
|
||||
static::$instance->setAliases($aliases);
|
||||
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a class alias if it is registered.
|
||||
*
|
||||
* @param string $alias
|
||||
* @return bool|null
|
||||
*/
|
||||
public function load($alias)
|
||||
{
|
||||
if (static::$facadeNamespace && str_starts_with($alias, static::$facadeNamespace)) {
|
||||
$this->loadFacade($alias);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isset($this->aliases[$alias])) {
|
||||
return class_alias($this->aliases[$alias], $alias);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a real-time facade for the given alias.
|
||||
*
|
||||
* @param string $alias
|
||||
* @return void
|
||||
*/
|
||||
protected function loadFacade($alias)
|
||||
{
|
||||
require $this->ensureFacadeExists($alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the given alias has an existing real-time facade class.
|
||||
*
|
||||
* @param string $alias
|
||||
* @return string
|
||||
*/
|
||||
protected function ensureFacadeExists($alias)
|
||||
{
|
||||
if (is_file($path = storage_path('framework/cache/facade-'.sha1($alias).'.php'))) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
file_put_contents($path, $this->formatFacadeStub(
|
||||
$alias, file_get_contents(__DIR__.'/stubs/facade.stub')
|
||||
));
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the facade stub with the proper namespace and class.
|
||||
*
|
||||
* @param string $alias
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function formatFacadeStub($alias, $stub)
|
||||
{
|
||||
$replacements = [
|
||||
str_replace('/', '\\', dirname(str_replace('\\', '/', $alias))),
|
||||
class_basename($alias),
|
||||
substr($alias, strlen(static::$facadeNamespace)),
|
||||
];
|
||||
|
||||
return str_replace(
|
||||
['DummyNamespace', 'DummyClass', 'DummyTarget'], $replacements, $stub
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an alias to the loader.
|
||||
*
|
||||
* @param string $alias
|
||||
* @param string $class
|
||||
* @return void
|
||||
*/
|
||||
public function alias($alias, $class)
|
||||
{
|
||||
$this->aliases[$alias] = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the loader on the auto-loader stack.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
if (! $this->registered) {
|
||||
$this->prependToLoaderStack();
|
||||
|
||||
$this->registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend the load method to the auto-loader stack.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prependToLoaderStack()
|
||||
{
|
||||
spl_autoload_register([$this, 'load'], true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the registered aliases.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAliases()
|
||||
{
|
||||
return $this->aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the registered aliases.
|
||||
*
|
||||
* @param array $aliases
|
||||
* @return void
|
||||
*/
|
||||
public function setAliases(array $aliases)
|
||||
{
|
||||
$this->aliases = $aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the loader has been registered.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isRegistered()
|
||||
{
|
||||
return $this->registered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the "registered" state of the loader.
|
||||
*
|
||||
* @param bool $value
|
||||
* @return void
|
||||
*/
|
||||
public function setRegistered($value)
|
||||
{
|
||||
$this->registered = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the real-time facade namespace.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @return void
|
||||
*/
|
||||
public static function setFacadeNamespace($namespace)
|
||||
{
|
||||
static::$facadeNamespace = rtrim($namespace, '\\').'\\';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the singleton alias loader.
|
||||
*
|
||||
* @param \Illuminate\Foundation\AliasLoader $loader
|
||||
* @return void
|
||||
*/
|
||||
public static function setInstance($loader)
|
||||
{
|
||||
static::$instance = $loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone method.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function __clone()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Auth\Access;
|
||||
|
||||
use Illuminate\Contracts\Auth\Access\Gate;
|
||||
|
||||
trait Authorizable
|
||||
{
|
||||
/**
|
||||
* Determine if the entity has the given abilities.
|
||||
*
|
||||
* @param iterable|string $abilities
|
||||
* @param array|mixed $arguments
|
||||
* @return bool
|
||||
*/
|
||||
public function can($abilities, $arguments = [])
|
||||
{
|
||||
return app(Gate::class)->forUser($this)->check($abilities, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the entity has any of the given abilities.
|
||||
*
|
||||
* @param iterable|string $abilities
|
||||
* @param array|mixed $arguments
|
||||
* @return bool
|
||||
*/
|
||||
public function canAny($abilities, $arguments = [])
|
||||
{
|
||||
return app(Gate::class)->forUser($this)->any($abilities, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the entity does not have the given abilities.
|
||||
*
|
||||
* @param iterable|string $abilities
|
||||
* @param array|mixed $arguments
|
||||
* @return bool
|
||||
*/
|
||||
public function cant($abilities, $arguments = [])
|
||||
{
|
||||
return ! $this->can($abilities, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the entity does not have the given abilities.
|
||||
*
|
||||
* @param iterable|string $abilities
|
||||
* @param array|mixed $arguments
|
||||
* @return bool
|
||||
*/
|
||||
public function cannot($abilities, $arguments = [])
|
||||
{
|
||||
return $this->cant($abilities, $arguments);
|
||||
}
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Auth\Access;
|
||||
|
||||
use Illuminate\Contracts\Auth\Access\Gate;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
trait AuthorizesRequests
|
||||
{
|
||||
/**
|
||||
* Authorize a given action for the current user.
|
||||
*
|
||||
* @param mixed $ability
|
||||
* @param mixed|array $arguments
|
||||
* @return \Illuminate\Auth\Access\Response
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function authorize($ability, $arguments = [])
|
||||
{
|
||||
[$ability, $arguments] = $this->parseAbilityAndArguments($ability, $arguments);
|
||||
|
||||
return app(Gate::class)->authorize($ability, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize a given action for a user.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Auth\Authenticatable|mixed $user
|
||||
* @param mixed $ability
|
||||
* @param mixed|array $arguments
|
||||
* @return \Illuminate\Auth\Access\Response
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function authorizeForUser($user, $ability, $arguments = [])
|
||||
{
|
||||
[$ability, $arguments] = $this->parseAbilityAndArguments($ability, $arguments);
|
||||
|
||||
return app(Gate::class)->forUser($user)->authorize($ability, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guesses the ability's name if it wasn't provided.
|
||||
*
|
||||
* @param mixed $ability
|
||||
* @param mixed|array $arguments
|
||||
* @return array
|
||||
*/
|
||||
protected function parseAbilityAndArguments($ability, $arguments)
|
||||
{
|
||||
if (is_string($ability) && ! str_contains($ability, '\\')) {
|
||||
return [$ability, $arguments];
|
||||
}
|
||||
|
||||
$method = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3)[2]['function'];
|
||||
|
||||
return [$this->normalizeGuessedAbilityName($method), $ability];
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the ability name that has been guessed from the method name.
|
||||
*
|
||||
* @param string $ability
|
||||
* @return string
|
||||
*/
|
||||
protected function normalizeGuessedAbilityName($ability)
|
||||
{
|
||||
$map = $this->resourceAbilityMap();
|
||||
|
||||
return $map[$ability] ?? $ability;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize a resource action based on the incoming request.
|
||||
*
|
||||
* @param string|array $model
|
||||
* @param string|array|null $parameter
|
||||
* @param array $options
|
||||
* @param \Illuminate\Http\Request|null $request
|
||||
* @return void
|
||||
*/
|
||||
public function authorizeResource($model, $parameter = null, array $options = [], $request = null)
|
||||
{
|
||||
$model = is_array($model) ? implode(',', $model) : $model;
|
||||
|
||||
$parameter = is_array($parameter) ? implode(',', $parameter) : $parameter;
|
||||
|
||||
$parameter = $parameter ?: Str::snake(class_basename($model));
|
||||
|
||||
$middleware = [];
|
||||
|
||||
foreach ($this->resourceAbilityMap() as $method => $ability) {
|
||||
$modelName = in_array($method, $this->resourceMethodsWithoutModels()) ? $model : $parameter;
|
||||
|
||||
$middleware["can:{$ability},{$modelName}"][] = $method;
|
||||
}
|
||||
|
||||
foreach ($middleware as $middlewareName => $methods) {
|
||||
$this->middleware($middlewareName, $options)->only($methods);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the map of resource methods to ability names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function resourceAbilityMap()
|
||||
{
|
||||
return [
|
||||
'index' => 'viewAny',
|
||||
'show' => 'view',
|
||||
'create' => 'create',
|
||||
'store' => 'create',
|
||||
'edit' => 'update',
|
||||
'update' => 'update',
|
||||
'destroy' => 'delete',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of resource methods which do not have model parameters.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function resourceMethodsWithoutModels()
|
||||
{
|
||||
return ['index', 'create', 'store'];
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Auth;
|
||||
|
||||
use Illuminate\Auth\Events\Verified;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
class EmailVerificationRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
if (! hash_equals((string) $this->user()->getKey(), (string) $this->route('id'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! hash_equals(sha1($this->user()->getEmailForVerification()), (string) $this->route('hash'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fulfill the email verification request.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function fulfill()
|
||||
{
|
||||
if (! $this->user()->hasVerifiedEmail()) {
|
||||
$this->user()->markEmailAsVerified();
|
||||
|
||||
event(new Verified($this->user()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the validator instance.
|
||||
*
|
||||
* @param \Illuminate\Validation\Validator $validator
|
||||
* @return \Illuminate\Validation\Validator
|
||||
*/
|
||||
public function withValidator(Validator $validator)
|
||||
{
|
||||
return $validator;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Auth;
|
||||
|
||||
use Illuminate\Auth\Authenticatable;
|
||||
use Illuminate\Auth\MustVerifyEmail;
|
||||
use Illuminate\Auth\Passwords\CanResetPassword;
|
||||
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
|
||||
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
||||
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Foundation\Auth\Access\Authorizable;
|
||||
|
||||
class User extends Model implements
|
||||
AuthenticatableContract,
|
||||
AuthorizableContract,
|
||||
CanResetPasswordContract
|
||||
{
|
||||
use Authenticatable, Authorizable, CanResetPassword, MustVerifyEmail;
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Bootstrap;
|
||||
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
|
||||
class BootProviders
|
||||
{
|
||||
/**
|
||||
* Bootstrap the given application.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrap(Application $app)
|
||||
{
|
||||
$app->boot();
|
||||
}
|
||||
}
|
||||
+362
@@ -0,0 +1,362 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Bootstrap;
|
||||
|
||||
use ErrorException;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Log\LogManager;
|
||||
use Illuminate\Support\Env;
|
||||
use Monolog\Handler\NullHandler;
|
||||
use PHPUnit\Runner\ErrorHandler;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\ErrorHandler\Error\FatalError;
|
||||
use Throwable;
|
||||
|
||||
class HandleExceptions
|
||||
{
|
||||
/**
|
||||
* Reserved memory so that errors can be displayed properly on memory exhaustion.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public static $reservedMemory;
|
||||
|
||||
/**
|
||||
* The application instance.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Foundation\Application
|
||||
*/
|
||||
protected static $app;
|
||||
|
||||
/**
|
||||
* Bootstrap the given application.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrap(Application $app)
|
||||
{
|
||||
static::$reservedMemory = str_repeat('x', 32768);
|
||||
|
||||
static::$app = $app;
|
||||
|
||||
error_reporting(-1);
|
||||
|
||||
set_error_handler($this->forwardsTo('handleError'));
|
||||
|
||||
set_exception_handler($this->forwardsTo('handleException'));
|
||||
|
||||
register_shutdown_function($this->forwardsTo('handleShutdown'));
|
||||
|
||||
if (! $app->environment('testing')) {
|
||||
ini_set('display_errors', 'Off');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Report PHP deprecations, or convert PHP errors to ErrorException instances.
|
||||
*
|
||||
* @param int $level
|
||||
* @param string $message
|
||||
* @param string $file
|
||||
* @param int $line
|
||||
* @return void
|
||||
*
|
||||
* @throws \ErrorException
|
||||
*/
|
||||
public function handleError($level, $message, $file = '', $line = 0)
|
||||
{
|
||||
if ($this->isDeprecation($level)) {
|
||||
$this->handleDeprecationError($message, $file, $line, $level);
|
||||
} elseif (error_reporting() & $level) {
|
||||
throw new ErrorException($message, 0, $level, $file, $line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports a deprecation to the "deprecations" logger.
|
||||
*
|
||||
* @param string $message
|
||||
* @param string $file
|
||||
* @param int $line
|
||||
* @param int $level
|
||||
* @return void
|
||||
*/
|
||||
public function handleDeprecationError($message, $file, $line, $level = E_DEPRECATED)
|
||||
{
|
||||
if ($this->shouldIgnoreDeprecationErrors()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$logger = static::$app->make(LogManager::class);
|
||||
} catch (Exception) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->ensureDeprecationLoggerIsConfigured();
|
||||
|
||||
$options = static::$app['config']->get('logging.deprecations') ?? [];
|
||||
|
||||
with($logger->channel('deprecations'), function ($log) use ($message, $file, $line, $level, $options) {
|
||||
if ($options['trace'] ?? false) {
|
||||
$log->warning((string) new ErrorException($message, 0, $level, $file, $line));
|
||||
} else {
|
||||
$log->warning(sprintf('%s in %s on line %s',
|
||||
$message, $file, $line
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if deprecation errors should be ignored.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function shouldIgnoreDeprecationErrors()
|
||||
{
|
||||
return ! class_exists(LogManager::class)
|
||||
|| ! static::$app->hasBeenBootstrapped()
|
||||
|| (static::$app->runningUnitTests() && ! Env::get('LOG_DEPRECATIONS_WHILE_TESTING'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the "deprecations" logger is configured.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function ensureDeprecationLoggerIsConfigured()
|
||||
{
|
||||
with(static::$app['config'], function ($config) {
|
||||
if ($config->get('logging.channels.deprecations')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->ensureNullLogDriverIsConfigured();
|
||||
|
||||
if (is_array($options = $config->get('logging.deprecations'))) {
|
||||
$driver = $options['channel'] ?? 'null';
|
||||
} else {
|
||||
$driver = $options ?? 'null';
|
||||
}
|
||||
|
||||
$config->set('logging.channels.deprecations', $config->get("logging.channels.{$driver}"));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the "null" log driver is configured.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function ensureNullLogDriverIsConfigured()
|
||||
{
|
||||
with(static::$app['config'], function ($config) {
|
||||
if ($config->get('logging.channels.null')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$config->set('logging.channels.null', [
|
||||
'driver' => 'monolog',
|
||||
'handler' => NullHandler::class,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an uncaught exception from the application.
|
||||
*
|
||||
* Note: Most exceptions can be handled via the try / catch block in
|
||||
* the HTTP and Console kernels. But, fatal error exceptions must
|
||||
* be handled differently since they are not normal exceptions.
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @return void
|
||||
*/
|
||||
public function handleException(Throwable $e)
|
||||
{
|
||||
static::$reservedMemory = null;
|
||||
|
||||
try {
|
||||
$this->getExceptionHandler()->report($e);
|
||||
} catch (Exception) {
|
||||
$exceptionHandlerFailed = true;
|
||||
}
|
||||
|
||||
if (static::$app->runningInConsole()) {
|
||||
$this->renderForConsole($e);
|
||||
|
||||
if ($exceptionHandlerFailed ?? false) {
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
$this->renderHttpResponse($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception to the console.
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @return void
|
||||
*/
|
||||
protected function renderForConsole(Throwable $e)
|
||||
{
|
||||
$this->getExceptionHandler()->renderForConsole(new ConsoleOutput, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception as an HTTP response and send it.
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @return void
|
||||
*/
|
||||
protected function renderHttpResponse(Throwable $e)
|
||||
{
|
||||
$this->getExceptionHandler()->render(static::$app['request'], $e)->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the PHP shutdown event.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handleShutdown()
|
||||
{
|
||||
static::$reservedMemory = null;
|
||||
|
||||
if (! is_null($error = error_get_last()) && $this->isFatal($error['type'])) {
|
||||
$this->handleException($this->fatalErrorFromPhpError($error, 0));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new fatal error instance from an error array.
|
||||
*
|
||||
* @param array $error
|
||||
* @param int|null $traceOffset
|
||||
* @return \Symfony\Component\ErrorHandler\Error\FatalError
|
||||
*/
|
||||
protected function fatalErrorFromPhpError(array $error, $traceOffset = null)
|
||||
{
|
||||
return new FatalError($error['message'], 0, $error, $traceOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward a method call to the given method if an application instance exists.
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
protected function forwardsTo($method)
|
||||
{
|
||||
return fn (...$arguments) => static::$app
|
||||
? $this->{$method}(...$arguments)
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the error level is a deprecation.
|
||||
*
|
||||
* @param int $level
|
||||
* @return bool
|
||||
*/
|
||||
protected function isDeprecation($level)
|
||||
{
|
||||
return in_array($level, [E_DEPRECATED, E_USER_DEPRECATED]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the error type is fatal.
|
||||
*
|
||||
* @param int $type
|
||||
* @return bool
|
||||
*/
|
||||
protected function isFatal($type)
|
||||
{
|
||||
return in_array($type, [E_COMPILE_ERROR, E_CORE_ERROR, E_ERROR, E_PARSE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of the exception handler.
|
||||
*
|
||||
* @return \Illuminate\Contracts\Debug\ExceptionHandler
|
||||
*/
|
||||
protected function getExceptionHandler()
|
||||
{
|
||||
return static::$app->make(ExceptionHandler::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the local application instance from memory.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @deprecated This method will be removed in a future Laravel version.
|
||||
*/
|
||||
public static function forgetApp()
|
||||
{
|
||||
static::$app = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the bootstrapper's global state.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function flushState()
|
||||
{
|
||||
if (is_null(static::$app)) {
|
||||
return;
|
||||
}
|
||||
|
||||
static::flushHandlersState();
|
||||
|
||||
static::$app = null;
|
||||
|
||||
static::$reservedMemory = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the bootstrapper's global handlers state.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function flushHandlersState()
|
||||
{
|
||||
while (true) {
|
||||
$previousHandler = set_exception_handler(static fn () => null);
|
||||
|
||||
restore_exception_handler();
|
||||
|
||||
if ($previousHandler === null) {
|
||||
break;
|
||||
}
|
||||
|
||||
restore_exception_handler();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
$previousHandler = set_error_handler(static fn () => null);
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
if ($previousHandler === null) {
|
||||
break;
|
||||
}
|
||||
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
if (class_exists(ErrorHandler::class)) {
|
||||
$instance = ErrorHandler::instance();
|
||||
|
||||
if ((fn () => $this->enabled ?? false)->call($instance)) {
|
||||
$instance->disable();
|
||||
$instance->enable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Bootstrap;
|
||||
|
||||
use Illuminate\Config\Repository;
|
||||
use Illuminate\Contracts\Config\Repository as RepositoryContract;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use SplFileInfo;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
|
||||
class LoadConfiguration
|
||||
{
|
||||
/**
|
||||
* Bootstrap the given application.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrap(Application $app)
|
||||
{
|
||||
$items = [];
|
||||
|
||||
// First we will see if we have a cache configuration file. If we do, we'll load
|
||||
// the configuration items from that file so that it is very quick. Otherwise
|
||||
// we will need to spin through every configuration file and load them all.
|
||||
if (file_exists($cached = $app->getCachedConfigPath())) {
|
||||
$items = require $cached;
|
||||
|
||||
$app->instance('config_loaded_from_cache', $loadedFromCache = true);
|
||||
}
|
||||
|
||||
// Next we will spin through all of the configuration files in the configuration
|
||||
// directory and load each one into the repository. This will make all of the
|
||||
// options available to the developer for use in various parts of this app.
|
||||
$app->instance('config', $config = new Repository($items));
|
||||
|
||||
if (! isset($loadedFromCache)) {
|
||||
$this->loadConfigurationFiles($app, $config);
|
||||
}
|
||||
|
||||
// Finally, we will set the application's environment based on the configuration
|
||||
// values that were loaded. We will pass a callback which will be used to get
|
||||
// the environment in a web context where an "--env" switch is not present.
|
||||
$app->detectEnvironment(fn () => $config->get('app.env', 'production'));
|
||||
|
||||
date_default_timezone_set($config->get('app.timezone', 'UTC'));
|
||||
|
||||
mb_internal_encoding('UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the configuration items from all of the files.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
||||
* @param \Illuminate\Contracts\Config\Repository $repository
|
||||
* @return void
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function loadConfigurationFiles(Application $app, RepositoryContract $repository)
|
||||
{
|
||||
$files = $this->getConfigurationFiles($app);
|
||||
|
||||
$shouldMerge = method_exists($app, 'shouldMergeFrameworkConfiguration')
|
||||
? $app->shouldMergeFrameworkConfiguration()
|
||||
: true;
|
||||
|
||||
$base = $shouldMerge
|
||||
? $this->getBaseConfiguration()
|
||||
: [];
|
||||
|
||||
foreach (array_diff(array_keys($base), array_keys($files)) as $name => $config) {
|
||||
$repository->set($name, $config);
|
||||
}
|
||||
|
||||
foreach ($files as $name => $path) {
|
||||
$base = $this->loadConfigurationFile($repository, $name, $path, $base);
|
||||
}
|
||||
|
||||
foreach ($base as $name => $config) {
|
||||
$repository->set($name, $config);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the given configuration file.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Config\Repository $repository
|
||||
* @param string $name
|
||||
* @param string $path
|
||||
* @param array $base
|
||||
* @return array
|
||||
*/
|
||||
protected function loadConfigurationFile(RepositoryContract $repository, $name, $path, array $base)
|
||||
{
|
||||
$config = require $path;
|
||||
|
||||
if (isset($base[$name])) {
|
||||
$config = array_merge($base[$name], $config);
|
||||
|
||||
foreach ($this->mergeableOptions($name) as $option) {
|
||||
if (isset($config[$option])) {
|
||||
$config[$option] = array_merge($base[$name][$option], $config[$option]);
|
||||
}
|
||||
}
|
||||
|
||||
unset($base[$name]);
|
||||
}
|
||||
|
||||
$repository->set($name, $config);
|
||||
|
||||
return $base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the options within the configuration file that should be merged again.
|
||||
*
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
protected function mergeableOptions($name)
|
||||
{
|
||||
return [
|
||||
'auth' => ['guards', 'providers', 'passwords'],
|
||||
'broadcasting' => ['connections'],
|
||||
'cache' => ['stores'],
|
||||
'database' => ['connections'],
|
||||
'filesystems' => ['disks'],
|
||||
'logging' => ['channels'],
|
||||
'mail' => ['mailers'],
|
||||
'queue' => ['connections'],
|
||||
][$name] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the configuration files for the application.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
||||
* @return array
|
||||
*/
|
||||
protected function getConfigurationFiles(Application $app)
|
||||
{
|
||||
$files = [];
|
||||
|
||||
$configPath = realpath($app->configPath());
|
||||
|
||||
if (! $configPath) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach (Finder::create()->files()->name('*.php')->in($configPath) as $file) {
|
||||
$directory = $this->getNestedDirectory($file, $configPath);
|
||||
|
||||
$files[$directory.basename($file->getRealPath(), '.php')] = $file->getRealPath();
|
||||
}
|
||||
|
||||
ksort($files, SORT_NATURAL);
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration file nesting path.
|
||||
*
|
||||
* @param \SplFileInfo $file
|
||||
* @param string $configPath
|
||||
* @return string
|
||||
*/
|
||||
protected function getNestedDirectory(SplFileInfo $file, $configPath)
|
||||
{
|
||||
$directory = $file->getPath();
|
||||
|
||||
if ($nested = trim(str_replace($configPath, '', $directory), DIRECTORY_SEPARATOR)) {
|
||||
$nested = str_replace(DIRECTORY_SEPARATOR, '.', $nested).'.';
|
||||
}
|
||||
|
||||
return $nested;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base configuration files.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getBaseConfiguration()
|
||||
{
|
||||
$config = [];
|
||||
|
||||
foreach (Finder::create()->files()->name('*.php')->in(__DIR__.'/../../../../config') as $file) {
|
||||
$config[basename($file->getRealPath(), '.php')] = require $file->getRealPath();
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Bootstrap;
|
||||
|
||||
use Dotenv\Dotenv;
|
||||
use Dotenv\Exception\InvalidFileException;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Support\Env;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
|
||||
class LoadEnvironmentVariables
|
||||
{
|
||||
/**
|
||||
* Bootstrap the given application.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrap(Application $app)
|
||||
{
|
||||
if ($app->configurationIsCached()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->checkForSpecificEnvironmentFile($app);
|
||||
|
||||
try {
|
||||
$this->createDotenv($app)->safeLoad();
|
||||
} catch (InvalidFileException $e) {
|
||||
$this->writeErrorAndDie($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if a custom environment file matching the APP_ENV exists.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
||||
* @return void
|
||||
*/
|
||||
protected function checkForSpecificEnvironmentFile($app)
|
||||
{
|
||||
if ($app->runningInConsole() &&
|
||||
($input = new ArgvInput)->hasParameterOption('--env') &&
|
||||
$this->setEnvironmentFilePath($app, $app->environmentFile().'.'.$input->getParameterOption('--env'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
$environment = Env::get('APP_ENV');
|
||||
|
||||
if (! $environment) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->setEnvironmentFilePath(
|
||||
$app, $app->environmentFile().'.'.$environment
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a custom environment file.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
||||
* @param string $file
|
||||
* @return bool
|
||||
*/
|
||||
protected function setEnvironmentFilePath($app, $file)
|
||||
{
|
||||
if (is_file($app->environmentPath().'/'.$file)) {
|
||||
$app->loadEnvironmentFrom($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Dotenv instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
||||
* @return \Dotenv\Dotenv
|
||||
*/
|
||||
protected function createDotenv($app)
|
||||
{
|
||||
return Dotenv::create(
|
||||
Env::getRepository(),
|
||||
$app->environmentPath(),
|
||||
$app->environmentFile()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the error information to the screen and exit.
|
||||
*
|
||||
* @param \Dotenv\Exception\InvalidFileException $e
|
||||
* @return never
|
||||
*/
|
||||
protected function writeErrorAndDie(InvalidFileException $e)
|
||||
{
|
||||
$output = (new ConsoleOutput)->getErrorOutput();
|
||||
|
||||
$output->writeln('The environment file is invalid!');
|
||||
$output->writeln($e->getMessage());
|
||||
|
||||
http_response_code(500);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Bootstrap;
|
||||
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Foundation\AliasLoader;
|
||||
use Illuminate\Foundation\PackageManifest;
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
|
||||
class RegisterFacades
|
||||
{
|
||||
/**
|
||||
* Bootstrap the given application.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrap(Application $app)
|
||||
{
|
||||
Facade::clearResolvedInstances();
|
||||
|
||||
Facade::setFacadeApplication($app);
|
||||
|
||||
AliasLoader::getInstance(array_merge(
|
||||
$app->make('config')->get('app.aliases', []),
|
||||
$app->make(PackageManifest::class)->aliases()
|
||||
))->register();
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Bootstrap;
|
||||
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class RegisterProviders
|
||||
{
|
||||
/**
|
||||
* The service providers that should be merged before registration.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $merge = [];
|
||||
|
||||
/**
|
||||
* The path to the bootstrap provider configuration file.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected static $bootstrapProviderPath;
|
||||
|
||||
/**
|
||||
* Bootstrap the given application.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrap(Application $app)
|
||||
{
|
||||
if (! $app->bound('config_loaded_from_cache') ||
|
||||
$app->make('config_loaded_from_cache') === false) {
|
||||
$this->mergeAdditionalProviders($app);
|
||||
}
|
||||
|
||||
$app->registerConfiguredProviders();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the additional configured providers into the configuration.
|
||||
*
|
||||
* @param \Illuminate\Foundation\Application $app
|
||||
*/
|
||||
protected function mergeAdditionalProviders(Application $app)
|
||||
{
|
||||
if (static::$bootstrapProviderPath &&
|
||||
file_exists(static::$bootstrapProviderPath)) {
|
||||
$packageProviders = require static::$bootstrapProviderPath;
|
||||
|
||||
foreach ($packageProviders as $index => $provider) {
|
||||
if (! class_exists($provider)) {
|
||||
unset($packageProviders[$index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$app->make('config')->set(
|
||||
'app.providers',
|
||||
array_merge(
|
||||
$app->make('config')->get('app.providers') ?? ServiceProvider::defaultProviders()->toArray(),
|
||||
static::$merge,
|
||||
array_values($packageProviders ?? []),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the given providers into the provider configuration before registration.
|
||||
*
|
||||
* @param array $providers
|
||||
* @param string|null $bootstrapProviderPath
|
||||
* @return void
|
||||
*/
|
||||
public static function merge(array $providers, ?string $bootstrapProviderPath = null)
|
||||
{
|
||||
static::$bootstrapProviderPath = $bootstrapProviderPath;
|
||||
|
||||
static::$merge = array_values(array_filter(array_unique(
|
||||
array_merge(static::$merge, $providers)
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the bootstrapper's global state.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function flushState()
|
||||
{
|
||||
static::$bootstrapProviderPath = null;
|
||||
|
||||
static::$merge = [];
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Bootstrap;
|
||||
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class SetRequestForConsole
|
||||
{
|
||||
/**
|
||||
* Bootstrap the given application.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrap(Application $app)
|
||||
{
|
||||
$uri = $app->make('config')->get('app.url', 'http://localhost');
|
||||
|
||||
$components = parse_url($uri);
|
||||
|
||||
$server = $_SERVER;
|
||||
|
||||
if (isset($components['path'])) {
|
||||
$server = array_merge($server, [
|
||||
'SCRIPT_FILENAME' => $components['path'],
|
||||
'SCRIPT_NAME' => $components['path'],
|
||||
]);
|
||||
}
|
||||
|
||||
$app->instance('request', Request::create(
|
||||
$uri, 'GET', [], [], [], $server
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Bus;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
use Illuminate\Support\Fluent;
|
||||
|
||||
trait Dispatchable
|
||||
{
|
||||
/**
|
||||
* Dispatch the job with the given arguments.
|
||||
*
|
||||
* @param mixed ...$arguments
|
||||
* @return \Illuminate\Foundation\Bus\PendingDispatch
|
||||
*/
|
||||
public static function dispatch(...$arguments)
|
||||
{
|
||||
return new PendingDispatch(new static(...$arguments));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the job with the given arguments if the given truth test passes.
|
||||
*
|
||||
* @param bool|\Closure $boolean
|
||||
* @param mixed ...$arguments
|
||||
* @return \Illuminate\Foundation\Bus\PendingDispatch|\Illuminate\Support\Fluent
|
||||
*/
|
||||
public static function dispatchIf($boolean, ...$arguments)
|
||||
{
|
||||
if ($boolean instanceof Closure) {
|
||||
$dispatchable = new static(...$arguments);
|
||||
|
||||
return value($boolean, $dispatchable)
|
||||
? new PendingDispatch($dispatchable)
|
||||
: new Fluent;
|
||||
}
|
||||
|
||||
return value($boolean)
|
||||
? new PendingDispatch(new static(...$arguments))
|
||||
: new Fluent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the job with the given arguments unless the given truth test passes.
|
||||
*
|
||||
* @param bool|\Closure $boolean
|
||||
* @param mixed ...$arguments
|
||||
* @return \Illuminate\Foundation\Bus\PendingDispatch|\Illuminate\Support\Fluent
|
||||
*/
|
||||
public static function dispatchUnless($boolean, ...$arguments)
|
||||
{
|
||||
if ($boolean instanceof Closure) {
|
||||
$dispatchable = new static(...$arguments);
|
||||
|
||||
return ! value($boolean, $dispatchable)
|
||||
? new PendingDispatch($dispatchable)
|
||||
: new Fluent;
|
||||
}
|
||||
|
||||
return ! value($boolean)
|
||||
? new PendingDispatch(new static(...$arguments))
|
||||
: new Fluent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a command to its appropriate handler in the current process.
|
||||
*
|
||||
* Queueable jobs will be dispatched to the "sync" queue.
|
||||
*
|
||||
* @param mixed ...$arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public static function dispatchSync(...$arguments)
|
||||
{
|
||||
return app(Dispatcher::class)->dispatchSync(new static(...$arguments));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a command to its appropriate handler after the current process.
|
||||
*
|
||||
* @param mixed ...$arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public static function dispatchAfterResponse(...$arguments)
|
||||
{
|
||||
return self::dispatch(...$arguments)->afterResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the jobs that should run if this job is successful.
|
||||
*
|
||||
* @param array $chain
|
||||
* @return \Illuminate\Foundation\Bus\PendingChain
|
||||
*/
|
||||
public static function withChain($chain)
|
||||
{
|
||||
return new PendingChain(static::class, $chain);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Bus;
|
||||
|
||||
trait DispatchesJobs
|
||||
{
|
||||
/**
|
||||
* Dispatch a job to its appropriate handler.
|
||||
*
|
||||
* @param mixed $job
|
||||
* @return mixed
|
||||
*/
|
||||
protected function dispatch($job)
|
||||
{
|
||||
return dispatch($job);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a job to its appropriate handler in the current process.
|
||||
*
|
||||
* Queueable jobs will be dispatched to the "sync" queue.
|
||||
*
|
||||
* @param mixed $job
|
||||
* @return mixed
|
||||
*/
|
||||
public function dispatchSync($job)
|
||||
{
|
||||
return dispatch_sync($job);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Bus;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
use Illuminate\Queue\CallQueuedClosure;
|
||||
use Illuminate\Support\Traits\Conditionable;
|
||||
use Laravel\SerializableClosure\SerializableClosure;
|
||||
|
||||
class PendingChain
|
||||
{
|
||||
use Conditionable;
|
||||
|
||||
/**
|
||||
* The class name of the job being dispatched.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $job;
|
||||
|
||||
/**
|
||||
* The jobs to be chained.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $chain;
|
||||
|
||||
/**
|
||||
* The name of the connection the chain should be sent to.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $connection;
|
||||
|
||||
/**
|
||||
* The name of the queue the chain should be sent to.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $queue;
|
||||
|
||||
/**
|
||||
* The number of seconds before the chain should be made available.
|
||||
*
|
||||
* @var \DateTimeInterface|\DateInterval|int|null
|
||||
*/
|
||||
public $delay;
|
||||
|
||||
/**
|
||||
* The callbacks to be executed on failure.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $catchCallbacks = [];
|
||||
|
||||
/**
|
||||
* Create a new PendingChain instance.
|
||||
*
|
||||
* @param mixed $job
|
||||
* @param array $chain
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($job, $chain)
|
||||
{
|
||||
$this->job = $job;
|
||||
$this->chain = $chain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the desired connection for the job.
|
||||
*
|
||||
* @param string|null $connection
|
||||
* @return $this
|
||||
*/
|
||||
public function onConnection($connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the desired queue for the job.
|
||||
*
|
||||
* @param string|null $queue
|
||||
* @return $this
|
||||
*/
|
||||
public function onQueue($queue)
|
||||
{
|
||||
$this->queue = $queue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the desired delay in seconds for the chain.
|
||||
*
|
||||
* @param \DateTimeInterface|\DateInterval|int|null $delay
|
||||
* @return $this
|
||||
*/
|
||||
public function delay($delay)
|
||||
{
|
||||
$this->delay = $delay;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback to be executed on job failure.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function catch($callback)
|
||||
{
|
||||
$this->catchCallbacks[] = $callback instanceof Closure
|
||||
? new SerializableClosure($callback)
|
||||
: $callback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "catch" callbacks that have been registered.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function catchCallbacks()
|
||||
{
|
||||
return $this->catchCallbacks ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the job chain.
|
||||
*
|
||||
* @return \Illuminate\Foundation\Bus\PendingDispatch
|
||||
*/
|
||||
public function dispatch()
|
||||
{
|
||||
if (is_string($this->job)) {
|
||||
$firstJob = new $this->job(...func_get_args());
|
||||
} elseif ($this->job instanceof Closure) {
|
||||
$firstJob = CallQueuedClosure::create($this->job);
|
||||
} else {
|
||||
$firstJob = $this->job;
|
||||
}
|
||||
|
||||
if ($this->connection) {
|
||||
$firstJob->chainConnection = $this->connection;
|
||||
$firstJob->connection = $firstJob->connection ?: $this->connection;
|
||||
}
|
||||
|
||||
if ($this->queue) {
|
||||
$firstJob->chainQueue = $this->queue;
|
||||
$firstJob->queue = $firstJob->queue ?: $this->queue;
|
||||
}
|
||||
|
||||
if ($this->delay) {
|
||||
$firstJob->delay = ! is_null($firstJob->delay) ? $firstJob->delay : $this->delay;
|
||||
}
|
||||
|
||||
$firstJob->chain($this->chain);
|
||||
$firstJob->chainCatchCallbacks = $this->catchCallbacks();
|
||||
|
||||
return app(Dispatcher::class)->dispatch($firstJob);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the job chain if the given truth test passes.
|
||||
*
|
||||
* @param bool|\Closure $boolean
|
||||
* @return \Illuminate\Foundation\Bus\PendingDispatch|null
|
||||
*/
|
||||
public function dispatchIf($boolean)
|
||||
{
|
||||
return value($boolean) ? $this->dispatch() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the job chain unless the given truth test passes.
|
||||
*
|
||||
* @param bool|\Closure $boolean
|
||||
* @return \Illuminate\Foundation\Bus\PendingDispatch|null
|
||||
*/
|
||||
public function dispatchUnless($boolean)
|
||||
{
|
||||
return ! value($boolean) ? $this->dispatch() : null;
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Bus;
|
||||
|
||||
use Closure;
|
||||
|
||||
class PendingClosureDispatch extends PendingDispatch
|
||||
{
|
||||
/**
|
||||
* Add a callback to be executed if the job fails.
|
||||
*
|
||||
* @param \Closure $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function catch(Closure $callback)
|
||||
{
|
||||
$this->job->onFailure($callback);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Bus;
|
||||
|
||||
use Illuminate\Bus\UniqueLock;
|
||||
use Illuminate\Container\Container;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
use Illuminate\Contracts\Cache\Repository as Cache;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
|
||||
class PendingDispatch
|
||||
{
|
||||
/**
|
||||
* The job.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $job;
|
||||
|
||||
/**
|
||||
* Indicates if the job should be dispatched immediately after sending the response.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $afterResponse = false;
|
||||
|
||||
/**
|
||||
* Create a new pending job dispatch.
|
||||
*
|
||||
* @param mixed $job
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($job)
|
||||
{
|
||||
$this->job = $job;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the desired connection for the job.
|
||||
*
|
||||
* @param string|null $connection
|
||||
* @return $this
|
||||
*/
|
||||
public function onConnection($connection)
|
||||
{
|
||||
$this->job->onConnection($connection);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the desired queue for the job.
|
||||
*
|
||||
* @param string|null $queue
|
||||
* @return $this
|
||||
*/
|
||||
public function onQueue($queue)
|
||||
{
|
||||
$this->job->onQueue($queue);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the desired connection for the chain.
|
||||
*
|
||||
* @param string|null $connection
|
||||
* @return $this
|
||||
*/
|
||||
public function allOnConnection($connection)
|
||||
{
|
||||
$this->job->allOnConnection($connection);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the desired queue for the chain.
|
||||
*
|
||||
* @param string|null $queue
|
||||
* @return $this
|
||||
*/
|
||||
public function allOnQueue($queue)
|
||||
{
|
||||
$this->job->allOnQueue($queue);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the desired delay in seconds for the job.
|
||||
*
|
||||
* @param \DateTimeInterface|\DateInterval|int|null $delay
|
||||
* @return $this
|
||||
*/
|
||||
public function delay($delay)
|
||||
{
|
||||
$this->job->delay($delay);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the job should be dispatched after all database transactions have committed.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function afterCommit()
|
||||
{
|
||||
$this->job->afterCommit();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the job should not wait until database transactions have been committed before dispatching.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function beforeCommit()
|
||||
{
|
||||
$this->job->beforeCommit();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the jobs that should run if this job is successful.
|
||||
*
|
||||
* @param array $chain
|
||||
* @return $this
|
||||
*/
|
||||
public function chain($chain)
|
||||
{
|
||||
$this->job->chain($chain);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the job should be dispatched after the response is sent to the browser.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function afterResponse()
|
||||
{
|
||||
$this->afterResponse = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the job should be dispatched.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function shouldDispatch()
|
||||
{
|
||||
if (! $this->job instanceof ShouldBeUnique) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (new UniqueLock(Container::getInstance()->make(Cache::class)))
|
||||
->acquire($this->job);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically proxy methods to the underlying job.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
* @return $this
|
||||
*/
|
||||
public function __call($method, $parameters)
|
||||
{
|
||||
$this->job->{$method}(...$parameters);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the object's destruction.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if (! $this->shouldDispatch()) {
|
||||
return;
|
||||
} elseif ($this->afterResponse) {
|
||||
app(Dispatcher::class)->dispatchAfterResponse($this->job);
|
||||
} else {
|
||||
app(Dispatcher::class)->dispatch($this->job);
|
||||
}
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation;
|
||||
|
||||
use Illuminate\Contracts\Cache\Factory;
|
||||
use Illuminate\Contracts\Cache\Repository;
|
||||
use Illuminate\Contracts\Foundation\MaintenanceMode;
|
||||
|
||||
class CacheBasedMaintenanceMode implements MaintenanceMode
|
||||
{
|
||||
/**
|
||||
* The cache factory.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Cache\Factory
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* The cache store that should be utilized.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $store;
|
||||
|
||||
/**
|
||||
* The cache key to use when storing maintenance mode information.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* Create a new cache based maintenance mode implementation.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Cache\Factory $cache
|
||||
* @param string $store
|
||||
* @param string $key
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Factory $cache, string $store, string $key)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
$this->store = $store;
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the application down for maintenance.
|
||||
*
|
||||
* @param array $payload
|
||||
* @return void
|
||||
*/
|
||||
public function activate(array $payload): void
|
||||
{
|
||||
$this->getStore()->put($this->key, $payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the application out of maintenance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deactivate(): void
|
||||
{
|
||||
$this->getStore()->forget($this->key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the application is currently down for maintenance.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function active(): bool
|
||||
{
|
||||
return $this->getStore()->has($this->key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data array which was provided when the application was placed into maintenance.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data(): array
|
||||
{
|
||||
return $this->getStore()->get($this->key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cache store to use.
|
||||
*
|
||||
* @return \Illuminate\Contracts\Cache\Repository
|
||||
*/
|
||||
protected function getStore(): Repository
|
||||
{
|
||||
return $this->cache->store($this->store);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation;
|
||||
|
||||
use Composer\Script\Event;
|
||||
|
||||
class ComposerScripts
|
||||
{
|
||||
/**
|
||||
* Handle the post-install Composer event.
|
||||
*
|
||||
* @param \Composer\Script\Event $event
|
||||
* @return void
|
||||
*/
|
||||
public static function postInstall(Event $event)
|
||||
{
|
||||
require_once $event->getComposer()->getConfig()->get('vendor-dir').'/autoload.php';
|
||||
|
||||
static::clearCompiled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the post-update Composer event.
|
||||
*
|
||||
* @param \Composer\Script\Event $event
|
||||
* @return void
|
||||
*/
|
||||
public static function postUpdate(Event $event)
|
||||
{
|
||||
require_once $event->getComposer()->getConfig()->get('vendor-dir').'/autoload.php';
|
||||
|
||||
static::clearCompiled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the post-autoload-dump Composer event.
|
||||
*
|
||||
* @param \Composer\Script\Event $event
|
||||
* @return void
|
||||
*/
|
||||
public static function postAutoloadDump(Event $event)
|
||||
{
|
||||
require_once $event->getComposer()->getConfig()->get('vendor-dir').'/autoload.php';
|
||||
|
||||
static::clearCompiled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cached Laravel bootstrapping files.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function clearCompiled()
|
||||
{
|
||||
$laravel = new Application(getcwd());
|
||||
|
||||
if (is_file($configPath = $laravel->getCachedConfigPath())) {
|
||||
@unlink($configPath);
|
||||
}
|
||||
|
||||
if (is_file($servicesPath = $laravel->getCachedServicesPath())) {
|
||||
@unlink($servicesPath);
|
||||
}
|
||||
|
||||
if (is_file($packagesPath = $laravel->getCachedPackagesPath())) {
|
||||
@unlink($packagesPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Concerns;
|
||||
|
||||
use Throwable;
|
||||
|
||||
trait ResolvesDumpSource
|
||||
{
|
||||
/**
|
||||
* All of the href formats for common editors.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $editorHrefs = [
|
||||
'atom' => 'atom://core/open/file?filename={file}&line={line}',
|
||||
'emacs' => 'emacs://open?url=file://{file}&line={line}',
|
||||
'idea' => 'idea://open?file={file}&line={line}',
|
||||
'macvim' => 'mvim://open/?url=file://{file}&line={line}',
|
||||
'netbeans' => 'netbeans://open/?f={file}:{line}',
|
||||
'nova' => 'nova://core/open/file?filename={file}&line={line}',
|
||||
'phpstorm' => 'phpstorm://open?file={file}&line={line}',
|
||||
'sublime' => 'subl://open?url=file://{file}&line={line}',
|
||||
'textmate' => 'txmt://open?url=file://{file}&line={line}',
|
||||
'vscode' => 'vscode://file/{file}:{line}',
|
||||
'vscode-insiders' => 'vscode-insiders://file/{file}:{line}',
|
||||
'vscode-insiders-remote' => 'vscode-insiders://vscode-remote/{file}:{line}',
|
||||
'vscode-remote' => 'vscode://vscode-remote/{file}:{line}',
|
||||
'vscodium' => 'vscodium://file/{file}:{line}',
|
||||
'xdebug' => 'xdebug://{file}@{line}',
|
||||
];
|
||||
|
||||
/**
|
||||
* Files that require special trace handling and their levels.
|
||||
*
|
||||
* @var array<string, int>
|
||||
*/
|
||||
protected static $adjustableTraces = [
|
||||
'symfony/var-dumper/Resources/functions/dump.php' => 1,
|
||||
'Illuminate/Collections/Traits/EnumeratesValues.php' => 4,
|
||||
];
|
||||
|
||||
/**
|
||||
* The source resolver.
|
||||
*
|
||||
* @var (callable(): (array{0: string, 1: string, 2: int|null}|null))|null|false
|
||||
*/
|
||||
protected static $dumpSourceResolver;
|
||||
|
||||
/**
|
||||
* Resolve the source of the dump call.
|
||||
*
|
||||
* @return array{0: string, 1: string, 2: int|null}|null
|
||||
*/
|
||||
public function resolveDumpSource()
|
||||
{
|
||||
if (static::$dumpSourceResolver === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (static::$dumpSourceResolver) {
|
||||
return call_user_func(static::$dumpSourceResolver);
|
||||
}
|
||||
|
||||
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 20);
|
||||
|
||||
$sourceKey = null;
|
||||
|
||||
foreach ($trace as $traceKey => $traceFile) {
|
||||
if (! isset($traceFile['file'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (self::$adjustableTraces as $name => $key) {
|
||||
if (str_ends_with(
|
||||
$traceFile['file'],
|
||||
str_replace('/', DIRECTORY_SEPARATOR, $name)
|
||||
)) {
|
||||
$sourceKey = $traceKey + $key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! is_null($sourceKey)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($sourceKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$file = $trace[$sourceKey]['file'] ?? null;
|
||||
$line = $trace[$sourceKey]['line'] ?? null;
|
||||
|
||||
if (is_null($file) || is_null($line)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$relativeFile = $file;
|
||||
|
||||
if ($this->isCompiledViewFile($file)) {
|
||||
$file = $this->getOriginalFileForCompiledView($file);
|
||||
$line = null;
|
||||
}
|
||||
|
||||
if (str_starts_with($file, $this->basePath)) {
|
||||
$relativeFile = substr($file, strlen($this->basePath) + 1);
|
||||
}
|
||||
|
||||
return [$file, $relativeFile, $line];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given file is a view compiled.
|
||||
*
|
||||
* @param string $file
|
||||
* @return bool
|
||||
*/
|
||||
protected function isCompiledViewFile($file)
|
||||
{
|
||||
return str_starts_with($file, $this->compiledViewPath) && str_ends_with($file, '.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the original view compiled file by the given compiled file.
|
||||
*
|
||||
* @param string $file
|
||||
* @return string
|
||||
*/
|
||||
protected function getOriginalFileForCompiledView($file)
|
||||
{
|
||||
preg_match('/\/\*\*PATH\s(.*)\sENDPATH/', file_get_contents($file), $matches);
|
||||
|
||||
if (isset($matches[1])) {
|
||||
$file = $matches[1];
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the source href, if possible.
|
||||
*
|
||||
* @param string $file
|
||||
* @param int|null $line
|
||||
* @return string|null
|
||||
*/
|
||||
protected function resolveSourceHref($file, $line)
|
||||
{
|
||||
try {
|
||||
$editor = config('app.editor');
|
||||
} catch (Throwable) {
|
||||
// ..
|
||||
}
|
||||
|
||||
if (! isset($editor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$href = is_array($editor) && isset($editor['href'])
|
||||
? $editor['href']
|
||||
: ($this->editorHrefs[$editor['name'] ?? $editor] ?? sprintf('%s://open?file={file}&line={line}', $editor['name'] ?? $editor));
|
||||
|
||||
if ($basePath = $editor['base_path'] ?? false) {
|
||||
$file = str_replace($this->basePath, $basePath, $file);
|
||||
}
|
||||
|
||||
$href = str_replace(
|
||||
['{file}', '{line}'],
|
||||
[$file, is_null($line) ? 1 : $line],
|
||||
$href,
|
||||
);
|
||||
|
||||
return $href;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resolver that resolves the source of the dump call.
|
||||
*
|
||||
* @param (callable(): (array{0: string, 1: string, 2: int|null}|null))|null $callable
|
||||
* @return void
|
||||
*/
|
||||
public static function resolveDumpSourceUsing($callable)
|
||||
{
|
||||
static::$dumpSourceResolver = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't include the location / file of the dump in dumps.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function dontIncludeSource()
|
||||
{
|
||||
static::$dumpSourceResolver = false;
|
||||
}
|
||||
}
|
||||
+425
@@ -0,0 +1,425 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Configuration;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Console\Application as Artisan;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Contracts\Console\Kernel as ConsoleKernel;
|
||||
use Illuminate\Contracts\Http\Kernel as HttpKernel;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Foundation\Bootstrap\RegisterProviders;
|
||||
use Illuminate\Foundation\Events\DiagnosingHealth;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as AppEventServiceProvider;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as AppRouteServiceProvider;
|
||||
use Illuminate\Support\Facades\Broadcast;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Laravel\Folio\Folio;
|
||||
|
||||
class ApplicationBuilder
|
||||
{
|
||||
/**
|
||||
* The service provider that are marked for registration.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $pendingProviders = [];
|
||||
|
||||
/**
|
||||
* The Folio / page middleware that have been defined by the user.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $pageMiddleware = [];
|
||||
|
||||
/**
|
||||
* Create a new application builder instance.
|
||||
*/
|
||||
public function __construct(protected Application $app)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the standard kernel classes for the application.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withKernels()
|
||||
{
|
||||
$this->app->singleton(
|
||||
\Illuminate\Contracts\Http\Kernel::class,
|
||||
\Illuminate\Foundation\Http\Kernel::class,
|
||||
);
|
||||
|
||||
$this->app->singleton(
|
||||
\Illuminate\Contracts\Console\Kernel::class,
|
||||
\Illuminate\Foundation\Console\Kernel::class,
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register additional service providers.
|
||||
*
|
||||
* @param array $providers
|
||||
* @param bool $withBootstrapProviders
|
||||
* @return $this
|
||||
*/
|
||||
public function withProviders(array $providers = [], bool $withBootstrapProviders = true)
|
||||
{
|
||||
RegisterProviders::merge(
|
||||
$providers,
|
||||
$withBootstrapProviders
|
||||
? $this->app->getBootstrapProvidersPath()
|
||||
: null
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the core event service provider for the application.
|
||||
*
|
||||
* @param array|bool $discover
|
||||
* @return $this
|
||||
*/
|
||||
public function withEvents(array|bool $discover = [])
|
||||
{
|
||||
if (is_array($discover) && count($discover) > 0) {
|
||||
AppEventServiceProvider::setEventDiscoveryPaths($discover);
|
||||
}
|
||||
|
||||
if ($discover === false) {
|
||||
AppEventServiceProvider::disableEventDiscovery();
|
||||
}
|
||||
|
||||
if (! isset($this->pendingProviders[AppEventServiceProvider::class])) {
|
||||
$this->app->booting(function () {
|
||||
$this->app->register(AppEventServiceProvider::class);
|
||||
});
|
||||
}
|
||||
|
||||
$this->pendingProviders[AppEventServiceProvider::class] = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the broadcasting services for the application.
|
||||
*
|
||||
* @param string $channels
|
||||
* @param array $attributes
|
||||
* @return $this
|
||||
*/
|
||||
public function withBroadcasting(string $channels, array $attributes = [])
|
||||
{
|
||||
$this->app->booted(function () use ($channels, $attributes) {
|
||||
Broadcast::routes(! empty($attributes) ? $attributes : null);
|
||||
|
||||
if (file_exists($channels)) {
|
||||
require $channels;
|
||||
}
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the routing services for the application.
|
||||
*
|
||||
* @param \Closure|null $using
|
||||
* @param array|string|null $web
|
||||
* @param array|string|null $api
|
||||
* @param string|null $commands
|
||||
* @param string|null $channels
|
||||
* @param string|null $pages
|
||||
* @param string $apiPrefix
|
||||
* @param callable|null $then
|
||||
* @return $this
|
||||
*/
|
||||
public function withRouting(?Closure $using = null,
|
||||
array|string|null $web = null,
|
||||
array|string|null $api = null,
|
||||
?string $commands = null,
|
||||
?string $channels = null,
|
||||
?string $pages = null,
|
||||
?string $health = null,
|
||||
string $apiPrefix = 'api',
|
||||
?callable $then = null)
|
||||
{
|
||||
if (is_null($using) && (is_string($web) || is_array($web) || is_string($api) || is_array($api) || is_string($pages) || is_string($health)) || is_callable($then)) {
|
||||
$using = $this->buildRoutingCallback($web, $api, $pages, $health, $apiPrefix, $then);
|
||||
}
|
||||
|
||||
AppRouteServiceProvider::loadRoutesUsing($using);
|
||||
|
||||
$this->app->booting(function () {
|
||||
$this->app->register(AppRouteServiceProvider::class, force: true);
|
||||
});
|
||||
|
||||
if (is_string($commands) && realpath($commands) !== false) {
|
||||
$this->withCommands([$commands]);
|
||||
}
|
||||
|
||||
if (is_string($channels) && realpath($channels) !== false) {
|
||||
$this->withBroadcasting($channels);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the routing callback for the application.
|
||||
*
|
||||
* @param array|string|null $web
|
||||
* @param array|string|null $api
|
||||
* @param string|null $pages
|
||||
* @param string|null $health
|
||||
* @param string $apiPrefix
|
||||
* @param callable|null $then
|
||||
* @return \Closure
|
||||
*/
|
||||
protected function buildRoutingCallback(array|string|null $web,
|
||||
array|string|null $api,
|
||||
?string $pages,
|
||||
?string $health,
|
||||
string $apiPrefix,
|
||||
?callable $then)
|
||||
{
|
||||
return function () use ($web, $api, $pages, $health, $apiPrefix, $then) {
|
||||
if (is_string($api) || is_array($api)) {
|
||||
if (is_array($api)) {
|
||||
foreach ($api as $apiRoute) {
|
||||
if (realpath($apiRoute) !== false) {
|
||||
Route::middleware('api')->prefix($apiPrefix)->group($apiRoute);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Route::middleware('api')->prefix($apiPrefix)->group($api);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_string($health)) {
|
||||
Route::get($health, function () {
|
||||
Event::dispatch(new DiagnosingHealth);
|
||||
|
||||
return View::file(__DIR__.'/../resources/health-up.blade.php');
|
||||
});
|
||||
}
|
||||
|
||||
if (is_string($web) || is_array($web)) {
|
||||
if (is_array($web)) {
|
||||
foreach ($web as $webRoute) {
|
||||
if (realpath($webRoute) !== false) {
|
||||
Route::middleware('web')->group($webRoute);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Route::middleware('web')->group($web);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_string($pages) &&
|
||||
realpath($pages) !== false &&
|
||||
class_exists(Folio::class)) {
|
||||
Folio::route($pages, middleware: $this->pageMiddleware);
|
||||
}
|
||||
|
||||
if (is_callable($then)) {
|
||||
$then($this->app);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the global middleware, middleware groups, and middleware aliases for the application.
|
||||
*
|
||||
* @param callable|null $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function withMiddleware(?callable $callback = null)
|
||||
{
|
||||
$this->app->afterResolving(HttpKernel::class, function ($kernel) use ($callback) {
|
||||
$middleware = (new Middleware)
|
||||
->redirectGuestsTo(fn () => route('login'));
|
||||
|
||||
if (! is_null($callback)) {
|
||||
$callback($middleware);
|
||||
}
|
||||
|
||||
$this->pageMiddleware = $middleware->getPageMiddleware();
|
||||
$kernel->setGlobalMiddleware($middleware->getGlobalMiddleware());
|
||||
$kernel->setMiddlewareGroups($middleware->getMiddlewareGroups());
|
||||
$kernel->setMiddlewareAliases($middleware->getMiddlewareAliases());
|
||||
|
||||
if ($priorities = $middleware->getMiddlewarePriority()) {
|
||||
$kernel->setMiddlewarePriority($priorities);
|
||||
}
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register additional Artisan commands with the application.
|
||||
*
|
||||
* @param array $commands
|
||||
* @return $this
|
||||
*/
|
||||
public function withCommands(array $commands = [])
|
||||
{
|
||||
if (empty($commands)) {
|
||||
$commands = [$this->app->path('Console/Commands')];
|
||||
}
|
||||
|
||||
$this->app->afterResolving(ConsoleKernel::class, function ($kernel) use ($commands) {
|
||||
[$commands, $paths] = collect($commands)->partition(fn ($command) => class_exists($command));
|
||||
[$routes, $paths] = $paths->partition(fn ($path) => is_file($path));
|
||||
|
||||
$this->app->booted(static function () use ($kernel, $commands, $paths, $routes) {
|
||||
$kernel->addCommands($commands->all());
|
||||
$kernel->addCommandPaths($paths->all());
|
||||
$kernel->addCommandRoutePaths($routes->all());
|
||||
});
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register additional Artisan route paths.
|
||||
*
|
||||
* @param array $paths
|
||||
* @return $this
|
||||
*/
|
||||
protected function withCommandRouting(array $paths)
|
||||
{
|
||||
$this->app->afterResolving(ConsoleKernel::class, function ($kernel) use ($paths) {
|
||||
$this->app->booted(fn () => $kernel->addCommandRoutePaths($paths));
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the scheduled tasks for the application.
|
||||
*
|
||||
* @param callable(\Illuminate\Console\Scheduling\Schedule $schedule): void $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function withSchedule(callable $callback)
|
||||
{
|
||||
Artisan::starting(fn () => $callback($this->app->make(Schedule::class)));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register and configure the application's exception handler.
|
||||
*
|
||||
* @param callable|null $using
|
||||
* @return $this
|
||||
*/
|
||||
public function withExceptions(?callable $using = null)
|
||||
{
|
||||
$this->app->singleton(
|
||||
\Illuminate\Contracts\Debug\ExceptionHandler::class,
|
||||
\Illuminate\Foundation\Exceptions\Handler::class
|
||||
);
|
||||
|
||||
$using ??= fn () => true;
|
||||
|
||||
$this->app->afterResolving(
|
||||
\Illuminate\Foundation\Exceptions\Handler::class,
|
||||
fn ($handler) => $using(new Exceptions($handler)),
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an array of container bindings to be bound when the application is booting.
|
||||
*
|
||||
* @param array $bindings
|
||||
* @return $this
|
||||
*/
|
||||
public function withBindings(array $bindings)
|
||||
{
|
||||
return $this->registered(function ($app) use ($bindings) {
|
||||
foreach ($bindings as $abstract => $concrete) {
|
||||
$app->bind($abstract, $concrete);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an array of singleton container bindings to be bound when the application is booting.
|
||||
*
|
||||
* @param array $singletons
|
||||
* @return $this
|
||||
*/
|
||||
public function withSingletons(array $singletons)
|
||||
{
|
||||
return $this->registered(function ($app) use ($singletons) {
|
||||
foreach ($singletons as $abstract => $concrete) {
|
||||
if (is_string($abstract)) {
|
||||
$app->singleton($abstract, $concrete);
|
||||
} else {
|
||||
$app->singleton($concrete);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked when the application's service providers are registered.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function registered(callable $callback)
|
||||
{
|
||||
$this->app->registered($callback);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked when the application is "booting".
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function booting(callable $callback)
|
||||
{
|
||||
$this->app->booting($callback);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked when the application is "booted".
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function booted(callable $callback)
|
||||
{
|
||||
$this->app->booted($callback);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the application instance.
|
||||
*
|
||||
* @return \Illuminate\Foundation\Application
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return $this->app;
|
||||
}
|
||||
}
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Configuration;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Foundation\Exceptions\Handler;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class Exceptions
|
||||
{
|
||||
/**
|
||||
* Create a new exception handling configuration instance.
|
||||
*
|
||||
* @param \Illuminate\Foundation\Exceptions\Handler $handler
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(public Handler $handler)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a reportable callback.
|
||||
*
|
||||
* @param callable $using
|
||||
* @return \Illuminate\Foundation\Exceptions\ReportableHandler
|
||||
*/
|
||||
public function report(callable $using)
|
||||
{
|
||||
return $this->handler->reportable($using);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a reportable callback.
|
||||
*
|
||||
* @param callable $reportUsing
|
||||
* @return \Illuminate\Foundation\Exceptions\ReportableHandler
|
||||
*/
|
||||
public function reportable(callable $reportUsing)
|
||||
{
|
||||
return $this->handler->reportable($reportUsing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a renderable callback.
|
||||
*
|
||||
* @param callable $using
|
||||
* @return $this
|
||||
*/
|
||||
public function render(callable $using)
|
||||
{
|
||||
$this->handler->renderable($using);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a renderable callback.
|
||||
*
|
||||
* @param callable $renderUsing
|
||||
* @return $this
|
||||
*/
|
||||
public function renderable(callable $renderUsing)
|
||||
{
|
||||
$this->handler->renderable($renderUsing);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to prepare the final, rendered exception response.
|
||||
*
|
||||
* @param callable $using
|
||||
* @return $this
|
||||
*/
|
||||
public function respond(callable $using)
|
||||
{
|
||||
$this->handler->respondUsing($using);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the callback that should be used to throttle reportable exceptions.
|
||||
*
|
||||
* @param callable $throttleUsing
|
||||
* @return $this
|
||||
*/
|
||||
public function throttle(callable $throttleUsing)
|
||||
{
|
||||
$this->handler->throttleUsing($throttleUsing);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new exception mapping.
|
||||
*
|
||||
* @param \Closure|string $from
|
||||
* @param \Closure|string|null $to
|
||||
* @return $this
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function map($from, $to = null)
|
||||
{
|
||||
$this->handler->map($from, $to);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the log level for the given exception type.
|
||||
*
|
||||
* @param class-string<\Throwable> $type
|
||||
* @param \Psr\Log\LogLevel::* $level
|
||||
* @return $this
|
||||
*/
|
||||
public function level(string $type, string $level)
|
||||
{
|
||||
$this->handler->level($type, $level);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a closure that should be used to build exception context data.
|
||||
*
|
||||
* @param \Closure $contextCallback
|
||||
* @return $this
|
||||
*/
|
||||
public function context(Closure $contextCallback)
|
||||
{
|
||||
$this->handler->buildContextUsing($contextCallback);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the given exception type should not be reported.
|
||||
*
|
||||
* @param array|string $class
|
||||
* @return $this
|
||||
*/
|
||||
public function dontReport(array|string $class)
|
||||
{
|
||||
foreach (Arr::wrap($class) as $exceptionClass) {
|
||||
$this->handler->dontReport($exceptionClass);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not report duplicate exceptions.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function dontReportDuplicates()
|
||||
{
|
||||
$this->handler->dontReportDuplicates();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the given attributes should never be flashed to the session on validation errors.
|
||||
*
|
||||
* @param array|string $attributes
|
||||
* @return $this
|
||||
*/
|
||||
public function dontFlash(array|string $attributes)
|
||||
{
|
||||
$this->handler->dontFlash($attributes);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the callable that determines if the exception handler response should be JSON.
|
||||
*
|
||||
* @param callable(\Illuminate\Http\Request $request, \Throwable): bool $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function shouldRenderJsonWhen(callable $callback)
|
||||
{
|
||||
$this->handler->shouldRenderJsonWhen($callback);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the given exception class should not be ignored.
|
||||
*
|
||||
* @param array<int, class-string<\Throwable>>|class-string<\Throwable> $class
|
||||
* @return $this
|
||||
*/
|
||||
public function stopIgnoring(array|string $class)
|
||||
{
|
||||
$this->handler->stopIgnoring($class);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
+768
@@ -0,0 +1,768 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Configuration;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Auth\Middleware\Authenticate;
|
||||
use Illuminate\Auth\Middleware\RedirectIfAuthenticated;
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies;
|
||||
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
|
||||
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance;
|
||||
use Illuminate\Foundation\Http\Middleware\TrimStrings;
|
||||
use Illuminate\Foundation\Http\Middleware\ValidateCsrfToken;
|
||||
use Illuminate\Http\Middleware\TrustHosts;
|
||||
use Illuminate\Http\Middleware\TrustProxies;
|
||||
use Illuminate\Routing\Middleware\ValidateSignature;
|
||||
use Illuminate\Session\Middleware\AuthenticateSession;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class Middleware
|
||||
{
|
||||
/**
|
||||
* The user defined global middleware stack.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $global = [];
|
||||
|
||||
/**
|
||||
* The middleware that should be prepended to the global middleware stack.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $prepends = [];
|
||||
|
||||
/**
|
||||
* The middleware that should be appended to the global middleware stack.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $appends = [];
|
||||
|
||||
/**
|
||||
* The middleware that should be removed from the global middleware stack.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $removals = [];
|
||||
|
||||
/**
|
||||
* The middleware that should be replaced in the global middleware stack.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $replacements = [];
|
||||
|
||||
/**
|
||||
* The user defined middleware groups.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $groups = [];
|
||||
|
||||
/**
|
||||
* The middleware that should be prepended to the specified groups.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $groupPrepends = [];
|
||||
|
||||
/**
|
||||
* The middleware that should be appended to the specified groups.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $groupAppends = [];
|
||||
|
||||
/**
|
||||
* The middleware that should be removed from the specified groups.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $groupRemovals = [];
|
||||
|
||||
/**
|
||||
* The middleware that should be replaced in the specified groups.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $groupReplacements = [];
|
||||
|
||||
/**
|
||||
* The Folio / page middleware for the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $pageMiddleware = [];
|
||||
|
||||
/**
|
||||
* Indicates if the "trust hosts" middleware is enabled.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $trustHosts = false;
|
||||
|
||||
/**
|
||||
* Indicates if Sanctum's frontend state middleware is enabled.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $statefulApi = false;
|
||||
|
||||
/**
|
||||
* Indicates the API middleware group's rate limiter.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $apiLimiter;
|
||||
|
||||
/**
|
||||
* Indicates if Redis throttling should be applied.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $throttleWithRedis = false;
|
||||
|
||||
/**
|
||||
* Indicates if sessions should be authenticated for the "web" middleware group.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $authenticatedSessions = false;
|
||||
|
||||
/**
|
||||
* The custom middleware aliases.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $customAliases = [];
|
||||
|
||||
/**
|
||||
* The custom middleware priority definition.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $priority = [];
|
||||
|
||||
/**
|
||||
* Prepend middleware to the application's global middleware stack.
|
||||
*
|
||||
* @param array|string $middleware
|
||||
* @return $this
|
||||
*/
|
||||
public function prepend(array|string $middleware)
|
||||
{
|
||||
$this->prepends = array_merge(
|
||||
Arr::wrap($middleware),
|
||||
$this->prepends
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append middleware to the application's global middleware stack.
|
||||
*
|
||||
* @param array|string $middleware
|
||||
* @return $this
|
||||
*/
|
||||
public function append(array|string $middleware)
|
||||
{
|
||||
$this->appends = array_merge(
|
||||
$this->appends,
|
||||
Arr::wrap($middleware)
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove middleware from the application's global middleware stack.
|
||||
*
|
||||
* @param array|string $middleware
|
||||
* @return $this
|
||||
*/
|
||||
public function remove(array|string $middleware)
|
||||
{
|
||||
$this->removals = array_merge(
|
||||
$this->removals,
|
||||
Arr::wrap($middleware)
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a middleware that should be replaced with another middleware.
|
||||
*
|
||||
* @param string $search
|
||||
* @param string $replace
|
||||
* @return $this
|
||||
*/
|
||||
public function replace(string $search, string $replace)
|
||||
{
|
||||
$this->replacements[$search] = $replace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the global middleware for the application.
|
||||
*
|
||||
* @param array $middleware
|
||||
* @return $this
|
||||
*/
|
||||
public function use(array $middleware)
|
||||
{
|
||||
$this->global = $middleware;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a middleware group.
|
||||
*
|
||||
* @param string $group
|
||||
* @param array $middleware
|
||||
* @return $this
|
||||
*/
|
||||
public function group(string $group, array $middleware)
|
||||
{
|
||||
$this->groups[$group] = $middleware;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend the given middleware to the specified group.
|
||||
*
|
||||
* @param string $group
|
||||
* @param array|string $middleware
|
||||
* @return $this
|
||||
*/
|
||||
public function prependToGroup(string $group, array|string $middleware)
|
||||
{
|
||||
$this->groupPrepends[$group] = array_merge(
|
||||
Arr::wrap($middleware),
|
||||
$this->groupPrepends[$group] ?? []
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the given middleware to the specified group.
|
||||
*
|
||||
* @param string $group
|
||||
* @param array|string $middleware
|
||||
* @return $this
|
||||
*/
|
||||
public function appendToGroup(string $group, array|string $middleware)
|
||||
{
|
||||
$this->groupAppends[$group] = array_merge(
|
||||
$this->groupAppends[$group] ?? [],
|
||||
Arr::wrap($middleware)
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given middleware from the specified group.
|
||||
*
|
||||
* @param string $group
|
||||
* @param array|string $middleware
|
||||
* @return $this
|
||||
*/
|
||||
public function removeFromGroup(string $group, array|string $middleware)
|
||||
{
|
||||
$this->groupRemovals[$group] = array_merge(
|
||||
Arr::wrap($middleware),
|
||||
$this->groupRemovals[$group] ?? []
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the given middleware in the specified group with another middleware.
|
||||
*
|
||||
* @param string $group
|
||||
* @param string $search
|
||||
* @param string $replace
|
||||
* @return $this
|
||||
*/
|
||||
public function replaceInGroup(string $group, string $search, string $replace)
|
||||
{
|
||||
$this->groupReplacements[$group][$search] = $replace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the middleware in the "web" group.
|
||||
*
|
||||
* @param array|string $append
|
||||
* @param array|string $prepend
|
||||
* @param array|string $remove
|
||||
* @param array $replace
|
||||
* @return $this
|
||||
*/
|
||||
public function web(array|string $append = [], array|string $prepend = [], array|string $remove = [], array $replace = [])
|
||||
{
|
||||
return $this->modifyGroup('web', $append, $prepend, $remove, $replace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the middleware in the "api" group.
|
||||
*
|
||||
* @param array|string $append
|
||||
* @param array|string $prepend
|
||||
* @param array|string $remove
|
||||
* @param array $replace
|
||||
* @return $this
|
||||
*/
|
||||
public function api(array|string $append = [], array|string $prepend = [], array|string $remove = [], array $replace = [])
|
||||
{
|
||||
return $this->modifyGroup('api', $append, $prepend, $remove, $replace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the middleware in the given group.
|
||||
*
|
||||
* @param string $group
|
||||
* @param array|string $append
|
||||
* @param array|string $prepend
|
||||
* @param array|string $remove
|
||||
* @param array $replace
|
||||
* @return $this
|
||||
*/
|
||||
protected function modifyGroup(string $group, array|string $append, array|string $prepend, array|string $remove, array $replace)
|
||||
{
|
||||
if (! empty($append)) {
|
||||
$this->appendToGroup($group, $append);
|
||||
}
|
||||
|
||||
if (! empty($prepend)) {
|
||||
$this->prependToGroup($group, $prepend);
|
||||
}
|
||||
|
||||
if (! empty($remove)) {
|
||||
$this->removeFromGroup($group, $remove);
|
||||
}
|
||||
|
||||
if (! empty($replace)) {
|
||||
foreach ($replace as $search => $replace) {
|
||||
$this->replaceInGroup($group, $search, $replace);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the Folio / page middleware for the application.
|
||||
*
|
||||
* @param array $middleware
|
||||
* @return $this
|
||||
*/
|
||||
public function pages(array $middleware)
|
||||
{
|
||||
$this->pageMiddleware = $middleware;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register additional middleware aliases.
|
||||
*
|
||||
* @param array $aliases
|
||||
* @return $this
|
||||
*/
|
||||
public function alias(array $aliases)
|
||||
{
|
||||
$this->customAliases = $aliases;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the middleware priority for the application.
|
||||
*
|
||||
* @param array $priority
|
||||
* @return $this
|
||||
*/
|
||||
public function priority(array $priority)
|
||||
{
|
||||
$this->priority = $priority;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the global middleware.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getGlobalMiddleware()
|
||||
{
|
||||
$middleware = $this->global ?: array_values(array_filter([
|
||||
$this->trustHosts ? \Illuminate\Http\Middleware\TrustHosts::class : null,
|
||||
\Illuminate\Http\Middleware\TrustProxies::class,
|
||||
\Illuminate\Http\Middleware\HandleCors::class,
|
||||
\Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||
\Illuminate\Http\Middleware\ValidatePostSize::class,
|
||||
\Illuminate\Foundation\Http\Middleware\TrimStrings::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
]));
|
||||
|
||||
$middleware = array_map(function ($middleware) {
|
||||
return $this->replacements[$middleware] ?? $middleware;
|
||||
}, $middleware);
|
||||
|
||||
return array_values(array_filter(
|
||||
array_diff(
|
||||
array_unique(array_merge($this->prepends, $middleware, $this->appends)),
|
||||
$this->removals
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the middleware groups.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMiddlewareGroups()
|
||||
{
|
||||
$middleware = [
|
||||
'web' => array_values(array_filter([
|
||||
\Illuminate\Cookie\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
$this->authenticatedSessions ? 'auth.session' : null,
|
||||
])),
|
||||
|
||||
'api' => array_values(array_filter([
|
||||
$this->statefulApi ? \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class : null,
|
||||
$this->apiLimiter ? 'throttle:'.$this->apiLimiter : null,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
])),
|
||||
];
|
||||
|
||||
$middleware = array_merge($middleware, $this->groups);
|
||||
|
||||
foreach ($middleware as $group => $groupedMiddleware) {
|
||||
foreach ($groupedMiddleware as $index => $groupMiddleware) {
|
||||
if (isset($this->groupReplacements[$group][$groupMiddleware])) {
|
||||
$middleware[$group][$index] = $this->groupReplacements[$group][$groupMiddleware];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->groupRemovals as $group => $removals) {
|
||||
$middleware[$group] = array_values(array_filter(
|
||||
array_diff($middleware[$group] ?? [], $removals)
|
||||
));
|
||||
}
|
||||
|
||||
foreach ($this->groupPrepends as $group => $prepends) {
|
||||
$middleware[$group] = array_values(array_filter(
|
||||
array_unique(array_merge($prepends, $middleware[$group] ?? []))
|
||||
));
|
||||
}
|
||||
|
||||
foreach ($this->groupAppends as $group => $appends) {
|
||||
$middleware[$group] = array_values(array_filter(
|
||||
array_unique(array_merge($middleware[$group] ?? [], $appends))
|
||||
));
|
||||
}
|
||||
|
||||
return $middleware;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure where guests are redirected by the "auth" middleware.
|
||||
*
|
||||
* @param callable|string $redirect
|
||||
* @return $this
|
||||
*/
|
||||
public function redirectGuestsTo(callable|string $redirect)
|
||||
{
|
||||
return $this->redirectTo(guests: $redirect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure where users are redirected by the "guest" middleware.
|
||||
*
|
||||
* @param callable|string $redirect
|
||||
* @return $this
|
||||
*/
|
||||
public function redirectUsersTo(callable|string $redirect)
|
||||
{
|
||||
return $this->redirectTo(users: $redirect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure where users are redirected by the authentication and guest middleware.
|
||||
*
|
||||
* @param callable|string $guests
|
||||
* @param callable|string $users
|
||||
* @return $this
|
||||
*/
|
||||
public function redirectTo(callable|string|null $guests = null, callable|string|null $users = null)
|
||||
{
|
||||
$guests = is_string($guests) ? fn () => $guests : $guests;
|
||||
$users = is_string($users) ? fn () => $users : $users;
|
||||
|
||||
if ($guests) {
|
||||
Authenticate::redirectUsing($guests);
|
||||
AuthenticateSession::redirectUsing($guests);
|
||||
AuthenticationException::redirectUsing($guests);
|
||||
}
|
||||
|
||||
if ($users) {
|
||||
RedirectIfAuthenticated::redirectUsing($users);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the cookie encryption middleware.
|
||||
*
|
||||
* @param array<int, string> $except
|
||||
* @return $this
|
||||
*/
|
||||
public function encryptCookies(array $except = [])
|
||||
{
|
||||
EncryptCookies::except($except);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the CSRF token validation middleware.
|
||||
*
|
||||
* @param array $except
|
||||
* @return $this
|
||||
*/
|
||||
public function validateCsrfTokens(array $except = [])
|
||||
{
|
||||
ValidateCsrfToken::except($except);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the URL signature validation middleware.
|
||||
*
|
||||
* @param array $except
|
||||
* @return $this
|
||||
*/
|
||||
public function validateSignatures(array $except = [])
|
||||
{
|
||||
ValidateSignature::except($except);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the empty string conversion middleware.
|
||||
*
|
||||
* @param array<int, (\Closure(\Illuminate\Http\Request): bool)> $except
|
||||
* @return $this
|
||||
*/
|
||||
public function convertEmptyStringsToNull(array $except = [])
|
||||
{
|
||||
collect($except)->each(fn (Closure $callback) => ConvertEmptyStringsToNull::skipWhen($callback));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the string trimming middleware.
|
||||
*
|
||||
* @param array<int, (\Closure(\Illuminate\Http\Request): bool)|string> $except
|
||||
* @return $this
|
||||
*/
|
||||
public function trimStrings(array $except = [])
|
||||
{
|
||||
[$skipWhen, $except] = collect($except)->partition(fn ($value) => $value instanceof Closure);
|
||||
|
||||
$skipWhen->each(fn (Closure $callback) => TrimStrings::skipWhen($callback));
|
||||
|
||||
TrimStrings::except($except->all());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the trusted host middleware should be enabled.
|
||||
*
|
||||
* @param array<int, string>|(callable(): array<int, string>)|null $at
|
||||
* @param bool $subdomains
|
||||
* @return $this
|
||||
*/
|
||||
public function trustHosts(array|callable|null $at = null, bool $subdomains = true)
|
||||
{
|
||||
$this->trustHosts = true;
|
||||
|
||||
if (! is_null($at)) {
|
||||
TrustHosts::at($at, $subdomains);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the trusted proxies for the application.
|
||||
*
|
||||
* @param array<int, string>|string|null $at
|
||||
* @param int|null $headers
|
||||
* @return $this
|
||||
*/
|
||||
public function trustProxies(array|string|null $at = null, ?int $headers = null)
|
||||
{
|
||||
if (! is_null($at)) {
|
||||
TrustProxies::at($at);
|
||||
}
|
||||
|
||||
if (! is_null($headers)) {
|
||||
TrustProxies::withHeaders($headers);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the middleware that prevents requests during maintenance mode.
|
||||
*
|
||||
* @param array<int, string> $except
|
||||
* @return $this
|
||||
*/
|
||||
public function preventRequestsDuringMaintenance(array $except = [])
|
||||
{
|
||||
PreventRequestsDuringMaintenance::except($except);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that Sanctum's frontend state middleware should be enabled.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function statefulApi()
|
||||
{
|
||||
$this->statefulApi = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the API middleware group's throttling middleware should be enabled.
|
||||
*
|
||||
* @param string $limiter
|
||||
* @param bool $redis
|
||||
* @return $this
|
||||
*/
|
||||
public function throttleApi($limiter = 'api', $redis = false)
|
||||
{
|
||||
$this->apiLimiter = $limiter;
|
||||
|
||||
if ($redis) {
|
||||
$this->throttleWithRedis();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that Laravel's throttling middleware should use Redis.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function throttleWithRedis()
|
||||
{
|
||||
$this->throttleWithRedis = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that sessions should be authenticated for the "web" middleware group.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function authenticateSessions()
|
||||
{
|
||||
$this->authenticatedSessions = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Folio / page middleware for the application.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPageMiddleware()
|
||||
{
|
||||
return $this->pageMiddleware;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the middleware aliases.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMiddlewareAliases()
|
||||
{
|
||||
return array_merge($this->defaultAliases(), $this->customAliases);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default middleware aliases.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function defaultAliases()
|
||||
{
|
||||
$aliases = [
|
||||
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
|
||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||
'guest' => \Illuminate\Auth\Middleware\RedirectIfAuthenticated::class,
|
||||
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
|
||||
'precognitive' => \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
|
||||
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
|
||||
'throttle' => $this->throttleWithRedis
|
||||
? \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class
|
||||
: \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||
];
|
||||
|
||||
if (class_exists(\Spark\Http\Middleware\VerifyBillableIsSubscribed::class)) {
|
||||
$aliases['subscribed'] = \Spark\Http\Middleware\VerifyBillableIsSubscribed::class;
|
||||
}
|
||||
|
||||
return $aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the middleware priority for the application.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMiddlewarePriority()
|
||||
{
|
||||
return $this->priority;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Composer;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'about')]
|
||||
class AboutCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command signature.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'about {--only= : The section to display}
|
||||
{--json : Output the information as JSON}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Display basic information about your application';
|
||||
|
||||
/**
|
||||
* The Composer instance.
|
||||
*
|
||||
* @var \Illuminate\Support\Composer
|
||||
*/
|
||||
protected $composer;
|
||||
|
||||
/**
|
||||
* The data to display.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $data = [];
|
||||
|
||||
/**
|
||||
* The registered callables that add custom data to the command output.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $customDataResolvers = [];
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @param \Illuminate\Support\Composer $composer
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Composer $composer)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->composer = $composer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->gatherApplicationInformation();
|
||||
|
||||
collect(static::$data)
|
||||
->map(fn ($items) => collect($items)
|
||||
->map(function ($value) {
|
||||
if (is_array($value)) {
|
||||
return [$value];
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$value = $this->laravel->make($value);
|
||||
}
|
||||
|
||||
return collect($this->laravel->call($value))
|
||||
->map(fn ($value, $key) => [$key, $value])
|
||||
->values()
|
||||
->all();
|
||||
})->flatten(1)
|
||||
)
|
||||
->sortBy(function ($data, $key) {
|
||||
$index = array_search($key, ['Environment', 'Cache', 'Drivers']);
|
||||
|
||||
return $index === false ? 99 : $index;
|
||||
})
|
||||
->filter(function ($data, $key) {
|
||||
return $this->option('only') ? in_array($this->toSearchKeyword($key), $this->sections()) : true;
|
||||
})
|
||||
->pipe(fn ($data) => $this->display($data));
|
||||
|
||||
$this->newLine();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the application information.
|
||||
*
|
||||
* @param \Illuminate\Support\Collection $data
|
||||
* @return void
|
||||
*/
|
||||
protected function display($data)
|
||||
{
|
||||
$this->option('json') ? $this->displayJson($data) : $this->displayDetail($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the application information as a detail view.
|
||||
*
|
||||
* @param \Illuminate\Support\Collection $data
|
||||
* @return void
|
||||
*/
|
||||
protected function displayDetail($data)
|
||||
{
|
||||
$data->each(function ($data, $section) {
|
||||
$this->newLine();
|
||||
|
||||
$this->components->twoColumnDetail(' <fg=green;options=bold>'.$section.'</>');
|
||||
|
||||
$data->pipe(fn ($data) => $section !== 'Environment' ? $data->sort() : $data)->each(function ($detail) {
|
||||
[$label, $value] = $detail;
|
||||
|
||||
$this->components->twoColumnDetail($label, value($value, false));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the application information as JSON.
|
||||
*
|
||||
* @param \Illuminate\Support\Collection $data
|
||||
* @return void
|
||||
*/
|
||||
protected function displayJson($data)
|
||||
{
|
||||
$output = $data->flatMap(function ($data, $section) {
|
||||
return [
|
||||
(string) Str::of($section)->snake() => $data->mapWithKeys(fn ($item, $key) => [
|
||||
$this->toSearchKeyword($item[0]) => value($item[1], true),
|
||||
]),
|
||||
];
|
||||
});
|
||||
|
||||
$this->output->writeln(strip_tags(json_encode($output)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gather information about the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function gatherApplicationInformation()
|
||||
{
|
||||
self::$data = [];
|
||||
|
||||
$formatEnabledStatus = fn ($value) => $value ? '<fg=yellow;options=bold>ENABLED</>' : 'OFF';
|
||||
$formatCachedStatus = fn ($value) => $value ? '<fg=green;options=bold>CACHED</>' : '<fg=yellow;options=bold>NOT CACHED</>';
|
||||
|
||||
static::addToSection('Environment', fn () => [
|
||||
'Application Name' => config('app.name'),
|
||||
'Laravel Version' => $this->laravel->version(),
|
||||
'PHP Version' => phpversion(),
|
||||
'Composer Version' => $this->composer->getVersion() ?? '<fg=yellow;options=bold>-</>',
|
||||
'Environment' => $this->laravel->environment(),
|
||||
'Debug Mode' => static::format(config('app.debug'), console: $formatEnabledStatus),
|
||||
'URL' => Str::of(config('app.url'))->replace(['http://', 'https://'], ''),
|
||||
'Maintenance Mode' => static::format($this->laravel->isDownForMaintenance(), console: $formatEnabledStatus),
|
||||
'Timezone' => config('app.timezone'),
|
||||
'Locale' => config('app.locale'),
|
||||
]);
|
||||
|
||||
static::addToSection('Cache', fn () => [
|
||||
'Config' => static::format($this->laravel->configurationIsCached(), console: $formatCachedStatus),
|
||||
'Events' => static::format($this->laravel->eventsAreCached(), console: $formatCachedStatus),
|
||||
'Routes' => static::format($this->laravel->routesAreCached(), console: $formatCachedStatus),
|
||||
'Views' => static::format($this->hasPhpFiles($this->laravel->storagePath('framework/views')), console: $formatCachedStatus),
|
||||
]);
|
||||
|
||||
static::addToSection('Drivers', fn () => array_filter([
|
||||
'Broadcasting' => config('broadcasting.default'),
|
||||
'Cache' => config('cache.default'),
|
||||
'Database' => config('database.default'),
|
||||
'Logs' => function ($json) {
|
||||
$logChannel = config('logging.default');
|
||||
|
||||
if (config('logging.channels.'.$logChannel.'.driver') === 'stack') {
|
||||
$secondary = collect(config('logging.channels.'.$logChannel.'.channels'));
|
||||
|
||||
return value(static::format(
|
||||
value: $logChannel,
|
||||
console: fn ($value) => '<fg=yellow;options=bold>'.$value.'</> <fg=gray;options=bold>/</> '.$secondary->implode(', '),
|
||||
json: fn () => $secondary->all(),
|
||||
), $json);
|
||||
} else {
|
||||
$logs = $logChannel;
|
||||
}
|
||||
|
||||
return $logs;
|
||||
},
|
||||
'Mail' => config('mail.default'),
|
||||
'Octane' => config('octane.server'),
|
||||
'Queue' => config('queue.default'),
|
||||
'Scout' => config('scout.driver'),
|
||||
'Session' => config('session.driver'),
|
||||
]));
|
||||
|
||||
collect(static::$customDataResolvers)->each->__invoke();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given directory has PHP files.
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasPhpFiles(string $path): bool
|
||||
{
|
||||
return count(glob($path.'/*.php')) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional data to the output of the "about" command.
|
||||
*
|
||||
* @param string $section
|
||||
* @param callable|string|array $data
|
||||
* @param string|null $value
|
||||
* @return void
|
||||
*/
|
||||
public static function add(string $section, $data, ?string $value = null)
|
||||
{
|
||||
static::$customDataResolvers[] = fn () => static::addToSection($section, $data, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional data to the output of the "about" command.
|
||||
*
|
||||
* @param string $section
|
||||
* @param callable|string|array $data
|
||||
* @param string|null $value
|
||||
* @return void
|
||||
*/
|
||||
protected static function addToSection(string $section, $data, ?string $value = null)
|
||||
{
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $key => $value) {
|
||||
self::$data[$section][] = [$key, $value];
|
||||
}
|
||||
} elseif (is_callable($data) || ($value === null && class_exists($data))) {
|
||||
self::$data[$section][] = $data;
|
||||
} else {
|
||||
self::$data[$section][] = [$data, $value];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sections provided to the command.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function sections()
|
||||
{
|
||||
return collect(explode(',', $this->option('only') ?? ''))
|
||||
->filter()
|
||||
->map(fn ($only) => $this->toSearchKeyword($only))
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Materialize a function that formats a given value for CLI or JSON output.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param (\Closure(mixed):(mixed))|null $console
|
||||
* @param (\Closure(mixed):(mixed))|null $json
|
||||
* @return \Closure(bool):mixed
|
||||
*/
|
||||
public static function format($value, ?Closure $console = null, ?Closure $json = null)
|
||||
{
|
||||
return function ($isJson) use ($value, $console, $json) {
|
||||
if ($isJson === true && $json instanceof Closure) {
|
||||
return value($json, $value);
|
||||
} elseif ($isJson === false && $console instanceof Closure) {
|
||||
return value($console, $value);
|
||||
}
|
||||
|
||||
return value($value);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the given string for searching.
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
protected function toSearchKeyword(string $value)
|
||||
{
|
||||
return (string) Str::of($value)->lower()->snake();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the registered about data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function flushState()
|
||||
{
|
||||
static::$data = [];
|
||||
|
||||
static::$customDataResolvers = [];
|
||||
}
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Support\Facades\Process;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Process\PhpExecutableFinder;
|
||||
|
||||
#[AsCommand(name: 'install:api')]
|
||||
class ApiInstallCommand extends Command
|
||||
{
|
||||
use InteractsWithComposerPackages;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'install:api
|
||||
{--composer=global : Absolute path to the Composer binary which should be used to install packages}
|
||||
{--force : Overwrite any existing API routes file}
|
||||
{--passport : Install Laravel Passport instead of Laravel Sanctum}
|
||||
{--without-migration-prompt : Do not prompt to run pending migrations}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create an API routes file and install Laravel Sanctum or Laravel Passport';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if ($this->option('passport')) {
|
||||
$this->installPassport();
|
||||
} else {
|
||||
$this->installSanctum();
|
||||
}
|
||||
|
||||
if (file_exists($apiRoutesPath = $this->laravel->basePath('routes/api.php')) &&
|
||||
! $this->option('force')) {
|
||||
$this->components->error('API routes file already exists.');
|
||||
} else {
|
||||
$this->components->info('Published API routes file.');
|
||||
|
||||
copy(__DIR__.'/stubs/api-routes.stub', $apiRoutesPath);
|
||||
|
||||
if ($this->option('passport')) {
|
||||
(new Filesystem)->replaceInFile(
|
||||
'auth:sanctum',
|
||||
'auth:api',
|
||||
$apiRoutesPath,
|
||||
);
|
||||
}
|
||||
|
||||
$this->uncommentApiRoutesFile();
|
||||
}
|
||||
|
||||
if ($this->option('passport')) {
|
||||
Process::run(array_filter([
|
||||
(new PhpExecutableFinder())->find(false) ?: 'php',
|
||||
defined('ARTISAN_BINARY') ? ARTISAN_BINARY : 'artisan',
|
||||
'passport:install',
|
||||
$this->confirm('Would you like to use UUIDs for all client IDs?') ? '--uuids' : null,
|
||||
]));
|
||||
|
||||
$this->components->info('API scaffolding installed. Please add the [Laravel\Passport\HasApiTokens] trait to your User model.');
|
||||
} else {
|
||||
if (! $this->option('without-migration-prompt')) {
|
||||
if ($this->confirm('One new database migration has been published. Would you like to run all pending database migrations?', true)) {
|
||||
$this->call('migrate');
|
||||
}
|
||||
}
|
||||
|
||||
$this->components->info('API scaffolding installed. Please add the [Laravel\Sanctum\HasApiTokens] trait to your User model.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uncomment the API routes file in the application bootstrap file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function uncommentApiRoutesFile()
|
||||
{
|
||||
$appBootstrapPath = $this->laravel->bootstrapPath('app.php');
|
||||
|
||||
$content = file_get_contents($appBootstrapPath);
|
||||
|
||||
if (str_contains($content, '// api: ')) {
|
||||
(new Filesystem)->replaceInFile(
|
||||
'// api: ',
|
||||
'api: ',
|
||||
$appBootstrapPath,
|
||||
);
|
||||
} elseif (str_contains($content, 'web: __DIR__.\'/../routes/web.php\',')) {
|
||||
(new Filesystem)->replaceInFile(
|
||||
'web: __DIR__.\'/../routes/web.php\',',
|
||||
'web: __DIR__.\'/../routes/web.php\','.PHP_EOL.' api: __DIR__.\'/../routes/api.php\',',
|
||||
$appBootstrapPath,
|
||||
);
|
||||
} else {
|
||||
$this->components->warn('Unable to automatically add API route definition to bootstrap file. API route file should be registered manually.');
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install Laravel Sanctum into the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function installSanctum()
|
||||
{
|
||||
$this->requireComposerPackages($this->option('composer'), [
|
||||
'laravel/sanctum:^4.0',
|
||||
]);
|
||||
|
||||
$migrationPublished = collect(scandir($this->laravel->databasePath('migrations')))->contains(function ($migration) {
|
||||
return preg_match('/\d{4}_\d{2}_\d{2}_\d{6}_create_personal_access_tokens_table.php/', $migration);
|
||||
});
|
||||
|
||||
if (! $migrationPublished) {
|
||||
Process::run([
|
||||
(new PhpExecutableFinder())->find(false) ?: 'php',
|
||||
defined('ARTISAN_BINARY') ? ARTISAN_BINARY : 'artisan',
|
||||
'vendor:publish',
|
||||
'--provider',
|
||||
'Laravel\\Sanctum\\SanctumServiceProvider',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install Laravel Passport into the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function installPassport()
|
||||
{
|
||||
$this->requireComposerPackages($this->option('composer'), [
|
||||
'laravel/passport:^12.0',
|
||||
]);
|
||||
}
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Composer\InstalledVersions;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Support\Facades\Process;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Process\PhpExecutableFinder;
|
||||
|
||||
use function Laravel\Prompts\confirm;
|
||||
|
||||
#[AsCommand(name: 'install:broadcasting')]
|
||||
class BroadcastingInstallCommand extends Command
|
||||
{
|
||||
use InteractsWithComposerPackages;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'install:broadcasting
|
||||
{--composer=global : Absolute path to the Composer binary which should be used to install packages}
|
||||
{--force : Overwrite any existing broadcasting routes file}
|
||||
{--without-reverb : Do not prompt to install Laravel Reverb}
|
||||
{--without-node : Do not prompt to install Node dependencies}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a broadcasting channel routes file';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->call('config:publish', ['name' => 'broadcasting']);
|
||||
|
||||
// Install channel routes file...
|
||||
if (! file_exists($broadcastingRoutesPath = $this->laravel->basePath('routes/channels.php')) || $this->option('force')) {
|
||||
$this->components->info("Published 'channels' route file.");
|
||||
|
||||
copy(__DIR__.'/stubs/broadcasting-routes.stub', $broadcastingRoutesPath);
|
||||
}
|
||||
|
||||
$this->uncommentChannelsRoutesFile();
|
||||
$this->enableBroadcastServiceProvider();
|
||||
|
||||
// Install bootstrapping...
|
||||
if (! file_exists($echoScriptPath = $this->laravel->resourcePath('js/echo.js'))) {
|
||||
if (! is_dir($directory = $this->laravel->resourcePath('js'))) {
|
||||
mkdir($directory, 0755, true);
|
||||
}
|
||||
|
||||
copy(__DIR__.'/stubs/echo-js.stub', $echoScriptPath);
|
||||
}
|
||||
|
||||
if (file_exists($bootstrapScriptPath = $this->laravel->resourcePath('js/bootstrap.js'))) {
|
||||
$bootstrapScript = file_get_contents(
|
||||
$bootstrapScriptPath
|
||||
);
|
||||
|
||||
if (! str_contains($bootstrapScript, './echo')) {
|
||||
file_put_contents(
|
||||
$bootstrapScriptPath,
|
||||
trim($bootstrapScript.PHP_EOL.file_get_contents(__DIR__.'/stubs/echo-bootstrap-js.stub')).PHP_EOL,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->installReverb();
|
||||
|
||||
$this->installNodeDependencies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uncomment the "channels" routes file in the application bootstrap file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function uncommentChannelsRoutesFile()
|
||||
{
|
||||
$appBootstrapPath = $this->laravel->bootstrapPath('app.php');
|
||||
|
||||
$content = file_get_contents($appBootstrapPath);
|
||||
|
||||
if (str_contains($content, '// channels: ')) {
|
||||
(new Filesystem)->replaceInFile(
|
||||
'// channels: ',
|
||||
'channels: ',
|
||||
$appBootstrapPath,
|
||||
);
|
||||
} elseif (str_contains($content, 'channels: ')) {
|
||||
return;
|
||||
} elseif (str_contains($content, 'commands: __DIR__.\'/../routes/console.php\',')) {
|
||||
(new Filesystem)->replaceInFile(
|
||||
'commands: __DIR__.\'/../routes/console.php\',',
|
||||
'commands: __DIR__.\'/../routes/console.php\','.PHP_EOL.' channels: __DIR__.\'/../routes/channels.php\',',
|
||||
$appBootstrapPath,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uncomment the "BroadcastServiceProvider" in the application configuration.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function enableBroadcastServiceProvider()
|
||||
{
|
||||
$filesystem = new Filesystem;
|
||||
|
||||
if (! $filesystem->exists(app()->configPath('app.php')) ||
|
||||
! $filesystem->exists('app/Providers/BroadcastServiceProvider.php')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$config = $filesystem->get(app()->configPath('app.php'));
|
||||
|
||||
if (str_contains($config, '// App\Providers\BroadcastServiceProvider::class')) {
|
||||
$filesystem->replaceInFile(
|
||||
'// App\Providers\BroadcastServiceProvider::class',
|
||||
'App\Providers\BroadcastServiceProvider::class',
|
||||
app()->configPath('app.php'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install Laravel Reverb into the application if desired.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function installReverb()
|
||||
{
|
||||
if ($this->option('without-reverb') || InstalledVersions::isInstalled('laravel/reverb')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$install = confirm('Would you like to install Laravel Reverb?', default: true);
|
||||
|
||||
if (! $install) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->requireComposerPackages($this->option('composer'), [
|
||||
'laravel/reverb:^1.0',
|
||||
]);
|
||||
|
||||
$php = (new PhpExecutableFinder())->find(false) ?: 'php';
|
||||
|
||||
Process::run([
|
||||
$php,
|
||||
defined('ARTISAN_BINARY') ? ARTISAN_BINARY : 'artisan',
|
||||
'reverb:install',
|
||||
]);
|
||||
|
||||
$this->components->info('Reverb installed successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Install and build Node dependencies.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function installNodeDependencies()
|
||||
{
|
||||
if ($this->option('without-node') || ! confirm('Would you like to install and build the Node dependencies required for broadcasting?', default: true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->components->info('Installing and building Node dependencies.');
|
||||
|
||||
if (file_exists(base_path('pnpm-lock.yaml'))) {
|
||||
$commands = [
|
||||
'pnpm add --save-dev laravel-echo pusher-js',
|
||||
'pnpm run build',
|
||||
];
|
||||
} elseif (file_exists(base_path('yarn.lock'))) {
|
||||
$commands = [
|
||||
'yarn add --dev laravel-echo pusher-js',
|
||||
'yarn run build',
|
||||
];
|
||||
} elseif (file_exists(base_path('bun.lockb'))) {
|
||||
$commands = [
|
||||
'bun add --dev laravel-echo pusher-js',
|
||||
'bun run build',
|
||||
];
|
||||
} else {
|
||||
$commands = [
|
||||
'npm install --save-dev laravel-echo pusher-js',
|
||||
'npm run build',
|
||||
];
|
||||
}
|
||||
|
||||
$command = Process::command(implode(' && ', $commands))
|
||||
->path(base_path());
|
||||
|
||||
if (! windows_os()) {
|
||||
$command->tty(true);
|
||||
}
|
||||
|
||||
if ($command->run()->failed()) {
|
||||
$this->components->warn("Node dependency installation failed. Please run the following commands manually: \n\n".implode(' && ', $commands));
|
||||
} else {
|
||||
$this->components->info('Node dependencies installed successfully.');
|
||||
}
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand(name: 'make:cast')]
|
||||
class CastMakeCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:cast';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new custom Eloquent cast class';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Cast';
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
return $this->option('inbound')
|
||||
? $this->resolveStubPath('/stubs/cast.inbound.stub')
|
||||
: $this->resolveStubPath('/stubs/cast.stub');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return $rootNamespace.'\Casts';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the cast already exists'],
|
||||
['inbound', null, InputOption::VALUE_NONE, 'Generate an inbound cast class'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Broadcasting\Broadcaster;
|
||||
use Illuminate\Support\Collection;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Terminal;
|
||||
|
||||
#[AsCommand(name: 'channel:list')]
|
||||
class ChannelListCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'channel:list';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'List all registered private broadcast channels';
|
||||
|
||||
/**
|
||||
* The terminal width resolver callback.
|
||||
*
|
||||
* @var \Closure|null
|
||||
*/
|
||||
protected static $terminalWidthResolver;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Broadcasting\Broadcaster $broadcaster
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Broadcaster $broadcaster)
|
||||
{
|
||||
$channels = $broadcaster->getChannels();
|
||||
|
||||
if (! $this->laravel->providerIsLoaded('App\Providers\BroadcastServiceProvider') &&
|
||||
file_exists($this->laravel->path('Providers/BroadcastServiceProvider.php'))) {
|
||||
$this->components->warn('The [App\Providers\BroadcastServiceProvider] has not been loaded. Your private channels may not be loaded.');
|
||||
}
|
||||
|
||||
if (! $channels->count()) {
|
||||
return $this->components->error("Your application doesn't have any private broadcasting channels.");
|
||||
}
|
||||
|
||||
$this->displayChannels($channels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the channel information on the console.
|
||||
*
|
||||
* @param Collection $channels
|
||||
* @return void
|
||||
*/
|
||||
protected function displayChannels($channels)
|
||||
{
|
||||
$this->output->writeln($this->forCli($channels));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given channels to regular CLI output.
|
||||
*
|
||||
* @param \Illuminate\Support\Collection $channels
|
||||
* @return array
|
||||
*/
|
||||
protected function forCli($channels)
|
||||
{
|
||||
$maxChannelName = $channels->keys()->max(function ($channelName) {
|
||||
return mb_strlen($channelName);
|
||||
});
|
||||
|
||||
$terminalWidth = $this->getTerminalWidth();
|
||||
|
||||
$channelCount = $this->determineChannelCountOutput($channels, $terminalWidth);
|
||||
|
||||
return $channels->map(function ($channel, $channelName) use ($maxChannelName, $terminalWidth) {
|
||||
$resolver = $channel instanceof Closure ? 'Closure' : $channel;
|
||||
|
||||
$spaces = str_repeat(' ', max($maxChannelName + 6 - mb_strlen($channelName), 0));
|
||||
|
||||
$dots = str_repeat('.', max(
|
||||
$terminalWidth - mb_strlen($channelName.$spaces.$resolver) - 6, 0
|
||||
));
|
||||
|
||||
$dots = empty($dots) ? $dots : " $dots";
|
||||
|
||||
return sprintf(
|
||||
' <fg=blue;options=bold>%s</> %s<fg=white>%s</><fg=#6C7280>%s</>',
|
||||
$channelName,
|
||||
$spaces,
|
||||
$resolver,
|
||||
$dots,
|
||||
);
|
||||
})
|
||||
->filter()
|
||||
->sort()
|
||||
->prepend('')
|
||||
->push('')->push($channelCount)->push('')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine and return the output for displaying the number of registered channels in the CLI output.
|
||||
*
|
||||
* @param \Illuminate\Support\Collection $channels
|
||||
* @param int $terminalWidth
|
||||
* @return string
|
||||
*/
|
||||
protected function determineChannelCountOutput($channels, $terminalWidth)
|
||||
{
|
||||
$channelCountText = 'Showing ['.$channels->count().'] private channels';
|
||||
|
||||
$offset = $terminalWidth - mb_strlen($channelCountText) - 2;
|
||||
|
||||
$spaces = str_repeat(' ', $offset);
|
||||
|
||||
return $spaces.'<fg=blue;options=bold>Showing ['.$channels->count().'] private channels</>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the terminal width.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getTerminalWidth()
|
||||
{
|
||||
return is_null(static::$terminalWidthResolver)
|
||||
? (new Terminal)->getWidth()
|
||||
: call_user_func(static::$terminalWidthResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback that should be used when resolving the terminal width.
|
||||
*
|
||||
* @param \Closure|null $resolver
|
||||
* @return void
|
||||
*/
|
||||
public static function resolveTerminalWidthUsing($resolver)
|
||||
{
|
||||
static::$terminalWidthResolver = $resolver;
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand(name: 'make:channel')]
|
||||
class ChannelMakeCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:channel';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new channel class';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Channel';
|
||||
|
||||
/**
|
||||
* Build the class with the given name.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
protected function buildClass($name)
|
||||
{
|
||||
return str_replace(
|
||||
['DummyUser', '{{ userModel }}'],
|
||||
class_basename($this->userProviderModel()),
|
||||
parent::buildClass($name)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
return __DIR__.'/stubs/channel.stub';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return $rootNamespace.'\Broadcasting';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the channel already exists'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand(name: 'make:class')]
|
||||
class ClassMakeCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:class';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new class';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Class';
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
return $this->option('invokable')
|
||||
? $this->resolveStubPath('/stubs/class.invokable.stub')
|
||||
: $this->resolveStubPath('/stubs/class.stub');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['invokable', 'i', InputOption::VALUE_NONE, 'Generate a single method, invokable class'],
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the class already exists'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'clear-compiled')]
|
||||
class ClearCompiledCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'clear-compiled';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Remove the compiled class file';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (is_file($servicesPath = $this->laravel->getCachedServicesPath())) {
|
||||
@unlink($servicesPath);
|
||||
}
|
||||
|
||||
if (is_file($packagesPath = $this->laravel->getCachedPackagesPath())) {
|
||||
@unlink($packagesPath);
|
||||
}
|
||||
|
||||
$this->components->info('Compiled services and packages files removed successfully.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Foundation\Concerns\ResolvesDumpSource;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\VarDumper\Caster\ReflectionCaster;
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||
use Symfony\Component\VarDumper\Dumper\CliDumper as BaseCliDumper;
|
||||
use Symfony\Component\VarDumper\VarDumper;
|
||||
|
||||
class CliDumper extends BaseCliDumper
|
||||
{
|
||||
use ResolvesDumpSource;
|
||||
|
||||
/**
|
||||
* The base path of the application.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $basePath;
|
||||
|
||||
/**
|
||||
* The output instance.
|
||||
*
|
||||
* @var \Symfony\Component\Console\Output\OutputInterface
|
||||
*/
|
||||
protected $output;
|
||||
|
||||
/**
|
||||
* The compiled view path for the application.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $compiledViewPath;
|
||||
|
||||
/**
|
||||
* If the dumper is currently dumping.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $dumping = false;
|
||||
|
||||
/**
|
||||
* Create a new CLI dumper instance.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @param string $basePath
|
||||
* @param string $compiledViewPath
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($output, $basePath, $compiledViewPath)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->basePath = $basePath;
|
||||
$this->output = $output;
|
||||
$this->compiledViewPath = $compiledViewPath;
|
||||
|
||||
$this->setColors($this->supportsColors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new CLI dumper instance and register it as the default dumper.
|
||||
*
|
||||
* @param string $basePath
|
||||
* @param string $compiledViewPath
|
||||
* @return void
|
||||
*/
|
||||
public static function register($basePath, $compiledViewPath)
|
||||
{
|
||||
$cloner = tap(new VarCloner())->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO);
|
||||
|
||||
$dumper = new static(new ConsoleOutput(), $basePath, $compiledViewPath);
|
||||
|
||||
VarDumper::setHandler(fn ($value) => $dumper->dumpWithSource($cloner->cloneVar($value)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump a variable with its source file / line.
|
||||
*
|
||||
* @param \Symfony\Component\VarDumper\Cloner\Data $data
|
||||
* @return void
|
||||
*/
|
||||
public function dumpWithSource(Data $data)
|
||||
{
|
||||
if ($this->dumping) {
|
||||
$this->dump($data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->dumping = true;
|
||||
|
||||
$output = (string) $this->dump($data, true);
|
||||
$lines = explode("\n", $output);
|
||||
|
||||
$lines[array_key_last($lines) - 1] .= $this->getDumpSourceContent();
|
||||
|
||||
$this->output->write(implode("\n", $lines));
|
||||
|
||||
$this->dumping = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dump's source console content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDumpSourceContent()
|
||||
{
|
||||
if (is_null($dumpSource = $this->resolveDumpSource())) {
|
||||
return '';
|
||||
}
|
||||
|
||||
[$file, $relativeFile, $line] = $dumpSource;
|
||||
|
||||
$href = $this->resolveSourceHref($file, $line);
|
||||
|
||||
return sprintf(
|
||||
' <fg=gray>// <fg=gray%s>%s%s</></>',
|
||||
is_null($href) ? '' : ";href=$href",
|
||||
$relativeFile,
|
||||
is_null($line) ? '' : ":$line"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function supportsColors(): bool
|
||||
{
|
||||
return $this->output->isDecorated();
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Console\ManuallyFailedException;
|
||||
use Illuminate\Support\Facades\Schedule;
|
||||
use Illuminate\Support\Traits\ForwardsCalls;
|
||||
use ReflectionFunction;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @mixin \Illuminate\Console\Scheduling\Event
|
||||
*/
|
||||
class ClosureCommand extends Command
|
||||
{
|
||||
use ForwardsCalls;
|
||||
|
||||
/**
|
||||
* The command callback.
|
||||
*
|
||||
* @var \Closure
|
||||
*/
|
||||
protected $callback;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @param string $signature
|
||||
* @param \Closure $callback
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($signature, Closure $callback)
|
||||
{
|
||||
$this->callback = $callback;
|
||||
$this->signature = $signature;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$inputs = array_merge($input->getArguments(), $input->getOptions());
|
||||
|
||||
$parameters = [];
|
||||
|
||||
foreach ((new ReflectionFunction($this->callback))->getParameters() as $parameter) {
|
||||
if (isset($inputs[$parameter->getName()])) {
|
||||
$parameters[$parameter->getName()] = $inputs[$parameter->getName()];
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return (int) $this->laravel->call(
|
||||
$this->callback->bindTo($this, $this), $parameters
|
||||
);
|
||||
} catch (ManuallyFailedException $e) {
|
||||
$this->components->error($e->getMessage());
|
||||
|
||||
return static::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the description for the command.
|
||||
*
|
||||
* @param string $description
|
||||
* @return $this
|
||||
*/
|
||||
public function purpose($description)
|
||||
{
|
||||
return $this->describe($description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the description for the command.
|
||||
*
|
||||
* @param string $description
|
||||
* @return $this
|
||||
*/
|
||||
public function describe($description)
|
||||
{
|
||||
$this->setDescription($description);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new scheduled event for the command.
|
||||
*
|
||||
* @param array $parameters
|
||||
* @return \Illuminate\Console\Scheduling\Event
|
||||
*/
|
||||
public function schedule($parameters = [])
|
||||
{
|
||||
return Schedule::command($this->name, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically proxy calls to a new scheduled event.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function __call($method, $parameters)
|
||||
{
|
||||
return $this->forwardCallTo($this->schedule(), $method, $parameters);
|
||||
}
|
||||
}
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Concerns\CreatesMatchingTest;
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Illuminate\Foundation\Inspiring;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand(name: 'make:component')]
|
||||
class ComponentMakeCommand extends GeneratorCommand
|
||||
{
|
||||
use CreatesMatchingTest;
|
||||
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:component';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new view component class';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Component';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if ($this->option('view')) {
|
||||
return $this->writeView();
|
||||
}
|
||||
|
||||
if (parent::handle() === false && ! $this->option('force')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->option('inline')) {
|
||||
$this->writeView();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the view for the component.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function writeView()
|
||||
{
|
||||
$path = $this->viewPath(
|
||||
str_replace('.', '/', 'components.'.$this->getView()).'.blade.php'
|
||||
);
|
||||
|
||||
if (! $this->files->isDirectory(dirname($path))) {
|
||||
$this->files->makeDirectory(dirname($path), 0777, true, true);
|
||||
}
|
||||
|
||||
if ($this->files->exists($path) && ! $this->option('force')) {
|
||||
$this->components->error('View already exists.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
file_put_contents(
|
||||
$path,
|
||||
'<div>
|
||||
<!-- '.Inspiring::quotes()->random().' -->
|
||||
</div>'
|
||||
);
|
||||
|
||||
$this->components->info(sprintf('%s [%s] created successfully.', 'View', $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the class with the given name.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
protected function buildClass($name)
|
||||
{
|
||||
if ($this->option('inline')) {
|
||||
return str_replace(
|
||||
['DummyView', '{{ view }}'],
|
||||
"<<<'blade'\n<div>\n <!-- ".Inspiring::quotes()->random()." -->\n</div>\nblade",
|
||||
parent::buildClass($name)
|
||||
);
|
||||
}
|
||||
|
||||
return str_replace(
|
||||
['DummyView', '{{ view }}'],
|
||||
'view(\'components.'.$this->getView().'\')',
|
||||
parent::buildClass($name)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view name relative to the components directory.
|
||||
*
|
||||
* @return string view
|
||||
*/
|
||||
protected function getView()
|
||||
{
|
||||
$name = str_replace('\\', '/', $this->argument('name'));
|
||||
|
||||
return collect(explode('/', $name))
|
||||
->map(function ($part) {
|
||||
return Str::kebab($part);
|
||||
})
|
||||
->implode('.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
return $this->resolveStubPath('/stubs/view-component.stub');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return $rootNamespace.'\View\Components';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the component already exists'],
|
||||
['inline', null, InputOption::VALUE_NONE, 'Create a component that renders an inline view'],
|
||||
['view', null, InputOption::VALUE_NONE, 'Create an anonymous component with only a view'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Console\Kernel as ConsoleKernelContract;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use LogicException;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Throwable;
|
||||
|
||||
#[AsCommand(name: 'config:cache')]
|
||||
class ConfigCacheCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'config:cache';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a cache file for faster configuration loading';
|
||||
|
||||
/**
|
||||
* The filesystem instance.
|
||||
*
|
||||
* @var \Illuminate\Filesystem\Filesystem
|
||||
*/
|
||||
protected $files;
|
||||
|
||||
/**
|
||||
* Create a new config cache command instance.
|
||||
*
|
||||
* @param \Illuminate\Filesystem\Filesystem $files
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Filesystem $files)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->files = $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->callSilent('config:clear');
|
||||
|
||||
$config = $this->getFreshConfiguration();
|
||||
|
||||
$configPath = $this->laravel->getCachedConfigPath();
|
||||
|
||||
$this->files->put(
|
||||
$configPath, '<?php return '.var_export($config, true).';'.PHP_EOL
|
||||
);
|
||||
|
||||
try {
|
||||
require $configPath;
|
||||
} catch (Throwable $e) {
|
||||
$this->files->delete($configPath);
|
||||
|
||||
throw new LogicException('Your configuration files are not serializable.', 0, $e);
|
||||
}
|
||||
|
||||
$this->components->info('Configuration cached successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Boot a fresh copy of the application configuration.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getFreshConfiguration()
|
||||
{
|
||||
$app = require $this->laravel->bootstrapPath('app.php');
|
||||
|
||||
$app->useStoragePath($this->laravel->storagePath());
|
||||
|
||||
$app->make(ConsoleKernelContract::class)->bootstrap();
|
||||
|
||||
return $app['config']->all();
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'config:clear')]
|
||||
class ConfigClearCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'config:clear';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Remove the configuration cache file';
|
||||
|
||||
/**
|
||||
* The filesystem instance.
|
||||
*
|
||||
* @var \Illuminate\Filesystem\Filesystem
|
||||
*/
|
||||
protected $files;
|
||||
|
||||
/**
|
||||
* Create a new config clear command instance.
|
||||
*
|
||||
* @param \Illuminate\Filesystem\Filesystem $files
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Filesystem $files)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->files = $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->files->delete($this->laravel->getCachedConfigPath());
|
||||
|
||||
$this->components->info('Configuration cache cleared successfully.');
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
|
||||
use function Laravel\Prompts\select;
|
||||
|
||||
#[AsCommand(name: 'config:publish')]
|
||||
class ConfigPublishCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'config:publish
|
||||
{name? : The name of the configuration file to publish}
|
||||
{--all : Publish all configuration files}
|
||||
{--force : Overwrite any existing configuration files}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Publish configuration files to your application';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$config = $this->getBaseConfigurationFiles();
|
||||
|
||||
if (is_null($this->argument('name')) && $this->option('all')) {
|
||||
foreach ($config as $key => $file) {
|
||||
$this->publish($key, $file, $this->laravel->configPath().'/'.$key.'.php');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$name = (string) (is_null($this->argument('name')) ? select(
|
||||
label: 'Which configuration file would you like to publish?',
|
||||
options: collect($config)->map(function (string $path) {
|
||||
return basename($path, '.php');
|
||||
}),
|
||||
) : $this->argument('name'));
|
||||
|
||||
if (! is_null($name) && ! isset($config[$name])) {
|
||||
$this->components->error('Unrecognized configuration file.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->publish($name, $config[$name], $this->laravel->configPath().'/'.$name.'.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish the given file to the given destination.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $file
|
||||
* @param string $destination
|
||||
* @return void
|
||||
*/
|
||||
protected function publish(string $name, string $file, string $destination)
|
||||
{
|
||||
if (file_exists($destination) && ! $this->option('force')) {
|
||||
$this->components->error("The '{$name}' configuration file already exists.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
copy($file, $destination);
|
||||
|
||||
$this->components->info("Published '{$name}' configuration file.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array containing the base configuration files.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getBaseConfigurationFiles()
|
||||
{
|
||||
$config = [];
|
||||
|
||||
$shouldMergeConfiguration = $this->laravel->shouldMergeFrameworkConfiguration();
|
||||
|
||||
foreach (Finder::create()->files()->name('*.php')->in(__DIR__.'/../../../../config') as $file) {
|
||||
$name = basename($file->getRealPath(), '.php');
|
||||
|
||||
$config[$name] = ($shouldMergeConfiguration === true && file_exists($stubPath = (__DIR__.'/../../../../config-stubs/'.$name.'.php')))
|
||||
? $stubPath
|
||||
: $file->getRealPath();
|
||||
}
|
||||
|
||||
return collect($config)->sortKeys()->all();
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Arr;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'config:show')]
|
||||
class ConfigShowCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command signature.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'config:show {config : The configuration file or key to show}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Display all of the values for a given configuration file or key';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$config = $this->argument('config');
|
||||
|
||||
if (! config()->has($config)) {
|
||||
$this->components->error("Configuration file or key <comment>{$config}</comment> does not exist.");
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
$this->render($config);
|
||||
$this->newLine();
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the configuration values.
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
public function render($name)
|
||||
{
|
||||
$data = config($name);
|
||||
|
||||
if (! is_array($data)) {
|
||||
$this->title($name, $this->formatValue($data));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->title($name);
|
||||
|
||||
foreach (Arr::dot($data) as $key => $value) {
|
||||
$this->components->twoColumnDetail(
|
||||
$this->formatKey($key),
|
||||
$this->formatValue($value)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the title.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string|null $subtitle
|
||||
* @return void
|
||||
*/
|
||||
public function title($title, $subtitle = null)
|
||||
{
|
||||
$this->components->twoColumnDetail(
|
||||
"<fg=green;options=bold>{$title}</>",
|
||||
$subtitle,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the given configuration key.
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function formatKey($key)
|
||||
{
|
||||
return preg_replace_callback(
|
||||
'/(.*)\.(.*)$/', fn ($matches) => sprintf(
|
||||
'<fg=gray>%s ⇁</> %s',
|
||||
str_replace('.', ' ⇁ ', $matches[1]),
|
||||
$matches[2]
|
||||
), $key
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the given configuration value.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
protected function formatValue($value)
|
||||
{
|
||||
return match (true) {
|
||||
is_bool($value) => sprintf('<fg=#ef8414;options=bold>%s</>', $value ? 'true' : 'false'),
|
||||
is_null($value) => '<fg=#ef8414;options=bold>null</>',
|
||||
is_numeric($value) => "<fg=#ef8414;options=bold>{$value}</>",
|
||||
is_array($value) => '[]',
|
||||
is_object($value) => get_class($value),
|
||||
is_string($value) => $value,
|
||||
default => print_r($value, true),
|
||||
};
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Concerns\CreatesMatchingTest;
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand(name: 'make:command')]
|
||||
class ConsoleMakeCommand extends GeneratorCommand
|
||||
{
|
||||
use CreatesMatchingTest;
|
||||
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:command';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new Artisan command';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Console command';
|
||||
|
||||
/**
|
||||
* Replace the class name for the given stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
protected function replaceClass($stub, $name)
|
||||
{
|
||||
$stub = parent::replaceClass($stub, $name);
|
||||
|
||||
$command = $this->option('command') ?: 'app:'.Str::of($name)->classBasename()->kebab()->value();
|
||||
|
||||
return str_replace(['dummy:command', '{{ command }}'], $command, $stub);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
$relativePath = '/stubs/console.stub';
|
||||
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($relativePath, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$relativePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return $rootNamespace.'\Console\Commands';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getArguments()
|
||||
{
|
||||
return [
|
||||
['name', InputArgument::REQUIRED, 'The name of the command'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the console command already exists'],
|
||||
['command', null, InputOption::VALUE_OPTIONAL, 'The terminal command that will be used to invoke the class'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,529 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Carbon\CarbonInterval;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Cache\Repository as Cache;
|
||||
use Illuminate\Http\Client\Factory as Http;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Env;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Process\Exception\ProcessFailedException;
|
||||
use Symfony\Component\Process\ExecutableFinder;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Throwable;
|
||||
|
||||
use function Laravel\Prompts\suggest;
|
||||
|
||||
#[AsCommand(name: 'docs')]
|
||||
class DocsCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'docs {page? : The documentation page to open} {section? : The section of the page to open}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Access the Laravel documentation';
|
||||
|
||||
/**
|
||||
* The console command help text.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $help = 'If you would like to perform a content search against the documentation, you may call: <fg=green>php artisan docs -- </><fg=green;options=bold;>search query here</>';
|
||||
|
||||
/**
|
||||
* The HTTP client instance.
|
||||
*
|
||||
* @var \Illuminate\Http\Client\Factory
|
||||
*/
|
||||
protected $http;
|
||||
|
||||
/**
|
||||
* The cache repository implementation.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Cache\Repository
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* The custom URL opener.
|
||||
*
|
||||
* @var callable|null
|
||||
*/
|
||||
protected $urlOpener;
|
||||
|
||||
/**
|
||||
* The custom documentation version to open.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $version;
|
||||
|
||||
/**
|
||||
* The operating system family.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $systemOsFamily = PHP_OS_FAMILY;
|
||||
|
||||
/**
|
||||
* Configure the current command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
if ($this->isSearching()) {
|
||||
$this->ignoreValidationErrors();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @param \Illuminate\Http\Client\Factory $http
|
||||
* @param \Illuminate\Contracts\Cache\Repository $cache
|
||||
* @return int
|
||||
*/
|
||||
public function handle(Http $http, Cache $cache)
|
||||
{
|
||||
$this->http = $http;
|
||||
$this->cache = $cache;
|
||||
|
||||
try {
|
||||
$this->openUrl();
|
||||
} catch (ProcessFailedException $e) {
|
||||
if ($e->getProcess()->getExitCodeText() === 'Interrupt') {
|
||||
return $e->getProcess()->getExitCode();
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->refreshDocs();
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the documentation URL.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function openUrl()
|
||||
{
|
||||
with($this->url(), function ($url) {
|
||||
$this->components->info("Opening the docs to: <fg=yellow>{$url}</>");
|
||||
|
||||
$this->open($url);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL to the documentation page.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function url()
|
||||
{
|
||||
if ($this->isSearching()) {
|
||||
return "https://laravel.com/docs/{$this->version()}?".Arr::query([
|
||||
'q' => $this->searchQuery(),
|
||||
]);
|
||||
}
|
||||
|
||||
return with($this->page(), function ($page) {
|
||||
return trim("https://laravel.com/docs/{$this->version()}/{$page}#{$this->section($page)}", '#/');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The page the user is opening.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function page()
|
||||
{
|
||||
return with($this->resolvePage(), function ($page) {
|
||||
if ($page === null) {
|
||||
$this->components->warn('Unable to determine the page you are trying to visit.');
|
||||
|
||||
return '/';
|
||||
}
|
||||
|
||||
return $page;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the page to open.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function resolvePage()
|
||||
{
|
||||
if ($this->option('no-interaction') && $this->didNotRequestPage()) {
|
||||
return '/';
|
||||
}
|
||||
|
||||
return $this->didNotRequestPage()
|
||||
? $this->askForPage()
|
||||
: $this->guessPage($this->argument('page'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user requested a specific page when calling the command.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function didNotRequestPage()
|
||||
{
|
||||
return $this->argument('page') === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask the user which page they would like to open.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function askForPage()
|
||||
{
|
||||
return $this->askForPageViaCustomStrategy() ?? $this->askForPageViaAutocomplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask the user which page they would like to open via a custom strategy.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function askForPageViaCustomStrategy()
|
||||
{
|
||||
try {
|
||||
$strategy = require Env::get('ARTISAN_DOCS_ASK_STRATEGY');
|
||||
} catch (Throwable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! is_callable($strategy)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $strategy($this) ?? '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask the user which page they would like to open using autocomplete.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function askForPageViaAutocomplete()
|
||||
{
|
||||
$choice = suggest(
|
||||
label: 'Which page would you like to open?',
|
||||
options: fn ($value) => $this->pages()
|
||||
->mapWithKeys(fn ($option) => [
|
||||
Str::lower($option['title']) => $option['title'],
|
||||
])
|
||||
->filter(fn ($title) => str_contains(Str::lower($title), Str::lower($value)))
|
||||
->all(),
|
||||
placeholder: 'E.g. Collections'
|
||||
);
|
||||
|
||||
return $this->pages()->filter(
|
||||
fn ($page) => $page['title'] === $choice || Str::lower($page['title']) === $choice
|
||||
)->keys()->first() ?: $this->guessPage($choice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guess the page the user is attempting to open.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function guessPage($search)
|
||||
{
|
||||
return $this->pages()
|
||||
->filter(fn ($page) => str_starts_with(
|
||||
Str::slug($page['title'], ' '),
|
||||
Str::slug($search, ' ')
|
||||
))->keys()->first() ?? $this->pages()->map(fn ($page) => similar_text(
|
||||
Str::slug($page['title'], ' '),
|
||||
Str::slug($search, ' '),
|
||||
))
|
||||
->filter(fn ($score) => $score >= min(3, Str::length($search)))
|
||||
->sortDesc()
|
||||
->keys()
|
||||
->sortByDesc(fn ($slug) => Str::contains(
|
||||
Str::slug($this->pages()[$slug]['title'], ' '),
|
||||
Str::slug($search, ' ')
|
||||
) ? 1 : 0)
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* The section the user specifically asked to open.
|
||||
*
|
||||
* @param string $page
|
||||
* @return string|null
|
||||
*/
|
||||
protected function section($page)
|
||||
{
|
||||
return $this->didNotRequestSection()
|
||||
? null
|
||||
: $this->guessSection($page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user requested a specific section when calling the command.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function didNotRequestSection()
|
||||
{
|
||||
return $this->argument('section') === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guess the section the user is attempting to open.
|
||||
*
|
||||
* @param string $page
|
||||
* @return string|null
|
||||
*/
|
||||
protected function guessSection($page)
|
||||
{
|
||||
return $this->sectionsFor($page)
|
||||
->filter(fn ($section) => str_starts_with(
|
||||
Str::slug($section['title'], ' '),
|
||||
Str::slug($this->argument('section'), ' ')
|
||||
))->keys()->first() ?? $this->sectionsFor($page)->map(fn ($section) => similar_text(
|
||||
Str::slug($section['title'], ' '),
|
||||
Str::slug($this->argument('section'), ' '),
|
||||
))
|
||||
->filter(fn ($score) => $score >= min(3, Str::length($this->argument('section'))))
|
||||
->sortDesc()
|
||||
->keys()
|
||||
->sortByDesc(fn ($slug) => Str::contains(
|
||||
Str::slug($this->sectionsFor($page)[$slug]['title'], ' '),
|
||||
Str::slug($this->argument('section'), ' ')
|
||||
) ? 1 : 0)
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the URL in the user's browser.
|
||||
*
|
||||
* @param string $url
|
||||
* @return void
|
||||
*/
|
||||
protected function open($url)
|
||||
{
|
||||
($this->urlOpener ?? function ($url) {
|
||||
if (Env::get('ARTISAN_DOCS_OPEN_STRATEGY')) {
|
||||
$this->openViaCustomStrategy($url);
|
||||
} elseif (in_array($this->systemOsFamily, ['Darwin', 'Windows', 'Linux'])) {
|
||||
$this->openViaBuiltInStrategy($url);
|
||||
} else {
|
||||
$this->components->warn('Unable to open the URL on your system. You will need to open it yourself or create a custom opener for your system.');
|
||||
}
|
||||
})($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the URL via a custom strategy.
|
||||
*
|
||||
* @param string $url
|
||||
* @return void
|
||||
*/
|
||||
protected function openViaCustomStrategy($url)
|
||||
{
|
||||
try {
|
||||
$command = require Env::get('ARTISAN_DOCS_OPEN_STRATEGY');
|
||||
} catch (Throwable) {
|
||||
$command = null;
|
||||
}
|
||||
|
||||
if (! is_callable($command)) {
|
||||
$this->components->warn('Unable to open the URL with your custom strategy. You will need to open it yourself.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$command($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the URL via the built in strategy.
|
||||
*
|
||||
* @param string $url
|
||||
* @return void
|
||||
*/
|
||||
protected function openViaBuiltInStrategy($url)
|
||||
{
|
||||
if ($this->systemOsFamily === 'Windows') {
|
||||
$process = tap(Process::fromShellCommandline(escapeshellcmd("start {$url}")))->run();
|
||||
|
||||
if (! $process->isSuccessful()) {
|
||||
throw new ProcessFailedException($process);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$binary = Collection::make(match ($this->systemOsFamily) {
|
||||
'Darwin' => ['open'],
|
||||
'Linux' => ['xdg-open', 'wslview'],
|
||||
})->first(fn ($binary) => (new ExecutableFinder)->find($binary) !== null);
|
||||
|
||||
if ($binary === null) {
|
||||
$this->components->warn('Unable to open the URL on your system. You will need to open it yourself or create a custom opener for your system.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$process = tap(Process::fromShellCommandline(escapeshellcmd("{$binary} {$url}")))->run();
|
||||
|
||||
if (! $process->isSuccessful()) {
|
||||
throw new ProcessFailedException($process);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The available sections for the page.
|
||||
*
|
||||
* @param string $page
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function sectionsFor($page)
|
||||
{
|
||||
return new Collection($this->pages()[$page]['sections']);
|
||||
}
|
||||
|
||||
/**
|
||||
* The pages available to open.
|
||||
*
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function pages()
|
||||
{
|
||||
return new Collection($this->docs()['pages']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the documentation index as a collection.
|
||||
*
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function docs()
|
||||
{
|
||||
return $this->cache->remember(
|
||||
"artisan.docs.{{$this->version()}}.index",
|
||||
CarbonInterval::months(2),
|
||||
fn () => $this->fetchDocs()->throw()->collect()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the cached copy of the documentation index.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function refreshDocs()
|
||||
{
|
||||
with($this->fetchDocs(), function ($response) {
|
||||
if ($response->successful()) {
|
||||
$this->cache->put("artisan.docs.{{$this->version()}}.index", $response->collect(), CarbonInterval::months(2));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the documentation index from the Laravel website.
|
||||
*
|
||||
* @return \Illuminate\Http\Client\Response
|
||||
*/
|
||||
protected function fetchDocs()
|
||||
{
|
||||
return $this->http->get("https://laravel.com/docs/{$this->version()}/index.json");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the version of the docs to open.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function version()
|
||||
{
|
||||
return Str::before($this->version ?? $this->laravel->version(), '.').'.x';
|
||||
}
|
||||
|
||||
/**
|
||||
* The search query the user provided.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function searchQuery()
|
||||
{
|
||||
return Collection::make($_SERVER['argv'])->skip(3)->implode(' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the command is intended to perform a search.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isSearching()
|
||||
{
|
||||
return ($_SERVER['argv'][2] ?? null) === '--';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the documentation version.
|
||||
*
|
||||
* @param string $version
|
||||
* @return $this
|
||||
*/
|
||||
public function setVersion($version)
|
||||
{
|
||||
$this->version = $version;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom URL opener.
|
||||
*
|
||||
* @param callable|null $opener
|
||||
* @return $this
|
||||
*/
|
||||
public function setUrlOpener($opener)
|
||||
{
|
||||
$this->urlOpener = $opener;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the system operating system family.
|
||||
*
|
||||
* @param string $family
|
||||
* @return $this
|
||||
*/
|
||||
public function setSystemOsFamily($family)
|
||||
{
|
||||
$this->systemOsFamily = $family;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use App\Http\Middleware\PreventRequestsDuringMaintenance;
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Foundation\Events\MaintenanceModeEnabled;
|
||||
use Illuminate\Foundation\Exceptions\RegisterErrorViewPaths;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Throwable;
|
||||
|
||||
#[AsCommand(name: 'down')]
|
||||
class DownCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command signature.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'down {--redirect= : The path that users should be redirected to}
|
||||
{--render= : The view that should be prerendered for display during maintenance mode}
|
||||
{--retry= : The number of seconds after which the request may be retried}
|
||||
{--refresh= : The number of seconds after which the browser may refresh}
|
||||
{--secret= : The secret phrase that may be used to bypass maintenance mode}
|
||||
{--with-secret : Generate a random secret phrase that may be used to bypass maintenance mode}
|
||||
{--status=503 : The status code that should be used when returning the maintenance mode response}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Put the application into maintenance / demo mode';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
if ($this->laravel->maintenanceMode()->active()) {
|
||||
$this->components->info('Application is already down.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$downFilePayload = $this->getDownFilePayload();
|
||||
|
||||
$this->laravel->maintenanceMode()->activate($downFilePayload);
|
||||
|
||||
file_put_contents(
|
||||
storage_path('framework/maintenance.php'),
|
||||
file_get_contents(__DIR__.'/stubs/maintenance-mode.stub')
|
||||
);
|
||||
|
||||
$this->laravel->get('events')->dispatch(new MaintenanceModeEnabled());
|
||||
|
||||
$this->components->info('Application is now in maintenance mode.');
|
||||
|
||||
if ($downFilePayload['secret'] !== null) {
|
||||
$this->components->info('You may bypass maintenance mode via ['.config('app.url')."/{$downFilePayload['secret']}].");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->components->error(sprintf(
|
||||
'Failed to enter maintenance mode: %s.',
|
||||
$e->getMessage(),
|
||||
));
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the payload to be placed in the "down" file.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getDownFilePayload()
|
||||
{
|
||||
return [
|
||||
'except' => $this->excludedPaths(),
|
||||
'redirect' => $this->redirectPath(),
|
||||
'retry' => $this->getRetryTime(),
|
||||
'refresh' => $this->option('refresh'),
|
||||
'secret' => $this->getSecret(),
|
||||
'status' => (int) $this->option('status', 503),
|
||||
'template' => $this->option('render') ? $this->prerenderView() : null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the paths that should be excluded from maintenance mode.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function excludedPaths()
|
||||
{
|
||||
try {
|
||||
return $this->laravel->make(PreventRequestsDuringMaintenance::class)->getExcludedPaths();
|
||||
} catch (Throwable) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path that users should be redirected to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function redirectPath()
|
||||
{
|
||||
if ($this->option('redirect') && $this->option('redirect') !== '/') {
|
||||
return '/'.trim($this->option('redirect'), '/');
|
||||
}
|
||||
|
||||
return $this->option('redirect');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prerender the specified view so that it can be rendered even before loading Composer.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function prerenderView()
|
||||
{
|
||||
(new RegisterErrorViewPaths)();
|
||||
|
||||
return view($this->option('render'), [
|
||||
'retryAfter' => $this->option('retry'),
|
||||
])->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of seconds the client should wait before retrying their request.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
protected function getRetryTime()
|
||||
{
|
||||
$retry = $this->option('retry');
|
||||
|
||||
return is_numeric($retry) && $retry > 0 ? (int) $retry : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the secret phrase that may be used to bypass maintenance mode.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getSecret()
|
||||
{
|
||||
return match (true) {
|
||||
! is_null($this->option('secret')) => $this->option('secret'),
|
||||
$this->option('with-secret') => Str::random(),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
use function Laravel\Prompts\select;
|
||||
|
||||
#[AsCommand(name: 'make:enum')]
|
||||
class EnumMakeCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:enum';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new enum';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Enum';
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
if ($this->option('string') || $this->option('int')) {
|
||||
return $this->resolveStubPath('/stubs/enum.backed.stub');
|
||||
}
|
||||
|
||||
return $this->resolveStubPath('/stubs/enum.stub');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return match (true) {
|
||||
is_dir(app_path('Enums')) => $rootNamespace.'\\Enums',
|
||||
is_dir(app_path('Enumerations')) => $rootNamespace.'\\Enumerations',
|
||||
default => $rootNamespace,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the class with the given name.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
protected function buildClass($name)
|
||||
{
|
||||
if ($this->option('string') || $this->option('int')) {
|
||||
return str_replace(
|
||||
['{{ type }}'],
|
||||
$this->option('string') ? 'string' : 'int',
|
||||
parent::buildClass($name)
|
||||
);
|
||||
}
|
||||
|
||||
return parent::buildClass($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interact further with the user if they were prompted for missing arguments.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return void
|
||||
*/
|
||||
protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if ($this->didReceiveOptions($input)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$type = select('Which type of enum would you like?', [
|
||||
'pure' => 'Pure enum',
|
||||
'string' => 'Backed enum (String)',
|
||||
'int' => 'Backed enum (Integer)',
|
||||
]);
|
||||
|
||||
if ($type !== 'pure') {
|
||||
$input->setOption($type, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['string', 's', InputOption::VALUE_NONE, 'Generate a string backed enum.'],
|
||||
['int', 'i', InputOption::VALUE_NONE, 'Generate an integer backed enum.'],
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the enum even if the enum already exists'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'env')]
|
||||
class EnvironmentCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'env';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Display the current framework environment';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->components->info(sprintf(
|
||||
'The application environment is [%s].',
|
||||
$this->laravel['env'],
|
||||
));
|
||||
}
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Encryption\Encrypter;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Support\Env;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'env:decrypt')]
|
||||
class EnvironmentDecryptCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'env:decrypt
|
||||
{--key= : The encryption key}
|
||||
{--cipher= : The encryption cipher}
|
||||
{--env= : The environment to be decrypted}
|
||||
{--force : Overwrite the existing environment file}
|
||||
{--path= : Path to write the decrypted file}
|
||||
{--filename= : Filename of the decrypted file}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Decrypt an environment file';
|
||||
|
||||
/**
|
||||
* The filesystem instance.
|
||||
*
|
||||
* @var \Illuminate\Filesystem\Filesystem
|
||||
*/
|
||||
protected $files;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @param \Illuminate\Filesystem\Filesystem $files
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Filesystem $files)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->files = $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$key = $this->option('key') ?: Env::get('LARAVEL_ENV_ENCRYPTION_KEY');
|
||||
|
||||
if (! $key) {
|
||||
$this->components->error('A decryption key is required.');
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
$cipher = $this->option('cipher') ?: 'AES-256-CBC';
|
||||
|
||||
$key = $this->parseKey($key);
|
||||
|
||||
$encryptedFile = ($this->option('env')
|
||||
? Str::finish(dirname($this->laravel->environmentFilePath()), DIRECTORY_SEPARATOR).'.env.'.$this->option('env')
|
||||
: $this->laravel->environmentFilePath()).'.encrypted';
|
||||
|
||||
$outputFile = $this->outputFilePath();
|
||||
|
||||
if (Str::endsWith($outputFile, '.encrypted')) {
|
||||
$this->components->error('Invalid filename.');
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
if (! $this->files->exists($encryptedFile)) {
|
||||
$this->components->error('Encrypted environment file not found.');
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
if ($this->files->exists($outputFile) && ! $this->option('force')) {
|
||||
$this->components->error('Environment file already exists.');
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
try {
|
||||
$encrypter = new Encrypter($key, $cipher);
|
||||
|
||||
$this->files->put(
|
||||
$outputFile,
|
||||
$encrypter->decrypt($this->files->get($encryptedFile))
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
$this->components->error($e->getMessage());
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
$this->components->info('Environment successfully decrypted.');
|
||||
|
||||
$this->components->twoColumnDetail('Decrypted file', $outputFile);
|
||||
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the encryption key.
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function parseKey(string $key)
|
||||
{
|
||||
if (Str::startsWith($key, $prefix = 'base64:')) {
|
||||
$key = base64_decode(Str::after($key, $prefix));
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the output file path that should be used for the command.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function outputFilePath()
|
||||
{
|
||||
$path = Str::finish($this->option('path') ?: dirname($this->laravel->environmentFilePath()), DIRECTORY_SEPARATOR);
|
||||
|
||||
$outputFile = $this->option('filename') ?: ('.env'.($this->option('env') ? '.'.$this->option('env') : ''));
|
||||
$outputFile = ltrim($outputFile, DIRECTORY_SEPARATOR);
|
||||
|
||||
return $path.$outputFile;
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Encryption\Encrypter;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'env:encrypt')]
|
||||
class EnvironmentEncryptCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'env:encrypt
|
||||
{--key= : The encryption key}
|
||||
{--cipher= : The encryption cipher}
|
||||
{--env= : The environment to be encrypted}
|
||||
{--prune : Delete the original environment file}
|
||||
{--force : Overwrite the existing encrypted environment file}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Encrypt an environment file';
|
||||
|
||||
/**
|
||||
* The filesystem instance.
|
||||
*
|
||||
* @var \Illuminate\Filesystem\Filesystem
|
||||
*/
|
||||
protected $files;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @param \Illuminate\Filesystem\Filesystem $files
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Filesystem $files)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->files = $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$cipher = $this->option('cipher') ?: 'AES-256-CBC';
|
||||
|
||||
$key = $this->option('key');
|
||||
|
||||
$keyPassed = $key !== null;
|
||||
|
||||
$environmentFile = $this->option('env')
|
||||
? Str::finish(dirname($this->laravel->environmentFilePath()), DIRECTORY_SEPARATOR).'.env.'.$this->option('env')
|
||||
: $this->laravel->environmentFilePath();
|
||||
|
||||
$encryptedFile = $environmentFile.'.encrypted';
|
||||
|
||||
if (! $keyPassed) {
|
||||
$key = Encrypter::generateKey($cipher);
|
||||
}
|
||||
|
||||
if (! $this->files->exists($environmentFile)) {
|
||||
$this->components->error('Environment file not found.');
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
if ($this->files->exists($encryptedFile) && ! $this->option('force')) {
|
||||
$this->components->error('Encrypted environment file already exists.');
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
try {
|
||||
$encrypter = new Encrypter($this->parseKey($key), $cipher);
|
||||
|
||||
$this->files->put(
|
||||
$encryptedFile,
|
||||
$encrypter->encrypt($this->files->get($environmentFile))
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
$this->components->error($e->getMessage());
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
if ($this->option('prune')) {
|
||||
$this->files->delete($environmentFile);
|
||||
}
|
||||
|
||||
$this->components->info('Environment successfully encrypted.');
|
||||
|
||||
$this->components->twoColumnDetail('Key', $keyPassed ? $key : 'base64:'.base64_encode($key));
|
||||
$this->components->twoColumnDetail('Cipher', $cipher);
|
||||
$this->components->twoColumnDetail('Encrypted file', $encryptedFile);
|
||||
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the encryption key.
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function parseKey(string $key)
|
||||
{
|
||||
if (Str::startsWith($key, $prefix = 'base64:')) {
|
||||
$key = base64_decode(Str::after($key, $prefix));
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'event:cache')]
|
||||
class EventCacheCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'event:cache';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = "Discover and cache the application's events and listeners";
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->callSilent('event:clear');
|
||||
|
||||
file_put_contents(
|
||||
$this->laravel->getCachedEventsPath(),
|
||||
'<?php return '.var_export($this->getEvents(), true).';'
|
||||
);
|
||||
|
||||
$this->components->info('Events cached successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the events and listeners configured for the application.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getEvents()
|
||||
{
|
||||
$events = [];
|
||||
|
||||
foreach ($this->laravel->getProviders(EventServiceProvider::class) as $provider) {
|
||||
$providerEvents = array_merge_recursive($provider->shouldDiscoverEvents() ? $provider->discoverEvents() : [], $provider->listens());
|
||||
|
||||
$events[get_class($provider)] = $providerEvents;
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'event:clear')]
|
||||
class EventClearCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'event:clear';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Clear all cached events and listeners';
|
||||
|
||||
/**
|
||||
* The filesystem instance.
|
||||
*
|
||||
* @var \Illuminate\Filesystem\Filesystem
|
||||
*/
|
||||
protected $files;
|
||||
|
||||
/**
|
||||
* Create a new config clear command instance.
|
||||
*
|
||||
* @param \Illuminate\Filesystem\Filesystem $files
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Filesystem $files)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->files = $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->files->delete($this->laravel->getCachedEventsPath());
|
||||
|
||||
$this->components->info('Cached events cleared successfully.');
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'event:generate')]
|
||||
class EventGenerateCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'event:generate';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generate the missing events and listeners based on registration';
|
||||
|
||||
/**
|
||||
* Indicates whether the command should be shown in the Artisan command list.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $hidden = true;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$providers = $this->laravel->getProviders(EventServiceProvider::class);
|
||||
|
||||
foreach ($providers as $provider) {
|
||||
foreach ($provider->listens() as $event => $listeners) {
|
||||
$this->makeEventAndListeners($event, $listeners);
|
||||
}
|
||||
}
|
||||
|
||||
$this->components->info('Events and listeners generated successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the event and listeners for the given event.
|
||||
*
|
||||
* @param string $event
|
||||
* @param array $listeners
|
||||
* @return void
|
||||
*/
|
||||
protected function makeEventAndListeners($event, $listeners)
|
||||
{
|
||||
if (! str_contains($event, '\\')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->callSilent('make:event', ['name' => $event]);
|
||||
|
||||
$this->makeListeners($event, $listeners);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the listeners for the given event.
|
||||
*
|
||||
* @param string $event
|
||||
* @param array $listeners
|
||||
* @return void
|
||||
*/
|
||||
protected function makeListeners($event, $listeners)
|
||||
{
|
||||
foreach ($listeners as $listener) {
|
||||
$listener = preg_replace('/@.+$/', '', $listener);
|
||||
|
||||
$this->callSilent('make:listener', array_filter(
|
||||
['name' => $listener, '--event' => $event]
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use ReflectionFunction;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'event:list')]
|
||||
class EventListCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'event:list {--event= : Filter the events by name}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = "List the application's events and listeners";
|
||||
|
||||
/**
|
||||
* The events dispatcher resolver callback.
|
||||
*
|
||||
* @var \Closure|null
|
||||
*/
|
||||
protected static $eventsResolver;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$events = $this->getEvents()->sortKeys();
|
||||
|
||||
if ($events->isEmpty()) {
|
||||
$this->components->info("Your application doesn't have any events matching the given criteria.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
|
||||
$events->each(function ($listeners, $event) {
|
||||
$this->components->twoColumnDetail($this->appendEventInterfaces($event));
|
||||
$this->components->bulletList($listeners);
|
||||
});
|
||||
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the events and listeners configured for the application.
|
||||
*
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
protected function getEvents()
|
||||
{
|
||||
$events = collect($this->getListenersOnDispatcher());
|
||||
|
||||
if ($this->filteringByEvent()) {
|
||||
$events = $this->filterEvents($events);
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the event / listeners from the dispatcher object.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getListenersOnDispatcher()
|
||||
{
|
||||
$events = [];
|
||||
|
||||
foreach ($this->getRawListeners() as $event => $rawListeners) {
|
||||
foreach ($rawListeners as $rawListener) {
|
||||
if (is_string($rawListener)) {
|
||||
$events[$event][] = $this->appendListenerInterfaces($rawListener);
|
||||
} elseif ($rawListener instanceof Closure) {
|
||||
$events[$event][] = $this->stringifyClosure($rawListener);
|
||||
} elseif (is_array($rawListener) && count($rawListener) === 2) {
|
||||
if (is_object($rawListener[0])) {
|
||||
$rawListener[0] = get_class($rawListener[0]);
|
||||
}
|
||||
|
||||
$events[$event][] = $this->appendListenerInterfaces(implode('@', $rawListener));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the event implemented interfaces to the output.
|
||||
*
|
||||
* @param string $event
|
||||
* @return string
|
||||
*/
|
||||
protected function appendEventInterfaces($event)
|
||||
{
|
||||
if (! class_exists($event)) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
$interfaces = class_implements($event);
|
||||
|
||||
if (in_array(ShouldBroadcast::class, $interfaces)) {
|
||||
$event .= ' <fg=bright-blue>(ShouldBroadcast)</>';
|
||||
}
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the listener implemented interfaces to the output.
|
||||
*
|
||||
* @param string $listener
|
||||
* @return string
|
||||
*/
|
||||
protected function appendListenerInterfaces($listener)
|
||||
{
|
||||
$listener = explode('@', $listener);
|
||||
|
||||
$interfaces = class_implements($listener[0]);
|
||||
|
||||
$listener = implode('@', $listener);
|
||||
|
||||
if (in_array(ShouldQueue::class, $interfaces)) {
|
||||
$listener .= ' <fg=bright-blue>(ShouldQueue)</>';
|
||||
}
|
||||
|
||||
return $listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a displayable string representation of a Closure listener.
|
||||
*
|
||||
* @param \Closure $rawListener
|
||||
* @return string
|
||||
*/
|
||||
protected function stringifyClosure(Closure $rawListener)
|
||||
{
|
||||
$reflection = new ReflectionFunction($rawListener);
|
||||
|
||||
$path = str_replace([base_path(), DIRECTORY_SEPARATOR], ['', '/'], $reflection->getFileName() ?: '');
|
||||
|
||||
return 'Closure at: '.$path.':'.$reflection->getStartLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the given events using the provided event name filter.
|
||||
*
|
||||
* @param \Illuminate\Support\Collection $events
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
protected function filterEvents($events)
|
||||
{
|
||||
if (! $eventName = $this->option('event')) {
|
||||
return $events;
|
||||
}
|
||||
|
||||
return $events->filter(
|
||||
fn ($listeners, $event) => str_contains($event, $eventName)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user is filtering by an event name.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function filteringByEvent()
|
||||
{
|
||||
return ! empty($this->option('event'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw version of event listeners from the event dispatcher.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getRawListeners()
|
||||
{
|
||||
return $this->getEventsDispatcher()->getRawListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the event dispatcher.
|
||||
*
|
||||
* @return \Illuminate\Events\Dispatcher
|
||||
*/
|
||||
public function getEventsDispatcher()
|
||||
{
|
||||
return is_null(self::$eventsResolver)
|
||||
? $this->getLaravel()->make('events')
|
||||
: call_user_func(self::$eventsResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback that should be used when resolving the events dispatcher.
|
||||
*
|
||||
* @param \Closure|null $resolver
|
||||
* @return void
|
||||
*/
|
||||
public static function resolveEventsUsing($resolver)
|
||||
{
|
||||
static::$eventsResolver = $resolver;
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand(name: 'make:event')]
|
||||
class EventMakeCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:event';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new event class';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Event';
|
||||
|
||||
/**
|
||||
* Determine if the class already exists.
|
||||
*
|
||||
* @param string $rawName
|
||||
* @return bool
|
||||
*/
|
||||
protected function alreadyExists($rawName)
|
||||
{
|
||||
return class_exists($rawName) ||
|
||||
$this->files->exists($this->getPath($this->qualifyClass($rawName)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
return $this->resolveStubPath('/stubs/event.stub');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return $rootNamespace.'\Events';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the event already exists'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
use function Laravel\Prompts\{confirm};
|
||||
|
||||
#[AsCommand(name: 'make:exception')]
|
||||
class ExceptionMakeCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:exception';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new custom exception class';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Exception';
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
if ($this->option('render')) {
|
||||
return $this->option('report')
|
||||
? __DIR__.'/stubs/exception-render-report.stub'
|
||||
: __DIR__.'/stubs/exception-render.stub';
|
||||
}
|
||||
|
||||
return $this->option('report')
|
||||
? __DIR__.'/stubs/exception-report.stub'
|
||||
: __DIR__.'/stubs/exception.stub';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the class already exists.
|
||||
*
|
||||
* @param string $rawName
|
||||
* @return bool
|
||||
*/
|
||||
protected function alreadyExists($rawName)
|
||||
{
|
||||
return class_exists($this->rootNamespace().'Exceptions\\'.$rawName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return $rootNamespace.'\Exceptions';
|
||||
}
|
||||
|
||||
/**
|
||||
* Interact further with the user if they were prompted for missing arguments.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return void
|
||||
*/
|
||||
protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if ($this->didReceiveOptions($input)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$input->setOption('report', confirm('Should the exception have a report method?', default: false));
|
||||
$input->setOption('render', confirm('Should the exception have a render method?', default: false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the exception already exists'],
|
||||
['render', null, InputOption::VALUE_NONE, 'Create the exception with an empty render method'],
|
||||
['report', null, InputOption::VALUE_NONE, 'Create the exception with an empty report method'],
|
||||
];
|
||||
}
|
||||
}
|
||||
Vendored
+44
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Symfony\Component\Process\PhpExecutableFinder;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
trait InteractsWithComposerPackages
|
||||
{
|
||||
/**
|
||||
* Installs the given Composer Packages into the application.
|
||||
*
|
||||
* @param string $composer
|
||||
* @param array $packages
|
||||
* @return bool
|
||||
*/
|
||||
protected function requireComposerPackages(string $composer, array $packages)
|
||||
{
|
||||
if ($composer !== 'global') {
|
||||
$command = [$this->phpBinary(), $composer, 'require'];
|
||||
}
|
||||
|
||||
$command = array_merge(
|
||||
$command ?? ['composer', 'require'],
|
||||
$packages,
|
||||
);
|
||||
|
||||
return ! (new Process($command, $this->laravel->basePath(), ['COMPOSER_MEMORY_LIMIT' => '-1']))
|
||||
->setTimeout(null)
|
||||
->run(function ($type, $output) {
|
||||
$this->output->write($output);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the appropriate PHP binary.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function phpBinary()
|
||||
{
|
||||
return (new PhpExecutableFinder())->find(false) ?: 'php';
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand(name: 'make:interface')]
|
||||
class InterfaceMakeCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:interface';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new interface';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Interface';
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
return __DIR__.'/stubs/interface.stub';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return match (true) {
|
||||
is_dir(app_path('Contracts')) => $rootNamespace.'\\Contracts',
|
||||
is_dir(app_path('Interfaces')) => $rootNamespace.'\\Interfaces',
|
||||
default => $rootNamespace,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the interface even if the interface already exists'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Concerns\CreatesMatchingTest;
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand(name: 'make:job')]
|
||||
class JobMakeCommand extends GeneratorCommand
|
||||
{
|
||||
use CreatesMatchingTest;
|
||||
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:job';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new job class';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Job';
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
return $this->option('sync')
|
||||
? $this->resolveStubPath('/stubs/job.stub')
|
||||
: $this->resolveStubPath('/stubs/job.queued.stub');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return $rootNamespace.'\Jobs';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the job already exists'],
|
||||
['sync', null, InputOption::VALUE_NONE, 'Indicates that job should be synchronous'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,634 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Carbon\CarbonInterval;
|
||||
use Closure;
|
||||
use DateTimeInterface;
|
||||
use Illuminate\Console\Application as Artisan;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Console\Events\CommandFinished;
|
||||
use Illuminate\Console\Events\CommandStarting;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Contracts\Console\Kernel as KernelContract;
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Foundation\Events\Terminating;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Env;
|
||||
use Illuminate\Support\InteractsWithTime;
|
||||
use Illuminate\Support\Str;
|
||||
use ReflectionClass;
|
||||
use SplFileInfo;
|
||||
use Symfony\Component\Console\ConsoleEvents;
|
||||
use Symfony\Component\Console\Event\ConsoleCommandEvent;
|
||||
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Throwable;
|
||||
|
||||
class Kernel implements KernelContract
|
||||
{
|
||||
use InteractsWithTime;
|
||||
|
||||
/**
|
||||
* The application implementation.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Foundation\Application
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* The event dispatcher implementation.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Events\Dispatcher
|
||||
*/
|
||||
protected $events;
|
||||
|
||||
/**
|
||||
* The Symfony event dispatcher implementation.
|
||||
*
|
||||
* @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface|null
|
||||
*/
|
||||
protected $symfonyDispatcher;
|
||||
|
||||
/**
|
||||
* The Artisan application instance.
|
||||
*
|
||||
* @var \Illuminate\Console\Application|null
|
||||
*/
|
||||
protected $artisan;
|
||||
|
||||
/**
|
||||
* The Artisan commands provided by the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commands = [];
|
||||
|
||||
/**
|
||||
* The paths where Artisan commands should be automatically discovered.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commandPaths = [];
|
||||
|
||||
/**
|
||||
* The paths where Artisan "routes" should be automatically discovered.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commandRoutePaths = [];
|
||||
|
||||
/**
|
||||
* Indicates if the Closure commands have been loaded.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $commandsLoaded = false;
|
||||
|
||||
/**
|
||||
* The commands paths that have been "loaded".
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $loadedPaths = [];
|
||||
|
||||
/**
|
||||
* All of the registered command duration handlers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commandLifecycleDurationHandlers = [];
|
||||
|
||||
/**
|
||||
* When the currently handled command started.
|
||||
*
|
||||
* @var \Illuminate\Support\Carbon|null
|
||||
*/
|
||||
protected $commandStartedAt;
|
||||
|
||||
/**
|
||||
* The bootstrap classes for the application.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $bootstrappers = [
|
||||
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
|
||||
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
|
||||
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
|
||||
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
|
||||
\Illuminate\Foundation\Bootstrap\SetRequestForConsole::class,
|
||||
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
|
||||
\Illuminate\Foundation\Bootstrap\BootProviders::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Create a new console kernel instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
||||
* @param \Illuminate\Contracts\Events\Dispatcher $events
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Application $app, Dispatcher $events)
|
||||
{
|
||||
if (! defined('ARTISAN_BINARY')) {
|
||||
define('ARTISAN_BINARY', 'artisan');
|
||||
}
|
||||
|
||||
$this->app = $app;
|
||||
$this->events = $events;
|
||||
|
||||
$this->app->booted(function () {
|
||||
if (! $this->app->runningUnitTests()) {
|
||||
$this->rerouteSymfonyCommandEvents();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-route the Symfony command events to their Laravel counterparts.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function rerouteSymfonyCommandEvents()
|
||||
{
|
||||
if (is_null($this->symfonyDispatcher)) {
|
||||
$this->symfonyDispatcher = new EventDispatcher;
|
||||
|
||||
$this->symfonyDispatcher->addListener(ConsoleEvents::COMMAND, function (ConsoleCommandEvent $event) {
|
||||
$this->events->dispatch(
|
||||
new CommandStarting($event->getCommand()->getName(), $event->getInput(), $event->getOutput())
|
||||
);
|
||||
});
|
||||
|
||||
$this->symfonyDispatcher->addListener(ConsoleEvents::TERMINATE, function (ConsoleTerminateEvent $event) {
|
||||
$this->events->dispatch(
|
||||
new CommandFinished($event->getCommand()->getName(), $event->getInput(), $event->getOutput(), $event->getExitCode())
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the console application.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface|null $output
|
||||
* @return int
|
||||
*/
|
||||
public function handle($input, $output = null)
|
||||
{
|
||||
$this->commandStartedAt = Carbon::now();
|
||||
|
||||
try {
|
||||
if (in_array($input->getFirstArgument(), ['env:encrypt', 'env:decrypt'], true)) {
|
||||
$this->bootstrapWithoutBootingProviders();
|
||||
}
|
||||
|
||||
$this->bootstrap();
|
||||
|
||||
return $this->getArtisan()->run($input, $output);
|
||||
} catch (Throwable $e) {
|
||||
$this->reportException($e);
|
||||
|
||||
$this->renderException($output, $e);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate the application.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param int $status
|
||||
* @return void
|
||||
*/
|
||||
public function terminate($input, $status)
|
||||
{
|
||||
$this->events->dispatch(new Terminating);
|
||||
|
||||
$this->app->terminate();
|
||||
|
||||
if ($this->commandStartedAt === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->commandStartedAt->setTimezone($this->app['config']->get('app.timezone') ?? 'UTC');
|
||||
|
||||
foreach ($this->commandLifecycleDurationHandlers as ['threshold' => $threshold, 'handler' => $handler]) {
|
||||
$end ??= Carbon::now();
|
||||
|
||||
if ($this->commandStartedAt->diffInMilliseconds($end) > $threshold) {
|
||||
$handler($this->commandStartedAt, $input, $status);
|
||||
}
|
||||
}
|
||||
|
||||
$this->commandStartedAt = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked when the command lifecycle duration exceeds a given amount of time.
|
||||
*
|
||||
* @param \DateTimeInterface|\Carbon\CarbonInterval|float|int $threshold
|
||||
* @param callable $handler
|
||||
* @return void
|
||||
*/
|
||||
public function whenCommandLifecycleIsLongerThan($threshold, $handler)
|
||||
{
|
||||
$threshold = $threshold instanceof DateTimeInterface
|
||||
? $this->secondsUntil($threshold) * 1000
|
||||
: $threshold;
|
||||
|
||||
$threshold = $threshold instanceof CarbonInterval
|
||||
? $threshold->totalMilliseconds
|
||||
: $threshold;
|
||||
|
||||
$this->commandLifecycleDurationHandlers[] = [
|
||||
'threshold' => $threshold,
|
||||
'handler' => $handler,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* When the command being handled started.
|
||||
*
|
||||
* @return \Illuminate\Support\Carbon|null
|
||||
*/
|
||||
public function commandStartedAt()
|
||||
{
|
||||
return $this->commandStartedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*
|
||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||
* @return void
|
||||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a console schedule instance.
|
||||
*
|
||||
* @return \Illuminate\Console\Scheduling\Schedule
|
||||
*/
|
||||
public function resolveConsoleSchedule()
|
||||
{
|
||||
return tap(new Schedule($this->scheduleTimezone()), function ($schedule) {
|
||||
$this->schedule($schedule->useCache($this->scheduleCache()));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timezone that should be used by default for scheduled events.
|
||||
*
|
||||
* @return \DateTimeZone|string|null
|
||||
*/
|
||||
protected function scheduleTimezone()
|
||||
{
|
||||
$config = $this->app['config'];
|
||||
|
||||
return $config->get('app.schedule_timezone', $config->get('app.timezone'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the cache store that should manage scheduling mutexes.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function scheduleCache()
|
||||
{
|
||||
return $this->app['config']->get('cache.schedule_store', Env::get('SCHEDULE_CACHE_DRIVER', function () {
|
||||
return Env::get('SCHEDULE_CACHE_STORE');
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the commands for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function commands()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a Closure based command with the application.
|
||||
*
|
||||
* @param string $signature
|
||||
* @param \Closure $callback
|
||||
* @return \Illuminate\Foundation\Console\ClosureCommand
|
||||
*/
|
||||
public function command($signature, Closure $callback)
|
||||
{
|
||||
$command = new ClosureCommand($signature, $callback);
|
||||
|
||||
Artisan::starting(function ($artisan) use ($command) {
|
||||
$artisan->add($command);
|
||||
});
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all of the commands in the given directory.
|
||||
*
|
||||
* @param array|string $paths
|
||||
* @return void
|
||||
*/
|
||||
protected function load($paths)
|
||||
{
|
||||
$paths = array_unique(Arr::wrap($paths));
|
||||
|
||||
$paths = array_filter($paths, function ($path) {
|
||||
return is_dir($path);
|
||||
});
|
||||
|
||||
if (empty($paths)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->loadedPaths = array_values(
|
||||
array_unique(array_merge($this->loadedPaths, $paths))
|
||||
);
|
||||
|
||||
$namespace = $this->app->getNamespace();
|
||||
|
||||
foreach (Finder::create()->in($paths)->files() as $file) {
|
||||
$command = $this->commandClassFromFile($file, $namespace);
|
||||
|
||||
if (is_subclass_of($command, Command::class) &&
|
||||
! (new ReflectionClass($command))->isAbstract()) {
|
||||
Artisan::starting(function ($artisan) use ($command) {
|
||||
$artisan->resolve($command);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the command class name from the given file path.
|
||||
*
|
||||
* @param \SplFileInfo $file
|
||||
* @param string $namespace
|
||||
* @return string
|
||||
*/
|
||||
protected function commandClassFromFile(SplFileInfo $file, string $namespace): string
|
||||
{
|
||||
return $namespace.str_replace(
|
||||
['/', '.php'],
|
||||
['\\', ''],
|
||||
Str::after($file->getRealPath(), realpath(app_path()).DIRECTORY_SEPARATOR)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given command with the console application.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Command\Command $command
|
||||
* @return void
|
||||
*/
|
||||
public function registerCommand($command)
|
||||
{
|
||||
$this->getArtisan()->add($command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run an Artisan console command by name.
|
||||
*
|
||||
* @param string $command
|
||||
* @param array $parameters
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface|null $outputBuffer
|
||||
* @return int
|
||||
*
|
||||
* @throws \Symfony\Component\Console\Exception\CommandNotFoundException
|
||||
*/
|
||||
public function call($command, array $parameters = [], $outputBuffer = null)
|
||||
{
|
||||
if (in_array($command, ['env:encrypt', 'env:decrypt'], true)) {
|
||||
$this->bootstrapWithoutBootingProviders();
|
||||
}
|
||||
|
||||
$this->bootstrap();
|
||||
|
||||
return $this->getArtisan()->call($command, $parameters, $outputBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue the given console command.
|
||||
*
|
||||
* @param string $command
|
||||
* @param array $parameters
|
||||
* @return \Illuminate\Foundation\Bus\PendingDispatch
|
||||
*/
|
||||
public function queue($command, array $parameters = [])
|
||||
{
|
||||
return QueuedCommand::dispatch(func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the commands registered with the console.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
$this->bootstrap();
|
||||
|
||||
return $this->getArtisan()->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the output for the last run command.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function output()
|
||||
{
|
||||
$this->bootstrap();
|
||||
|
||||
return $this->getArtisan()->output();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap the application for artisan commands.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrap()
|
||||
{
|
||||
if (! $this->app->hasBeenBootstrapped()) {
|
||||
$this->app->bootstrapWith($this->bootstrappers());
|
||||
}
|
||||
|
||||
$this->app->loadDeferredProviders();
|
||||
|
||||
if (! $this->commandsLoaded) {
|
||||
$this->commands();
|
||||
|
||||
if ($this->shouldDiscoverCommands()) {
|
||||
$this->discoverCommands();
|
||||
}
|
||||
|
||||
$this->commandsLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Discover the commands that should be automatically loaded.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function discoverCommands()
|
||||
{
|
||||
foreach ($this->commandPaths as $path) {
|
||||
$this->load($path);
|
||||
}
|
||||
|
||||
foreach ($this->commandRoutePaths as $path) {
|
||||
if (file_exists($path)) {
|
||||
require $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap the application without booting service providers.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrapWithoutBootingProviders()
|
||||
{
|
||||
$this->app->bootstrapWith(
|
||||
collect($this->bootstrappers())->reject(function ($bootstrapper) {
|
||||
return $bootstrapper === \Illuminate\Foundation\Bootstrap\BootProviders::class;
|
||||
})->all()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the kernel should discover commands.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function shouldDiscoverCommands()
|
||||
{
|
||||
return get_class($this) === __CLASS__;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Artisan application instance.
|
||||
*
|
||||
* @return \Illuminate\Console\Application
|
||||
*/
|
||||
protected function getArtisan()
|
||||
{
|
||||
if (is_null($this->artisan)) {
|
||||
$this->artisan = (new Artisan($this->app, $this->events, $this->app->version()))
|
||||
->resolveCommands($this->commands)
|
||||
->setContainerCommandLoader();
|
||||
|
||||
if ($this->symfonyDispatcher instanceof EventDispatcher) {
|
||||
$this->artisan->setDispatcher($this->symfonyDispatcher);
|
||||
$this->artisan->setSignalsToDispatchEvent();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->artisan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Artisan application instance.
|
||||
*
|
||||
* @param \Illuminate\Console\Application|null $artisan
|
||||
* @return void
|
||||
*/
|
||||
public function setArtisan($artisan)
|
||||
{
|
||||
$this->artisan = $artisan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Artisan commands provided by the application.
|
||||
*
|
||||
* @param array $commands
|
||||
* @return $this
|
||||
*/
|
||||
public function addCommands(array $commands)
|
||||
{
|
||||
$this->commands = array_values(array_unique(array_merge($this->commands, $commands)));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the paths that should have their Artisan commands automatically discovered.
|
||||
*
|
||||
* @param array $paths
|
||||
* @return $this
|
||||
*/
|
||||
public function addCommandPaths(array $paths)
|
||||
{
|
||||
$this->commandPaths = array_values(array_unique(array_merge($this->commandPaths, $paths)));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the paths that should have their Artisan "routes" automatically discovered.
|
||||
*
|
||||
* @param array $paths
|
||||
* @return $this
|
||||
*/
|
||||
public function addCommandRoutePaths(array $paths)
|
||||
{
|
||||
$this->commandRoutePaths = array_values(array_unique(array_merge($this->commandRoutePaths, $paths)));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bootstrap classes for the application.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function bootstrappers()
|
||||
{
|
||||
return $this->bootstrappers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the exception to the exception handler.
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @return void
|
||||
*/
|
||||
protected function reportException(Throwable $e)
|
||||
{
|
||||
$this->app[ExceptionHandler::class]->report($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the given exception.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @param \Throwable $e
|
||||
* @return void
|
||||
*/
|
||||
protected function renderException($output, Throwable $e)
|
||||
{
|
||||
$this->app[ExceptionHandler::class]->renderForConsole($output, $e);
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Console\ConfirmableTrait;
|
||||
use Illuminate\Encryption\Encrypter;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'key:generate')]
|
||||
class KeyGenerateCommand extends Command
|
||||
{
|
||||
use ConfirmableTrait;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'key:generate
|
||||
{--show : Display the key instead of modifying files}
|
||||
{--force : Force the operation to run when in production}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Set the application key';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$key = $this->generateRandomKey();
|
||||
|
||||
if ($this->option('show')) {
|
||||
return $this->line('<comment>'.$key.'</comment>');
|
||||
}
|
||||
|
||||
// Next, we will replace the application key in the environment file so it is
|
||||
// automatically setup for this developer. This key gets generated using a
|
||||
// secure random byte generator and is later base64 encoded for storage.
|
||||
if (! $this->setKeyInEnvironmentFile($key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->laravel['config']['app.key'] = $key;
|
||||
|
||||
$this->components->info('Application key set successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random key for the application.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generateRandomKey()
|
||||
{
|
||||
return 'base64:'.base64_encode(
|
||||
Encrypter::generateKey($this->laravel['config']['app.cipher'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the application key in the environment file.
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
protected function setKeyInEnvironmentFile($key)
|
||||
{
|
||||
$currentKey = $this->laravel['config']['app.key'];
|
||||
|
||||
if (strlen($currentKey) !== 0 && (! $this->confirmToProceed())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->writeNewEnvironmentFileWith($key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a new environment file with the given key.
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
protected function writeNewEnvironmentFileWith($key)
|
||||
{
|
||||
$replaced = preg_replace(
|
||||
$this->keyReplacementPattern(),
|
||||
'APP_KEY='.$key,
|
||||
$input = file_get_contents($this->laravel->environmentFilePath())
|
||||
);
|
||||
|
||||
if ($replaced === $input || $replaced === null) {
|
||||
$this->error('Unable to set application key. No APP_KEY variable was found in the .env file.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
file_put_contents($this->laravel->environmentFilePath(), $replaced);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a regex pattern that will match env APP_KEY with any random key.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function keyReplacementPattern()
|
||||
{
|
||||
$escaped = preg_quote('='.$this->laravel['config']['app.key'], '/');
|
||||
|
||||
return "/^APP_KEY{$escaped}/m";
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'lang:publish')]
|
||||
class LangPublishCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'lang:publish
|
||||
{--existing : Publish and overwrite only the files that have already been published}
|
||||
{--force : Overwrite any existing files}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Publish all language files that are available for customization';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (! is_dir($langPath = $this->laravel->basePath('lang/en'))) {
|
||||
(new Filesystem)->makeDirectory($langPath, recursive: true);
|
||||
}
|
||||
|
||||
$stubs = [
|
||||
realpath(__DIR__.'/../../Translation/lang/en/auth.php') => 'auth.php',
|
||||
realpath(__DIR__.'/../../Translation/lang/en/pagination.php') => 'pagination.php',
|
||||
realpath(__DIR__.'/../../Translation/lang/en/passwords.php') => 'passwords.php',
|
||||
realpath(__DIR__.'/../../Translation/lang/en/validation.php') => 'validation.php',
|
||||
];
|
||||
|
||||
foreach ($stubs as $from => $to) {
|
||||
$to = $langPath.DIRECTORY_SEPARATOR.ltrim($to, DIRECTORY_SEPARATOR);
|
||||
|
||||
if ((! $this->option('existing') && (! file_exists($to) || $this->option('force')))
|
||||
|| ($this->option('existing') && file_exists($to))) {
|
||||
file_put_contents($to, file_get_contents($from));
|
||||
}
|
||||
}
|
||||
|
||||
$this->components->info('Language files published successfully.');
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Concerns\CreatesMatchingTest;
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
use function Laravel\Prompts\suggest;
|
||||
|
||||
#[AsCommand(name: 'make:listener')]
|
||||
class ListenerMakeCommand extends GeneratorCommand
|
||||
{
|
||||
use CreatesMatchingTest;
|
||||
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:listener';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new event listener class';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Listener';
|
||||
|
||||
/**
|
||||
* Build the class with the given name.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
protected function buildClass($name)
|
||||
{
|
||||
$event = $this->option('event') ?? '';
|
||||
|
||||
if (! Str::startsWith($event, [
|
||||
$this->laravel->getNamespace(),
|
||||
'Illuminate',
|
||||
'\\',
|
||||
])) {
|
||||
$event = $this->laravel->getNamespace().'Events\\'.str_replace('/', '\\', $event);
|
||||
}
|
||||
|
||||
$stub = str_replace(
|
||||
['DummyEvent', '{{ event }}'], class_basename($event), parent::buildClass($name)
|
||||
);
|
||||
|
||||
return str_replace(
|
||||
['DummyFullEvent', '{{ eventNamespace }}'], trim($event, '\\'), $stub
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
if ($this->option('queued')) {
|
||||
return $this->option('event')
|
||||
? $this->resolveStubPath('/stubs/listener.typed.queued.stub')
|
||||
: $this->resolveStubPath('/stubs/listener.queued.stub');
|
||||
}
|
||||
|
||||
return $this->option('event')
|
||||
? $this->resolveStubPath('/stubs/listener.typed.stub')
|
||||
: $this->resolveStubPath('/stubs/listener.stub');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the class already exists.
|
||||
*
|
||||
* @param string $rawName
|
||||
* @return bool
|
||||
*/
|
||||
protected function alreadyExists($rawName)
|
||||
{
|
||||
return class_exists($rawName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return $rootNamespace.'\Listeners';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['event', 'e', InputOption::VALUE_OPTIONAL, 'The event class being listened for'],
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the listener already exists'],
|
||||
['queued', null, InputOption::VALUE_NONE, 'Indicates the event listener should be queued'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Interact further with the user if they were prompted for missing arguments.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return void
|
||||
*/
|
||||
protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if ($this->isReservedName($this->getNameInput()) || $this->didReceiveOptions($input)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$event = suggest(
|
||||
'What event should be listened for? (Optional)',
|
||||
$this->possibleEvents(),
|
||||
);
|
||||
|
||||
if ($event) {
|
||||
$input->setOption('event', $event);
|
||||
}
|
||||
}
|
||||
}
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Concerns\CreatesMatchingTest;
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Illuminate\Foundation\Inspiring;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
use function Laravel\Prompts\select;
|
||||
|
||||
#[AsCommand(name: 'make:mail')]
|
||||
class MailMakeCommand extends GeneratorCommand
|
||||
{
|
||||
use CreatesMatchingTest;
|
||||
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:mail';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new email class';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Mailable';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (parent::handle() === false && ! $this->option('force')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->option('markdown') !== false) {
|
||||
$this->writeMarkdownTemplate();
|
||||
}
|
||||
|
||||
if ($this->option('view') !== false) {
|
||||
$this->writeView();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the Markdown template for the mailable.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function writeMarkdownTemplate()
|
||||
{
|
||||
$path = $this->viewPath(
|
||||
str_replace('.', '/', $this->getView()).'.blade.php'
|
||||
);
|
||||
|
||||
$this->files->ensureDirectoryExists(dirname($path));
|
||||
|
||||
$this->files->put($path, file_get_contents(__DIR__.'/stubs/markdown.stub'));
|
||||
|
||||
$this->components->info(sprintf('%s [%s] created successfully.', 'Markdown view', $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the Blade template for the mailable.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function writeView()
|
||||
{
|
||||
$path = $this->viewPath(
|
||||
str_replace('.', '/', $this->getView()).'.blade.php'
|
||||
);
|
||||
|
||||
$this->files->ensureDirectoryExists(dirname($path));
|
||||
|
||||
$stub = str_replace(
|
||||
'{{ quote }}',
|
||||
Inspiring::quotes()->random(),
|
||||
file_get_contents(__DIR__.'/stubs/view.stub')
|
||||
);
|
||||
|
||||
$this->files->put($path, $stub);
|
||||
|
||||
$this->components->info(sprintf('%s [%s] created successfully.', 'View', $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the class with the given name.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
protected function buildClass($name)
|
||||
{
|
||||
$class = str_replace(
|
||||
'{{ subject }}',
|
||||
Str::headline(str_replace($this->getNamespace($name).'\\', '', $name)),
|
||||
parent::buildClass($name)
|
||||
);
|
||||
|
||||
if ($this->option('markdown') !== false || $this->option('view') !== false) {
|
||||
$class = str_replace(['DummyView', '{{ view }}'], $this->getView(), $class);
|
||||
}
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getView()
|
||||
{
|
||||
$view = $this->option('markdown') ?: $this->option('view');
|
||||
|
||||
if (! $view) {
|
||||
$name = str_replace('\\', '/', $this->argument('name'));
|
||||
|
||||
$view = 'mail.'.collect(explode('/', $name))
|
||||
->map(fn ($part) => Str::kebab($part))
|
||||
->implode('.');
|
||||
}
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
if ($this->option('markdown') !== false) {
|
||||
return $this->resolveStubPath('/stubs/markdown-mail.stub');
|
||||
}
|
||||
|
||||
if ($this->option('view') !== false) {
|
||||
return $this->resolveStubPath('/stubs/view-mail.stub');
|
||||
}
|
||||
|
||||
return $this->resolveStubPath('/stubs/mail.stub');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return $rootNamespace.'\Mail';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the mailable already exists'],
|
||||
['markdown', 'm', InputOption::VALUE_OPTIONAL, 'Create a new Markdown template for the mailable', false],
|
||||
['view', null, InputOption::VALUE_OPTIONAL, 'Create a new Blade template for the mailable', false],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Interact further with the user if they were prompted for missing arguments.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return void
|
||||
*/
|
||||
protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if ($this->didReceiveOptions($input)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$type = select('Would you like to create a view?', [
|
||||
'markdown' => 'Markdown View',
|
||||
'view' => 'Empty View',
|
||||
'none' => 'No View',
|
||||
]);
|
||||
|
||||
if ($type !== 'none') {
|
||||
$input->setOption($type, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Concerns\CreatesMatchingTest;
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
use function Laravel\Prompts\multiselect;
|
||||
|
||||
#[AsCommand(name: 'make:model')]
|
||||
class ModelMakeCommand extends GeneratorCommand
|
||||
{
|
||||
use CreatesMatchingTest;
|
||||
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:model';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new Eloquent model class';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Model';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (parent::handle() === false && ! $this->option('force')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->option('all')) {
|
||||
$this->input->setOption('factory', true);
|
||||
$this->input->setOption('seed', true);
|
||||
$this->input->setOption('migration', true);
|
||||
$this->input->setOption('controller', true);
|
||||
$this->input->setOption('policy', true);
|
||||
$this->input->setOption('resource', true);
|
||||
}
|
||||
|
||||
if ($this->option('factory')) {
|
||||
$this->createFactory();
|
||||
}
|
||||
|
||||
if ($this->option('migration')) {
|
||||
$this->createMigration();
|
||||
}
|
||||
|
||||
if ($this->option('seed')) {
|
||||
$this->createSeeder();
|
||||
}
|
||||
|
||||
if ($this->option('controller') || $this->option('resource') || $this->option('api')) {
|
||||
$this->createController();
|
||||
}
|
||||
|
||||
if ($this->option('policy')) {
|
||||
$this->createPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a model factory for the model.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function createFactory()
|
||||
{
|
||||
$factory = Str::studly($this->argument('name'));
|
||||
|
||||
$this->call('make:factory', [
|
||||
'name' => "{$factory}Factory",
|
||||
'--model' => $this->qualifyClass($this->getNameInput()),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a migration file for the model.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function createMigration()
|
||||
{
|
||||
$table = Str::snake(Str::pluralStudly(class_basename($this->argument('name'))));
|
||||
|
||||
if ($this->option('pivot')) {
|
||||
$table = Str::singular($table);
|
||||
}
|
||||
|
||||
$this->call('make:migration', [
|
||||
'name' => "create_{$table}_table",
|
||||
'--create' => $table,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a seeder file for the model.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function createSeeder()
|
||||
{
|
||||
$seeder = Str::studly(class_basename($this->argument('name')));
|
||||
|
||||
$this->call('make:seeder', [
|
||||
'name' => "{$seeder}Seeder",
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a controller for the model.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function createController()
|
||||
{
|
||||
$controller = Str::studly(class_basename($this->argument('name')));
|
||||
|
||||
$modelName = $this->qualifyClass($this->getNameInput());
|
||||
|
||||
$this->call('make:controller', array_filter([
|
||||
'name' => "{$controller}Controller",
|
||||
'--model' => $this->option('resource') || $this->option('api') ? $modelName : null,
|
||||
'--api' => $this->option('api'),
|
||||
'--requests' => $this->option('requests') || $this->option('all'),
|
||||
'--test' => $this->option('test'),
|
||||
'--pest' => $this->option('pest'),
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a policy file for the model.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function createPolicy()
|
||||
{
|
||||
$policy = Str::studly(class_basename($this->argument('name')));
|
||||
|
||||
$this->call('make:policy', [
|
||||
'name' => "{$policy}Policy",
|
||||
'--model' => $this->qualifyClass($this->getNameInput()),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
if ($this->option('pivot')) {
|
||||
return $this->resolveStubPath('/stubs/model.pivot.stub');
|
||||
}
|
||||
|
||||
if ($this->option('morph-pivot')) {
|
||||
return $this->resolveStubPath('/stubs/model.morph-pivot.stub');
|
||||
}
|
||||
|
||||
return $this->resolveStubPath('/stubs/model.stub');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return is_dir(app_path('Models')) ? $rootNamespace.'\\Models' : $rootNamespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['all', 'a', InputOption::VALUE_NONE, 'Generate a migration, seeder, factory, policy, resource controller, and form request classes for the model'],
|
||||
['controller', 'c', InputOption::VALUE_NONE, 'Create a new controller for the model'],
|
||||
['factory', 'f', InputOption::VALUE_NONE, 'Create a new factory for the model'],
|
||||
['force', null, InputOption::VALUE_NONE, 'Create the class even if the model already exists'],
|
||||
['migration', 'm', InputOption::VALUE_NONE, 'Create a new migration file for the model'],
|
||||
['morph-pivot', null, InputOption::VALUE_NONE, 'Indicates if the generated model should be a custom polymorphic intermediate table model'],
|
||||
['policy', null, InputOption::VALUE_NONE, 'Create a new policy for the model'],
|
||||
['seed', 's', InputOption::VALUE_NONE, 'Create a new seeder for the model'],
|
||||
['pivot', 'p', InputOption::VALUE_NONE, 'Indicates if the generated model should be a custom intermediate table model'],
|
||||
['resource', 'r', InputOption::VALUE_NONE, 'Indicates if the generated controller should be a resource controller'],
|
||||
['api', null, InputOption::VALUE_NONE, 'Indicates if the generated controller should be an API resource controller'],
|
||||
['requests', 'R', InputOption::VALUE_NONE, 'Create new form request classes and use them in the resource controller'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Interact further with the user if they were prompted for missing arguments.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return void
|
||||
*/
|
||||
protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if ($this->isReservedName($this->getNameInput()) || $this->didReceiveOptions($input)) {
|
||||
return;
|
||||
}
|
||||
|
||||
collect(multiselect('Would you like any of the following?', [
|
||||
'seed' => 'Database Seeder',
|
||||
'factory' => 'Factory',
|
||||
'requests' => 'Form Requests',
|
||||
'migration' => 'Migration',
|
||||
'policy' => 'Policy',
|
||||
'resource' => 'Resource Controller',
|
||||
]))->each(fn ($option) => $input->setOption($option, true));
|
||||
}
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Concerns\CreatesMatchingTest;
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand(name: 'make:notification')]
|
||||
class NotificationMakeCommand extends GeneratorCommand
|
||||
{
|
||||
use CreatesMatchingTest;
|
||||
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:notification';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new notification class';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Notification';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (parent::handle() === false && ! $this->option('force')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->option('markdown')) {
|
||||
$this->writeMarkdownTemplate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the Markdown template for the mailable.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function writeMarkdownTemplate()
|
||||
{
|
||||
$path = $this->viewPath(
|
||||
str_replace('.', '/', $this->option('markdown')).'.blade.php'
|
||||
);
|
||||
|
||||
if (! $this->files->isDirectory(dirname($path))) {
|
||||
$this->files->makeDirectory(dirname($path), 0755, true);
|
||||
}
|
||||
|
||||
$this->files->put($path, file_get_contents(__DIR__.'/stubs/markdown.stub'));
|
||||
|
||||
$this->components->info(sprintf('%s [%s] created successfully.', 'Markdown', $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the class with the given name.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
protected function buildClass($name)
|
||||
{
|
||||
$class = parent::buildClass($name);
|
||||
|
||||
if ($this->option('markdown')) {
|
||||
$class = str_replace(['DummyView', '{{ view }}'], $this->option('markdown'), $class);
|
||||
}
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
return $this->option('markdown')
|
||||
? $this->resolveStubPath('/stubs/markdown-notification.stub')
|
||||
: $this->resolveStubPath('/stubs/notification.stub');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return $rootNamespace.'\Notifications';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the notification already exists'],
|
||||
['markdown', 'm', InputOption::VALUE_OPTIONAL, 'Create a new Markdown template for the notification'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use InvalidArgumentException;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
use function Laravel\Prompts\suggest;
|
||||
|
||||
#[AsCommand(name: 'make:observer')]
|
||||
class ObserverMakeCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:observer';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new observer class';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Observer';
|
||||
|
||||
/**
|
||||
* Build the class with the given name.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
protected function buildClass($name)
|
||||
{
|
||||
$stub = parent::buildClass($name);
|
||||
|
||||
$model = $this->option('model');
|
||||
|
||||
return $model ? $this->replaceModel($stub, $model) : $stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the model for the given stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @param string $model
|
||||
* @return string
|
||||
*/
|
||||
protected function replaceModel($stub, $model)
|
||||
{
|
||||
$modelClass = $this->parseModel($model);
|
||||
|
||||
$replace = [
|
||||
'DummyFullModelClass' => $modelClass,
|
||||
'{{ namespacedModel }}' => $modelClass,
|
||||
'{{namespacedModel}}' => $modelClass,
|
||||
'DummyModelClass' => class_basename($modelClass),
|
||||
'{{ model }}' => class_basename($modelClass),
|
||||
'{{model}}' => class_basename($modelClass),
|
||||
'DummyModelVariable' => lcfirst(class_basename($modelClass)),
|
||||
'{{ modelVariable }}' => lcfirst(class_basename($modelClass)),
|
||||
'{{modelVariable}}' => lcfirst(class_basename($modelClass)),
|
||||
];
|
||||
|
||||
return str_replace(
|
||||
array_keys($replace), array_values($replace), $stub
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fully-qualified model class name.
|
||||
*
|
||||
* @param string $model
|
||||
* @return string
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function parseModel($model)
|
||||
{
|
||||
if (preg_match('([^A-Za-z0-9_/\\\\])', $model)) {
|
||||
throw new InvalidArgumentException('Model name contains invalid characters.');
|
||||
}
|
||||
|
||||
return $this->qualifyModel($model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
return $this->option('model')
|
||||
? $this->resolveStubPath('/stubs/observer.stub')
|
||||
: $this->resolveStubPath('/stubs/observer.plain.stub');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return $rootNamespace.'\Observers';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the observer already exists'],
|
||||
['model', 'm', InputOption::VALUE_OPTIONAL, 'The model that the observer applies to'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Interact further with the user if they were prompted for missing arguments.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return void
|
||||
*/
|
||||
protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if ($this->isReservedName($this->getNameInput()) || $this->didReceiveOptions($input)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$model = suggest(
|
||||
'What model should this observer apply to? (Optional)',
|
||||
$this->possibleModels(),
|
||||
);
|
||||
|
||||
if ($model) {
|
||||
$input->setOption('model', $model);
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'optimize:clear')]
|
||||
class OptimizeClearCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'optimize:clear';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Remove the cached bootstrap files';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->components->info('Clearing cached bootstrap files.');
|
||||
|
||||
collect([
|
||||
'cache' => fn () => $this->callSilent('cache:clear') == 0,
|
||||
'compiled' => fn () => $this->callSilent('clear-compiled') == 0,
|
||||
'config' => fn () => $this->callSilent('config:clear') == 0,
|
||||
'events' => fn () => $this->callSilent('event:clear') == 0,
|
||||
'routes' => fn () => $this->callSilent('route:clear') == 0,
|
||||
'views' => fn () => $this->callSilent('view:clear') == 0,
|
||||
])->each(fn ($task, $description) => $this->components->task($description, $task));
|
||||
|
||||
$this->newLine();
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'optimize')]
|
||||
class OptimizeCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'optimize';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Cache framework bootstrap, configuration, and metadata to increase performance';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->components->info('Caching framework bootstrap, configuration, and metadata.');
|
||||
|
||||
collect([
|
||||
'config' => fn () => $this->callSilent('config:cache') == 0,
|
||||
'events' => fn () => $this->callSilent('event:cache') == 0,
|
||||
'routes' => fn () => $this->callSilent('route:cache') == 0,
|
||||
'views' => fn () => $this->callSilent('view:cache') == 0,
|
||||
])->each(fn ($task, $description) => $this->components->task($description, $task));
|
||||
|
||||
$this->newLine();
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Foundation\PackageManifest;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'package:discover')]
|
||||
class PackageDiscoverCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command signature.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'package:discover';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Rebuild the cached package manifest';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @param \Illuminate\Foundation\PackageManifest $manifest
|
||||
* @return void
|
||||
*/
|
||||
public function handle(PackageManifest $manifest)
|
||||
{
|
||||
$this->components->info('Discovering packages');
|
||||
|
||||
$manifest->build();
|
||||
|
||||
collect($manifest->manifest)
|
||||
->keys()
|
||||
->each(fn ($description) => $this->components->task($description))
|
||||
->whenNotEmpty(fn () => $this->newLine());
|
||||
}
|
||||
}
|
||||
+228
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Illuminate\Support\Str;
|
||||
use LogicException;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
use function Laravel\Prompts\suggest;
|
||||
|
||||
#[AsCommand(name: 'make:policy')]
|
||||
class PolicyMakeCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:policy';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new policy class';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Policy';
|
||||
|
||||
/**
|
||||
* Build the class with the given name.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
protected function buildClass($name)
|
||||
{
|
||||
$stub = $this->replaceUserNamespace(
|
||||
parent::buildClass($name)
|
||||
);
|
||||
|
||||
$model = $this->option('model');
|
||||
|
||||
return $model ? $this->replaceModel($stub, $model) : $stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the User model namespace.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function replaceUserNamespace($stub)
|
||||
{
|
||||
$model = $this->userProviderModel();
|
||||
|
||||
if (! $model) {
|
||||
return $stub;
|
||||
}
|
||||
|
||||
return str_replace(
|
||||
$this->rootNamespace().'User',
|
||||
$model,
|
||||
$stub
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model for the guard's user provider.
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
protected function userProviderModel()
|
||||
{
|
||||
$config = $this->laravel['config'];
|
||||
|
||||
$guard = $this->option('guard') ?: $config->get('auth.defaults.guard');
|
||||
|
||||
if (is_null($guardProvider = $config->get('auth.guards.'.$guard.'.provider'))) {
|
||||
throw new LogicException('The ['.$guard.'] guard is not defined in your "auth" configuration file.');
|
||||
}
|
||||
|
||||
if (! $config->get('auth.providers.'.$guardProvider.'.model')) {
|
||||
return 'App\\Models\\User';
|
||||
}
|
||||
|
||||
return $config->get(
|
||||
'auth.providers.'.$guardProvider.'.model'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the model for the given stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @param string $model
|
||||
* @return string
|
||||
*/
|
||||
protected function replaceModel($stub, $model)
|
||||
{
|
||||
$model = str_replace('/', '\\', $model);
|
||||
|
||||
if (str_starts_with($model, '\\')) {
|
||||
$namespacedModel = trim($model, '\\');
|
||||
} else {
|
||||
$namespacedModel = $this->qualifyModel($model);
|
||||
}
|
||||
|
||||
$model = class_basename(trim($model, '\\'));
|
||||
|
||||
$dummyUser = class_basename($this->userProviderModel());
|
||||
|
||||
$dummyModel = Str::camel($model) === 'user' ? 'model' : $model;
|
||||
|
||||
$replace = [
|
||||
'NamespacedDummyModel' => $namespacedModel,
|
||||
'{{ namespacedModel }}' => $namespacedModel,
|
||||
'{{namespacedModel}}' => $namespacedModel,
|
||||
'DummyModel' => $model,
|
||||
'{{ model }}' => $model,
|
||||
'{{model}}' => $model,
|
||||
'dummyModel' => Str::camel($dummyModel),
|
||||
'{{ modelVariable }}' => Str::camel($dummyModel),
|
||||
'{{modelVariable}}' => Str::camel($dummyModel),
|
||||
'DummyUser' => $dummyUser,
|
||||
'{{ user }}' => $dummyUser,
|
||||
'{{user}}' => $dummyUser,
|
||||
'$user' => '$'.Str::camel($dummyUser),
|
||||
];
|
||||
|
||||
$stub = str_replace(
|
||||
array_keys($replace), array_values($replace), $stub
|
||||
);
|
||||
|
||||
return preg_replace(
|
||||
vsprintf('/use %s;[\r\n]+use %s;/', [
|
||||
preg_quote($namespacedModel, '/'),
|
||||
preg_quote($namespacedModel, '/'),
|
||||
]),
|
||||
"use {$namespacedModel};",
|
||||
$stub
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
return $this->option('model')
|
||||
? $this->resolveStubPath('/stubs/policy.stub')
|
||||
: $this->resolveStubPath('/stubs/policy.plain.stub');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return $rootNamespace.'\Policies';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the policy already exists'],
|
||||
['model', 'm', InputOption::VALUE_OPTIONAL, 'The model that the policy applies to'],
|
||||
['guard', 'g', InputOption::VALUE_OPTIONAL, 'The guard that the policy relies on'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Interact further with the user if they were prompted for missing arguments.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return void
|
||||
*/
|
||||
protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if ($this->isReservedName($this->getNameInput()) || $this->didReceiveOptions($input)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$model = suggest(
|
||||
'What model should this policy apply to? (Optional)',
|
||||
$this->possibleModels(),
|
||||
);
|
||||
|
||||
if ($model) {
|
||||
$input->setOption('model', $model);
|
||||
}
|
||||
}
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand(name: 'make:provider')]
|
||||
class ProviderMakeCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:provider';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new service provider class';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Provider';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return bool|null
|
||||
*
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$result = parent::handle();
|
||||
|
||||
if ($result === false) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
ServiceProvider::addProviderToBootstrapFile(
|
||||
$this->qualifyClass($this->getNameInput()),
|
||||
$this->laravel->getBootstrapProvidersPath(),
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
return $this->resolveStubPath('/stubs/provider.stub');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return $rootNamespace.'\Providers';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the provider already exists'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Console\Kernel as KernelContract;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
|
||||
class QueuedCommand implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, Queueable;
|
||||
|
||||
/**
|
||||
* The data to pass to the Artisan command.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the job.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Console\Kernel $kernel
|
||||
* @return void
|
||||
*/
|
||||
public function handle(KernelContract $kernel)
|
||||
{
|
||||
$kernel->call(...array_values($this->data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the display name for the queued job.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function displayName()
|
||||
{
|
||||
return array_values($this->data)[0];
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand(name: 'make:request')]
|
||||
class RequestMakeCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:request';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new form request class';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Request';
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
return $this->resolveStubPath('/stubs/request.stub');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return $rootNamespace.'\Http\Requests';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the request already exists'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand(name: 'make:resource')]
|
||||
class ResourceMakeCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:resource';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new resource';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Resource';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if ($this->collection()) {
|
||||
$this->type = 'Resource collection';
|
||||
}
|
||||
|
||||
parent::handle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
return $this->collection()
|
||||
? $this->resolveStubPath('/stubs/resource-collection.stub')
|
||||
: $this->resolveStubPath('/stubs/resource.stub');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the command is generating a resource collection.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function collection()
|
||||
{
|
||||
return $this->option('collection') ||
|
||||
str_ends_with($this->argument('name'), 'Collection');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return $rootNamespace.'\Http\Resources';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the resource already exists'],
|
||||
['collection', 'c', InputOption::VALUE_NONE, 'Create a resource collection'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Console\Kernel as ConsoleKernelContract;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Routing\RouteCollection;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'route:cache')]
|
||||
class RouteCacheCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'route:cache';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a route cache file for faster route registration';
|
||||
|
||||
/**
|
||||
* The filesystem instance.
|
||||
*
|
||||
* @var \Illuminate\Filesystem\Filesystem
|
||||
*/
|
||||
protected $files;
|
||||
|
||||
/**
|
||||
* Create a new route command instance.
|
||||
*
|
||||
* @param \Illuminate\Filesystem\Filesystem $files
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Filesystem $files)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->files = $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->callSilent('route:clear');
|
||||
|
||||
$routes = $this->getFreshApplicationRoutes();
|
||||
|
||||
if (count($routes) === 0) {
|
||||
return $this->components->error("Your application doesn't have any routes.");
|
||||
}
|
||||
|
||||
foreach ($routes as $route) {
|
||||
$route->prepareForSerialization();
|
||||
}
|
||||
|
||||
$this->files->put(
|
||||
$this->laravel->getCachedRoutesPath(), $this->buildRouteCacheFile($routes)
|
||||
);
|
||||
|
||||
$this->components->info('Routes cached successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Boot a fresh copy of the application and get the routes.
|
||||
*
|
||||
* @return \Illuminate\Routing\RouteCollection
|
||||
*/
|
||||
protected function getFreshApplicationRoutes()
|
||||
{
|
||||
return tap($this->getFreshApplication()['router']->getRoutes(), function ($routes) {
|
||||
$routes->refreshNameLookups();
|
||||
$routes->refreshActionLookups();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a fresh application instance.
|
||||
*
|
||||
* @return \Illuminate\Contracts\Foundation\Application
|
||||
*/
|
||||
protected function getFreshApplication()
|
||||
{
|
||||
return tap(require $this->laravel->bootstrapPath('app.php'), function ($app) {
|
||||
$app->make(ConsoleKernelContract::class)->bootstrap();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the route cache file.
|
||||
*
|
||||
* @param \Illuminate\Routing\RouteCollection $routes
|
||||
* @return string
|
||||
*/
|
||||
protected function buildRouteCacheFile(RouteCollection $routes)
|
||||
{
|
||||
$stub = $this->files->get(__DIR__.'/stubs/routes.stub');
|
||||
|
||||
return str_replace('{{routes}}', var_export($routes->compile(), true), $stub);
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'route:clear')]
|
||||
class RouteClearCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'route:clear';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Remove the route cache file';
|
||||
|
||||
/**
|
||||
* The filesystem instance.
|
||||
*
|
||||
* @var \Illuminate\Filesystem\Filesystem
|
||||
*/
|
||||
protected $files;
|
||||
|
||||
/**
|
||||
* Create a new route clear command instance.
|
||||
*
|
||||
* @param \Illuminate\Filesystem\Filesystem $files
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Filesystem $files)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->files = $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->files->delete($this->laravel->getCachedRoutesPath());
|
||||
|
||||
$this->components->info('Route cache cleared successfully.');
|
||||
}
|
||||
}
|
||||
+503
@@ -0,0 +1,503 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Routing\UrlGenerator;
|
||||
use Illuminate\Routing\Route;
|
||||
use Illuminate\Routing\Router;
|
||||
use Illuminate\Routing\ViewController;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use ReflectionClass;
|
||||
use ReflectionFunction;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Terminal;
|
||||
|
||||
#[AsCommand(name: 'route:list')]
|
||||
class RouteListCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'route:list';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'List all registered routes';
|
||||
|
||||
/**
|
||||
* The router instance.
|
||||
*
|
||||
* @var \Illuminate\Routing\Router
|
||||
*/
|
||||
protected $router;
|
||||
|
||||
/**
|
||||
* The table headers for the command.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $headers = ['Domain', 'Method', 'URI', 'Name', 'Action', 'Middleware'];
|
||||
|
||||
/**
|
||||
* The terminal width resolver callback.
|
||||
*
|
||||
* @var \Closure|null
|
||||
*/
|
||||
protected static $terminalWidthResolver;
|
||||
|
||||
/**
|
||||
* The verb colors for the command.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $verbColors = [
|
||||
'ANY' => 'red',
|
||||
'GET' => 'blue',
|
||||
'HEAD' => '#6C7280',
|
||||
'OPTIONS' => '#6C7280',
|
||||
'POST' => 'yellow',
|
||||
'PUT' => 'yellow',
|
||||
'PATCH' => 'yellow',
|
||||
'DELETE' => 'red',
|
||||
];
|
||||
|
||||
/**
|
||||
* Create a new route command instance.
|
||||
*
|
||||
* @param \Illuminate\Routing\Router $router
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Router $router)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (! $this->output->isVeryVerbose()) {
|
||||
$this->router->flushMiddlewareGroups();
|
||||
}
|
||||
|
||||
if (! $this->router->getRoutes()->count()) {
|
||||
return $this->components->error("Your application doesn't have any routes.");
|
||||
}
|
||||
|
||||
if (empty($routes = $this->getRoutes())) {
|
||||
return $this->components->error("Your application doesn't have any routes matching the given criteria.");
|
||||
}
|
||||
|
||||
$this->displayRoutes($routes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the routes into a displayable format.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getRoutes()
|
||||
{
|
||||
$routes = collect($this->router->getRoutes())->map(function ($route) {
|
||||
return $this->getRouteInformation($route);
|
||||
})->filter()->all();
|
||||
|
||||
if (($sort = $this->option('sort')) !== null) {
|
||||
$routes = $this->sortRoutes($sort, $routes);
|
||||
} else {
|
||||
$routes = $this->sortRoutes('uri', $routes);
|
||||
}
|
||||
|
||||
if ($this->option('reverse')) {
|
||||
$routes = array_reverse($routes);
|
||||
}
|
||||
|
||||
return $this->pluckColumns($routes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the route information for a given route.
|
||||
*
|
||||
* @param \Illuminate\Routing\Route $route
|
||||
* @return array
|
||||
*/
|
||||
protected function getRouteInformation(Route $route)
|
||||
{
|
||||
return $this->filterRoute([
|
||||
'domain' => $route->domain(),
|
||||
'method' => implode('|', $route->methods()),
|
||||
'uri' => $route->uri(),
|
||||
'name' => $route->getName(),
|
||||
'action' => ltrim($route->getActionName(), '\\'),
|
||||
'middleware' => $this->getMiddleware($route),
|
||||
'vendor' => $this->isVendorRoute($route),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the routes by a given element.
|
||||
*
|
||||
* @param string $sort
|
||||
* @param array $routes
|
||||
* @return array
|
||||
*/
|
||||
protected function sortRoutes($sort, array $routes)
|
||||
{
|
||||
if (Str::contains($sort, ',')) {
|
||||
$sort = explode(',', $sort);
|
||||
}
|
||||
|
||||
return collect($routes)
|
||||
->sortBy($sort)
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unnecessary columns from the routes.
|
||||
*
|
||||
* @param array $routes
|
||||
* @return array
|
||||
*/
|
||||
protected function pluckColumns(array $routes)
|
||||
{
|
||||
return array_map(function ($route) {
|
||||
return Arr::only($route, $this->getColumns());
|
||||
}, $routes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the route information on the console.
|
||||
*
|
||||
* @param array $routes
|
||||
* @return void
|
||||
*/
|
||||
protected function displayRoutes(array $routes)
|
||||
{
|
||||
$routes = collect($routes);
|
||||
|
||||
$this->output->writeln(
|
||||
$this->option('json') ? $this->asJson($routes) : $this->forCli($routes)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the middleware for the route.
|
||||
*
|
||||
* @param \Illuminate\Routing\Route $route
|
||||
* @return string
|
||||
*/
|
||||
protected function getMiddleware($route)
|
||||
{
|
||||
return collect($this->router->gatherRouteMiddleware($route))->map(function ($middleware) {
|
||||
return $middleware instanceof Closure ? 'Closure' : $middleware;
|
||||
})->implode("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the route has been defined outside of the application.
|
||||
*
|
||||
* @param \Illuminate\Routing\Route $route
|
||||
* @return bool
|
||||
*/
|
||||
protected function isVendorRoute(Route $route)
|
||||
{
|
||||
if ($route->action['uses'] instanceof Closure) {
|
||||
$path = (new ReflectionFunction($route->action['uses']))
|
||||
->getFileName();
|
||||
} elseif (is_string($route->action['uses']) &&
|
||||
str_contains($route->action['uses'], 'SerializableClosure')) {
|
||||
return false;
|
||||
} elseif (is_string($route->action['uses'])) {
|
||||
if ($this->isFrameworkController($route)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = (new ReflectionClass($route->getControllerClass()))
|
||||
->getFileName();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return str_starts_with($path, base_path('vendor'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the route uses a framework controller.
|
||||
*
|
||||
* @param \Illuminate\Routing\Route $route
|
||||
* @return bool
|
||||
*/
|
||||
protected function isFrameworkController(Route $route)
|
||||
{
|
||||
return in_array($route->getControllerClass(), [
|
||||
'\Illuminate\Routing\RedirectController',
|
||||
'\Illuminate\Routing\ViewController',
|
||||
], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the route by URI and / or name.
|
||||
*
|
||||
* @param array $route
|
||||
* @return array|null
|
||||
*/
|
||||
protected function filterRoute(array $route)
|
||||
{
|
||||
if (($this->option('name') && ! Str::contains((string) $route['name'], $this->option('name'))) ||
|
||||
($this->option('path') && ! Str::contains($route['uri'], $this->option('path'))) ||
|
||||
($this->option('method') && ! Str::contains($route['method'], strtoupper($this->option('method')))) ||
|
||||
($this->option('domain') && ! Str::contains((string) $route['domain'], $this->option('domain'))) ||
|
||||
($this->option('except-vendor') && $route['vendor']) ||
|
||||
($this->option('only-vendor') && ! $route['vendor'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->option('except-path')) {
|
||||
foreach (explode(',', $this->option('except-path')) as $path) {
|
||||
if (str_contains($route['uri'], $path)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the table headers for the visible columns.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getHeaders()
|
||||
{
|
||||
return Arr::only($this->headers, array_keys($this->getColumns()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the column names to show (lowercase table headers).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getColumns()
|
||||
{
|
||||
return array_map('strtolower', $this->headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the column list.
|
||||
*
|
||||
* @param array $columns
|
||||
* @return array
|
||||
*/
|
||||
protected function parseColumns(array $columns)
|
||||
{
|
||||
$results = [];
|
||||
|
||||
foreach ($columns as $column) {
|
||||
if (str_contains($column, ',')) {
|
||||
$results = array_merge($results, explode(',', $column));
|
||||
} else {
|
||||
$results[] = $column;
|
||||
}
|
||||
}
|
||||
|
||||
return array_map('strtolower', $results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given routes to JSON.
|
||||
*
|
||||
* @param \Illuminate\Support\Collection $routes
|
||||
* @return string
|
||||
*/
|
||||
protected function asJson($routes)
|
||||
{
|
||||
return $routes
|
||||
->map(function ($route) {
|
||||
$route['middleware'] = empty($route['middleware']) ? [] : explode("\n", $route['middleware']);
|
||||
|
||||
return $route;
|
||||
})
|
||||
->values()
|
||||
->toJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given routes to regular CLI output.
|
||||
*
|
||||
* @param \Illuminate\Support\Collection $routes
|
||||
* @return array
|
||||
*/
|
||||
protected function forCli($routes)
|
||||
{
|
||||
$routes = $routes->map(
|
||||
fn ($route) => array_merge($route, [
|
||||
'action' => $this->formatActionForCli($route),
|
||||
'method' => $route['method'] == 'GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS' ? 'ANY' : $route['method'],
|
||||
'uri' => $route['domain'] ? ($route['domain'].'/'.ltrim($route['uri'], '/')) : $route['uri'],
|
||||
]),
|
||||
);
|
||||
|
||||
$maxMethod = mb_strlen($routes->max('method'));
|
||||
|
||||
$terminalWidth = $this->getTerminalWidth();
|
||||
|
||||
$routeCount = $this->determineRouteCountOutput($routes, $terminalWidth);
|
||||
|
||||
return $routes->map(function ($route) use ($maxMethod, $terminalWidth) {
|
||||
[
|
||||
'action' => $action,
|
||||
'domain' => $domain,
|
||||
'method' => $method,
|
||||
'middleware' => $middleware,
|
||||
'uri' => $uri,
|
||||
] = $route;
|
||||
|
||||
$middleware = Str::of($middleware)->explode("\n")->filter()->whenNotEmpty(
|
||||
fn ($collection) => $collection->map(
|
||||
fn ($middleware) => sprintf(' %s⇂ %s', str_repeat(' ', $maxMethod), $middleware)
|
||||
)
|
||||
)->implode("\n");
|
||||
|
||||
$spaces = str_repeat(' ', max($maxMethod + 6 - mb_strlen($method), 0));
|
||||
|
||||
$dots = str_repeat('.', max(
|
||||
$terminalWidth - mb_strlen($method.$spaces.$uri.$action) - 6 - ($action ? 1 : 0), 0
|
||||
));
|
||||
|
||||
$dots = empty($dots) ? $dots : " $dots";
|
||||
|
||||
if ($action && ! $this->output->isVerbose() && mb_strlen($method.$spaces.$uri.$action.$dots) > ($terminalWidth - 6)) {
|
||||
$action = substr($action, 0, $terminalWidth - 7 - mb_strlen($method.$spaces.$uri.$dots)).'…';
|
||||
}
|
||||
|
||||
$method = Str::of($method)->explode('|')->map(
|
||||
fn ($method) => sprintf('<fg=%s>%s</>', $this->verbColors[$method] ?? 'default', $method),
|
||||
)->implode('<fg=#6C7280>|</>');
|
||||
|
||||
return [sprintf(
|
||||
' <fg=white;options=bold>%s</> %s<fg=white>%s</><fg=#6C7280>%s %s</>',
|
||||
$method,
|
||||
$spaces,
|
||||
preg_replace('#({[^}]+})#', '<fg=yellow>$1</>', $uri),
|
||||
$dots,
|
||||
str_replace(' ', ' › ', $action ?? ''),
|
||||
), $this->output->isVerbose() && ! empty($middleware) ? "<fg=#6C7280>$middleware</>" : null];
|
||||
})
|
||||
->flatten()
|
||||
->filter()
|
||||
->prepend('')
|
||||
->push('')->push($routeCount)->push('')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formatted action for display on the CLI.
|
||||
*
|
||||
* @param array $route
|
||||
* @return string
|
||||
*/
|
||||
protected function formatActionForCli($route)
|
||||
{
|
||||
['action' => $action, 'name' => $name] = $route;
|
||||
|
||||
if ($action === 'Closure' || $action === ViewController::class) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
$name = $name ? "$name " : null;
|
||||
|
||||
$rootControllerNamespace = $this->laravel[UrlGenerator::class]->getRootControllerNamespace()
|
||||
?? ($this->laravel->getNamespace().'Http\\Controllers');
|
||||
|
||||
if (str_starts_with($action, $rootControllerNamespace)) {
|
||||
return $name.substr($action, mb_strlen($rootControllerNamespace) + 1);
|
||||
}
|
||||
|
||||
$actionClass = explode('@', $action)[0];
|
||||
|
||||
if (class_exists($actionClass) && str_starts_with((new ReflectionClass($actionClass))->getFilename(), base_path('vendor'))) {
|
||||
$actionCollection = collect(explode('\\', $action));
|
||||
|
||||
return $name.$actionCollection->take(2)->implode('\\').' '.$actionCollection->last();
|
||||
}
|
||||
|
||||
return $name.$action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine and return the output for displaying the number of routes in the CLI output.
|
||||
*
|
||||
* @param \Illuminate\Support\Collection $routes
|
||||
* @param int $terminalWidth
|
||||
* @return string
|
||||
*/
|
||||
protected function determineRouteCountOutput($routes, $terminalWidth)
|
||||
{
|
||||
$routeCountText = 'Showing ['.$routes->count().'] routes';
|
||||
|
||||
$offset = $terminalWidth - mb_strlen($routeCountText) - 2;
|
||||
|
||||
$spaces = str_repeat(' ', $offset);
|
||||
|
||||
return $spaces.'<fg=blue;options=bold>Showing ['.$routes->count().'] routes</>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the terminal width.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getTerminalWidth()
|
||||
{
|
||||
return is_null(static::$terminalWidthResolver)
|
||||
? (new Terminal)->getWidth()
|
||||
: call_user_func(static::$terminalWidthResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback that should be used when resolving the terminal width.
|
||||
*
|
||||
* @param \Closure|null $resolver
|
||||
* @return void
|
||||
*/
|
||||
public static function resolveTerminalWidthUsing($resolver)
|
||||
{
|
||||
static::$terminalWidthResolver = $resolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['json', null, InputOption::VALUE_NONE, 'Output the route list as JSON'],
|
||||
['method', null, InputOption::VALUE_OPTIONAL, 'Filter the routes by method'],
|
||||
['name', null, InputOption::VALUE_OPTIONAL, 'Filter the routes by name'],
|
||||
['domain', null, InputOption::VALUE_OPTIONAL, 'Filter the routes by domain'],
|
||||
['path', null, InputOption::VALUE_OPTIONAL, 'Only show routes matching the given path pattern'],
|
||||
['except-path', null, InputOption::VALUE_OPTIONAL, 'Do not display the routes matching the given path pattern'],
|
||||
['reverse', 'r', InputOption::VALUE_NONE, 'Reverse the ordering of the routes'],
|
||||
['sort', null, InputOption::VALUE_OPTIONAL, 'The column (domain, method, uri, name, action, middleware) to sort by', 'uri'],
|
||||
['except-vendor', null, InputOption::VALUE_NONE, 'Do not display routes defined by vendor packages'],
|
||||
['only-vendor', null, InputOption::VALUE_NONE, 'Only display routes defined by vendor packages'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand(name: 'make:rule')]
|
||||
class RuleMakeCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:rule';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new validation rule';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Rule';
|
||||
|
||||
/**
|
||||
* Build the class with the given name.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
protected function buildClass($name)
|
||||
{
|
||||
return str_replace(
|
||||
'{{ ruleType }}',
|
||||
$this->option('implicit') ? 'ImplicitRule' : 'Rule',
|
||||
parent::buildClass($name)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
$stub = $this->option('implicit')
|
||||
? '/stubs/rule.implicit.stub'
|
||||
: '/stubs/rule.stub';
|
||||
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return $rootNamespace.'\Rules';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the rule already exists'],
|
||||
['implicit', 'i', InputOption::VALUE_NONE, 'Generate an implicit rule'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand(name: 'make:scope')]
|
||||
class ScopeMakeCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:scope';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new scope class';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Scope';
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
return $this->resolveStubPath('/stubs/scope.stub');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return is_dir(app_path('Models')) ? $rootNamespace.'\\Models\\Scopes' : $rootNamespace.'\Scopes';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the scope already exists'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Env;
|
||||
use Illuminate\Support\InteractsWithTime;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Process\PhpExecutableFinder;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
use function Termwind\terminal;
|
||||
|
||||
#[AsCommand(name: 'serve')]
|
||||
class ServeCommand extends Command
|
||||
{
|
||||
use InteractsWithTime;
|
||||
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'serve';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Serve the application on the PHP development server';
|
||||
|
||||
/**
|
||||
* The current port offset.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $portOffset = 0;
|
||||
|
||||
/**
|
||||
* The list of lines that are pending to be output.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $outputBuffer = '';
|
||||
|
||||
/**
|
||||
* The list of requests being handled and their start time.
|
||||
*
|
||||
* @var array<int, \Illuminate\Support\Carbon>
|
||||
*/
|
||||
protected $requestsPool;
|
||||
|
||||
/**
|
||||
* Indicates if the "Server running on..." output message has been displayed.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $serverRunningHasBeenDisplayed = false;
|
||||
|
||||
/**
|
||||
* The environment variables that should be passed from host machine to the PHP server process.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $passthroughVariables = [
|
||||
'APP_ENV',
|
||||
'HERD_PHP_81_INI_SCAN_DIR',
|
||||
'HERD_PHP_82_INI_SCAN_DIR',
|
||||
'HERD_PHP_83_INI_SCAN_DIR',
|
||||
'IGNITION_LOCAL_SITES_PATH',
|
||||
'LARAVEL_SAIL',
|
||||
'PATH',
|
||||
'PHP_CLI_SERVER_WORKERS',
|
||||
'PHP_IDE_CONFIG',
|
||||
'SYSTEMROOT',
|
||||
'XDEBUG_CONFIG',
|
||||
'XDEBUG_MODE',
|
||||
'XDEBUG_SESSION',
|
||||
];
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$environmentFile = $this->option('env')
|
||||
? base_path('.env').'.'.$this->option('env')
|
||||
: base_path('.env');
|
||||
|
||||
$hasEnvironment = file_exists($environmentFile);
|
||||
|
||||
$environmentLastModified = $hasEnvironment
|
||||
? filemtime($environmentFile)
|
||||
: now()->addDays(30)->getTimestamp();
|
||||
|
||||
$process = $this->startProcess($hasEnvironment);
|
||||
|
||||
while ($process->isRunning()) {
|
||||
if ($hasEnvironment) {
|
||||
clearstatcache(false, $environmentFile);
|
||||
}
|
||||
|
||||
if (! $this->option('no-reload') &&
|
||||
$hasEnvironment &&
|
||||
filemtime($environmentFile) > $environmentLastModified) {
|
||||
$environmentLastModified = filemtime($environmentFile);
|
||||
|
||||
$this->newLine();
|
||||
|
||||
$this->components->info('Environment modified. Restarting server...');
|
||||
|
||||
$process->stop(5);
|
||||
|
||||
$this->serverRunningHasBeenDisplayed = false;
|
||||
|
||||
$process = $this->startProcess($hasEnvironment);
|
||||
}
|
||||
|
||||
usleep(500 * 1000);
|
||||
}
|
||||
|
||||
$status = $process->getExitCode();
|
||||
|
||||
if ($status && $this->canTryAnotherPort()) {
|
||||
$this->portOffset += 1;
|
||||
|
||||
return $this->handle();
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new server process.
|
||||
*
|
||||
* @param bool $hasEnvironment
|
||||
* @return \Symfony\Component\Process\Process
|
||||
*/
|
||||
protected function startProcess($hasEnvironment)
|
||||
{
|
||||
$process = new Process($this->serverCommand(), public_path(), collect($_ENV)->mapWithKeys(function ($value, $key) use ($hasEnvironment) {
|
||||
if ($this->option('no-reload') || ! $hasEnvironment) {
|
||||
return [$key => $value];
|
||||
}
|
||||
|
||||
return in_array($key, static::$passthroughVariables) ? [$key => $value] : [$key => false];
|
||||
})->all());
|
||||
|
||||
$this->trap(fn () => [SIGTERM, SIGINT, SIGHUP, SIGUSR1, SIGUSR2, SIGQUIT], function ($signal) use ($process) {
|
||||
if ($process->isRunning()) {
|
||||
$process->stop(10, $signal);
|
||||
}
|
||||
|
||||
exit;
|
||||
});
|
||||
|
||||
$process->start($this->handleProcessOutput());
|
||||
|
||||
return $process;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full server command.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function serverCommand()
|
||||
{
|
||||
$server = file_exists(base_path('server.php'))
|
||||
? base_path('server.php')
|
||||
: __DIR__.'/../resources/server.php';
|
||||
|
||||
return [
|
||||
(new PhpExecutableFinder)->find(false),
|
||||
'-S',
|
||||
$this->host().':'.$this->port(),
|
||||
$server,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the host for the command.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function host()
|
||||
{
|
||||
[$host] = $this->getHostAndPort();
|
||||
|
||||
return $host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the port for the command.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function port()
|
||||
{
|
||||
$port = $this->input->getOption('port');
|
||||
|
||||
if (is_null($port)) {
|
||||
[, $port] = $this->getHostAndPort();
|
||||
}
|
||||
|
||||
$port = $port ?: 8000;
|
||||
|
||||
return $port + $this->portOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the host and port from the host option string.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getHostAndPort()
|
||||
{
|
||||
if (preg_match('/(\[.*\]):?([0-9]+)?/', $this->input->getOption('host'), $matches) !== false) {
|
||||
return [
|
||||
$matches[1] ?? $this->input->getOption('host'),
|
||||
$matches[2] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
$hostParts = explode(':', $this->input->getOption('host'));
|
||||
|
||||
return [
|
||||
$hostParts[0],
|
||||
$hostParts[1] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the command has reached its maximum number of port tries.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function canTryAnotherPort()
|
||||
{
|
||||
return is_null($this->input->getOption('port')) &&
|
||||
($this->input->getOption('tries') > $this->portOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a "callable" to handle the process output.
|
||||
*
|
||||
* @return callable(string, string): void
|
||||
*/
|
||||
protected function handleProcessOutput()
|
||||
{
|
||||
return function ($type, $buffer) {
|
||||
$this->outputBuffer .= $buffer;
|
||||
|
||||
$this->flushOutputBuffer();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the output buffer.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function flushOutputBuffer()
|
||||
{
|
||||
$lines = str($this->outputBuffer)->explode("\n");
|
||||
|
||||
$this->outputBuffer = (string) $lines->pop();
|
||||
|
||||
$lines
|
||||
->map(fn ($line) => trim($line))
|
||||
->filter()
|
||||
->each(function ($line) {
|
||||
if (str($line)->contains('Development Server (http')) {
|
||||
if ($this->serverRunningHasBeenDisplayed === false) {
|
||||
$this->serverRunningHasBeenDisplayed = true;
|
||||
|
||||
$this->components->info("Server running on [http://{$this->host()}:{$this->port()}].");
|
||||
$this->comment(' <fg=yellow;options=bold>Press Ctrl+C to stop the server</>');
|
||||
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (str($line)->contains(' Accepted')) {
|
||||
$requestPort = $this->getRequestPortFromLine($line);
|
||||
|
||||
$this->requestsPool[$requestPort] = [
|
||||
$this->getDateFromLine($line),
|
||||
$this->requestsPool[$requestPort][1] ?? false,
|
||||
microtime(true),
|
||||
];
|
||||
} elseif (str($line)->contains([' [200]: GET '])) {
|
||||
$requestPort = $this->getRequestPortFromLine($line);
|
||||
|
||||
$this->requestsPool[$requestPort][1] = trim(explode('[200]: GET', $line)[1]);
|
||||
} elseif (str($line)->contains('URI:')) {
|
||||
$requestPort = $this->getRequestPortFromLine($line);
|
||||
|
||||
$this->requestsPool[$requestPort][1] = trim(explode('URI: ', $line)[1]);
|
||||
} elseif (str($line)->contains(' Closing')) {
|
||||
$requestPort = $this->getRequestPortFromLine($line);
|
||||
|
||||
if (empty($this->requestsPool[$requestPort])) {
|
||||
$this->requestsPool[$requestPort] = [
|
||||
$this->getDateFromLine($line),
|
||||
false,
|
||||
microtime(true),
|
||||
];
|
||||
}
|
||||
|
||||
[$startDate, $file, $startMicrotime] = $this->requestsPool[$requestPort];
|
||||
|
||||
$formattedStartedAt = $startDate->format('Y-m-d H:i:s');
|
||||
|
||||
unset($this->requestsPool[$requestPort]);
|
||||
|
||||
[$date, $time] = explode(' ', $formattedStartedAt);
|
||||
|
||||
$this->output->write(" <fg=gray>$date</> $time");
|
||||
|
||||
$runTime = $this->runTimeForHumans($startMicrotime);
|
||||
|
||||
if ($file) {
|
||||
$this->output->write($file = " $file");
|
||||
}
|
||||
|
||||
$dots = max(terminal()->width() - mb_strlen($formattedStartedAt) - mb_strlen($file) - mb_strlen($runTime) - 9, 0);
|
||||
|
||||
$this->output->write(' '.str_repeat('<fg=gray>.</>', $dots));
|
||||
$this->output->writeln(" <fg=gray>~ {$runTime}</>");
|
||||
} elseif (str($line)->contains(['Closed without sending a request', 'Failed to poll event'])) {
|
||||
// ...
|
||||
} elseif (! empty($line)) {
|
||||
if (str($line)->startsWith('[')) {
|
||||
$line = str($line)->after('] ');
|
||||
}
|
||||
|
||||
$this->output->writeln(" <fg=gray>$line</>");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the date from the given PHP server output.
|
||||
*
|
||||
* @param string $line
|
||||
* @return \Illuminate\Support\Carbon
|
||||
*/
|
||||
protected function getDateFromLine($line)
|
||||
{
|
||||
$regex = env('PHP_CLI_SERVER_WORKERS', 1) > 1
|
||||
? '/^\[\d+]\s\[([a-zA-Z0-9: ]+)\]/'
|
||||
: '/^\[([^\]]+)\]/';
|
||||
|
||||
$line = str_replace(' ', ' ', $line);
|
||||
|
||||
preg_match($regex, $line, $matches);
|
||||
|
||||
return Carbon::createFromFormat('D M d H:i:s Y', $matches[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request port from the given PHP server output.
|
||||
*
|
||||
* @param string $line
|
||||
* @return int
|
||||
*/
|
||||
protected function getRequestPortFromLine($line)
|
||||
{
|
||||
preg_match('/:(\d+)\s(?:(?:\w+$)|(?:\[.*))/', $line, $matches);
|
||||
|
||||
return (int) $matches[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['host', null, InputOption::VALUE_OPTIONAL, 'The host address to serve the application on', Env::get('SERVER_HOST', '127.0.0.1')],
|
||||
['port', null, InputOption::VALUE_OPTIONAL, 'The port to serve the application on', Env::get('SERVER_PORT')],
|
||||
['tries', null, InputOption::VALUE_OPTIONAL, 'The max number of ports to attempt to serve from', 10],
|
||||
['no-reload', null, InputOption::VALUE_NONE, 'Do not reload the development server on .env file changes'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'storage:link')]
|
||||
class StorageLinkCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command signature.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'storage:link
|
||||
{--relative : Create the symbolic link using relative paths}
|
||||
{--force : Recreate existing symbolic links}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create the symbolic links configured for the application';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$relative = $this->option('relative');
|
||||
|
||||
foreach ($this->links() as $link => $target) {
|
||||
if (file_exists($link) && ! $this->isRemovableSymlink($link, $this->option('force'))) {
|
||||
$this->components->error("The [$link] link already exists.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_link($link)) {
|
||||
$this->laravel->make('files')->delete($link);
|
||||
}
|
||||
|
||||
if ($relative) {
|
||||
$this->laravel->make('files')->relativeLink($target, $link);
|
||||
} else {
|
||||
$this->laravel->make('files')->link($target, $link);
|
||||
}
|
||||
|
||||
$this->components->info("The [$link] link has been connected to [$target].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the symbolic links that are configured for the application.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function links()
|
||||
{
|
||||
return $this->laravel['config']['filesystems.links'] ??
|
||||
[public_path('storage') => storage_path('app/public')];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the provided path is a symlink that can be removed.
|
||||
*
|
||||
* @param string $link
|
||||
* @param bool $force
|
||||
* @return bool
|
||||
*/
|
||||
protected function isRemovableSymlink(string $link, bool $force): bool
|
||||
{
|
||||
return is_link($link) && $force;
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'storage:unlink')]
|
||||
class StorageUnlinkCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command signature.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'storage:unlink';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Delete existing symbolic links configured for the application';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
foreach ($this->links() as $link => $target) {
|
||||
if (! file_exists($link) || ! is_link($link)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->laravel->make('files')->delete($link);
|
||||
|
||||
$this->components->info("The [$link] link has been deleted.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the symbolic links that are configured for the application.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function links()
|
||||
{
|
||||
return $this->laravel['config']['filesystems.links'] ??
|
||||
[public_path('storage') => storage_path('app/public')];
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Foundation\Events\PublishingStubs;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'stub:publish')]
|
||||
class StubPublishCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'stub:publish
|
||||
{--existing : Publish and overwrite only the files that have already been published}
|
||||
{--force : Overwrite any existing files}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Publish all stubs that are available for customization';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (! is_dir($stubsPath = $this->laravel->basePath('stubs'))) {
|
||||
(new Filesystem)->makeDirectory($stubsPath);
|
||||
}
|
||||
|
||||
$stubs = [
|
||||
__DIR__.'/stubs/cast.inbound.stub' => 'cast.inbound.stub',
|
||||
__DIR__.'/stubs/cast.stub' => 'cast.stub',
|
||||
__DIR__.'/stubs/class.stub' => 'class.stub',
|
||||
__DIR__.'/stubs/class.invokable.stub' => 'class.invokable.stub',
|
||||
__DIR__.'/stubs/console.stub' => 'console.stub',
|
||||
__DIR__.'/stubs/enum.stub' => 'enum.stub',
|
||||
__DIR__.'/stubs/enum.backed.stub' => 'enum.backed.stub',
|
||||
__DIR__.'/stubs/event.stub' => 'event.stub',
|
||||
__DIR__.'/stubs/job.queued.stub' => 'job.queued.stub',
|
||||
__DIR__.'/stubs/job.stub' => 'job.stub',
|
||||
__DIR__.'/stubs/listener.typed.queued.stub' => 'listener.typed.queued.stub',
|
||||
__DIR__.'/stubs/listener.queued.stub' => 'listener.queued.stub',
|
||||
__DIR__.'/stubs/listener.typed.stub' => 'listener.typed.stub',
|
||||
__DIR__.'/stubs/listener.stub' => 'listener.stub',
|
||||
__DIR__.'/stubs/mail.stub' => 'mail.stub',
|
||||
__DIR__.'/stubs/markdown-mail.stub' => 'markdown-mail.stub',
|
||||
__DIR__.'/stubs/markdown-notification.stub' => 'markdown-notification.stub',
|
||||
__DIR__.'/stubs/model.pivot.stub' => 'model.pivot.stub',
|
||||
__DIR__.'/stubs/model.stub' => 'model.stub',
|
||||
__DIR__.'/stubs/notification.stub' => 'notification.stub',
|
||||
__DIR__.'/stubs/observer.plain.stub' => 'observer.plain.stub',
|
||||
__DIR__.'/stubs/observer.stub' => 'observer.stub',
|
||||
__DIR__.'/stubs/pest.stub' => 'pest.stub',
|
||||
__DIR__.'/stubs/pest.unit.stub' => 'pest.unit.stub',
|
||||
__DIR__.'/stubs/policy.plain.stub' => 'policy.plain.stub',
|
||||
__DIR__.'/stubs/policy.stub' => 'policy.stub',
|
||||
__DIR__.'/stubs/provider.stub' => 'provider.stub',
|
||||
__DIR__.'/stubs/request.stub' => 'request.stub',
|
||||
__DIR__.'/stubs/resource.stub' => 'resource.stub',
|
||||
__DIR__.'/stubs/resource-collection.stub' => 'resource-collection.stub',
|
||||
__DIR__.'/stubs/rule.stub' => 'rule.stub',
|
||||
__DIR__.'/stubs/scope.stub' => 'scope.stub',
|
||||
__DIR__.'/stubs/test.stub' => 'test.stub',
|
||||
__DIR__.'/stubs/test.unit.stub' => 'test.unit.stub',
|
||||
__DIR__.'/stubs/trait.stub' => 'trait.stub',
|
||||
__DIR__.'/stubs/view-component.stub' => 'view-component.stub',
|
||||
realpath(__DIR__.'/../../Database/Console/Factories/stubs/factory.stub') => 'factory.stub',
|
||||
realpath(__DIR__.'/../../Database/Console/Seeds/stubs/seeder.stub') => 'seeder.stub',
|
||||
realpath(__DIR__.'/../../Database/Migrations/stubs/migration.create.stub') => 'migration.create.stub',
|
||||
realpath(__DIR__.'/../../Database/Migrations/stubs/migration.stub') => 'migration.stub',
|
||||
realpath(__DIR__.'/../../Database/Migrations/stubs/migration.update.stub') => 'migration.update.stub',
|
||||
realpath(__DIR__.'/../../Routing/Console/stubs/controller.api.stub') => 'controller.api.stub',
|
||||
realpath(__DIR__.'/../../Routing/Console/stubs/controller.invokable.stub') => 'controller.invokable.stub',
|
||||
realpath(__DIR__.'/../../Routing/Console/stubs/controller.model.api.stub') => 'controller.model.api.stub',
|
||||
realpath(__DIR__.'/../../Routing/Console/stubs/controller.model.stub') => 'controller.model.stub',
|
||||
realpath(__DIR__.'/../../Routing/Console/stubs/controller.nested.api.stub') => 'controller.nested.api.stub',
|
||||
realpath(__DIR__.'/../../Routing/Console/stubs/controller.nested.singleton.api.stub') => 'controller.nested.singleton.api.stub',
|
||||
realpath(__DIR__.'/../../Routing/Console/stubs/controller.nested.singleton.stub') => 'controller.nested.singleton.stub',
|
||||
realpath(__DIR__.'/../../Routing/Console/stubs/controller.nested.stub') => 'controller.nested.stub',
|
||||
realpath(__DIR__.'/../../Routing/Console/stubs/controller.plain.stub') => 'controller.plain.stub',
|
||||
realpath(__DIR__.'/../../Routing/Console/stubs/controller.singleton.api.stub') => 'controller.singleton.api.stub',
|
||||
realpath(__DIR__.'/../../Routing/Console/stubs/controller.singleton.stub') => 'controller.singleton.stub',
|
||||
realpath(__DIR__.'/../../Routing/Console/stubs/controller.stub') => 'controller.stub',
|
||||
realpath(__DIR__.'/../../Routing/Console/stubs/middleware.stub') => 'middleware.stub',
|
||||
];
|
||||
|
||||
$this->laravel['events']->dispatch($event = new PublishingStubs($stubs));
|
||||
|
||||
foreach ($event->stubs as $from => $to) {
|
||||
$to = $stubsPath.DIRECTORY_SEPARATOR.ltrim($to, DIRECTORY_SEPARATOR);
|
||||
|
||||
if ((! $this->option('existing') && (! file_exists($to) || $this->option('force')))
|
||||
|| ($this->option('existing') && file_exists($to))) {
|
||||
file_put_contents($to, file_get_contents($from));
|
||||
}
|
||||
}
|
||||
|
||||
$this->components->info('Stubs published successfully.');
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
use function Laravel\Prompts\select;
|
||||
|
||||
#[AsCommand(name: 'make:test')]
|
||||
class TestMakeCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:test';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new test class';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Test';
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
$suffix = $this->option('unit') ? '.unit.stub' : '.stub';
|
||||
|
||||
return $this->usingPest()
|
||||
? $this->resolveStubPath('/stubs/pest'.$suffix)
|
||||
: $this->resolveStubPath('/stubs/test'.$suffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the destination class path.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
protected function getPath($name)
|
||||
{
|
||||
$name = Str::replaceFirst($this->rootNamespace(), '', $name);
|
||||
|
||||
return base_path('tests').str_replace('\\', '/', $name).'.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
if ($this->option('unit')) {
|
||||
return $rootNamespace.'\Unit';
|
||||
} else {
|
||||
return $rootNamespace.'\Feature';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root namespace for the class.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function rootNamespace()
|
||||
{
|
||||
return 'Tests';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the test even if the test already exists'],
|
||||
['unit', 'u', InputOption::VALUE_NONE, 'Create a unit test'],
|
||||
['pest', null, InputOption::VALUE_NONE, 'Create a Pest test'],
|
||||
['phpunit', null, InputOption::VALUE_NONE, 'Create a PHPUnit test'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Interact further with the user if they were prompted for missing arguments.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return void
|
||||
*/
|
||||
protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if ($this->isReservedName($this->getNameInput()) || $this->didReceiveOptions($input)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$type = select('Which type of test would you like?', [
|
||||
'feature' => 'Feature',
|
||||
'unit' => 'Unit',
|
||||
]);
|
||||
|
||||
match ($type) {
|
||||
'feature' => null,
|
||||
'unit' => $input->setOption('unit', true),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if Pest is being used by the application.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function usingPest()
|
||||
{
|
||||
if ($this->option('phpunit')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->option('pest') ||
|
||||
(function_exists('\Pest\\version') &&
|
||||
file_exists(base_path('tests').'/Pest.php'));
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand(name: 'make:trait')]
|
||||
class TraitMakeCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:trait';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new trait';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Trait';
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
return $this->resolveStubPath('/stubs/trait.stub');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return match (true) {
|
||||
is_dir(app_path('Concerns')) => $rootNamespace.'\\Concerns',
|
||||
is_dir(app_path('Traits')) => $rootNamespace.'\\Traits',
|
||||
default => $rootNamespace,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the trait even if the trait already exists'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Foundation\Events\MaintenanceModeDisabled;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'up')]
|
||||
class UpCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'up';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Bring the application out of maintenance mode';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
if (! $this->laravel->maintenanceMode()->active()) {
|
||||
$this->components->info('Application is already up.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->laravel->maintenanceMode()->deactivate();
|
||||
|
||||
if (is_file(storage_path('framework/maintenance.php'))) {
|
||||
unlink(storage_path('framework/maintenance.php'));
|
||||
}
|
||||
|
||||
$this->laravel->get('events')->dispatch(new MaintenanceModeDisabled());
|
||||
|
||||
$this->components->info('Application is now live.');
|
||||
} catch (Exception $e) {
|
||||
$this->components->error(sprintf(
|
||||
'Failed to disable maintenance mode: %s.',
|
||||
$e->getMessage(),
|
||||
));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+405
@@ -0,0 +1,405 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Foundation\Events\VendorTagPublished;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Str;
|
||||
use League\Flysystem\Filesystem as Flysystem;
|
||||
use League\Flysystem\Local\LocalFilesystemAdapter as LocalAdapter;
|
||||
use League\Flysystem\MountManager;
|
||||
use League\Flysystem\UnixVisibility\PortableVisibilityConverter;
|
||||
use League\Flysystem\Visibility;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
use function Laravel\Prompts\search;
|
||||
use function Laravel\Prompts\select;
|
||||
|
||||
#[AsCommand(name: 'vendor:publish')]
|
||||
class VendorPublishCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The filesystem instance.
|
||||
*
|
||||
* @var \Illuminate\Filesystem\Filesystem
|
||||
*/
|
||||
protected $files;
|
||||
|
||||
/**
|
||||
* The provider to publish.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $provider = null;
|
||||
|
||||
/**
|
||||
* The tags to publish.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $tags = [];
|
||||
|
||||
/**
|
||||
* The time the command started.
|
||||
*
|
||||
* @var \Illuminate\Support\Carbon|null
|
||||
*/
|
||||
protected $publishedAt;
|
||||
|
||||
/**
|
||||
* The console command signature.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'vendor:publish
|
||||
{--existing : Publish and overwrite only the files that have already been published}
|
||||
{--force : Overwrite any existing files}
|
||||
{--all : Publish assets for all service providers without prompt}
|
||||
{--provider= : The service provider that has assets you want to publish}
|
||||
{--tag=* : One or many tags that have assets you want to publish}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Publish any publishable assets from vendor packages';
|
||||
|
||||
/**
|
||||
* Indicates if migration dates should be updated while publishing.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected static $updateMigrationDates = true;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @param \Illuminate\Filesystem\Filesystem $files
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Filesystem $files)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->files = $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->publishedAt = now();
|
||||
|
||||
$this->determineWhatShouldBePublished();
|
||||
|
||||
foreach ($this->tags ?: [null] as $tag) {
|
||||
$this->publishTag($tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the provider or tag(s) to publish.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function determineWhatShouldBePublished()
|
||||
{
|
||||
if ($this->option('all')) {
|
||||
return;
|
||||
}
|
||||
|
||||
[$this->provider, $this->tags] = [
|
||||
$this->option('provider'), (array) $this->option('tag'),
|
||||
];
|
||||
|
||||
if (! $this->provider && ! $this->tags) {
|
||||
$this->promptForProviderOrTag();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt for which provider or tag to publish.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function promptForProviderOrTag()
|
||||
{
|
||||
$choices = $this->publishableChoices();
|
||||
|
||||
$choice = windows_os()
|
||||
? select(
|
||||
"Which provider or tag's files would you like to publish?",
|
||||
$choices,
|
||||
scroll: 15,
|
||||
)
|
||||
: search(
|
||||
label: "Which provider or tag's files would you like to publish?",
|
||||
placeholder: 'Search...',
|
||||
options: fn ($search) => array_values(array_filter(
|
||||
$choices,
|
||||
fn ($choice) => str_contains(strtolower($choice), strtolower($search))
|
||||
)),
|
||||
scroll: 15,
|
||||
);
|
||||
|
||||
if ($choice == $choices[0] || is_null($choice)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->parseChoice($choice);
|
||||
}
|
||||
|
||||
/**
|
||||
* The choices available via the prompt.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function publishableChoices()
|
||||
{
|
||||
return array_merge(
|
||||
['All providers and tags'],
|
||||
preg_filter('/^/', '<fg=gray>Provider:</> ', Arr::sort(ServiceProvider::publishableProviders())),
|
||||
preg_filter('/^/', '<fg=gray>Tag:</> ', Arr::sort(ServiceProvider::publishableGroups()))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the answer that was given via the prompt.
|
||||
*
|
||||
* @param string $choice
|
||||
* @return void
|
||||
*/
|
||||
protected function parseChoice($choice)
|
||||
{
|
||||
[$type, $value] = explode(': ', strip_tags($choice));
|
||||
|
||||
if ($type === 'Provider') {
|
||||
$this->provider = $value;
|
||||
} elseif ($type === 'Tag') {
|
||||
$this->tags = [$value];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes the assets for a tag.
|
||||
*
|
||||
* @param string $tag
|
||||
* @return mixed
|
||||
*/
|
||||
protected function publishTag($tag)
|
||||
{
|
||||
$pathsToPublish = $this->pathsToPublish($tag);
|
||||
|
||||
if ($publishing = count($pathsToPublish) > 0) {
|
||||
$this->components->info(sprintf(
|
||||
'Publishing %sassets',
|
||||
$tag ? "[$tag] " : '',
|
||||
));
|
||||
}
|
||||
|
||||
foreach ($pathsToPublish as $from => $to) {
|
||||
$this->publishItem($from, $to);
|
||||
}
|
||||
|
||||
if ($publishing === false) {
|
||||
$this->components->info('No publishable resources for tag ['.$tag.'].');
|
||||
} else {
|
||||
$this->laravel['events']->dispatch(new VendorTagPublished($tag, $pathsToPublish));
|
||||
|
||||
$this->newLine();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the paths to publish.
|
||||
*
|
||||
* @param string $tag
|
||||
* @return array
|
||||
*/
|
||||
protected function pathsToPublish($tag)
|
||||
{
|
||||
return ServiceProvider::pathsToPublish(
|
||||
$this->provider, $tag
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish the given item from and to the given location.
|
||||
*
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @return void
|
||||
*/
|
||||
protected function publishItem($from, $to)
|
||||
{
|
||||
if ($this->files->isFile($from)) {
|
||||
return $this->publishFile($from, $to);
|
||||
} elseif ($this->files->isDirectory($from)) {
|
||||
return $this->publishDirectory($from, $to);
|
||||
}
|
||||
|
||||
$this->components->error("Can't locate path: <{$from}>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish the file to the given path.
|
||||
*
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @return void
|
||||
*/
|
||||
protected function publishFile($from, $to)
|
||||
{
|
||||
if ((! $this->option('existing') && (! $this->files->exists($to) || $this->option('force')))
|
||||
|| ($this->option('existing') && $this->files->exists($to))) {
|
||||
$to = $this->ensureMigrationNameIsUpToDate($from, $to);
|
||||
|
||||
$this->createParentDirectory(dirname($to));
|
||||
|
||||
$this->files->copy($from, $to);
|
||||
|
||||
$this->status($from, $to, 'file');
|
||||
} else {
|
||||
if ($this->option('existing')) {
|
||||
$this->components->twoColumnDetail(sprintf(
|
||||
'File [%s] does not exist',
|
||||
str_replace(base_path().'/', '', $to),
|
||||
), '<fg=yellow;options=bold>SKIPPED</>');
|
||||
} else {
|
||||
$this->components->twoColumnDetail(sprintf(
|
||||
'File [%s] already exists',
|
||||
str_replace(base_path().'/', '', realpath($to)),
|
||||
), '<fg=yellow;options=bold>SKIPPED</>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish the directory to the given directory.
|
||||
*
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @return void
|
||||
*/
|
||||
protected function publishDirectory($from, $to)
|
||||
{
|
||||
$visibility = PortableVisibilityConverter::fromArray([], Visibility::PUBLIC);
|
||||
|
||||
$this->moveManagedFiles($from, new MountManager([
|
||||
'from' => new Flysystem(new LocalAdapter($from)),
|
||||
'to' => new Flysystem(new LocalAdapter($to, $visibility)),
|
||||
]));
|
||||
|
||||
$this->status($from, $to, 'directory');
|
||||
}
|
||||
|
||||
/**
|
||||
* Move all the files in the given MountManager.
|
||||
*
|
||||
* @param string $from
|
||||
* @param \League\Flysystem\MountManager $manager
|
||||
* @return void
|
||||
*/
|
||||
protected function moveManagedFiles($from, $manager)
|
||||
{
|
||||
foreach ($manager->listContents('from://', true)->sortByPath() as $file) {
|
||||
$path = Str::after($file['path'], 'from://');
|
||||
|
||||
if (
|
||||
$file['type'] === 'file'
|
||||
&& (
|
||||
(! $this->option('existing') && (! $manager->fileExists('to://'.$path) || $this->option('force')))
|
||||
|| ($this->option('existing') && $manager->fileExists('to://'.$path))
|
||||
)
|
||||
) {
|
||||
$path = $this->ensureMigrationNameIsUpToDate($from, $path);
|
||||
|
||||
$manager->write('to://'.$path, $manager->read($file['path']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the directory to house the published files if needed.
|
||||
*
|
||||
* @param string $directory
|
||||
* @return void
|
||||
*/
|
||||
protected function createParentDirectory($directory)
|
||||
{
|
||||
if (! $this->files->isDirectory($directory)) {
|
||||
$this->files->makeDirectory($directory, 0755, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the given migration name is up-to-date.
|
||||
*
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @return string
|
||||
*/
|
||||
protected function ensureMigrationNameIsUpToDate($from, $to)
|
||||
{
|
||||
if (static::$updateMigrationDates === false) {
|
||||
return $to;
|
||||
}
|
||||
|
||||
$from = realpath($from);
|
||||
|
||||
foreach (ServiceProvider::publishableMigrationPaths() as $path) {
|
||||
$path = realpath($path);
|
||||
|
||||
if ($from === $path && preg_match('/\d{4}_(\d{2})_(\d{2})_(\d{6})_/', $to)) {
|
||||
$this->publishedAt->addSecond();
|
||||
|
||||
return preg_replace(
|
||||
'/\d{4}_(\d{2})_(\d{2})_(\d{6})_/',
|
||||
$this->publishedAt->format('Y_m_d_His').'_',
|
||||
$to,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a status message to the console.
|
||||
*
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @param string $type
|
||||
* @return void
|
||||
*/
|
||||
protected function status($from, $to, $type)
|
||||
{
|
||||
$from = str_replace(base_path().'/', '', realpath($from));
|
||||
|
||||
$to = str_replace(base_path().'/', '', realpath($to));
|
||||
|
||||
$this->components->task(sprintf(
|
||||
'Copying %s [%s] to [%s]',
|
||||
$type,
|
||||
$from,
|
||||
$to,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Instruct the command to not update the dates on migrations when publishing.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function dontUpdateMigrationDates()
|
||||
{
|
||||
static::$updateMigrationDates = false;
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Collection;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
#[AsCommand(name: 'view:cache')]
|
||||
class ViewCacheCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'view:cache';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = "Compile all of the application's Blade templates";
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->callSilent('view:clear');
|
||||
|
||||
$this->paths()->each(function ($path) {
|
||||
$prefix = $this->output->isVeryVerbose() ? '<fg=yellow;options=bold>DIR</> ' : '';
|
||||
|
||||
$this->components->task($prefix.$path, null, OutputInterface::VERBOSITY_VERBOSE);
|
||||
|
||||
$this->compileViews($this->bladeFilesIn([$path]));
|
||||
});
|
||||
|
||||
$this->newLine();
|
||||
|
||||
$this->components->info('Blade templates cached successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the given view files.
|
||||
*
|
||||
* @param \Illuminate\Support\Collection $views
|
||||
* @return void
|
||||
*/
|
||||
protected function compileViews(Collection $views)
|
||||
{
|
||||
$compiler = $this->laravel['view']->getEngineResolver()->resolve('blade')->getCompiler();
|
||||
|
||||
$views->map(function (SplFileInfo $file) use ($compiler) {
|
||||
$this->components->task(' '.$file->getRelativePathname(), null, OutputInterface::VERBOSITY_VERY_VERBOSE);
|
||||
|
||||
$compiler->compile($file->getRealPath());
|
||||
});
|
||||
|
||||
if ($this->output->isVeryVerbose()) {
|
||||
$this->newLine();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Blade files in the given path.
|
||||
*
|
||||
* @param array $paths
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
protected function bladeFilesIn(array $paths)
|
||||
{
|
||||
$extensions = collect($this->laravel['view']->getExtensions())
|
||||
->filter(fn ($value) => $value === 'blade')
|
||||
->keys()
|
||||
->map(fn ($extension) => "*.{$extension}")
|
||||
->all();
|
||||
|
||||
return collect(
|
||||
Finder::create()
|
||||
->in($paths)
|
||||
->exclude('vendor')
|
||||
->name($extensions)
|
||||
->files()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the possible view paths.
|
||||
*
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
protected function paths()
|
||||
{
|
||||
$finder = $this->laravel['view']->getFinder();
|
||||
|
||||
return collect($finder->getPaths())->merge(
|
||||
collect($finder->getHints())->flatten()
|
||||
);
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'view:clear')]
|
||||
class ViewClearCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'view:clear';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Clear all compiled view files';
|
||||
|
||||
/**
|
||||
* The filesystem instance.
|
||||
*
|
||||
* @var \Illuminate\Filesystem\Filesystem
|
||||
*/
|
||||
protected $files;
|
||||
|
||||
/**
|
||||
* Create a new config clear command instance.
|
||||
*
|
||||
* @param \Illuminate\Filesystem\Filesystem $files
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Filesystem $files)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->files = $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$path = $this->laravel['config']['view.compiled'];
|
||||
|
||||
if (! $path) {
|
||||
throw new RuntimeException('View path not found.');
|
||||
}
|
||||
|
||||
$this->laravel['view.engine.resolver']
|
||||
->resolve('blade')
|
||||
->forgetCompiledOrNotExpired();
|
||||
|
||||
foreach ($this->files->glob("{$path}/*") as $view) {
|
||||
$this->files->delete($view);
|
||||
}
|
||||
|
||||
$this->components->info('Compiled views cleared successfully.');
|
||||
}
|
||||
}
|
||||
+256
@@ -0,0 +1,256 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Console;
|
||||
|
||||
use Illuminate\Console\Concerns\CreatesMatchingTest;
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Illuminate\Foundation\Inspiring;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand(name: 'make:view')]
|
||||
class ViewMakeCommand extends GeneratorCommand
|
||||
{
|
||||
use CreatesMatchingTest;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new view';
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:view';
|
||||
|
||||
/**
|
||||
* The type of file being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'View';
|
||||
|
||||
/**
|
||||
* Build the class with the given name.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
protected function buildClass($name)
|
||||
{
|
||||
$contents = parent::buildClass($name);
|
||||
|
||||
return str_replace(
|
||||
'{{ quote }}',
|
||||
Inspiring::quotes()->random(),
|
||||
$contents,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the destination view path.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
protected function getPath($name)
|
||||
{
|
||||
return $this->viewPath(
|
||||
$this->getNameInput().'.'.$this->option('extension'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the desired view name from the input.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getNameInput()
|
||||
{
|
||||
$name = trim($this->argument('name'));
|
||||
|
||||
$name = str_replace(['\\', '.'], '/', $this->argument('name'));
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
return $this->resolveStubPath(
|
||||
'/stubs/view.stub',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully-qualified path to the stub.
|
||||
*
|
||||
* @param string $stub
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStubPath($stub)
|
||||
{
|
||||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
|
||||
? $customPath
|
||||
: __DIR__.$stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the destination test case path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getTestPath()
|
||||
{
|
||||
return base_path(
|
||||
Str::of($this->testClassFullyQualifiedName())
|
||||
->replace('\\', '/')
|
||||
->replaceFirst('Tests/Feature', 'tests/Feature')
|
||||
->append('Test.php')
|
||||
->value()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the matching test case if requested.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
protected function handleTestCreation($path): bool
|
||||
{
|
||||
if (! $this->option('test') && ! $this->option('pest') && ! $this->option('phpunit')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$contents = preg_replace(
|
||||
['/\{{ namespace \}}/', '/\{{ class \}}/', '/\{{ name \}}/'],
|
||||
[$this->testNamespace(), $this->testClassName(), $this->testViewName()],
|
||||
File::get($this->getTestStub()),
|
||||
);
|
||||
|
||||
File::ensureDirectoryExists(dirname($this->getTestPath()), 0755, true);
|
||||
|
||||
$result = File::put($path = $this->getTestPath(), $contents);
|
||||
|
||||
$this->components->info(sprintf('%s [%s] created successfully.', 'Test', $path));
|
||||
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the namespace for the test.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function testNamespace()
|
||||
{
|
||||
return Str::of($this->testClassFullyQualifiedName())
|
||||
->beforeLast('\\')
|
||||
->value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class name for the test.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function testClassName()
|
||||
{
|
||||
return Str::of($this->testClassFullyQualifiedName())
|
||||
->afterLast('\\')
|
||||
->append('Test')
|
||||
->value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class fully qualified name for the test.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function testClassFullyQualifiedName()
|
||||
{
|
||||
$name = Str::of(Str::lower($this->getNameInput()))->replace('.'.$this->option('extension'), '');
|
||||
|
||||
$namespacedName = Str::of(
|
||||
Str::of($name)
|
||||
->replace('/', ' ')
|
||||
->explode(' ')
|
||||
->map(fn ($part) => Str::of($part)->ucfirst())
|
||||
->implode('\\')
|
||||
)
|
||||
->replace(['-', '_'], ' ')
|
||||
->explode(' ')
|
||||
->map(fn ($part) => Str::of($part)->ucfirst())
|
||||
->implode('');
|
||||
|
||||
return 'Tests\\Feature\\View\\'.$namespacedName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the test stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getTestStub()
|
||||
{
|
||||
$stubName = 'view.'.($this->usingPest() ? 'pest' : 'test').'.stub';
|
||||
|
||||
return file_exists($customPath = $this->laravel->basePath("stubs/$stubName"))
|
||||
? $customPath
|
||||
: __DIR__.'/stubs/'.$stubName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view name for the test.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function testViewName()
|
||||
{
|
||||
return Str::of($this->getNameInput())
|
||||
->replace('/', '.')
|
||||
->lower()
|
||||
->value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if Pest is being used by the application.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function usingPest()
|
||||
{
|
||||
if ($this->option('phpunit')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->option('pest') ||
|
||||
(function_exists('\Pest\\version') &&
|
||||
file_exists(base_path('tests').'/Pest.php'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['extension', null, InputOption::VALUE_OPTIONAL, 'The extension of the generated view', 'blade.php'],
|
||||
['force', 'f', InputOption::VALUE_NONE, 'Create the view even if the view already exists'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('/user', function (Request $request) {
|
||||
return $request->user();
|
||||
})->middleware('auth:sanctum');
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Broadcast;
|
||||
|
||||
Broadcast::channel('App.Models.User.{id}', function ($user, $id) {
|
||||
return (int) $user->id === (int) $id;
|
||||
});
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace {{ namespace }};
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;
|
||||
|
||||
class {{ class }} implements CastsInboundAttributes
|
||||
{
|
||||
/**
|
||||
* Prepare the given value for storage.
|
||||
*
|
||||
* @param array<string, mixed> $attributes
|
||||
*/
|
||||
public function set(Model $model, string $key, mixed $value, array $attributes): mixed
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace {{ namespace }};
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||
|
||||
class {{ class }} implements CastsAttributes
|
||||
{
|
||||
/**
|
||||
* Cast the given value.
|
||||
*
|
||||
* @param array<string, mixed> $attributes
|
||||
*/
|
||||
public function get(Model $model, string $key, mixed $value, array $attributes): mixed
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the given value for storage.
|
||||
*
|
||||
* @param array<string, mixed> $attributes
|
||||
*/
|
||||
public function set(Model $model, string $key, mixed $value, array $attributes): mixed
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace {{ namespace }};
|
||||
|
||||
use {{ namespacedUserModel }};
|
||||
|
||||
class {{ class }}
|
||||
{
|
||||
/**
|
||||
* Create a new channel instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the user's access to the channel.
|
||||
*/
|
||||
public function join({{ userModel }} $user): array|bool
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace {{ namespace }};
|
||||
|
||||
class {{ class }}
|
||||
{
|
||||
/**
|
||||
* Create a new class instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the class instance.
|
||||
*/
|
||||
public function __invoke(): void
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace {{ namespace }};
|
||||
|
||||
class {{ class }}
|
||||
{
|
||||
/**
|
||||
* Create a new class instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace {{ namespace }};
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class {{ class }} extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = '{{ command }}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Command description';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Echo exposes an expressive API for subscribing to channels and listening
|
||||
* for events that are broadcast by Laravel. Echo and event broadcasting
|
||||
* allow your team to quickly build robust real-time web applications.
|
||||
*/
|
||||
|
||||
import './echo';
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import Echo from 'laravel-echo';
|
||||
|
||||
import Pusher from 'pusher-js';
|
||||
window.Pusher = Pusher;
|
||||
|
||||
window.Echo = new Echo({
|
||||
broadcaster: 'reverb',
|
||||
key: import.meta.env.VITE_REVERB_APP_KEY,
|
||||
wsHost: import.meta.env.VITE_REVERB_HOST,
|
||||
wsPort: import.meta.env.VITE_REVERB_PORT ?? 80,
|
||||
wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
|
||||
forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
|
||||
enabledTransports: ['ws', 'wss'],
|
||||
});
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace {{ namespace }};
|
||||
|
||||
enum {{ class }}: {{ type }}
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace {{ namespace }};
|
||||
|
||||
enum {{ class }}
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace {{ namespace }};
|
||||
|
||||
use Illuminate\Broadcasting\Channel;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Broadcasting\PresenceChannel;
|
||||
use Illuminate\Broadcasting\PrivateChannel;
|
||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class {{ class }}
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the channels the event should broadcast on.
|
||||
*
|
||||
* @return array<int, \Illuminate\Broadcasting\Channel>
|
||||
*/
|
||||
public function broadcastOn(): array
|
||||
{
|
||||
return [
|
||||
new PrivateChannel('channel-name'),
|
||||
];
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user