diff --git a/.github/workflows/grumphp.yml b/.github/workflows/grumphp.yml
index 03409b3ab..ba57508bd 100644
--- a/.github/workflows/grumphp.yml
+++ b/.github/workflows/grumphp.yml
@@ -7,9 +7,9 @@ jobs:
strategy:
matrix:
operating-system: [ubuntu-latest, macos-latest] #windows-latest currently not working
- php-versions: ['7.3', '7.4', '8.0']
+ php-versions: ['8.0', '8.1']
composer-options: ['', '--prefer-lowest']
- composer-versions: ['composer:v1', 'composer:v2']
+ composer-versions: ['composer:v2']
fail-fast: false
name: PHP ${{ matrix.php-versions }} @ ${{ matrix.operating-system }} with ${{ matrix.composer-versions }} ${{ matrix.composer-options }}
steps:
@@ -36,7 +36,11 @@ jobs:
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install dependencies
+ if: matrix.php-versions != '8.1'
run: composer update --prefer-dist --no-progress --no-suggest ${{ matrix.composer-options }}
+ - name: Install dependencies (Ignore platform)
+ if: matrix.php-versions == '8.1'
+ run: composer update --prefer-dist --no-progress --no-suggest ${{ matrix.composer-options }} --ignore-platform-req=php
- name: Set git variables
run: |
git config --global user.email "you@example.com"
diff --git a/appveyor.yml b/appveyor.yml
index 6728996d5..b055e3b27 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -5,16 +5,14 @@ shallow_clone: false
platform:
- x64
-matrix:
- allow_failures:
- - php_version: 8.0
+#matrix:
+# allow_failures:
+# - php_version: 8.1
environment:
matrix:
- dependencies: highest
- php_version: 7.3
- - dependencies: highest
- php_version: 7.4.3 # OpenSSL issue will be resolved in 7.4.6
+ php_version: 8.1
- dependencies: highest
php_version: 8.0
@@ -33,8 +31,6 @@ cache:
init:
- ps: $Env:php_directory = $Env:php_root_directory + '\' + $Env:php_version
- # TODO: This is a workaround for https://github.com/chocolatey/choco/issues/1843. Once this is fixed we
- # should go back to latest version in appveyor saving ourselves test time
- ps: choco install chocolatey -y --version 0.10.13 --allow-downgrade
- ps: $Env:exact_php_version = (((choco search php --exact --all --limit-output | Select-String -pattern $Env:php_version) -replace '[php|]', '') | %{ New-Object System.Version $_ } | Sort-Object | Select-Object -Last 1).ToString()
- ps: $Env:PATH = $Env:php_directory + ';' + $Env:composer_directory + ';' + $Env:PATH
@@ -68,8 +64,8 @@ install:
# Install dependencies
- ps: cd $Env:project_directory
- - IF %dependencies%==lowest composer update --prefer-dist --prefer-lowest --prefer-stable --no-progress --no-scripts --no-suggest --profile
- - IF %dependencies%==highest composer update --prefer-dist --no-progress --no-scripts --no-suggest --profile
+ - IF %dependencies%==lowest composer update --prefer-dist --prefer-lowest --prefer-stable --no-progress --no-scripts --no-suggest --profile --ignore-platform-req=php
+ - IF %dependencies%==highest composer update --prefer-dist --no-progress --no-scripts --no-suggest --profile --ignore-platform-req=php
- git config --global user.email "doesntmatter@dispostable.com"
- git config --global user.name "GrumPHP"
diff --git a/composer.json b/composer.json
index 87e1271b4..7f2c9d573 100644
--- a/composer.json
+++ b/composer.json
@@ -14,38 +14,38 @@
}
],
"require": {
- "php": "^7.3 || ^8.0",
+ "php": "^8.0",
"ext-json": "*",
- "composer-plugin-api": "~1.0 || ~2.0",
- "amphp/amp": "^2.4",
+ "composer-plugin-api": "~2.0",
+ "amphp/amp": "^2.6",
"amphp/parallel": "^1.4",
"amphp/parallel-functions": "^1.0",
- "doctrine/collections": "^1.6.7",
- "gitonomy/gitlib": "^1.0.3",
- "monolog/monolog": "~1.16 || ^2.0",
- "ondram/ci-detector": "^3.5 || ^4.0",
- "opis/closure": "^3.5",
- "psr/container": "^1.0",
- "seld/jsonlint": "~1.1",
- "symfony/config": "~4.4 || ~5.0",
- "symfony/console": "~4.4 || ~5.0",
- "symfony/dependency-injection": "~4.4 || ~5.0",
- "symfony/dotenv": "~4.4 || ~5.0",
- "symfony/event-dispatcher": "~4.4 || ~5.0",
- "symfony/filesystem": "~4.4 || ~5.0",
- "symfony/finder": "~4.4 || ~5.0",
- "symfony/options-resolver": "~4.4 || ~5.0",
- "symfony/process": "~4.4 || ~5.0",
- "symfony/yaml": "~4.4 || ~5.0"
+ "doctrine/collections": "^1.6.8",
+ "gitonomy/gitlib": "^1.3",
+ "monolog/monolog": "^2.0",
+ "ondram/ci-detector": "^4.0",
+ "opis/closure": "^3.6",
+ "psr/container": "^1.1",
+ "seld/jsonlint": "~1.8",
+ "symfony/config": "~5.3 || ~6.0",
+ "symfony/console": "~5.3 || ~6.0",
+ "symfony/dependency-injection": "~5.3 || ~6.0",
+ "symfony/dotenv": "~5.3 || ~6.0",
+ "symfony/event-dispatcher": "~5.3 || ~6.0",
+ "symfony/filesystem": "~5.3 || ~6.0",
+ "symfony/finder": "~5.3 || ~6.0",
+ "symfony/options-resolver": "~5.3 || ~6.0",
+ "symfony/process": "~5.3 || ~6.0",
+ "symfony/yaml": "~5.3 || ~6.0"
},
"require-dev": {
"brianium/paratest": "^6.3",
- "composer/composer": "^1.10.22 || ^2.0.13",
- "nikic/php-parser": "~4.0",
+ "composer/composer": "^2.1.8",
+ "nikic/php-parser": "~4.13",
"php-parallel-lint/php-parallel-lint": "^1.3",
"phpspec/phpspec": "^7.1",
"phpspec/prophecy-phpunit": "^2.0",
- "phpunit/phpunit": "^9.5"
+ "phpunit/phpunit": "^9.5.10"
},
"suggest": {
"atoum/atoum": "Lets GrumPHP run your unit tests.",
diff --git a/phive.xml b/phive.xml
index 905d55981..da5f8b82e 100644
--- a/phive.xml
+++ b/phive.xml
@@ -1,7 +1,7 @@
-
-
-
-
+
+
+
+
diff --git a/resources/config/util.yml b/resources/config/util.yml
index 4479ec804..4f1fbff91 100644
--- a/resources/config/util.yml
+++ b/resources/config/util.yml
@@ -20,3 +20,4 @@ services:
'7.3': '2021-12-06 23:59:59'
'7.4': '2022-11-28 23:59:59'
'8.0': '2023-11-26 23:59:59'
+ '8.1': '2024-11-25 23:59:59'
diff --git a/spec/Parser/Php/Context/ParserContextSpec.php b/spec/Parser/Php/Context/ParserContextSpec.php
index cb0ce4468..20ca41186 100644
--- a/spec/Parser/Php/Context/ParserContextSpec.php
+++ b/spec/Parser/Php/Context/ParserContextSpec.php
@@ -11,6 +11,7 @@ class ParserContextSpec extends ObjectBehavior
{
function let(SplFileInfo $file, ParseErrorsCollection $errors)
{
+ $file->beConstructedWith(['somefile']);
$this->beConstructedWith($file, $errors);
}
diff --git a/spec/Parser/Php/PhpParserSpec.php b/spec/Parser/Php/PhpParserSpec.php
index e2c5c4599..32171678a 100644
--- a/spec/Parser/Php/PhpParserSpec.php
+++ b/spec/Parser/Php/PhpParserSpec.php
@@ -72,6 +72,7 @@ function it_parses_a_file(NodeTraverser $traverser, Filesystem $filesystem)
function it_catches_parse_exceptions(Parser $parser, Filesystem $filesystem, SplFileInfo $file)
{
+ $file->beConstructedWith(['somefile']);
$file->getRealPath()->willReturn('a real path');
$filesystem->readFromFileInfo($file)->willReturn('file content');
diff --git a/src/Collection/FilesCollection.php b/src/Collection/FilesCollection.php
index 5135353c3..4ee01eed0 100644
--- a/src/Collection/FilesCollection.php
+++ b/src/Collection/FilesCollection.php
@@ -14,9 +14,9 @@
use Traversable;
/**
- * @extends ArrayCollection
+ * @extends ArrayCollection
*/
-class FilesCollection extends ArrayCollection implements \Serializable
+class FilesCollection extends ArrayCollection
{
/**
* Adds a rule that files must match.
@@ -47,7 +47,7 @@ public function names(array $patterns): self
{
$filter = new Iterator\FilenameFilterIterator($this->getIterator(), $patterns, []);
- return new self(iterator_to_array($filter));
+ return new self([...$filter]);
}
/**
@@ -63,7 +63,7 @@ public function notName(string $pattern): self
{
$filter = new Iterator\FilenameFilterIterator($this->getIterator(), [], [$pattern]);
- return new self(iterator_to_array($filter));
+ return new self([...$filter]);
}
/**
@@ -85,7 +85,7 @@ public function paths(array $patterns): self
{
$filter = new Iterator\PathFilterIterator($this->getIterator(), $patterns, []);
- return new self(iterator_to_array($filter));
+ return new self([...$filter]);
}
/**
@@ -111,7 +111,7 @@ public function notPaths(array $pattern): self
{
$filter = new Iterator\PathFilterIterator($this->getIterator(), [], $pattern);
- return new self(iterator_to_array($filter));
+ return new self([...$filter]);
}
public function extensions(array $extensions): self
@@ -139,7 +139,7 @@ public function size(string $size): self
$comparator = new Comparator\NumberComparator($size);
$filter = new Iterator\SizeRangeFilterIterator($this->getIterator(), [$comparator]);
- return new self(iterator_to_array($filter));
+ return new self([...$filter]);
}
/**
@@ -161,7 +161,7 @@ public function date(string $date): self
$comparator = new Comparator\DateComparator($date);
$filter = new Iterator\DateRangeFilterIterator($this->getIterator(), [$comparator]);
- return new self(iterator_to_array($filter));
+ return new self([...$filter]);
}
/**
@@ -182,14 +182,14 @@ public function filter(Closure $p): self
{
$filter = new Iterator\CustomFilterIterator($this->getIterator(), [$p]);
- return new self(iterator_to_array($filter));
+ return new self([...$filter]);
}
public function filterByFileList(Traversable $fileList): FilesCollection
{
$allowedFiles = array_map(function (SplFileInfo $file) {
return $file->getPathname();
- }, iterator_to_array($fileList));
+ }, [...$fileList]);
return $this->filter(function (SplFileInfo $file) use ($allowedFiles) {
return \in_array($file->getPathname(), $allowedFiles, true);
@@ -216,26 +216,26 @@ public function ignoreSymlinks(): FilesCollection
});
}
- /*
+ /**
* SplFileInfo cannot be serialized. Therefor, we help PHP a bit.
* This stuff is used for running tasks in parallel.
*/
- public function serialize(): string
+ public function __serialize(): array
{
- return serialize($this->map(function (SplFileInfo $fileInfo): string {
+ return $this->map(function (SplFileInfo $fileInfo): string {
return $fileInfo instanceof SymfonySplFileInfo
? $fileInfo->getRelativePathname()
: $fileInfo->getPathname();
- })->toArray());
+ })->toArray();
}
- /*
+ /**
* SplFileInfo cannot be serialized. Therefor, we help PHP a bit.
* This stuff is used for running tasks in parallel.
*/
- public function unserialize($serialized): void
+ public function __unserialize(array $data): void
{
- $files = unserialize($serialized, ['allowed_classes' => false]);
+ $files = $data;
$this->clear();
foreach ($files as $file) {
$this->add(new SymfonySplFileInfo($file, dirname($file), $file));
@@ -245,7 +245,7 @@ public function unserialize($serialized): void
/**
* Help Psalm out a bit:
*
- * @return \ArrayIterator
+ * @return \ArrayIterator
*/
public function getIterator(): \ArrayIterator
{
diff --git a/src/Composer/GrumPHPPlugin.php b/src/Composer/GrumPHPPlugin.php
index 5c90e016d..bfbeb8d1a 100644
--- a/src/Composer/GrumPHPPlugin.php
+++ b/src/Composer/GrumPHPPlugin.php
@@ -222,7 +222,10 @@ private function runGrumPhpCommand(string $command): void
// Windows requires double double quotes
// https://bugs.php.net/bug.php?id=49139
$windowsIsInsane = function (string $command): string {
- return $this->runsOnWindows() ? '"'.$command.'"' : $command;
+ return $command;
+ // Looks like this is not needed anymore since PHP8 - even though the bug is still open.
+ // Leaving this here for reference if the bug pops up again for some poeple!
+ // return $this->runsOnWindows() ? '"'.$command.'"' : $command;
};
// Run command
diff --git a/src/Configuration/GrumPHPExtension.php b/src/Configuration/GrumPHPExtension.php
index f8ff3a5c4..209948da3 100644
--- a/src/Configuration/GrumPHPExtension.php
+++ b/src/Configuration/GrumPHPExtension.php
@@ -27,7 +27,7 @@ public function getConfiguration(array $config, ContainerBuilder $container): Co
return new Configuration();
}
- public function getAlias()
+ public function getAlias(): string
{
return 'grumphp';
}
diff --git a/src/Console/Command/Git/InitCommand.php b/src/Console/Command/Git/InitCommand.php
index 604d0759a..1aadf49bd 100644
--- a/src/Console/Command/Git/InitCommand.php
+++ b/src/Console/Command/Git/InitCommand.php
@@ -102,10 +102,10 @@ public function execute(InputInterface $input, OutputInterface $output): int
foreach (self::$hooks as $hook) {
$gitHook = $this->filesystem->buildPath($gitHooksPath, $hook);
$hookTemplate = $this->filesystem->guessFile(
- array_filter([
+ [
$customHooksPath,
$resourceHooksPath,
- ]),
+ ],
[$hook]
);
diff --git a/src/IO/ConsoleIO.php b/src/IO/ConsoleIO.php
index 859b9a8dc..6d5268789 100644
--- a/src/IO/ConsoleIO.php
+++ b/src/IO/ConsoleIO.php
@@ -14,7 +14,7 @@
use Symfony\Component\Console\Style\StyleInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
-class ConsoleIO implements IOInterface, \Serializable
+class ConsoleIO implements IOInterface
{
private $input;
private $output;
@@ -143,16 +143,16 @@ static function (string $message) use ($color) : string {
* Serializing this IO will result in an unwritable resource stream.
* Therefor we serialize the data end build up a new stream instead.
*/
- public function serialize()
+ public function __serialize(): array
{
- return serialize([
+ return [
'input' => [
'arguments' => $this->input->getArguments(),
],
'output' => [
'verbosity' => $this->output->getVerbosity(),
],
- ]);
+ ];
}
/**
@@ -160,10 +160,8 @@ public function serialize()
* Note: When you run in parallel mode, the stream will be non-blocking.
* All tasks can write at the same time, which is not optimal.
*/
- public function unserialize($serialized)
+ public function __unserialize(array $data): void
{
- $data = unserialize($serialized, ['allowed_classes' => false]);
-
$this->input = new ArrayInput(
(array) ($data['input']['arguments'] ?? [])
);
diff --git a/src/Locator/GuessedPathsLocator.php b/src/Locator/GuessedPathsLocator.php
index 973e61845..093223be8 100644
--- a/src/Locator/GuessedPathsLocator.php
+++ b/src/Locator/GuessedPathsLocator.php
@@ -38,7 +38,7 @@ public function __construct(
public function locate(?string $cliConfigFile): GuessedPaths
{
- $workingDir = getcwd();
+ $workingDir = (string) getcwd();
$cliConfigFile = $this->makeOptionalPathAbsolute($workingDir, $cliConfigFile);
$cliConfigPath = $cliConfigFile ? dirname($cliConfigFile) : null;
$projectDirEnv = $this->makeOptionalPathAbsolute($workingDir, (string) ($_SERVER['GRUMPHP_PROJECT_DIR'] ?? ''));
@@ -55,13 +55,13 @@ public function locate(?string $cliConfigFile): GuessedPaths
);
$composerFilePathname = $this->filesystem->guessFile(
- array_filter([
+ [
$this->makeOptionalPathAbsolute($workingDir, (string) ($_SERVER['GRUMPHP_COMPOSER_DIR'] ?? '')),
$cliConfigPath,
$projectDirEnv,
$workingDir,
$gitWorkingDir
- ]),
+ ],
[
'composer.json'
]
@@ -74,13 +74,13 @@ public function locate(?string $cliConfigFile): GuessedPaths
: []
);
- $binDir = $this->filesystem->guessPath(array_filter([
+ $binDir = $this->filesystem->guessPath([
$this->makeOptionalPathAbsolute($workingDir, (string) ($_SERVER['GRUMPHP_BIN_DIR'] ?? '')),
$this->makeOptionalPathAbsolute(
$composerFilePath,
$this->ensureOptionalArgumentWithValidSlashes($composerFile->getBinDir())
)
- ]));
+ ]);
$composerConfigDefaultPath = $this->makeOptionalPathAbsolute(
$composerFilePath,
@@ -97,14 +97,14 @@ public function locate(?string $cliConfigFile): GuessedPaths
]);
$defaultConfigFile = $this->filesystem->guessFile(
- array_filter([
+ [
$cliConfigFile,
$cliConfigPath,
$composerConfigDefaultPath,
$projectDir,
$workingDir,
$gitWorkingDir,
- ]),
+ ],
[
'grumphp.yml',
'grumphp.yaml',
diff --git a/src/Task/CloverCoverage.php b/src/Task/CloverCoverage.php
index bbd9829b7..9fa9fa3f9 100644
--- a/src/Task/CloverCoverage.php
+++ b/src/Task/CloverCoverage.php
@@ -84,7 +84,7 @@ public function run(ContextInterface $context): TaskResultInterface
{
$configuration = $this->getConfig()->getOptions();
$percentage = round(min(100, max(0, (float) $configuration['level'])), 2);
- $cloverFile = $configuration['clover_file'];
+ $cloverFile = (string) $configuration['clover_file'];
if (!$this->filesystem->exists($cloverFile)) {
return TaskResult::createFailed($this, $context, 'Invalid input file provided');
diff --git a/src/Task/TwigCs.php b/src/Task/TwigCs.php
index 51459bac2..e23d5fbe6 100644
--- a/src/Task/TwigCs.php
+++ b/src/Task/TwigCs.php
@@ -58,7 +58,7 @@ public function run(ContextInterface $context): TaskResultInterface
$arguments->addOptionalArgument('--ansi', true);
// removes all NULL, FALSE and Empty Strings
- $exclude = array_filter($config['exclude'], 'strlen');
+ $exclude = array_filter($config['exclude'], static fn (mixed $exclude): bool => $exclude && $exclude !== '');
$arguments->addArgumentArray('--exclude=%s', $exclude);
$process = $this->processBuilder->buildProcess($arguments);
diff --git a/src/Util/Filesystem.php b/src/Util/Filesystem.php
index e86fa838f..ff13c331c 100644
--- a/src/Util/Filesystem.php
+++ b/src/Util/Filesystem.php
@@ -63,19 +63,30 @@ public function buildPath(string $baseDir, string $path): string
return $baseDir.DIRECTORY_SEPARATOR.$path;
}
+ /**
+ * @param non-empty-list $paths
+ */
public function guessPath(array $paths): string
{
+ $paths = array_filter($paths);
foreach ($paths as $path) {
if ($this->exists($path) && is_dir($path)) {
return $path;
}
}
- return current($paths);
+ return (string) current($paths);
}
+ /**
+ * @param non-empty-list $paths
+ * @param non-empty-list $fileNames
+ */
public function guessFile(array $paths, array $fileNames): string
{
+ $paths = array_filter($paths);
+ $fileNames = array_filter($fileNames);
+
foreach ($paths as $path) {
if (!$this->exists($path)) {
continue;
@@ -93,8 +104,8 @@ public function guessFile(array $paths, array $fileNames): string
}
}
- $firstPath = current($paths);
- $firstName = current($fileNames);
+ $firstPath = (string) current($paths);
+ $firstName = (string) current($fileNames);
if (preg_match('#'.preg_quote($firstName, '#').'$#', $firstPath)) {
return $firstPath;
diff --git a/test/E2E/AbstractE2ETestCase.php b/test/E2E/AbstractE2ETestCase.php
index c00dff31a..5020ddb1b 100644
--- a/test/E2E/AbstractE2ETestCase.php
+++ b/test/E2E/AbstractE2ETestCase.php
@@ -407,6 +407,7 @@ protected function runCommand(string $action, Process $process)
$process->setTimeout(300);
$process->run();
+
if (!$process->isSuccessful()) {
throw new \RuntimeException(
'Could not '.$action.'! '.$process->getOutput().PHP_EOL.$process->getErrorOutput()
@@ -472,8 +473,9 @@ protected function changeGitPermissions()
->in($this->rootDir)
->path('.git');
+ /** @var \SplFileInfo $gitDir */
foreach ($gitDirs as $gitDir) {
- $this->filesystem->chmod($gitDir, 0777, 0000, true);
+ $this->filesystem->chmod($gitDir->getPathname(), 0777, 0000, true);
}
}
diff --git a/test/Unit/Locator/GuessedPathsLocatorTest.php b/test/Unit/Locator/GuessedPathsLocatorTest.php
index e3b66f01b..69e91ee51 100644
--- a/test/Unit/Locator/GuessedPathsLocatorTest.php
+++ b/test/Unit/Locator/GuessedPathsLocatorTest.php
@@ -3,7 +3,7 @@
declare(strict_types=1);
namespace GrumPHP\Locator {
- function getcwd() {
+ function getcwd(): string {
return $GLOBALS['__current_workspace'] ?? \getcwd();
}
}
diff --git a/tools/composer-normalize b/tools/composer-normalize
index dfa36f728..0accdc477 100755
Binary files a/tools/composer-normalize and b/tools/composer-normalize differ
diff --git a/tools/phpcbf b/tools/phpcbf
index dcf50adc3..b66f5834f 100755
Binary files a/tools/phpcbf and b/tools/phpcbf differ
diff --git a/tools/phpcs b/tools/phpcs
index f49d1379e..5ed709315 100755
Binary files a/tools/phpcs and b/tools/phpcs differ
diff --git a/tools/psalm b/tools/psalm
index 35b6dd494..0452e69cc 100755
Binary files a/tools/psalm and b/tools/psalm differ