Repository: archtechx/tenancy (language: php)

Issue:
Tenant run command, argument with spaces not work
### Bug description

Run tenant aware command with argument with spaces is broken.


```shell
artisan tenants:run "make:company-plugin-resource user 'change email request'" --tenants=01JG6F1TDYG9075VK6H52WZ9QE

-tokens: array:5 [
  0 => "make:company-plugin-resource"
  1 => "user"
  2 => "'change"
  3 => "email"
  4 => "request'"
]
```

It splits into many arguments

this fix it
```php

// Convert string command to array
$subCommand = $this->explodeIgnoringQuotes($commandName);

function explodeIgnoringQuotes($input) {
      preg_match_all('/[^\s"\']+|"(.*?)"|\'(.*?)\'|`(.*?)`/', $input, $matches);

      $result = array_map(function ($match) {
          return trim($match, "\"'`");
      }, array_filter(array_merge($matches[0])));

      return $result;
  }
  ```
  
With this it works with:
- single quote (artisan tenants:run "make:company-plugin-resource user 'change email request'" --tenants=01JG6F1TDYG9075VK6H52WZ9QE)
- double quote (artisan tenants:run 'make:company-plugin-resource user "change email request"' --tenants=01JG6F1TDYG9075VK6H52WZ9QE)
- no quote (artisan tenants:run 'make:company-plugin-resource user XYZ' --tenants=01JG6F1TDYG9075VK6H52WZ9QE)

### Steps to reproduce

Run a command with argument with space

```shell
artisan tenants:run "make:company-plugin-resource user 'change email request'" --tenants=01JG6F1TDYG9075VK6H52WZ9QE

artisan tenants:run 'make:company-plugin-resource user "change email request"' --tenants=01JG6F1TDYG9075VK6H52WZ9QE
```

### Expected behavior

Run command with the right arguments

### Laravel version

8.4

### stancl/tenancy version

dev-master


Discussion / hints:

Fix patch:
diff --git a/src/Commands/Run.php b/src/Commands/Run.php
index afc9871a2..7dd69e0fd 100644
--- a/src/Commands/Run.php
+++ b/src/Commands/Run.php
@@ -7,7 +7,7 @@
 use Illuminate\Console\Command;
 use Illuminate\Contracts\Console\Kernel;
 use Stancl\Tenancy\Concerns\HasTenantOptions;
-use Symfony\Component\Console\Input\ArgvInput;
+use Symfony\Component\Console\Input\StringInput;
 use Symfony\Component\Console\Output\ConsoleOutput;
 
 class Run extends Command
@@ -21,30 +21,19 @@ class Run extends Command
 
     public function handle(): int
     {
-        $argvInput = $this->argvInput();
+        /** @var string $commandName */
+        $commandName = $this->argument('commandname');
+
+        $stringInput = new StringInput($commandName);
 
-        tenancy()->runForMultiple($this->getTenants(), function ($tenant) use ($argvInput) {
+        tenancy()->runForMultiple($this->getTenants(), function ($tenant) use ($stringInput) {
             $this->components->info("Tenant: {$tenant->getTenantKey()}");
 
             $this->getLaravel()
                 ->make(Kernel::class)
-                ->handle($argvInput, new ConsoleOutput);
+                ->handle($stringInput, new ConsoleOutput);
         });
 
         return 0;
     }
-
-    protected function argvInput(): ArgvInput
-    {
-        /** @var string $commandName */
-        $commandName = $this->argument('commandname');
-
-        // Convert string command to array
-        $subCommand = explode(' ', $commandName);
-
-        // Add "artisan" as first parameter because ArgvInput expects "artisan" as first parameter and later removes it
-        array_unshift($subCommand, 'artisan');
-
-        return new ArgvInput($subCommand);
-    }
 }


Test patch:
diff --git a/tests/CommandsTest.php b/tests/CommandsTest.php
index ad50ba1a1..aecbb07cf 100644
--- a/tests/CommandsTest.php
+++ b/tests/CommandsTest.php
@@ -389,6 +389,21 @@
     expect($user->email)->toBe('email@localhost');
 });
 
+test('run command accepts arguments and options correctly', function() {
+    $tenant = Tenant::create();
+    $id = $tenant->getTenantKey();
+
+    // Use unquoted single-word arguments and quoted arguments with spaces
+    pest()->artisan("tenants:run \"bar username 'email@localhost' adsfg123 'some Arg' --option='some option'\" --tenants=$id")
+        ->expectsOutputToContain("Tenant: $id.")
+        ->expectsOutput("Name: username")
+        ->expectsOutput("Email: email@localhost")
+        ->expectsOutput("Password: adsfg123")
+        ->expectsOutput("Argument: some Arg")
+        ->expectsOutput("Option: some option")
+        ->assertExitCode(0);
+});
+
 test('migrate fresh command only deletes tenant databases if drop_tenant_databases_on_migrate_fresh is true', function (bool $dropTenantDBsOnMigrateFresh) {
     Event::listen(DeletingTenant::class,
         JobPipeline::make([DeleteDomains::class])->send(function (DeletingTenant $event) {
diff --git a/tests/Etc/Console/AnotherExampleCommand.php b/tests/Etc/Console/AnotherExampleCommand.php
new file mode 100644
index 000000000..eba0a0c3b
--- /dev/null
+++ b/tests/Etc/Console/AnotherExampleCommand.php
@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Stancl\Tenancy\Tests\Etc\Console;
+
+use Illuminate\Console\Command;
+
+class AnotherExampleCommand extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'bar {name} {email} {password} {arg} {--option=}';
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $this->line('Name: ' . $this->argument('name'));
+        $this->line('Email: ' . $this->argument('email'));
+        $this->line('Password: ' . $this->argument('password'));
+        $this->line('Argument: ' . $this->argument('arg'));
+        $this->line('Option: ' . $this->option('option'));
+    }
+}
diff --git a/tests/Etc/Console/ConsoleKernel.php b/tests/Etc/Console/ConsoleKernel.php
index c5e5ee85f..b46d21c77 100644
--- a/tests/Etc/Console/ConsoleKernel.php
+++ b/tests/Etc/Console/ConsoleKernel.php
@@ -10,6 +10,7 @@ class ConsoleKernel extends Kernel
 {
     protected $commands = [
         ExampleCommand::class,
+        AnotherExampleCommand::class,
         ExampleQuestionCommand::class,
         AddUserCommand::class,
     ];