GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — develop ( a52d9e...9343ac )
by Dane
02:58
created

ServerRepository::updateSFTPPassword()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 24
rs 8.9713
cc 2
eloc 14
nc 2
nop 2
1
<?php
2
/**
3
 * Pterodactyl - Panel
4
 * Copyright (c) 2015 - 2017 Dane Everitt <[email protected]>.
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in all
14
 * copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
 * SOFTWARE.
23
 */
24
25
namespace Pterodactyl\Repositories;
26
27
use DB;
28
use Crypt;
29
use Validator;
30
use Pterodactyl\Models\Node;
31
use Pterodactyl\Models\Pack;
32
use Pterodactyl\Models\User;
33
use Pterodactyl\Models\Server;
34
use Pterodactyl\Models\Service;
35
use Pterodactyl\Models\Allocation;
36
use Pterodactyl\Models\ServiceOption;
37
use Pterodactyl\Services\UuidService;
38
use Pterodactyl\Models\ServerVariable;
39
use Pterodactyl\Models\ServiceVariable;
40
use GuzzleHttp\Exception\ClientException;
41
use GuzzleHttp\Exception\TransferException;
42
use Pterodactyl\Services\DeploymentService;
43
use Pterodactyl\Exceptions\DisplayException;
44
use Pterodactyl\Exceptions\DisplayValidationException;
45
46
class ServerRepository
47
{
48
    /**
49
     * An array of daemon permission to assign to this server.
50
     *
51
     * @var array
52
     */
53
    protected $daemonPermissions = [
54
        's:*',
55
    ];
56
57
    /**
58
     * Generates a SFTP username for a server given a server name.
59
     * format: mumble_67c7a4b0.
60
     *
61
     * @param  string       $name
62
     * @param  null|string  $identifier
63
     * @return string
64
     */
65
    protected function generateSFTPUsername($name, $identifier = null)
66
    {
67
        if (is_null($identifier) || ! ctype_alnum($identifier)) {
68
            $unique = str_random(8);
69
        } else {
70
            if (strlen($identifier) < 8) {
71
                $unique = $identifier . str_random((8 - strlen($identifier)));
72
            } else {
73
                $unique = substr($identifier, 0, 8);
74
            }
75
        }
76
77
        // Filter the Server Name
78
        $name = trim(preg_replace('/[^\w]+/', '', $name), '_');
79
        $name = (strlen($name) < 1) ? str_random(6) : $name;
80
81
        return strtolower(substr($name, 0, 6) . '_' . $unique);
82
    }
83
84
    /**
85
     * Adds a new server to the system.
86
     *
87
     * @param   array  $data
88
     * @return \Pterodactyl\Models\Server
89
     *
90
     * @throws \Pterodactyl\Exceptions\DisplayException
91
     * @throws \Pterodactyl\Exceptions\AutoDeploymentException
92
     * @throws \Pterodactyl\Exceptions\DisplayValidationException
93
     */
94
    public function create(array $data)
95
    {
96
        $validator = Validator::make($data, [
97
            'user_id' => 'required|exists:users,id',
98
            'name' => 'required|regex:/^([\w .-]{1,200})$/',
99
            'description' => 'sometimes|nullable|string',
100
            'memory' => 'required|numeric|min:0',
101
            'swap' => 'required|numeric|min:-1',
102
            'io' => 'required|numeric|min:10|max:1000',
103
            'cpu' => 'required|numeric|min:0',
104
            'disk' => 'required|numeric|min:0',
105
            'service_id' => 'required|numeric|min:1|exists:services,id',
106
            'option_id' => 'required|numeric|min:1|exists:service_options,id',
107
            'location_id' => 'required|numeric|min:1|exists:locations,id',
108
            'pack_id' => 'sometimes|nullable|numeric|min:0',
109
            'custom_container' => 'string',
110
            'startup' => 'string',
111
            'auto_deploy' => 'sometimes|required|accepted',
112
            'custom_id' => 'sometimes|required|numeric|unique:servers,id',
113
            'skip_scripts' => 'sometimes|required|boolean',
114
        ]);
115
116
        $validator->sometimes('node_id', 'required|numeric|min:1|exists:nodes,id', function ($input) {
117
            return ! ($input->auto_deploy);
118
        });
119
120
        $validator->sometimes('allocation_id', 'required|numeric|exists:allocations,id', function ($input) {
121
            return ! ($input->auto_deploy);
122
        });
123
124
        $validator->sometimes('allocation_additional.*', 'sometimes|required|numeric|exists:allocations,id', function ($input) {
125
            return ! ($input->auto_deploy);
126
        });
127
128
        // Run validator, throw catchable and displayable exception if it fails.
129
        // Exception includes a JSON result of failed validation rules.
130
        if ($validator->fails()) {
131
            throw new DisplayValidationException(json_encode($validator->errors()));
132
        }
133
134
        $user = User::findOrFail($data['user_id']);
135
136
        $deployment = false;
137
        if (isset($data['auto_deploy'])) {
138
            $deployment = new DeploymentService;
139
140
            if (isset($data['location_id'])) {
141
                $deployment->setLocation($data['location_id']);
142
            }
143
144
            $deployment->setMemory($data['memory'])->setDisk($data['disk'])->select();
145
        }
146
147
        $node = (! $deployment) ? Node::findOrFail($data['node_id']) : $deployment->node();
148
149
        // Verify IP & Port are a.) free and b.) assigned to the node.
150
        // We know the node exists because of 'exists:nodes,id' in the validation
151
        if (! $deployment) {
152
            $allocation = Allocation::where('id', $data['allocation_id'])->where('node_id', $data['node_id'])->whereNull('server_id')->first();
153
        } else {
154
            $allocation = $deployment->allocation();
155
        }
156
157
        // Something failed in the query, either that combo doesn't exist, or it is in use.
158
        if (! $allocation) {
159
            throw new DisplayException('The selected Allocation ID is either already in use, or unavaliable for this node.');
160
        }
161
162
        // Validate those Service Option Variables
163
        // We know the service and option exists because of the validation.
164
        // We need to verify that the option exists for the service, and then check for
165
        // any required variable fields. (fields are labeled env_<env_variable>)
166
        $option = ServiceOption::where('id', $data['option_id'])->where('service_id', $data['service_id'])->first();
167
        if (! $option) {
168
            throw new DisplayException('The requested service option does not exist for the specified service.');
169
        }
170
171
        // Validate the Pack
172 View Code Duplication
        if (! isset($data['pack_id']) || (int) $data['pack_id'] < 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
173
            $data['pack_id'] = null;
174
        } else {
175
            $pack = Pack::where('id', $data['pack_id'])->where('option_id', $data['option_id'])->first();
176
            if (! $pack) {
177
                throw new DisplayException('The requested service pack does not seem to exist for this combination.');
178
            }
179
        }
180
181
        // Load up the Service Information
182
        $service = Service::find($option->service_id);
183
184
        // Check those Variables
185
        $variables = ServiceVariable::where('option_id', $data['option_id'])->get();
186
        $variableList = [];
187
        if ($variables) {
188
            foreach ($variables as $variable) {
189
190
                // Is the variable required?
191
                if (! isset($data['env_' . $variable->env_variable])) {
192
                    if ($variable->required) {
193
                        throw new DisplayException('A required service option variable field (env_' . $variable->env_variable . ') was missing from the request.');
194
                    }
195
                    $variableList[] = [
196
                        'id' => $variable->id,
197
                        'env' => $variable->env_variable,
198
                        'val' => $variable->default_value,
199
                    ];
200
                    continue;
201
                }
202
203
                // Check aganist Regex Pattern
204
                if (! is_null($variable->regex) && ! preg_match($variable->regex, $data['env_' . $variable->env_variable])) {
205
                    throw new DisplayException('Failed to validate service option variable field (env_' . $variable->env_variable . ') aganist regex (' . $variable->regex . ').');
206
                }
207
208
                $variableList[] = [
209
                    'id' => $variable->id,
210
                    'env' => $variable->env_variable,
211
                    'val' => $data['env_' . $variable->env_variable],
212
                ];
213
                continue;
214
            }
215
        }
216
217
        // Check Overallocation
218
        if (! $deployment) {
219
            if (is_numeric($node->memory_overallocate) || is_numeric($node->disk_overallocate)) {
220
                $totals = Server::select(DB::raw('SUM(memory) as memory, SUM(disk) as disk'))->where('node_id', $node->id)->first();
221
222
                // Check memory limits
223 View Code Duplication
                if (is_numeric($node->memory_overallocate)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
224
                    $newMemory = $totals->memory + $data['memory'];
225
                    $memoryLimit = ($node->memory * (1 + ($node->memory_overallocate / 100)));
226
                    if ($newMemory > $memoryLimit) {
227
                        throw new DisplayException('The amount of memory allocated to this server would put the node over its allocation limits. This node is allowed ' . ($node->memory_overallocate + 100) . '% of its assigned ' . $node->memory . 'Mb of memory (' . $memoryLimit . 'Mb) of which ' . (($totals->memory / $node->memory) * 100) . '% (' . $totals->memory . 'Mb) is in use already. By allocating this server the node would be at ' . (($newMemory / $node->memory) * 100) . '% (' . $newMemory . 'Mb) usage.');
228
                    }
229
                }
230
231
                // Check Disk Limits
232 View Code Duplication
                if (is_numeric($node->disk_overallocate)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
233
                    $newDisk = $totals->disk + $data['disk'];
234
                    $diskLimit = ($node->disk * (1 + ($node->disk_overallocate / 100)));
235
                    if ($newDisk > $diskLimit) {
236
                        throw new DisplayException('The amount of disk allocated to this server would put the node over its allocation limits. This node is allowed ' . ($node->disk_overallocate + 100) . '% of its assigned ' . $node->disk . 'Mb of disk (' . $diskLimit . 'Mb) of which ' . (($totals->disk / $node->disk) * 100) . '% (' . $totals->disk . 'Mb) is in use already. By allocating this server the node would be at ' . (($newDisk / $node->disk) * 100) . '% (' . $newDisk . 'Mb) usage.');
237
                    }
238
                }
239
            }
240
        }
241
242
        DB::beginTransaction();
243
244
        try {
245
            $uuid = new UuidService;
246
247
            // Add Server to the Database
248
            $server = new Server;
249
            $genUuid = $uuid->generate('servers', 'uuid');
250
            $genShortUuid = $uuid->generateShort('servers', 'uuidShort', $genUuid);
251
252
            if (isset($data['custom_id'])) {
253
                $server->id = $data['custom_id'];
254
            }
255
256
            $server->fill([
257
                'uuid' => $genUuid,
258
                'uuidShort' => $genShortUuid,
259
                'node_id' => $node->id,
260
                'name' => $data['name'],
261
                'description' => $data['description'],
262
                'skip_scripts' => isset($data['skip_scripts']),
263
                'suspended' => false,
264
                'owner_id' => $user->id,
265
                'memory' => $data['memory'],
266
                'swap' => $data['swap'],
267
                'disk' => $data['disk'],
268
                'io' => $data['io'],
269
                'cpu' => $data['cpu'],
270
                'oom_disabled' => isset($data['oom_disabled']),
271
                'allocation_id' => $allocation->id,
272
                'service_id' => $data['service_id'],
273
                'option_id' => $data['option_id'],
274
                'pack_id' => $data['pack_id'],
275
                'startup' => $data['startup'],
276
                'daemonSecret' => $uuid->generate('servers', 'daemonSecret'),
277
                'image' => (isset($data['custom_container']) && ! empty($data['custom_container'])) ? $data['custom_container'] : $option->docker_image,
278
                'username' => $this->generateSFTPUsername($data['name'], $genShortUuid),
279
                'sftp_password' => Crypt::encrypt('not set'),
280
            ]);
281
            $server->save();
282
283
            // Mark Allocation in Use
284
            $allocation->server_id = $server->id;
285
            $allocation->save();
286
287
            // Add Additional Allocations
288
            if (isset($data['allocation_additional']) && is_array($data['allocation_additional'])) {
289
                foreach ($data['allocation_additional'] as $allocation) {
290
                    $model = Allocation::where('id', $allocation)->where('node_id', $data['node_id'])->whereNull('server_id')->first();
291
                    if (! $model) {
292
                        continue;
293
                    }
294
295
                    $model->server_id = $server->id;
296
                    $model->save();
297
                }
298
            }
299
300
            foreach ($variableList as $item) {
301
                ServerVariable::create([
302
                    'server_id' => $server->id,
303
                    'variable_id' => $item['id'],
304
                    'variable_value' => $item['val'],
305
                ]);
306
            }
307
308
            $environment = $this->parseVariables($server);
309
            $server->load('allocation', 'allocations');
310
311
            $node->guzzleClient(['X-Access-Token' => $node->daemonSecret])->request('POST', '/servers', [
312
                'json' => [
313
                    'uuid' => (string) $server->uuid,
314
                    'user' => $server->username,
315
                    'build' => [
316
                        'default' => [
317
                            'ip' => $server->allocation->ip,
318
                            'port' => $server->allocation->port,
319
                        ],
320
                        'ports' => $server->allocations->groupBy('ip')->map(function ($item) {
321
                            return $item->pluck('port');
322
                        })->toArray(),
323
                        'env' => $environment->pluck('value', 'variable')->toArray(),
324
                        'memory' => (int) $server->memory,
325
                        'swap' => (int) $server->swap,
326
                        'io' => (int) $server->io,
327
                        'cpu' => (int) $server->cpu,
328
                        'disk' => (int) $server->disk,
329
                        'image' => $server->image,
330
                    ],
331
                    'service' => [
332
                        'type' => $service->folder,
333
                        'option' => $option->tag,
334
                        'pack' => (isset($pack)) ? $pack->uuid : null,
335
                        'skip_scripts' => $server->skip_scripts,
336
                    ],
337
                    'keys' => [
338
                        (string) $server->daemonSecret => $this->daemonPermissions,
339
                    ],
340
                    'rebuild' => false,
341
                    'start_on_completion' => isset($data['start_on_completion']),
342
                ],
343
            ]);
344
345
            DB::commit();
346
347
            return $server;
348
        } catch (\Exception $ex) {
349
            DB::rollBack();
350
            throw $ex;
351
        }
352
    }
353
354
    /**
355
     * Update the details for a server.
356
     *
357
     * @param  int    $id
358
     * @param  array  $data
359
     * @return \Pterodactyl\Models\Server
360
     *
361
     * @throws \Pterodactyl\Exceptions\DisplayException
362
     * @throws \Pterodactyl\Exceptions\DisplayValidationException
363
     */
364
    public function updateDetails($id, array $data)
365
    {
366
        $uuid = new UuidService;
367
        $resetDaemonKey = false;
368
369
        // Validate Fields
370
        $validator = Validator::make($data, [
371
            'owner_id' => 'sometimes|required|integer|exists:users,id',
372
            'name' => 'sometimes|required|regex:([\w .-]{1,200})',
373
            'description' => 'sometimes|required|string',
374
            'reset_token' => 'sometimes|required|accepted',
375
        ]);
376
377
        // Run validator, throw catchable and displayable exception if it fails.
378
        // Exception includes a JSON result of failed validation rules.
379
        if ($validator->fails()) {
380
            throw new DisplayValidationException(json_encode($validator->errors()));
381
        }
382
383
        DB::beginTransaction();
384
385
        try {
386
            $server = Server::with('user')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
387
388
            // Update daemon secret if it was passed.
389
            if (isset($data['reset_token']) || (isset($data['owner_id']) && (int) $data['owner_id'] !== $server->user->id)) {
390
                $oldDaemonKey = $server->daemonSecret;
391
                $server->daemonSecret = $uuid->generate('servers', 'daemonSecret');
392
                $resetDaemonKey = true;
393
            }
394
395
            // Save our changes
396
            $server->fill($data)->save();
397
398
            // Do we need to update? If not, return successful.
399
            if (! $resetDaemonKey) {
400
                return DB::commit();
401
            }
402
403
            $res = $server->node->guzzleClient([
404
                'X-Access-Server' => $server->uuid,
405
                'X-Access-Token' => $server->node->daemonSecret,
406
            ])->request('PATCH', '/server', [
407
                'exceptions' => false,
408
                'json' => [
409
                    'keys' => [
410
                        (string) $oldDaemonKey => [],
0 ignored issues
show
Bug introduced by
The variable $oldDaemonKey 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...
411
                        (string) $server->daemonSecret => $this->daemonPermissions,
412
                    ],
413
                ],
414
            ]);
415
416
            if ($res->getStatusCode() === 204) {
417
                DB::commit();
418
419
                return $server;
420
            } else {
421
                throw new DisplayException('Daemon returned a a non HTTP/204 error code. HTTP/' + $res->getStatusCode());
422
            }
423
        } catch (\Exception $ex) {
424
            DB::rollBack();
425
            throw $ex;
426
        }
427
    }
428
429
    /**
430
     * Update the container for a server.
431
     *
432
     * @param  int    $id
433
     * @param  array  $data
434
     * @return \Pterodactyl\Models\Server
435
     *
436
     * @throws \Pterodactyl\Exceptions\DisplayValidationException
437
     */
438
    public function updateContainer($id, array $data)
439
    {
440
        $validator = Validator::make($data, [
441
            'docker_image' => 'required|string',
442
        ]);
443
444
        // Run validator, throw catchable and displayable exception if it fails.
445
        // Exception includes a JSON result of failed validation rules.
446
        if ($validator->fails()) {
447
            throw new DisplayValidationException(json_encode($validator->errors()));
448
        }
449
450
        DB::beginTransaction();
451
        try {
452
            $server = Server::findOrFail($id);
453
454
            $server->image = $data['docker_image'];
455
            $server->save();
456
457
            $server->node->guzzleClient([
458
                'X-Access-Server' => $server->uuid,
459
                'X-Access-Token' => $server->node->daemonSecret,
460
            ])->request('PATCH', '/server', [
461
                'json' => [
462
                    'build' => [
463
                        'image' => $server->image,
464
                    ],
465
                ],
466
            ]);
467
468
            DB::commit();
469
470
            return $server;
471
        } catch (\Exception $ex) {
472
            DB::rollBack();
473
            throw $ex;
474
        }
475
    }
476
477
    /**
478
     * Update the build details for a server.
479
     *
480
     * @param  int    $id
481
     * @param  array  $data
482
     * @return \Pterodactyl\Models\Server
483
     *
484
     * @throws \Pterodactyl\Exceptions\DisplayException
485
     * @throws \Pterodactyl\Exceptions\DisplayValidationException
486
     */
487
    public function changeBuild($id, array $data)
488
    {
489
        $validator = Validator::make($data, [
490
            'allocation_id' => 'sometimes|required|exists:allocations,id',
491
            'add_allocations' => 'sometimes|required|array',
492
            'remove_allocations' => 'sometimes|required|array',
493
            'memory' => 'sometimes|required|integer|min:0',
494
            'swap' => 'sometimes|required|integer|min:-1',
495
            'io' => 'sometimes|required|integer|min:10|max:1000',
496
            'cpu' => 'sometimes|required|integer|min:0',
497
            'disk' => 'sometimes|required|integer|min:0',
498
        ]);
499
500
        // Run validator, throw catchable and displayable exception if it fails.
501
        // Exception includes a JSON result of failed validation rules.
502
        if ($validator->fails()) {
503
            throw new DisplayValidationException(json_encode($validator->errors()));
504
        }
505
506
        DB::beginTransaction();
507
508
        try {
509
            $server = Server::with('allocation', 'allocations')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
510
            $newBuild = [];
511
            $newAllocations = [];
0 ignored issues
show
Unused Code introduced by
$newAllocations is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
512
513
            if (isset($data['allocation_id'])) {
514
                if ((int) $data['allocation_id'] !== $server->allocation_id) {
515
                    $selection = $server->allocations->where('id', $data['allocation_id'])->first();
516
                    if (! $selection) {
517
                        throw new DisplayException('The requested default connection is not allocated to this server.');
518
                    }
519
520
                    $server->allocation_id = $selection->id;
521
                    $newBuild['default'] = ['ip' => $selection->ip, 'port' => $selection->port];
522
523
                    $server->load('allocation');
524
                }
525
            }
526
527
            $newPorts = false;
528
            $firstNewAllocation = null;
529
            // Add Assignments
530
            if (isset($data['add_allocations'])) {
531
                foreach ($data['add_allocations'] as $allocation) {
532
                    $model = Allocation::where('id', $allocation)->whereNull('server_id')->first();
533
                    if (! $model) {
534
                        continue;
535
                    }
536
537
                    $newPorts = true;
538
                    $firstNewAllocation = (is_null($firstNewAllocation)) ? $model->id : $firstNewAllocation;
539
                    $model->update([
540
                        'server_id' => $server->id,
541
                    ]);
542
                }
543
544
                $server->load('allocations');
545
            }
546
547
            // Remove Assignments
548
            if (isset($data['remove_allocations'])) {
549
                foreach ($data['remove_allocations'] as $allocation) {
550
                    // Can't remove the assigned IP/Port combo
551
                    if ((int) $allocation === $server->allocation_id) {
552
                        // No New Allocation
553
                        if (is_null($firstNewAllocation)) {
554
                            continue;
555
                        }
556
557
                        // New Allocation, set as the default.
558
                        $server->allocation_id = $firstNewAllocation;
559
                    }
560
561
                    $newPorts = true;
562
                    Allocation::where('id', $allocation)->where('server_id', $server->id)->update([
563
                        'server_id' => null,
564
                    ]);
565
                }
566
567
                $server->load('allocations');
568
            }
569
570
            if ($newPorts) {
571
                $newBuild['ports|overwrite'] = $server->allocations->groupBy('ip')->map(function ($item) {
572
                    return $item->pluck('port');
573
                })->toArray();
574
575
                $newBuild['env|overwrite'] = $this->parseVariables($server)->pluck('value', 'variable')->toArray();
576
            }
577
578
            // @TODO: verify that server can be set to this much memory without
579
            // going over node limits.
580 View Code Duplication
            if (isset($data['memory']) && $server->memory !== (int) $data['memory']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
581
                $server->memory = $data['memory'];
582
                $newBuild['memory'] = (int) $server->memory;
583
            }
584
585 View Code Duplication
            if (isset($data['swap']) && $server->swap !== (int) $data['swap']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
586
                $server->swap = $data['swap'];
587
                $newBuild['swap'] = (int) $server->swap;
588
            }
589
590
            // @TODO: verify that server can be set to this much disk without
591
            // going over node limits.
592 View Code Duplication
            if (isset($data['disk']) && $server->disk !== (int) $data['disk']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
593
                $server->disk = $data['disk'];
594
                $newBuild['disk'] = (int) $server->disk;
595
            }
596
597 View Code Duplication
            if (isset($data['cpu']) && $server->cpu !== (int) $data['cpu']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
598
                $server->cpu = $data['cpu'];
599
                $newBuild['cpu'] = (int) $server->cpu;
600
            }
601
602 View Code Duplication
            if (isset($data['io']) && $server->io !== (int) $data['io']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
603
                $server->io = $data['io'];
604
                $newBuild['io'] = (int) $server->io;
605
            }
606
607
            // Try save() here so if it fails we haven't contacted the daemon
608
            // This won't be committed unless the HTTP request succeedes anyways
609
            $server->save();
610
611
            if (! empty($newBuild)) {
612
                $server->node->guzzleClient([
613
                    'X-Access-Server' => $server->uuid,
614
                    'X-Access-Token' => $server->node->daemonSecret,
615
                ])->request('PATCH', '/server', [
616
                    'json' => [
617
                        'build' => $newBuild,
618
                    ],
619
                ]);
620
            }
621
622
            DB::commit();
623
624
            return $server;
625
        } catch (\Exception $ex) {
626
            DB::rollBack();
627
            throw $ex;
628
        }
629
    }
630
631
    /**
632
     * Process the variables for a server, and save to the database.
633
     *
634
     * @param  \Pterodactyl\Models\Server  $server
635
     * @param  array                       $data
636
     * @param  bool                        $admin
637
     * @return \Illuminate\Support\Collection
638
     *
639
     * @throws \Pterodactyl\Exceptions\DisplayValidationException
640
     */
641
    protected function processVariables(Server $server, $data, $admin = false)
642
    {
643
        $server->load('option.variables');
644
645
        if ($admin) {
646
            $server->startup = $data['startup'];
647
            $server->save();
648
        }
649
650
        if ($server->option->variables) {
651
            foreach ($server->option->variables as &$variable) {
652
                $set = isset($data['env_' . $variable->id]);
653
654
                // If user is not an admin and are trying to edit a non-editable field
655
                // or an invisible field just silently skip the variable.
656
                if (! $admin && (! $variable->user_editable || ! $variable->user_viewable)) {
657
                    continue;
658
                }
659
660
                // Perform Field Validation
661
                $validator = Validator::make([
662
                    'variable_value' => ($set) ? $data['env_' . $variable->id] : null,
663
                ], [
664
                    'variable_value' => $variable->rules,
665
                ]);
666
667
                if ($validator->fails()) {
668
                    throw new DisplayValidationException(json_encode(
669
                        collect([
670
                            'notice' => ['There was a validation error with the `' . $variable->name . '` variable.'],
671
                        ])->merge($validator->errors()->toArray())
672
                    ));
673
                }
674
675
                $svar = ServerVariable::firstOrNew([
676
                    'server_id' => $server->id,
677
                    'variable_id' => $variable->id,
678
                ]);
679
680
                // Set the value; if one was not passed set it to the default value
681
                if ($set) {
682
                    $svar->variable_value = $data['env_' . $variable->id];
683
684
                // Not passed, check if this record exists if so keep value, otherwise set default
685
                } else {
686
                    $svar->variable_value = ($svar->exists) ? $svar->variable_value : $variable->default_value;
687
                }
688
689
                $svar->save();
690
            }
691
        }
692
693
        return $this->parseVariables($server);
694
    }
695
696
    /**
697
     * Parse the variables and return in a standardized format.
698
     *
699
     * @param  \Pterodactyl\Models\Server  $server
700
     * @return \Illuminate\Support\Collection
701
     */
702
    protected function parseVariables(Server $server)
703
    {
704
        // Reload Variables
705
        $server->load('variables');
706
707 View Code Duplication
        $parsed = $server->option->variables->map(function ($item, $key) use ($server) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
708
            $display = $server->variables->where('variable_id', $item->id)->pluck('variable_value')->first();
709
710
            return [
711
                'variable' => $item->env_variable,
712
                'value' => (! is_null($display)) ? $display : $item->default_value,
713
            ];
714
        });
715
716
        $merge = [[
717
            'variable' => 'STARTUP',
718
            'value' => $server->startup,
719
        ], [
720
            'variable' => 'P_VARIABLE__LOCATION',
721
            'value' => $server->location->short,
722
        ]];
723
724
        $allocations = $server->allocations->where('id', '!=', $server->allocation_id);
725
        $i = 0;
726
727
        foreach($allocations as $allocation) {
728
            $merge[] = [
729
                'variable' => 'ALLOC_' . $i . '__PORT',
730
                'value' => $allocation->port,
731
            ];
732
733
            $i++;
734
        }
735
736
        return $parsed->merge($merge);
737
    }
738
739
    /**
740
     * Update the startup details for a server.
741
     *
742
     * @param  int    $id
743
     * @param  array  $data
744
     * @param  bool   $admin
745
     * @return bool
746
     *
747
     * @throws \GuzzleHttp\Exception\RequestException
748
     * @throws \Pterodactyl\Exceptions\DisplayException
749
     * @throws \Pterodactyl\Exceptions\DisplayValidationException
750
     */
751
    public function updateStartup($id, array $data, $admin = false)
752
    {
753
        $server = Server::with('variables', 'option.variables')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
754
        $hasServiceChanges = false;
755
756
        if ($admin) {
757
            // User is an admin, lots of things to do here.
758
            $validator = Validator::make($data, [
759
                'startup' => 'required|string',
760
                'skip_scripts' => 'sometimes|required|boolean',
761
                'service_id' => 'required|numeric|min:1|exists:services,id',
762
                'option_id' => 'required|numeric|min:1|exists:service_options,id',
763
                'pack_id' => 'sometimes|nullable|numeric|min:0',
764
            ]);
765
766
            if ((int) $data['pack_id'] < 1) {
767
                $data['pack_id'] = null;
768
            }
769
770
            if ($validator->fails()) {
771
                throw new DisplayValidationException(json_encode($validator->errors()));
772
            }
773
774
            if (
775
                $server->service_id != $data['service_id'] ||
776
                $server->option_id != $data['option_id'] ||
777
                $server->pack_id != $data['pack_id']
778
            ) {
779
                $hasServiceChanges = true;
780
            }
781
        }
782
783
        // If user isn't an administrator, this function is being access from the front-end
784
        // Just try to update specific variables.
785
        if (! $admin || ! $hasServiceChanges) {
786
            return DB::transaction(function () use ($admin, $data, $server) {
787
                $environment = $this->processVariables($server, $data, $admin);
788
789
                $server->node->guzzleClient([
790
                    'X-Access-Server' => $server->uuid,
791
                    'X-Access-Token' => $server->node->daemonSecret,
792
                ])->request('PATCH', '/server', [
793
                    'json' => [
794
                        'build' => [
795
                            'env|overwrite' => $environment->pluck('value', 'variable')->toArray(),
796
                        ],
797
                    ],
798
                ]);
799
800
                return false;
801
            });
802
        }
803
804
        // Validate those Service Option Variables
805
        // We know the service and option exists because of the validation.
806
        // We need to verify that the option exists for the service, and then check for
807
        // any required variable fields. (fields are labeled env_<env_variable>)
808
        $option = ServiceOption::where('id', $data['option_id'])->where('service_id', $data['service_id'])->first();
809
        if (! $option) {
810
            throw new DisplayException('The requested service option does not exist for the specified service.');
811
        }
812
813
        // Validate the Pack
814 View Code Duplication
        if (! isset($data['pack_id']) || (int) $data['pack_id'] < 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
815
            $data['pack_id'] = null;
816
        } else {
817
            $pack = Pack::where('id', $data['pack_id'])->where('option_id', $data['option_id'])->first();
818
            if (! $pack) {
819
                throw new DisplayException('The requested service pack does not seem to exist for this combination.');
820
            }
821
        }
822
823
        return DB::transaction(function () use ($admin, $data, $server) {
824
            $server->installed = 0;
825
            $server->service_id = $data['service_id'];
826
            $server->option_id = $data['option_id'];
827
            $server->pack_id = $data['pack_id'];
828
            $server->skip_scripts = isset($data['skip_scripts']);
829
            $server->save();
830
831
            $server->variables->each->delete();
832
833
            $server->load('service', 'pack');
834
835
            // Send New Environment
836
            $environment = $this->processVariables($server, $data, $admin);
837
838
            $server->node->guzzleClient([
839
                'X-Access-Server' => $server->uuid,
840
                'X-Access-Token' => $server->node->daemonSecret,
841
            ])->request('POST', '/server/reinstall', [
842
                'json' => [
843
                    'build' => [
844
                        'env|overwrite' => $environment->pluck('value', 'variable')->toArray(),
845
                    ],
846
                    'service' => [
847
                        'type' => $server->option->service->folder,
848
                        'option' => $server->option->tag,
849
                        'pack' => (! is_null($server->pack_id)) ? $server->pack->uuid : null,
850
                        'skip_scripts' => $server->skip_scripts,
851
                    ],
852
                ],
853
            ]);
854
855
            return true;
856
        });
857
    }
858
859
    /**
860
     * Delete a server from the system permanetly.
861
     *
862
     * @param  int   $id
863
     * @param  bool  $force
864
     * @return void
865
     *
866
     * @throws \Pterodactyl\Exceptions\DisplayException
867
     */
868
    public function delete($id, $force = false)
869
    {
870
        $server = Server::with('node', 'allocations', 'variables')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
871
872
        // Due to MySQL lockouts if the daemon response fails, we need to
873
        // delete the server from the daemon first. If it succeedes and then
874
        // MySQL fails, users just need to force delete the server.
875
        //
876
        // If this is a force delete, continue anyways.
877
        try {
878
            $server->node->guzzleClient([
879
                'X-Access-Token' => $server->node->daemonSecret,
880
                'X-Access-Server' => $server->uuid,
881
            ])->request('DELETE', '/servers');
882
        } catch (ClientException $ex) {
883
            // Exception is thrown on 4XX HTTP errors, so catch and determine
884
            // if we should continue, or if there is a permissions error.
885
            //
886
            // Daemon throws a 404 if the server doesn't exist, if that is returned
887
            // continue with deletion, even if not a force deletion.
888
            $response = $ex->getResponse();
0 ignored issues
show
Unused Code introduced by
$response is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
889
            if ($ex->getResponse()->getStatusCode() !== 404 && ! $force) {
890
                throw new DisplayException($ex->getMessage());
891
            }
892
        } catch (TransferException $ex) {
893
            if (! $force) {
894
                throw new DisplayException($ex->getMessage());
895
            }
896
        } catch (\Exception $ex) {
897
            throw $ex;
898
        }
899
900
        DB::transaction(function () use ($server) {
901
            $server->allocations->each(function ($item) {
902
                $item->server_id = null;
903
                $item->save();
904
            });
905
906
            $server->variables->each->delete();
907
908
            $server->load('subusers.permissions');
909
            $server->subusers->each(function ($subuser) {
910
                $subuser->permissions->each->delete();
911
                $subuser->delete();
912
            });
913
914
            $server->tasks->each->delete();
915
916
            // Delete Databases
917
            // This is the one un-recoverable point where
918
            // transactions will not save us.
919
            $repository = new DatabaseRepository;
920
            $server->databases->each(function ($item) use ($repository) {
921
                $repository->drop($item->id);
922
            });
923
924
            // Fully delete the server.
925
            $server->delete();
926
        });
927
    }
928
929
    /**
930
     * Toggle the install status of a serve.
931
     *
932
     * @param  int    $id
933
     * @return bool
934
     *
935
     * @throws \Pterodactyl\Exceptions\DisplayException
936
     */
937
    public function toggleInstall($id)
938
    {
939
        $server = Server::findOrFail($id);
940
        if ($server->installed > 1) {
941
            throw new DisplayException('This server was marked as having a failed install or being deleted, you cannot override this.');
942
        }
943
        $server->installed = ! $server->installed;
944
945
        return $server->save();
946
    }
947
948
    /**
949
     * Suspends or unsuspends a server.
950
     *
951
     * @param  int   $id
952
     * @param  bool  $unsuspend
953
     * @return void
954
     */
955
    public function toggleAccess($id, $unsuspend = true)
956
    {
957
        $server = Server::with('node')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
958
959
        DB::transaction(function () use ($server, $unsuspend) {
960
            if (
961
                (! $unsuspend && $server->suspended) ||
962
                ($unsuspend && ! $server->suspended)
963
            ) {
964
                return true;
965
            }
966
967
            $server->suspended = ! $unsuspend;
968
            $server->save();
969
970
            $server->node->guzzleClient([
971
                'X-Access-Token' => $server->node->daemonSecret,
972
                'X-Access-Server' => $server->uuid,
973
            ])->request('POST', ($unsuspend) ? '/server/unsuspend' : '/server/suspend');
974
        });
975
    }
976
977
    /**
978
     * Updates the SFTP password for a server.
979
     *
980
     * @param  int     $id
981
     * @param  string  $password
982
     * @return void
983
     *
984
     * @throws \Pterodactyl\Exceptions\DisplayValidationException
985
     */
986
    public function updateSFTPPassword($id, $password)
987
    {
988
        $server = Server::with('node')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
989
990
        $validator = Validator::make(['password' => $password], [
991
            'password' => 'required|regex:/^((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,})$/',
992
        ]);
993
994
        if ($validator->fails()) {
995
            throw new DisplayValidationException(json_encode($validator->errors()));
996
        }
997
998
        DB::transaction(function () use ($password, $server) {
999
            $server->sftp_password = Crypt::encrypt($password);
1000
            $server->save();
1001
1002
            $server->node->guzzleClient([
1003
                'X-Access-Token' => $server->node->daemonSecret,
1004
                'X-Access-Server' => $server->uuid,
1005
            ])->request('POST', '/server/password', [
1006
                'json' => ['password' => $password],
1007
            ]);
1008
        });
1009
    }
1010
1011
    /**
1012
     * Marks a server for reinstallation on the node.
1013
     *
1014
     * @param  int     $id
1015
     * @return void
1016
     */
1017
    public function reinstall($id)
1018
    {
1019
        $server = Server::with('node')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1020
1021
        DB::transaction(function () use ($server) {
1022
            $server->installed = 0;
1023
            $server->save();
1024
1025
            $server->node->guzzleClient([
1026
                'X-Access-Token' => $server->node->daemonSecret,
1027
                'X-Access-Server' => $server->uuid,
1028
            ])->request('POST', '/server/reinstall');
1029
        });
1030
    }
1031
}
1032