File: release-process.md

package info (click to toggle)
php8.4 8.4.11-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 208,108 kB
  • sloc: ansic: 1,060,628; php: 35,345; sh: 11,866; cpp: 7,201; pascal: 4,913; javascript: 3,091; asm: 2,810; yacc: 2,411; makefile: 689; xml: 446; python: 301; awk: 148
file content (1120 lines) | stat: -rw-r--r-- 45,871 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
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
# PHP Release Process

A release manager's role includes making packaged source code from the canonical
repository available according to the release schedule.

The release schedule for each version is published on the
[PHP wiki](https://wiki.php.net):

- [PHP 8.3](https://wiki.php.net/todo/php83)
- [PHP 8.2](https://wiki.php.net/todo/php82)
- [PHP 8.1](https://wiki.php.net/todo/php81)

The PHP project publishes builds every two weeks.

We publish [general availability][] (GA) releases for major and minor versions of
PHP on the fourth Thursday of November each year. Following the GA release, we
publish patch-level releases every four weeks, with at least one release
candidate (RC) published two weeks before each patch-level release.

Each major and minor version undergoes a 24-week pre-release cycle before GA
release. The pre-release cycle begins on the second Thursday of June with the
first alpha release of the new major/minor version. The pre-release cycle
consists of at least:

- 3 alpha releases
- 3 beta releases
- 6 release candidates

Feature freeze for the next major/minor occurs with the first beta release.

We refer to alpha, beta, and RC as *non-stable releases*, while GA are *stable*.

The process of making packaged source available and announcing availability is
explained in detail below. The process differs slightly for non-stable and
stable releases.

New release managers should review [New release manager
checklist](#new-release-manager-checklist) at the end of this document. This
section  explains the procedures for getting ready to begin work on managing PHP
releases.


## General notes and tips

1. Do not release on Fridays, Saturdays, or Sundays as this gives poor lead
   time for downstream consumers adhering to a typical work week.

   Our general procedure is to release on Thursdays, whenever possible.

2. Package two days before a release.

   If the release is to be on Thursday, package on Tuesday. Think about
   timezones as well.

3. Ensure that the relevant tests on CI are green.

   - https://cirrus-ci.com/github/php/php-src
   - https://github.com/php/php-src/actions

   > 💡 **Tip** \
   > We recommend checking the build status a couple of days before packaging day
   > to allow enough time to investigate failures, communicate with the authors,
   > and commit any fixes.
   >
   > Check the CI status for your branch periodically and resolve the failures
   > ASAP.
   >
   > See more in https://wiki.php.net/rfc/travis_ci.

4. Follow all steps to the letter.

   > 💡 **Tip** \
   > When you are unsure about anything, ask a previous RM before proceeding.
   > Ideally, make sure a previous RM is available to answer questions during
   > the first few releases of your version. For the steps related to the
   > `web-php`, `web-qa`, and `web-php-distributions` repositories, try to have
   > someone from the webmaster team on hand.

5. Verify the tags to be extra sure everything was tagged properly.

6. There is a PHP release Docker image https://github.com/sgolemon/php-release
   with forks available to help with releases.

7. Communicate with release managers via release-managers@php.net.

8. References to repositories in this document refer to the canonical source
   located at https://github.com/php.

9. It might be helpful to name your remote repositories something other than
   "origin" to avoid accidentally pushing changes to "origin" with [muscle
   memory][].

10. It might also be helpful to set up conditional includes in your global
    `~/.gitconfig` to set the proper `user.name`, `user.email`, and
    `user.signingKey` values to use with your local PHP repositories. See
    [Conditional Includes For Git Config][] for more information.


## Packaging a non-stable release (alpha/beta/RC)

All releases during the pre-release cycle (alpha/beta/RC) leading up to the GA
release for a version are *non-stable* releases. Following the GA release, all
RCs are *non-stable* releases.

All non-stable releases follow a similar pattern, though pre-GA releases have
slightly different steps. We'll call attention where the steps differ.

1. Check that CI is passing (see above).

2. Run `./scripts/dev/credits` in php-src and commit the changes to the credits
   files in `ext/standard`.

   > 💬 **Hint** \
   > It's rare this script will make any changes, so if you run `git diff`
   > and do not see anything, there's no need to panic. That means there are no
   > changes to the credits files.

3. Check out the *release branch* for this release from the *version branch*.

   > 🔶 **Non-stable version branches: pre-GA** \
   > There is no *version branch* for alpha or beta releases. Instead, treat the
   > main branch as the version branch. You will create a local-only release
   > branch from the main branch. Do not push it!
   >
   > ```shell
   > git checkout -b php-X.Y.0alpha1-local-release-branch upstream/master
   > ```
   >
   > During the first RC release, you will create (and push!) the version
   > branch for the pre-GA release, e.g., `PHP-8.2`. See
   > "[Forking a new version branch](#forking-a-new-version-branch)" below.
   > From this point forward, all pre-GA release branches will be created from
   > this version branch. Again, these release branches are local-only. Do not
   > push them!
   >
   > ```shell
   > git checkout -b php-X.Y.0beta2-local-release-branch upstream/PHP-X.Y
   > ```

   > 🔷 **Non-stable version branches: post-GA** \
   > After GA, you will create (and push) a new *patch-level version branch*
   > along with each non-stable release. For example, if you are building a
   > release for PHP 8.2.8 RC1, you will create the `PHP-8.2.8` patch-level
   > version branch from the `PHP-8.2` version branch.
   >
   > ```shell
   > git checkout -b PHP-X.Y.Z upstream/PHP-X.Y
   > git push upstream PHP-X.Y.Z
   > ```
   >
   > We will use the patch-level version branch to commit any critical bug or
   > security fixes before this version's GA.
   >
   > Then, from the patch-level version branch, you will create another
   > local-only release branch. Do not push this one!
   >
   > ```shell
   > git checkout -b php-X.Y.ZRC1-local-release-branch upstream/PHP-X.Y.Z
   > ```

4. Using your local-only release branch, bump the version numbers in
   `main/php_version.h`, `Zend/zend.h`, `configure.ac`, and possibly
   `NEWS`.

   For examples, see [Update versions for PHP 8.1.0beta3][] (for a pre-GA
   example) or [Update versions for PHP 8.1.6RC1][] along with
   [Update NEWS for PHP 8.1.6RC1][] (for a post-GA example).

   > ⚠️ **Important** \
   > Do not use abbreviations for alpha or beta. Do not use dashes as
   > separators.
   >
   > Do this:
   >
   > ```c
   > #define PHP_VERSION "7.4.22RC1"
   > ```
   >
   > Not this:
   >
   > ```c
   > #define PHP_VERSION "7.4.22-RC1"
   > ```

   > 🗒 **Note** \
   > We update `Zend/zend.h` only when preparing RC and GA releases. We do not
   > update `ZEND_VERSION` for alpha or beta releases.

   > 🚨 **API version bump for pre-GA** \
   > When releasing the *first release candidate* of a pre-GA release, you must
   > also bump the API version numbers in `Zend/zend_extensions.h`,
   > `Zend/zend_modules.h`, and `main/php.h`. See [Prepare for PHP 8.1.0RC1][],
   > for example.
   >
   > The API versions between the alpha, beta, and X.Y.0RCn releases may remain
   > the same, or be bumped as little as possible because PHP extensions need to
   > be rebuilt with each bump.
   >
   > Do *not* bump the API versions after RC1.

5. Compile and run `make test`, with and without ZTS (Zend Thread Safety), using
   the correct Bison and re2c versions, e.g., for PHP 7.4, Bison 3.0.0 and re2c
   0.13.4 are required, as a minimum.

   For example:

   ```shell
   # With ZTS
   make distclean || \
   ./buildconf --force \
       && ./configure --enable-zts --disable-all --enable-debug --enable-opcache --enable-opcache-jit \
       && make -j$(nproc) \
       && make test TEST_PHP_ARGS="-q -j$(nproc)" \
       || ./sapi/cli/php -v

   # Without ZTS
   make distclean || \
   ./buildconf --force \
       && ./configure --disable-all --enable-debug --enable-opcache --enable-opcache-jit \
       && make -j$(nproc) \
       && make test TEST_PHP_ARGS="-q -j$(nproc)" \
       || ./sapi/cli/php -v
   ```

6. After each build, check the output of `./sapi/cli/php -v` to ensure the
   versions match the release.

7. If all is correct, commit the changes to your local-only release branch.

   ```shell
   git add -p
   git commit --gpg-sign=YOURKEYID -m "Update versions for PHP X.Y.ZRCn"
   ```

8. Tag your local-only release branch with the release version.

   ```shell
   git tag -s -u YOURKEYID php-X.Y.ZRCn -m "Tag for php-X.Y.ZRCn"
   ```

9. 🔸 **For pre-GA releases only,** switch back to the `master` (for alphas and betas),
   or `PHP-X.Y` (for RCs), and update a `NEWS` for the new version. See
   [Update NEWS for PHP 8.2.0 alpha2][] and [Update NEWS for PHP 8.2.0RC6][] for a real example.

   Commit the changes to the `master` (or `PHP-X.Y`) branch.

   ```shell
   git add -p
   git commit --gpg-sign=YOURKEYID -m "[ci skip] Update NEWS for PHP X.Y.Z alpha2"
   ```

   🔷 **For post-GA releases only,** switch back to the *version branch* for
   your release (e.g., `PHP-8.2`) and bump the version numbers in
   `main/php_version.h`, `Zend/zend.h`, `configure.ac` and `NEWS`. This prepares
   the version branch for the next version.

   For example, if the RC is `8.2.1RC1` then the version numbers in the version
   branch should be bumped to `8.2.2-dev`. We do this regardless of whether we
   build a new RC to make sure `version_compare()` works correctly. See
   [Bump for 8.1.8-dev][] for a real example.

   Commit the changes to the version branch.

   ```shell
   git add -p
   git commit --gpg-sign=YOURKEYID -m "PHP-X.Y is now for PHP X.Y.Z-dev"
   ```

   > 💡 **Tip** \
   > Version branches (e.g. `PHP-8.1`) will *always* have version numbers in
   > `main/php_version.h`, `Zend/zend.h`, and `configure.ac` that end in `-dev`.
   > Patch-level version branches (e.g. `PHP-8.1.7`) will also *always* have
   > version numbers that end in `-dev` in these files. The main branch (i.e.
   > `master`) will *always* have version numbers that end in `-dev` in these
   > files.
   >
   > Only release tags should have version numbers in these files that do not
   > end in `-dev` (e.g., `8.1.7`, `8.1.7RC1`, `8.2.0alpha1`, etc.).

10. Push the changes to the `php-src`.

    ```shell
    git push upstream php-X.Y.ZRCn # tag name
    git push upstream PHP-X.Y.Z    # patch-level version branch (post-GA only)
    git push upstream PHP-X.Y      # version branch
    ```

    > 🚨 **Attention** \
    > Do not push with `--tags`, as this will push all local tags, including
    > tags you might not wish to push.
    >
    > Local-only release branches should not be pushed!

11. Run the following using the release tag to export the tree, create the
    `configure` script, and build and compress three tarballs (`.tar.gz`,
    `.tar.bz2` and `.tar.xz`).

    ```shell
    ./scripts/dev/makedist php-X.Y.ZRCn
    ```

12. Run the following using the release tag and your GPG key ID to sign the
    tarballs and save the signatures to `php-X.Y.ZRCn.manifest`, which you can
    upload to GitHub and include in the announcement emails.

    ```shell
    ./scripts/dev/gen_verify_stub X.Y.ZRCn YOURKEYID > php-X.Y.ZRCn.manifest
    ```

13. If you have the [GitHub command line tool][] installed, run the following to
    create a public Gist for the manifest file:

    ```shell
    gh gist create --public php-X.Y.ZRCn.manifest
    ```

    Or you may go to https://gist.github.com to create it manually.

14. Copy the tarballs (using scp, rsync, etc.) to your `public_html/` folder on
    downloads.php.net.

    ```shell
    scp php-X.Y.ZRCn.tar.* downloads.php.net:~/public_html/
    ```

    > 💬 **Hint** \
    > If you do not have a `public_html` directory, create it and set its
    > permissions to `0755`.

15. Now the tarballs and signatures may be found at
    `https://downloads.php.net/~yourname/`, e.g. https://downloads.php.net/~derick/.

16. Once the release is tagged, contact the release-managers@php.net distribution
    list so that Windows binaries can be created. Once those are made, they may
    be found at https://windows.php.net/qa/.

    Here is an example "ready for builds" message to release-managers@php.net:

    ```text
    Subject: PHP 8.1.6RC1 ready for builds

    Hi, all!

    Tag: php-8.1.6RC1
    Tarballs: https://downloads.php.net/~ramsey/
    Manifest: https://gist.github.com/ramsey/5d73f0717effb6d8d17699381361e4b1

    Cheers,
    Ben

    << PASTE FULL MANIFEST CONTENTS HERE >>
    ```


## Announcing a non-stable release (alpha/beta/RC)

1. Switch to your local clone of the `web-qa` repository and update the
   information in the `$QA_RELEASES` array in `include/release-qa.php`.

   Follow the documentation in the file for editing the QA release information.
   See also [Announce 8.1.0RC3][] and [8.1.6RC1][] for examples.

   Add, commit, and push your changes, when finished.

   ```shell
   git add -p
   git commit --gpg-sign=YOURKEYID -m "Announce PHP X.Y.ZRCn"
   git push upstream master
   ```

2. 🔶 **For pre-GA releases only,** add a short notice to `web-php` stating
   there is a new release, and highlight the major changes (e.g., security
   fixes).

   To help produce the files for this, use the `bin/createNewsEntry` tool. When
   you run it, it will ask several questions (see below). For pre-GA non-stable
   releases, use only the "frontpage" category.

   ```shell
   cd /path/to/repos/php/web-php
   ./bin/createNewsEntry
   # Please type in the title: PHP X.Y.0 Alpha n available for testing
   # Categories:
   #     0: PHP.net frontpage news   [frontpage]
   #     1: New PHP release          [releases]
   #     2: Conference announcement  [conferences]
   #     3: Call for Papers          [cfp]
   # Please select appropriate categories, separated with space: 0
   # Will a picture be accompanying this entry? no
   # And at last; paste/write your news item here.
   # To end it, hit <enter>.<enter>
   #
   #   [[ Here you will paste the full HTML of the post. ]]
   # .
   #
   git add -p
   git add archive/entries/*.xml
   git commit --gpg-sign=YOURKEYID -m "Announce PHP X.Y.0RCn"
   git push upstream master
   ```

   Each news entry for pre-GA releases will be similar, though we change the
   text slightly to indicate progression through the pre-release cycle. For
   example, here are all the news posts for the pre-GA releases of PHP 8.1.0:

   * [Announce 8.1.0alpha1](https://github.com/php/web-php/commit/57b9675c8d8550493289fa1fba77427c93cdd472)
   * [Announce 8.1.0alpha2](https://github.com/php/web-php/commit/cec044fc0763f5cfa77d0e79479f8b6279023570)
   * [Announce 8.1.0alpha3](https://github.com/php/web-php/commit/5c480765f444a3fddfd575e01fe0be3fcfdde05b)
   * [Announce 8.1.0beta1](https://github.com/php/web-php/commit/40840e3c3f89d6dd95baa4b8cdf22d6f206f86c2)
   * [Announce 8.1.0beta2](https://github.com/php/web-php/commit/7bf6acdadd4940bd9db711bf3f9d5a4054dc0722)
   * [Announce 8.1.0beta3](https://github.com/php/web-php/commit/38c8a872700fb0c2ebde49e2eae3374257ba6d08)
   * [Announce 8.1.0RC1](https://github.com/php/web-php/commit/6e4bf3d0228ce113728d5f1a769ed42e0d63ca10)
   * [Announce 8.1.0RC2](https://github.com/php/web-php/commit/1ae95f4b686a5d614a94a664a7466ee0e5cd21eb)
   * [Announce 8.1.0RC3](https://github.com/php/web-php/commit/3091246d77a3f445fcc593587597d0abcab8c373)
   * [Announce 8.1.0RC4](https://github.com/php/web-php/commit/fbaeb9403f4e2856115889946d3f63751e183c7b)
   * [Announce 8.1.0RC5](https://github.com/php/web-php/commit/46473ccccfb5f7fedc3f169c55fb7c22a596b55d)
   * [Announce 8.1.0RC6](https://github.com/php/web-php/commit/cacaef9c41352b5dbf3fbbf44702cc6c0cbfb478)

   > ⚠️ **Important** \
   > In your announcement news entry, be sure to include the following text or
   > text similar to the following:
   >
   > > Please DO NOT use this version in production, it is an early test version.

   > 🗒 **Note** \
   > When a version is in its post-GA phase, we do not post news entries for
   > non-stable releases.

3. Wait for the web and qa sites to update with the new information before
   sending announcements. This could take up to an hour.

4. Send *separate* announcement emails to:

   * `internals@lists.php.net`
   * `php-general@lists.php.net`
   * `php-qa@lists.php.net`

   In the announcement message, point out the location of the release and the
   possible release date of either the next RC or the final release. Also
   include the manifest generated by `gen_verify_stub` when you packaged the
   build.

   Here are a few examples of non-stable release announcement emails:

   * [PHP 8.1.0alpha1 is available for testing](https://news-web.php.net/php.qa/69043)
   * [PHP 8.1.0beta3 available for testing](https://news-web.php.net/php.qa/69079)
   * [PHP 8.1.0RC6 available for testing](https://news-web.php.net/php.qa/69117)
   * [PHP 8.1.7RC1 Ready for testing](https://news-web.php.net/php.qa/69163)

   > 🚨 **Send separate emails!** \
   > Do *not* send a single email message with all addresses in the `To`, `Cc`,
   > or `Bcc` headers. If a user replies to one of these messages, we do not
   > want their email client to automatically send the reply to each list, as
   > often occurs.

   > 💬 **Hint** \
   > We send emails to the followers of these mailing lists to notify them of
   > new releases, so they can make sure their projects keep working and can
   > report any potential bugs that should be fixed before the upcoming GA
   > release.

5. 🔶 **For pre-GA *RCs* only,** coordinate with the social media team (i.e.,
   Derick) to post a tweet with the RC release announcement and link to the news
   entry on php.net. ([@official_php](https://twitter.com/official_php))


## Packaging a stable release

1. Check out the *patch-level version branch* for the release
   (e.g., `PHP-8.1.7`).

   > 💬 **Hint** \
   > You should have created this branch when packaging the non-stable release
   > candidate for this version. If it is for a PHP-X.Y.0 version, then just
   > create and push this branch.

2. If a CVE commit needs to be merged to the release, have it committed to
   the base branches and [merged upwards as usual][] (e.g. commit the CVE fix
   to 7.2, merge to 7.3, 7.4, etc.). Then, you can cherry-pick it into the
   patch-level version branch for this release.

   Commit these changes and push the patch-level version branch. Ensure
   that CI is still passing (see above).

   > 💡 **Tip** \
   > Don't forget to update `NEWS` manually in an extra commit to the
   > patch-level version branch.

3. Run the `./scripts/dev/credits` script in the patch-level version branch,
   and commit the changes in the credits files in `ext/standard`.

   > 🗒 **Note** \
   > It's very rare this will make changes at this point, but we run it here
   > in case the credits changed as a result of a bug fix that was
   > cherry-picked into this branch.

4. Create a local-only release branch for this release from the *patch-level
   version branch*.

   ```shell
   git checkout -b php-X.Y.Z-local-release-branch upstream/PHP-X.Y.Z
   ```

5. Using your local-only release branch, bump the version numbers in
   `main/php_version.h`, `Zend/zend.h`, `configure.ac`, and possibly
   `NEWS`.

   For example, if you're releasing a stable version for PHP 8.1.8, then all
   the version numbers in the patch-level version branch should be
   `8.1.8-dev`. In your local-only release branch, you will change them all to
   `8.1.8`.

   See [Update versions for PHP 8.1.7][] and [Update NEWS for PHP 8.1.7][] for
   an example.

6. Compile and run `make test`, with and without ZTS (Zend Thread Safety), using
   the correct Bison and re2c versions, e.g., for PHP 7.4, Bison 3.0.0 and re2c
   0.13.4 are required, as a minimum.

   For example:

   ```shell
   # With ZTS
   make distclean || \
   ./buildconf --force \
       && ./configure --enable-zts --disable-all --enable-debug --enable-opcache --enable-opcache-jit \
       && make -j$(nproc) \
       && make test TEST_PHP_ARGS="-q -j$(nproc)" \
       || ./sapi/cli/php -v

   # Without ZTS
   make distclean || \
   ./buildconf --force \
       && ./configure --disable-all --enable-debug --enable-opcache --enable-opcache-jit \
       && make -j$(nproc) \
       && make test TEST_PHP_ARGS="-q -j$(nproc)" \
       || ./sapi/cli/php -v
   ```

7. After each build, check the output of `./sapi/cli/php -v` to ensure the
   versions match the release.

8. If all is correct, commit the changes to your local-only release branch.

   ```shell
   git add -p
   git commit --gpg-sign=YOURKEYID -m "Update versions for PHP X.Y.Z"
   ```

9. Tag your local-only release branch with the release version and push the tag.

   ```shell
   git tag -s -u YOURKEYID php-X.Y.Z -m "Tag for php-X.Y.Z"
   git push upstream php-X.Y.Z
   ```

10. Run the following using the release tag to export the tree, create the
    `configure` script, and build and compress three tarballs (`.tar.gz`,
    `.tar.bz2` and `.tar.xz`).

    ```shell
    ./scripts/dev/makedist php-X.Y.Z
    ```

    > 💬 **Hint** \
    > Check if the PEAR files are updated (Phar).

    > 💡 **Tip** \
    > On some systems the behavior of GNU tar can default to produce POSIX
    > compliant archives with PAX headers. As not every application is
    > compatible with that format, creation of archives with PAX headers should
    > be avoided. When packaging on such a system, the GNU tar can be influenced
    > by defining the environment variable `TAR_OPTIONS='--format=gnu'`.

11. Run the following using the release tag and your GPG key ID to sign the
    tarballs and save the signatures to `php-X.Y.Z.manifest`, which you can
    upload to GitHub and include in the announcement emails.

    ```shell
    ./scripts/dev/gen_verify_stub X.Y.Z YOURKEYID > php-X.Y.Z.manifest
    ```

12. If you have the [GitHub command line tool][] installed, run the following to
    create a public Gist for the manifest file:

    ```shell
    gh gist create --public php-X.Y.Z.manifest
    ```

    Or you may go to https://gist.github.com to create it manually.

13. Switch to your local clone of the `web-php-distributions` repository and
    copy the tarballs and signature files into the repository. Add, commit, and
    push them.

    ```shell
    cd /path/to/repos/php/web-php-distributions
    mv /path/to/repos/php/php-src/php-X.Y.Z.tar.* .
    git add php-X.Y.Z.tar.*
    git commit --gpg-sign=YOURKEYID -m "Add tarballs for php-X.Y.Z"
    git push upstream master
    ```

14. Switch to your local clone of the `web-php` repository and update the
    `web-php-distributions` submodule.

    ```shell
    cd /path/to/repos/php/web-php
    git pull --rebase upstream master
    git submodule init
    git submodule update
    cd distributions
    git fetch --all
    git pull --rebase upstream master
    cd ..
    git commit distributions -m "X.Y.Z tarballs"
    git push upstream master
    ```

    > 💬 **Hint** \
    > This fetches the last commit ID from `web-php-distributions` and pins the
    > "distributions" submodule in `web-php` to this commit ID.
    >
    > When the website syncs, which should happen within an hour, the tarballs
    > will be available from `https://www.php.net/distributions/php-X.Y.Z.tar.gz`,
    > etc.

15. Once the release is tagged, contact the release-managers@php.net distribution
    list so that Windows binaries can be created. Once those are made, they may
    be found at https://windows.php.net/qa/.

    > ⚠️ **Important** \
    > Do *not* send this announcement to any public lists.

    Here is an example "ready for builds" message to release-managers@php.net:

    ```text
    Subject: PHP 8.1.6 ready for builds

    Hi, all!

    Tag: php-8.1.6
    Tarballs: web-php-distributions
    Manifest: https://gist.github.com/ramsey/432fcf8afcbfb1f1de6c3ab47d82e366

    Cheers,
    Ben

    << PASTE FULL MANIFEST CONTENTS HERE >>
    ```


## Announcing a stable release

1. This steps applies only for releases after PHP-X.Y.0.

   Switch to your local clone of `web-php` and add the information for the
   previous release to `include/releases.inc`.

   For example, if you are preparing to announce version 8.2.2, then the
   previous release is 8.2.1, so you will add the information for 8.2.1 to this
   file. Most of the time, you can do this using the `bin/bumpRelease` tool.

   ```shell
   ./bin/bumpRelease 8 2
   ```

   The first number is the major version, and the second number is the minor
   version. In this example, we're bumping the release information for version
   8.2. There is no need to provide the patch level.

   > 💡 **Tip** \
   > If this fails for any reason, you can manually copy the information
   > for the previous release from `include/version.inc` into
   > `include/releases.inc`.

3. Update the version information for the new release in `include/version.inc`.

   Find the part of the `$data` array that is related to your version (e.g.,
   `$data['8.2']` for 8.2.x releases) or create a new section if releasing
   PHP-X.Y.0 version, and make the following edits / additions:

   * Set `version` to the full version number (e.g. '8.2.1')
   * Set `date` to the release date in `j M Y` format (e.g. '5 Jan 2021')
   * Update the `tags` array to include `'security'` if this is a security release
   * Set the `sha256` array with the hashes for each of the release tarballs

5. Create the release file and news entry for the new version.

   ```shell
   ./bin/createReleaseEntry -v X.Y.Z -r # --security for security releases
   ```

   This will create a release file (i.e., `releases/X_Y_Z.php`) and a news entry
   file (i.e., `archive/entries/YYYY-MM-DD-n.xml`), while also updating
   `archive/archive.xml`.

   Within these files, it will generate standard messages for the new version.
   You may edit the generated files to expand on the base message, if needed.

   The edits are necessary for PHP-X.Y.0 version where the format is different.
   See for example [PHP-8.2 announcement](https://github.com/php/web-php/commit/c966868202caafa880213055f4e3e97c0483119b)

7. Update the ChangeLog file for the given major version (e.g., `ChangeLog-8.php`).

   If PHP-X.Y.0 is released, modify the `ChangeLog-X.php` (where `X` is the major
   version) file manually first. The `$MINOR_VERSIONS` field needs to be extended
   with the new version and initial anchor (e.g. `<a id="PHP_8_4"></a>` if added
   for PHP 8.4) added above the first anchor of the previous version.

   ```shell
   ./bin/news2html 'https://github.com/php/php-src/raw/php-X.Y.Z/NEWS' 'X.Y.Z' 'ChangeLog-X.php'
   ```

9. Review all the changes in `web-php`, commit, and push them.

   ```shell
   git add -p
   git add archive/entries/*.xml releases/*.php
   git commit --gpg-sign=YOURKEYID -m "Announce PHP X.Y.Z"
   git push upstream master
   ```

   See [Announce PHP 8.1.6][] for an example commit.

10. Switch to your local clone of the `web-qa` repository and update the
   information in the `$QA_RELEASES` array in `include/release-qa.php`.

   The array probably contains information about the RC released two weeks ago
   in preparation for the current release. Since the current release is now GA,
   it's time to remove the RC build from the QA website.

   It is sufficient to set the `number` property for the release to `0` to
   stop displaying the RC build on the QA website. You may also remove the
   sha256 hashes for the RC tarballs, but it's not necessary. For an example,
   see [PHP 8.1.6 released][].

   Add, commit, and push your changes, when finished.

   ```shell
   git add -p
   git commit --gpg-sign=YOURKEYID -m "PHP X.Y.Z released"
   git push upstream master
   ```

11. 🚨 **Before sending announcement emails, check to make sure the websites have
   synced.**

   * Make sure the tarballs are available from, e.g.,
     `https://www.php.net/distributions/php-X.Y.Z.tar.gz`
   * Check the "downloads" page to make sure the new version appears:
     https://www.php.net/downloads
   * Does the news entry show up on the home page? https://www.php.net
   * Do the updates to the ChangeLog appear?
     e.g., https://www.php.net/ChangeLog-8.php
   * Is there a release page for the new version?
     e.g., `https://www.php.net/releases/X_Y_Z.php`
   * Does the RC for this version still appear on the QA home page?
     https://qa.php.net

   Keep in mind it may take up to an hour for the websites to sync.

11. Please note down the sha256 and the PGP signature (.asc). These *must* be
   included in the release mail.

12. Send *separate* announcement emails to:

   * `php-announce@lists.php.net`
   * `php-general@lists.php.net`
   * `internals@lists.php.net`

   Release announcement emails must include the manifest generated when
   packaging the build, along with links to the sources, Windows binaries, and
   changelog. Here are a few examples of stable release announcement emails:

   * [PHP 8.1.0 Released](https://news-web.php.net/php.announce/321)
   * [PHP 8.1.3 Released](https://news-web.php.net/php.announce/325)
   * [PHP 8.1.6 Released](https://news-web.php.net/php.announce/331)

   > ⚠️ **Important** \
   > For standard patch-level releases, we will note "This is a bugfix release."
   > If it is a security release, we must note "This is a security release."

   > 🚨 **Send separate emails!** \
   > Do *not* send a single email message with all addresses in the `To`, `Cc`,
   > or `Bcc` headers. If a user replies to one of these messages, we do not
   > want their email client to automatically send the reply to each list, as
   > often occurs.

13. Coordinate with the social media team (i.e., Derick) to
    [create a PR request](https://github.com/derickr/toot-together/blob/main/toots/README.md)
    for posting the release announcement to Mastodon. Posts need to be
    approved.

## Re-releasing the same version or a patch-level (i.e., `-plN`)

While unlikely, there may be times we need to re-release the same version. This
might happen if the tarballs have a corrupted file, for example.

Should this occur *before* announcing the release, you may choose to delete the
tag and go through the full packaging process again, as described above.

> 💬 **Hint** \
> This is one of the reasons we package releases two days before announcing
> them.

If this happens *after* announcing the release, you may choose to tag, package,
and release a patch-level (i.e., *pl*) release. If it is not critical and/or
affects a very limited subset of users, then you may choose to wait until the
next release.

If you choose to create a patch-level release, follow these steps:

1. Commit the new binaries to `web-php-distributions`

2. Update $data['X.Y'] in `web-php:/include/version.inc`
   (X.Y=major.minor release, e.g. '8.0'):

    * `version` to the full version number (e.g. '8.0.1-pl1')
    * `date` to the release date in `j M Y` format (e.g. '9 Jan 2021')
    * `tags` array should include `security` if this is a security release
    * `sha256` array and sub-elements for all SHA256 sums

3. Add a short notice to `web-php` stating that there is a new release, and
   highlight the major important things (security fixes) and when it is
   important to upgrade.

    * Call `php bin/createReleaseEntry -v <version> [ --security ]` in your
      local web-php checkout.

4. Commit all the changes (`include/version.inc`, `archive/archive.xml`,
   `archive/entries/YYYY-MM-DD-N.xml`).

5. Wait an hour or two, then send a mail to php-announce@lists.php.net,
   php-general@lists.php.net and internals@lists.php.net with a text similar to
   the news entry.

   Please make sure that the mail to php-announce@ is its own completely
   separate email. This is to make sure that replies to the announcement on
   php-general@ or internals@ will not accidentally hit the php-announce@
   mailinglist.


## Feature freeze

A major/minor version [feature freeze][] occurs with the first beta release.
Specifically, it occurs when the first beta release is packaged, which means the
feature freeze occurs two days before the first beta release.

The feature freeze for `php-src` means that we will not accept any new features
after the date of the feature freeze. For any RFCs to be included in the new
version, they should be discussed and have the voting polls closed no later than
the feature freeze date. However, this does not mean the new feature must have a
complete implementation by this date.

Following the feature freeze, the focus of work for the new version will be on
fixing bugs, writing tests, and completing/polishing all accepted features.

As a courtesy to the community, the release managers should remind others about
the upcoming feature freeze by posting reminders to internals@lists.php.net at
4-weeks, 3-weeks, 2-weeks, and 1-week prior to the feature freeze. This is a
recommendation and the intervals may vary based on work load.


## Forking a new version branch

When the new version has reached the first RC, it is time to create a new
version branch. This frees up the main branch (i.e., `master`) for any new
feature development that cannot go into the new version.

1. One week prior to tagging `X.Y.0RC1`, warn internals@ that your version's
   branch is about to be created. Be specific about when the branch creation
   will occur. For example: https://news-web.php.net/php.internals/99864

2. Just prior to tagging `X.Y.0RC1`, create the new version branch locally,
   i.e. `PHP-X.Y`.

3. Add a commit on the main branch (i.e., `master`) after the branch point.

   This commit should:

   * clear the `NEWS`, `UPGRADING`, and `UPGRADING.INTERNALS` files;
   * update the version numbers in `configure.ac`, `main/php_version.h`,
     `Zend/zend.h`, and `win32/build/confutils.js`;
   * update the API version numbers in `Zend/zend_extensions.h`,
     `Zend/zend_modules.h`, and `main/php.h`; and
   * add the new branch to the list in `CONTRIBUTING.md`.

   See [Prepare for PHP 8.2][] and [Prepare for PHP 8.2 (bis)][] for an example
   of what this commit should include.

4. Push the new version branch and the changes to the `master` branch, with an
   appropriate commit message (e.g., "master is now for PHP 8.3.0-dev").

5. Immediately notify internals@ of the new branch and advise on the new merging
   order. For example: https://news-web.php.net/php.internals/99903

6. Update `web-php:git.php` and https://wiki.php.net/vcs/gitworkflow to reflect
   the new branch.

   For example:

   * [Add PHP-8.1 to the Git steps page][]
   * [Changes to the wiki][]

> 💬 **Hint** \
> We create the new version branch at the first release candidate rather than at
> feature freeze to allow a period of time where the focus is on making the new
> version ready for RC and GA. During this time, the main branch is *only* for
> minor improvements and bug fixes. All major improvements and new features must
> wait.


## Preparing for the initial stable version (PHP X.Y.0)

1. When you release the first pre-GA RC, remind the documentation team
   (phpdoc@lists.php.net) to write the [migration guide][]. Make sure the guide
   is available before releasing the initial stable version, since you should
   link to it in the release announcements.

2. Timely get used to the differences in preparing and announcing a stable
   release.

3. Before releasing X.Y.0, merge the `NEWS` entries of the pre-releases, so that
   there is only a single section about PHP X.Y.0, instead of individual
   sections for each pre-release.

   All the changes that are already present in the previous version NEWS should be
   removed. It means all bug fixes that went to the previous version as well should
   have their entries removed. It is possible to use `grep` to compare the changes.
   For example if `82/NEWS` is NEWS for PHP 8.2 and `83/NEWS` is NEWS file for PHP 8.3,
   then following command will show changes present in both files:

   ```sh
   grep -Fxf 82/NEWS 83/NEWS
   ```


5. On the announcement day for the initial stable version (or shortly before),
   update the `Expires` field in the <https://www.php.net/.well-known/security.txt>
   file. The `Expires` field should be set to the expected date of the next X.Y.0
   release (following the one currently being prepared), which is usually the
   fourth Thursday of November in the next year.

   Following the recommendation of [RFC 9116](https://www.rfc-editor.org/rfc/rfc9116),
   we maintain an `Expires` time of about a year for our security policies. This
   provides security researchers with confidence they are using our most
   up-to-date reporting policies.

   The `security.txt` file is located in the [web-php repository](https://github.com/php/web-php)
   under the `.well-known/` directory. We may make changes to this file at other
   times, as needed, but we will always advance the `Expires` timestamp on a
   yearly cadence, coinciding with our X.Y.0 releases.

   Please see the instructions for
   [making changes to security.txt][security-txt].


## Prime the selection of release managers for the next version

About three months prior to the scheduled release of the first alpha release of
the next  minor or major version (around March 1st or shortly thereafter), the
release managers for the latest version branch should issue a call for
volunteers to begin the selection process for the next release managers.

1. Issue the call for volunteers on internals@lists.php.net on or around March
   1st. See, for example: https://news-web.php.net/php.internals/113334

   There is no rule for how long the call for volunteers must remain open. We
   should aim to select the release managers by early April, so announcing the
   call in early March gives people about a month to decide whether they wish to
   volunteer.

2. There should be two or more volunteers. Typically, one should be a veteran
   release manager (having previously served as a `php-src` release manager),
   while the other one (or two) should be rookies. Hold a vote if necessary (see
   https://wiki.php.net/rfc/releaseprocess#release_managers_selection).

3. Help the new release managers with their first steps.


## New release manager checklist

1. Request membership to the
   [release managers group](https://github.com/orgs/php/teams/release-managers) on GitHub.

2. Subscribe to the php-announce@lists.php.net mailing list by emailing
   php-announce+subscribe@lists.php.net

3. Email systems@php.net to get setup for access to downloads.php.net, to be
   added to the release-managers@php.net distribution list, and to be added to
   the moderators for php-announce@lists.php.net so you are able to moderate
   your release announcements.

   Provide the following information in a single email:

   - Preferred Unix username (will also become part of location to download RCs,
     such as `https://downloads.php.net/~derick/`).
   - An SSH public key, preferably a new unique one for PHP systems and
     projects.
   - Read [Machine Access](https://wiki.php.net/systems#machine_access) to set
     up access to downloads.php.net through jump hosts, and provide a
     `.google_authenticator` file for 2FA.
   - Your @php.net email address to use for the release-managers@php.net
     distribution list and php-announce@lists.php.net moderator address. This
     should preferably not forward to a Gmail address.
   - Your GitHub account name, so that your membership to the release managers
     group may be approved.

   A system admin will then contact you to go through with steps 5 through 8 of
   [2FA setup instructions](https://wiki.php.net/systems#fa_setup_instructions).

   > 💬 **Hint** \
   > To send email from your @php.net address, you will need to use a custom
   > SMTP server. If you use Gmail, you may
   > "[Send emails from a different address or alias][]."


4. Create a [GPG key][] for your @php.net address.

   > 💡 **Tip** \
   > If you're new to GPG, follow GitHub's instructions for
   > [Generating a new GPG key][].

   Publish your key by editing `include/gpg-keys.inc` in the `web-php`
   repository. Add a `case` for your username to the `gpg_key_get()` function,
   and paste the output from `gpg --fingerprint`. You may also need to update
   the `$branches` array in the `gpg_key_get_branches()` function to include
   your username alongside your branch.

   ```console
   ❯ gpg --fingerprint ramsey@php.net
   pub   rsa4096 2021-04-26 [SC] [expires: 2025-11-24]
   39B6 4134 3D8C 104B 2B14  6DC3 F9C3 9DC0 B969 8544
   uid           [ultimate] Ben Ramsey <ramsey@php.net>
   sub   rsa4096 2021-04-26 [E] [expires: 2025-11-24]
   ```

   Have one or more of the other RMs [sign your GPG key][], and publish your
   public key to a keyserver:

   ```shell
   gpg --keyserver keys.openpgp.org --send-keys YOURKEYID
   gpg --keyserver keyserver.ubuntu.com --send-keys YOURKEYID
   ```

   Add your public key to `php-keyring.gpg` in `web-php-distributions`. To do
   this, you will need to import all keys from the current PHP keyring file to
   your local GPG keyring. You will need to take note of the key IDs for each of
   the release managers listed in `php-keyring.gpg`. Then, you will export,
   specifying your key ID in addition to the key IDs of every other release
   manager. Save this export back to `php-keyring.gpg`, commit the changes,
   and push.

   ```shell
   cd /path/to/repos/php/web-php-distributions
   gpg php-keyring.gpg            # lists all keys in the keyring
   gpg --import php-keyring.gpg   # imports all keys to your local keyring
   gpg --export \
       --export-options export-minimal \
       --armor \
       YOURKEYID F9C39DC0B9698544 DBDB397470D12172 MORE RM KEY IDS ... \
       > php-keyring.gpg
   gpg php-keyring.gpg  # verify all the keys are present, including yours
   git add -p
   git commit --gpg-sign=YOURKEYID -m "Update PHP release manager keyring"
   git push
   ```

   `web-php-distributions` is a submodule of `web-php`. You'll now have to update
   the commit reference to reflect the change made in web-php-distributions.

   ```shell
   cd /path/to/repos/php/web-php
   git submodule update
   cd distributions           # This is the submodule referring to web-php-distributions
   git pull origin master
   cd ..
   git add distributions
   git commit --gpg-sign=YOURKEYID -m "Update php-keyring.gpg in distributions"
   git push
   ```

5. Make sure you have the following repositories cloned locally:

   * https://github.com/php/php-src
   * https://github.com/php/web-php
   * https://github.com/php/web-qa
   * https://github.com/php/web-php-distributions


[general availability]: https://en.wikipedia.org/wiki/Software_release_life_cycle#General_availability_(GA)
[muscle memory]: https://en.wikipedia.org/wiki/Muscle_memory
[Conditional Includes For Git Config]: https://motowilliams.com/2017-05-11-conditional-includes-for-git-config/
[Update versions for PHP 8.1.0beta3]: https://github.com/php/php-src/commit/3edd1087c70bee2ec21f0fbec1a575d78a500f15
[Update versions for PHP 8.1.6RC1]: https://github.com/php/php-src/commit/40e8ced23898e3069340ca03ea5febc5361015ad
[Update NEWS for PHP 8.1.6RC1]: https://github.com/php/php-src/commit/a4fdeaebe419b88e3b4a1f5aba845c2d4e81fd4e
[Prepare for PHP 8.1.0RC1]: https://github.com/php/php-src/commit/5764414eb8900ae98020a3c20693f4fb793efa99
[Update NEWS for PHP 8.2.0 alpha2]: https://github.com/php/php-src/commit/418f7211f71658d79d934861be20f277db96fe2c
[Update NEWS for PHP 8.2.0RC6]: https://github.com/php/php-src/commit/4ccc414961a70200d638ca281a35f893226d74e2
[Bump for 8.1.8-dev]: https://github.com/php/php-src/commit/3b6ee1eb19c14c3339ebfcf5c967065a9f828971
[GitHub command line tool]: https://cli.github.com
[Announce 8.1.0RC3]: https://github.com/php/web-qa/commit/f264b711fd3827803b79bbb342959eae57ea502b
[8.1.6RC1]: https://github.com/php/web-qa/commit/e6d61ad7a9d8be0b1cd159af29f3b9cbdde33384
[merged upwards as usual]: https://wiki.php.net/vcs/gitworkflow
[Update versions for PHP 8.1.7]: https://github.com/php/php-src/commit/d35e577a1bd0b35b9386cea97cddc73fd98eed6d
[Update NEWS for PHP 8.1.7]: https://github.com/php/php-src/commit/b241f07f52ca9f87bf52be81817f475e6e727439
[Announce PHP 8.1.6]: https://github.com/php/web-php/commit/9f796a96c65f07e45845ec248933bfb0010b94a9
[PHP 8.1.6 released]: https://github.com/php/web-qa/commit/bff725f8373cf6fd9d97ba62a8517b19721a4c2e
[feature freeze]: https://en.wikipedia.org/wiki/Freeze_(software_engineering)
[Prepare for PHP 8.2]: https://github.com/php/php-src/commit/1c33ddb5e5598c5385c4c965992c6e031fd00dd6
[Prepare for PHP 8.2 (bis)]: https://github.com/php/php-src/commit/a93e12f8a6dfc23e334339317c97aa35356db821
[Add PHP-8.1 to the Git steps page]: https://github.com/php/web-php/commit/1fcd78c2817cf1fbf1a1de2ddec1350be4e26491
[Changes to the wiki]: https://wiki.php.net/vcs/gitworkflow?do=diff&rev2%5B0%5D=1617123194&rev2%5B1%5D=1654728193&difftype=sidebyside
[migration guide]: https://www.php.net/manual/en/migration81.php
[GPG key]: https://en.wikipedia.org/wiki/GNU_Privacy_Guard
[Generating a new GPG key]: https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key
[sign your GPG key]: https://carouth.com/articles/signing-pgp-keys/
[Send emails from a different address or alias]: https://support.google.com/mail/answer/22370?hl=en
[security-txt]: https://github.com/php/policies/blob/main/security-policies.rst#making-changes-to-securitytxt