1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of tenside/core-bundle. |
5
|
|
|
* |
6
|
|
|
* (c) Christian Schiffler <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
* |
11
|
|
|
* This project is provided in good faith and hope to be usable by anyone. |
12
|
|
|
* |
13
|
|
|
* @package tenside/core-bundle |
14
|
|
|
* @author Christian Schiffler <[email protected]> |
15
|
|
|
* @copyright 2015 Christian Schiffler <[email protected]> |
16
|
|
|
* @license https://github.com/tenside/core-bundle/blob/master/LICENSE MIT |
17
|
|
|
* @link https://github.com/tenside/core-bundle |
18
|
|
|
* @filesource |
19
|
|
|
*/ |
20
|
|
|
|
21
|
|
|
namespace Tenside\CoreBundle\Controller; |
22
|
|
|
|
23
|
|
|
use Composer\Util\RemoteFilesystem; |
24
|
|
|
use Nelmio\ApiDocBundle\Annotation\ApiDoc; |
25
|
|
|
use Symfony\Component\HttpFoundation\JsonResponse; |
26
|
|
|
use Symfony\Component\HttpFoundation\Request; |
27
|
|
|
use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException; |
28
|
|
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; |
29
|
|
|
use Tenside\Core\Composer\AuthJson; |
30
|
|
|
use Tenside\CoreBundle\Security\UserInformation; |
31
|
|
|
use Tenside\CoreBundle\Security\UserInformationInterface; |
32
|
|
|
use Tenside\Core\Task\Composer\InstallTask; |
33
|
|
|
use Tenside\Core\Util\JsonArray; |
34
|
|
|
use Tenside\CoreBundle\Annotation\ApiDescription; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Controller for manipulating the composer.json file. |
38
|
|
|
*/ |
39
|
|
|
class InstallProjectController extends AbstractController |
40
|
|
|
{ |
41
|
|
|
/** |
42
|
|
|
* Configure tenside. |
43
|
|
|
* |
44
|
|
|
* NOTE: This method will become inaccessible after the first successful call. |
45
|
|
|
* |
46
|
|
|
* @param Request $request The request. |
47
|
|
|
* |
48
|
|
|
* @return JsonResponse |
49
|
|
|
* |
50
|
|
|
* @throws NotAcceptableHttpException When the configuration is already complete. |
51
|
|
|
* |
52
|
|
|
* @ApiDoc( |
53
|
|
|
* section="install", |
54
|
|
|
* statusCodes = { |
55
|
|
|
* 201 = "When everything worked out ok", |
56
|
|
|
* 406 = "When the configuration is already complete" |
57
|
|
|
* } |
58
|
|
|
* ) |
59
|
|
|
* @ApiDescription( |
60
|
|
|
* request={ |
61
|
|
|
* "credentials" = { |
62
|
|
|
* "description" = "The credentials of the admin user.", |
63
|
|
|
* "children" = { |
64
|
|
|
* "secret" = { |
65
|
|
|
* "dataType" = "string", |
66
|
|
|
* "description" = "The secret to use for encryption and signing.", |
67
|
|
|
* "required" = true |
68
|
|
|
* }, |
69
|
|
|
* "username" = { |
70
|
|
|
* "dataType" = "string", |
71
|
|
|
* "description" = "The name of the admin user.", |
72
|
|
|
* "required" = true |
73
|
|
|
* }, |
74
|
|
|
* "password" = { |
75
|
|
|
* "dataType" = "string", |
76
|
|
|
* "description" = "The password to use for the admin.", |
77
|
|
|
* "required" = false |
78
|
|
|
* } |
79
|
|
|
* } |
80
|
|
|
* }, |
81
|
|
|
* "configuration" = { |
82
|
|
|
* "description" = "The application configuration.", |
83
|
|
|
* "children" = { |
84
|
|
|
* "php_cli" = { |
85
|
|
|
* "dataType" = "string", |
86
|
|
|
* "description" = "The PHP interpreter to run on command line." |
87
|
|
|
* }, |
88
|
|
|
* "php_cli_arguments" = { |
89
|
|
|
* "dataType" = "string", |
90
|
|
|
* "description" = "Command line arguments to add." |
91
|
|
|
* }, |
92
|
|
|
* "github_oauth_token" = { |
93
|
|
|
* "dataType" = "string", |
94
|
|
|
* "description" = "Github OAuth token.", |
95
|
|
|
* "required" = false |
96
|
|
|
* } |
97
|
|
|
* } |
98
|
|
|
* } |
99
|
|
|
* }, |
100
|
|
|
* response={ |
101
|
|
|
* "token" = { |
102
|
|
|
* "dataType" = "string", |
103
|
|
|
* "description" = "The API token for the created user" |
104
|
|
|
* } |
105
|
|
|
* } |
106
|
|
|
* ) |
107
|
|
|
*/ |
108
|
|
|
public function configureAction(Request $request) |
109
|
|
|
{ |
110
|
|
|
if ($this->get('tenside.status')->isTensideConfigured()) { |
111
|
|
|
throw new NotAcceptableHttpException('Already configured.'); |
112
|
|
|
} |
113
|
|
|
$inputData = new JsonArray($request->getContent()); |
|
|
|
|
114
|
|
|
|
115
|
|
|
$secret = bin2hex(random_bytes(40)); |
116
|
|
|
if ($inputData->has('credentials/secret')) { |
117
|
|
|
$secret = $inputData->get('credentials/secret'); |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
// Add tenside configuration. |
121
|
|
|
$tensideConfig = $this->get('tenside.config'); |
122
|
|
|
$tensideConfig->set('secret', $secret); |
123
|
|
|
|
124
|
|
|
if ($inputData->has('configuration')) { |
125
|
|
|
$this->handleConfiguration($inputData->get('configuration', true)); |
126
|
|
|
} |
127
|
|
|
$user = $this->createUser($inputData->get('credentials/username'), $inputData->get('credentials/password')); |
128
|
|
|
|
129
|
|
|
return new JsonResponse( |
130
|
|
|
[ |
131
|
|
|
'status' => 'OK', |
132
|
|
|
'token' => $this->get('tenside.jwt_authenticator')->getTokenForData($user) |
133
|
|
|
], |
134
|
|
|
JsonResponse::HTTP_CREATED |
135
|
|
|
); |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* Create a project. |
140
|
|
|
* |
141
|
|
|
* NOTE: This method will become inaccessible after the returned task has been run successfully. |
142
|
|
|
* |
143
|
|
|
* @param Request $request The request. |
144
|
|
|
* |
145
|
|
|
* @return JsonResponse |
146
|
|
|
* |
147
|
|
|
* @throws NotAcceptableHttpException When the installation is already complete. |
148
|
|
|
* |
149
|
|
|
* @ApiDoc( |
150
|
|
|
* section="install", |
151
|
|
|
* statusCodes = { |
152
|
|
|
* 201 = "When everything worked out ok", |
153
|
|
|
* 406 = "When the installation is already been completed" |
154
|
|
|
* }, |
155
|
|
|
* ) |
156
|
|
|
* @ApiDescription( |
157
|
|
|
* request={ |
158
|
|
|
* "project" = { |
159
|
|
|
* "description" = "The project to install.", |
160
|
|
|
* "children" = { |
161
|
|
|
* "name" = { |
162
|
|
|
* "dataType" = "string", |
163
|
|
|
* "description" = "The name of the project to install.", |
164
|
|
|
* "required" = true |
165
|
|
|
* }, |
166
|
|
|
* "version" = { |
167
|
|
|
* "dataType" = "string", |
168
|
|
|
* "description" = "The version of the project to install (optional).", |
169
|
|
|
* "required" = false |
170
|
|
|
* } |
171
|
|
|
* } |
172
|
|
|
* } |
173
|
|
|
* }, |
174
|
|
|
* response={ |
175
|
|
|
* "task" = { |
176
|
|
|
* "dataType" = "string", |
177
|
|
|
* "description" = "The id of the created install task" |
178
|
|
|
* } |
179
|
|
|
* } |
180
|
|
|
* ) |
181
|
|
|
*/ |
182
|
|
|
public function createProjectAction(Request $request) |
183
|
|
|
{ |
184
|
|
|
$status = $this->get('tenside.status'); |
185
|
|
|
if (!$status->isTensideConfigured()) { |
186
|
|
|
throw new NotAcceptableHttpException('Need to configure first.'); |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
$this->checkUninstalled(); |
190
|
|
|
$result = []; |
191
|
|
|
$header = []; |
192
|
|
|
|
193
|
|
|
$installDir = $this->getTensideHome(); |
194
|
|
|
$inputData = new JsonArray($request->getContent()); |
|
|
|
|
195
|
|
|
$taskData = new JsonArray(); |
196
|
|
|
|
197
|
|
|
$taskData->set(InstallTask::SETTING_DESTINATION_DIR, $installDir); |
198
|
|
|
$taskData->set(InstallTask::SETTING_PACKAGE, $inputData->get('project/name')); |
199
|
|
|
if ($version = $inputData->get('project/version')) { |
200
|
|
|
$taskData->set(InstallTask::SETTING_VERSION, $version); |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
$taskId = $this->getTensideTasks()->queue('install', $taskData); |
204
|
|
|
$result['task'] = $taskId; |
205
|
|
|
$header['Location'] = $this->generateUrl( |
206
|
|
|
'task_get', |
207
|
|
|
['taskId' => $taskId], |
208
|
|
|
UrlGeneratorInterface::ABSOLUTE_URL |
209
|
|
|
); |
210
|
|
|
|
211
|
|
|
return new JsonResponse( |
212
|
|
|
[ |
213
|
|
|
'status' => 'OK', |
214
|
|
|
'task' => $taskId |
215
|
|
|
], |
216
|
|
|
JsonResponse::HTTP_CREATED, |
217
|
|
|
$header |
218
|
|
|
); |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* This is a gateway to the self test controller available only at install time. |
223
|
|
|
* |
224
|
|
|
* This is just here as the other route is protected with login. |
225
|
|
|
* |
226
|
|
|
* NOTE: This method will become inaccessible as soon as the installation is complete. |
227
|
|
|
* |
228
|
|
|
* @return JsonResponse |
229
|
|
|
* |
230
|
|
|
* @ApiDoc( |
231
|
|
|
* section="install", |
232
|
|
|
* description="Install time - self test.", |
233
|
|
|
* statusCodes = { |
234
|
|
|
* 201 = "When everything worked out ok", |
235
|
|
|
* 406 = "When the installation is already complete" |
236
|
|
|
* }, |
237
|
|
|
* ) |
238
|
|
|
* @ApiDescription( |
239
|
|
|
* response={ |
240
|
|
|
* "results" = { |
241
|
|
|
* "actualType" = "collection", |
242
|
|
|
* "subType" = "object", |
243
|
|
|
* "description" = "The test results.", |
244
|
|
|
* "children" = { |
245
|
|
|
* "name" = { |
246
|
|
|
* "dataType" = "string", |
247
|
|
|
* "description" = "The name of the test" |
248
|
|
|
* }, |
249
|
|
|
* "state" = { |
250
|
|
|
* "dataType" = "choice", |
251
|
|
|
* "description" = "The test result state.", |
252
|
|
|
* "format" = "[FAIL|SKIPPED|SUCCESS|WARNING]" |
253
|
|
|
* }, |
254
|
|
|
* "message" = { |
255
|
|
|
* "dataType" = "string", |
256
|
|
|
* "description" = "The detailed message of the test result." |
257
|
|
|
* }, |
258
|
|
|
* "explain" = { |
259
|
|
|
* "dataType" = "string", |
260
|
|
|
* "description" = "Optional description that could hint any problems and/or explain the error further." |
261
|
|
|
* } |
262
|
|
|
* } |
263
|
|
|
* } |
264
|
|
|
* } |
265
|
|
|
* ) |
266
|
|
|
*/ |
267
|
|
|
public function getSelfTestAction() |
268
|
|
|
{ |
269
|
|
|
$this->checkUninstalled(); |
270
|
|
|
|
271
|
|
|
return $this->forward('TensideCoreBundle:SelfTest:getAllTests'); |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* Install time gateway to the auto config. |
276
|
|
|
* |
277
|
|
|
* This is just here as the other route is protected with login. |
278
|
|
|
* |
279
|
|
|
* NOTE: This method will become inaccessible as soon as the installation is complete. |
280
|
|
|
* |
281
|
|
|
* @return JsonResponse |
282
|
|
|
* |
283
|
|
|
* @ApiDoc( |
284
|
|
|
* section="install", |
285
|
|
|
* description="Install time - auto config.", |
286
|
|
|
* statusCodes = { |
287
|
|
|
* 201 = "When everything worked out ok", |
288
|
|
|
* 406 = "When the installation is already complete" |
289
|
|
|
* }, |
290
|
|
|
* ) |
291
|
|
|
* @ApiDescription( |
292
|
|
|
* response={ |
293
|
|
|
* "php_cli" = { |
294
|
|
|
* "dataType" = "string", |
295
|
|
|
* "description" = "The PHP interpreter to run on command line." |
296
|
|
|
* }, |
297
|
|
|
* "php_cli_arguments" = { |
298
|
|
|
* "dataType" = "string", |
299
|
|
|
* "description" = "Command line arguments to add." |
300
|
|
|
* } |
301
|
|
|
* } |
302
|
|
|
* ) |
303
|
|
|
*/ |
304
|
|
|
public function getAutoConfigAction() |
305
|
|
|
{ |
306
|
|
|
$this->checkUninstalled(); |
307
|
|
|
|
308
|
|
|
return $this->forward('TensideCoreBundle:SelfTest:getAutoConfig'); |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
/** |
312
|
|
|
* Retrieve the available versions of a package. |
313
|
|
|
* |
314
|
|
|
* NOTE: This method will become inaccessible as soon as the installation is complete. |
315
|
|
|
* |
316
|
|
|
* @param string $vendor The vendor name of the package. |
317
|
|
|
* |
318
|
|
|
* @param string $project The name of the package. |
319
|
|
|
* |
320
|
|
|
* @return JsonResponse |
321
|
|
|
* |
322
|
|
|
* @ApiDoc( |
323
|
|
|
* section="install", |
324
|
|
|
* statusCodes = { |
325
|
|
|
* 201 = "When everything worked out ok", |
326
|
|
|
* 406 = "When the installation is already complete" |
327
|
|
|
* }, |
328
|
|
|
* ) |
329
|
|
|
* @ApiDescription( |
330
|
|
|
* response={ |
331
|
|
|
* "versions" = { |
332
|
|
|
* "actualType" = "collection", |
333
|
|
|
* "subType" = "object", |
334
|
|
|
* "description" = "The list of versions", |
335
|
|
|
* "children" = { |
336
|
|
|
* "name" = { |
337
|
|
|
* "dataType" = "string", |
338
|
|
|
* "description" = "The name of the package" |
339
|
|
|
* }, |
340
|
|
|
* "version" = { |
341
|
|
|
* "dataType" = "string", |
342
|
|
|
* "description" = "The version of the package" |
343
|
|
|
* }, |
344
|
|
|
* "version_normalized" = { |
345
|
|
|
* "dataType" = "string", |
346
|
|
|
* "description" = "The normalized version of the package" |
347
|
|
|
* }, |
348
|
|
|
* "reference" = { |
349
|
|
|
* "dataType" = "string", |
350
|
|
|
* "description" = "The optional reference" |
351
|
|
|
* } |
352
|
|
|
* } |
353
|
|
|
* } |
354
|
|
|
* } |
355
|
|
|
* ) |
356
|
|
|
*/ |
357
|
|
|
public function getProjectVersionsAction($vendor, $project) |
358
|
|
|
{ |
359
|
|
|
$this->checkUninstalled(); |
360
|
|
|
|
361
|
|
|
$url = sprintf('https://packagist.org/packages/%s/%s.json', $vendor, $project); |
362
|
|
|
$rfs = new RemoteFilesystem($this->getInputOutput()); |
363
|
|
|
$results = $rfs->getContents($url, $url); |
364
|
|
|
$data = new JsonArray($results); |
|
|
|
|
365
|
|
|
|
366
|
|
|
$versions = []; |
367
|
|
|
|
368
|
|
|
foreach ($data->get('package/versions') as $information) { |
|
|
|
|
369
|
|
|
$version = [ |
370
|
|
|
'name' => $information['name'], |
371
|
|
|
'version' => $information['version'], |
372
|
|
|
'version_normalized' => $information['version_normalized'], |
373
|
|
|
]; |
374
|
|
|
|
375
|
|
|
$normalized = $information['version']; |
376
|
|
|
if ('dev-' === substr($normalized, 0, 4)) { |
377
|
|
|
if (isset($information['extra']['branch-alias'][$normalized])) { |
378
|
|
|
$version['version_normalized'] = $information['extra']['branch-alias'][$normalized]; |
379
|
|
|
} |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
if (isset($information['source']['reference'])) { |
383
|
|
|
$version['reference'] = $information['source']['reference']; |
384
|
|
|
} elseif (isset($information['dist']['reference'])) { |
385
|
|
|
$version['reference'] = $information['dist']['reference']; |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
$versions[] = $version; |
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
return new JsonResponse( |
392
|
|
|
[ |
393
|
|
|
'status' => 'OK', |
394
|
|
|
'versions' => $versions |
395
|
|
|
] |
396
|
|
|
); |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
/** |
400
|
|
|
* Check if installation is new, partial or complete. |
401
|
|
|
* |
402
|
|
|
* @return JsonResponse |
403
|
|
|
* |
404
|
|
|
* @ApiDoc( |
405
|
|
|
* section="install", |
406
|
|
|
* description="This method provides information about the installation.", |
407
|
|
|
* statusCodes = { |
408
|
|
|
* 201 = "When everything worked out ok", |
409
|
|
|
* 406 = "When the installation is already complete" |
410
|
|
|
* }, |
411
|
|
|
* ) |
412
|
|
|
* @ApiDescription( |
413
|
|
|
* response={ |
414
|
|
|
* "state" = { |
415
|
|
|
* "children" = { |
416
|
|
|
* "tenside_configured" = { |
417
|
|
|
* "dataType" = "bool", |
418
|
|
|
* "description" = "Flag if tenside has been completely configured." |
419
|
|
|
* }, |
420
|
|
|
* "project_created" = { |
421
|
|
|
* "dataType" = "bool", |
422
|
|
|
* "description" = "Flag determining if a composer.json is present." |
423
|
|
|
* }, |
424
|
|
|
* "project_installed" = { |
425
|
|
|
* "dataType" = "bool", |
426
|
|
|
* "description" = "Flag determining if the composer project has been installed (vendor present)." |
427
|
|
|
* } |
428
|
|
|
* } |
429
|
|
|
* }, |
430
|
|
|
* "status" = { |
431
|
|
|
* "dataType" = "string", |
432
|
|
|
* "description" = "Either OK or ERROR" |
433
|
|
|
* }, |
434
|
|
|
* "message" = { |
435
|
|
|
* "dataType" = "string", |
436
|
|
|
* "description" = "The API error message if any (only present when status is ERROR)" |
437
|
|
|
* } |
438
|
|
|
* } |
439
|
|
|
* ) |
440
|
|
|
*/ |
441
|
|
|
public function getInstallationStateAction() |
442
|
|
|
{ |
443
|
|
|
$status = $this->get('tenside.status'); |
444
|
|
|
|
445
|
|
|
return new JsonResponse( |
446
|
|
|
[ |
447
|
|
|
'state' => [ |
448
|
|
|
'tenside_configured' => $status->isTensideConfigured(), |
449
|
|
|
'project_created' => $status->isProjectPresent(), |
450
|
|
|
'project_installed' => $status->isProjectInstalled(), |
451
|
|
|
], |
452
|
|
|
'status' => 'OK' |
453
|
|
|
] |
454
|
|
|
); |
455
|
|
|
} |
456
|
|
|
|
457
|
|
|
/** |
458
|
|
|
* Ensure that we are not installed yet. |
459
|
|
|
* |
460
|
|
|
* @return void |
461
|
|
|
* |
462
|
|
|
* @throws NotAcceptableHttpException When the installation is already complete. |
463
|
|
|
*/ |
464
|
|
|
private function checkUninstalled() |
465
|
|
|
{ |
466
|
|
|
if ($this->get('tenside.status')->isComplete()) { |
467
|
|
|
throw new NotAcceptableHttpException('Already installed in ' . $this->getTensideHome()); |
468
|
|
|
} |
469
|
|
|
} |
470
|
|
|
|
471
|
|
|
/** |
472
|
|
|
* Add an user to the database. |
473
|
|
|
* |
474
|
|
|
* @param string $username The username. |
475
|
|
|
* |
476
|
|
|
* @param string $password The password. |
477
|
|
|
* |
478
|
|
|
* @return UserInformation |
479
|
|
|
*/ |
480
|
|
|
private function createUser($username, $password) |
481
|
|
|
{ |
482
|
|
|
$user = new UserInformation( |
483
|
|
|
[ |
484
|
|
|
'username' => $username, |
485
|
|
|
'acl' => UserInformationInterface::ROLE_ALL |
486
|
|
|
] |
487
|
|
|
); |
488
|
|
|
|
489
|
|
|
$user->set('password', $this->get('security.password_encoder')->encodePassword($user, $password)); |
490
|
|
|
|
491
|
|
|
$user = $this->get('tenside.user_provider')->addUser($user)->refreshUser($user); |
492
|
|
|
|
493
|
|
|
return $user; |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
/** |
497
|
|
|
* Absorb the passed configuration. |
498
|
|
|
* |
499
|
|
|
* @param array $configuration The configuration to absorb. |
500
|
|
|
* |
501
|
|
|
* @return void |
502
|
|
|
*/ |
503
|
|
|
private function handleConfiguration($configuration) |
504
|
|
|
{ |
505
|
|
|
$tensideConfig = $this->get('tenside.config'); |
506
|
|
|
|
507
|
|
|
if (isset($configuration['php_cli'])) { |
508
|
|
|
$tensideConfig->setPhpCliBinary($configuration['php_cli']); |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
if (isset($configuration['php_cli_arguments'])) { |
512
|
|
|
$tensideConfig->setPhpCliArguments($configuration['php_cli_arguments']); |
513
|
|
|
} |
514
|
|
|
|
515
|
|
|
if (isset($configuration['php_cli_environment'])) { |
516
|
|
|
$tensideConfig->setPhpCliEnvironment($configuration['php_cli_environment']); |
517
|
|
|
} |
518
|
|
|
|
519
|
|
|
if (isset($configuration['php_force_background'])) { |
520
|
|
|
$tensideConfig->setForceToBackground($configuration['php_force_background']); |
521
|
|
|
} |
522
|
|
|
|
523
|
|
|
if (isset($configuration['php_can_fork'])) { |
524
|
|
|
$tensideConfig->setForkingAvailable($configuration['php_can_fork']); |
525
|
|
|
} |
526
|
|
|
|
527
|
|
|
if (isset($configuration['github_oauth_token'])) { |
528
|
|
|
$composerAuth = new AuthJson( |
529
|
|
|
$this->get('tenside.home')->tensideDataDir() . DIRECTORY_SEPARATOR . 'auth.json', |
530
|
|
|
null |
531
|
|
|
); |
532
|
|
|
|
533
|
|
|
$composerAuth->setGithubOAuthToken($configuration['github_oauth_token']); |
534
|
|
|
} |
535
|
|
|
} |
536
|
|
|
} |
537
|
|
|
|
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.