Вход Регистрация
Файл: vendor/laravel/framework/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php
Строк: 535
<?php

namespace IlluminateFoundationConsole;

use 
ComposerInstalledVersions;
use 
IlluminateConsoleCommand;
use 
IlluminateFilesystemFilesystem;
use 
IlluminateSupportEnv;
use 
IlluminateSupportFacadesProcess;
use 
SymfonyComponentConsoleAttributeAsCommand;

use function 
IlluminateSupportartisan_binary;
use function 
IlluminateSupportphp_binary;
use function 
LaravelPromptsconfirm;
use function 
LaravelPromptspassword;
use function 
LaravelPromptsselect;
use function 
LaravelPromptstext;

#[AsCommand(name: 'install:broadcasting')]
class BroadcastingInstallCommand extends Command
{
    use 
InteractsWithComposerPackages;

    
/**
     * The name and signature of the console command.
     *
     * @var string
     */
    
protected $signature 'install:broadcasting
                    {--composer=global : Absolute path to the Composer binary which should be used to install packages}
                    {--force : Overwrite any existing broadcasting routes file}
                    {--without-reverb : Do not prompt to install Laravel Reverb}
                    {--reverb : Install Laravel Reverb as the default broadcaster}
                    {--pusher : Install Pusher as the default broadcaster}
                    {--ably : Install Ably as the default broadcaster}
                    {--without-node : Do not prompt to install Node dependencies}'
;

    
/**
     * The console command description.
     *
     * @var string
     */
    
protected $description 'Create a broadcasting channel routes file';

    
/**
     * The broadcasting driver to use.
     *
     * @var string|null
     */
    
protected $driver null;

    
/**
     * The framework packages to install.
     *
     * @var array
     */
    
protected $frameworkPackages = [
        
'react' => '@laravel/echo-react',
        
'vue' => '@laravel/echo-vue',
    ];

    
/**
     * Execute the console command.
     *
     * @return void
     */
    
public function handle()
    {
        
$this->call('config:publish', ['name' => 'broadcasting']);

        
// Install channel routes file...
        
if (! file_exists($broadcastingRoutesPath $this->laravel->basePath('routes/channels.php')) || $this->option('force')) {
            
$this->components->info("Published 'channels' route file.");

            
copy(__DIR__.'/stubs/broadcasting-routes.stub'$broadcastingRoutesPath);
        }

        
$this->uncommentChannelsRoutesFile();
        
$this->enableBroadcastServiceProvider();

        
$this->driver $this->resolveDriver();

        
Env::writeVariable('BROADCAST_CONNECTION'$this->driver$this->laravel->basePath('.env'), true);

        
$this->collectDriverConfig();
        
$this->installDriverPackages();

        if (
$this->isUsingSupportedFramework()) {
            
// If this is a supported framework, we will use the framework-specific Echo helpers...
            
$this->injectFrameworkSpecificConfiguration();
        } else {
            
// Standard JavaScript implementation...
            
if (! file_exists($echoScriptPath $this->laravel->resourcePath('js/echo.js'))) {
                if (! 
is_dir($directory $this->laravel->resourcePath('js'))) {
                    
mkdir($directory0755true);
                }

                
$stubPath __DIR__.'/stubs/echo-js-'.$this->driver.'.stub';

                if (! 
file_exists($stubPath)) {
                    
$stubPath __DIR__.'/stubs/echo-js-reverb.stub';
                }

                
copy($stubPath$echoScriptPath);
            }

            
// Only add the bootstrap import for the standard JS implementation...
            
if (file_exists($bootstrapScriptPath $this->laravel->resourcePath('js/bootstrap.js'))) {
                
$bootstrapScript file_get_contents(
                    
$bootstrapScriptPath
                
);

                if (! 
str_contains($bootstrapScript'./echo')) {
                    
file_put_contents(
                        
$bootstrapScriptPath,
                        
trim($bootstrapScript.PHP_EOL.file_get_contents(__DIR__.'/stubs/echo-bootstrap-js.stub')).PHP_EOL,
                    );
                }
            } elseif (
file_exists($appScriptPath $this->laravel->resourcePath('js/app.js'))) {
                
// If no bootstrap.js, try app.js...
                
$appScript file_get_contents(
                    
$appScriptPath
                
);

                if (! 
str_contains($appScript'./echo')) {
                    
file_put_contents(
                        
$appScriptPath,
                        
trim($appScript.PHP_EOL.file_get_contents(__DIR__.'/stubs/echo-bootstrap-js.stub')).PHP_EOL,
                    );
                }
            }
        }

        
$this->installReverb();

        
$this->installNodeDependencies();
    }

    
/**
     * Uncomment the "channels" routes file in the application bootstrap file.
     *
     * @return void
     */
    
protected function uncommentChannelsRoutesFile()
    {
        
$appBootstrapPath $this->laravel->bootstrapPath('app.php');

        
$content file_get_contents($appBootstrapPath);

        if (
str_contains($content'// channels: ')) {
            (new 
Filesystem)->replaceInFile(
                
'// channels: ',
                
'channels: ',
                
$appBootstrapPath,
            );
        } elseif (
str_contains($content'channels: ')) {
            return;
        } elseif (
str_contains($content'commands: __DIR__.'/../routes/console.php',')) {
            (new 
Filesystem)->replaceInFile(
                
'commands: __DIR__.'/../routes/console.php',',
                
'commands: __DIR__.'/../routes/console.php','.PHP_EOL.'        channels: __DIR__.'/../routes/channels.php',',
                
$appBootstrapPath,
            );
        } elseif (
str_contains($content'->withRouting(')) {
            (new 
Filesystem)->replaceInFile(
                
'->withRouting(',
                
'->withRouting('.PHP_EOL.'        channels: __DIR__.'/../routes/channels.php',',
                
$appBootstrapPath,
            );
        } else {
            
$this->components->error('Unable to register broadcast routes. Please register them manually in ['.$appBootstrapPath.'].');
        }
    }

    
/**
     * Uncomment the "BroadcastServiceProvider" in the application configuration.
     *
     * @return void
     */
    
protected function enableBroadcastServiceProvider()
    {
        
$filesystem = new Filesystem;

        if (
            ! 
$filesystem->exists(app()->configPath('app.php')) ||
            ! 
$filesystem->exists('app/Providers/BroadcastServiceProvider.php')
        ) {
            return;
        }

        
$config $filesystem->get(app()->configPath('app.php'));

        if (
str_contains($config'// AppProvidersBroadcastServiceProvider::class')) {
            
$filesystem->replaceInFile(
                
'// AppProvidersBroadcastServiceProvider::class',
                
'AppProvidersBroadcastServiceProvider::class',
                
app()->configPath('app.php'),
            );
        }
    }

    
/**
     * Collect the driver configuration.
     *
     * @return void
     */
    
protected function collectDriverConfig()
    {
        
$envPath $this->laravel->basePath('.env');

        if (! 
file_exists($envPath)) {
            return;
        }

        
match ($this->driver) {
            
'pusher' => $this->collectPusherConfig(),
            
'ably' => $this->collectAblyConfig(),
            default => 
null,
        };
    }

    
/**
     * Install the driver packages.
     *
     * @return void
     */
    
protected function installDriverPackages()
    {
        
$package match ($this->driver) {
            
'pusher' => 'pusher/pusher-php-server',
            
'ably' => 'ably/ably-php',
            default => 
null,
        };

        if (! 
$package || InstalledVersions::isInstalled($package)) {
            return;
        }

        
$this->requireComposerPackages($this->option('composer'), [$package]);
    }

    
/**
     * Collect the Pusher configuration.
     *
     * @return void
     */
    
protected function collectPusherConfig()
    {
        
$appId text('Pusher App ID''Enter your Pusher app ID');
        
$key password('Pusher App Key''Enter your Pusher app key');
        
$secret password('Pusher App Secret''Enter your Pusher app secret');

        
$cluster select('Pusher App Cluster', [
            
'mt1',
            
'us2',
            
'us3',
            
'eu',
            
'ap1',
            
'ap2',
            
'ap3',
            
'ap4',
            
'sa1',
        ]);

        
Env::writeVariables([
            
'PUSHER_APP_ID' => $appId,
            
'PUSHER_APP_KEY' => $key,
            
'PUSHER_APP_SECRET' => $secret,
            
'PUSHER_APP_CLUSTER' => $cluster,
            
'PUSHER_PORT' => 443,
            
'PUSHER_SCHEME' => 'https',
            
'VITE_PUSHER_APP_KEY' => '${PUSHER_APP_KEY}',
            
'VITE_PUSHER_APP_CLUSTER' => '${PUSHER_APP_CLUSTER}',
            
'VITE_PUSHER_HOST' => '${PUSHER_HOST}',
            
'VITE_PUSHER_PORT' => '${PUSHER_PORT}',
            
'VITE_PUSHER_SCHEME' => '${PUSHER_SCHEME}',
        ], 
$this->laravel->basePath('.env'));
    }

    
/**
     * Collect the Ably configuration.
     *
     * @return void
     */
    
protected function collectAblyConfig()
    {
        
$this->components->warn('Make sure to enable "Pusher protocol support" in your Ably app settings.');

        
$key password('Ably Key''Enter your Ably key');

        
$publicKey explode(':'$key)[0] ?? $key;

        
Env::writeVariables([
            
'ABLY_KEY' => $key,
            
'ABLY_PUBLIC_KEY' => $publicKey,
            
'VITE_ABLY_PUBLIC_KEY' => '${ABLY_PUBLIC_KEY}',
        ], 
$this->laravel->basePath('.env'));
    }

    
/**
     * Inject Echo configuration into the application's main file.
     *
     * @return void
     */
    
protected function injectFrameworkSpecificConfiguration()
    {
        if (
$this->appUsesVue()) {
            
$importPath $this->frameworkPackages['vue'];

            
$filePaths = [
                
$this->laravel->resourcePath('js/app.ts'),
                
$this->laravel->resourcePath('js/app.js'),
            ];
        } else {
            
$importPath $this->frameworkPackages['react'];

            
$filePaths = [
                
$this->laravel->resourcePath('js/app.tsx'),
                
$this->laravel->resourcePath('js/app.jsx'),
            ];
        }

        
$filePath array_filter($filePaths, function ($path) {
            return 
file_exists($path);
        })[
0] ?? null;

        if (! 
$filePath) {
            
$this->components->warn("Could not find file [{$filePaths[0]}]. Skipping automatic Echo configuration.");

            return;
        }

        
$contents file_get_contents($filePath);

        
$echoCode = <<<JS
        import { configureEcho } from '{$importPath}';

        configureEcho({
            broadcaster: '
{$this->driver}',
        });
        JS;

        
preg_match_all('/^import .+;$/m'$contents$matches);

        if (empty(
$matches[0])) {
            
// Add the Echo configuration to the top of the file if no import statements are found...
            
$newContents $echoCode.PHP_EOL.$contents;

            
file_put_contents($filePath$newContents);
        } else {
            
// Add Echo configuration after the last import...
            
$lastImport array_last($matches[0]);

            
$positionOfLastImport strrpos($contents$lastImport);

            if (
$positionOfLastImport !== false) {
                
$insertPosition $positionOfLastImport strlen($lastImport);
                
$newContents substr($contents0$insertPosition).PHP_EOL.$echoCode.substr($contents$insertPosition);

                
file_put_contents($filePath$newContents);
            }
        }

        
$this->components->info('Echo configuration added to ['.basename($filePath).'].');
    }

    
/**
     * Install Laravel Reverb into the application if desired.
     *
     * @return void
     */
    
protected function installReverb()
    {
        if (
$this->driver !== 'reverb' || $this->option('without-reverb') || InstalledVersions::isInstalled('laravel/reverb')) {
            return;
        }

        if (! 
confirm('Would you like to install Laravel Reverb?', default: true)) {
            return;
        }

        
$this->requireComposerPackages($this->option('composer'), [
            
'laravel/reverb:^1.0',
        ]);

        
Process::run([
            
php_binary(),
            
artisan_binary(),
            
'reverb:install',
        ]);

        
$this->components->info('Reverb installed successfully.');
    }

    
/**
     * Install and build Node dependencies.
     *
     * @return void
     */
    
protected function installNodeDependencies()
    {
        if (
$this->option('without-node') || ! confirm('Would you like to install and build the Node dependencies required for broadcasting?', default: true)) {
            return;
        }

        
$this->components->info('Installing and building Node dependencies.');

        if (
file_exists(base_path('pnpm-lock.yaml'))) {
            
$commands = [
                
'pnpm add --save-dev laravel-echo pusher-js --ignore-scripts',
                
'pnpm run build',
            ];
        } elseif (
file_exists(base_path('yarn.lock'))) {
            
$commands = [
                
'yarn add --dev laravel-echo pusher-js --ignore-scripts',
                
'yarn run build',
            ];
        } elseif (
file_exists(base_path('bun.lock')) || file_exists(base_path('bun.lockb'))) {
            
$commands = [
                
'bun add --dev laravel-echo pusher-js',
                
'bun run build',
            ];
        } else {
            
$commands = [
                
'npm install --save-dev laravel-echo pusher-js --ignore-scripts',
                
'npm run build',
            ];
        }

        if (
$this->appUsesVue()) {
            
$commands[0] .= ' '.$this->frameworkPackages['vue'];
        } elseif (
$this->appUsesReact()) {
            
$commands[0] .= ' '.$this->frameworkPackages['react'];
        }

        
$command Process::command(implode(' && '$commands))
            ->
path(base_path());

        if (! 
windows_os()) {
            
$command->tty(true);
        }

        if (
$command->run()->failed()) {
            
$this->components->warn("Node dependency installation failed. Please run the following commands manually: nn".implode(' && '$commands));
        } else {
            
$this->components->info('Node dependencies installed successfully.');
        }
    }

    
/**
     * Resolve the provider to use based on the user's choice.
     *
     * @return string
     */
    
protected function resolveDriver(): string
    
{
        if (
$this->option('reverb')) {
            return 
'reverb';
        }

        if (
$this->option('pusher')) {
            return 
'pusher';
        }

        if (
$this->option('ably')) {
            return 
'ably';
        }

        return 
select('Which broadcasting driver would you like to use?', [
            
'reverb' => 'Laravel Reverb',
            
'pusher' => 'Pusher',
            
'ably' => 'Ably',
        ]);
    }

    
/**
     * Detect if the user is using a supported framework (React or Vue).
     *
     * @return bool
     */
    
protected function isUsingSupportedFramework(): bool
    
{
        return 
$this->appUsesReact() || $this->appUsesVue();
    }

    
/**
     * Detect if the user is using React.
     *
     * @return bool
     */
    
protected function appUsesReact(): bool
    
{
        return 
$this->packageDependenciesInclude('react');
    }

    
/**
     * Detect if the user is using Vue.
     *
     * @return bool
     */
    
protected function appUsesVue(): bool
    
{
        return 
$this->packageDependenciesInclude('vue');
    }

    
/**
     * Detect if the package is installed.
     *
     * @return bool
     */
    
protected function packageDependenciesInclude(string $package): bool
    
{
        
$packageJsonPath $this->laravel->basePath('package.json');

        if (! 
file_exists($packageJsonPath)) {
            return 
false;
        }

        
$packageJson json_decode(file_get_contents($packageJsonPath), true);

        return isset(
$packageJson['dependencies'][$package]) ||
            isset(
$packageJson['devDependencies'][$package]);
    }
}
Онлайн: 1
Реклама