File: scripts.md

package info (click to toggle)
composer 2.9.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 11,476 kB
  • sloc: php: 82,636; makefile: 59; xml: 39
file content (507 lines) | stat: -rw-r--r-- 17,775 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
<!--
    tagline: Script are callbacks that are called before/after installing packages
-->

# Scripts

## What is a script?

A script, in Composer's terms, can either be a PHP callback (defined as a
static method) or any command-line executable command. Scripts are useful
for executing a package's custom code or package-specific commands during
the Composer execution process.

As of Composer 2.5 scripts can also be Symfony Console Command classes,
which allows you to easily run them including passing options. This is
however not recommended for handling events.

> **Note:** Only scripts defined in the root package's `composer.json` are
> executed. If a dependency of the root package specifies its own scripts,
> Composer does not execute those additional scripts.

## Event names

Composer fires the following named events during its execution process:

### Command Events

- **pre-install-cmd**: occurs before the `install` command is executed with a
  lock file present.
- **post-install-cmd**: occurs after the `install` command has been executed
  with a lock file present.
- **pre-update-cmd**: occurs before the `update` command is executed, or before
  the `install` command is executed without a lock file present.
- **post-update-cmd**: occurs after the `update` command has been executed, or
  after the `install` command has been executed without a lock file present.
- **pre-status-cmd**: occurs before the `status` command is executed.
- **post-status-cmd**: occurs after the `status` command has been executed.
- **pre-archive-cmd**: occurs before the `archive` command is executed.
- **post-archive-cmd**: occurs after the `archive` command has been executed.
- **pre-autoload-dump**: occurs before the autoloader is dumped, either during
  `install`/`update`, or via the `dump-autoload` command.
- **post-autoload-dump**: occurs after the autoloader has been dumped, either
  during `install`/`update`, or via the `dump-autoload` command.
- **post-root-package-install**: occurs after the root package has been
  installed during the `create-project` command (but before its
  dependencies are installed).
- **post-create-project-cmd**: occurs after the `create-project` command has
  been executed.

### Installer Events

- **pre-operations-exec**: occurs before the install/upgrade/.. operations
  are executed when installing a lock file. Plugins that need to hook into
  this event will need to be installed globally to be usable, as otherwise
  they would not be loaded yet when a fresh install of a project happens.

### Package Events

- **pre-package-install**: occurs before a package is installed.
- **post-package-install**: occurs after a package has been installed.
- **pre-package-update**: occurs before a package is updated.
- **post-package-update**: occurs after a package has been updated.
- **pre-package-uninstall**: occurs before a package is uninstalled.
- **post-package-uninstall**: occurs after a package has been uninstalled.

### Plugin Events

- **init**: occurs after a Composer instance is done being initialized.
- **command**: occurs before any Composer Command is executed on the CLI. It
  provides you with access to the input and output objects of the program.
- **pre-file-download**: occurs before files are downloaded and allows
  you to manipulate the `HttpDownloader` object prior to downloading files
  based on the URL to be downloaded.
- **post-file-download**: occurs after package dist files are downloaded and
  allows you to perform additional checks on the file if required.
- **pre-command-run**: occurs before a command is executed and allows you to
  manipulate the `InputInterface` object's options and arguments to tweak
  a command's behavior.
- **pre-pool-create**: occurs before the Pool of packages is created, and lets
  you filter the list of packages that is going to enter the Solver.

> **Note:** Composer makes no assumptions about the state of your dependencies
> prior to `install` or `update`. Therefore, you should not specify scripts
> that require Composer-managed dependencies in the `pre-update-cmd` or
> `pre-install-cmd` event hooks. If you need to execute scripts prior to
> `install` or `update` please make sure they are self-contained within your
> root package.

## Defining scripts

The root JSON object in `composer.json` should have a property called
`"scripts"`, which contains pairs of named events and each event's
corresponding scripts. An event's scripts can be defined as either a string
(only for a single script) or an array (for single or multiple scripts.)

For any given event:

- Scripts execute in the order defined when their corresponding event is fired.
- An array of scripts wired to a single event can contain both PHP callbacks
and command-line executable commands.
- PHP classes and commands containing defined callbacks must be autoloadable
via Composer's autoload functionality.
- Callbacks can only autoload classes from psr-0, psr-4 and classmap
definitions. If a defined callback relies on functions defined outside of a
class, the callback itself is responsible for loading the file containing these
functions.

Script definition example:

```json
{
    "scripts": {
        "post-update-cmd": "MyVendor\\MyClass::postUpdate",
        "post-package-install": [
            "MyVendor\\MyClass::postPackageInstall"
        ],
        "post-install-cmd": [
            "MyVendor\\MyClass::warmCache",
            "phpunit -c app/"
        ],
        "post-autoload-dump": [
            "MyVendor\\MyClass::postAutoloadDump"
        ],
        "post-create-project-cmd": [
            "php -r \"copy('config/local-example.php', 'config/local.php');\""
        ]
    }
}
```

Using the previous definition example, here's the class `MyVendor\MyClass`
that might be used to execute the PHP callbacks:

```php
<?php

namespace MyVendor;

use Composer\Script\Event;
use Composer\Installer\PackageEvent;

class MyClass
{
    public static function postUpdate(Event $event)
    {
        $composer = $event->getComposer();
        // do stuff
    }

    public static function postAutoloadDump(Event $event)
    {
        $vendorDir = $event->getComposer()->getConfig()->get('vendor-dir');
        require $vendorDir . '/autoload.php';

        some_function_from_an_autoloaded_file();
    }

    public static function postPackageInstall(PackageEvent $event)
    {
        $installedPackage = $event->getOperation()->getPackage();
        // do stuff
    }

    public static function warmCache(Event $event)
    {
        // make cache toasty
    }
}
```

**Note:** During a Composer `install` or `update` command run, a variable named
`COMPOSER_DEV_MODE` will be added to the environment. If the command was run
with the `--no-dev` flag, this variable will be set to 0, otherwise it will be
set to 1. The variable is also available while `dump-autoload` runs, and it
will be set to the same as the last `install` or `update` was run in.

## Event classes

When an event is fired, your PHP callback receives as first argument a
`Composer\EventDispatcher\Event` object. This object has a `getName()` method
that lets you retrieve the event name.

Depending on the [script types](#event-names) you will get various event
subclasses containing various getters with relevant data and associated
objects:

- Base class: [`Composer\EventDispatcher\Event`](https://github.com/composer/composer/blob/main/src/Composer/EventDispatcher/Event.php)
- Command Events: [`Composer\Script\Event`](https://github.com/composer/composer/blob/main/src/Composer/Script/Event.php)
- Installer Events: [`Composer\Installer\InstallerEvent`](https://github.com/composer/composer/blob/main/src/Composer/Installer/InstallerEvent.php)
- Package Events: [`Composer\Installer\PackageEvent`](https://github.com/composer/composer/blob/main/src/Composer/Installer/PackageEvent.php)
- Plugin Events:
  - init: [`Composer\EventDispatcher\Event`](https://github.com/composer/composer/blob/main/src/Composer/EventDispatcher/Event.php)
  - command: [`Composer\Plugin\CommandEvent`](https://github.com/composer/composer/blob/main/src/Composer/Plugin/CommandEvent.php)
  - pre-file-download: [`Composer\Plugin\PreFileDownloadEvent`](https://github.com/composer/composer/blob/main/src/Composer/Plugin/PreFileDownloadEvent.php)
  - post-file-download: [`Composer\Plugin\PostFileDownloadEvent`](https://github.com/composer/composer/blob/main/src/Composer/Plugin/PostFileDownloadEvent.php)

## Running scripts manually

If you would like to run the scripts for an event manually, the syntax is:

```shell
php composer.phar run-script [--dev] [--no-dev] script
```

For example, `composer run-script post-install-cmd` will run any
**post-install-cmd** scripts and [plugins](plugins.md) that have been defined.

You can also give additional arguments to the script handler by appending `--`
followed by the handler arguments. e.g.
`composer run-script post-install-cmd -- --check` will pass`--check` along to
the script handler. Those arguments are received as CLI arg by CLI handlers,
and can be retrieved as an array via `$event->getArguments()` by PHP handlers.

## Writing custom commands

If you add custom scripts that do not fit one of the predefined event names
above, you can either run them with `run-script`, or as native
Composer commands. For example, the handler defined below is executable by
running `composer test`:

```json
{
    "scripts": {
        "test": "phpunit",
        "do-something": "MyVendor\\MyClass::doSomething",
        "my-cmd": "MyVendor\\MyCommand"
    }
}
```

Similar to the `run-script` command you can give additional arguments to scripts,
e.g. `composer test -- --filter <pattern>` will pass `--filter <pattern>` along
to the `phpunit` script.

Using a PHP method via `composer do-something arg` lets you execute a
`static function doSomething(\Composer\Script\Event $event)` and `arg` becomes
available in `$event->getArguments()`. This however does not let you easily pass
custom options in the form of `--flags`.

Using a [symfony/console](https://packagist.org/packages/symfony/console) `Command`
class you can describe your script, define and access arguments and options more
easily.

For example, with the command below you can then simply call `composer my-cmd
--arbitrary-flag` without even the need for a `--` separator. To be detected
as symfony/console commands, the class name must end with `Command` and extend
Symfony's `Command` class. Also note that this will run using Composer's built-in
symfony/console version, which may not match the one you have required in your
project and may change between Composer minor releases. If you need more
safety guarantees, you should rather use your own binary file that runs your own
symfony/console version in isolation in its own process then.

Script names and descriptions defined inside a `Command` class will override the
details from your `composer.json`: the key for the entry in `scripts` (used as
the command passed to `run-script`) will be replaced with either `$defaultName`
or the value passed to `setName()`, and similar replacement will happen with
anything included in `scripts-descriptions` for that script class.

```php
<?php

namespace MyVendor;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class MyCommand extends Command
{
    protected function configure(): void
    {
        $this
//          ->setName('custom-cmd') //if this gets included, it would execute with `composer custom-cmd` instead
            ->setDescription('Custom description for this command')
            ->setDefinition([
                new InputOption('arbitrary-flag', null, InputOption::VALUE_NONE, 'Example flag'),
                new InputArgument('foo', InputArgument::OPTIONAL, 'Optional arg'),
            ])
            ->setHelp(
                "Here you can define a long description for your command\n".
                "This would be visible with composer my-cmd --help"
            );
    }

    public function execute(InputInterface $input, OutputInterface $output): int
    {
        if ($input->getOption('arbitrary-flag')) {
            $output->writeln('The flag was used');
        }

        return 0;
    }
}
```

> **Note:** Before executing scripts, Composer's bin-dir is temporarily pushed
> on top of the PATH environment variable, so that binaries of dependencies
> are directly accessible. In this example, no matter if the `phpunit` binary is
> actually in `vendor/bin/phpunit` or `bin/phpunit`, it will be found and executed.


## Managing the process timeout

Although Composer is not intended to manage long-running processes and other
such aspects of PHP projects, it can sometimes be handy to disable the process
timeout on custom commands. This timeout defaults to 300 seconds and can be
overridden in a variety of ways depending on the desired effect:

- disable it for all commands using the config key `process-timeout`,
- disable it for the current or future invocations of composer using the
  environment variable `COMPOSER_PROCESS_TIMEOUT`,
- for a specific invocation using the `--timeout` flag of the `run-script` command,
- using a static helper for specific scripts.

To disable the timeout for specific scripts with the static helper directly in
composer.json:

```json
{
    "scripts": {
        "test": [
            "Composer\\Config::disableProcessTimeout",
            "phpunit"
        ]
    }
}
```

To disable the timeout for every script on a given project, you can use the
composer.json configuration:

```json
{
    "config": {
        "process-timeout": 0
    }
}
```

It's also possible to set the global environment variable to disable the timeout
of all following scripts in the current terminal environment:

```shell
export COMPOSER_PROCESS_TIMEOUT=0
```

To disable the timeout of a single script call, you must use the `run-script` composer
command and specify the `--timeout` parameter:

```shell
php composer.phar run-script --timeout=0 test
```

## Referencing scripts

To enable script re-use and avoid duplicates, you can call a script from another
one by prefixing the command name with `@`:

```json
{
    "scripts": {
        "test": [
            "@clearCache",
            "phpunit"
        ],
        "clearCache": "rm -rf cache/*"
    }
}
```

You can also refer a script and pass it new arguments:

```json
{
    "scripts": {
        "tests": "phpunit",
        "testsVerbose": "@tests -vvv"
    }
}
```

## Calling Composer commands

To call Composer commands, you can use `@composer` which will automatically
resolve to whatever composer.phar is currently being used:

```json
{
    "scripts": {
        "test": [
            "@composer install",
            "phpunit"
        ]
    }
}
```

One limitation of this is that you can not call multiple composer commands in
a row like `@composer install && @composer foo`. You must split them up in a
JSON array of commands.

## Executing PHP scripts

To execute PHP scripts, you can use `@php` which will automatically
resolve to whatever php process is currently being used:

```json
{
    "scripts": {
        "test": [
            "@php script.php",
            "phpunit"
        ]
    }
}
```

One limitation of this is that you can not call multiple commands in
a row like `@php install && @php foo`. You must split them up in a
JSON array of commands.

You can also call a shell/bash script, which will have the path to
the PHP executable available in it as a `PHP_BINARY` env var.

## Controlling additional arguments

As of Composer 2.8, you can control how additional arguments are passed to script commands.

When running scripts like `composer script-name arg arg2` or `composer script-name -- --option`,
Composer will by default append `arg`, `arg2` and `--option` to the script's command.

If you do not want these args in a given command, you can put `@no_additional_args`
anywhere in it, that will remove the default behavior and that flag will be removed
as well before running the command.

If you want the args to be added somewhere else than at the very end, then you can put
`@additional_args` to be able to choose exactly where they go.

For example running `composer run-commands ARG` with the below config:

```json
{
    "scripts": {
        "run-commands": [
            "echo hello @no_additional_args",
            "command-with-args @additional_args && do-something-without-args --here"
        ]
    }
}
```

Would end up executing these commands:

```
echo hello
command-with-args ARG && do-something-without-args --here
```

## Setting environment variables

To set an environment variable in a cross-platform way, you can use `@putenv`:

```json
{
    "scripts": {
        "install-phpstan": [
            "@putenv COMPOSER=phpstan-composer.json",
            "@composer install --prefer-dist"
        ]
    }
}
```

## Custom descriptions

You can set custom script descriptions with the following in your `composer.json`:

```json
{
    "scripts-descriptions": {
        "test": "Run all tests!"
    }
}
```

The descriptions are used in `composer list` or `composer run -l` commands to
describe what the scripts do when the command is run.

> **Note:** You can only set custom descriptions of custom commands.

## Custom aliases

As of Composer 2.7, you can set custom script aliases with the following in your `composer.json`:

```json
{
    "scripts-aliases": {
        "phpstan": ["stan", "analyze"]
    }
}
```

The aliases provide alternate command names.

> **Note:** You can only set custom aliases of custom commands.