This commit is contained in:
foobar
2022-08-21 21:39:06 +02:00
commit 27c1969aaa
7354 changed files with 897064 additions and 0 deletions

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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'];
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace Illuminate\Foundation\Auth;
use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Http\FormRequest;
class EmailVerificationRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
if (! hash_equals((string) $this->route('id'),
(string) $this->user()->getKey())) {
return false;
}
if (! hash_equals((string) $this->route('hash'),
sha1($this->user()->getEmailForVerification()))) {
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 void
*/
public function withValidator($validator)
{
return $validator;
}
}

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,302 @@
<?php
namespace Illuminate\Foundation\Bootstrap;
use ErrorException;
use Exception;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Log\LogManager;
use Monolog\Handler\NullHandler;
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
*/
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)
{
self::$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
* @param array $context
* @return void
*
* @throws \ErrorException
*/
public function handleError($level, $message, $file = '', $line = 0, $context = [])
{
if ($this->isDeprecation($level)) {
return $this->handleDeprecationError($message, $file, $line, $level);
}
if (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
* @return void
*
* @deprecated Use handleDeprecationError instead.
*/
public function handleDeprecation($message, $file, $line)
{
$this->handleDeprecationError($message, $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 (! class_exists(LogManager::class)
|| ! static::$app->hasBeenBootstrapped()
|| static::$app->runningUnitTests()
) {
return;
}
try {
$logger = static::$app->make(LogManager::class);
} catch (Exception $e) {
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
));
}
});
}
/**
* 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();
$options = $config->get('logging.deprecations');
$driver = is_array($options) ? $options['channel'] : ($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)
{
self::$reservedMemory = null;
try {
$this->getExceptionHandler()->report($e);
} catch (Exception $e) {
//
}
if (static::$app->runningInConsole()) {
$this->renderForConsole($e);
} 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()
{
self::$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
*/
public static function forgetApp()
{
static::$app = null;
}
}

View File

@@ -0,0 +1,116 @@
<?php
namespace Illuminate\Foundation\Bootstrap;
use Exception;
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;
$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(function () use ($config) {
return $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);
if (! isset($files['app'])) {
throw new Exception('Unable to load the "app" configuration file.');
}
foreach ($files as $key => $path) {
$repository->set($key, require $path);
}
}
/**
* 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());
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;
}
}

View File

@@ -0,0 +1,108 @@
<?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 void
*/
protected function writeErrorAndDie(InvalidFileException $e)
{
$output = (new ConsoleOutput)->getErrorOutput();
$output->writeln('The environment file is invalid!');
$output->writeln($e->getMessage());
exit(1);
}
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Illuminate\Foundation\Bootstrap;
use Illuminate\Contracts\Foundation\Application;
class RegisterProviders
{
/**
* Bootstrap the given application.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function bootstrap(Application $app)
{
$app->registerConfiguredProviders();
}
}

View File

@@ -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
));
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace Illuminate\Foundation\Bus;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Fluent;
trait Dispatchable
{
/**
* Dispatch the job with the given 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 $boolean
* @param mixed ...$arguments
* @return \Illuminate\Foundation\Bus\PendingDispatch|\Illuminate\Support\Fluent
*/
public static function dispatchIf($boolean, ...$arguments)
{
return $boolean
? new PendingDispatch(new static(...$arguments))
: new Fluent;
}
/**
* Dispatch the job with the given arguments unless the given truth test passes.
*
* @param bool $boolean
* @param mixed ...$arguments
* @return \Illuminate\Foundation\Bus\PendingDispatch|\Illuminate\Support\Fluent
*/
public static function dispatchUnless($boolean, ...$arguments)
{
return ! $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.
*
* @return mixed
*/
public static function dispatchSync(...$arguments)
{
return app(Dispatcher::class)->dispatchSync(new static(...$arguments));
}
/**
* Dispatch a command to its appropriate handler in the current process.
*
* @return mixed
*
* @deprecated Will be removed in a future Laravel version.
*/
public static function dispatchNow(...$arguments)
{
return app(Dispatcher::class)->dispatchNow(new static(...$arguments));
}
/**
* Dispatch a command to its appropriate handler after the current process.
*
* @return mixed
*/
public static function dispatchAfterResponse(...$arguments)
{
return app(Dispatcher::class)->dispatchAfterResponse(new static(...$arguments));
}
/**
* 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);
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Illuminate\Foundation\Bus;
use Illuminate\Contracts\Bus\Dispatcher;
trait DispatchesJobs
{
/**
* Dispatch a job to its appropriate handler.
*
* @param mixed $job
* @return mixed
*/
protected function dispatch($job)
{
return app(Dispatcher::class)->dispatch($job);
}
/**
* Dispatch a job to its appropriate handler in the current process.
*
* @param mixed $job
* @return mixed
*
* @deprecated Will be removed in a future Laravel version.
*/
public function dispatchNow($job)
{
return app(Dispatcher::class)->dispatchNow($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 app(Dispatcher::class)->dispatchSync($job);
}
}

View File

@@ -0,0 +1,165 @@
<?php
namespace Illuminate\Foundation\Bus;
use Closure;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Queue\CallQueuedClosure;
use Laravel\SerializableClosure\SerializableClosure;
class PendingChain
{
/**
* 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 with the given arguments.
*
* @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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,266 @@
<?php
namespace Illuminate\Foundation\Console;
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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = 'about';
/**
* 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(Str::of($key)->lower()->snake(), $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));
});
});
}
/**
* 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) => [(string) Str::of($item[0])->lower()->snake() => value($item[1])])];
});
$this->output->writeln(strip_tags(json_encode($output)));
}
/**
* Gather information about the application.
*
* @return void
*/
protected function gatherApplicationInformation()
{
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' => config('app.debug') ? '<fg=yellow;options=bold>ENABLED</>' : 'OFF',
'URL' => Str::of(config('app.url'))->replace(['http://', 'https://'], ''),
'Maintenance Mode' => $this->laravel->isDownForMaintenance() ? '<fg=yellow;options=bold>ENABLED</>' : 'OFF',
]);
static::addToSection('Cache', fn () => [
'Config' => file_exists($this->laravel->bootstrapPath('cache/config.php')) ? '<fg=green;options=bold>CACHED</>' : '<fg=yellow;options=bold>NOT CACHED</>',
'Events' => file_exists($this->laravel->bootstrapPath('cache/events.php')) ? '<fg=green;options=bold>CACHED</>' : '<fg=yellow;options=bold>NOT CACHED</>',
'Routes' => file_exists($this->laravel->bootstrapPath('cache/routes-v7.php')) ? '<fg=green;options=bold>CACHED</>' : '<fg=yellow;options=bold>NOT CACHED</>',
'Views' => $this->hasPhpFiles($this->laravel->storagePath('framework/views')) ? '<fg=green;options=bold>CACHED</>' : '<fg=yellow;options=bold>NOT CACHED</>',
]);
$logChannel = config('logging.default');
if (config('logging.channels.'.$logChannel.'.driver') === 'stack') {
$secondary = collect(config('logging.channels.'.$logChannel.'.channels'))
->implode(', ');
$logs = '<fg=yellow;options=bold>'.$logChannel.'</> <fg=gray;options=bold>/</> '.$secondary;
} else {
$logs = $logChannel;
}
static::addToSection('Drivers', fn () => array_filter([
'Broadcasting' => config('broadcasting.default'),
'Cache' => config('cache.default'),
'Database' => config('database.default'),
'Logs' => $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 array_filter(explode(',', $this->option('only') ?? ''));
}
}

View File

@@ -0,0 +1,91 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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 [
['inbound', null, InputOption::VALUE_OPTIONAL, 'Generate an inbound cast class'],
];
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace Illuminate\Foundation\Console;
use Illuminate\Console\GeneratorCommand;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'make:channel')]
class ChannelMakeCommand extends GeneratorCommand
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'make:channel';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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';
}
}

View File

@@ -0,0 +1,53 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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.');
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace Illuminate\Foundation\Console;
use Closure;
use Illuminate\Console\Command;
use ReflectionFunction;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class ClosureCommand extends Command
{
/**
* 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)
{
$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()];
}
}
return (int) $this->laravel->call(
$this->callback->bindTo($this, $this), $parameters
);
}
/**
* 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;
}
}

View File

@@ -0,0 +1,190 @@
<?php
namespace Illuminate\Foundation\Console;
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
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'make:component';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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')) {
$this->writeView(function () {
$this->components->info($this->type.' created successfully.');
});
return;
}
if (parent::handle() === false && ! $this->option('force')) {
return false;
}
if (! $this->option('inline')) {
$this->writeView();
}
}
/**
* Write the view for the component.
*
* @param callable|null $onSuccess
* @return void
*/
protected function writeView($onSuccess = null)
{
$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>'
);
if ($onSuccess) {
$onSuccess();
}
}
/**
* 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', null, 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'],
];
}
}

View File

@@ -0,0 +1,105 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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();
}
}

View File

@@ -0,0 +1,68 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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.');
}
}

View File

@@ -0,0 +1,110 @@
<?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\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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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);
return str_replace(['dummy:command', '{{ command }}'], $this->option('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 [
['command', null, InputOption::VALUE_OPTIONAL, 'The terminal command that should be assigned', 'command:name'],
];
}
}

View File

@@ -0,0 +1,546 @@
<?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;
#[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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = 'docs';
/**
* 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 documention, 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;
/**
* Create a new command instance.
*
* @param \Illuminate\Http\Client\Factory $http
* @param \Illuminate\Contracts\Cache\Repository $cache
* @return void
*/
public function __construct(Http $http, Cache $cache)
{
parent::__construct();
$this->http = $http;
$this->cache = $cache;
}
/**
* Configure the current command.
*
* @return void
*/
protected function configure()
{
parent::configure();
if ($this->isSearching()) {
$this->ignoreValidationErrors();
}
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
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
*/
protected function resolvePage()
{
if ($this->option('no-interaction') && $this->didNotRequestPage()) {
return '/';
}
return $this->didNotRequestPage()
? $this->askForPage()
: $this->guessPage();
}
/**
* 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
*/
protected function askForPage()
{
return $this->askForPageViaCustomStrategy() ?? $this->askForPageViaAutocomplete();
}
/**
* Ask the user which page they would like to open via a custom strategy.
*
* @return ?string
*/
protected function askForPageViaCustomStrategy()
{
try {
$strategy = require Env::get('ARTISAN_DOCS_ASK_STRATEGY');
} catch (Throwable $e) {
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
*/
protected function askForPageViaAutocomplete()
{
$choice = $this->components->choice(
'Which page would you like to open?',
$this->pages()->mapWithKeys(fn ($option) => [
Str::lower($option['title']) => $option['title'],
])->all(),
'installation',
3
);
return $this->pages()->filter(
fn ($page) => $page['title'] === $choice || Str::lower($page['title']) === $choice
)->keys()->first() ?: null;
}
/**
* Guess the page the user is attempting to open.
*
* @return ?string
*/
protected function guessPage()
{
return $this->pages()
->filter(fn ($page) => str_starts_with(
Str::slug($page['title'], ' '),
Str::slug($this->argument('page'), ' ')
))->keys()->first() ?? $this->pages()->map(fn ($page) => similar_text(
Str::slug($page['title'], ' '),
Str::slug($this->argument('page'), ' '),
))
->filter(fn ($score) => $score >= min(3, Str::length($this->argument('page'))))
->sortDesc()
->keys()
->sortByDesc(fn ($slug) => Str::contains(
Str::slug($this->pages()[$slug]['title'], ' '),
Str::slug($this->argument('page'), ' ')
) ? 1 : 0)
->first();
}
/**
* The section the user specifically asked to open.
*
* @param string $page
* @return ?string
*/
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
*/
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 $e) {
$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;
}
}

View File

@@ -0,0 +1,151 @@
<?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 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}
{--status=503 : The status code that should be used when returning the maintenance mode response}';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = 'down';
/**
* 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;
}
$this->laravel->maintenanceMode()->activate($this->getDownFilePayload());
file_put_contents(
storage_path('framework/maintenance.php'),
file_get_contents(__DIR__.'/stubs/maintenance-mode.stub')
);
$this->laravel->get('events')->dispatch(MaintenanceModeEnabled::class);
$this->components->info('Application is now in maintenance mode.');
} 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->option('secret'),
'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 $e) {
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;
}
}

View File

@@ -0,0 +1,48 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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'],
));
}
}

View File

@@ -0,0 +1,71 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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;
}
}

View File

@@ -0,0 +1,70 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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.');
}
}

View File

@@ -0,0 +1,90 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = 'event:generate';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate the missing events and listeners based on registration';
/**
* 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]
));
}
}
}

View File

@@ -0,0 +1,232 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = 'event:list';
/**
* 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;
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace Illuminate\Foundation\Console;
use Illuminate\Console\GeneratorCommand;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'make:event')]
class EventMakeCommand extends GeneratorCommand
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'make:event';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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';
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace Illuminate\Foundation\Console;
use Illuminate\Console\GeneratorCommand;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputOption;
#[AsCommand(name: 'make:exception')]
class ExceptionMakeCommand extends GeneratorCommand
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'make:exception';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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';
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
['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'],
];
}
}

View File

@@ -0,0 +1,94 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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 [
['sync', null, InputOption::VALUE_NONE, 'Indicates that job should be synchronous'],
];
}
}

View File

@@ -0,0 +1,381 @@
<?php
namespace Illuminate\Foundation\Console;
use Closure;
use Illuminate\Console\Application as Artisan;
use Illuminate\Console\Command;
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\Support\Arr;
use Illuminate\Support\Env;
use Illuminate\Support\Str;
use ReflectionClass;
use Symfony\Component\Finder\Finder;
use Throwable;
class Kernel implements KernelContract
{
/**
* The application implementation.
*
* @var \Illuminate\Contracts\Foundation\Application
*/
protected $app;
/**
* The event dispatcher implementation.
*
* @var \Illuminate\Contracts\Events\Dispatcher
*/
protected $events;
/**
* The Artisan application instance.
*
* @var \Illuminate\Console\Application|null
*/
protected $artisan;
/**
* The Artisan commands provided by the application.
*
* @var array
*/
protected $commands = [];
/**
* Indicates if the Closure commands have been loaded.
*
* @var bool
*/
protected $commandsLoaded = false;
/**
* 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 () {
$this->defineConsoleSchedule();
});
}
/**
* Define the application's command schedule.
*
* @return void
*/
protected function defineConsoleSchedule()
{
$this->app->singleton(Schedule::class, function ($app) {
return tap(new Schedule($this->scheduleTimezone()), function ($schedule) {
$this->schedule($schedule->useCache($this->scheduleCache()));
});
});
}
/**
* Get the name of the cache store that should manage scheduling mutexes.
*
* @return string
*/
protected function scheduleCache()
{
return $this->app['config']->get('cache.schedule_store', Env::get('SCHEDULE_CACHE_DRIVER'));
}
/**
* 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)
{
try {
$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->app->terminate();
}
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
//
}
/**
* 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'));
}
/**
* 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;
}
$namespace = $this->app->getNamespace();
foreach ((new Finder)->in($paths)->files() as $command) {
$command = $namespace.str_replace(
['/', '.php'],
['\\', ''],
Str::after($command->getRealPath(), realpath(app_path()).DIRECTORY_SEPARATOR)
);
if (is_subclass_of($command, Command::class) &&
! (new ReflectionClass($command))->isAbstract()) {
Artisan::starting(function ($artisan) use ($command) {
$artisan->resolve($command);
});
}
}
}
/**
* 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)
{
$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();
$this->commandsLoaded = true;
}
}
/**
* 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();
}
return $this->artisan;
}
/**
* Set the Artisan application instance.
*
* @param \Illuminate\Console\Application $artisan
* @return void
*/
public function setArtisan($artisan)
{
$this->artisan = $artisan;
}
/**
* 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);
}
}

View File

@@ -0,0 +1,124 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = 'key:generate';
/**
* 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;
}
$this->writeNewEnvironmentFileWith($key);
return true;
}
/**
* Write a new environment file with the given key.
*
* @param string $key
* @return void
*/
protected function writeNewEnvironmentFileWith($key)
{
file_put_contents($this->laravel->environmentFilePath(), preg_replace(
$this->keyReplacementPattern(),
'APP_KEY='.$key,
file_get_contents($this->laravel->environmentFilePath())
));
}
/**
* 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";
}
}

View File

@@ -0,0 +1,128 @@
<?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\InputOption;
#[AsCommand(name: 'make:listener')]
class ListenerMakeCommand extends GeneratorCommand
{
use CreatesMatchingTest;
/**
* The console command name.
*
* @var string
*/
protected $name = 'make:listener';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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
);
}
/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStub()
{
if ($this->option('queued')) {
return $this->option('event')
? __DIR__.'/stubs/listener-queued.stub'
: __DIR__.'/stubs/listener-queued-duck.stub';
}
return $this->option('event')
? __DIR__.'/stubs/listener.stub'
: __DIR__.'/stubs/listener-duck.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'],
['queued', null, InputOption::VALUE_NONE, 'Indicates the event listener should be queued'],
];
}
}

View File

@@ -0,0 +1,169 @@
<?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\InputOption;
#[AsCommand(name: 'make:mail')]
class MailMakeCommand extends GeneratorCommand
{
use CreatesMatchingTest;
/**
* The console command name.
*
* @var string
*/
protected $name = 'make:mail';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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 = 'Mail';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
if (parent::handle() === false && ! $this->option('force')) {
return;
}
if ($this->option('markdown') !== false) {
$this->writeMarkdownTemplate();
}
}
/**
* Write the Markdown template for the mailable.
*
* @return void
*/
protected function writeMarkdownTemplate()
{
$path = $this->viewPath(
str_replace('.', '/', $this->getView()).'.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'));
}
/**
* Build the class with the given name.
*
* @param string $name
* @return string
*/
protected function buildClass($name)
{
$class = parent::buildClass($name);
if ($this->option('markdown') !== false) {
$class = str_replace(['DummyView', '{{ view }}'], $this->getView(), $class);
}
return $class;
}
/**
* Get the view name.
*
* @return string
*/
protected function getView()
{
$view = $this->option('markdown');
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()
{
return $this->resolveStubPath(
$this->option('markdown') !== false
? '/stubs/markdown-mail.stub'
: '/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 File

@@ -0,0 +1,235 @@
<?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\InputOption;
#[AsCommand(name: 'make:model')]
class ModelMakeCommand extends GeneratorCommand
{
use CreatesMatchingTest;
/**
* The console command name.
*
* @var string
*/
protected $name = 'make:model';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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'),
]));
}
/**
* 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, and resource controller 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 controller'],
['requests', 'R', InputOption::VALUE_NONE, 'Create new form request classes and use them in the resource controller'],
];
}
}

View File

@@ -0,0 +1,147 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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'));
}
/**
* 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'],
];
}
}

View File

@@ -0,0 +1,152 @@
<?php
namespace Illuminate\Foundation\Console;
use Illuminate\Console\GeneratorCommand;
use InvalidArgumentException;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputOption;
#[AsCommand(name: 'make:observer')]
class ObserverMakeCommand extends GeneratorCommand
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'make:observer';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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 [
['model', 'm', InputOption::VALUE_OPTIONAL, 'The model that the observer applies to.'],
];
}
}

View File

@@ -0,0 +1,56 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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([
'events' => fn () => $this->callSilent('event:clear') == 0,
'views' => fn () => $this->callSilent('view:clear') == 0,
'cache' => fn () => $this->callSilent('cache:clear') == 0,
'route' => fn () => $this->callSilent('route:clear') == 0,
'config' => fn () => $this->callSilent('config:clear') == 0,
'compiled' => fn () => $this->callSilent('clear-compiled') == 0,
])->each(fn ($task, $description) => $this->components->task($description, $task));
$this->newLine();
}
}

View File

@@ -0,0 +1,52 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = 'optimize';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Cache the framework bootstrap files';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$this->components->info('Caching the framework bootstrap files');
collect([
'config' => fn () => $this->callSilent('config:cache') == 0,
'routes' => fn () => $this->callSilent('route:cache') == 0,
])->each(fn ($task, $description) => $this->components->task($description, $task));
$this->newLine();
}
}

View File

@@ -0,0 +1,54 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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());
}
}

View File

@@ -0,0 +1,211 @@
<?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\InputOption;
#[AsCommand(name: 'make:policy')]
class PolicyMakeCommand extends GeneratorCommand
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'make:policy';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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 [
['model', 'm', InputOption::VALUE_OPTIONAL, 'The model that the policy applies to'],
['guard', 'g', InputOption::VALUE_OPTIONAL, 'The guard that the policy relies on'],
];
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace Illuminate\Foundation\Console;
use Illuminate\Console\GeneratorCommand;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'make:provider')]
class ProviderMakeCommand extends GeneratorCommand
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'make:provider';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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';
/**
* 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';
}
}

View File

@@ -0,0 +1,42 @@
<?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));
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace Illuminate\Foundation\Console;
use Illuminate\Console\GeneratorCommand;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'make:request')]
class RequestMakeCommand extends GeneratorCommand
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'make:request';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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';
}
}

View File

@@ -0,0 +1,116 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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 [
['collection', 'c', InputOption::VALUE_NONE, 'Create a resource collection'],
];
}
}

View File

@@ -0,0 +1,122 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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);
}
}

View File

@@ -0,0 +1,68 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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.');
}
}

View File

@@ -0,0 +1,508 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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()
{
$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)
{
return Arr::sort($routes, function ($route) use ($sort) {
return $route[$sort];
});
}
/**
* 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'],
];
}
}

View File

@@ -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:rule')]
class RuleMakeCommand extends GeneratorCommand
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'make:rule';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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 = '/stubs/rule.stub';
if ($this->option('invokable')) {
$stub = '/stubs/rule.invokable.stub';
}
if ($this->option('implicit') && $this->option('invokable')) {
$stub = str_replace('.stub', '.implicit.stub', $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 [
['implicit', 'i', InputOption::VALUE_NONE, 'Generate an implicit rule.'],
['invokable', null, InputOption::VALUE_NONE, 'Generate a single method, invokable rule class.'],
];
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace Illuminate\Foundation\Console;
use Illuminate\Console\GeneratorCommand;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'make:scope')]
class ScopeMakeCommand extends GeneratorCommand
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'make:scope';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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';
}
}

View File

@@ -0,0 +1,319 @@
<?php
namespace Illuminate\Foundation\Console;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use Illuminate\Support\Env;
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
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'serve';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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 requests being handled and their start time.
*
* @var array<int, \Illuminate\Support\Carbon>
*/
protected $requestsPool;
/**
* The environment variables that should be passed from host machine to the PHP server process.
*
* @var string[]
*/
public static $passthroughVariables = [
'APP_ENV',
'LARAVEL_SAIL',
'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);
$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());
$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()
{
$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 fn ($type, $buffer) => str($buffer)->explode("\n")->each(function ($line) {
if (str($line)->contains('Development Server (http')) {
$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();
} elseif (str($line)->contains(' Accepted')) {
$requestPort = $this->getRequestPortFromLine($line);
$this->requestsPool[$requestPort] = [
$this->getDateFromLine($line),
false,
];
} elseif (str($line)->contains([' [200]: GET '])) {
$requestPort = $this->getRequestPortFromLine($line);
$this->requestsPool[$requestPort][1] = trim(explode('[200]: GET', $line)[1]);
} elseif (str($line)->contains(' Closing')) {
$requestPort = $this->getRequestPortFromLine($line);
$request = $this->requestsPool[$requestPort];
[$startDate, $file] = $request;
$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->getDateFromLine($line)->diffInSeconds($startDate);
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}s</>");
} elseif (str($line)->contains(['Closed without sending a request'])) {
// ...
} elseif (! empty($line)) {
$warning = explode('] ', $line);
$this->components->warn(count($warning) > 1 ? $warning[1] : $warning[0]);
}
});
}
/**
* Get the date from the given PHP server output.
*
* @param string $line
* @return \Illuminate\Support\Carbon
*/
protected function getDateFromLine($line)
{
preg_match('/^\[([^\]]+)\]/', $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'],
];
}
}

View File

@@ -0,0 +1,468 @@
<?php
namespace Illuminate\Foundation\Console;
use BackedEnum;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Types\DecimalType;
use Illuminate\Console\Command;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Database\Console\DatabaseInspectionCommand;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Str;
use ReflectionClass;
use ReflectionMethod;
use SplFileObject;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Output\OutputInterface;
use UnitEnum;
#[AsCommand(name: 'model:show')]
class ShowModelCommand extends DatabaseInspectionCommand
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'model:show {model}';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = 'model:show';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Show information about an Eloquent model';
/**
* The console command signature.
*
* @var string
*/
protected $signature = 'model:show {model : The model to show}
{--database= : The database connection to use}
{--json : Output the model as JSON}';
/**
* The methods that can be called in a model to indicate a relation.
*
* @var array
*/
protected $relationMethods = [
'hasMany',
'hasManyThrough',
'hasOneThrough',
'belongsToMany',
'hasOne',
'belongsTo',
'morphOne',
'morphTo',
'morphMany',
'morphToMany',
'morphedByMany',
];
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
if (! $this->ensureDependenciesExist()) {
return 1;
}
$class = $this->qualifyModel($this->argument('model'));
try {
$model = $this->laravel->make($class);
} catch (BindingResolutionException $e) {
return $this->components->error($e->getMessage());
}
if ($this->option('database')) {
$model->setConnection($this->option('database'));
}
$this->display(
$class,
$model->getConnection()->getName(),
$model->getConnection()->getTablePrefix().$model->getTable(),
$this->getAttributes($model),
$this->getRelations($model),
);
}
/**
* Get the column attributes for the given model.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Support\Collection
*/
protected function getAttributes($model)
{
$schema = $model->getConnection()->getDoctrineSchemaManager();
$this->registerTypeMappings($schema->getDatabasePlatform());
$table = $model->getConnection()->getTablePrefix().$model->getTable();
$columns = $schema->listTableColumns($table);
$indexes = $schema->listTableIndexes($table);
return collect($columns)
->values()
->map(fn (Column $column) => [
'name' => $column->getName(),
'type' => $this->getColumnType($column),
'increments' => $column->getAutoincrement(),
'nullable' => ! $column->getNotnull(),
'default' => $this->getColumnDefault($column, $model),
'unique' => $this->columnIsUnique($column->getName(), $indexes),
'fillable' => $model->isFillable($column->getName()),
'hidden' => $this->attributeIsHidden($column->getName(), $model),
'appended' => null,
'cast' => $this->getCastType($column->getName(), $model),
])
->merge($this->getVirtualAttributes($model, $columns));
}
/**
* Get the virtual (non-column) attributes for the given model.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param \Doctrine\DBAL\Schema\Column[] $columns
* @return \Illuminate\Support\Collection
*/
protected function getVirtualAttributes($model, $columns)
{
$class = new ReflectionClass($model);
return collect($class->getMethods())
->reject(
fn (ReflectionMethod $method) => $method->isStatic()
|| $method->isAbstract()
|| $method->getDeclaringClass()->getName() !== get_class($model)
)
->mapWithKeys(function (ReflectionMethod $method) use ($model) {
if (preg_match('/^get(.*)Attribute$/', $method->getName(), $matches) === 1) {
return [Str::snake($matches[1]) => 'accessor'];
} elseif ($model->hasAttributeMutator($method->getName())) {
return [Str::snake($method->getName()) => 'attribute'];
} else {
return [];
}
})
->reject(fn ($cast, $name) => collect($columns)->has($name))
->map(fn ($cast, $name) => [
'name' => $name,
'type' => null,
'increments' => false,
'nullable' => null,
'default' => null,
'unique' => null,
'fillable' => $model->isFillable($name),
'hidden' => $this->attributeIsHidden($name, $model),
'appended' => $model->hasAppended($name),
'cast' => $cast,
])
->values();
}
/**
* Get the relations from the given model.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Support\Collection
*/
protected function getRelations($model)
{
return collect(get_class_methods($model))
->map(fn ($method) => new ReflectionMethod($model, $method))
->reject(
fn (ReflectionMethod $method) => $method->isStatic()
|| $method->isAbstract()
|| $method->getDeclaringClass()->getName() !== get_class($model)
)
->filter(function (ReflectionMethod $method) {
$file = new SplFileObject($method->getFileName());
$file->seek($method->getStartLine() - 1);
$code = '';
while ($file->key() < $method->getEndLine()) {
$code .= $file->current();
$file->next();
}
return collect($this->relationMethods)
->contains(fn ($relationMethod) => str_contains($code, '$this->'.$relationMethod.'('));
})
->map(function (ReflectionMethod $method) use ($model) {
$relation = $method->invoke($model);
if (! $relation instanceof Relation) {
return null;
}
return [
'name' => $method->getName(),
'type' => Str::afterLast(get_class($relation), '\\'),
'related' => get_class($relation->getRelated()),
];
})
->filter()
->values();
}
/**
* Render the model information.
*
* @param string $class
* @param string $database
* @param string $table
* @param \Illuminate\Support\Collection $attributes
* @param \Illuminate\Support\Collection $relations
* @return void
*/
protected function display($class, $database, $table, $attributes, $relations)
{
$this->option('json')
? $this->displayJson($class, $database, $table, $attributes, $relations)
: $this->displayCli($class, $database, $table, $attributes, $relations);
}
/**
* Render the model information as JSON.
*
* @param string $class
* @param string $database
* @param string $table
* @param \Illuminate\Support\Collection $attributes
* @param \Illuminate\Support\Collection $relations
* @return void
*/
protected function displayJson($class, $database, $table, $attributes, $relations)
{
$this->output->writeln(
collect([
'class' => $class,
'database' => $database,
'table' => $table,
'attributes' => $attributes,
'relations' => $relations,
])->toJson()
);
}
/**
* Render the model information for the CLI.
*
* @param string $class
* @param string $database
* @param string $table
* @param \Illuminate\Support\Collection $attributes
* @param \Illuminate\Support\Collection $relations
* @return void
*/
protected function displayCli($class, $database, $table, $attributes, $relations)
{
$this->newLine();
$this->components->twoColumnDetail('<fg=green;options=bold>'.$class.'</>');
$this->components->twoColumnDetail('Database', $database);
$this->components->twoColumnDetail('Table', $table);
$this->newLine();
$this->components->twoColumnDetail(
'<fg=green;options=bold>Attributes</>',
'type <fg=gray>/</> <fg=yellow;options=bold>cast</>',
);
foreach ($attributes as $attribute) {
$first = trim(sprintf(
'%s %s',
$attribute['name'],
collect(['increments', 'unique', 'nullable', 'fillable', 'hidden', 'appended'])
->filter(fn ($property) => $attribute[$property])
->map(fn ($property) => sprintf('<fg=gray>%s</>', $property))
->implode('<fg=gray>,</> ')
));
$second = collect([
$attribute['type'],
$attribute['cast'] ? '<fg=yellow;options=bold>'.$attribute['cast'].'</>' : null,
])->filter()->implode(' <fg=gray>/</> ');
$this->components->twoColumnDetail($first, $second);
if ($attribute['default'] !== null) {
$this->components->bulletList(
[sprintf('default: %s', $attribute['default'])],
OutputInterface::VERBOSITY_VERBOSE
);
}
}
$this->newLine();
$this->components->twoColumnDetail('<fg=green;options=bold>Relations</>');
foreach ($relations as $relation) {
$this->components->twoColumnDetail(
sprintf('%s <fg=gray>%s</>', $relation['name'], $relation['type']),
$relation['related']
);
}
$this->newLine();
}
/**
* Get the cast type for the given column.
*
* @param string $column
* @param \Illuminate\Database\Eloquent\Model $model
* @return string|null
*/
protected function getCastType($column, $model)
{
if ($model->hasGetMutator($column) || $model->hasSetMutator($column)) {
return 'accessor';
}
if ($model->hasAttributeMutator($column)) {
return 'attribute';
}
return $this->getCastsWithDates($model)->get($column) ?? null;
}
/**
* Get the model casts, including any date casts.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Support\Collection
*/
protected function getCastsWithDates($model)
{
return collect($model->getDates())
->flip()
->map(fn () => 'datetime')
->merge($model->getCasts());
}
/**
* Get the type of the given column.
*
* @param \Doctrine\DBAL\Schema\Column $column
* @return string
*/
protected function getColumnType($column)
{
$name = $column->getType()->getName();
$unsigned = $column->getUnsigned() ? ' unsigned' : '';
$details = match (get_class($column->getType())) {
DecimalType::class => $column->getPrecision().','.$column->getScale(),
default => $column->getLength(),
};
if ($details) {
return sprintf('%s(%s)%s', $name, $details, $unsigned);
}
return sprintf('%s%s', $name, $unsigned);
}
/**
* Get the default value for the given column.
*
* @param \Doctrine\DBAL\Schema\Column $column
* @param \Illuminate\Database\Eloquent\Model $model
* @return mixed|null
*/
protected function getColumnDefault($column, $model)
{
$attributeDefault = $model->getAttributes()[$column->getName()] ?? null;
return match (true) {
$attributeDefault instanceof BackedEnum => $attributeDefault->value,
$attributeDefault instanceof UnitEnum => $attributeDefault->name,
default => $attributeDefault ?? $column->getDefault(),
};
}
/**
* Determine if the given attribute is hidden.
*
* @param string $attribute
* @param \Illuminate\Database\Eloquent\Model $model
* @return bool
*/
protected function attributeIsHidden($attribute, $model)
{
if (count($model->getHidden()) > 0) {
return in_array($attribute, $model->getHidden());
}
if (count($model->getVisible()) > 0) {
return ! in_array($attribute, $model->getVisible());
}
return false;
}
/**
* Determine if the given attribute is unique.
*
* @param string $column
* @param \Doctrine\DBAL\Schema\Index[] $indexes
* @return bool
*/
protected function columnIsUnique($column, $indexes)
{
return collect($indexes)
->filter(fn (Index $index) => count($index->getColumns()) === 1 && $index->getColumns()[0] === $column)
->contains(fn (Index $index) => $index->isUnique());
}
/**
* Qualify the given model class base name.
*
* @param string $model
* @return string
*
* @see \Illuminate\Console\GeneratorCommand
*/
protected function qualifyModel(string $model)
{
if (str_contains($model, '\\') && class_exists($model)) {
return $model;
}
$model = ltrim($model, '\\/');
$model = str_replace('/', '\\', $model);
$rootNamespace = $this->laravel->getNamespace();
if (Str::startsWith($model, $rootNamespace)) {
return $model;
}
return is_dir(app_path('Models'))
? $rootNamespace.'Models\\'.$model
: $rootNamespace.$model;
}
}

View File

@@ -0,0 +1,89 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = 'storage:link';
/**
* 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;
}
}

View File

@@ -0,0 +1,101 @@
<?php
namespace Illuminate\Foundation\Console;
use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = 'stub:publish';
/**
* 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);
}
$files = [
__DIR__.'/stubs/cast.inbound.stub' => $stubsPath.'/cast.inbound.stub',
__DIR__.'/stubs/cast.stub' => $stubsPath.'/cast.stub',
__DIR__.'/stubs/console.stub' => $stubsPath.'/console.stub',
__DIR__.'/stubs/event.stub' => $stubsPath.'/event.stub',
__DIR__.'/stubs/job.queued.stub' => $stubsPath.'/job.queued.stub',
__DIR__.'/stubs/job.stub' => $stubsPath.'/job.stub',
__DIR__.'/stubs/mail.stub' => $stubsPath.'/mail.stub',
__DIR__.'/stubs/markdown-mail.stub' => $stubsPath.'/markdown-mail.stub',
__DIR__.'/stubs/markdown-notification.stub' => $stubsPath.'/markdown-notification.stub',
__DIR__.'/stubs/model.pivot.stub' => $stubsPath.'/model.pivot.stub',
__DIR__.'/stubs/model.stub' => $stubsPath.'/model.stub',
__DIR__.'/stubs/notification.stub' => $stubsPath.'/notification.stub',
__DIR__.'/stubs/observer.plain.stub' => $stubsPath.'/observer.plain.stub',
__DIR__.'/stubs/observer.stub' => $stubsPath.'/observer.stub',
__DIR__.'/stubs/policy.plain.stub' => $stubsPath.'/policy.plain.stub',
__DIR__.'/stubs/policy.stub' => $stubsPath.'/policy.stub',
__DIR__.'/stubs/provider.stub' => $stubsPath.'/provider.stub',
__DIR__.'/stubs/request.stub' => $stubsPath.'/request.stub',
__DIR__.'/stubs/resource.stub' => $stubsPath.'/resource.stub',
__DIR__.'/stubs/resource-collection.stub' => $stubsPath.'/resource-collection.stub',
__DIR__.'/stubs/rule.stub' => $stubsPath.'/rule.stub',
__DIR__.'/stubs/scope.stub' => $stubsPath.'/scope.stub',
__DIR__.'/stubs/test.stub' => $stubsPath.'/test.stub',
__DIR__.'/stubs/test.unit.stub' => $stubsPath.'/test.unit.stub',
__DIR__.'/stubs/view-component.stub' => $stubsPath.'/view-component.stub',
realpath(__DIR__.'/../../Database/Console/Factories/stubs/factory.stub') => $stubsPath.'/factory.stub',
realpath(__DIR__.'/../../Database/Console/Seeds/stubs/seeder.stub') => $stubsPath.'/seeder.stub',
realpath(__DIR__.'/../../Database/Migrations/stubs/migration.create.stub') => $stubsPath.'/migration.create.stub',
realpath(__DIR__.'/../../Database/Migrations/stubs/migration.stub') => $stubsPath.'/migration.stub',
realpath(__DIR__.'/../../Database/Migrations/stubs/migration.update.stub') => $stubsPath.'/migration.update.stub',
realpath(__DIR__.'/../../Routing/Console/stubs/controller.api.stub') => $stubsPath.'/controller.api.stub',
realpath(__DIR__.'/../../Routing/Console/stubs/controller.invokable.stub') => $stubsPath.'/controller.invokable.stub',
realpath(__DIR__.'/../../Routing/Console/stubs/controller.model.api.stub') => $stubsPath.'/controller.model.api.stub',
realpath(__DIR__.'/../../Routing/Console/stubs/controller.model.stub') => $stubsPath.'/controller.model.stub',
realpath(__DIR__.'/../../Routing/Console/stubs/controller.nested.api.stub') => $stubsPath.'/controller.nested.api.stub',
realpath(__DIR__.'/../../Routing/Console/stubs/controller.nested.stub') => $stubsPath.'/controller.nested.stub',
realpath(__DIR__.'/../../Routing/Console/stubs/controller.plain.stub') => $stubsPath.'/controller.plain.stub',
realpath(__DIR__.'/../../Routing/Console/stubs/controller.stub') => $stubsPath.'/controller.stub',
realpath(__DIR__.'/../../Routing/Console/stubs/middleware.stub') => $stubsPath.'/middleware.stub',
];
foreach ($files as $from => $to) {
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.');
}
}

View File

@@ -0,0 +1,122 @@
<?php
namespace Illuminate\Foundation\Console;
use Illuminate\Console\GeneratorCommand;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputOption;
#[AsCommand(name: 'make:test')]
class TestMakeCommand extends GeneratorCommand
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'make:test';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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->option('pest')
? $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 [
['unit', 'u', InputOption::VALUE_NONE, 'Create a unit test.'],
['pest', 'p', InputOption::VALUE_NONE, 'Create a Pest test.'],
];
}
}

View File

@@ -0,0 +1,70 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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(MaintenanceModeDisabled::class);
$this->components->info('Application is now live.');
} catch (Exception $e) {
$this->components->error(sprintf(
'Failed to disable maintenance mode: %s.',
$e->getMessage(),
));
return 1;
}
}
}

View File

@@ -0,0 +1,339 @@
<?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;
#[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 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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = 'vendor:publish';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Publish any publishable assets from vendor packages';
/**
* 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->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()
{
$choice = $this->components->choice(
"Which provider or tag's files would you like to publish?",
$choices = $this->publishableChoices()
);
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(
['<comment>Publish files from all providers and tags listed below</comment>'],
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)
{
$published = false;
$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))) {
$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(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 \League\Flysystem\MountManager $manager
* @return void
*/
protected function moveManagedFiles($manager)
{
foreach ($manager->listContents('from://', true) 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))
)
) {
$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);
}
}
/**
* 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,
));
}
}

View File

@@ -0,0 +1,100 @@
<?php
namespace Illuminate\Foundation\Console;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
use Symfony\Component\Console\Attribute\AsCommand;
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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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) {
$this->compileViews($this->bladeFilesIn([$path]));
});
$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) {
$compiler->compile($file->getRealPath());
});
}
/**
* Get the Blade files in the given path.
*
* @param array $paths
* @return \Illuminate\Support\Collection
*/
protected function bladeFilesIn(array $paths)
{
return collect(
Finder::create()
->in($paths)
->exclude('vendor')
->name('*.blade.php')
->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()
);
}
}

View File

@@ -0,0 +1,79 @@
<?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 name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = '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.');
}
foreach ($this->files->glob("{$path}/*") as $view) {
$this->files->delete($view);
}
$this->components->info('Compiled views cleared successfully.');
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace {{ namespace }};
use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;
class {{ class }} implements CastsInboundAttributes
{
/**
* Prepare the given value for storage.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return mixed
*/
public function set($model, string $key, $value, array $attributes)
{
return $value;
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace {{ namespace }};
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class {{ class }} implements CastsAttributes
{
/**
* Cast the given value.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return mixed
*/
public function get($model, string $key, $value, array $attributes)
{
return $value;
}
/**
* Prepare the given value for storage.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return mixed
*/
public function set($model, string $key, $value, array $attributes)
{
return $value;
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace {{ namespace }};
use {{ namespacedUserModel }};
class {{ class }}
{
/**
* Create a new channel instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Authenticate the user's access to the channel.
*
* @param \{{ namespacedUserModel }} $user
* @return array|bool
*/
public function join({{ userModel }} $user)
{
//
}
}

View File

@@ -0,0 +1,32 @@
<?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.
*
* @return int
*/
public function handle()
{
return 0;
}
}

View File

@@ -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.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace {{ namespace }};
use Exception;
class {{ class }} extends Exception
{
/**
* Report the exception.
*
* @return bool|null
*/
public function report()
{
//
}
/**
* Render the exception as an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function render($request)
{
//
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace {{ namespace }};
use Exception;
class {{ class }} extends Exception
{
/**
* Render the exception as an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function render($request)
{
//
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace {{ namespace }};
use Exception;
class {{ class }} extends Exception
{
/**
* Report the exception.
*
* @return bool|null
*/
public function report()
{
//
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace {{ namespace }};
use Exception;
class {{ class }} extends Exception
{
//
}

View File

@@ -0,0 +1,35 @@
<?php
namespace {{ namespace }};
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class {{ class }} implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
//
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace {{ namespace }};
use Illuminate\Foundation\Bus\Dispatchable;
class {{ class }}
{
use Dispatchable;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
//
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace {{ namespace }};
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class {{ class }}
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param object $event
* @return void
*/
public function handle($event)
{
//
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace {{ namespace }};
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class {{ class }} implements ShouldQueue
{
use InteractsWithQueue;
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param object $event
* @return void
*/
public function handle($event)
{
//
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace {{ namespace }};
use {{ eventNamespace }};
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class {{ class }} implements ShouldQueue
{
use InteractsWithQueue;
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param {{ eventNamespace }} $event
* @return void
*/
public function handle({{ event }} $event)
{
//
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace {{ namespace }};
use {{ eventNamespace }};
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class {{ class }}
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param \{{ eventNamespace }} $event
* @return void
*/
public function handle({{ event }} $event)
{
//
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace {{ namespace }};
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class {{ class }} extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->view('view.name');
}
}

View File

@@ -0,0 +1,78 @@
<?php
// Check if the application is in maintenance mode...
if (! file_exists($down = __DIR__.'/down')) {
return;
}
// Decode the "down" file's JSON...
$data = json_decode(file_get_contents($down), true);
// Allow framework to handle request if no prerendered template...
if (! isset($data['template'])) {
return;
}
// Allow framework to handle request if request URI is in the exclude list...
if (isset($data['except'])) {
$uri = parse_url($_SERVER['REQUEST_URI'])['path'];
$uri = rawurldecode($uri !== '/' ? trim($uri, '/') : $uri);
foreach ((array) $data['except'] as $except) {
$except = $except !== '/' ? trim($except, '/') : $except;
if ($except == $uri) {
return;
}
$except = preg_quote($except, '#');
$except = str_replace('\*', '.*', $except);
if (preg_match('#^'.$except.'\z#u', $uri) === 1) {
return;
}
}
}
// Allow framework to handle maintenance mode bypass route...
if (isset($data['secret']) && $_SERVER['REQUEST_URI'] === '/'.$data['secret']) {
return;
}
// Determine if maintenance mode bypass cookie is valid...
if (isset($_COOKIE['laravel_maintenance']) && isset($data['secret'])) {
$payload = json_decode(base64_decode($_COOKIE['laravel_maintenance']), true);
if (is_array($payload) &&
is_numeric($payload['expires_at'] ?? null) &&
isset($payload['mac']) &&
hash_equals(hash_hmac('sha256', $payload['expires_at'], $data['secret']), $payload['mac']) &&
(int) $payload['expires_at'] >= time()) {
return;
}
}
// Redirect to the proper path if necessary...
if (isset($data['redirect']) && $_SERVER['REQUEST_URI'] !== $data['redirect']) {
http_response_code(302);
header('Location: '.$data['redirect']);
exit;
}
// Output the prerendered template...
http_response_code($data['status'] ?? 503);
if (isset($data['retry'])) {
header('Retry-After: '.$data['retry']);
}
if (isset($data['refresh'])) {
header('Refresh: '.$data['refresh']);
}
echo $data['template'];
exit;

View File

@@ -0,0 +1,33 @@
<?php
namespace {{ namespace }};
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class {{ class }} extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->markdown('{{ view }}');
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace {{ namespace }};
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class {{ class }} extends Notification
{
use Queueable;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)->markdown('{{ view }}');
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}

View File

@@ -0,0 +1,12 @@
@component('mail::message')
# Introduction
The body of your message.
@component('mail::button', ['url' => ''])
Button Text
@endcomponent
Thanks,<br>
{{ config('app.name') }}
@endcomponent

View File

@@ -0,0 +1,10 @@
<?php
namespace {{ namespace }};
use Illuminate\Database\Eloquent\Relations\MorphPivot;
class {{ class }} extends MorphPivot
{
//
}

View File

@@ -0,0 +1,10 @@
<?php
namespace {{ namespace }};
use Illuminate\Database\Eloquent\Relations\Pivot;
class {{ class }} extends Pivot
{
//
}

View File

@@ -0,0 +1,11 @@
<?php
namespace {{ namespace }};
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class {{ class }} extends Model
{
use HasFactory;
}

View File

@@ -0,0 +1,61 @@
<?php
namespace {{ namespace }};
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class {{ class }} extends Notification
{
use Queueable;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->line('The introduction to the notification.')
->action('Notification Action', url('/'))
->line('Thank you for using our application!');
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace {{ namespace }};
class {{ class }}
{
//
}

View File

@@ -0,0 +1,63 @@
<?php
namespace {{ namespace }};
use {{ namespacedModel }};
class {{ class }}
{
/**
* Handle the {{ model }} "created" event.
*
* @param \{{ namespacedModel }} ${{ modelVariable }}
* @return void
*/
public function created({{ model }} ${{ modelVariable }})
{
//
}
/**
* Handle the {{ model }} "updated" event.
*
* @param \{{ namespacedModel }} ${{ modelVariable }}
* @return void
*/
public function updated({{ model }} ${{ modelVariable }})
{
//
}
/**
* Handle the {{ model }} "deleted" event.
*
* @param \{{ namespacedModel }} ${{ modelVariable }}
* @return void
*/
public function deleted({{ model }} ${{ modelVariable }})
{
//
}
/**
* Handle the {{ model }} "restored" event.
*
* @param \{{ namespacedModel }} ${{ modelVariable }}
* @return void
*/
public function restored({{ model }} ${{ modelVariable }})
{
//
}
/**
* Handle the {{ model }} "force deleted" event.
*
* @param \{{ namespacedModel }} ${{ modelVariable }}
* @return void
*/
public function forceDeleted({{ model }} ${{ modelVariable }})
{
//
}
}

View File

@@ -0,0 +1,7 @@
<?php
test('example', function () {
$response = $this->get('/');
$response->assertStatus(200);
});

View File

@@ -0,0 +1,5 @@
<?php
test('example', function () {
expect(true)->toBeTrue();
});

View File

@@ -0,0 +1,21 @@
<?php
namespace {{ namespace }};
use Illuminate\Auth\Access\HandlesAuthorization;
use {{ namespacedUserModel }};
class {{ class }}
{
use HandlesAuthorization;
/**
* Create a new policy instance.
*
* @return void
*/
public function __construct()
{
//
}
}

View File

@@ -0,0 +1,94 @@
<?php
namespace {{ namespace }};
use Illuminate\Auth\Access\HandlesAuthorization;
use {{ namespacedModel }};
use {{ namespacedUserModel }};
class {{ class }}
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*
* @param \{{ namespacedUserModel }} $user
* @return \Illuminate\Auth\Access\Response|bool
*/
public function viewAny({{ user }} $user)
{
//
}
/**
* Determine whether the user can view the model.
*
* @param \{{ namespacedUserModel }} $user
* @param \{{ namespacedModel }} ${{ modelVariable }}
* @return \Illuminate\Auth\Access\Response|bool
*/
public function view({{ user }} $user, {{ model }} ${{ modelVariable }})
{
//
}
/**
* Determine whether the user can create models.
*
* @param \{{ namespacedUserModel }} $user
* @return \Illuminate\Auth\Access\Response|bool
*/
public function create({{ user }} $user)
{
//
}
/**
* Determine whether the user can update the model.
*
* @param \{{ namespacedUserModel }} $user
* @param \{{ namespacedModel }} ${{ modelVariable }}
* @return \Illuminate\Auth\Access\Response|bool
*/
public function update({{ user }} $user, {{ model }} ${{ modelVariable }})
{
//
}
/**
* Determine whether the user can delete the model.
*
* @param \{{ namespacedUserModel }} $user
* @param \{{ namespacedModel }} ${{ modelVariable }}
* @return \Illuminate\Auth\Access\Response|bool
*/
public function delete({{ user }} $user, {{ model }} ${{ modelVariable }})
{
//
}
/**
* Determine whether the user can restore the model.
*
* @param \{{ namespacedUserModel }} $user
* @param \{{ namespacedModel }} ${{ modelVariable }}
* @return \Illuminate\Auth\Access\Response|bool
*/
public function restore({{ user }} $user, {{ model }} ${{ modelVariable }})
{
//
}
/**
* Determine whether the user can permanently delete the model.
*
* @param \{{ namespacedUserModel }} $user
* @param \{{ namespacedModel }} ${{ modelVariable }}
* @return \Illuminate\Auth\Access\Response|bool
*/
public function forceDelete({{ user }} $user, {{ model }} ${{ modelVariable }})
{
//
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace {{ namespace }};
use Illuminate\Support\ServiceProvider;
class {{ class }} extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
//
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace {{ namespace }};
use Illuminate\Foundation\Http\FormRequest;
class {{ class }} extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
//
];
}
}

Some files were not shown because too many files have changed in this diff Show More