Completed
Push — master ( de8a25...a9a104 )
by Greg
01:42
created

Application::removeCommand()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 8
nc 2
nop 4
1
<?php
2
3
namespace Consolidation\Cgr;
4
5
/**
6
 * Note that this command is deliberately written using only php-native
7
 * libraries, and no external dependencies whatsoever, so that it may
8
 * be installed via `composer global require` without causing any conflicts
9
 * with any other project.
10
 *
11
 * This technique is NOT recommended for other tools. Use Symfony Console
12
 * directly, or, better yet, use Robo (http://robo.li) as a framework.
13
 * See: http://robo.li/framework/
14
 */
15
class Application
16
{
17
    protected $outputFile = '';
18
19
    /**
20
     * Run the cgr tool, a safer alternative to `composer global require`.
21
     *
22
     * @param array $argv The global $argv array passed in by PHP
23
     * @param string $home The path to the composer home directory
24
     * @return integer
25
     */
26
    public function run($argv, $home)
27
    {
28
        $optionDefaultValues = $this->getDefaultOptionValues($home);
29
        $optionDefaultValues = $this->overlayEnvironmentValues($optionDefaultValues);
30
31
        list($argv, $options) = $this->parseOutOurOptions($argv, $optionDefaultValues);
32
33
        $helpArg = $this->getHelpArgValue($argv);
34
        if (!empty($helpArg)) {
35
            return $this->help($helpArg);
36
        }
37
38
        $commandList = $this->separateProjectAndGetCommandList($argv, $home, $options);
39
        if (empty($commandList)) {
40
            return 1;
41
        }
42
        return $this->runCommandList($commandList, $options);
0 ignored issues
show
Documentation introduced by
$commandList is of type object<Consolidation\Cgr\CommandToExec>, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
43
    }
44
45
    /**
46
     * Returns the first argument after `help`, or the
47
     * first argument if `--help` is present. Otherwise,
48
     * returns an empty string.
49
     */
50
    public function getHelpArgValue($argv)
51
    {
52
        $hasHelp = false;
53
        $helpArg = '';
54
55
        foreach ($argv as $arg) {
56
            if (($arg == 'help') || ($arg == '--help') || ($arg == '-h')) {
57
                $hasHelp = true;
58
            } elseif (($arg[0] != '-') && empty($helpArg)) {
59
                $helpArg = $arg;
60
            }
61
        }
62
63
        if (!$hasHelp) {
64
            return false;
65
        }
66
67
        if (empty($helpArg)) {
68
            return 'help';
69
        }
70
71
        return $helpArg;
72
    }
73
74
    public function help($helpArg)
75
    {
76
        $helpFile = dirname(__DIR__) . '/help/' . $helpArg;
77
78
        if (!file_exists($helpFile)) {
79
            print "No help available for '$helpArg'\n";
80
            return 1;
81
        }
82
83
        $helpContents = file_get_contents($helpFile);
84
        print $helpContents;
85
        return 0;
86
    }
87
88
    /**
89
     * Set up output redirection. Used by tests.
90
     */
91
    public function setOutputFile($outputFile)
92
    {
93
        $this->outputFile = $outputFile;
94
    }
95
96
    /**
97
     * Figure out everything we're going to do, but don't do any of it
98
     * yet, just return the command objects to run.
99
     */
100
    public function parseArgvAndGetCommandList($argv, $home)
101
    {
102
        $optionDefaultValues = $this->getDefaultOptionValues($home);
103
        $optionDefaultValues = $this->overlayEnvironmentValues($optionDefaultValues);
104
105
        list($argv, $options) = $this->parseOutOurOptions($argv, $optionDefaultValues);
106
        return $this->separateProjectAndGetCommandList($argv, $home, $options);
107
    }
108
109
    /**
110
     * Figure out everything we're going to do, but don't do any of it
111
     * yet, just return the command objects to run.
112
     */
113
    public function separateProjectAndGetCommandList($argv, $home, $options)
0 ignored issues
show
Unused Code introduced by
The parameter $home 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...
114
    {
115
        list($command, $projects, $composerArgs) = $this->separateProjectsFromArgs($argv, $options);
116
117
        // If command was unknown, then exit with an error message
118
        if (empty($command)) {
119
            print "Unknown command: " . implode(' ', $composerArgs) . "\n";
120
            exit(1);
0 ignored issues
show
Coding Style Compatibility introduced by
The method separateProjectAndGetCommandList() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
121
        }
122
123
        $commandList = $this->getCommandsToExec($command, $composerArgs, $projects, $options);
124
        return $commandList;
125
    }
126
127
    /**
128
     * Run all of the commands in a list.  Abort early if any fail.
129
     *
130
     * @param array $commandList An array of CommandToExec
131
     * @return integer
132
     */
133
    public function runCommandList($commandList, $options)
0 ignored issues
show
Unused Code introduced by
The parameter $options 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...
134
    {
135
        foreach ($commandList as $command) {
136
            $exitCode = $command->run($this->outputFile);
137
            if ($exitCode) {
138
                return $exitCode;
139
            }
140
        }
141
        return 0;
142
    }
143
144
    /**
145
     * Return an array containing a list of commands to execute.  Depending on
146
     * the composition of the aguments and projects parameters, this list will
147
     * contain either a single command string to call through to composer (if
148
     * cgr is being used as a composer alias), or it will contain a list of
149
     * appropriate replacement 'composer global require' commands that install
150
     * each project in its own installation directory, while installing each
151
     * projects' binaries in the global Composer bin directory,
152
     * ~/.composer/vendor/bin.
153
     *
154
     * @param array $composerArgs
155
     * @param array $projects
156
     * @param array $options
157
     * @return CommandToExec
158
     */
159
    public function getCommandsToExec($command, $composerArgs, $projects, $options)
160
    {
161
        $execPath = $options['composer-path'];
162
163
        // Call requireCommand, updateCommand, or removeCommand, as appropriate.
164
        $methodName = "{$command}Command";
165
        if (method_exists($this, $methodName)) {
166
            return $this->$methodName($execPath, $composerArgs, $projects, $options);
167
        } else {
168
            // If there is no specific implementation for the requested command, then call 'generalCommand'.
169
            return $this->generalCommand($command, $execPath, $composerArgs, $projects, $options);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->generalCom..., $projects, $options); (array) is incompatible with the return type documented by Consolidation\Cgr\Application::getCommandsToExec of type Consolidation\Cgr\CommandToExec.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
170
        }
171
    }
172
173
    /**
174
     * Return our list of default option values, with paths relative to
175
     * the provided home directory.
176
     * @param string $home The composer home directory
177
     * @return array
178
     */
179
    public function getDefaultOptionValues($home)
180
    {
181
        return array(
182
            'composer' => false,
183
            'composer-path' => 'composer',
184
            'base-dir' => "$home/global",
185
            'bin-dir' => "$home/vendor/bin",
186
            'stability' => false,
187
        );
188
    }
189
190
    /**
191
     * Replace option default values with the corresponding
192
     * environment variable value, if it is set.
193
     */
194
    protected function overlayEnvironmentValues($defaults)
195
    {
196
        foreach ($defaults as $key => $value) {
197
            $envKey = 'CGR_' . strtoupper(strtr($key, '-', '_'));
198
            $envValue = getenv($envKey);
199
            if ($envValue) {
200
                $defaults[$key] = $envValue;
201
            }
202
        }
203
        return $defaults;
204
    }
205
206
    /**
207
     * We use our own special-purpose argv parser. The options that apply
208
     * to this tool are identified by a simple associative array, where
209
     * the key is the option name, and the value is its default value.
210
     * The result of this function is an array of two items containing:
211
     *  - An array of the items in $argv not used to set an option value
212
     *  - An array of options containing the user-specified or default values
213
     *
214
     * @param array $argv The global $argv passed in by php
215
     * @param array $optionDefaultValues An associative array
216
     * @return array
217
     */
218
    public function parseOutOurOptions($argv, $optionDefaultValues)
219
    {
220
        $argv0 = array_shift($argv);
221
        $options['composer'] = (strpos($argv0, 'composer') !== false);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $options = 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...
222
        $passAlongArgvItems = array();
223
        $options = array();
224
        while (!empty($argv)) {
225
            $arg = array_shift($argv);
226
            if ((substr($arg, 0, 2) == '--') && array_key_exists(substr($arg, 2), $optionDefaultValues)) {
227
                $options[substr($arg, 2)] = array_shift($argv);
228
            } else {
229
                $passAlongArgvItems[] = $arg;
230
            }
231
        }
232
        return array($passAlongArgvItems, $options + $optionDefaultValues);
233
    }
234
235
    /**
236
     * After our options are removed by parseOutOurOptions, those items remaining
237
     * in $argv will be separated into a list of projects and versions, and
238
     * anything else that is not a project:version. Returns an array of two
239
     * items containing:
240
     *  - An associative array, where the key is the project name and the value
241
     *    is the version (or an empty string, if no version was specified)
242
     *  - The remaining $argv items not used to build the projects array.
243
     *
244
     * @param array $argv The $argv array from parseOutOurOptions()
245
     * @return array
246
     */
247
    public function separateProjectsFromArgs($argv, $options)
248
    {
249
        $cgrCommands = array('info', 'require', 'update', 'remove');
250
        $command = 'require';
251
        $composerArgs = array();
252
        $projects = array();
253
        $globalMode = !$options['composer'];
254
        foreach ($argv as $arg) {
255
            if ($arg[0] == '-') {
256
                // Any flags (first character is '-') will just be passed
257
                // through to to composer. Flags interpreted by cgr have
258
                // already been removed from $argv.
259
                $composerArgs[] = $arg;
260
            } elseif (strpos($arg, '/') !== false) {
261
                // Arguments containing a '/' name projects.  We will split
262
                // the project from its version, allowing the separator
263
                // character to be either a '=' or a ':', and then store the
264
                // result in the $projects array.
265
                $projectAndVersion = explode(':', strtr($arg, '=', ':'), 2) + array('', '');
266
                list($project, $version) = $projectAndVersion;
267
                $projects[$project] = $version;
268
            } elseif ($this->isComposerVersion($arg)) {
269
                // If an argument is a composer version, then we will alter
270
                // the last project we saw, attaching this version to it.
271
                // This allows us to handle 'a/b:1.0' and 'a/b 1.0' equivalently.
272
                $keys = array_keys($projects);
273
                $lastProject = array_pop($keys);
274
                unset($projects[$lastProject]);
275
                $projects[$lastProject] = $arg;
276
            } elseif ($arg == 'global') {
277
                // Make note if we see the 'global' command.
278
                $globalMode = true;
279
            } else {
280
                // If we see any command other than 'global [require|update|remove]',
281
                // then we will pass *all* of the arguments through to
282
                // composer unchanged. We return an empty projects array
283
                // to indicate that this should be a pass-through call
284
                // to composer, rather than one or more calls to
285
                // 'composer require' to install global projects.
286
                if ((!$globalMode) || (!in_array($arg, $cgrCommands))) {
287
                    return array('', array(), $argv);
288
                }
289
                // Remember which command we saw
290
                $command = $arg;
291
            }
292
        }
293
        return array($command, $projects, $composerArgs);
294
    }
295
296
    /**
297
     * Provide a safer version of `composer global require`.  Each project
298
     * listed in $projects will be installed into its own project directory.
299
     * The binaries from each project will still be placed in the global
300
     * composer bin directory.
301
     *
302
     * @param string $execPath The path to composer
303
     * @param array $composerArgs Anything from the global $argv to be passed
304
     *   on to Composer
305
     * @param array $projects A list of projects to install, with the key
306
     *   specifying the project name, and the value specifying its version.
307
     * @param array $options User options from the command line; see
308
     *   $optionDefaultValues in the main() function.
309
     * @return array
310
     */
311
    public function requireCommand($execPath, $composerArgs, $projects, $options)
312
    {
313
        $stabilityCommands = array();
314
        if ($options['stability']) {
315
            $stabilityCommands = $this->configureProjectStability($execPath, $composerArgs, $projects, $options);
316
        }
317
        $requireCommands = $this->generalCommand('require', $execPath, $composerArgs, $projects, $options);
318
        return array_merge($stabilityCommands, $requireCommands);
319
    }
320
321
    /**
322
     * General command handler.
323
     *
324
     * @param string $composerCommand The composer command to run e.g. require
325
     * @param string $execPath The path to composer
326
     * @param array $composerArgs Anything from the global $argv to be passed
327
     *   on to Composer
328
     * @param array $projects A list of projects to install, with the key
329
     *   specifying the project name, and the value specifying its version.
330
     * @param array $options User options from the command line; see
331
     *   $optionDefaultValues in the main() function.
332
     * @return array
333
     */
334
    public function generalCommand($composerCommand, $execPath, $composerArgs, $projects, $options)
335
    {
336
        $globalBaseDir = $options['base-dir'];
337
        $binDir = $options['bin-dir'];
338
        $env = array("COMPOSER_BIN_DIR" => $binDir);
339
        $result = array();
340
        foreach ($projects as $project => $version) {
341
            $installLocation = "$globalBaseDir/$project";
342
            $projectWithVersion = $this->projectWithVersion($project, $version);
343
            $commandToExec = $this->buildGlobalCommand($composerCommand, $execPath, $composerArgs, $projectWithVersion, $env, $installLocation);
344
            $result[] = $commandToExec;
345
        }
346
        return $result;
347
    }
348
349
    /**
350
     * Remove command handler. Build an `rm -rf` command.
351
     *
352
     * @param string $execPath The path to composer (ignored)
353
     * @param array $composerArgs Anything from the global $argv to be passed
354
     *   on to Composer (ignored)
355
     * @param array $projects A list of projects to install, with the key
356
     *   specifying the project name, and the value specifying its version.
357
     * @param array $options User options from the command line; see
358
     *   $optionDefaultValues in the main() function.
359
     * @return array
360
     */
361
    public function removeCommand($execPath, $composerArgs, $projects, $options)
0 ignored issues
show
Unused Code introduced by
The parameter $execPath 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...
Unused Code introduced by
The parameter $composerArgs 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...
362
    {
363
        $globalBaseDir = $options['base-dir'];
364
        $env = array();
365
        $result = array();
366
        foreach ($projects as $project => $version) {
367
            $installLocation = "$globalBaseDir/$project";
368
            $result[] = new CommandToExec('rm', array('-rf', $installLocation), $env, $installLocation);
369
        }
370
        return $result;
371
    }
372
373
    /**
374
     * Command handler for commands where the project should not be provided
375
     * as a parameter to Composer (e.g. 'update').
376
     *
377
     * @param string $composerCommand The composer command to run e.g. require
378
     * @param string $execPath The path to composer
379
     * @param array $composerArgs Anything from the global $argv to be passed
380
     *   on to Composer
381
     * @param array $projects A list of projects to install, with the key
382
     *   specifying the project name, and the value specifying its version.
383
     * @param array $options User options from the command line; see
384
     *   $optionDefaultValues in the main() function.
385
     * @return array
386
     */
387
    public function noProjectArgCommand($composerCommand, $execPath, $composerArgs, $projects, $options)
388
    {
389
        $globalBaseDir = $options['base-dir'];
390
        $binDir = $options['bin-dir'];
391
        $env = array("COMPOSER_BIN_DIR" => $binDir);
392
        $result = array();
393
        foreach ($projects as $project => $version) {
394
            $installLocation = "$globalBaseDir/$project";
395
            $commandToExec = $this->buildGlobalCommand($composerCommand, $execPath, $composerArgs, '', $env, $installLocation);
396
            $result[] = $commandToExec;
397
        }
398
        return $result;
399
    }
400
401
    /**
402
     * If --stability VALUE is provided, then run a `composer config minimum-stability VALUE`
403
     * command to configure composer.json appropriately.
404
     *
405
     * @param string $execPath The path to composer
406
     * @param array $composerArgs Anything from the global $argv to be passed
407
     *   on to Composer
408
     * @param array $projects A list of projects to install, with the key
409
     *   specifying the project name, and the value specifying its version.
410
     * @param array $options User options from the command line; see
411
     *   $optionDefaultValues in the main() function.
412
     * @return array
413
     */
414
    public function configureProjectStability($execPath, $composerArgs, $projects, $options)
415
    {
416
        $globalBaseDir = $options['base-dir'];
417
        $stability = $options['stability'];
418
        $result = array();
419
        $env = array();
420
421
        foreach ($projects as $project => $version) {
422
            $installLocation = "$globalBaseDir/$project";
423
            FileSystemUtils::mkdirParents($installLocation);
424
            if (!file_exists("$installLocation/composer.json")) {
425
                file_put_contents("$installLocation/composer.json", '{}');
426
            }
427
            $result[] = $this->buildConfigCommand($execPath, $composerArgs, 'minimum-stability', $stability, $env, $installLocation);
428
        }
429
        return $result;
430
    }
431
432
    /**
433
     * Run `composer info`. Not only do we want to display the information of
434
     * the "global" Composer project, we also want to get the infomation of
435
     * all the "isolated" projects installed via cgr in ~/.composer/global.
436
     *
437
     * @param string $command The path to composer
0 ignored issues
show
Bug introduced by
There is no parameter named $command. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
438
     * @param array $composerArgs Anything from the global $argv to be passed
439
     *   on to Composer
440
     * @param array $projects A list of projects to update.
441
     * @param array $options User options from the command line; see
442
     *   $optionDefaultValues in the main() function.
443
     * @return array
444
     */
445 View Code Duplication
    public function infoCommand($execPath, $composerArgs, $projects, $options)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
446
    {
447
        // If 'projects' list is empty, make a list of everything currently installed
448
        if (empty($projects)) {
449
            $projects = FileSystemUtils::allInstalledProjectsInBaseDir($options['base-dir']);
450
            $projects = $this->flipProjectsArray($projects);
451
        }
452
        return $this->generalCommand('info', $execPath, $composerArgs, $projects, $options);
453
    }
454
455
    /**
456
     * Run `composer global update`. Not only do we want to update the
457
     * "global" Composer project, we also want to update all of the
458
     * "isolated" projects installed via cgr in ~/.composer/global.
459
     *
460
     * @param string $command The path to composer
0 ignored issues
show
Bug introduced by
There is no parameter named $command. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
461
     * @param array $composerArgs Anything from the global $argv to be passed
462
     *   on to Composer
463
     * @param array $projects A list of projects to update.
464
     * @param array $options User options from the command line; see
465
     *   $optionDefaultValues in the main() function.
466
     * @return array
467
     */
468 View Code Duplication
    public function updateCommand($execPath, $composerArgs, $projects, $options)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
469
    {
470
        // If 'projects' list is empty, make a list of everything currently installed
471
        if (empty($projects)) {
472
            $projects = FileSystemUtils::allInstalledProjectsInBaseDir($options['base-dir']);
473
            $projects = $this->flipProjectsArray($projects);
474
        }
475
        return $this->noProjectArgCommand('update', $execPath, $composerArgs, $projects, $options);
476
    }
477
478
    /**
479
     * Convert from an array of projects to an array where the key is the
480
     * project name, and the value (version) is an empty string.
481
     *
482
     * @param string[] $projects
483
     * @return array
484
     */
485
    public function flipProjectsArray($projects)
486
    {
487
        return array_map(function () {
488
            return '';
489
        }, array_flip($projects));
490
    }
491
492
    /**
493
     * Return $project:$version, or just $project if there is no $version.
494
     *
495
     * @param string $project The project to install
496
     * @param string $version The version desired
497
     * @return string
498
     */
499
    public function projectWithVersion($project, $version)
500
    {
501
        if (empty($version)) {
502
            return $project;
503
        }
504
        return "$project:$version";
505
    }
506
507
    /**
508
     * Generate command string to call `composer COMMAND` to install one project.
509
     *
510
     * @param string $command The path to composer
0 ignored issues
show
Documentation introduced by
There is no parameter named $command. Did you maybe mean $composerCommand?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
511
     * @param array $composerArgs The arguments to pass to composer
512
     * @param string $projectWithVersion The project:version to install
513
     * @param array $env Environment to set prior to exec
514
     * @param string $installLocation Location to install the project
515
     * @return CommandToExec
516
     */
517
    public function buildGlobalCommand($composerCommand, $execPath, $composerArgs, $projectWithVersion, $env, $installLocation)
518
    {
519
        $projectSpecificArgs = array("--working-dir=$installLocation", $composerCommand);
520
        if (!empty($projectWithVersion)) {
521
            $projectSpecificArgs[] = $projectWithVersion;
522
        }
523
        $arguments = array_merge($composerArgs, $projectSpecificArgs);
524
        return new CommandToExec($execPath, $arguments, $env, $installLocation);
525
    }
526
527
    /**
528
     * Generate command string to call `composer config KEY VALUE` to install one project.
529
     *
530
     * @param string $execPath The path to composer
531
     * @param array $composerArgs The arguments to pass to composer
532
     * @param string $key The config item to set
533
     * @param string $value The value to set the config item to
534
     * @param array $env Environment to set prior to exec
535
     * @param string $installLocation Location to install the project
536
     * @return CommandToExec
537
     */
538
    public function buildConfigCommand($execPath, $composerArgs, $key, $value, $env, $installLocation)
539
    {
540
        $projectSpecificArgs = array("--working-dir=$installLocation", 'config', $key, $value);
541
        $arguments = array_merge($composerArgs, $projectSpecificArgs);
542
        return new CommandToExec($execPath, $arguments, $env, $installLocation);
543
    }
544
545
    /**
546
     * Identify an argument that could be a Composer version string.
547
     *
548
     * @param string $arg The argument to test
549
     * @return boolean
550
     */
551
    public function isComposerVersion($arg)
552
    {
553
        // Allow for 'dev-master', et. al.
554
        if (substr($arg, 0, 4) == 'dev-') {
555
            return true;
556
        }
557
        $specialVersionChars = array('^', '~', '<', '>');
558
        return is_numeric($arg[0]) || in_array($arg[0], $specialVersionChars);
559
    }
560
}
561