Repository: box-project/box (php)

Issue:
Support PHP8
## Feature Request
Does it have any plan to support PHP8?
Do you think it should be OK with enabling PHP8.0 on CI and update composer.json?

Pull request:
add PHP 8 support
resolves #486 

Patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1ee35298..85b0320e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -8,44 +8,26 @@ on:
         types: [ created ]
 
 jobs:
-    tests:
+    unit-tests:
         runs-on: ubuntu-latest
-        name: Build and test
+        name: "Unit-Tests: ${{ matrix.php }} - ${{ matrix.tools }} - PHAR readonly ${{ matrix.phar-readonly }}"
         strategy:
             fail-fast: false
             matrix:
-                include:
-                    -   php: 7.3
-                        tools: "composer:v1"
-                        coverage: "none"
-                        publish-phar: true
-                        git-fetch-depth: 0 # box/composer needs history to determine a recent git version
-                    -   php: 7.3
-                        tools: "composer:v1"
-                        coverage: "none"
-                        phar-readonly: true
-                        git-fetch-depth: 1
-                    -   php: 7.4
-                        tools: "composer:v1"
-                        coverage: "pcov"
-                        git-fetch-depth: 1
-                    -   php: 7.4
-                        tools: "composer:v2"
-                        coverage: "none"
-                        git-fetch-depth: 1
+                php: [ 7.3, 7.4, 8.0 ]
+                tools: [ "composer:v1", "composer:v2" ]
+                phar-readonly: [ true, false ]
 
         steps:
             -   uses: actions/checkout@v2
-                with:
-                    fetch-depth: ${{ matrix.git-fetch-depth }}
 
             -   name: Setup PHP
                 uses: shivammathur/setup-php@v2
                 with:
                     php-version: ${{ matrix.php }}
-                    ini-values: "phar.readonly=0"
+                    ini-values: phar.readonly=0
                     tools: ${{ matrix.tools }}
-                    coverage: ${{ matrix.coverage }}
+                    coverage: none
                     extensions: ctype, iconv, xml
 
             -   name: Get composer cache directory
@@ -71,29 +53,160 @@ jobs:
             -   name: validate box config
                 run: bin/box validate
 
-            -   name: Run tests (coverage)
-                if: matrix.coverage == 'pcov'
-                run: make tm
-
             -   name: Run tests (phar readonly)
                 if: matrix.phar-readonly == true
                 run: make tu_box_phar_readonly
 
             -   name: Run tests
-                if: matrix.coverage != 'pcov' && matrix.phar-readonly != true
-                run: make test
+                if: matrix.phar-readonly == false
+                run: make tu
+
+    coverage:
+        runs-on: ubuntu-latest
+        name: Coverage
+
+        steps:
+            -   uses: actions/checkout@v2
+
+            -   name: Setup PHP
+                uses: shivammathur/setup-php@v2
+                with:
+                    php-version: 7.4
+                    ini-values: phar.readonly=0
+                    tools: composer:v2
+                    coverage: pcov
+                    extensions: ctype, iconv, xml
+
+            -   name: Get composer cache directory
+                id: composercache
+                run: echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+            -   name: Cache composer dependencies
+                uses: actions/cache@v2
+                with:
+                    path: ${{ steps.composercache.outputs.dir }}
+                    key: composer-${{ runner.os }}-${{ matrix.php }}-${{ hashFiles('composer.*') }}
+                    restore-keys: |
+                        composer-${{ runner.os }}-${{ matrix.php }}-
+                        composer-${{ runner.os }}-
+                        composer-
+
+            -   name: Install dependencies
+                run: composer install --no-interaction --no-progress --prefer-dist
+
+            -   name: Install requirement-checker dependencies
+                run: composer install --no-interaction --no-progress --prefer-dist --working-dir requirement-checker
+
+            -   name: Run tests (coverage)
+                run: make tm
+
+    e2e-tests:
+        runs-on: ubuntu-latest
+        name: "e2e-Tests: ${{ matrix.e2e }} - ${{ matrix.php }} - ${{ matrix.tools }}"
+        strategy:
+            fail-fast: false
+            matrix:
+                e2e:
+                    - 'e2e_php_settings_checker'
+                    - 'e2e_scoper_alias'
+                    - 'e2e_scoper_whitelist'
+                    - 'e2e_check_requirements'
+                    - 'e2e_symfony'
+                    - 'e2e_composer_installed_versions'
+                php: [ '7.3', '8.0' ]
+                tools: [ 'composer:v1', 'composer:v2' ]
+
+        steps:
+            -   uses: actions/checkout@v2
+                with:
+                    fetch-depth: 0
+
+            -   name: Setup PHP
+                uses: shivammathur/setup-php@v2
+                with:
+                    php-version: ${{ matrix.php }}
+                    ini-values: "phar.readonly=0"
+                    tools: ${{ matrix.tools }}
+                    coverage: pcov
+
+            -   name: Get composer cache directory
+                id: composercache
+                run: echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+            -   name: Cache composer dependencies
+                uses: actions/cache@v2
+                with:
+                    path: ${{ steps.composercache.outputs.dir }}
+                    key: composer-${{ runner.os }}-${{ matrix.php }}-${{ hashFiles('composer.*') }}
+                    restore-keys: |
+                        composer-${{ runner.os }}-${{ matrix.php }}-
+                        composer-${{ runner.os }}-
+                        composer-
+
+            -   name: Install dependencies
+                run: composer install --no-interaction --no-progress --no-suggest --prefer-dist
+
+            -   name: Install requirement-checker dependencies
+                run: composer install --no-interaction --no-progress --prefer-dist --working-dir requirement-checker
+
+            -   name: Run e2e ${{ matrix.e2e }}
+                run: make ${{ matrix.e2e }}
+
+    build-phar:
+        runs-on: ubuntu-latest
+        name: Build PHAR
+
+        steps:
+            -   uses: actions/checkout@v2
+                with:
+                    fetch-depth: 0
+
+            -   name: Setup PHP
+                uses: shivammathur/setup-php@v2
+                with:
+                    php-version: 7.3
+                    ini-values: phar.readonly=0
+                    tools: composer:v2
+                    coverage: none
+
+            -   name: Get composer cache directory
+                id: composercache
+                run: echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+            -   name: Cache composer dependencies
+                uses: actions/cache@v2
+                with:
+                    path: ${{ steps.composercache.outputs.dir }}
+                    key: composer-${{ runner.os }}-${{ matrix.php }}-${{ hashFiles('composer.*') }}
+                    restore-keys: |
+                        composer-${{ runner.os }}-${{ matrix.php }}-
+                        composer-${{ runner.os }}-
+                        composer-
+
+            -   name: Install dependencies
+                run: composer install --no-interaction --no-progress --prefer-dist
+
+            -   name: Install requirement-checker dependencies
+                run: composer install --no-interaction --no-progress --prefer-dist --working-dir requirement-checker
+
+            -   name: Build PHAR
+                run: make compile
 
             -   uses: actions/upload-artifact@v1
-                name: Publish the PHAR
-                if: matrix.publish-phar == true
+                name: Upload the PHAR artifact
                 with:
                     name: box.phar
                     path: bin/box.phar
 
+
     publish-phar:
         runs-on: ubuntu-latest
         name: Publish the PHAR
-        needs: tests
+        needs:
+            - unit-tests
+            - coverage
+            - e2e-tests
+            - build-phar
         if: github.event_name == 'release'
         steps:
             -   uses: actions/download-artifact@v1
diff --git a/Makefile b/Makefile
index 4474a4f8..a471c9e1 100644
--- a/Makefile
+++ b/Makefile
@@ -81,7 +81,7 @@ tc: bin/phpunit
 INFECTION=vendor-bin/infection/vendor/bin/infection
 tm:			 ## Runs Infection
 tm:	$(TU_BOX_DEPS) $(INFECTION)
-	$(PHPNOGC) $(INFECTION) --only-covered
+	$(PHPNOGC) $(INFECTION) --threads=$(shell nproc || sysctl -n hw.ncpu || 1) --only-covered
 
 .PHONY: e2e
 e2e:			 ## Runs all the end-to-end tests
diff --git a/composer.json b/composer.json
index 766b707e..09d933ec 100644
--- a/composer.json
+++ b/composer.json
@@ -41,7 +41,7 @@
     "minimum-stability": "dev",
     "prefer-stable": true,
     "require": {
-        "php": "^7.3",
+        "php": "^7.3 || ^8.0",
         "ext-phar": "*",
         "composer-plugin-api": "^1.0 || ^2.0",
         "amphp/parallel-functions": "^0.1.3",
diff --git a/composer.lock b/composer.lock
index c1228124..da77fe7b 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,20 +4,20 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "cb30c7d71d00991c23a397b6f0599ca4",
+    "content-hash": "f16b9a3f7ca8490bef00843a80d032ef",
     "packages": [
         {
             "name": "amphp/amp",
-            "version": "v2.5.0",
+            "version": "v2.5.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/amphp/amp.git",
-                "reference": "f220a51458bf4dd0dedebb171ac3457813c72bbc"
+                "reference": "ecdc3c476b3ccff02f8e5d5bcc04f7ccfd18751c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/amphp/amp/zipball/f220a51458bf4dd0dedebb171ac3457813c72bbc",
-                "reference": "f220a51458bf4dd0dedebb171ac3457813c72bbc",
+                "url": "https://api.github.com/repos/amphp/amp/zipball/ecdc3c476b3ccff02f8e5d5bcc04f7ccfd18751c",
+                "reference": "ecdc3c476b3ccff02f8e5d5bcc04f7ccfd18751c",
                 "shasum": ""
             },
             "require": {
@@ -85,7 +85,7 @@
             "support": {
                 "irc": "irc://irc.freenode.org/amphp",
                 "issues": "https://github.com/amphp/amp/issues",
-                "source": "https://github.com/amphp/amp/tree/master"
+                "source": "https://github.com/amphp/amp/tree/v2.5.1"
             },
             "funding": [
                 {
@@ -93,7 +93,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2020-07-14T21:47:18+00:00"
+            "time": "2020-11-03T16:23:45+00:00"
         },
         {
             "name": "amphp/byte-stream",
@@ -521,32 +521,31 @@
         },
         {
             "name": "beberlei/assert",
-            "version": "v3.2.7",
+            "version": "v3.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/beberlei/assert.git",
-                "reference": "d63a6943fc4fd1a2aedb65994e3548715105abcf"
+                "reference": "5367e3895976b49704ae671f75bc5f0ba1b986ab"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/beberlei/assert/zipball/d63a6943fc4fd1a2aedb65994e3548715105abcf",
-                "reference": "d63a6943fc4fd1a2aedb65994e3548715105abcf",
+                "url": "https://api.github.com/repos/beberlei/assert/zipball/5367e3895976b49704ae671f75bc5f0ba1b986ab",
+                "reference": "5367e3895976b49704ae671f75bc5f0ba1b986ab",
                 "shasum": ""
             },
             "require": {
                 "ext-ctype": "*",
+                "ext-intl": "*",
                 "ext-json": "*",
                 "ext-mbstring": "*",
                 "ext-simplexml": "*",
-                "php": "^7"
+                "php": "^7.0 || ^8.0"
             },
             "require-dev": {
                 "friendsofphp/php-cs-fixer": "*",
-                "phpstan/phpstan-shim": "*",
-                "phpunit/phpunit": ">=6.0.0 <8"
-            },
-            "suggest": {
-                "ext-intl": "Needed to allow Assertion::count(), Assertion::isCountable(), Assertion::minCount(), and Assertion::maxCount() to operate on ResourceBundles"
+                "phpstan/phpstan": "*",
+                "phpunit/phpunit": ">=6.0.0",
+                "yoast/phpunit-polyfills": "^0.1.0"
             },
             "type": "library",
             "autoload": {
@@ -581,9 +580,9 @@
             ],
             "support": {
                 "issues": "https://github.com/beberlei/assert/issues",
-                "source": "https://github.com/beberlei/assert/tree/v3"
+                "source": "https://github.com/beberlei/assert/tree/v3.3.0"
             },
-            "time": "2019-12-19T17:51:41+00:00"
+            "time": "2020-11-13T20:02:54+00:00"
         },
         {
             "name": "composer/package-versions-deprecated",
@@ -933,7 +932,7 @@
             "support": {
                 "source": "https://github.com/JetBrains/phpstorm-stubs/tree/master"
             },
-            "time": "2020-11-17T11:49:11+00:00"
+            "time": "2020-12-14T09:34:51+00:00"
         },
         {
             "name": "justinrainbow/json-schema",
@@ -1118,29 +1117,29 @@
         },
         {
             "name": "opis/closure",
-            "version": "3.5.7",
+            "version": "3.6.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/opis/closure.git",
-                "reference": "4531e53afe2fc660403e76fb7644e95998bff7bf"
+                "reference": "943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/opis/closure/zipball/4531e53afe2fc660403e76fb7644e95998bff7bf",
-                "reference": "4531e53afe2fc660403e76fb7644e95998bff7bf",
+                "url": "https://api.github.com/repos/opis/closure/zipball/943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5",
+                "reference": "943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.4 || ^7.0"
+                "php": "^5.4 || ^7.0 || ^8.0"
             },
             "require-dev": {
                 "jeremeamia/superclosure": "^2.0",
-                "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
+                "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.5.x-dev"
+                    "dev-master": "3.6.x-dev"
                 }
             },
             "autoload": {
@@ -1177,9 +1176,9 @@
             ],
             "support": {
                 "issues": "https://github.com/opis/closure/issues",
-                "source": "https://github.com/opis/closure/tree/3.5.7"
+                "source": "https://github.com/opis/closure/tree/3.6.1"
             },
-            "time": "2020-09-06T17:02:15+00:00"
+            "time": "2020-11-07T02:01:34+00:00"
         },
         {
             "name": "paragonie/constant_time_encoding",
@@ -4744,7 +4743,7 @@
     "prefer-stable": true,
     "prefer-lowest": false,
     "platform": {
-        "php": "^7.3",
+        "php": "^7.3 || ^8.0",
         "ext-phar": "*",
         "composer-plugin-api": "^1.0 || ^2.0"
     },
diff --git a/fixtures/build/dir011/composer.json b/fixtures/build/dir011/composer.json
index 0bc7b5f9..b0367efb 100644
--- a/fixtures/build/dir011/composer.json
+++ b/fixtures/build/dir011/composer.json
@@ -7,5 +7,5 @@
             "src/functions.php"
         ]
     },
-    "bin": "index.php"
+    "bin": ["index.php"]
 }
diff --git a/fixtures/build/dir012/composer.json b/fixtures/build/dir012/composer.json
index a9e872cf..92e1d63d 100644
--- a/fixtures/build/dir012/composer.json
+++ b/fixtures/build/dir012/composer.json
@@ -20,7 +20,7 @@
             "php": "7.2.9"
         }
     },
-    "bin": "bin/console",
+    "bin": ["bin/console"],
     "autoload": {
         "psr-4": {
             "App\\": "src/"
diff --git a/src/Box.php b/src/Box.php
index a55dd451..0b7b9ee7 100644
--- a/src/Box.php
+++ b/src/Box.php
@@ -362,7 +362,7 @@ final class Box implements Countable
 
         $resource = openssl_pkey_get_private($key, (string) $password);
 
-        Assertion::isResource($resource, 'Could not retrieve the private key, check that the password is correct.');
+        Assertion::notSame(false, $resource, 'Could not retrieve the private key, check that the password is correct.');
 
         openssl_pkey_export($resource, $private);
 
diff --git a/src/PharInfo/PharDiff.php b/src/PharInfo/PharDiff.php
index 6d9867a1..3eb8bcb6 100644
--- a/src/PharInfo/PharDiff.php
+++ b/src/PharInfo/PharDiff.php
@@ -71,18 +71,22 @@ final class PharDiff
         $argA = escapeshellarg($this->pharA->tmp);
         $argB = escapeshellarg($this->pharB->tmp);
 
-        /** @var string $diff */
         // TODO: replace by the process component
-        $diff = `git diff --no-index $argA $argB`;
+        $diff = shell_exec("git diff --no-index $argA $argB");
+
+        if (null === $diff) {
+            return null;
+        }
 
         $diff = str_replace(
-            $this->pharA->tmp,
-            $this->pharA->getFileName(),
-            $diff
-        );
-        $diff = str_replace(
-            $this->pharB->tmp,
-            $this->pharB->getFileName(),
+            [
+                $this->pharA->tmp,
+                $this->pharB->tmp
+            ],
+            [
+                $this->pharA->getFileName(),
+                $this->pharB->getFileName()
+            ],
             $diff
         );
 
@@ -94,18 +98,22 @@ final class PharDiff
         $argA = escapeshellarg($this->pharA->tmp);
         $argB = escapeshellarg($this->pharB->tmp);
 
-        /** @var string $diff */
         // TODO: replace by the process component
-        $diff = `diff $argA $argB`;
+        $diff = shell_exec("diff $argA $argB");
+
+        if (null === $diff) {
+            return null;
+        }
 
         $diff = str_replace(
-            $this->pharA->tmp,
-            $this->pharA->getFileName(),
-            $diff
-        );
-        $diff = str_replace(
-            $this->pharB->tmp,
-            $this->pharB->getFileName(),
+            [
+                $this->pharA->tmp,
+                $this->pharB->tmp
+            ],
+            [
+                $this->pharA->getFileName(),
+                $this->pharB->getFileName()
+            ],
             $diff
         );
 


Test patch:
diff --git a/tests/Compactor/PhpTest.php b/tests/Compactor/PhpTest.php
index 213d9ec2..666171b3 100644
--- a/tests/Compactor/PhpTest.php
+++ b/tests/Compactor/PhpTest.php
@@ -111,7 +111,7 @@ class AClass
      */
     public function aMethod()
     {
-        \$test = true;# a comment
+        \$test = true;
     }
 }
 PHP
@@ -130,7 +130,7 @@ class AClass
 public function aMethod()
 {
 \$test = true;
- }
+}
 }
 PHP
         ];
diff --git a/tests/Configuration/ConfigurationFileNoConfigTest.php b/tests/Configuration/ConfigurationFileNoConfigTest.php
index 87f05e2e..20c4a431 100644
--- a/tests/Configuration/ConfigurationFileNoConfigTest.php
+++ b/tests/Configuration/ConfigurationFileNoConfigTest.php
@@ -19,6 +19,7 @@ use function KevinGH\Box\FileSystem\dump_file;
 use function KevinGH\Box\FileSystem\mkdir;
 use function KevinGH\Box\FileSystem\remove;
 use function KevinGH\Box\FileSystem\touch;
+use function natcasesort;
 use const PHP_OS_FAMILY;
 use function symlink;
 
@@ -689,6 +690,8 @@ JSON
             'Upgrade.php',
             'UpgradeCommand.php',
         ];
+        natcasesort($expected);
+        $expected = array_values($expected);
 
         $noFileConfig = $this->getNoFileConfig();
 
diff --git a/tests/Configuration/ConfigurationPhpCompactorTest.php b/tests/Configuration/ConfigurationPhpCompactorTest.php
index 6cbb4506..b9cd948f 100644
--- a/tests/Configuration/ConfigurationPhpCompactorTest.php
+++ b/tests/Configuration/ConfigurationPhpCompactorTest.php
@@ -231,7 +231,6 @@ class ConfigurationPhpCompactorTest extends ConfigurationTestCase
  * @Acme(type = "function")
  */
 function foo($x, $y): int {
-    // Compare the two values
     return $x <=> $y;
 }
 PHP
@@ -253,8 +252,7 @@ PHP
 
 */
 function foo($x, $y): int {
-
- return $x <=> $y;
+return $x <=> $y;
 }
 PHP
         ];
@@ -293,7 +291,6 @@ PHP
  * @Acme(type = "function")
  */
 function foo($x, $y): int {
-    // Compare the two values
     return $x <=> $y;
 }
 PHP
@@ -315,8 +312,7 @@ PHP
 
 */
 function foo($x, $y): int {
-
- return $x <=> $y;
+return $x <=> $y;
 }
 PHP
             ];
@@ -350,7 +346,6 @@ PHP
  * @Acme(type = "function")
  */
 function foo($x, $y): int {
-    // Compare the two values
     return $x <=> $y;
 }
 PHP
@@ -372,8 +367,7 @@ PHP
 
 */
 function foo($x, $y): int {
-
- return $x <=> $y;
+return $x <=> $y;
 }
 PHP
             ];
@@ -400,7 +394,6 @@ PHP
  * @Acme(type = "function")
  */
 function foo($x, $y): int {
-    // Compare the two values
     return $x <=> $y;
 }
 PHP
@@ -422,8 +415,7 @@ PHP
 
 */
 function foo($x, $y): int {
-
- return $x <=> $y;
+return $x <=> $y;
 }
 PHP
         ];
diff --git a/tests/PhpScoper/SerializablePhpScoperTest.php b/tests/PhpScoper/SerializablePhpScoperTest.php
index 5045f3d0..3470dce0 100644
--- a/tests/PhpScoper/SerializablePhpScoperTest.php
+++ b/tests/PhpScoper/SerializablePhpScoperTest.php
@@ -180,7 +180,9 @@ class SerializablePhpScoperTest extends TestCase
                 static function () use ($scoper) {
                     return $scoper;
                 },
-                "Serialization of 'class@anonymous' is not allowed",
+                PHP_VERSION_ID < 80000
+                    ? "Serialization of 'class@anonymous' is not allowed"
+                    : "Serialization of 'Humbug\PhpScoper\Scoper@anonymous' is not allowed",
             ];
         })();
     }