Completed
Pull Request — master (#5)
by
unknown
01:45
created

MakeUser::setDefaultPassword()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
c 0
b 0
f 0
ccs 0
cts 3
cp 0
rs 9.4285
cc 1
eloc 4
nc 1
nop 1
crap 2
1
<?php
2
3
namespace Dyrynda\Artisan\Console\Commands;
4
5
use Exception;
6
use SplFileInfo;
7
use Illuminate\Console\Command;
8
use Illuminate\Support\Facades\Password;
9
use Dyrynda\Artisan\Exceptions\MakeUserException;
10
use Dyrynda\Artisan\Exceptions\ImportFileException;
11
use Dyrynda\Artisan\BulkImport\BulkImportFileHandler;
12
13
class MakeUser extends Command
14
{
15
    /**
16
     * The name and signature of the console command.
17
     *
18
     * @var string
19
     */
20
    protected $signature = 'make:user
21
                                {--email=       : Set the email for the new user}
22
                                {--name=        : Set the name for the new user}
23
                                {--password=    : The password to set for the new user}
24
                                {--send-reset   : Send a password reset email for the new user}
25
                                {--fields=      : Additional database fields to set on the user}
26
                                {--force        : Create the user model circumventing guarded fields}
27
                                {--import-file= : Relative path and filename for a file to import users from. File name MUST contain the extension representing the type of file (Ex: ./path/to/file.csv)}
28
    ';
29
30
    /**
31
     * The console command description.
32
     *
33
     * @var string
34
     */
35
    protected $description = 'Create new application users';
36
37
    /**
38
     * Execute the console command.
39
     *
40
     * Handle creation of the new application user.
41
     *
42
     * @return void
43
     */
44
    public function handle()
45
    {
46
        try {
47
            $bulkImportFile = $this->option('import-file') ? $this->fileHandlerFactory($this->option('import-file')) : null;
0 ignored issues
show
Bug introduced by
It seems like $this->option('import-file') targeting Illuminate\Console\Command::option() can also be of type array; however, Dyrynda\Artisan\Console\...r::fileHandlerFactory() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
48
49
            $modelCommand = $this->option('force') ? 'forceCreate' : 'create';
50
51
            if (! is_null($bulkImportFile)) {
52
                $dataToProcess = $bulkImportFile->getData();
53
54
                $sendReset = false;
55
56
                if (! in_array('password', array_keys($dataToProcess[0]))) {
57
                    $dataToProcess = $this->setDefaultPassword($dataToProcess);
58
                    $sendReset = true;
59
                }
60
61
            } else {
62
                $email = $this->option('email');
63
                $name = $this->option('name') ?: '';
64
                $password = bcrypt($this->option('password') ?: str_random(32));
65
                $sendReset = ! $this->option('password') || $this->option('send-reset');
66
67
                $dataToProcess[0] = [
0 ignored issues
show
Coding Style Comprehensibility introduced by
$dataToProcess was never initialized. Although not strictly required by PHP, it is generally a good practice to add $dataToProcess = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
68
                    'email' => $email,
69
                    'name' => $name,
70
                    'password' => $password,
71
                ];
72
            }
73
74
            foreach ($dataToProcess as $dataRow) {
75
76
                $email = $dataRow['email'] ?? null;
77
78
                app('db')->beginTransaction();
79
80
                $this->validateEmail($email);
81
82
                app(config('auth.providers.users.model'))->{$modelCommand}(array_merge(
83
                    $dataRow,
84
                    $bulkImportFile ? [] : $this->additionalFields()
85
                ));
86
87
                if ($sendReset) {
88
                    Password::sendResetLink(compact('email'));
89
                }
90
91
                app('db')->commit();
92
            }
93
94
            $createdMessage = $bulkImportFile
95
                ? "Created " . count($dataToProcess) . " user(s)."
96
                : "Created new user for email {$email}.";
0 ignored issues
show
Bug introduced by
The variable $email does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
97
98
            $passwordResetMessage =  $bulkImportFile
99
                ? "Sent password reset emails."
100
                : "Sent password reset email to {$email}.";
101
102
            $this->info($createdMessage);
103
104
            if ($sendReset) {
105
                $this->info($passwordResetMessage);
106
            }
107
108
        } catch (Exception $e) {
109
            $this->error($e->getMessage());
110
111
            $this->error('The user(s) were not created');
112
113
            app('db')->rollBack();
114
        }
115
    }
116
117
    /**
118
     * Determine if the given email address already exists.
119
     *
120
     * @param  string  $email
121
     * @return void
122
     *
123
     * @throws \Dyrynda\Artisan\Exceptions\MakeUserException
124
     */
125
    private function validateEmail($email)
126
    {
127
        if (! filter_var($email, FILTER_VALIDATE_EMAIL)) {
128
            throw MakeUserException::invalidEmail($email);
129
        }
130
131
        if (app(config('auth.providers.users.model'))->where('email', $email)->exists()) {
132
            throw MakeUserException::emailExists($email);
133
        }
134
    }
135
136
    /**
137
     * Return any additional database fields passed by the --fields option.
138
     *
139
     * @return array
140
     */
141
    private function additionalFields()
142
    {
143
        if (! $this->option('fields')) {
144
            return [];
145
        }
146
147
        return collect(explode(',', $this->option('fields')))->mapWithKeys(function ($field) {
148
            list($column, $value) = explode(':', $field);
149
150
            return [trim($column) => $this->normaliseValue($value)];
151
        })->toArray();
152
    }
153
154
    /**
155
     * Normalise the given (database) field input value.
156
     *
157
     * @param  mixed  $value
158
     * @return mixed
159
     */
160
    private function normaliseValue($value)
161
    {
162
        if ($value == 'null') {
163
            return;
164
        }
165
166 View Code Duplication
        if (in_array($value, [1, 'true', true], true)) {
167
            return true;
168
        }
169
170 View Code Duplication
        if (in_array($value, [0, 'false', false], true)) {
171
            return false;
172
        }
173
174
        return trim($value);
175
    }
176
177
    /**
178
     * Create file handler objects
179
     *
180
     * @param string  $path
181
     * @return BulkImportFileHandler
182
     *
183
     * @throws \Dyrynda\Artisan\Exceptions\ImportFileException
184
     */
185
    private function fileHandlerFactory($path) : BulkImportFileHandler
186
    {
187
        if (! strpos($path, '.')) {
188
            throw ImportFileException::noExtension();
189
        }
190
191
        $file = new SplFileInfo($path);
192
193
        if (! class_exists($class = '\\Dyrynda\\Artisan\\BulkImport\\Handlers\\' . studly_case($file->getExtension()))) {
194
            throw ImportFileException::unsupported($file->getExtension());
195
        }
196
197
        return new $class($path);
198
    }
199
200
    /**
201
     * Add default password to data
202
     *
203
     * @param array  $data
204
     * @return array
205
     *
206
     */
207
    private function setDefaultPassword($data)
208
    {
209
        return collect($data)->map(function($row){
210
            return array_merge(['password' => str_random(32)], $row);
211
        })->all();
212
    }
213
}
214