The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .editorconfig
├── .phpspec
    └── specification.tpl
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── SECURITY.md
├── UPGRADE-v2.md
├── appveyor.yml
├── bin
    └── grumphp
├── composer.json
├── composer.lock
├── doc
    ├── commands.md
    ├── conventions.md
    ├── extensions.md
    ├── faq.md
    ├── installation
    │   ├── exotic.md
    │   └── global.md
    ├── parameters.md
    ├── runner.md
    ├── tasks.md
    ├── tasks
    │   ├── ant.md
    │   ├── atoum.md
    │   ├── behat.md
    │   ├── brunch.md
    │   ├── clover_coverage.md
    │   ├── codeception.md
    │   ├── composer.md
    │   ├── composer_normalize.md
    │   ├── composer_require_checker.md
    │   ├── composer_script.md
    │   ├── deptrac.md
    │   ├── doctrine_orm.md
    │   ├── ecs.md
    │   ├── eslint.md
    │   ├── file_size.md
    │   ├── gherkin.md
    │   ├── git_blacklist.md
    │   ├── git_branch_name.md
    │   ├── git_commit_message.md
    │   ├── grunt.md
    │   ├── gulp.md
    │   ├── infection.md
    │   ├── jsonlint.md
    │   ├── kahlan.md
    │   ├── make.md
    │   ├── npm_script.md
    │   ├── paratest.md
    │   ├── pest.md
    │   ├── phan.md
    │   ├── phing.md
    │   ├── php7cc.md
    │   ├── phparkitect.md
    │   ├── phpcpd.md
    │   ├── phpcs.md
    │   ├── phpcsfixer.md
    │   ├── phplint.md
    │   ├── phpmd.md
    │   ├── phpmnd.md
    │   ├── phpparser.md
    │   ├── phpspec.md
    │   ├── phpstan.md
    │   ├── phpunit.md
    │   ├── phpunitbridge.md
    │   ├── phpversion.md
    │   ├── progpilot.md
    │   ├── psalm.md
    │   ├── rector.md
    │   ├── robo.md
    │   ├── securitychecker.md
    │   ├── securitychecker
    │   │   ├── composeraudit.md
    │   │   ├── enlightn.md
    │   │   ├── local.md
    │   │   ├── roave.md
    │   │   └── symfony.md
    │   ├── shell.md
    │   ├── stylelint.md
    │   ├── symfony_console.md
    │   ├── tester.md
    │   ├── twigcs.md
    │   ├── twigcsfixer.md
    │   ├── xmllint.md
    │   └── yamllint.md
    └── testsuites.md
├── grumphp.yml.dist
├── phpspec.yml
├── phpunit.xml.dist
├── psalm.xml
├── resources
    ├── ascii
    │   ├── failed.txt
    │   ├── grumphp-grumpy.txt
    │   ├── grumphp-happy.txt
    │   ├── me-gusta.txt
    │   ├── nopecat.txt
    │   └── succeeded.txt
    ├── config
    │   ├── config.yml
    │   ├── console.yml
    │   ├── fixer.yml
    │   ├── formatter.yml
    │   ├── linters.yml
    │   ├── locators.yml
    │   ├── parsers.yml
    │   ├── runner.yml
    │   ├── services.yml
    │   ├── subscribers.yml
    │   ├── tasks.yml
    │   └── util.yml
    ├── hooks
    │   ├── local
    │   │   ├── commit-msg
    │   │   └── pre-commit
    │   └── vagrant
    │   │   ├── commit-msg
    │   │   └── pre-commit
    └── logo
    │   ├── grumphp-grumpy.png
    │   ├── grumphp-happy.png
    │   ├── simplified-grumpy.png
    │   └── simplified-happy.png
└── src
    ├── Collection
        ├── FilesCollection.php
        ├── LintErrorsCollection.php
        ├── ParseErrorsCollection.php
        ├── ProcessArgumentsCollection.php
        ├── TaskResultCollection.php
        ├── TasksCollection.php
        └── TestSuiteCollection.php
    ├── Composer
        ├── DevelopmentIntegrator.php
        └── GrumPHPPlugin.php
    ├── Configuration
        ├── Compiler
        │   ├── ExtensionCompilerPass.php
        │   ├── PhpParserCompilerPass.php
        │   ├── RegisterListenersPass.php
        │   ├── TaskCompilerPass.php
        │   └── TestSuiteCompilerPass.php
        ├── Configuration.php
        ├── Configurator
        │   └── TaskConfigurator.php
        ├── ContainerBuilder.php
        ├── ContainerFactory.php
        ├── Environment
        │   ├── DotEnvRegistrar.php
        │   ├── DotEnvSerializer.php
        │   └── PathsRegistrar.php
        ├── GrumPHPExtension.php
        ├── GuessedPaths.php
        ├── Loader
        │   └── DistFileLoader.php
        ├── LoaderFactory.php
        ├── Model
        │   ├── AsciiConfig.php
        │   ├── EnvConfig.php
        │   ├── FixerConfig.php
        │   ├── GitStashConfig.php
        │   ├── HooksConfig.php
        │   ├── ParallelConfig.php
        │   ├── ProcessConfig.php
        │   └── RunnerConfig.php
        └── Resolver
        │   └── TaskConfigResolver.php
    ├── Console
        ├── ApplicationConfigurator.php
        └── Command
        │   ├── ConfigureCommand.php
        │   ├── Git
        │       ├── CommitMsgCommand.php
        │       ├── DeInitCommand.php
        │       ├── InitCommand.php
        │       └── PreCommitCommand.php
        │   └── RunCommand.php
    ├── Event
        ├── Dispatcher
        │   ├── Bridge
        │   │   └── SymfonyEventDispatcher.php
        │   └── EventDispatcherInterface.php
        ├── Event.php
        ├── RunnerEvent.php
        ├── RunnerEvents.php
        ├── RunnerFailedEvent.php
        ├── Subscriber
        │   ├── StashUnstagedChangesSubscriber.php
        │   └── VerboseLoggerSubscriber.php
        ├── TaskEvent.php
        ├── TaskEvents.php
        └── TaskFailedEvent.php
    ├── Exception
        ├── ExceptionInterface.php
        ├── ExecutableNotFoundException.php
        ├── FileNotFoundException.php
        ├── FixerException.php
        ├── InvalidArgumentException.php
        ├── ParallelException.php
        ├── PlatformException.php
        ├── ProcessException.php
        ├── RuntimeException.php
        └── TaskConfigResolverException.php
    ├── Extension
        └── ExtensionInterface.php
    ├── Fixer
        ├── FixResult.php
        ├── FixerUpper.php
        └── Provider
        │   ├── FixableProcessProvider.php
        │   └── FixableProcessResultProvider.php
    ├── Formatter
        ├── GitBlacklistFormatter.php
        ├── PhpCsFixerFormatter.php
        ├── PhpcsFormatter.php
        ├── ProcessFormatterInterface.php
        └── RawProcessFormatter.php
    ├── Git
        └── GitRepository.php
    ├── IO
        ├── ConsoleIO.php
        ├── GitHubActionsIO.php
        ├── IOFactory.php
        └── IOInterface.php
    ├── Linter
        ├── Json
        │   ├── JsonLintError.php
        │   └── JsonLinter.php
        ├── LintError.php
        ├── LinterInterface.php
        ├── Xml
        │   ├── XmlLintError.php
        │   └── XmlLinter.php
        └── Yaml
        │   ├── YamlLintError.php
        │   └── YamlLinter.php
    ├── Locator
        ├── AsciiLocator.php
        ├── ChangedFiles.php
        ├── EnrichedGuessedPathsFromDotEnvLocator.php
        ├── ExternalCommand.php
        ├── GitRepositoryDirLocator.php
        ├── GitRepositoryLocator.php
        ├── GitWorkingDirLocator.php
        ├── GuessedPathsLocator.php
        ├── ListedFiles.php
        ├── RegisteredFiles.php
        └── StdInFiles.php
    ├── Parser
        ├── ParseError.php
        ├── ParserInterface.php
        └── Php
        │   ├── Configurator
        │       └── TraverserConfigurator.php
        │   ├── Container
        │       └── VisitorContainer.php
        │   ├── Context
        │       └── ParserContext.php
        │   ├── Factory
        │       ├── ParserFactory.php
        │       └── TraverserFactory.php
        │   ├── PhpParser.php
        │   ├── PhpParserError.php
        │   └── Visitor
        │       ├── AbstractVisitor.php
        │       ├── ConfigurableVisitorInterface.php
        │       ├── ContextAwareVisitorInterface.php
        │       ├── DeclareStrictTypesVisitor.php
        │       ├── ForbiddenClassMethodCallsVisitor.php
        │       ├── ForbiddenFunctionCallsVisitor.php
        │       ├── ForbiddenStaticMethodCallsVisitor.php
        │       ├── NeverUseElseVisitor.php
        │       └── NoExitStatementsVisitor.php
    ├── Process
        ├── InputWritingProcessRunner.php
        ├── ProcessBuilder.php
        ├── ProcessFactory.php
        └── TmpFileUsingProcessRunner.php
    ├── Runner
        ├── Ci
        │   └── CiDetector.php
        ├── FixableTaskResult.php
        ├── MemoizedTaskResultMap.php
        ├── Middleware
        │   ├── EventDispatchingRunnerMiddleware.php
        │   ├── FixCodeMiddleware.php
        │   ├── GroupByPriorityMiddleware.php
        │   ├── HandleRunnerMiddleware.php
        │   ├── ReportingRunnerMiddleware.php
        │   ├── ReportingTasksSectionRunnerMiddleware.php
        │   ├── RunnerMiddlewareInterface.php
        │   └── TasksFilteringRunnerMiddleware.php
        ├── MiddlewareStack.php
        ├── Parallel
        │   ├── PoolFactory.php
        │   └── SerializedClosureTask.php
        ├── Reporting
        │   ├── RunnerReporter.php
        │   └── TaskResultsReporter.php
        ├── StopOnFailure.php
        ├── TaskHandler
        │   ├── Middleware
        │   │   ├── ErrorHandlingTaskHandlerMiddleware.php
        │   │   ├── EventDispatchingTaskHandlerMiddleware.php
        │   │   ├── MemoizedResultsTaskHandlerMiddleware.php
        │   │   ├── NonBlockingTaskHandlerMiddleware.php
        │   │   ├── ParallelProcessingMiddleware.php
        │   │   ├── ReportingTaskHandlerMiddleware.php
        │   │   ├── StopOnFailureTaskHandlerMiddleware.php
        │   │   └── TaskHandlerMiddlewareInterface.php
        │   └── TaskHandler.php
        ├── TaskResult.php
        ├── TaskResultInterface.php
        ├── TaskRunner.php
        └── TaskRunnerContext.php
    ├── Task
        ├── AbstractExternalTask.php
        ├── AbstractLinterTask.php
        ├── AbstractParserTask.php
        ├── Ant.php
        ├── Atoum.php
        ├── Behat.php
        ├── Brunch.php
        ├── CloverCoverage.php
        ├── Codeception.php
        ├── Composer.php
        ├── ComposerNormalize.php
        ├── ComposerRequireChecker.php
        ├── ComposerScript.php
        ├── Config
        │   ├── ConfigOptionsResolver.php
        │   ├── EmptyTaskConfig.php
        │   ├── LazyTaskConfig.php
        │   ├── Metadata.php
        │   ├── TaskConfig.php
        │   └── TaskConfigInterface.php
        ├── Context
        │   ├── ContextInterface.php
        │   ├── GitCommitMsgContext.php
        │   ├── GitPreCommitContext.php
        │   └── RunContext.php
        ├── Deptrac.php
        ├── DoctrineOrm.php
        ├── ESLint.php
        ├── Ecs.php
        ├── FileSize.php
        ├── Gherkin.php
        ├── Git
        │   ├── Blacklist.php
        │   ├── BranchName.php
        │   └── CommitMessage.php
        ├── Grunt.php
        ├── Gulp.php
        ├── Infection.php
        ├── JsonLint.php
        ├── Kahlan.php
        ├── Make.php
        ├── NpmScript.php
        ├── Paratest.php
        ├── Pest.php
        ├── Phan.php
        ├── Phing.php
        ├── Php7cc.php
        ├── PhpArkitect.php
        ├── PhpCpd.php
        ├── PhpCsFixer.php
        ├── PhpLint.php
        ├── PhpMd.php
        ├── PhpMnd.php
        ├── PhpParser.php
        ├── PhpStan.php
        ├── PhpVersion.php
        ├── Phpcs.php
        ├── Phpspec.php
        ├── Phpunit.php
        ├── PhpunitBridge.php
        ├── Progpilot.php
        ├── Psalm.php
        ├── Rector.php
        ├── Robo.php
        ├── SecurityChecker.php
        ├── SecurityCheckerComposeraudit.php
        ├── SecurityCheckerEnlightn.php
        ├── SecurityCheckerLocal.php
        ├── SecurityCheckerRoave.php
        ├── SecurityCheckerSymfony.php
        ├── Shell.php
        ├── Stylelint.php
        ├── SymfonyConsole.php
        ├── TaskInterface.php
        ├── Tester.php
        ├── TwigCs.php
        ├── TwigCsFixer.php
        ├── XmlLint.php
        └── YamlLint.php
    ├── Test
        ├── Runner
        │   ├── AbstractMiddlewareTestCase.php
        │   ├── AbstractRunnerMiddlewareTestCase.php
        │   └── AbstractTaskHandlerMiddlewareTestCase.php
        └── Task
        │   ├── AbstractExternalTaskTestCase.php
        │   └── AbstractTaskTestCase.php
    ├── TestSuite
        ├── TestSuite.php
        └── TestSuiteInterface.php
    └── Util
        ├── ComposerFile.php
        ├── Filesystem.php
        ├── Paths.php
        ├── PhpVersion.php
        ├── Platform.php
        ├── Regex.php
        └── Str.php


/.editorconfig:
--------------------------------------------------------------------------------
 1 | # EditorConfig is awesome: https://EditorConfig.org
 2 | 
 3 | # top-most EditorConfig file
 4 | root = true
 5 | 
 6 | # Unix-style newlines with a newline ending every file
 7 | [*]
 8 | end_of_line = lf
 9 | insert_final_newline = true
10 | 
11 | # 4 space indentation
12 | [*.{php,md}]
13 | indent_style = space
14 | indent_size = 4
15 | 


--------------------------------------------------------------------------------
/.phpspec/specification.tpl:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | namespace %namespace%;
 4 | 
 5 | use PhpSpec\ObjectBehavior;
 6 | use Prophecy\Argument;
 7 | use %subject%;
 8 | 
 9 | class %name% extends ObjectBehavior
10 | {
11 |     function it_is_initializable()
12 |     {
13 |         $this->shouldHaveType(%subject_class%::class);
14 |     }
15 | }
16 | 


--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
 1 | # Contributing
 2 | 
 3 | GrumPHP is an open source, community-driven project. If you'd like to contribute,
 4 | feel free to do this, but remember to follow this few simple rules:
 5 | 
 6 | ## Branching strategy
 7 | 
 8 | - __Always__ base your changes on the latest version `v<version>.x` branch (all new development happens here),
 9 | - When you create Pull Request, always select `v<version>.x` branch as target, otherwise it
10 | will be closed (this is selected by default).
11 | 
12 | ## Coverage
13 | 
14 | - All classes that interact solely with the core logic should be covered by Specs
15 | - Any infrastructure adaptors should be covered by integration tests using PHPUnit
16 | - All features should be covered with .feature descriptions automated with Behat
17 | 
18 | ## Code style / Formatting
19 | 
20 | - All new classes must carry the standard copyright notice docblock
21 | - All code in the `src` folder must follow the PSR-2 standard
22 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | Copyright (c) 2015-2018 Phpro
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
 1 | help:
 2 | 	@echo "Please use \`make <target>' where <target> is one of"
 3 | 	@echo "  run            to perform GrumPHP tests"
 4 | 	@echo "  tag            to modify the version and tag"
 5 | 
 6 | run:
 7 | 	./vendor/bin/grumphp run
 8 | 
 9 | tag:
10 | 	$(if $(TAG),,$(error TAG is not defined. Pass via "make tag TAG=4.2.1"))
11 | 	@echo Tagging $(TAG)
12 | 	sed -i '' -e "s/APP_VERSION = '.*'/APP_VERSION = '$(TAG)'/" src/Console/ApplicationConfigurator.php
13 | 	php -l src/Console/ApplicationConfigurator.php
14 | 	git add -A
15 | 	git commit -m '$(TAG) release'
16 | 	git tag -s 'v$(TAG)' -m'Version $(TAG)'
17 | 
18 | lock:
19 | 	$(if $(PHP),,$(error PHP is not defined. Pass via "make lock PHP=8.1"))
20 | 	composer self-update
21 | 	composer config platform.php '$(PHP)'
22 | 	composer update --no-scripts --no-plugins --no-interaction --optimize-autoloader
23 | 	composer validate
24 | 


--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
 1 | # Security Policy
 2 | 
 3 | ## Supported Versions
 4 | 
 5 | Following versions are supported and will receive security updates depending on the vulnerability:
 6 | 
 7 | | Version | Supported          |
 8 | | ------- | ------------------ |
 9 | | > 2.x   | :white_check_mark: |
10 | | < 2.0   | :x:                |
11 | 
12 | 
13 | ## Reporting a Vulnerability
14 | 
15 | DO NOT PUBLISH SECURITY REPORTS PUBLICLY.
16 | 
17 | (Since no-one is waiting for a zero-day vulnerability!)
18 | 
19 | If you found any issues that might have security implications,
20 | please send a report through the security advisories form https://github.com/phpro/grumphp/security/advisories.
21 | This form will report a security vulnerability that is visible for the owners only.
22 | 
23 | From there on, we can triage the issue and start fixing it.
24 | 
25 | 
26 | ## Security Bug Bounties
27 | 
28 | GrumPHP is an Open-Source project where most of the work is done by volunteers. We appreciate that developers are trying to find security issues in GrumPHP and report them responsibly, but we are currently unable to pay bug bounties. 
29 | 
30 | 
31 | 


--------------------------------------------------------------------------------
/bin/grumphp:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env php
 2 | <?php
 3 | 
 4 | use GrumPHP\Configuration\ContainerFactory;
 5 | use Symfony\Component\Console\Input\ArgvInput;
 6 | use Symfony\Component\Console\Output\ConsoleOutput;
 7 | 
 8 | (function () {
 9 |     // First load current projects autoloader for smarter  global / phar installs ....
10 |     // A bit opinionated at the moment though:
11 |     $autoloaderInWorkingDirectory = getcwd() . '/vendor/autoload.php';
12 |     if (is_file($autoloaderInWorkingDirectory)) {
13 |         require_once $autoloaderInWorkingDirectory;
14 |     }
15 | 
16 |     // Next load the GrumPHP autoloader
17 |     $loaded = array_reduce(
18 |         [
19 |             __DIR__.'/../vendor/autoload.php', // (Normal bin dir)
20 |             __DIR__.'/../../../autoload.php', // From location inside folder
21 |             __DIR__.'/../../vendor/autoload.php' // (Development integration)
22 |         ],
23 |         static function (?string $loaded, string $file): ?string {
24 |             if ( ! $loaded && is_file($file)) {
25 |                 require_once($file);
26 | 
27 |                 return $file;
28 |             }
29 | 
30 |             return $loaded;
31 |         }
32 |     );
33 | 
34 |     if (!$loaded) {
35 |         fwrite(
36 |             STDERR,
37 |             'You must set up the project dependencies, run the following commands:'.PHP_EOL.
38 |             'curl -s http://getcomposer.org/installer | php'.PHP_EOL.
39 |             'php composer.phar install'.PHP_EOL
40 |         );
41 |         exit(1);
42 |     }
43 | 
44 |     $container = ContainerFactory::build(
45 |         $input = new ArgvInput(),
46 |         $output = new ConsoleOutput()
47 |     );
48 | 
49 |     $application = $container->get('GrumPHP\Console\Application');
50 |     $application->run($input, $output);
51 | })();
52 | 


--------------------------------------------------------------------------------
/doc/installation/global.md:
--------------------------------------------------------------------------------
 1 | # Global installation
 2 | 
 3 | It is possible to install or update GrumPHP on your system with following commands:
 4 | 
 5 | ```sh
 6 | composer global require phpro/grumphp
 7 | ```
 8 | 
 9 | This will install the `grumphp` executable in the `~/.composer/vendor/bin` folder.
10 | Make sure to add this folder to your system `$PATH` variable:
11 | 
12 | ```sh
13 | # .zshrc or .bashrc
14 | export PATH="$HOME/.composer/vendor/bin:$PATH"
15 | ```
16 | 
17 | That's all! The `grumphp` command will be available on your CLI.
18 | When your project also has a grumphp executable, this on will be used in favour of the one globally installed.
19 | The same goes for other tools like e.g. `phpunit` you have installed both globally and locally.
20 | 


--------------------------------------------------------------------------------
/doc/tasks/ant.md:
--------------------------------------------------------------------------------
 1 | # Ant
 2 | 
 3 | The Ant task will run your automated Ant tasks.
 4 | It lives under the `ant` namespace and has following configurable parameters:
 5 | 
 6 | ```yaml
 7 | # grumphp.yml
 8 | grumphp:
 9 |     tasks:
10 |         ant:
11 |             build_file: ~
12 |             task: ~
13 |             triggered_by: [php]
14 | ```
15 | 
16 | **build_file**
17 | 
18 | *Default: null*
19 | 
20 | If your build.xml file is located at an exotic location, you can specify your custom build file location with this option.
21 | This option is set to `null` by default.
22 | This means that `build.xml` is automatically loaded if the file exists in the current directory.
23 | 
24 | 
25 | **task**
26 | 
27 | *Default: null*
28 | 
29 | This option specifies which Ant task you want to run.
30 | This option is set to `null` by default.
31 | This means that ant will run the `default` task.
32 | Note that this task should be used to verify things. 
33 | It is also possible to alter code during commit, but this is surely **NOT** recommended!
34 | 
35 | 
36 | **triggered_by**
37 | 
38 | *Default: [php]*
39 | 
40 | This option will specify which file extensions will trigger the ant task.
41 | By default, Ant will be triggered by altering a PHP file. 
42 | You can overwrite this option to whatever file you want to use!
43 | 


--------------------------------------------------------------------------------
/doc/tasks/behat.md:
--------------------------------------------------------------------------------
 1 | #  Behat
 2 | 
 3 | The Behat task will run your Behat tests.
 4 | 
 5 | ***Composer***
 6 | 
 7 | ```
 8 | composer require --dev behat/behat
 9 | ```
10 | 
11 | ***Config***
12 | 
13 | The task lives under the `behat` namespace and has following configurable parameters:
14 | 
15 | ```yaml
16 | # grumphp.yml
17 | grumphp:
18 |     tasks:
19 |         behat:
20 |             config: ~
21 |             format: ~
22 |             suite: ~
23 |             profile: ~
24 |             stop_on_failure: false
25 | ```
26 | 
27 | **config**
28 | 
29 | *Default: null*
30 | 
31 | If you want to use a different config file than the default behat.yml, you can specify your custom config file location with this option.
32 | 
33 | 
34 | **format**
35 | 
36 | *Default: null*
37 | 
38 | If you want to use a different formatter than the default one, specify it with this option.
39 | 
40 | 
41 | **suite**
42 | 
43 | *Default: null*
44 | 
45 | If you want to run a particular suite only, specify it with this option.
46 | 
47 | 
48 | **profile**
49 | 
50 | *Default: null*
51 | 
52 | If you want to use a specific configuration profile other than the default one, specify it with this option. 
53 | 
54 | 
55 | **stop_on_failure**
56 | 
57 | *Default: false*
58 | 
59 | When this option is enabled, behat will stop at the first error. This means that it will not run your full test suite when an error occurs.
60 | 


--------------------------------------------------------------------------------
/doc/tasks/brunch.md:
--------------------------------------------------------------------------------
 1 | # Brunch
 2 | 
 3 | The Brunch task will run your automated frontend tasks.
 4 | It lives under the `brunch` namespace and has following configurable parameters:
 5 | 
 6 | ```yaml
 7 | # grumphp.yml
 8 | grumphp:
 9 |     tasks:
10 |         brunch:
11 |             task: build
12 |             env: production
13 |             jobs: 4
14 |             debug: false
15 |             triggered_by: [js, jsx, coffee, ts, less, sass, scss]
16 | ```
17 | 
18 | **task**
19 | 
20 | *Default: build*
21 | 
22 | This option specifies which Brunch task you want to run.
23 | This option is set to `build` by default.
24 | This means that brunch will run the `build` task.
25 | Note that this task should be used to compile your assets. 
26 | It is also possible to alter code during commit, but this is surely **NOT** recommended!
27 | 
28 | **env**
29 | 
30 | *Default: production*
31 | 
32 | This option specifies in which format you want to compile your assets.
33 | E.g: `--env production`. You can specify the env you set up in your brunch config file.
34 | 
35 | **jobs**
36 | 
37 | *Default: 4*
38 | 
39 | This option enables experimental multi-process support. May improve compilation speed of large projects. Try different WORKERS amount to see which one works best for your system.
40 | 
41 | **debug**
42 | 
43 | *Default: false*
44 | 
45 | It enables verbose debug output.
46 | 
47 | **triggered_by**
48 | 
49 | *Default: [js, jsx, coffee, ts, less, sass, scss]*
50 | 
51 | This option will specify which file extensions will trigger the brunch task.
52 | By default, Brunch will be triggered by altering a front-end file. 
53 | You can overwrite this option to whatever file you want to use!
54 | 


--------------------------------------------------------------------------------
/doc/tasks/clover_coverage.md:
--------------------------------------------------------------------------------
 1 | # Clover Coverage
 2 | 
 3 | The Clover Coverage task will run your unit tests.
 4 | 
 5 | Note that to make sure that there is always a clover file available, you might need to
 6 | set `always_execute` to `true` in the `phpunit` task configuration.
 7 | 
 8 | It lives under the `clover_coverage` namespace and has following configurable parameters:
 9 | 
10 | ```yaml
11 | # grumphp.yml
12 | grumphp:
13 |     tasks:
14 |         clover_coverage:
15 |             clover_file: /tmp/clover.xml
16 |             minimum_level: 100
17 |             target_level: null
18 | ```
19 | 
20 | **clover_file**
21 | 
22 | *Required*
23 | 
24 | The location of the clover code coverage XML file.
25 | 
26 | **minimum_level**
27 | 
28 | *Default: 100*
29 | 
30 | The minimum code coverage percentage required to pass.
31 | 
32 | **target_level**
33 | 
34 | *Default: null*
35 | 
36 | Setting a minimum code coverage level is letting the task fail hard.
37 | When you are in the process of increasing your code coverage, you can set a target level.
38 | When the code coverage is below the target level, the task will fail in a non-blocking way.
39 | This gives you the opportunity to increase the code coverage step by step in a non-blocking way whilst keeping track of the progress.
40 | 


--------------------------------------------------------------------------------
/doc/tasks/codeception.md:
--------------------------------------------------------------------------------
 1 | # Codeception
 2 | The Codeception task will run your full-stack tests.
 3 | 
 4 | ***Composer***
 5 | 
 6 | ```
 7 | composer require --dev codeception/codeception
 8 | ```
 9 | 
10 | ***Config***
11 | 
12 | The task lives under the `codeception` namespace and has the following configurable parameters:
13 | 
14 | ```yaml
15 | # grumphp.yml
16 | grumphp:
17 |     tasks:
18 |         codeception:
19 |             config_file: ~
20 |             fail_fast: false
21 |             suite: ~
22 |             test: ~
23 |             xml: false
24 |             html: false
25 | ```
26 | 
27 | 
28 | **config_file**
29 | 
30 | *Default: null*
31 | 
32 | If your `codeception.yml` file is located in an exotic location, you can specify your custom config file location with this option. This option is set to `null` by default. This means that `codeception.yml` is automatically located if it exists in the current directory.
33 | 
34 | **fail_fast**
35 | 
36 | *Default: false*
37 | 
38 | When this option is enabled, Codeception will stop at the first error. This means that it will not run your full test suite when an error occurs.
39 | 
40 | **suite**
41 | 
42 | *Default: null*
43 | 
44 | When this option is specified it will only run tests for the given suite. If left `null` Codeception will run tests for your full test suite.
45 | 
46 | **test**
47 | 
48 | *Default: null*
49 | 
50 | When this option is specified it will only run the given test. If left `null` Codeception will run all tests within the suite.
51 | This option can only be used in combination with a suite.
52 | 
53 | **xml**
54 | 
55 | *Default: false*
56 | 
57 | When this option is enabled, Codeception will output an XML report for the test run.
58 | 
59 | **html**
60 | 
61 | *Default: false*
62 | 
63 | When this option is enabled, Codeception will output an HTML report for the test run.
64 | 


--------------------------------------------------------------------------------
/doc/tasks/composer.md:
--------------------------------------------------------------------------------
 1 | # Composer
 2 | 
 3 | When the `composer.json` file has changed, the new file should be checked for issues.
 4 | This task will execute [`composer validate`](https://getcomposer.org/doc/03-cli.md#validate) to make sure that everything is OK.
 5 | The configuration looks like:
 6 | 
 7 | ```yaml
 8 | # grumphp.yml
 9 | grumphp:
10 |     tasks:
11 |         composer:
12 |             file: ./composer.json
13 |             no_check_all: false
14 |             no_check_lock: false
15 |             no_check_publish: false
16 |             no_local_repository: false
17 |             with_dependencies: false
18 |             strict: false
19 | ```
20 | 
21 | **file**
22 | 
23 | *Default: ./composer.json*
24 | 
25 | Specifies at which location the `composer.json` file can be found.
26 | 
27 | 
28 | **no_check_all**
29 | 
30 | *Default: false*
31 | 
32 | Do not emit a warning if requirements in composer.json use unbound version constraints.
33 | 
34 | 
35 | **no_check_lock**
36 | 
37 | *Default: false*
38 | 
39 | Do not emit an error if composer.lock exists and is not up to date.
40 | 
41 | 
42 | **no_check_publish**
43 | 
44 | *Default: false*
45 | 
46 | Do not emit an error if composer.json is unsuitable for publishing as a package on Packagist but is otherwise valid.
47 | 
48 | 
49 | **no_local_repository**
50 | 
51 | *Default: false*
52 | 
53 | Do emit an error if composer.json declares local repositories (see https://getcomposer.org/doc/05-repositories.md#path).
54 | 
55 | 
56 | **with_dependencies**
57 | 
58 | *Default: false*
59 | 
60 | Also validate the `composer.json` of all installed dependencies.
61 | 
62 | 
63 | **strict**
64 | 
65 | *Default: false*
66 | 
67 | Return a non-zero exit code for warnings as well as errors.
68 | 


--------------------------------------------------------------------------------
/doc/tasks/composer_normalize.md:
--------------------------------------------------------------------------------
 1 | # Composer Normalize ![fixer](https://img.shields.io/badge/-fixer-informational)
 2 | 
 3 | If you are using `composer`, you have probably modified the file `composer.json` at least once to keep things nice
 4 | and tidy.
 5 | 
 6 | ***Composer***
 7 | 
 8 | ```
 9 | composer require --dev ergebnis/composer-normalize
10 | ```
11 | 
12 | ***Config***
13 | 
14 | This task is a wrapper around a composer plugin for tidying up the file `composer.json`.
15 | 
16 | The default configuration looks like:
17 | 
18 | ```yaml
19 | # grumphp.yml
20 | grumphp:
21 |     tasks:
22 |         composer_normalize:
23 |             indent_size: ~
24 |             indent_style: ~
25 |             no_check_lock: false
26 |             no_update_lock: true
27 |             verbose: false
28 | ```
29 | 
30 | **indent_size**
31 | 
32 | *Default: null*
33 | 
34 | Indent size (an integer greater than 0); must be used with the `indent_style` option
35 | 
36 | **indent_style**
37 | 
38 | *Default: null*
39 | 
40 | Indent style (one of "space", "tab"); must be used with the `indent_size` option
41 | 
42 | **no_check_lock**
43 | 
44 | *Default: false*
45 | 
46 | If `true`, do not check if lock file is up to date.
47 | 
48 | **no_update_lock**
49 | 
50 | *Default: true*
51 | 
52 | If `false`, do not update lock file if it exists.
53 | 
54 | **use_standalone**
55 | 
56 | *Default: false*
57 | 
58 | If `true`, use the standalone `composer-normalize` command instead of the Composer plugin.
59 | 
60 | **verbose**
61 | 
62 | *Default: false*
63 | 
64 | Set this to true if you want to see the diff.
65 | 


--------------------------------------------------------------------------------
/doc/tasks/composer_require_checker.md:
--------------------------------------------------------------------------------
 1 | # Composer Require Checker
 2 | 
 3 | The Composer Require Checker task analyzes composer dependencies and verifies that no unknown symbols are used in the
 4 | code. This will prevent you from using "soft" dependencies that are not defined within your composer.json.
 5 | It lives under the `composer_require_checker` namespace and has following configurable parameters:
 6 | 
 7 | ## Composer
 8 | ```bash
 9 | composer require --dev maglnet/composer-require-checker
10 | ```
11 | 
12 | ## Config
13 | ```yaml
14 | # grumphp.yml
15 | grumphp:
16 |     tasks:
17 |         composer_require_checker:
18 |             composer_file: 'composer.json'
19 |             config_file: ~
20 |             ignore_parse_errors: false
21 |             triggered_by: ['composer.json', 'composer.lock', '*.php']
22 | ```
23 | 
24 | **composer_file**
25 | 
26 | *Default: null*
27 | 
28 | The composer.json of your code base that should be checked.
29 | 
30 | **config_file**
31 | 
32 | *Default: null*
33 | 
34 | Composer Require Checker is configured to whitelist some symbols by default. You can now override this configuration
35 | with your own and tell GrumPHP to use that configuration file instead.
36 | 
37 | **ignore_parse_errors**
38 | 
39 | *Default: false*
40 | 
41 | This will cause Composer Require Checker to ignore errors when files cannot be parsed, otherwise errors will be thrown.
42 | 
43 | This option is only available in version 0.2.0 of `maglnet/composer-require-checker` and above.
44 | 
45 | **triggered_by**
46 | 
47 | *Default: ['composer.json', 'composer.lock', '\*.php']*
48 | 
49 | This is a list of file names that should trigger this task.
50 | 


--------------------------------------------------------------------------------
/doc/tasks/composer_script.md:
--------------------------------------------------------------------------------
 1 | # Composer script
 2 | 
 3 | The Composer script task will run your configured Composer script.
 4 | It lives under the `composer_script` namespace and has following configurable parameters:
 5 | 
 6 | ```yaml
 7 | # grumphp.yml
 8 | grumphp:
 9 |     tasks:
10 |         composer_script:
11 |             script: ~
12 |             triggered_by: [php, phtml]
13 |             working_directory: ~
14 | ```
15 | 
16 | **script**
17 | 
18 | *Default: null*
19 | 
20 | This option specifies which Composer script you want to run.
21 | This option is set to null by default.
22 | This means that grumphp will stop immediately.
23 | Note that this script should be used to verify things.
24 | It is also possible to alter code during commit,
25 | but this is surely NOT recommended!
26 | 
27 | 
28 | **triggered_by**
29 | 
30 | *Default: [php, phtml]*
31 | 
32 | This option will specify which file extensions will trigger the Composer script.
33 | By default, Composer script will be triggered by altering any file.
34 | You can overwrite this option to whatever file you want to use!
35 | 
36 | 
37 | **working_directory**
38 | 
39 | *Default: null*
40 | 
41 | This option specifies in which directory the Composer script should be run.
42 | 


--------------------------------------------------------------------------------
/doc/tasks/deptrac.md:
--------------------------------------------------------------------------------
 1 | # Deptrac
 2 | 
 3 | Follow the [installation instructions](https://qossmic.github.io/deptrac/#installation) to add deptrac to your 
 4 | project.
 5 | 
 6 | The Deptrac task will check for dependencies between the software layers of your project. It lives under the `deptrac` 
 7 | namespace and has following configurable parameters:
 8 | 
 9 | 
10 | ```yaml
11 | # grumphp.yml
12 | grumphp:
13 |     tasks:
14 |         deptrac:
15 |             cache_file: ~
16 |             depfile: ~
17 |             formatter: ~
18 |             output: ~
19 | ```
20 | 
21 | **cache_file**
22 | 
23 | *Default: null*
24 | 
25 | Set location where cache file will be stored. Example: `/var/www/src/.deptrac.cache`
26 | 
27 | **depfile**
28 | 
29 | *Default: null*
30 | 
31 | Set path to deptrac configuration file. Example: `/var/www/src/deptrac.yaml`
32 | 
33 | **formatter**
34 | 
35 | *Default: null*
36 | 
37 | Enable formatter with this option, e.g. `console`, `github-actions`, `graphviz-display`, `graphviz-image`, `graphviz-dot`, `graphviz-html`, `junit`, `table`, `xml`, `baseline`, `json`.
38 | 
39 | **output**
40 | 
41 | *Default: null*
42 | 
43 | Set output file path for formatter (if applicable).
44 | 


--------------------------------------------------------------------------------
/doc/tasks/doctrine_orm.md:
--------------------------------------------------------------------------------
 1 | # Doctrine ORM
 2 | 
 3 | The Doctrine ORM task will validate that your Doctrine mapping files and check if the mapping is in sync with the database.
 4 | It lives under the `doctrine_orm` namespace and has following configurable parameters:
 5 | 
 6 | ```yaml
 7 | # grumphp.yml
 8 | grumphp:
 9 |     tasks:
10 |         doctrine_orm:
11 |             skip_mapping: false
12 |             skip_sync: false
13 |             skip_property_types: false
14 |             triggered_by: ['php', 'xml', 'yml']
15 | ```
16 | 
17 | **skip_mapping**
18 | 
19 | *Default: false*
20 | 
21 | With this parameter you can skip the mapping validation check.
22 | 
23 | **skip_sync**
24 | 
25 | *Default: false*
26 | 
27 | With this parameter you can skip checking if the mapping is in sync with the database.
28 | 
29 | **skip_property_types**
30 | 
31 | *Default: false*
32 | 
33 | With this parameter you can skip checking if Entity field property types match the Doctrine types.
34 | 
35 | **triggered_by**
36 | 
37 | *Default: [php, xml, yml]*
38 | 
39 | This is a list of extensions that should trigger the Doctrine task.
40 | 


--------------------------------------------------------------------------------
/doc/tasks/file_size.md:
--------------------------------------------------------------------------------
 1 | # File size
 2 | 
 3 | The file size task ensures a maximum size for a file to be added to git.
 4 | 
 5 | ```yaml
 6 | # grumphp.yml
 7 | grumphp:
 8 |     tasks:
 9 |         file_size:
10 |             max_size: 10M
11 |             ignore_patterns: []
12 | ```
13 | 
14 | **max_size**
15 | 
16 | *Default: 10M*
17 | 
18 | Defines the maximum size. The target value may use magnitudes of kilobytes (k, ki),
19 | megabytes (m, mi), or gigabytes (g, gi). Those suffixed with an i use the appropriate 2**n version
20 | in accordance with the IEC standard.
21 | 
22 | 
23 | **ignore_patterns**
24 | 
25 | *Default: []*
26 | 
27 | This is a list of patterns that will be ignored. With this option you can skip files.
28 | Leave this option blank to run analysis for all files.
29 | 


--------------------------------------------------------------------------------
/doc/tasks/gherkin.md:
--------------------------------------------------------------------------------
 1 | # Gherkin
 2 | 
 3 | The Gherkin task will lint your Gherkin feature files.
 4 | It lives under the `gherkin` namespace and has following configurable parameters:
 5 | 
 6 | ```yaml
 7 | # grumphp.yml
 8 | grumphp:
 9 |     tasks:
10 |         gherkin:
11 |             directory: 'features'
12 |             align: ~
13 | ```
14 | 
15 | **directory**
16 | 
17 | *Default: 'features'*
18 | 
19 | This option will specify the location of your Gherkin feature files.
20 | By default, the Behat preferred `features` folder is chosen.
21 | 
22 | **align**
23 | 
24 | *Default: null*
25 | 
26 | This option will specify the alignment of your file.
27 | Possible values can be `left` or `right`.
28 | 


--------------------------------------------------------------------------------
/doc/tasks/git_branch_name.md:
--------------------------------------------------------------------------------
 1 | # Git branch name
 2 | 
 3 | The Git branch name task ensures that the current branch name matches the specified patterns.
 4 | For this task to succeed, **any** whitelist patterns and **none** of the blacklist patterns have to
 5 | match the branch name. For example: if you are working with JIRA, it is possible to add a
 6 | pattern for the JIRA issue number.
 7 |  
 8 | ```yaml
 9 | # grumphp.yml
10 | grumphp:
11 |     tasks:
12 |         git_branch_name:
13 |             whitelist:
14 |                 - "/JIRA-\d+/"
15 |             blacklist:
16 |                 - "develop"
17 |                 - "master"
18 |             additional_modifiers: ''
19 |             allow_detached_head: true
20 | ```
21 | 
22 | 
23 | **whitelist**
24 | 
25 | *Default: []*
26 | 
27 | Use this parameter to specify one or multiple patterns. The value can be in regex or glob style.
28 | Here are some example matchers:
29 | 
30 | - /JIRA-([0-9]*)/
31 | - pre-fix*
32 | - *suffix
33 | 
34 | **blacklist**
35 | 
36 | *Default: []*
37 | 
38 | Use this parameter to specify one or multiple patterns. The value can be in regex or glob style.
39 | Here are some example matchers:
40 | 
41 | - /JIRA-([0-9]*)/
42 | - pre-fix*
43 | - *suffix
44 | 
45 | 
46 | **additional_modifiers**
47 | 
48 | *Default: ''*
49 | 
50 | Add one or multiple additional modifiers like:
51 | 
52 | ```yaml
53 | additional_modifiers: 'u'
54 | 
55 | # or
56 | 
57 | additional_modifiers: 'xu'
58 | ```
59 | 
60 | 
61 | **allow_detached_head**
62 | 
63 | *Default: true*
64 | 
65 | Set this to `false` if you wish the task to fail when ran on a detached HEAD. If set to `true` the
66 | task will always pass.
67 | 


--------------------------------------------------------------------------------
/doc/tasks/grunt.md:
--------------------------------------------------------------------------------
 1 | # Grunt
 2 | 
 3 | The Grunt task will run your automated frontend tasks.
 4 | It lives under the `grunt` namespace and has following configurable parameters:
 5 | 
 6 | ```yaml
 7 | # grumphp.yml
 8 | grumphp:
 9 |     tasks:
10 |         grunt:
11 |             grunt_file: ~
12 |             task: ~
13 |             triggered_by: [js, jsx, coffee, ts, less, sass, scss]
14 | ```
15 | 
16 | **grunt_file**
17 | 
18 | *Default: null*
19 | 
20 | If your Gruntfile.js file is located at an exotic location, you can specify your custom grunt file location with this option.
21 | This option is set to `null` by default.
22 | This means that `Gruntfile.js` is automatically loaded if the file exists in the current directory.
23 | 
24 | 
25 | **task**
26 | 
27 | *Default: null*
28 | 
29 | This option specifies which Grunt task you want to run.
30 | This option is set to `null` by default.
31 | This means that grunt will run the `default` task.
32 | Note that this task should be used to verify things. 
33 | It is also possible to alter code during commit, but this is surely **NOT** recommended!
34 | 
35 | 
36 | **triggered_by**
37 | 
38 | *Default: [js, jsx, coffee, ts, less, sass, scss]*
39 | 
40 | This option will specify which file extensions will trigger the grunt task.
41 | By default, Grunt will be triggered by altering a front-end file. 
42 | You can overwrite this option to whatever file you want to use!
43 | 


--------------------------------------------------------------------------------
/doc/tasks/gulp.md:
--------------------------------------------------------------------------------
 1 | # Gulp
 2 | 
 3 | The Gulp task will run your automated frontend tasks.
 4 | It lives under the `gulp` namespace and has following configurable parameters:
 5 | 
 6 | ```yaml
 7 | # grumphp.yml
 8 | grumphp:
 9 |     tasks:
10 |         gulp:
11 |             gulp_file: ~
12 |             task: ~
13 |             triggered_by: [js, jsx, coffee, ts, less, sass, scss]
14 | ```
15 | 
16 | **gulp_file**
17 | 
18 | *Default: null*
19 | 
20 | If your Gulpfile.js file is located at an exotic location, you can specify your custom gulp file location with this option.
21 | This option is set to `null` by default.
22 | This means that `gulpfile.js` is automatically loaded if the file exists in the current directory.
23 | 
24 | 
25 | **task**
26 | 
27 | *Default: null*
28 | 
29 | This option specifies which Gulp task you want to run.
30 | This option is set to `null` by default.
31 | This means that gulp will run the `default` task.
32 | Note that this task should be used to verify things. 
33 | It is also possible to alter code during commit, but this is surely **NOT** recommended!
34 | 
35 | 
36 | **triggered_by**
37 | 
38 | *Default: [js, jsx, coffee, ts, less, sass, scss]*
39 | 
40 | This option will specify which file extensions will trigger the gulp task.
41 | By default, Gulp will be triggered by altering a front-end file. 
42 | You can overwrite this option to whatever file you want to use!
43 | 


--------------------------------------------------------------------------------
/doc/tasks/jsonlint.md:
--------------------------------------------------------------------------------
 1 | # JsonLint
 2 | 
 3 | The JsonLint task will lint all your json files.
 4 | It lives under the `jsonlint` namespace and has following configurable parameters:
 5 | 
 6 | ```yaml
 7 | # grumphp.yml
 8 | grumphp:
 9 |     tasks:
10 |         jsonlint:
11 |             ignore_patterns: []
12 |             detect_key_conflicts: false
13 | ```
14 | 
15 | **ignore_patterns**
16 | 
17 | *Default: []*
18 | 
19 | This is a list of patterns that will be ignored by the linter. 
20 | With this option you can skip files like test fixtures. Leave this option blank to run the linter for every json file.
21 | 
22 | 
23 | **detect_key_conflicts**
24 | 
25 | *Default: false*
26 | 
27 | This option will throw exceptions when duplicate keys are detected in the json file.
28 | 


--------------------------------------------------------------------------------
/doc/tasks/make.md:
--------------------------------------------------------------------------------
 1 | # Make
 2 | 
 3 | The Make task will run your automated make tasks.
 4 | It lives under the `make` namespace and has following configurable parameters:
 5 | 
 6 | ```yaml
 7 | # grumphp.yml
 8 | grumphp:
 9 |     tasks:
10 |         make:
11 |             make_file: ~
12 |             task: ~
13 |             triggered_by: [php]
14 | ```
15 | 
16 | **make_file**
17 | 
18 | *Default: null*
19 | 
20 | If your Makefile file is located at an exotic location, you can specify your custom make file location with this option.
21 | This option is set to `null` by default.
22 | This means that `Makefile` is automatically loaded if the file exists in the current directory.
23 | 
24 | 
25 | **task**
26 | 
27 | *Default: null*
28 | 
29 | This option specifies which Make task you want to run.
30 | This option is set to `null` by default.
31 | This means that make will run the `default` task.
32 | Note that this task should be used to verify things. 
33 | It is also possible to alter code during commit, but this is surely **NOT** recommended!
34 | 
35 | 
36 | **triggered_by**
37 | 
38 | *Default: [php]*
39 | 
40 | This option will specify which file extensions will trigger the make task.
41 | By default, Make will be triggered by altering a PHP file. 
42 | You can overwrite this option to whatever file you want to use!
43 | 


--------------------------------------------------------------------------------
/doc/tasks/npm_script.md:
--------------------------------------------------------------------------------
 1 | # NPM script
 2 | 
 3 | The NPM script task will run your configured npm script.
 4 | It lives under the `npm_script` namespace and has following configurable parameters:
 5 | 
 6 | ```yaml
 7 | # grumphp.yml
 8 | grumphp:
 9 |     tasks:
10 |         npm_script:
11 |             script: ~
12 |             triggered_by: [js, jsx, coffee, ts, less, sass, scss]
13 |             working_directory: "./"
14 |             is_run_task: false
15 |             silent: false
16 | ```
17 | 
18 | **script**
19 | 
20 | *Default: null*
21 | 
22 | This option specifies which NPM script you want to run.
23 | This option is set to null by default.
24 | This means that grumphp will stop immediately.
25 | Note that this script should be used to verify things.
26 | It is also possible to alter code during commit,
27 | but this is surely NOT recommended!
28 | 
29 | 
30 | **triggered_by**
31 | 
32 | *Default: [js, jsx, coffee, ts, less, sass, scss]*
33 | 
34 | This option will specify which file extensions will trigger the NPM script.
35 | By default, NPM script will be triggered by altering any file.
36 | You can overwrite this option to whatever file you want to use!
37 | 
38 | 
39 | **working_directory**
40 | 
41 | *Default: "./"*
42 | 
43 | This option specifies in which directory the NPM script should be run.
44 | 
45 | **is_run_task**
46 | 
47 | *Default: false*
48 | 
49 | This option will append 'run' to the npm command to make it possible to run custom npm scripts.
50 | 
51 | **silent**
52 | 
53 | *Default: false*
54 | 
55 | This option will append '--silent' to the npm script to suppress `npm ERR!` messages from showing.
56 | 


--------------------------------------------------------------------------------
/doc/tasks/pest.md:
--------------------------------------------------------------------------------
 1 | # Pest
 2 | 
 3 | The Pest task will run your unit tests.
 4 | 
 5 | ***Composer***
 6 | 
 7 | ```
 8 | composer require --dev pestphp/pest
 9 | ```
10 | 
11 | ***Config***
12 | 
13 | The task lives under the `pest` namespace and has following configurable parameters:
14 | 
15 | ```yaml
16 | # grumphp.yml
17 | grumphp:
18 |     tasks:
19 |         pest:
20 |             config_file: ~
21 |             testsuite: ~
22 |             group: []
23 |             always_execute: false
24 | ```
25 | 
26 | **config_file**
27 | 
28 | *Default: null*
29 | 
30 | If your phpunit.xml file is located at an exotic location, you can specify your custom config file location with this option.
31 | This option is set to `null` by default.
32 | This means that `phpunit.xml` or `phpunit.xml.dist` are automatically loaded if one of them exist in the current directory.
33 | 
34 | 
35 | **testsuite**
36 | 
37 | *Default: null*
38 | 
39 | If you wish to only run tests from a certain Suite.
40 | `testsuite: unit`
41 | 
42 | 
43 | **group**
44 | 
45 | *Default: array()*
46 | 
47 | If you wish to only run tests from a certain Group.
48 | `group: [fast,quick,small]`
49 | 
50 | 
51 | **always_execute**
52 | 
53 | *Default: false*
54 | 
55 | Always run the whole test suite, even if no PHP files were changed.
56 | 


--------------------------------------------------------------------------------
/doc/tasks/phan.md:
--------------------------------------------------------------------------------
 1 | # Phan
 2 | 
 3 | The Phan task will run your automated PHP tasks.
 4 | 
 5 | ***Composer***
 6 | 
 7 | ```
 8 | composer require --dev phan/phan
 9 | ```
10 | 
11 | ***Config***
12 | 
13 | The task lives under the `phan` namespace and has following configurable parameters.
14 | 
15 | ```yaml
16 | # grumphp.yml
17 | grumphp:
18 |     tasks:
19 |         phan:
20 |             config_file: .phan/config.php
21 |             output_mode: text
22 |             output: null
23 |             triggered_by: [php]
24 | ```
25 | 
26 | **config_file**
27 | 
28 | *Default: .phan/config.php*
29 | 
30 | If your config.php file is located at an exotic location, you can specify your custom build file location with this option.
31 | This option is set to `.phan/config.php` by default.
32 | This means that `.phan/config.php` is automatically loaded if the file exists in the current directory.
33 | 
34 | 
35 | **output_mode**
36 | 
37 | *Default: text*
38 | 
39 | This option sets the output mode. Valid output modes are 'text', 'json', 'csv', 'codeclimate', 'checkstyle', or 'pylint'.
40 | This option is set to `text` by default.
41 | 
42 | **output**
43 | 
44 | *Default: null*
45 | 
46 | It's possible to save the output to a file, you can specify the file name with this option.
47 | This option is set to `null` by default.
48 | 
49 | **triggered_by**
50 | 
51 | *Default: [php]*
52 | 
53 | This option will specify which file extensions will trigger the phan task.
54 | By default, Phan will be triggered by altering a PHP file.
55 | You can overwrite this option to whatever file you want to use!
56 | 


--------------------------------------------------------------------------------
/doc/tasks/phing.md:
--------------------------------------------------------------------------------
 1 | # Phing
 2 | 
 3 | The Phing task will run your automated PHP tasks.
 4 | 
 5 | ***Composer***
 6 | 
 7 | ```
 8 | composer require --dev phing/phing
 9 | ```
10 | 
11 | ***Config***
12 | 
13 | The task lives under the `phing` namespace and has following configurable parameters:
14 | 
15 | ```yaml
16 | # grumphp.yml
17 | grumphp:
18 |     tasks:
19 |         phing:
20 |             build_file: ~
21 |             task: ~
22 |             triggered_by: [php]
23 | ```
24 | 
25 | **build_file**
26 | 
27 | *Default: null*
28 | 
29 | If your build.xml file is located at an exotic location, you can specify your custom build file location with this option.
30 | This option is set to `null` by default.
31 | This means that `build.xml` is automatically loaded if the file exists in the current directory.
32 | 
33 | 
34 | **task**
35 | 
36 | *Default: null*
37 | 
38 | This option specifies which Phing task you want to run.
39 | This option is set to `null` by default.
40 | This means that phing will run the `default` task.
41 | Note that this task should be used to verify things. 
42 | It is also possible to alter code during commit, but this is surely **NOT** recommended!
43 | 
44 | 
45 | **triggered_by**
46 | 
47 | *Default: [php]*
48 | 
49 | This option will specify which file extensions will trigger the phing task.
50 | By default, Phing will be triggered by altering a PHP file. 
51 | You can overwrite this option to whatever file you want to use!
52 | 


--------------------------------------------------------------------------------
/doc/tasks/php7cc.md:
--------------------------------------------------------------------------------
 1 | # Php7cc
 2 | 
 3 | The Php7cc task will check PHP 5.3 - 5.6 code compatibility with PHP 7.
 4 | 
 5 | ***Composer***
 6 | 
 7 | ```
 8 | composer require --dev sstalle/php7cc
 9 | ```
10 | 
11 | ***Config***
12 | 
13 | The task lives under the `php7cc` namespace and has following configurable parameters:
14 | 
15 | ```yaml
16 | # grumphp.yml
17 | grumphp:
18 |     tasks:
19 |         php7cc:
20 |             exclude: []
21 |             level: ~
22 |             triggered_by: ['php']
23 | ```
24 | 
25 | **exclude**
26 | 
27 | *Default: []*
28 | 
29 | This is a list of directories to be excluded
30 | 
31 | **level**
32 | 
33 | *Default: null*
34 | 
35 | Minimum issue level. There are 3 issue levels: "info", "warning" and "error". "info" is reserved for future use and is the same as "warning".
36 | 
37 | **triggered_by**
38 | 
39 | *Default: [php]*
40 | 
41 | This is a list of extensions to be sniffed.
42 | 
43 | 
44 | ## Known issues
45 | 
46 | - Since this task is using an old version of phpparser, it currently cannot be used in combination with the `phpparser` task. 
47 | [Click here for more information](https://github.com/sstalle/php7cc/issues/79)
48 | 


--------------------------------------------------------------------------------
/doc/tasks/phparkitect.md:
--------------------------------------------------------------------------------
 1 | # PHPArkitect
 2 | 
 3 | PHPArkitect helps you to keep your PHP codebase coherent and solid, by permitting to add some architectural constraint check to your workflow.
 4 | It lives under the `phparkitect` namespace and has following configurable parameters:
 5 | 
 6 | PhpArkitect doesn't support checking only the changed files.
 7 | It will always run on the directory specified in your config file.
 8 | 
 9 | ## Composer
10 | ```bash
11 | composer require --dev phparkitect/phparkitect
12 | ```
13 | 
14 | ## Config
15 | ```yaml
16 | # grumphp.yml
17 | grumphp:
18 |     tasks:
19 |         phparkitect:
20 |             config: ~
21 |             target_php_version: ~
22 |             stop_on_failure: ~
23 | ```
24 | 
25 | **config**
26 | 
27 | *Default: null*
28 | 
29 | With this parameter you can specify the path your project's configuration file.
30 | By default PHPArkitect will search all rules in phparkitect.php located in the root of your project.
31 | 
32 | **target_php_version**
33 | 
34 | *Default: null*
35 | 
36 | With this parameter, you can specify which PHP version should use the parser.
37 | This can be useful to debug problems and to understand if there are problems with a different PHP version.
38 | 
39 | **stop_on_failure**
40 | 
41 | *Default: false*
42 | 
43 | With this option the process will end immediately after the first violation.
44 | 


--------------------------------------------------------------------------------
/doc/tasks/phpcpd.md:
--------------------------------------------------------------------------------
 1 | # PhpCpd
 2 | 
 3 | The PhpCpd task will sniff your code for duplicated lines.
 4 | 
 5 | ***Composer***
 6 | 
 7 | ```
 8 | composer require --dev sebastian/phpcpd
 9 | ```
10 | 
11 | ***Config***
12 | 
13 | The task lives under the `phpcpd` namespace and has following configurable parameters:
14 | 
15 | ```yaml
16 | # grumphp.yml
17 | grumphp:
18 |     tasks:
19 |         phpcpd:
20 |             directory: ['.']
21 |             exclude: ['vendor']
22 |             fuzzy: false
23 |             min_lines: 5
24 |             min_tokens: 70
25 |             triggered_by: ['php']
26 | ```
27 | 
28 | **directory**
29 | 
30 | *Default: [.]*
31 | 
32 | With this parameter you can define which directories you want to run `phpcpd` in (must be relative to cwd).
33 | 
34 | **exclude**
35 | 
36 | *Default: [vendor]*
37 | 
38 | With this parameter you will be able to exclude one or multiple directories from code analysis (must be relative to `directory`).
39 | 
40 | **fuzzy**
41 | 
42 | *Default: false*
43 | 
44 | With this parameter you will be able to fuzz variable names.
45 | 
46 | **min_lines**
47 | 
48 | *Default: 5*
49 | 
50 | With this parameter you will be able to set minimum number of identical lines.
51 | 
52 | **min_tokens**
53 | 
54 | *Default: 70*
55 | 
56 | With this parameter you will be able to set minimum number of identical tokens.
57 | 
58 | **triggered_by**
59 | 
60 | *Default: [php]*
61 | 
62 | This is a list of extensions to be sniffed.
63 | 


--------------------------------------------------------------------------------
/doc/tasks/phplint.md:
--------------------------------------------------------------------------------
 1 | # PHPLint
 2 | 
 3 | The PHPLint task will check your source files for syntax errors.
 4 | 
 5 | ***Composer***
 6 | 
 7 | ```
 8 | composer require --dev php-parallel-lint/php-parallel-lint
 9 | ```
10 | 
11 | ***Config***
12 | 
13 | ```yaml
14 | # grumphp.yml
15 | grumphp:
16 |     tasks:
17 |         phplint:
18 |             exclude: []
19 |             jobs: ~
20 |             short_open_tag: false
21 |             ignore_patterns: []
22 |             triggered_by: ['php', 'phtml', 'php3', 'php4', 'php5']
23 | ```
24 | **exclude**
25 | 
26 | *Default: []*
27 | 
28 | Any directories to be excluded from linting. You can specify which
29 | directories you wish to exclude, such as the vendor directory.
30 | 
31 | **jobs**
32 | 
33 | *Default: null*
34 | 
35 | The number of jobs you wish to use for parallel processing. If no number
36 | is given, it is left up to parallel-lint itself, which currently
37 | defaults to 10.
38 | 
39 | **short_open_tag**
40 | 
41 | *Default: false*
42 | 
43 | This option can allow PHP short open tags. 
44 | 
45 | **ignore_patterns**
46 | 
47 | *Default: []*
48 | 
49 | This is a list of patterns that will be ignored by PHPLint. Leave this option blank to run PHPLint for every php file.
50 | 
51 | **triggered_by**
52 | 
53 | *Default: ['php', 'phtml', 'php3', 'php4', 'php5']*
54 | 
55 | Any file extensions that you wish to be passed to the linter.
56 | 


--------------------------------------------------------------------------------
/doc/tasks/phpspec.md:
--------------------------------------------------------------------------------
 1 | # Phpspec
 2 | 
 3 | The Phpspec task will spec your code with Phpspec.
 4 | 
 5 | ***Composer***
 6 | 
 7 | ```
 8 | composer require --dev phpspec/phpspec
 9 | ```
10 | 
11 | ***Config***
12 | 
13 | The task lives under the `phpspec` namespace and has following configurable parameters:
14 | 
15 | ```yaml
16 | # grumphp.yml
17 | grumphp:
18 |     tasks:
19 |         phpspec:
20 |             config_file: ~
21 |             format: ~
22 |             stop_on_failure: false
23 |             verbose: false
24 | ```
25 | 
26 | **config_file**
27 | 
28 | *Default: null*
29 | 
30 | If your phpspec.yml file is located at an exotic location, you can specify your custom config file location with this option.
31 | 
32 | 
33 | **format**
34 | 
35 | *Default: null*
36 | 
37 | You can overwrite the default `progress` format or the one specified in the `phpspec.yml` config file by configuring this option.
38 | 
39 | [A list of all formatters](http://www.phpspec.net/en/stable/cookbook/configuration.html#formatter)
40 | 
41 | 
42 | **stop_on_failure**
43 | 
44 | *Default: false*
45 | 
46 | When this option is enabled, phpspec will stop at the first error. This means that it will not run your full test suite when an error occurs.
47 | 
48 | 
49 | **verbose**
50 | 
51 | *Default: false*
52 | 
53 | When this option is enabled, phpspec will display a verbose error message about the failed example. This way, it is easier to debug what went wrong in the specs.
54 | 


--------------------------------------------------------------------------------
/doc/tasks/phpunitbridge.md:
--------------------------------------------------------------------------------
 1 | # Phpunit bridge
 2 | 
 3 | The Phpunit Bridge task will run your unit tests thanks to the Symfony Phpunit Bridge.
 4 | 
 5 | ***Composer***
 6 | 
 7 | ```
 8 | composer require --dev symfony/phpunit-bridge
 9 | ```
10 | 
11 | ***Config***
12 | 
13 | The task lives under the `phpunitbridge` namespace and has following configurable parameters:
14 | 
15 | ```yaml
16 | # grumphp.yml
17 | grumphp:
18 |     tasks:
19 |         phpunitbridge:
20 |             config_file: ~
21 |             testsuite: ~
22 |             group: []
23 |             exclude_group: []
24 |             always_execute: false
25 |             order: null
26 | ```
27 | 
28 | **config_file**
29 | 
30 | *Default: null*
31 | 
32 | If your phpunit.xml file is located at an exotic location, you can specify your custom config file location with this option.
33 | This option is set to `null` by default.
34 | This means that `phpunit.xml` or `phpunit.xml.dist` are automatically loaded if one of them exist in the current directory.
35 | 
36 | 
37 | **testsuite**
38 | 
39 | *Default: null*
40 | 
41 | If you wish to only run tests from a certain Suite.
42 | `testsuite: unit`
43 | 
44 | 
45 | **group**
46 | 
47 | *Default: array()*
48 | 
49 | If you wish to only run tests from a certain Group.
50 | `group: ['fast','quick','small']`
51 | 
52 | 
53 | **exclude_group**
54 | 
55 | *Default: array()*
56 | 
57 | If you wish to run tests excluding a certain Group.
58 | `group: ['big','risky']`
59 | 
60 | 
61 | **always_execute**
62 | 
63 | *Default: false*
64 | 
65 | Always run the whole test suite, even if no PHP files were changed.
66 | 
67 | **order**
68 | 
69 | *Default: null*
70 | 
71 | If you wish to run tests in a specific order. `order: [default,defects,duration,no-depends,random,reverse,size]`
72 | 


--------------------------------------------------------------------------------
/doc/tasks/phpversion.md:
--------------------------------------------------------------------------------
 1 | # PhpVersion
 2 | 
 3 | The Phpversion task will check if your current php version is still supported.
 4 | The date of the php version that is checked, is the end of the security updates that can be found [here](https://secure.php.net/supported-versions.php).
 5 | It lives under the `phpversion` namespace and has following configurable parameters:
 6 | 
 7 | ```yaml
 8 | # grumphp.yml
 9 | grumphp:
10 |     tasks:
11 |         phpversion:
12 |             project: '7.2'
13 | ```
14 | 
15 | **project**
16 | 
17 | *Default: null*
18 | 
19 | Manually set a minimum version for your project.
20 | 


--------------------------------------------------------------------------------
/doc/tasks/progpilot.md:
--------------------------------------------------------------------------------
 1 | # Progpilot
 2 | 
 3 | The Progpilot task will run your automated PHP tasks.
 4 | 
 5 | ***Composer***
 6 | 
 7 | ```
 8 | composer config minimum-stability dev
 9 | composer require --dev designsecurity/progpilot:dev-master
10 | ```
11 | 
12 | ***Config***
13 | 
14 | The task lives under the `progpilot` namespace and has following configurable parameters.
15 | 
16 | ```yaml
17 | # grumphp.yml
18 | grumphp:
19 |     tasks:
20 |         progpilot:
21 |             config_file: .progpilot/configuration.yml
22 |             triggered_by: [php]
23 | ```
24 | 
25 | **config_file**
26 | 
27 | *Default: configuration.yml*
28 | 
29 | You can specify your custom configuration file location with this option.
30 | By default `.progpilot/configuration.yml` is automatically loaded if the file exists in the current directory.
31 | If not or yml file format is invalid default configuration of progpilot will be used.
32 | 
33 | 
34 | **triggered_by**
35 | 
36 | *Default: [php]*
37 | 
38 | This option will specify which file extensions will trigger the Progpilot task.
39 | By default, Progpilot will be triggered by altering a PHP file.
40 |  
41 | 


--------------------------------------------------------------------------------
/doc/tasks/psalm.md:
--------------------------------------------------------------------------------
 1 | # Psalm
 2 | 
 3 | Psalm is a static analysis tool for finding errors in PHP applications, built on top of PHP Parser.
 4 | It lives under the `psalm` namespace and has following configurable parameters:
 5 | 
 6 | ## Composer
 7 | ```bash
 8 | composer require --dev vimeo/psalm
 9 | ```
10 | 
11 | If you'd like to use the Phar version
12 | ```bash
13 | composer require --dev psalm/phar
14 | ```
15 | 
16 | ## Config
17 | ```yaml
18 | # grumphp.yml
19 | grumphp:
20 |     tasks:
21 |         psalm:
22 |             config: psalm.xml
23 |             ignore_patterns: []
24 |             no_cache: false
25 |             report: ~
26 |             output_format: null
27 |             threads: 1
28 |             triggered_by: ['php']
29 |             show_info: false
30 | ```
31 | 
32 | 
33 | **config**
34 | 
35 | *Default: null*
36 | 
37 | With this parameter you can specify the path your project's configuration file.
38 | 
39 | 
40 | **ignore_patterns**
41 | 
42 | *Default: []*
43 | 
44 | This is a list of patterns that will be ignored by psalm. With this option you can skip files like
45 | tests. Leave this option blank to run psalm for every php file/directory specified in your
46 | configuration.
47 | 
48 | 
49 | **no_cache**
50 | 
51 | *Default: false*
52 | 
53 | With this parameter you can run Psalm without using the cache file.
54 | 
55 | 
56 | **report**
57 | 
58 | *Default: null*
59 | 
60 | With this path you can specify the path your psalm report file 
61 | 
62 | 
63 | **output_format**
64 | 
65 | *Default: null*
66 | 
67 | Changes the output format.
68 | Available formats: compact, console, emacs, json, pylint, xml, checkstyle, junit, sonarqube
69 | 
70 | **threads**
71 | 
72 | *Default: null*
73 | 
74 | This parameter defines on how many threads Psalm's analysis stage is ran.
75 | 
76 | 
77 | **triggered_by**
78 | 
79 | *Default: [php]*
80 | 
81 | This is a list of extensions to be sniffed.
82 | 
83 | **show_info**
84 | 
85 | *Default: false*
86 | 
87 | Show non-exception parser findings
88 | 


--------------------------------------------------------------------------------
/doc/tasks/rector.md:
--------------------------------------------------------------------------------
 1 | # Rector
 2 | 
 3 | Rector is a tool to instantly upgrade and automatically refactor your PHP 5.3+ code.
 4 | It lives under the `rector` namespace and has following configurable parameters:
 5 | 
 6 | ## Composer
 7 | ```bash
 8 | composer require --dev rector/rector
 9 | ```
10 | 
11 | ## Config
12 | ```yaml
13 | # grumphp.yml
14 | grumphp:
15 |     tasks:
16 |         rector:
17 |             config: null
18 |             triggered_by: ['php']
19 |             ignore_patterns: []
20 |             clear_cache: true
21 |             no_diffs: false
22 | ```
23 | 
24 | **config**
25 | 
26 | *Default: null*
27 | 
28 | With this parameter you can specify the path your project's configuration file. When 'null' rector will run with the default file: rector.php
29 | 
30 | **triggered_by**
31 | 
32 | *Default: [php]*
33 | 
34 | This is a list of extensions to be sniffed.
35 | 
36 | 
37 | **ignore_patterns**
38 | 
39 | *Default: []*
40 | 
41 | This is a list of patterns that will be ignored by Rector. With this option you can skip files like
42 | tests. Leave this option blank to run Rector for every php file/directory specified in your
43 | configuration.
44 | 
45 | 
46 | **clear_cache**
47 | 
48 | *Default: true*
49 | 
50 | With this parameter you can run Rector without using the cache.
51 | 
52 | **no_diffs**
53 | 
54 | *Default: false*
55 | 
56 | With this parameter you can run Rector without showing file diffs.
57 | 


--------------------------------------------------------------------------------
/doc/tasks/robo.md:
--------------------------------------------------------------------------------
 1 | # Robo
 2 | 
 3 | The Robo task will run your automated PHP tasks.
 4 | 
 5 | ***Composer***
 6 | 
 7 | ```
 8 | composer require --dev consolidation/robo
 9 | ```
10 | 
11 | ***Config***
12 | 
13 | The task lives under the `robo` namespace and has following configurable parameters:
14 | 
15 | ```yaml
16 | # grumphp.yml
17 | grumphp:
18 |     tasks:
19 |         robo:
20 |             load_from: ~
21 |             task: ~
22 |             triggered_by: [php]
23 | ```
24 | 
25 | **load_from**
26 | 
27 | *Default: null*
28 | 
29 | If your Robofile.php file is located at an exotic location, you can specify the path to your custom location with this option.
30 | This option is set to `null` by default.
31 | This means that `Robofile.php` is automatically loaded if the file exists in the current directory.
32 | 
33 | 
34 | **task**
35 | 
36 | *Default: null*
37 | 
38 | This option specifies which Robo task you want to run.
39 | This option is set to `null` by default.
40 | This means that robo will run the `default` task.
41 | Note that this task should be used to verify things. 
42 | It is also possible to alter code during commit, but this is surely **NOT** recommended!
43 | 
44 | 
45 | **triggered_by**
46 | 
47 | *Default: [php]*
48 | 
49 | This option will specify which file extensions will trigger the robo task.
50 | By default, Robo will be triggered by altering a PHP file. 
51 | You can overwrite this option to whatever file you want to use!
52 | 


--------------------------------------------------------------------------------
/doc/tasks/securitychecker.md:
--------------------------------------------------------------------------------
 1 | # Security Checker
 2 | 
 3 | The SensioLabs Security Checker API is abandoned
 4 | 
 5 | You can use one of following tasks as a replacement:
 6 | 
 7 | - [securitychecker_composeraudit](securitychecker/composeraudit.md)
 8 | - [securitychecker_enlightn](securitychecker/enlightn.md)
 9 | - [securitychecker_local](securitychecker/local.md)
10 | - [securitychecker_roave](securitychecker/roave.md)
11 | - [securitychecker_symfony](securitychecker/symfony.md)
12 | 


--------------------------------------------------------------------------------
/doc/tasks/securitychecker/composeraudit.md:
--------------------------------------------------------------------------------
 1 | # Composer Audit Security Checker
 2 | 
 3 | The Security Checker will check your `composer.lock` file for known security vulnerabilities.
 4 | 
 5 | ***Config***
 6 | 
 7 | The task lives under the `securitychecker_composeraudit` namespace and has the following configurable parameters:
 8 | 
 9 | ```yaml
10 | # grumphp.yml
11 | grumphp:
12 |     tasks:
13 |         securitychecker_composeraudit:
14 |             abandoned: null
15 |             format: null
16 |             locked: true
17 |             no_dev: false
18 |             run_always: false
19 |             working_dir: null
20 | ```
21 | 
22 | **abandoned**
23 | 
24 | *Default: null*
25 | 
26 | You can choose the behavior on abandoned packages. The available options are `ignore`, `report` and `fail`. By default, grumphp will use the `fail` behavior.
27 | 
28 | **format**
29 | 
30 | *Default: null*
31 | 
32 | You can choose the format of the output. The available options are `table`, `plain`, `json` and `summary`. By default, grumphp will use the format `table`.
33 | 
34 | **locked**
35 | 
36 | *Default: true*
37 | 
38 | Audit packages from the lock file, regardless of what is currently in vendor dir.
39 | 
40 | **no_dev**
41 | 
42 | *Default: false*
43 | 
44 | When this option is set to `true`, the task will skip packages under `require-dev`.
45 | 
46 | **run_always**
47 | 
48 | *Default: false*
49 | 
50 | When this option is set to `false`, the task will only run when the `composer.lock` file has changed. If it is set to `true`, the `composer.lock` file will be checked on every commit.
51 | 
52 | **working_dir**
53 | 
54 | *Default: null
55 | 
56 | If your `composer.lock` file is located in an exotic location, you can specify the location with this option. By default, the task will try to load a `composer.lock` file in the current directory.


--------------------------------------------------------------------------------
/doc/tasks/securitychecker/enlightn.md:
--------------------------------------------------------------------------------
 1 | # Enlightn Security Checker
 2 | 
 3 | The Security Checker will check your `composer.lock` file for known security vulnerabilities.
 4 | 
 5 | ***Composer***
 6 | 
 7 | ```
 8 | composer require --dev enlightn/security-checker
 9 | ```
10 | 
11 | ***Config***
12 | 
13 | The task lives under the `securitychecker_enlightn` namespace and has the following configurable parameters:
14 | 
15 | ```yaml
16 | # grumphp.yml
17 | grumphp:
18 |     tasks:
19 |         securitychecker_enlightn:
20 |             lockfile: ./composer.lock
21 |             run_always: false
22 |             allow_list:
23 |                 - CVE-2018-15133
24 |                 - CVE-2024-51755
25 |                 - CVE-2024-45411
26 | ```
27 | 
28 | **lockfile**
29 | 
30 | *Default: ./composer.lock*
31 | 
32 | If your `composer.lock` file is located in an exotic location, you can specify the location with this option. By default, the task will try to load a `composer.lock` file in the current directory.
33 | 
34 | **run_always**
35 | 
36 | *Default: false*
37 | 
38 | When this option is set to `false`, the task will only run when the `composer.lock` file has changed. If it is set to `true`, the `composer.lock` file will be checked on every commit.
39 | 
40 | **allow_list**
41 | 
42 | *Default: []*
43 | 
44 | This option allows you to specify a list of CVE identifiers that should be ignored during the security check. This is useful if you have assessed certain vulnerabilities and determined that they do not pose a risk to your project. The CVE identifiers should be provided as an array of strings. For example:
45 | 
46 | ```yaml
47 | allow_list:
48 |     - CVE-2018-15133
49 |     - CVE-2024-51755
50 |     - CVE-2024-45411
51 | ```
52 | 
53 | 


--------------------------------------------------------------------------------
/doc/tasks/securitychecker/local.md:
--------------------------------------------------------------------------------
 1 | # Local Security Checker
 2 | 
 3 | The Security Checker will check your `composer.lock` file for known security vulnerabilities.
 4 | 
 5 | ***Binary***
 6 | 
 7 | Download the latest binary from [fabpot/local-php-security-checker ](https://github.com/fabpot/local-php-security-checker/releases) and make sure it is part of your PATH or place it in one of the directories defined by environment.paths in your grumphp.yml file.
 8 | 
 9 | ***Config***
10 | 
11 | The task lives under the `securitychecker_local` namespace and has the following configurable parameters:
12 | 
13 | ```yaml
14 | # grumphp.yml
15 | grumphp:
16 |     tasks:
17 |         securitychecker_local:
18 |             lockfile: ./composer.lock
19 |             format: ~
20 |             run_always: false
21 |             no_dev: false
22 | ```
23 | 
24 | **lockfile**
25 | 
26 | *Default: ./composer.lock*
27 | 
28 | If your `composer.lock` file is located in an exotic location, you can specify the location with this option. By default, the task will try to load a `composer.lock` file in the current directory.
29 | 
30 | **format**
31 | 
32 | *Default: null*
33 | 
34 | You can choose the format of the output. The available options are `ansi`, `json`, `markdown` and `yaml`. By default, grumphp will use the format `ansi`.
35 | 
36 | **run_always**
37 | 
38 | *Default: false*
39 | 
40 | When this option is set to `false`, the task will only run when the `composer.lock` file has changed. If it is set to `true`, the `composer.lock` file will be checked on every commit.
41 | 
42 | **no_dev**
43 | 
44 | *Default: false*
45 | 
46 | When this option is set to `true`, the task will skip packages under `require-dev`.
47 | 


--------------------------------------------------------------------------------
/doc/tasks/securitychecker/roave.md:
--------------------------------------------------------------------------------
 1 | # Roave Security Checker
 2 | 
 3 | The Security Checker will check your `composer.lock` file for known security vulnerabilities.
 4 | 
 5 | ***Composer***
 6 | 
 7 | ```
 8 | composer require --dev roave/security-advisories:dev-latest
 9 | ```
10 | More information about the library can be found on [GitHub](https://github.com/Roave/SecurityAdvisories).
11 | 
12 | ***Config***
13 | 
14 | The task lives under the `securitychecker_roave` namespace and has the following configurable parameters:
15 | 
16 | ```yaml
17 | # grumphp.yml
18 | grumphp:
19 |     tasks:
20 |         securitychecker_roave:
21 |             jsonfile: ./composer.json
22 |             lockfile: ./composer.lock
23 |             run_always: false
24 | ```
25 | 
26 | **jsonfile**
27 | 
28 | *Default: ./composer.json*
29 | 
30 | If your `composer.json` file is located in an exotic location, you can specify the location with this option. By default, the task will try to load a `composer.json` file in the current directory.
31 | 
32 | **lockfile**
33 | 
34 | *Default: ./composer.lock*
35 | 
36 | If your `composer.lock` file is located in an exotic location, you can specify the location with this option. By default, the task will try to load a `composer.lock` file in the current directory.
37 | 
38 | **run_always**
39 | 
40 | *Default: false*
41 | 
42 | When this option is set to `false`, the task will only run when the `composer.lock` file has changed. If it is set to `true`, the `composer.lock` file will be checked on every commit.
43 | 


--------------------------------------------------------------------------------
/doc/tasks/securitychecker/symfony.md:
--------------------------------------------------------------------------------
 1 | # Symfony Security Checker
 2 | 
 3 | The Security Checker will check your `composer.lock` file for known security vulnerabilities.
 4 | 
 5 | ***Binary***
 6 | 
 7 | Download the latest binary from [Symfony CLI](https://symfony.com/download) and make sure it is part of your PATH or place it in one of the directories defined by environment.paths in your grumphp.yml file.
 8 | 
 9 | ***Config***
10 | 
11 | The task lives under the `securitychecker_symfony` namespace and has the following configurable parameters:
12 | 
13 | ```yaml
14 | # grumphp.yml
15 | grumphp:
16 |     tasks:
17 |         securitychecker_symfony:
18 |             lockfile: ./composer.lock
19 |             format: ~
20 |             run_always: false
21 | ```
22 | 
23 | **lockfile**
24 | 
25 | *Default: ./composer.lock*
26 | 
27 | If your `composer.lock` file is located in an exotic location, you can specify the location with this option. By default, the task will try to load a `composer.lock` file in the current directory.
28 | 
29 | **format**
30 | 
31 | *Default: null*
32 | 
33 | You can choose the format of the output. The available options are `ansi`, `json`, `markdown` and `yaml`. By default, grumphp will use the format `ansi`.
34 | 
35 | **run_always**
36 | 
37 | *Default: false*
38 | 
39 | When this option is set to `false`, the task will only run when the `composer.lock` file has changed. If it is set to `true`, the `composer.lock` file will be checked on every commit.
40 | 


--------------------------------------------------------------------------------
/doc/tasks/twigcs.md:
--------------------------------------------------------------------------------
 1 | #  TwigCs
 2 | 
 3 | Check Twig coding standard based on [FriendsOfTwig/TwigCs](https://github.com/FriendsOfTwig/TwigCs) .
 4 | 
 5 | ***Composer***
 6 | 
 7 | ```
 8 | composer require --dev "friendsoftwig/twigcs:>=4"
 9 | ```
10 | 
11 | ***Config***
12 | 
13 | The task lives under the `twigcs` namespace and has following configurable parameters:
14 | 
15 | ```yaml
16 | # grumphp.yml
17 | grumphp:
18 |     tasks:
19 |         twigcs:
20 |             path: '.'
21 |             severity: 'warning'
22 |             display: 'all'
23 |             ruleset: 'FriendsOfTwig\Twigcs\Ruleset\Official'
24 |             triggered_by: ['twig']
25 |             exclude: []
26 | ```
27 | 
28 | **path**
29 | 
30 | *Default: null*
31 | 
32 | By default `.` (current folder) will be used.
33 | On precommit the path will not be used, changed files will be passed as arguments instead.
34 | You can specify an alternate location by changing this option. If the path doesn't exist or is not accessible an exception will be thrown.
35 | 
36 | **severity**
37 | 
38 | *Default: 'warning'*
39 | 
40 | Severity level of sniffing (possibles values are : 'IGNORE', 'INFO', 'WARNING', 'ERROR').
41 | 
42 | **display**
43 | 
44 | *Default: 'all'*
45 | 
46 | The violations to display (possibles values are : 'all', 'blocking').
47 | 
48 | **ruleset**
49 | 
50 | *Default: 'FriendsOfTwig\Twigcs\Ruleset\Official'*
51 | 
52 | Ruleset used, default ruleset is based on [official one from twig](https://twig.symfony.com/doc/2.x/coding_standards.html)
53 | 
54 | **triggered_by**
55 | 
56 | *Default: [twig]*
57 | 
58 | This option will specify which file extensions will trigger this task.
59 | 
60 | **exclude**
61 | 
62 | *Default: []*
63 | 
64 | This option will specify which relative subfolders or files will be excluded from this task.
65 | 


--------------------------------------------------------------------------------
/doc/tasks/xmllint.md:
--------------------------------------------------------------------------------
 1 | # XmlLint
 2 | 
 3 | The XmlLint task will lint all your XML files.
 4 | It lives under the `xmllint` namespace and has following configurable parameters:
 5 | 
 6 | ```yaml
 7 | # grumphp.yml
 8 | grumphp:
 9 |     tasks:
10 |         xmllint:
11 |             ignore_patterns: []
12 |             load_from_net: false
13 |             x_include: false
14 |             dtd_validation: false
15 |             scheme_validation: false
16 |             triggered_by: ['xml']
17 | ```
18 | 
19 | **ignore_patterns**
20 | 
21 | *Default: []*
22 | 
23 | This is a list of patterns that will be ignored by the linter. 
24 | With this option you can skip files like test fixtures. Leave this option blank to run the linter for every xml file.
25 | 
26 | 
27 | **load_from_net**
28 | 
29 | *Default: false*
30 | 
31 | This option can be used to tell the linter if external files can be loaded from the net.
32 | When enabled all online DTD and XSD resources will be loaded and validated if required.
33 | You can speed up the validation a lot by disabling this option.
34 | 
35 | **x_include**
36 | 
37 | *Default: false*
38 | 
39 | By enabling this option, the xincluded resources you specified in the XMl are fetched. 
40 | After fetching the resources, all additional validations are run on the complete XML resource.
41 | 
42 | 
43 | **dtd_validation**
44 | 
45 | *Default: false*
46 | 
47 | It is possible to validate XML against the specified DTD. 
48 | Both internal, external as online resources are fetched and used for validation.
49 | 
50 | 
51 | **scheme_validation**
52 | 
53 | *Default: false*
54 | 
55 | It is possible to validate XML against the specified XSD schemes. 
56 | Both internal, external as online resources are fetched and used for validation.
57 | 
58 | **triggered_by**
59 | 
60 | *Default: [xml]*
61 | 
62 | This is a list of extensions to be sniffed. Extend it for including xsd, wsdl, and others.
63 | 


--------------------------------------------------------------------------------
/doc/testsuites.md:
--------------------------------------------------------------------------------
 1 | # Test Suites
 2 | Don't want to run all the tests during a commit?
 3 | Do you want to specify some pre-defined tasks you want to run?
 4 | It is easy to configure and run custom testsuites in GrumPHP
 5 | Test suites live under their own namespace in the parameters part.
 6 | 
 7 | 
 8 | ```yaml
 9 | # grumphp.yml
10 | grumphp:
11 |     testsuites:
12 |         suitename:
13 |             tasks:
14 |                 - phpcs
15 |                 - phpspec
16 | ```
17 | 
18 | It is possible to define multiple testsuites in the `grumphp.yml` file.
19 | Every test-suite has a unique name that can be used in the run command.
20 | A test-suite has the following parameters:
21 | 
22 | 
23 | **tasks**
24 | 
25 | *Default: []*
26 | 
27 | A test-suite consists of a list of tasks that should be executed.
28 | You can use any registered task name in the list of tasks.
29 | The list is validated against this list of registered tasks. 
30 | When you enter an unknown task, an error will be thrown.
31 | 
32 | 
33 | ## Overriding git hook test-suites
34 | To make it possible to define which tests should run during a git hook,
35 | we made it possible to use one of following pre-defined test suites:
36 | 
37 | ```yaml
38 | # grumphp.yml
39 | grumphp:
40 |     testsuites:
41 |         # Specify the test-suite for the git:commit-msg command:
42 |         git_commit_msg:
43 |             tasks: []
44 |         # Specify the test-suite for the git:pre-commit command:
45 |         git_pre_commit:
46 |             tasks: []
47 | ```
48 | 


--------------------------------------------------------------------------------
/grumphp.yml.dist:
--------------------------------------------------------------------------------
 1 | grumphp:
 2 |   process_timeout: 480
 3 |   tasks:
 4 |     phpcs:
 5 |       standard: PSR2
 6 |       ignore_patterns:
 7 |         - "spec/*Spec.php"
 8 |         - "test/*.php"
 9 |         - "stubs/*.php"
10 |     phpspec:
11 |       format: progress
12 |       verbose: true
13 |     phpunit:
14 |       testsuite: Unit
15 |     composer:
16 |       no_check_lock: true
17 |     composer_normalize:
18 |       use_standalone: true
19 |     yamllint:
20 |       parse_custom_tags: true
21 |       ignore_patterns:
22 |         - "#test/(.*).yml#"
23 |     phplint: ~
24 |     phpparser:
25 |       ignore_patterns:
26 |         - '#src/Event/Event.php#'
27 |         - '#test/Symfony/(.*)#'
28 |       visitors:
29 |         no_exit_statements: ~
30 |         never_use_else: ~
31 |         forbidden_function_calls:
32 |           blacklist: [var_dump]
33 |     paratest:
34 |       testsuite: E2E
35 |       verbose: true
36 |       functional: true
37 |     psalm:
38 |       no_cache: true
39 |   testsuites:
40 |     git_pre_commit:
41 |       tasks: [phpcs, phpspec, phpunit, composer, composer_normalize, yamllint, phplint, phpparser, psalm]
42 |     # On CI, we run paratest separately. For some reason this currently fails in GitHub actions.
43 |     ci:
44 |       tasks: [phpcs, phpspec, phpunit, composer, composer_normalize, yamllint, phplint, phpparser, psalm]
45 |     # Don't run psalm on Windows for now. There is a known issue with the Windows phar:
46 |     # https://github.com/vimeo/psalm/issues/2858
47 |     windows:
48 |       tasks: [phpcs, phpspec, phpunit, composer, composer_normalize, yamllint, phplint, phpparser]
49 |   environment:
50 |     variables:
51 |       BOX_REQUIREMENT_CHECKER: 0
52 |     paths:
53 |       - tools
54 | 


--------------------------------------------------------------------------------
/phpspec.yml:
--------------------------------------------------------------------------------
1 | suites:
2 |   default:
3 |     namespace: GrumPHP
4 |     psr4_prefix: GrumPHP
5 | 
6 | formatter.name: pretty
7 | 


--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
 1 | <phpunit bootstrap="test/Bootstrap.php" colors="true">
 2 |     <php>
 3 |         <ini name="error_reporting" value="-1" />
 4 |     </php>
 5 |     <testsuites>
 6 |         <testsuite name="Unit">
 7 |             <directory>test/Unit</directory>
 8 |         </testsuite>
 9 |         <testsuite name="E2E">
10 |             <directory>test/E2E</directory>
11 |         </testsuite>
12 |     </testsuites>
13 | </phpunit>
14 | 


--------------------------------------------------------------------------------
/psalm.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0"?>
 2 | <psalm
 3 |     errorLevel="2"
 4 |     resolveFromConfigFile="true"
 5 |     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 6 |     xmlns="https://getpsalm.org/schema/config"
 7 |     xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
 8 |     findUnusedBaselineEntry="false"
 9 |     findUnusedCode="false"
10 | >
11 |     <projectFiles>
12 |         <directory name="src" />
13 |         <ignoreFiles>
14 |             <directory name="src/Test" />
15 |             <directory name="vendor" />
16 |             <directory name="test" />
17 |             <directory name="spec" />
18 |             <file name="src/Parser/Php/Visitor/AbstractVisitor.php" />
19 |         </ignoreFiles>
20 |     </projectFiles>
21 |     <issueHandlers>
22 |         <RiskyTruthyFalsyComparison>
23 |             <errorLevel type="suppress">
24 |                 <directory name="src/" />
25 |             </errorLevel>
26 |         </RiskyTruthyFalsyComparison>
27 |     </issueHandlers>
28 | </psalm>
29 | 


--------------------------------------------------------------------------------
/resources/ascii/failed.txt:
--------------------------------------------------------------------------------
1 | ███████╗ █████╗ ██╗██╗     ███████╗██████╗
2 | ██╔════╝██╔══██╗██║██║     ██╔════╝██╔══██╗
3 | █████╗  ███████║██║██║     █████╗  ██║  ██║
4 | ██╔══╝  ██╔══██║██║██║     ██╔══╝  ██║  ██║
5 | ██║     ██║  ██║██║███████╗███████╗██████╔╝
6 | ╚═╝     ╚═╝  ╚═╝╚═╝╚══════╝╚══════╝╚═════╝
7 | 


--------------------------------------------------------------------------------
/resources/ascii/grumphp-grumpy.txt:
--------------------------------------------------------------------------------
 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 | 


--------------------------------------------------------------------------------
/resources/ascii/grumphp-happy.txt:
--------------------------------------------------------------------------------
 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 | 


--------------------------------------------------------------------------------
/resources/ascii/me-gusta.txt:
--------------------------------------------------------------------------------
 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 | 


--------------------------------------------------------------------------------
/resources/ascii/nopecat.txt:
--------------------------------------------------------------------------------
 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 | 


--------------------------------------------------------------------------------
/resources/ascii/succeeded.txt:
--------------------------------------------------------------------------------
1 | ███████╗██╗   ██╗ ██████╗ ██████╗███████╗███████╗██████╗ ███████╗██████╗
2 | ██╔════╝██║   ██║██╔════╝██╔════╝██╔════╝██╔════╝██╔══██╗██╔════╝██╔══██╗
3 | ███████╗██║   ██║██║     ██║     █████╗  █████╗  ██║  ██║█████╗  ██║  ██║
4 | ╚════██║██║   ██║██║     ██║     ██╔══╝  ██╔══╝  ██║  ██║██╔══╝  ██║  ██║
5 | ███████║╚██████╔╝╚██████╗╚██████╗███████╗███████╗██████╔╝███████╗██████╔╝
6 | ╚══════╝ ╚═════╝  ╚═════╝ ╚═════╝╚══════╝╚══════╝╚═════╝ ╚══════╝╚═════╝
7 | 


--------------------------------------------------------------------------------
/resources/config/config.yml:
--------------------------------------------------------------------------------
 1 | #
 2 | # This entry makes sure to trigger the grumphp container extension.
 3 | # It can be removed in the future if everybody migrated `parameters:` to `grumphp:` in the configuration file.
 4 | grumphp: ~
 5 | 
 6 | #
 7 | # Load config based on configured parameters
 8 | #
 9 | services:
10 |     GrumPHP\Configuration\Model\AsciiConfig:
11 |         arguments:
12 |             - '%ascii%'
13 |     GrumPHP\Configuration\Model\EnvConfig:
14 |         public: true
15 |         factory: ['GrumPHP\Configuration\Model\EnvConfig', 'fromArray']
16 |         arguments:
17 |             - '%environment%'
18 |     GrumPHP\Configuration\Model\HooksConfig:
19 |         arguments:
20 |             - '%hooks_dir%'
21 |             - '%hooks_preset%'
22 |             - '%git_hook_variables%'
23 |     GrumPHP\Configuration\Model\ParallelConfig:
24 |         factory: ['GrumPHP\Configuration\Model\ParallelConfig', 'fromArray']
25 |         arguments:
26 |             - '%parallel%'
27 |     GrumPHP\Configuration\Model\FixerConfig:
28 |         factory: ['GrumPHP\Configuration\Model\FixerConfig', 'fromArray']
29 |         arguments:
30 |             - '%fixer%'
31 |     GrumPHP\Configuration\Model\ProcessConfig:
32 |         arguments:
33 |             - '%process_timeout%'
34 |     GrumPHP\Configuration\Model\GitStashConfig:
35 |         arguments:
36 |             - '%ignore_unstaged_changes%'
37 |     GrumPHP\Configuration\Model\RunnerConfig:
38 |         arguments:
39 |             - '%stop_on_failure%'
40 |             - '%hide_circumvention_tip%'
41 |             - '%additional_info%'
42 | 


--------------------------------------------------------------------------------
/resources/config/fixer.yml:
--------------------------------------------------------------------------------
1 | services:
2 |     GrumPHP\Fixer\FixerUpper:
3 |         arguments:
4 |             - '@grumphp.io'
5 |             - '@GrumPHP\Configuration\Model\FixerConfig'
6 | 


--------------------------------------------------------------------------------
/resources/config/formatter.yml:
--------------------------------------------------------------------------------
 1 | services:
 2 |     formatter.raw_process:
 3 |         class: GrumPHP\Formatter\RawProcessFormatter
 4 |     formatter.phpcsfixer:
 5 |         class: GrumPHP\Formatter\PhpCsFixerFormatter
 6 |     formatter.phpcs:
 7 |         class: GrumPHP\Formatter\PhpcsFormatter
 8 |     formatter.git_blacklist:
 9 |         class: GrumPHP\Formatter\GitBlacklistFormatter
10 |         arguments:
11 |             - '@grumphp.io'
12 | 


--------------------------------------------------------------------------------
/resources/config/linters.yml:
--------------------------------------------------------------------------------
 1 | services:
 2 |     linter.jsonlint:
 3 |         class: GrumPHP\Linter\Json\JsonLinter
 4 |         arguments:
 5 |           - '@grumphp.util.filesystem'
 6 |           - '@json.parser'
 7 | 
 8 |     linter.xmllint:
 9 |         class: GrumPHP\Linter\Xml\XmlLinter
10 |         arguments: []
11 | 
12 |     linter.yamllint:
13 |         class: GrumPHP\Linter\Yaml\YamlLinter
14 |         arguments:
15 |           - '@grumphp.util.filesystem'
16 | 


--------------------------------------------------------------------------------
/resources/config/locators.yml:
--------------------------------------------------------------------------------
 1 | services:
 2 |     GrumPHP\Locator\AsciiLocator:
 3 |         arguments:
 4 |             - '@GrumPHP\Configuration\Model\AsciiConfig'
 5 |             - '@filesystem'
 6 |             - '@GrumPHP\Util\Paths'
 7 | 
 8 |     GrumPHP\Locator\ExternalCommand:
 9 |         class:
10 |         factory: ['GrumPHP\Locator\ExternalCommand', 'loadWithPaths']
11 |         arguments:
12 |             - '@GrumPHP\Util\Paths'
13 |             - '@executable_finder'
14 | 
15 |     GrumPHP\Locator\ChangedFiles:
16 |         arguments:
17 |             - '@GrumPHP\Git\GitRepository'
18 |             - '@filesystem'
19 |             - '@GrumPHP\Util\Paths'
20 | 
21 |     GrumPHP\Locator\ListedFiles:
22 |         arguments:
23 |             - '@GrumPHP\Util\Paths'
24 | 
25 |     GrumPHP\Locator\RegisteredFiles:
26 |         arguments:
27 |             - '@GrumPHP\Git\GitRepository'
28 |             - '@GrumPHP\Util\Paths'
29 |             - '@GrumPHP\Locator\ListedFiles'
30 | 
31 |     GrumPHP\Locator\StdInFiles:
32 |         arguments:
33 |             - '@GrumPHP\Locator\ChangedFiles'
34 |             - '@GrumPHP\Locator\ListedFiles'
35 | 
36 |     GrumPHP\Locator\GitWorkingDirLocator:
37 |         arguments:
38 |             - '@executable_finder'
39 |     GrumPHP\Locator\GitRepositoryDirLocator:
40 |         arguments:
41 |             - '@grumphp.util.filesystem'
42 |     GrumPHP\Locator\GitRepositoryLocator:
43 |         arguments:
44 |             - '@GrumPHP\Util\Paths'
45 |     GrumPHP\Locator\EnrichedGuessedPathsFromDotEnvLocator:
46 |         public: true
47 |         arguments:
48 |             - '@grumphp.util.filesystem'
49 | 


--------------------------------------------------------------------------------
/resources/config/subscribers.yml:
--------------------------------------------------------------------------------
 1 | services:
 2 |   subscriber.stash_unstaged_changes:
 3 |     class: GrumPHP\Event\Subscriber\StashUnstagedChangesSubscriber
 4 |     arguments:
 5 |       - '@GrumPHP\Configuration\Model\GitStashConfig'
 6 |       - '@GrumPHP\Git\GitRepository'
 7 |       - '@grumphp.io'
 8 |     tags:
 9 |       - { name: grumphp.event_subscriber }
10 |     public: true
11 | 
12 |   GrumPHP\Event\Subscriber\VerboseLoggerSubscriber:
13 |     arguments:
14 |       - '@grumphp.logger'
15 |       - '@GrumPHP\Configuration\GuessedPaths'
16 |     tags:
17 |       - { name: grumphp.event_subscriber }
18 | 


--------------------------------------------------------------------------------
/resources/config/util.yml:
--------------------------------------------------------------------------------
 1 | services:
 2 |     grumphp.util.filesystem:
 3 |         class: GrumPHP\Util\Filesystem
 4 |         public: true
 5 | 
 6 |     GrumPHP\Util\Paths:
 7 |         public: true
 8 |         arguments:
 9 |             - '@grumphp.util.filesystem'
10 |             - '@GrumPHP\Configuration\GuessedPaths'
11 | 
12 |     grumphp.util.phpversion:
13 |         class: GrumPHP\Util\PhpVersion
14 |         arguments:
15 |             -
16 |               '5.6': '2018-12-31 23:59:59'
17 |               '7.0': '2018-12-03 23:59:59'
18 |               '7.1': '2019-12-01 23:59:59'
19 |               '7.2': '2020-11-30 23:59:59'
20 |               '7.3': '2021-12-06 23:59:59'
21 |               '7.4': '2022-11-28 23:59:59'
22 |               '8.0': '2023-11-26 23:59:59'
23 |               '8.1': '2025-12-31 23:59:59'
24 |               '8.2': '2026-12-31 23:59:59'
25 |               '8.3': '2027-12-31 23:59:59'
26 |               '8.4': '2028-12-31 23:59:59'
27 | 


--------------------------------------------------------------------------------
/resources/hooks/local/commit-msg:
--------------------------------------------------------------------------------
 1 | #!/bin/sh
 2 | 
 3 | #
 4 | # Run the hook command.
 5 | # Note: this will be replaced by the real command during copy.
 6 | #
 7 | 
 8 | GIT_USER=$(git config user.name)
 9 | GIT_EMAIL=$(git config user.email)
10 | COMMIT_MSG_FILE=$1
11 | 
12 | # Fetch the GIT diff and format it as command input:
13 | DIFF=$(git -c diff.mnemonicprefix=false -c diff.noprefix=false --no-pager diff -r -p -m -M --full-index --no-color --staged | cat)
14 | 
15 | # Grumphp env vars
16 | $(ENV)
17 | export GRUMPHP_GIT_WORKING_DIR="$(git rev-parse --show-toplevel)"
18 | 
19 | # Run GrumPHP
20 | (cd "${HOOK_EXEC_PATH}" && printf "%s\n" "${DIFF}" | $(EXEC_GRUMPHP_COMMAND) $(HOOK_COMMAND) "--git-user='$GIT_USER'" "--git-email='$GIT_EMAIL'" "$COMMIT_MSG_FILE")
21 | 


--------------------------------------------------------------------------------
/resources/hooks/local/pre-commit:
--------------------------------------------------------------------------------
 1 | #!/bin/sh
 2 | 
 3 | #
 4 | # Run the hook command.
 5 | # Note: this will be replaced by the real command during copy.
 6 | #
 7 | 
 8 | # Fetch the GIT diff and format it as command input:
 9 | DIFF=$(git -c diff.mnemonicprefix=false -c diff.noprefix=false --no-pager diff -r -p -m -M --full-index --no-color --staged | cat)
10 | 
11 | # Grumphp env vars
12 | $(ENV)
13 | export GRUMPHP_GIT_WORKING_DIR="$(git rev-parse --show-toplevel)"
14 | 
15 | # Run GrumPHP
16 | (cd "${HOOK_EXEC_PATH}" && printf "%s\n" "${DIFF}" | $(EXEC_GRUMPHP_COMMAND) $(HOOK_COMMAND) '--skip-success-output')
17 | 


--------------------------------------------------------------------------------
/resources/hooks/vagrant/commit-msg:
--------------------------------------------------------------------------------
 1 | #!/bin/sh
 2 | 
 3 | #
 4 | # Run the hook command.
 5 | # Note: this will be replaced by the real command during copy.
 6 | #
 7 | 
 8 | GIT_USER=$(git config user.name)
 9 | GIT_EMAIL=$(git config user.email)
10 | 
11 | # Fetch the commit message
12 | COMMIT_MSG_FILE=$1
13 | COMMIT_MSG=$(cat "${COMMIT_MSG_FILE}")
14 | 
15 | # Fetch the GIT diff and format it as command input:
16 | DIFF=$(git -c diff.mnemonicprefix=false -c diff.noprefix=false --no-pager diff -r -p -m -M --full-index --no-color --staged | cat)
17 | 
18 | # Copy the commit message and run GrumPHP
19 | cd $(VAGRANT_HOST_DIR) && vagrant ssh --command '$(which sh)' << COMMANDS
20 | 
21 |   cd $(VAGRANT_PROJECT_DIR)
22 | 
23 |   # Add grumphp envs:
24 |   $(ENV)
25 | 
26 |   # Transfer the DIFF
27 |   DIFF=\$(cat <<- '__GRUMPHP_DIFF_HEREDOC__'
28 | 	${DIFF}
29 | 	__GRUMPHP_DIFF_HEREDOC__
30 |   )
31 | 
32 |   # Transfer the commit message
33 |   COMMIT_MSG=\$(cat <<- '__GRUMPHP_COMMIT_MSG_HEREDOC__'
34 | 	${COMMIT_MSG}
35 | 	__GRUMPHP_COMMIT_MSG_HEREDOC__
36 |   )
37 | 
38 |   VAGRANT_COMMIT_MSG_FILE=\$(mktemp -t "grumphp-commitmsg.XXXXXXXXXX")
39 |   echo "\${COMMIT_MSG}" > \$VAGRANT_COMMIT_MSG_FILE
40 |   printf "%s\n" "\${DIFF}" | $(EXEC_GRUMPHP_COMMAND) $(HOOK_COMMAND) '--ansi' "--git-user='$GIT_USER'" "--git-email='$GIT_EMAIL'" \$VAGRANT_COMMIT_MSG_FILE
41 |   RC=\$?
42 |   rm \$VAGRANT_COMMIT_MSG_FILE
43 |   exit \$RC
44 | COMMANDS
45 | 


--------------------------------------------------------------------------------
/resources/hooks/vagrant/pre-commit:
--------------------------------------------------------------------------------
 1 | #!/bin/sh
 2 | 
 3 | #
 4 | # Run the hook command.
 5 | # Note: this will be replaced by the real command during copy.
 6 | #
 7 | 
 8 | # Fetch the GIT diff and format it as command input:
 9 | DIFF=$(git -c diff.mnemonicprefix=false -c diff.noprefix=false --no-pager diff -r -p -m -M --full-index --no-color --staged | cat)
10 | 
11 | # Run GrumPHP
12 | cd $(VAGRANT_HOST_DIR) && vagrant ssh --command '$(which sh)' << COMMANDS
13 | 
14 |   cd $(VAGRANT_PROJECT_DIR)
15 | 
16 |   # Add grumphp envs:
17 |   $(ENV)
18 | 
19 |   # Transfer the DIFF
20 |   DIFF=\$(cat <<- '__GRUMPHP_DIFF_HEREDOC__'
21 | 	${DIFF}
22 | 	__GRUMPHP_DIFF_HEREDOC__
23 |   )
24 | 
25 |   printf "%s\n" "\${DIFF}" | $(EXEC_GRUMPHP_COMMAND) $(HOOK_COMMAND) '--ansi' '--skip-success-output'
26 | COMMANDS
27 | 


--------------------------------------------------------------------------------
/resources/logo/grumphp-grumpy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phpro/grumphp/5f5d566f3aa15548884b6309e785effaad9a9ea5/resources/logo/grumphp-grumpy.png


--------------------------------------------------------------------------------
/resources/logo/grumphp-happy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phpro/grumphp/5f5d566f3aa15548884b6309e785effaad9a9ea5/resources/logo/grumphp-happy.png


--------------------------------------------------------------------------------
/resources/logo/simplified-grumpy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phpro/grumphp/5f5d566f3aa15548884b6309e785effaad9a9ea5/resources/logo/simplified-grumpy.png


--------------------------------------------------------------------------------
/resources/logo/simplified-happy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phpro/grumphp/5f5d566f3aa15548884b6309e785effaad9a9ea5/resources/logo/simplified-happy.png


--------------------------------------------------------------------------------
/src/Collection/LintErrorsCollection.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Collection;
 6 | 
 7 | use Doctrine\Common\Collections\ArrayCollection;
 8 | 
 9 | /**
10 |  * @extends ArrayCollection<int, \GrumPHP\Linter\LintError>
11 |  */
12 | class LintErrorsCollection extends ArrayCollection
13 | {
14 |     public function __toString(): string
15 |     {
16 |         $errors = [];
17 |         foreach ($this->getIterator() as $error) {
18 |             $errors[] = $error->__toString();
19 |         }
20 | 
21 |         return implode(PHP_EOL, $errors);
22 |     }
23 | }
24 | 


--------------------------------------------------------------------------------
/src/Collection/ParseErrorsCollection.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Collection;
 6 | 
 7 | use Doctrine\Common\Collections\ArrayCollection;
 8 | 
 9 | /**
10 |  * @extends ArrayCollection<int, \GrumPHP\Parser\ParseError>
11 |  */
12 | class ParseErrorsCollection extends ArrayCollection
13 | {
14 |     public function __toString(): string
15 |     {
16 |         $errors = [];
17 |         foreach ($this->getIterator() as $error) {
18 |             $errors[] = $error->__toString();
19 |         }
20 | 
21 |         return implode(PHP_EOL, $errors);
22 |     }
23 | }
24 | 


--------------------------------------------------------------------------------
/src/Collection/TaskResultCollection.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Collection;
 6 | 
 7 | use Doctrine\Common\Collections\ArrayCollection;
 8 | use GrumPHP\Runner\TaskResult;
 9 | use GrumPHP\Runner\TaskResultInterface;
10 | 
11 | /**
12 |  * @extends ArrayCollection<int, TaskResultInterface>
13 |  */
14 | class TaskResultCollection extends ArrayCollection
15 | {
16 |     const NO_TASKS = -100;
17 | 
18 |     public function isPassed(): bool
19 |     {
20 |         return TaskResult::PASSED === $this->getResultCode();
21 |     }
22 | 
23 |     public function isFailed(): bool
24 |     {
25 |         foreach ($this as $taskResult) {
26 |             if (TaskResult::FAILED === $taskResult->getResultCode()) {
27 |                 return true;
28 |             }
29 |         }
30 | 
31 |         return false;
32 |     }
33 | 
34 |     public function getResultCode(): int
35 |     {
36 |         $resultCode = static::NO_TASKS;
37 |         foreach ($this as $taskResult) {
38 |             $resultCode = (int) max($resultCode, $taskResult->getResultCode());
39 |         }
40 | 
41 |         return $resultCode;
42 |     }
43 | 
44 |     public function filterByResultCode(int $resultCode): self
45 |     {
46 |         return $this->filter(function (TaskResultInterface $taskResult) use ($resultCode): bool {
47 |             return $resultCode === $taskResult->getResultCode();
48 |         });
49 |     }
50 | 
51 |     /**
52 |      * @return array<string, string>
53 |      */
54 |     public function getAllMessages(): array
55 |     {
56 |         $messages = [];
57 | 
58 |         /** @var TaskResultInterface $taskResult */
59 |         foreach ($this as $taskResult) {
60 |             $config = $taskResult->getTask()->getConfig();
61 |             $label = $config->getMetadata()->label() ?: $config->getName();
62 |             $messages[$label] = $taskResult->getMessage();
63 |         }
64 | 
65 |         return $messages;
66 |     }
67 | }
68 | 


--------------------------------------------------------------------------------
/src/Collection/TestSuiteCollection.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Collection;
 6 | 
 7 | use Doctrine\Common\Collections\ArrayCollection;
 8 | use GrumPHP\Exception\InvalidArgumentException;
 9 | use GrumPHP\TestSuite\TestSuiteInterface;
10 | 
11 | /**
12 |  * @extends ArrayCollection<string, TestSuiteInterface>
13 |  */
14 | class TestSuiteCollection extends ArrayCollection
15 | {
16 |     public function getRequired(string $name): TestSuiteInterface
17 |     {
18 |         if (!$result = $this->get($name)) {
19 |             throw InvalidArgumentException::unknownTestSuite($name);
20 |         }
21 | 
22 |         return $result;
23 |     }
24 | 
25 |     public function getOptional(string $name): ?TestSuiteInterface
26 |     {
27 |         return $this->get($name);
28 |     }
29 | }
30 | 


--------------------------------------------------------------------------------
/src/Configuration/Compiler/ExtensionCompilerPass.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Configuration\Compiler;
 6 | 
 7 | use GrumPHP\Configuration\LoaderFactory;
 8 | use GrumPHP\Exception\RuntimeException;
 9 | use GrumPHP\Extension\ExtensionInterface;
10 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
11 | use Symfony\Component\DependencyInjection\ContainerBuilder;
12 | 
13 | class ExtensionCompilerPass implements CompilerPassInterface
14 | {
15 |     public function process(ContainerBuilder $container): void
16 |     {
17 |         $loader = LoaderFactory::createLoader($container);
18 |         $extensions = $container->getParameter('extensions');
19 |         $extensions = \is_array($extensions) ? $extensions : [];
20 |         foreach ($extensions as $extensionClass) {
21 |             if (!class_exists($extensionClass)) {
22 |                 throw new RuntimeException(sprintf('Invalid extension class specified: %s', $extensionClass));
23 |             }
24 | 
25 |             $extension = new $extensionClass();
26 |             if (!$extension instanceof ExtensionInterface) {
27 |                 throw new RuntimeException(sprintf(
28 |                     'Extension class must implement ExtensionInterface. But `%s` is not.',
29 |                     $extensionClass
30 |                 ));
31 |             }
32 | 
33 |             foreach ($extension->imports() as $import) {
34 |                 $loader->load($import);
35 |             }
36 |         }
37 |     }
38 | }
39 | 


--------------------------------------------------------------------------------
/src/Configuration/Compiler/RegisterListenersPass.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | declare(strict_types=1);
 3 | 
 4 | namespace GrumPHP\Configuration\Compiler;
 5 | 
 6 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 7 | use Symfony\Component\DependencyInjection\ContainerBuilder;
 8 | use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass as SymfonyRegisterListenersPass;
 9 | 
10 | /**
11 |  * @see https://github.com/symfony/symfony/pull/40468
12 |  * Symfony removed the ability to add custom tags. So we need to take care of that!
13 |  */
14 | final class RegisterListenersPass implements CompilerPassInterface
15 | {
16 |     private SymfonyRegisterListenersPass $pass;
17 | 
18 |     public function __construct(SymfonyRegisterListenersPass $pass)
19 |     {
20 |         $this->pass = $pass;
21 |     }
22 | 
23 |     public static function create(): self
24 |     {
25 |         return new self(new SymfonyRegisterListenersPass());
26 |     }
27 | 
28 |     public function process(ContainerBuilder $container): void
29 |     {
30 |         $this->changeKey($container, 'grumphp.event_listener', 'kernel.event_listener');
31 |         $this->changeKey($container, 'grumphp.event_subscriber', 'kernel.event_subscriber');
32 | 
33 |         $this->pass->process($container);
34 |     }
35 | 
36 |     private function changeKey(ContainerBuilder $container, string $sourceKey, string $targetKey): void
37 |     {
38 |         foreach ($container->getDefinitions() as $definition) {
39 |             if ($definition->hasTag($sourceKey)) {
40 |                 $attributes = $definition->getTag($sourceKey)[0];
41 |                 $definition->addTag($targetKey, $attributes);
42 |                 $definition->clearTag($sourceKey);
43 |             }
44 |         }
45 |     }
46 | }
47 | 


--------------------------------------------------------------------------------
/src/Configuration/Configurator/TaskConfigurator.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Configuration\Configurator;
 6 | 
 7 | use GrumPHP\Task\Config\TaskConfigInterface;
 8 | use GrumPHP\Task\TaskInterface;
 9 | 
10 | class TaskConfigurator
11 | {
12 |     public function __invoke(TaskInterface $task, TaskConfigInterface $config): TaskInterface
13 |     {
14 |         return $task->withConfig($config);
15 |     }
16 | }
17 | 


--------------------------------------------------------------------------------
/src/Configuration/Environment/DotEnvRegistrar.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Configuration\Environment;
 6 | 
 7 | use GrumPHP\Configuration\Model\EnvConfig;
 8 | use Symfony\Component\Dotenv\Dotenv;
 9 | 
10 | class DotEnvRegistrar
11 | {
12 |     public static function register(EnvConfig $config): void
13 |     {
14 |         $env = new Dotenv();
15 | 
16 |         if ($config->hasFiles()) {
17 |             /** @psalm-suppress InvalidArgument - Psalm types in Dotenv class are not valid currently  */
18 |             $env->overload(...$config->getFiles());
19 |         }
20 | 
21 |         if ($config->hasVariables()) {
22 |             $env->populate($config->getVariables(), true);
23 |         }
24 |     }
25 | }
26 | 


--------------------------------------------------------------------------------
/src/Configuration/Environment/DotEnvSerializer.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Configuration\Environment;
 6 | 
 7 | class DotEnvSerializer
 8 | {
 9 |     /**
10 |      * @param array<string,string> $env
11 |      *
12 |      * @return string
13 |      */
14 |     public static function serialize(array $env): string
15 |     {
16 |         return implode("\n", array_map(
17 |             static function (string $key, string $value): string {
18 |                 return 'export '.$key.'='.$value;
19 |             },
20 |             array_keys($env),
21 |             $env
22 |         ));
23 |     }
24 | }
25 | 


--------------------------------------------------------------------------------
/src/Configuration/GrumPHPExtension.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Configuration;
 6 | 
 7 | use Symfony\Component\Config\Definition\ConfigurationInterface;
 8 | use Symfony\Component\DependencyInjection\ContainerBuilder;
 9 | use Symfony\Component\DependencyInjection\Extension\Extension;
10 | 
11 | class GrumPHPExtension extends Extension
12 | {
13 |     public function load(array $configs, ContainerBuilder $container): void
14 |     {
15 |         $this->loadInternal(
16 |             $this->processConfiguration(
17 |                 $this->getConfiguration($configs, $container),
18 |                 $configs
19 |             ),
20 |             $container
21 |         );
22 |     }
23 | 
24 |     public function getConfiguration(array $config, ContainerBuilder $container): ConfigurationInterface
25 |     {
26 |         return new Configuration();
27 |     }
28 | 
29 |     public function getAlias(): string
30 |     {
31 |         return 'grumphp';
32 |     }
33 | 
34 |     private function loadInternal(array $config, ContainerBuilder $container): void
35 |     {
36 |         foreach ($config as $key => $value) {
37 |             $container->setParameter($key, $value);
38 |         }
39 |     }
40 | }
41 | 


--------------------------------------------------------------------------------
/src/Configuration/Loader/DistFileLoader.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | declare(strict_types=1);
 3 | 
 4 | namespace GrumPHP\Configuration\Loader;
 5 | 
 6 | use Symfony\Component\Config\Loader\LoaderInterface;
 7 | use Symfony\Component\Config\Loader\LoaderResolverInterface;
 8 | 
 9 | /**
10 |  * A decorating dist loader that supports **.dist files and defers loading.
11 |  */
12 | final class DistFileLoader implements LoaderInterface
13 | {
14 |     private LoaderInterface $loader;
15 | 
16 |     public function __construct(LoaderInterface $loader)
17 |     {
18 |         $this->loader = $loader;
19 |     }
20 | 
21 |     public function load(mixed $resource, ?string $type = null): mixed
22 |     {
23 |         return $this->loader->load($resource, $type);
24 |     }
25 | 
26 |     public function supports(mixed $resource, ?string $type = null): bool
27 |     {
28 |         if (!\is_string($resource)) {
29 |             return false;
30 |         }
31 | 
32 |         if ($type !== null) {
33 |             return $this->loader->supports($resource, $type);
34 |         }
35 | 
36 |         $extension = pathinfo($resource, \PATHINFO_EXTENSION);
37 |         if ($extension !== 'dist') {
38 |             return false;
39 |         }
40 | 
41 |         $distForFile = pathinfo($resource, \PATHINFO_FILENAME);
42 | 
43 |         return $this->loader->supports($distForFile);
44 |     }
45 | 
46 |     public function getResolver(): LoaderResolverInterface
47 |     {
48 |         return $this->loader->getResolver();
49 |     }
50 | 
51 |     public function setResolver(LoaderResolverInterface $resolver): void
52 |     {
53 |         $this->loader->setResolver($resolver);
54 |     }
55 | }
56 | 


--------------------------------------------------------------------------------
/src/Configuration/LoaderFactory.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | declare(strict_types=1);
 3 | 
 4 | namespace GrumPHP\Configuration;
 5 | 
 6 | use GrumPHP\Configuration\Loader\DistFileLoader;
 7 | use Symfony\Component\Config\FileLocator;
 8 | use Symfony\Component\Config\Loader\DelegatingLoader;
 9 | use Symfony\Component\Config\Loader\LoaderResolver;
10 | use Symfony\Component\DependencyInjection\ContainerBuilder;
11 | use Symfony\Component\DependencyInjection\Loader\DirectoryLoader;
12 | use Symfony\Component\DependencyInjection\Loader\GlobFileLoader;
13 | use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
14 | use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
15 | use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
16 | 
17 | final class LoaderFactory
18 | {
19 |     private const ENV = 'grumphp';
20 | 
21 |     /**
22 |      * @param list<string> $paths
23 |      */
24 |     public static function createLoader(ContainerBuilder $container, array $paths = []): DelegatingLoader
25 |     {
26 |         $locator = new FileLocator($paths);
27 |         $resolver = new LoaderResolver([
28 |             $xmlLoader = new XmlFileLoader($container, $locator, self::ENV),
29 |             $yamlLoader = new YamlFileLoader($container, $locator, self::ENV),
30 |             $iniLoader = new IniFileLoader($container, $locator, self::ENV),
31 |             new GlobFileLoader($container, $locator, self::ENV),
32 |             new DirectoryLoader($container, $locator, self::ENV),
33 |             new DistFileLoader($xmlLoader),
34 |             new DistFileLoader($yamlLoader),
35 |             new DistFileLoader($iniLoader),
36 |         ]);
37 | 
38 |         return new DelegatingLoader($resolver);
39 |     }
40 | }
41 | 


--------------------------------------------------------------------------------
/src/Configuration/Model/AsciiConfig.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Configuration\Model;
 6 | 
 7 | class AsciiConfig
 8 | {
 9 |     /**
10 |      * @var array|null
11 |      */
12 |     private $asciiConfig;
13 | 
14 |     public function __construct(?array $asciiConfig)
15 |     {
16 |         $this->asciiConfig = $asciiConfig;
17 |     }
18 | 
19 |     public function fetchResource(string $resource): ?string
20 |     {
21 |         if (null === $this->asciiConfig) {
22 |             return null;
23 |         }
24 | 
25 |         $paths = $this->asciiConfig;
26 |         if (!array_key_exists($resource, $paths)) {
27 |             return null;
28 |         }
29 | 
30 |         // Deal with multiple ascii files by returning one at random.
31 |         if (\is_array($paths[$resource])) {
32 |             shuffle($paths[$resource]);
33 |             return reset($paths[$resource]);
34 |         }
35 | 
36 |         return $paths[$resource];
37 |     }
38 | }
39 | 


--------------------------------------------------------------------------------
/src/Configuration/Model/FixerConfig.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Configuration\Model;
 6 | 
 7 | /**
 8 |  * @psalm-immutable
 9 |  */
10 | class FixerConfig
11 | {
12 |     /**
13 |      * @var bool
14 |      */
15 |     private $enabled;
16 | 
17 |     /**
18 |      * @var bool
19 |      */
20 |     private $fixByDefault;
21 | 
22 |     public function __construct(
23 |         bool $enabled,
24 |         bool $fixByDefault
25 |     ) {
26 |         $this->enabled = $enabled;
27 |         $this->fixByDefault = $fixByDefault;
28 |     }
29 | 
30 |     /**
31 |      * @param array{fix_by_default: bool, enabled: bool} $config
32 |      */
33 |     public static function fromArray(array $config): self
34 |     {
35 |         return new self(
36 |             ($config['enabled'] ?? false),
37 |             ($config['fix_by_default'] ?? false)
38 |         );
39 |     }
40 | 
41 |     public function isEnabled(): bool
42 |     {
43 |         return $this->enabled;
44 |     }
45 | 
46 |     public function fixByDefault(): bool
47 |     {
48 |         return $this->fixByDefault;
49 |     }
50 | }
51 | 


--------------------------------------------------------------------------------
/src/Configuration/Model/GitStashConfig.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Configuration\Model;
 6 | 
 7 | class GitStashConfig
 8 | {
 9 |     /**
10 |      * @var bool
11 |      */
12 |     private $ignoreUnstagedChanges;
13 | 
14 |     public function __construct(bool $ignoreUnstagedChanges)
15 |     {
16 |         $this->ignoreUnstagedChanges = $ignoreUnstagedChanges;
17 |     }
18 | 
19 |     public function ignoreUnstagedChanges(): bool
20 |     {
21 |         return $this->ignoreUnstagedChanges;
22 |     }
23 | }
24 | 


--------------------------------------------------------------------------------
/src/Configuration/Model/HooksConfig.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Configuration\Model;
 6 | 
 7 | /**
 8 |  * @psalm-immutable
 9 |  */
10 | class HooksConfig
11 | {
12 |     /**
13 |      * @var string|null
14 |      */
15 |     private $dir;
16 | 
17 |     /**
18 |      * @var string
19 |      */
20 |     private $preset;
21 | 
22 |     /**
23 |      * @var array
24 |      */
25 |     private $variables;
26 | 
27 |     public function __construct(
28 |         ?string $dir,
29 |         string $preset,
30 |         array $variables
31 |     ) {
32 |         $this->dir = $dir;
33 |         $this->preset = $preset;
34 |         $this->variables = $variables;
35 |     }
36 | 
37 |     public function getDir(): ?string
38 |     {
39 |         return $this->dir;
40 |     }
41 | 
42 |     public function getPreset(): string
43 |     {
44 |         return $this->preset;
45 |     }
46 | 
47 |     public function getVariables(): array
48 |     {
49 |         return $this->variables;
50 |     }
51 | }
52 | 


--------------------------------------------------------------------------------
/src/Configuration/Model/ParallelConfig.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Configuration\Model;
 6 | 
 7 | /**
 8 |  * @psalm-immutable
 9 |  */
10 | class ParallelConfig
11 | {
12 |     /**
13 |      * @var bool
14 |      */
15 |     private $enabled;
16 | 
17 |     /**
18 |      * @var int
19 |      */
20 |     private $maxWorkers;
21 | 
22 |     public function __construct(
23 |         bool $enabled,
24 |         int $maxWorkers
25 |     ) {
26 |         $this->enabled = $enabled;
27 |         $this->maxWorkers = $maxWorkers;
28 |     }
29 | 
30 |     /**
31 |      * @param array{max_workers: int, enabled: bool} $config
32 |      */
33 |     public static function fromArray(array $config): self
34 |     {
35 |         return new self(
36 |             ($config['enabled'] ?? false),
37 |             ($config['max_workers'] ?? 1)
38 |         );
39 |     }
40 | 
41 |     public function isEnabled(): bool
42 |     {
43 |         return $this->enabled;
44 |     }
45 | 
46 |     public function getMaxWorkers(): int
47 |     {
48 |         return $this->maxWorkers;
49 |     }
50 | }
51 | 


--------------------------------------------------------------------------------
/src/Configuration/Model/ProcessConfig.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Configuration\Model;
 6 | 
 7 | /**
 8 |  * @psalm-immutable
 9 |  */
10 | class ProcessConfig
11 | {
12 |     /**
13 |      * @var float|null
14 |      */
15 |     private $timeout;
16 | 
17 |     public function __construct(
18 |         ?float $timeout
19 |     ) {
20 |         $this->timeout = $timeout;
21 |     }
22 | 
23 |     public function getTimeout(): ?float
24 |     {
25 |         return $this->timeout;
26 |     }
27 | }
28 | 


--------------------------------------------------------------------------------
/src/Configuration/Model/RunnerConfig.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Configuration\Model;
 6 | 
 7 | class RunnerConfig
 8 | {
 9 |     /**
10 |      * @var bool
11 |      */
12 |     private $stopOnFailure;
13 | 
14 |     /**
15 |      * @var bool
16 |      */
17 |     private $hideCircumventionTip;
18 | 
19 |     /**
20 |      * @var string|null
21 |      */
22 |     private $additionalInfo;
23 | 
24 |     public function __construct(
25 |         bool $stopOnFailure,
26 |         bool $hideCircumventionTip,
27 |         ?string $additionalInfo
28 |     ) {
29 | 
30 |         $this->stopOnFailure = $stopOnFailure;
31 |         $this->hideCircumventionTip = $hideCircumventionTip;
32 |         $this->additionalInfo = $additionalInfo;
33 |     }
34 | 
35 |     public function stopOnFailure(): bool
36 |     {
37 |         return $this->stopOnFailure;
38 |     }
39 | 
40 |     public function hideCircumventionTip(): bool
41 |     {
42 |         return $this->hideCircumventionTip;
43 |     }
44 | 
45 |     public function getAdditionalInfo(): ?string
46 |     {
47 |         return $this->additionalInfo;
48 |     }
49 | }
50 | 


--------------------------------------------------------------------------------
/src/Configuration/Resolver/TaskConfigResolver.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Configuration\Resolver;
 6 | 
 7 | use GrumPHP\Exception\TaskConfigResolverException;
 8 | use GrumPHP\Task\Config\ConfigOptionsResolver;
 9 | use GrumPHP\Task\TaskInterface;
10 | 
11 | class TaskConfigResolver
12 | {
13 |     /**
14 |      * @var array<string, string>
15 |      */
16 |     private $taskMap;
17 | 
18 |     public function __construct(array $taskMap)
19 |     {
20 |         $this->taskMap = $taskMap;
21 |     }
22 | 
23 |     /**
24 |      * @return array<string>
25 |      */
26 |     public function listAvailableTaskNames(): array
27 |     {
28 |         return array_keys($this->taskMap);
29 |     }
30 | 
31 |     public function resolve(string $taskName, array $config): array
32 |     {
33 |         $resolver = $this->fetchByName($taskName);
34 | 
35 |         // Make sure metadata is never a part of the task configuration
36 |         unset($config['metadata']);
37 | 
38 |         return $resolver->resolve($config);
39 |     }
40 | 
41 |     public function fetchByName(string $taskName): ConfigOptionsResolver
42 |     {
43 |         if (!array_key_exists($taskName, $this->taskMap)) {
44 |             throw TaskConfigResolverException::unknownTask($taskName);
45 |         }
46 | 
47 |         $class = $this->taskMap[$taskName];
48 |         if (!class_exists($class) || !is_subclass_of($class, TaskInterface::class)) {
49 |             throw TaskConfigResolverException::unknownClass($class);
50 |         }
51 | 
52 |         return $class::getConfigurableOptions();
53 |     }
54 | }
55 | 


--------------------------------------------------------------------------------
/src/Console/ApplicationConfigurator.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Console;
 6 | 
 7 | use Symfony\Component\Console\Application;
 8 | use Symfony\Component\Console\Input\InputOption;
 9 | 
10 | class ApplicationConfigurator
11 | {
12 |     const APP_NAME = 'GrumPHP';
13 |     const APP_VERSION = '2.13.0';
14 | 
15 |     public function configure(Application $application): void
16 |     {
17 |         $application->setVersion(self::APP_VERSION);
18 |         $application->setName(self::APP_NAME);
19 |         $this->registerInputDefinitions($application);
20 |     }
21 | 
22 |     private function registerInputDefinitions(Application $application): void
23 |     {
24 |         $definition = $application->getDefinition();
25 |         $definition->addOption(
26 |             new InputOption(
27 |                 'config',
28 |                 'c',
29 |                 InputOption::VALUE_REQUIRED,
30 |                 'Path to config'
31 |             )
32 |         );
33 |     }
34 | }
35 | 


--------------------------------------------------------------------------------
/src/Event/Dispatcher/Bridge/SymfonyEventDispatcher.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Event\Dispatcher\Bridge;
 6 | 
 7 | use GrumPHP\Event\Dispatcher\EventDispatcherInterface;
 8 | use GrumPHP\Event\Event;
 9 | use Symfony\Component\EventDispatcher\EventDispatcherInterface as SymfonyLegacyEventDispatcher;
10 | use Symfony\Contracts\EventDispatcher\EventDispatcherInterface as SymfonyEventDispatcherContract;
11 | 
12 | class SymfonyEventDispatcher implements EventDispatcherInterface
13 | {
14 |     /**
15 |      * @var SymfonyLegacyEventDispatcher|SymfonyEventDispatcherContract
16 |      */
17 |     private $dispatcher;
18 | 
19 |     /**
20 |      * @param SymfonyLegacyEventDispatcher|SymfonyEventDispatcherContract $eventDispatcher
21 |      */
22 |     public function __construct($eventDispatcher)
23 |     {
24 |         $this->dispatcher = $eventDispatcher;
25 |     }
26 | 
27 |     public function dispatch(Event $event, ?string $name = null): void
28 |     {
29 |         $interfacesImplemented = class_implements($this->dispatcher);
30 |         if (in_array(SymfonyEventDispatcherContract::class, $interfacesImplemented, true)) {
31 |             /**
32 |              * @psalm-suppress InvalidArgument
33 |              * @psalm-suppress TooManyArguments
34 |              */
35 |             $this->dispatcher->dispatch($event, $name);
36 |             return;
37 |         }
38 | 
39 |         /**
40 |          * @psalm-suppress InvalidArgument
41 |          * @psalm-suppress TooManyArguments
42 |          */
43 |         $this->dispatcher->dispatch($name, $event);
44 |     }
45 | }
46 | 


--------------------------------------------------------------------------------
/src/Event/Dispatcher/EventDispatcherInterface.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Event\Dispatcher;
 6 | 
 7 | use GrumPHP\Event\Event;
 8 | 
 9 | interface EventDispatcherInterface
10 | {
11 |     public function dispatch(Event $event, ?string $name = null): void;
12 | }
13 | 


--------------------------------------------------------------------------------
/src/Event/Event.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Event;
 6 | 
 7 | use Symfony\Contracts\EventDispatcher\Event as SymfonyEventContract;
 8 | use Symfony\Component\EventDispatcher\Event as SymfonyLegacyEvent;
 9 | 
10 | // @codingStandardsIgnoreStart
11 | if (class_exists(SymfonyEventContract::class)) {
12 |     class Event extends SymfonyEventContract
13 |     {
14 |     }
15 | } else {
16 |     class Event extends SymfonyLegacyEvent
17 |     {
18 |     }
19 | }
20 | // @codingStandardsIgnoreEnd
21 | 


--------------------------------------------------------------------------------
/src/Event/RunnerEvent.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Event;
 6 | 
 7 | use GrumPHP\Collection\TaskResultCollection;
 8 | use GrumPHP\Collection\TasksCollection;
 9 | use GrumPHP\Task\Context\ContextInterface;
10 | 
11 | class RunnerEvent extends Event
12 | {
13 |     /**
14 |      * @var TasksCollection
15 |      */
16 |     private $tasks;
17 | 
18 |     /**
19 |      * @var ContextInterface
20 |      */
21 |     private $context;
22 | 
23 |     /**
24 |      * @var TaskResultCollection
25 |      */
26 |     private $taskResults;
27 | 
28 |     public function __construct(TasksCollection $tasks, ContextInterface $context, TaskResultCollection $taskResults)
29 |     {
30 |         $this->tasks = $tasks;
31 |         $this->context = $context;
32 |         $this->taskResults = $taskResults;
33 |     }
34 | 
35 |     public function getTasks(): TasksCollection
36 |     {
37 |         return $this->tasks;
38 |     }
39 | 
40 |     public function getContext(): ContextInterface
41 |     {
42 |         return $this->context;
43 |     }
44 | 
45 |     public function getTaskResults(): TaskResultCollection
46 |     {
47 |         return $this->taskResults;
48 |     }
49 | }
50 | 


--------------------------------------------------------------------------------
/src/Event/RunnerEvents.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Event;
 6 | 
 7 | final class RunnerEvents
 8 | {
 9 |     const RUNNER_RUN = 'grumphp.runner.run';
10 |     const RUNNER_COMPLETE = 'grumphp.runner.complete';
11 |     const RUNNER_FAILED = 'grumphp.runner.failed';
12 | }
13 | 


--------------------------------------------------------------------------------
/src/Event/RunnerFailedEvent.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Event;
 6 | 
 7 | class RunnerFailedEvent extends RunnerEvent
 8 | {
 9 |     public function getMessages(): array
10 |     {
11 |         $messages = [];
12 | 
13 |         foreach ($this->getTaskResults() as $taskResult) {
14 |             if ('' !== $taskResult->getMessage()) {
15 |                 $messages[] = $taskResult->getMessage();
16 |             }
17 |         }
18 | 
19 |         return $messages;
20 |     }
21 | }
22 | 


--------------------------------------------------------------------------------
/src/Event/TaskEvent.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Event;
 6 | 
 7 | use GrumPHP\Task\Context\ContextInterface;
 8 | use GrumPHP\Task\TaskInterface;
 9 | 
10 | class TaskEvent extends Event
11 | {
12 |     /**
13 |      * @var TaskInterface
14 |      */
15 |     private $task;
16 | 
17 |     /**
18 |      * @var ContextInterface
19 |      */
20 |     private $context;
21 | 
22 |     public function __construct(TaskInterface $task, ContextInterface $context)
23 |     {
24 |         $this->task = $task;
25 |         $this->context = $context;
26 |     }
27 | 
28 |     public function getTask(): TaskInterface
29 |     {
30 |         return $this->task;
31 |     }
32 | 
33 |     public function getContext(): ContextInterface
34 |     {
35 |         return $this->context;
36 |     }
37 | }
38 | 


--------------------------------------------------------------------------------
/src/Event/TaskEvents.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Event;
 6 | 
 7 | final class TaskEvents
 8 | {
 9 |     const TASK_RUN = 'grumphp.task.run';
10 |     const TASK_COMPLETE = 'grumphp.task.complete';
11 |     const TASK_FAILED = 'grumphp.task.failed';
12 |     const TASK_SKIPPED = 'grumphp.task.skipped';
13 | }
14 | 


--------------------------------------------------------------------------------
/src/Event/TaskFailedEvent.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Event;
 6 | 
 7 | use Exception;
 8 | use GrumPHP\Task\Context\ContextInterface;
 9 | use GrumPHP\Task\TaskInterface;
10 | 
11 | class TaskFailedEvent extends TaskEvent
12 | {
13 |     /**
14 |      * @var Exception
15 |      */
16 |     private $exception;
17 | 
18 |     public function __construct(TaskInterface $task, ContextInterface $context, Exception $exception)
19 |     {
20 |         parent::__construct($task, $context);
21 | 
22 |         $this->exception = $exception;
23 |     }
24 | 
25 |     public function getException(): Exception
26 |     {
27 |         return $this->exception;
28 |     }
29 | }
30 | 


--------------------------------------------------------------------------------
/src/Exception/ExceptionInterface.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Exception;
 6 | 
 7 | interface ExceptionInterface
 8 | {
 9 | }
10 | 


--------------------------------------------------------------------------------
/src/Exception/ExecutableNotFoundException.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Exception;
 6 | 
 7 | class ExecutableNotFoundException extends RuntimeException
 8 | {
 9 |     public static function forCommand(string $command): self
10 |     {
11 |         return new self(
12 |             sprintf('The executable for "%s" could not be found.', $command)
13 |         );
14 |     }
15 | }
16 | 


--------------------------------------------------------------------------------
/src/Exception/FileNotFoundException.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Exception;
 6 | 
 7 | class FileNotFoundException extends RuntimeException
 8 | {
 9 |     public function __construct(string $path)
10 |     {
11 |         parent::__construct(sprintf('File "%s" doesn\'t exists.', $path));
12 |     }
13 | }
14 | 


--------------------------------------------------------------------------------
/src/Exception/FixerException.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Exception;
 6 | 
 7 | use GrumPHP\Formatter\RawProcessFormatter;
 8 | use Symfony\Component\Process\Process;
 9 | 
10 | class FixerException extends RuntimeException
11 | {
12 |     public static function fromProcess(Process $process): self
13 |     {
14 |         return new self(
15 |             'Error while fixing: '.
16 |             $process->getCommandLine()
17 |             . PHP_EOL
18 |             . (new RawProcessFormatter())->format($process)
19 |         );
20 |     }
21 | }
22 | 


--------------------------------------------------------------------------------
/src/Exception/InvalidArgumentException.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Exception;
 6 | 
 7 | class InvalidArgumentException extends RuntimeException
 8 | {
 9 |     public static function unknownTestSuite(string $testSuiteName): self
10 |     {
11 |         return new self(sprintf('Unknown testsuite specified: %s', $testSuiteName));
12 |     }
13 | }
14 | 


--------------------------------------------------------------------------------
/src/Exception/ParallelException.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Exception;
 6 | 
 7 | class ParallelException extends RuntimeException
 8 | {
 9 |     public static function fromThrowable(\Throwable $error): self
10 |     {
11 |         return new self($error->getMessage(), (int)$error->getCode(), $error);
12 |     }
13 | 
14 |     public static function fromVerboseThrowable(\Throwable $error): self
15 |     {
16 |         return new self(
17 |             $error->getMessage() . PHP_EOL . $error->getTraceAsString() . PHP_EOL . (string) $error->getPrevious(),
18 |             (int)$error->getCode(),
19 |             $error
20 |         );
21 |     }
22 | }
23 | 


--------------------------------------------------------------------------------
/src/Exception/PlatformException.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Exception;
 6 | 
 7 | use GrumPHP\Util\Platform;
 8 | use Symfony\Component\Process\Process;
 9 | 
10 | class PlatformException extends RuntimeException
11 | {
12 |     public static function commandLineStringLimit(Process $process): self
13 |     {
14 |         return new self(sprintf(
15 |             'The Windows maximum amount of %s input characters exceeded while running process: %s ...',
16 |             Platform::WINDOWS_COMMANDLINE_STRING_LIMITATION,
17 |             substr($process->getCommandLine(), 0, 75)
18 |         ));
19 |     }
20 | }
21 | 


--------------------------------------------------------------------------------
/src/Exception/ProcessException.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Exception;
 6 | 
 7 | class ProcessException extends RuntimeException
 8 | {
 9 |     public static function tmpFileCouldNotBeCreated(): self
10 |     {
11 |         return new self(
12 |             'The process requires a temporary file in order to run. We could not create one.'
13 |             . 'Please check your ini setting!'
14 |         );
15 |     }
16 | }
17 | 


--------------------------------------------------------------------------------
/src/Exception/RuntimeException.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Exception;
 6 | 
 7 | use Exception;
 8 | use GrumPHP\Task\TaskInterface;
 9 | use RuntimeException as BaseRuntimeException;
10 | 
11 | class RuntimeException extends BaseRuntimeException implements ExceptionInterface
12 | {
13 |     public static function fromAnyException(Exception $e): self
14 |     {
15 |         return new self($e->getMessage(), (int)$e->getCode(), $e);
16 |     }
17 | 
18 |     public static function invalidTaskReturnType(TaskInterface $task): self
19 |     {
20 |         return new self(sprintf('The %s task did not return a TaskResult.', $task->getConfig()->getName()));
21 |     }
22 | }
23 | 


--------------------------------------------------------------------------------
/src/Exception/TaskConfigResolverException.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Exception;
 6 | 
 7 | use GrumPHP\Task\TaskInterface;
 8 | 
 9 | class TaskConfigResolverException extends RuntimeException
10 | {
11 |     public static function unknownTask(string $task): self
12 |     {
13 |         return new self('Could not load config resolver for task: "'.$task.'". The task is not known.');
14 |     }
15 | 
16 |     public static function unknownClass(string $class): self
17 |     {
18 |         return new self(
19 |             sprintf(
20 |                 'Could not load config resolver for class: "%s". Expected an instance of: "%s"',
21 |                 $class,
22 |                 TaskInterface::class
23 |             )
24 |         );
25 |     }
26 | }
27 | 


--------------------------------------------------------------------------------
/src/Extension/ExtensionInterface.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Extension;
 6 | 
 7 | /**
 8 |  * Registers your own GrumPHP.
 9 |  */
10 | interface ExtensionInterface
11 | {
12 |     /**
13 |      * Return a list of additional symfony/conso:e service imports that
14 |      * GrumPHP needs to perform after loading all internal configurations.
15 |      *
16 |      * We support following loaders: YAML, XML, INI, GLOB, DIR
17 |      *
18 |      * More info
19 |      * @link https://symfony.com/doc/current/service_container.html
20 |      *
21 |      * @return iterable<string>
22 |      */
23 |     public function imports(): iterable;
24 | }
25 | 


--------------------------------------------------------------------------------
/src/Fixer/FixResult.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Fixer;
 6 | 
 7 | /**
 8 |  * @template TResult
 9 |  * @psalm-readonly
10 |  */
11 | class FixResult
12 | {
13 |     /**
14 |      * @var \Throwable|null
15 |      */
16 |     private $error;
17 | 
18 |     /**
19 |      * @var TResult|null
20 |      */
21 |     private $result;
22 | 
23 |     /**
24 |      * @param TResult|null $result
25 |      */
26 |     private function __construct($result, ?\Throwable $error)
27 |     {
28 |         $this->error = $error;
29 |         $this->result = $result;
30 |     }
31 | 
32 |     public static function failed(\Throwable $error): self
33 |     {
34 |         return new self(null, $error);
35 |     }
36 | 
37 |     /**
38 |      * @param mixed $result
39 |      */
40 |     public static function success($result): self
41 |     {
42 |         return new self($result, null);
43 |     }
44 | 
45 |     public function ok(): bool
46 |     {
47 |         return null === $this->error;
48 |     }
49 | 
50 |     /**
51 |      * @return TResult|null
52 |      */
53 |     public function result()
54 |     {
55 |         return $this->result;
56 |     }
57 | 
58 |     public function error(): ?\Throwable
59 |     {
60 |         return $this->error;
61 |     }
62 | }
63 | 


--------------------------------------------------------------------------------
/src/Fixer/Provider/FixableProcessProvider.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Fixer\Provider;
 6 | 
 7 | use GrumPHP\Exception\FixerException;
 8 | use GrumPHP\Fixer\FixResult;
 9 | use Laravel\SerializableClosure\SerializableClosure;
10 | use Symfony\Component\Process\Process;
11 | 
12 | class FixableProcessProvider
13 | {
14 |     /**
15 |      * @param int[] $successExitCodes
16 |      *
17 |      * @return callable(): FixResult
18 |      */
19 |     public static function provide(string $command, array $successExitCodes = [0]): callable
20 |     {
21 |         return new SerializableClosure(
22 |             static function () use ($command, $successExitCodes): FixResult {
23 |                 $process = Process::fromShellCommandline($command);
24 |                 $process->run();
25 | 
26 |                 if (!in_array($process->getExitCode(), $successExitCodes, true)) {
27 |                     return FixResult::failed(FixerException::fromProcess($process));
28 |                 }
29 | 
30 |                 return FixResult::success($process->getOutput());
31 |             }
32 |         );
33 |     }
34 | }
35 | 


--------------------------------------------------------------------------------
/src/Fixer/Provider/FixableProcessResultProvider.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Fixer\Provider;
 6 | 
 7 | use GrumPHP\Runner\FixableTaskResult;
 8 | use GrumPHP\Runner\TaskResultInterface;
 9 | use Symfony\Component\Process\Process;
10 | 
11 | class FixableProcessResultProvider
12 | {
13 |     /**
14 |      * @param callable(): Process $fixerProcessBuilder
15 |      */
16 |     public static function provide(
17 |         TaskResultInterface $taskResult,
18 |         callable $fixerProcessBuilder,
19 |         array $successExitCodes = [0]
20 |     ): FixableTaskResult {
21 |         $fixerProcess = $fixerProcessBuilder();
22 |         /** @psalm-suppress RedundantConditionGivenDocblockType */
23 |         assert($fixerProcess instanceof Process);
24 | 
25 |         $fixerCommand = $fixerProcess->getCommandLine();
26 |         $fixerMessage = sprintf(
27 |             '%sYou can fix errors by running the following command:%s',
28 |             PHP_EOL . PHP_EOL,
29 |             PHP_EOL . $fixerCommand
30 |         );
31 | 
32 |         return new FixableTaskResult(
33 |             $taskResult->withAppendedMessage($fixerMessage),
34 |             FixableProcessProvider::provide($fixerCommand, $successExitCodes)
35 |         );
36 |     }
37 | }
38 | 


--------------------------------------------------------------------------------
/src/Formatter/PhpCsFixerFormatter.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Formatter;
 6 | 
 7 | use Symfony\Component\Process\Process;
 8 | 
 9 | class PhpCsFixerFormatter implements ProcessFormatterInterface
10 | {
11 |     /**
12 |      * @var int
13 |      */
14 |     private $counter = 0;
15 | 
16 |     /**
17 |      * Resets the internal counter.
18 |      */
19 |     public function resetCounter(): void
20 |     {
21 |         $this->counter = 0;
22 |     }
23 | 
24 |     public function format(Process $process): string
25 |     {
26 |         $output = $process->getOutput();
27 |         if (!$output) {
28 |             return $process->getErrorOutput();
29 |         }
30 | 
31 |         if (!$json = json_decode($output, true)) {
32 |             return $output;
33 |         }
34 | 
35 |         return $this->formatJsonResponse($json);
36 |     }
37 | 
38 |     private function formatJsonResponse(array $json): string
39 |     {
40 |         $formatted = [];
41 |         foreach ($json['files'] as $file) {
42 |             if (!\is_array($file) || !isset($file['name'])) {
43 |                 $formatted[] = 'Invalid file: '.print_r($file, true);
44 |                 continue;
45 |             }
46 | 
47 |             $formatted[] = $this->formatFile($file);
48 |         }
49 | 
50 |         return implode(PHP_EOL, $formatted);
51 |     }
52 | 
53 |     private function formatFile(array $file): string
54 |     {
55 |         if (!isset($file['name'])) {
56 |             return 'Invalid file: '.print_r($file, true);
57 |         }
58 | 
59 |         $hasFixers = isset($file['appliedFixers']);
60 |         $hasDiff = isset($file['diff']);
61 | 
62 |         return sprintf(
63 |             '%s) %s%s%s',
64 |             ++$this->counter,
65 |             $file['name'],
66 |             $hasFixers ? ' ('.implode(', ', $file['appliedFixers']).')' : '',
67 |             $hasDiff ? PHP_EOL.PHP_EOL.$file['diff'] : ''
68 |         );
69 |     }
70 | }
71 | 


--------------------------------------------------------------------------------
/src/Formatter/ProcessFormatterInterface.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Formatter;
 6 | 
 7 | use Symfony\Component\Process\Process;
 8 | 
 9 | interface ProcessFormatterInterface
10 | {
11 |     public function format(Process $process): string;
12 | }
13 | 


--------------------------------------------------------------------------------
/src/Formatter/RawProcessFormatter.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Formatter;
 6 | 
 7 | use Symfony\Component\Process\Process;
 8 | 
 9 | class RawProcessFormatter implements ProcessFormatterInterface
10 | {
11 |     public function format(Process $process): string
12 |     {
13 |         $stdout = $process->getOutput();
14 |         $stderr = $process->getErrorOutput();
15 | 
16 |         return trim($stdout.PHP_EOL.$stderr);
17 |     }
18 | }
19 | 


--------------------------------------------------------------------------------
/src/IO/GitHubActionsIO.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | namespace GrumPHP\IO;
 4 | 
 5 | use Symfony\Component\Console\Output\ConsoleSectionOutput;
 6 | 
 7 | class GitHubActionsIO extends ConsoleIO
 8 | {
 9 |     public function startGroup(string $title): void
10 |     {
11 |         $this->write(['::group::' . $title]);
12 |         parent::startGroup($title);
13 |     }
14 | 
15 |     public function endGroup(): void
16 |     {
17 |         parent::endGroup();
18 |         $this->write(['::endgroup::']);
19 |     }
20 | }
21 | 


--------------------------------------------------------------------------------
/src/IO/IOFactory.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | namespace GrumPHP\IO;
 4 | 
 5 | use OndraM\CiDetector\Ci\GitHubActions;
 6 | use OndraM\CiDetector\CiDetector;
 7 | use Symfony\Component\Console\Input\InputInterface;
 8 | use Symfony\Component\Console\Output\OutputInterface;
 9 | 
10 | class IOFactory
11 | {
12 |     public function __construct(private CiDetector $ciDetector)
13 |     {
14 |     }
15 | 
16 |     public function create(InputInterface $input, OutputInterface $output): IOInterface
17 |     {
18 |         if ($this->ciDetector->isCiDetected()) {
19 |             $platform = $this->ciDetector->detect();
20 | 
21 |             if ($platform instanceof GitHubActions) {
22 |                 return new GitHubActionsIO($input, $output);
23 |             }
24 |         }
25 | 
26 |         return new ConsoleIO($input, $output);
27 |     }
28 | }
29 | 


--------------------------------------------------------------------------------
/src/IO/IOInterface.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\IO;
 6 | 
 7 | use Symfony\Component\Console\Output\ConsoleSectionOutput;
 8 | use Symfony\Component\Console\Style\StyleInterface;
 9 | 
10 | interface IOInterface
11 | {
12 |     public function isInteractive(): bool;
13 | 
14 |     public function isVerbose(): bool;
15 | 
16 |     public function isVeryVerbose(): bool;
17 | 
18 |     public function isDebug(): bool;
19 | 
20 |     public function isDecorated(): bool;
21 | 
22 |     /**
23 |      * @return void
24 |      */
25 |     public function write(array $messages, bool $newline = true);
26 | 
27 |     /**
28 |      * @return void
29 |      */
30 |     public function writeError(array $messages, bool $newline = true);
31 | 
32 |     public function style(): StyleInterface;
33 | 
34 |     public function section(): ConsoleSectionOutput;
35 | 
36 |     public function colorize(array $messages, string $color): array;
37 | 
38 |     public function startGroup(string $title): void;
39 | 
40 |     public function endGroup(): void;
41 | 
42 |     /**
43 |      * @param resource $handle
44 |      */
45 |     public function readCommandInput($handle): string;
46 | }
47 | 


--------------------------------------------------------------------------------
/src/Linter/Json/JsonLintError.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Linter\Json;
 6 | 
 7 | use GrumPHP\Linter\LintError;
 8 | use Seld\JsonLint\ParsingException;
 9 | use SplFileInfo;
10 | 
11 | class JsonLintError extends LintError
12 | {
13 |     public static function fromParsingException(SplFileInfo $file, ParsingException $exception): self
14 |     {
15 |         return new self(LintError::TYPE_ERROR, $exception->getMessage(), $file->getPathname(), 0);
16 |     }
17 | 
18 |     public function __toString(): string
19 |     {
20 |         return sprintf(
21 |             '[%s] %s: %s',
22 |             strtoupper($this->getType()),
23 |             $this->getFile(),
24 |             $this->getError()
25 |         );
26 |     }
27 | }
28 | 


--------------------------------------------------------------------------------
/src/Linter/LintError.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Linter;
 6 | 
 7 | class LintError
 8 | {
 9 |     const TYPE_NONE = 'none';
10 |     const TYPE_WARNING = 'warning';
11 |     const TYPE_ERROR = 'error';
12 |     const TYPE_FATAL = 'fatal';
13 | 
14 |     private $type;
15 |     private $error;
16 |     private $file;
17 |     private $line;
18 | 
19 |     public function __construct(string $type, string $error, string $file, int $line)
20 |     {
21 |         $this->type = $type;
22 |         $this->error = $error;
23 |         $this->file = $file;
24 |         $this->line = $line;
25 |     }
26 | 
27 |     public function getType(): string
28 |     {
29 |         return $this->type;
30 |     }
31 | 
32 |     public function getError(): string
33 |     {
34 |         return $this->error;
35 |     }
36 | 
37 |     public function getFile(): string
38 |     {
39 |         return $this->file;
40 |     }
41 | 
42 |     public function getLine(): int
43 |     {
44 |         return $this->line;
45 |     }
46 | 
47 |     public function __toString(): string
48 |     {
49 |         return sprintf(
50 |             '[%s] %s: %s on line %s',
51 |             strtoupper($this->getType()),
52 |             $this->getFile(),
53 |             $this->getError(),
54 |             $this->getLine()
55 |         );
56 |     }
57 | }
58 | 


--------------------------------------------------------------------------------
/src/Linter/LinterInterface.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Linter;
 6 | 
 7 | use GrumPHP\Collection\LintErrorsCollection;
 8 | use SplFileInfo;
 9 | 
10 | interface LinterInterface
11 | {
12 |     public function lint(SplFileInfo $file): LintErrorsCollection;
13 | 
14 |     public function isInstalled(): bool;
15 | }
16 | 


--------------------------------------------------------------------------------
/src/Linter/Xml/XmlLintError.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Linter\Xml;
 6 | 
 7 | use GrumPHP\Linter\LintError;
 8 | use LibXMLError;
 9 | 
10 | class XmlLintError extends LintError
11 | {
12 |     /**
13 |      * @var int
14 |      */
15 |     private $code;
16 | 
17 |     /**
18 |      * @var int
19 |      */
20 |     private $column;
21 | 
22 |     public function __construct(
23 |         string $type,
24 |         int $code,
25 |         string $error,
26 |         string $file,
27 |         int $line,
28 |         int $column
29 |     ) {
30 |         parent::__construct($type, $error, $file, $line);
31 |         $this->code = $code;
32 |         $this->column = $column;
33 |     }
34 | 
35 |     public static function fromLibXmlError(LibXMLError $error): self
36 |     {
37 |         $type = LintError::TYPE_NONE;
38 |         switch ($error->level) {
39 |             case LIBXML_ERR_WARNING:
40 |                 $type = LintError::TYPE_WARNING;
41 |                 break;
42 |             case LIBXML_ERR_FATAL:
43 |                 $type = LintError::TYPE_FATAL;
44 |                 break;
45 |             case LIBXML_ERR_ERROR:
46 |                 $type = LintError::TYPE_ERROR;
47 |                 break;
48 |         }
49 | 
50 |         return new self($type, $error->code, $error->message, $error->file, $error->line, $error->column);
51 |     }
52 | 
53 |     public function getCode(): int
54 |     {
55 |         return $this->code;
56 |     }
57 | 
58 |     public function getColumn(): int
59 |     {
60 |         return $this->column;
61 |     }
62 | 
63 |     public function __toString(): string
64 |     {
65 |         return sprintf(
66 |             '[%s] %s: %s (%s) on line %s,%s',
67 |             strtoupper($this->getType()),
68 |             $this->getFile(),
69 |             $this->getError(),
70 |             $this->getCode() ?: 0,
71 |             $this->getLine(),
72 |             $this->getColumn() ?: 0
73 |         );
74 |     }
75 | }
76 | 


--------------------------------------------------------------------------------
/src/Linter/Yaml/YamlLintError.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Linter\Yaml;
 6 | 
 7 | use GrumPHP\Linter\LintError;
 8 | use Symfony\Component\Yaml\Exception\ParseException;
 9 | 
10 | class YamlLintError extends LintError
11 | {
12 |     /**
13 |      * @var string
14 |      */
15 |     private $snippet;
16 | 
17 |     public function __construct(string $type, string $error, string $file, int $line = -1, string $snippet = '')
18 |     {
19 |         parent::__construct($type, $error, $file, $line);
20 |         $this->snippet = $snippet;
21 |     }
22 | 
23 |     public static function fromParseException(ParseException $exception): self
24 |     {
25 |         return new self(
26 |             LintError::TYPE_ERROR,
27 |             $exception->getMessage(),
28 |             $exception->getParsedFile(),
29 |             $exception->getParsedLine(),
30 |             $exception->getSnippet()
31 |         );
32 |     }
33 | 
34 |     public function getSnippet(): string
35 |     {
36 |         return $this->snippet;
37 |     }
38 | 
39 |     public function __toString(): string
40 |     {
41 |         return sprintf('[%s] %s', strtoupper($this->getType()), $this->getError());
42 |     }
43 | }
44 | 


--------------------------------------------------------------------------------
/src/Locator/AsciiLocator.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Locator;
 6 | 
 7 | use GrumPHP\Configuration\Model\AsciiConfig;
 8 | use GrumPHP\Util\Filesystem;
 9 | use GrumPHP\Util\Paths;
10 | use SplFileInfo;
11 | 
12 | class AsciiLocator
13 | {
14 |     /**
15 |      * @var AsciiConfig
16 |      */
17 |     private $config;
18 | 
19 |     /**
20 |      * @var Filesystem
21 |      */
22 |     private $filesystem;
23 | 
24 |     /**
25 |      * @var Paths
26 |      */
27 |     private $paths;
28 | 
29 |     public function __construct(AsciiConfig $config, Filesystem $filesystem, Paths $paths)
30 |     {
31 |         $this->config = $config;
32 |         $this->filesystem = $filesystem;
33 |         $this->paths = $paths;
34 |     }
35 | 
36 |     public function locate(string $resource): string
37 |     {
38 |         $file = $this->config->fetchResource($resource);
39 | 
40 |         // Disabled:
41 |         if (null === $file) {
42 |             return '';
43 |         }
44 | 
45 |         // Specified by user:
46 |         if ($this->filesystem->exists($file)) {
47 |             return $this->filesystem->readFromFileInfo(new SplFileInfo($file));
48 |         }
49 | 
50 |         // Embedded ASCII art:
51 |         $embeddedFile = $this->filesystem->buildPath($this->paths->getInternalAsciiPath(), $file);
52 |         if ($this->filesystem->exists($embeddedFile)) {
53 |             return $this->filesystem->readFromFileInfo(new SplFileInfo($embeddedFile));
54 |         }
55 | 
56 |         // Error:
57 |         return sprintf('ASCII file %s could not be found.', $file);
58 |     }
59 | }
60 | 


--------------------------------------------------------------------------------
/src/Locator/EnrichedGuessedPathsFromDotEnvLocator.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Locator;
 6 | 
 7 | use GrumPHP\Configuration\GuessedPaths;
 8 | use GrumPHP\Util\Filesystem;
 9 | 
10 | /**
11 |  * @psalm-suppress RedundantCast - We don't want to blindly assume variables in $_SERVER are from the string type.
12 |  */
13 | class EnrichedGuessedPathsFromDotEnvLocator
14 | {
15 |     /**
16 |      * @var Filesystem
17 |      */
18 |     private $filesystem;
19 | 
20 |     public function __construct(Filesystem $filesystem)
21 |     {
22 |         $this->filesystem = $filesystem;
23 |     }
24 | 
25 |     public function locate(GuessedPaths $guessedPaths): GuessedPaths
26 |     {
27 |         $workingDir = $guessedPaths->getWorkingDir();
28 |         $projectDir = $this->filesystem->makePathAbsolute(
29 |             (string) ($_SERVER['GRUMPHP_PROJECT_DIR'] ?? $guessedPaths->getProjectDir()),
30 |             $workingDir
31 |         );
32 |         $gitWorkingDir = $this->filesystem->makePathAbsolute(
33 |             (string) ($_SERVER['GRUMPHP_GIT_WORKING_DIR'] ?? $guessedPaths->getGitWorkingDir()),
34 |             $workingDir
35 |         );
36 |         $gitRepositoryDir = $this->filesystem->makePathAbsolute(
37 |             (string) ($_SERVER['GRUMPHP_GIT_REPOSITORY_DIR'] ?? $guessedPaths->getGitRepositoryDir()),
38 |             $workingDir
39 |         );
40 |         $binDir = $this->filesystem->makePathAbsolute(
41 |             (string) ($_SERVER['GRUMPHP_BIN_DIR'] ?? $guessedPaths->getBinDir()),
42 |             $workingDir
43 |         );
44 | 
45 |         return new GuessedPaths(
46 |             $gitWorkingDir,
47 |             $gitRepositoryDir,
48 |             $workingDir,
49 |             $projectDir,
50 |             $binDir,
51 |             $guessedPaths->getComposerFile(),
52 |             $guessedPaths->getConfigFile()
53 |         );
54 |     }
55 | }
56 | 


--------------------------------------------------------------------------------
/src/Locator/ExternalCommand.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Locator;
 6 | 
 7 | use GrumPHP\Exception\ExecutableNotFoundException;
 8 | use GrumPHP\Util\Paths;
 9 | use GrumPHP\Util\Platform;
10 | use Symfony\Component\Process\ExecutableFinder;
11 | 
12 | class ExternalCommand
13 | {
14 |     /**
15 |      * @var string
16 |      */
17 |     protected $binDir;
18 | 
19 |     /**
20 |      * @var ExecutableFinder
21 |      */
22 |     protected $executableFinder;
23 | 
24 |     public function __construct(string $binDir, ExecutableFinder $executableFinder)
25 |     {
26 |         $this->binDir = rtrim($binDir, '/\\');
27 |         $this->executableFinder = $executableFinder;
28 |     }
29 | 
30 |     public static function loadWithPaths(Paths $paths, ExecutableFinder $executableFinder): self
31 |     {
32 |         return new self(
33 |             $paths->getBinDir(),
34 |             $executableFinder
35 |         );
36 |     }
37 | 
38 |     public function locate(string $command): string
39 |     {
40 |         $suffixes = Platform::isWindows() ? ['.bat', '', '.phar'] : ['', '.phar'];
41 |         foreach ($suffixes as $suffix) {
42 |             $cmdName = $command . $suffix;
43 |             $executable = $this->executableFinder->find($cmdName, null, [$this->binDir]);
44 | 
45 |             if ($executable) {
46 |                 return $executable;
47 |             }
48 |         }
49 | 
50 |         throw ExecutableNotFoundException::forCommand($command);
51 |     }
52 | }
53 | 


--------------------------------------------------------------------------------
/src/Locator/GitRepositoryLocator.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Locator;
 6 | 
 7 | use Gitonomy\Git\Repository;
 8 | use GrumPHP\Util\Paths;
 9 | 
10 | class GitRepositoryLocator
11 | {
12 |     /**
13 |      * @var Paths
14 |      */
15 |     private $paths;
16 | 
17 |     public function __construct(Paths $paths)
18 |     {
19 |         $this->paths = $paths;
20 |     }
21 | 
22 |     public function locate(array $options): Repository
23 |     {
24 |         return new Repository(
25 |             $this->paths->getGitRepositoryDir(),
26 |             array_merge(
27 |                 [
28 |                     'working_dir' => $this->paths->getGitWorkingDir(),
29 |                 ],
30 |                 $options
31 |             )
32 |         );
33 |     }
34 | }
35 | 


--------------------------------------------------------------------------------
/src/Locator/GitWorkingDirLocator.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Locator;
 6 | 
 7 | use GrumPHP\Collection\ProcessArgumentsCollection;
 8 | use GrumPHP\Exception\RuntimeException;
 9 | use GrumPHP\Process\ProcessFactory;
10 | use Symfony\Component\Process\ExecutableFinder;
11 | 
12 | class GitWorkingDirLocator
13 | {
14 |     /**
15 |      * @var ExecutableFinder
16 |      */
17 |     private $executableFinder;
18 | 
19 |     public function __construct(ExecutableFinder $executableFinder)
20 |     {
21 |         $this->executableFinder = $executableFinder;
22 |     }
23 | 
24 |     public function locate(): string
25 |     {
26 |         $arguments = ProcessArgumentsCollection::forExecutable((string) $this->executableFinder->find('git', 'git'));
27 |         $arguments->add('rev-parse');
28 |         $arguments->add('--show-toplevel');
29 | 
30 |         $process = ProcessFactory::fromArguments($arguments);
31 |         $process->run();
32 | 
33 |         if (!$process->isSuccessful()) {
34 |             throw new RuntimeException(
35 |                 'The git directory could not be found. Did you initialize git? ('.$process->getErrorOutput().')'
36 |             );
37 |         }
38 | 
39 |         return trim($process->getOutput());
40 |     }
41 | }
42 | 


--------------------------------------------------------------------------------
/src/Locator/ListedFiles.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Locator;
 6 | 
 7 | use GrumPHP\Collection\FilesCollection;
 8 | use GrumPHP\Util\Paths;
 9 | use Symfony\Component\Finder\SplFileInfo;
10 | 
11 | class ListedFiles
12 | {
13 |     /**
14 |      * @var Paths
15 |      */
16 |     private $paths;
17 | 
18 |     public function __construct(Paths $paths)
19 |     {
20 |         $this->paths = $paths;
21 |     }
22 | 
23 |     public function locate(string $fileList): FilesCollection
24 |     {
25 |         $filePaths = preg_split("/\r\n|\n|\r/", $fileList);
26 | 
27 |         $files = [];
28 |         foreach (array_filter($filePaths) as $file) {
29 |             $relativeFile = $this->paths->makePathRelativeToProjectDir($file);
30 |             $files[] = new SplFileInfo($relativeFile, dirname($relativeFile), $relativeFile);
31 |         }
32 | 
33 |         return new FilesCollection($files);
34 |     }
35 | }
36 | 


--------------------------------------------------------------------------------
/src/Locator/RegisteredFiles.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Locator;
 6 | 
 7 | use GrumPHP\Collection\FilesCollection;
 8 | use GrumPHP\Git\GitRepository;
 9 | use GrumPHP\Util\Paths;
10 | 
11 | class RegisteredFiles
12 | {
13 |     /**
14 |      * @var GitRepository
15 |      */
16 |     private $repository;
17 | 
18 |     /**
19 |      * @var Paths
20 |      */
21 |     private $paths;
22 | 
23 |     /**
24 |      * @var ListedFiles
25 |      */
26 |     private $listedFiles;
27 | 
28 |     public function __construct(GitRepository $repository, Paths $paths, ListedFiles $listedFiles)
29 |     {
30 |         $this->repository = $repository;
31 |         $this->paths = $paths;
32 |         $this->listedFiles = $listedFiles;
33 |     }
34 | 
35 |     public function locate(): FilesCollection
36 |     {
37 |         // Make sure to only return the files that are registered to GIT inside current project directory:
38 |         $allFiles = trim((string) $this->repository->run('ls-files', [$this->paths->getProjectDir()]));
39 | 
40 |         return $this->listedFiles->locate($allFiles);
41 |     }
42 | }
43 | 


--------------------------------------------------------------------------------
/src/Locator/StdInFiles.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Locator;
 6 | 
 7 | use GrumPHP\Collection\FilesCollection;
 8 | 
 9 | class StdInFiles
10 | {
11 |     /**
12 |      * @var ChangedFiles
13 |      */
14 |     private $changedFilesLocator;
15 | 
16 |     /**
17 |      * @var ListedFiles
18 |      */
19 |     private $listedFiles;
20 | 
21 |     public function __construct(
22 |         ChangedFiles $changedFilesLocator,
23 |         ListedFiles $listedFiles
24 |     ) {
25 |         $this->changedFilesLocator = $changedFilesLocator;
26 |         $this->listedFiles = $listedFiles;
27 |     }
28 | 
29 |     public function locate(string $stdIn): FilesCollection
30 |     {
31 |         if (preg_match('/^diff --git/', $stdIn)) {
32 |             return $this->changedFilesLocator->locateFromRawDiffInput($stdIn);
33 |         }
34 | 
35 |         return $this->listedFiles->locate($stdIn);
36 |     }
37 | }
38 | 


--------------------------------------------------------------------------------
/src/Parser/ParseError.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Parser;
 6 | 
 7 | class ParseError
 8 | {
 9 |     const TYPE_NOTICE = 'notice';
10 |     const TYPE_WARNING = 'warning';
11 |     const TYPE_ERROR = 'error';
12 |     const TYPE_FATAL = 'fatal';
13 | 
14 |     private $type;
15 |     private $error;
16 |     private $file;
17 |     private $line;
18 | 
19 |     public function __construct(string $type, string $error, string $file, int $line = -1)
20 |     {
21 |         $this->type = $type;
22 |         $this->error = $error;
23 |         $this->file = $file;
24 |         $this->line = $line;
25 |     }
26 | 
27 |     public function getType(): string
28 |     {
29 |         return $this->type;
30 |     }
31 | 
32 |     public function getError(): string
33 |     {
34 |         return $this->error;
35 |     }
36 | 
37 |     public function getFile(): string
38 |     {
39 |         return $this->file;
40 |     }
41 | 
42 |     public function getLine(): int
43 |     {
44 |         return $this->line;
45 |     }
46 | 
47 |     public function __toString(): string
48 |     {
49 |         if ($this->getLine() < 0) {
50 |             return sprintf(
51 |                 '[%s] %s: %s',
52 |                 strtoupper($this->getType()),
53 |                 $this->getFile(),
54 |                 $this->getError()
55 |             );
56 |         }
57 | 
58 |         return sprintf(
59 |             '[%s] %s: %s on line %d',
60 |             strtoupper($this->getType()),
61 |             $this->getFile(),
62 |             $this->getError(),
63 |             $this->getLine()
64 |         );
65 |     }
66 | }
67 | 


--------------------------------------------------------------------------------
/src/Parser/ParserInterface.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Parser;
 6 | 
 7 | use GrumPHP\Collection\ParseErrorsCollection;
 8 | use SplFileInfo;
 9 | 
10 | interface ParserInterface
11 | {
12 |     public function parse(SplFileInfo $file): ParseErrorsCollection;
13 | 
14 |     public function isInstalled(): bool;
15 | }
16 | 


--------------------------------------------------------------------------------
/src/Parser/Php/Container/VisitorContainer.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Parser\Php\Container;
 6 | 
 7 | use PhpParser\NodeVisitor;
 8 | use Psr\Container\ContainerInterface;
 9 | use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
10 | 
11 | /**
12 |  * This class is added to make sure the Symfony container doesn't get serialized for the phpparser task.
13 |  * It will only contain an emty state of the visitor instances that are configured.
14 |  * Every time a visitor is loaded, a cloned version will be provided instead.
15 |  */
16 | class VisitorContainer implements ContainerInterface
17 | {
18 |     /**
19 |      * @var array<string, NodeVisitor>
20 |      */
21 |     private $instances;
22 | 
23 |     public function __construct(array $instances)
24 |     {
25 |         $this->instances = $instances;
26 |     }
27 | 
28 |     /**
29 |      * Always provide a cloned version of the visitor to make sure all properties get reset after a parser run.
30 |      * @param string $id
31 |      */
32 |     public function get($id): NodeVisitor
33 |     {
34 |         if (!$this->has($id)) {
35 |             throw new ServiceNotFoundException($id);
36 |         }
37 | 
38 |         return clone $this->instances[$id];
39 |     }
40 | 
41 |     /**
42 |      * @param string $id
43 |      */
44 |     public function has($id): bool
45 |     {
46 |         return array_key_exists($id, $this->instances);
47 |     }
48 | }
49 | 


--------------------------------------------------------------------------------
/src/Parser/Php/Context/ParserContext.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Parser\Php\Context;
 6 | 
 7 | use GrumPHP\Collection\ParseErrorsCollection;
 8 | use SplFileInfo;
 9 | 
10 | class ParserContext
11 | {
12 |     /**
13 |      * @var SplFileInfo
14 |      */
15 |     private $file;
16 | 
17 |     /**
18 |      * @var ParseErrorsCollection
19 |      */
20 |     private $errors;
21 | 
22 |     /**
23 |      * ParserContext constructor.
24 |      */
25 |     public function __construct(SplFileInfo $file, ParseErrorsCollection $errors)
26 |     {
27 |         $this->file = $file;
28 |         $this->errors = $errors;
29 |     }
30 | 
31 |     public function getFile(): SplFileInfo
32 |     {
33 |         return $this->file;
34 |     }
35 | 
36 |     public function getErrors(): ParseErrorsCollection
37 |     {
38 |         return $this->errors;
39 |     }
40 | }
41 | 


--------------------------------------------------------------------------------
/src/Parser/Php/Factory/ParserFactory.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Parser\Php\Factory;
 6 | 
 7 | use PhpParser\ParserFactory as PhpParserFactory;
 8 | use PhpParser\PhpVersion;
 9 | 
10 | class ParserFactory
11 | {
12 |     public function createFromOptions(array $options): \PhpParser\Parser
13 |     {
14 |         $version = $options['php_version'] ?? null;
15 | 
16 |         return (new PhpParserFactory())->createForVersion(
17 |             match ($version) {
18 |                 null => PhpVersion::getHostVersion(),
19 |                 'latest' => PhpVersion::getNewestSupported(),
20 |                 default => PhpVersion::fromString($version)
21 |             }
22 |         );
23 |     }
24 | }
25 | 


--------------------------------------------------------------------------------
/src/Parser/Php/Factory/TraverserFactory.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Parser\Php\Factory;
 6 | 
 7 | use GrumPHP\Parser\Php\Configurator\TraverserConfigurator;
 8 | use GrumPHP\Parser\Php\Context\ParserContext;
 9 | use PhpParser\NodeTraverser;
10 | 
11 | class TraverserFactory
12 | {
13 |     /**
14 |      * @var TraverserConfigurator
15 |      */
16 |     private $configurator;
17 | 
18 |     /**
19 |      * TraverserFactory constructor.
20 |      */
21 |     public function __construct(TraverserConfigurator $configurator)
22 |     {
23 |         $this->configurator = $configurator;
24 |     }
25 | 
26 |     /**
27 |      * @throws \GrumPHP\Exception\RuntimeException
28 |      */
29 |     public function createForTaskContext(array $parserOptions, ParserContext $context): NodeTraverser
30 |     {
31 |         $this->configurator->registerOptions($parserOptions);
32 |         $this->configurator->registerContext($context);
33 | 
34 |         $traverser = new NodeTraverser();
35 |         $this->configurator->configure($traverser);
36 | 
37 |         return $traverser;
38 |     }
39 | }
40 | 


--------------------------------------------------------------------------------
/src/Parser/Php/PhpParserError.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Parser\Php;
 6 | 
 7 | use GrumPHP\Parser\ParseError;
 8 | use PhpParser\Error;
 9 | 
10 | class PhpParserError extends ParseError
11 | {
12 |     public static function fromParseException(Error $exception, string $filename): self
13 |     {
14 |         return new self(
15 |             ParseError::TYPE_FATAL,
16 |             $exception->getRawMessage(),
17 |             $filename,
18 |             $exception->getStartLine()
19 |         );
20 |     }
21 | }
22 | 


--------------------------------------------------------------------------------
/src/Parser/Php/Visitor/AbstractVisitor.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Parser\Php\Visitor;
 6 | 
 7 | use GrumPHP\Parser\ParseError;
 8 | use GrumPHP\Parser\Php\Context\ParserContext;
 9 | use GrumPHP\Parser\Php\PhpParserError;
10 | use PhpParser\NodeVisitorAbstract;
11 | 
12 | /**
13 |  * @psalm-suppress MissingConstructor
14 |  */
15 | class AbstractVisitor extends NodeVisitorAbstract implements ContextAwareVisitorInterface
16 | {
17 |     /**
18 |      * @var ParserContext
19 |      */
20 |     protected $context;
21 | 
22 |     public function setContext(ParserContext $context): void
23 |     {
24 |         $this->context = $context;
25 |     }
26 | 
27 |     protected function addError(string $message, int $line = -1, string $type = ParseError::TYPE_ERROR): void
28 |     {
29 |         $errors = $this->context->getErrors();
30 |         $fileName = $this->context->getFile()->getPathname();
31 |         $errors->add(new PhpParserError($type, $message, $fileName, $line));
32 |     }
33 | }
34 | 


--------------------------------------------------------------------------------
/src/Parser/Php/Visitor/ConfigurableVisitorInterface.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Parser\Php\Visitor;
 6 | 
 7 | use PhpParser\NodeVisitor;
 8 | 
 9 | interface ConfigurableVisitorInterface extends NodeVisitor
10 | {
11 |     public function configure(array $options): void;
12 | }
13 | 


--------------------------------------------------------------------------------
/src/Parser/Php/Visitor/ContextAwareVisitorInterface.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Parser\Php\Visitor;
 6 | 
 7 | use GrumPHP\Parser\Php\Context\ParserContext;
 8 | use PhpParser\NodeVisitor;
 9 | 
10 | interface ContextAwareVisitorInterface extends NodeVisitor
11 | {
12 |     public function setContext(ParserContext $context): void;
13 | }
14 | 


--------------------------------------------------------------------------------
/src/Parser/Php/Visitor/DeclareStrictTypesVisitor.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Parser\Php\Visitor;
 6 | 
 7 | use PhpParser\Node;
 8 | 
 9 | class DeclareStrictTypesVisitor extends AbstractVisitor
10 | {
11 |     /**
12 |      * @var bool
13 |      */
14 |     private $hasStrictType = false;
15 | 
16 |     public function leaveNode(Node $node): void
17 |     {
18 |         if (!$node instanceof Node\Stmt\Declare_) {
19 |             return;
20 |         }
21 | 
22 |         foreach ($node->declares as $id => $declare) {
23 |             // In PhpParser 3 and lower the key used in a `declare()` statement
24 |             // is represented as a string value. Starting with PhpParser 4 this
25 |             // key is represented by a 'PhpParser\Node\Identifier' object. To
26 |             // support backwards compatibility with older versions the object
27 |             // can be cast to a string to get the original string value.
28 |             if ((string) $declare->key !== 'strict_types') {
29 |                 continue;
30 |             }
31 | 
32 |             /** @psalm-suppress UndefinedPropertyFetch */
33 |             $this->hasStrictType = $declare->value->value === 1;
34 |         }
35 |     }
36 | 
37 |     public function afterTraverse(array $nodes): void
38 |     {
39 |         if (!$this->hasStrictType) {
40 |             $this->addError('No "declare(strict_types = 1)" found in file!');
41 |         }
42 |     }
43 | }
44 | 


--------------------------------------------------------------------------------
/src/Parser/Php/Visitor/ForbiddenClassMethodCallsVisitor.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Parser\Php\Visitor;
 6 | 
 7 | use GrumPHP\Parser\ParseError;
 8 | use PhpParser\Node;
 9 | use Symfony\Component\OptionsResolver\OptionsResolver;
10 | 
11 | class ForbiddenClassMethodCallsVisitor extends AbstractVisitor implements ConfigurableVisitorInterface
12 | {
13 |     /**
14 |      * @var array
15 |      */
16 |     private $blacklist = [];
17 | 
18 |     public function configure(array $options): void
19 |     {
20 |         $resolver = new OptionsResolver();
21 | 
22 |         $resolver->setDefaults([
23 |             'blacklist' => [],
24 |         ]);
25 | 
26 |         $resolver->setAllowedTypes('blacklist', ['array']);
27 | 
28 |         $config = $resolver->resolve($options);
29 |         $this->blacklist = $config['blacklist'];
30 |     }
31 | 
32 |     /**
33 |      * @psalm-suppress UndefinedPropertyFetch
34 |      * @psalm-suppress PossiblyInvalidCast
35 |      * @psalm-suppress ImplicitToStringCast
36 |      * @psalm-suppress InvalidArgument
37 |      */
38 |     public function leaveNode(Node $node): void
39 |     {
40 |         if (!$node instanceof Node\Expr\MethodCall || !isset($node->var->name)) {
41 |             return;
42 |         }
43 | 
44 |         $variable = $node->var->name;
45 |         $method = $node->name;
46 |         $normalized = sprintf('$%s->%s', $variable, $method);
47 |         if (!\in_array($normalized, $this->blacklist, true)) {
48 |             return;
49 |         }
50 | 
51 |         $this->addError(
52 |             sprintf('Found blacklisted "%s" method call', $normalized),
53 |             $node->getLine(),
54 |             ParseError::TYPE_ERROR
55 |         );
56 |     }
57 | }
58 | 


--------------------------------------------------------------------------------
/src/Parser/Php/Visitor/ForbiddenFunctionCallsVisitor.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Parser\Php\Visitor;
 6 | 
 7 | use PhpParser\Node;
 8 | use Symfony\Component\OptionsResolver\OptionsResolver;
 9 | 
10 | class ForbiddenFunctionCallsVisitor extends AbstractVisitor implements ConfigurableVisitorInterface
11 | {
12 |     /**
13 |      * @var array
14 |      */
15 |     private $blacklist = [];
16 | 
17 |     public function configure(array $options): void
18 |     {
19 |         $resolver = new OptionsResolver();
20 | 
21 |         $resolver->setDefaults([
22 |             'blacklist' => [],
23 |         ]);
24 | 
25 |         $resolver->setAllowedTypes('blacklist', ['array']);
26 | 
27 |         $config = $resolver->resolve($options);
28 |         $this->blacklist = $config['blacklist'];
29 |     }
30 | 
31 |     /**
32 |      * @psalm-suppress UndefinedPropertyFetch
33 |      * @psalm-suppress PossiblyInvalidCast
34 |      */
35 |     public function leaveNode(Node $node): void
36 |     {
37 | 
38 |         if (!$node instanceof Node\Expr\FuncCall || !$node->name instanceof Node\Name) {
39 |             return;
40 |         }
41 | 
42 | 
43 |         $function = (string) $node->name;
44 |         if (!\in_array($function, $this->blacklist, false)) {
45 |             return;
46 |         }
47 | 
48 |         $this->addError(
49 |             sprintf('Found blacklisted "%s" function call', $function),
50 |             $node->getLine()
51 |         );
52 |     }
53 | }
54 | 


--------------------------------------------------------------------------------
/src/Parser/Php/Visitor/ForbiddenStaticMethodCallsVisitor.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Parser\Php\Visitor;
 6 | 
 7 | use GrumPHP\Parser\ParseError;
 8 | use PhpParser\Node;
 9 | use Symfony\Component\OptionsResolver\OptionsResolver;
10 | 
11 | class ForbiddenStaticMethodCallsVisitor extends AbstractVisitor implements ConfigurableVisitorInterface
12 | {
13 |     /**
14 |      * @var array
15 |      */
16 |     private $blacklist = [];
17 | 
18 |     public function configure(array $options): void
19 |     {
20 |         $resolver = new OptionsResolver();
21 | 
22 |         $resolver->setDefaults([
23 |             'blacklist' => [],
24 |         ]);
25 | 
26 |         $resolver->setAllowedTypes('blacklist', ['array']);
27 | 
28 |         $config = $resolver->resolve($options);
29 |         $this->blacklist = $config['blacklist'];
30 |     }
31 | 
32 |     /**
33 |      * @psalm-suppress UndefinedPropertyFetch
34 |      * @psalm-suppress PossiblyInvalidCast
35 |      * @psalm-suppress ImplicitToStringCast
36 |      * @psalm-suppress InvalidArgument
37 |      */
38 |     public function leaveNode(Node $node): void
39 |     {
40 |         if (!$node instanceof Node\Expr\StaticCall || !$node->class instanceof Node\Name) {
41 |             return;
42 |         }
43 | 
44 |         $class = implode('\\', $node->class->getParts());
45 |         $method = $node->name;
46 |         $normalized = sprintf('%s::%s', $class, $method);
47 | 
48 |         if (!\in_array($normalized, $this->blacklist, true)) {
49 |             return;
50 |         }
51 | 
52 |         $this->addError(
53 |             sprintf('Found blacklisted "%s" static method call', $normalized),
54 |             $node->getLine(),
55 |             ParseError::TYPE_ERROR
56 |         );
57 |     }
58 | }
59 | 


--------------------------------------------------------------------------------
/src/Parser/Php/Visitor/NeverUseElseVisitor.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Parser\Php\Visitor;
 6 | 
 7 | use GrumPHP\Parser\ParseError;
 8 | use PhpParser\Node;
 9 | 
10 | class NeverUseElseVisitor extends AbstractVisitor
11 | {
12 |     /**
13 |      * @see http://www.slideshare.net/rdohms/your-code-sucks-lets-fix-it-15471808
14 |      * @see http://www.slideshare.net/guilhermeblanco/object-calisthenics-applied-to-php
15 |      */
16 |     public function leaveNode(Node $node): void
17 |     {
18 |         if (!$node instanceof Node\Stmt\Else_ && !$node instanceof Node\Stmt\ElseIf_) {
19 |             return;
20 |         }
21 | 
22 |         $this->addError(
23 |             sprintf(
24 |                 'Object Calisthenics error: Do not use the "%s" keyword!',
25 |                 $node instanceof  Node\Stmt\ElseIf_ ? 'elseif' : 'else'
26 |             ),
27 |             $node->getLine(),
28 |             ParseError::TYPE_ERROR
29 |         );
30 |     }
31 | }
32 | 


--------------------------------------------------------------------------------
/src/Parser/Php/Visitor/NoExitStatementsVisitor.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Parser\Php\Visitor;
 6 | 
 7 | use GrumPHP\Parser\ParseError;
 8 | use PhpParser\Node;
 9 | 
10 | class NoExitStatementsVisitor extends AbstractVisitor
11 | {
12 |     public function leaveNode(Node $node): void
13 |     {
14 |         if (!$node instanceof Node\Expr\Exit_) {
15 |             return;
16 |         }
17 | 
18 |         $this->addError(
19 |             'Found a forbidden exit statement.',
20 |             $node->getLine(),
21 |             ParseError::TYPE_ERROR
22 |         );
23 |     }
24 | }
25 | 


--------------------------------------------------------------------------------
/src/Process/InputWritingProcessRunner.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Process;
 6 | 
 7 | use Symfony\Component\Process\InputStream;
 8 | use Symfony\Component\Process\Process;
 9 | 
10 | /**
11 |  * This runner can be used to run a process whilst writing data to STDIN
12 |  */
13 | class InputWritingProcessRunner
14 | {
15 |     /**
16 |      * @param callable(): Process $processBuilder
17 |      * @param callable(): \Generator<array-key, string, mixed, void> $writer
18 |      */
19 |     public static function run(callable $processBuilder, callable $writer): Process
20 |     {
21 |         $process = $processBuilder();
22 |         $inputStream = new InputStream();
23 |         $process->setInput($inputStream);
24 |         $process->start();
25 |         foreach ($writer() as $input) {
26 |             $inputStream->write($input);
27 |         }
28 | 
29 |         $inputStream->close();
30 |         $process->wait();
31 | 
32 |         return $process;
33 |     }
34 | }
35 | 


--------------------------------------------------------------------------------
/src/Process/ProcessFactory.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Process;
 6 | 
 7 | use GrumPHP\Collection\ProcessArgumentsCollection;
 8 | use Symfony\Component\Process\Process;
 9 | 
10 | /**
11 |  * @internal
12 |  */
13 | final class ProcessFactory
14 | {
15 |     public static function fromArguments(ProcessArgumentsCollection $arguments): Process
16 |     {
17 |         return new Process($arguments->getValues());
18 |     }
19 | 
20 |     /**
21 |      * @param array|string $arguments
22 |      */
23 |     public static function fromScalar($arguments): Process
24 |     {
25 |         return is_array($arguments) ? new Process($arguments) : Process::fromShellCommandline($arguments);
26 |     }
27 | }
28 | 


--------------------------------------------------------------------------------
/src/Process/TmpFileUsingProcessRunner.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Process;
 6 | 
 7 | use GrumPHP\Exception\ProcessException;
 8 | use Symfony\Component\Process\Process;
 9 | 
10 | /**
11 |  * This runner can be used to run a process whilst creating a file with temporary data.
12 |  * Once the command has finished, the temporary file is removed.
13 |  */
14 | class TmpFileUsingProcessRunner
15 | {
16 |     /**
17 |      * @param callable(string): Process $processBuilder
18 |      * @param callable(): \Generator<array-key, string, mixed, void> $writer
19 |      */
20 |     public static function run(callable $processBuilder, callable $writer): Process
21 |     {
22 |         if (!$tmp = tmpfile()) {
23 |             throw ProcessException::tmpFileCouldNotBeCreated();
24 |         }
25 | 
26 |         $path = stream_get_meta_data($tmp)['uri'] ?? null;
27 |         if (!$path) {
28 |             throw ProcessException::tmpFileCouldNotBeCreated();
29 |         }
30 | 
31 |         foreach ($writer() as $entry) {
32 |             fwrite($tmp, $entry);
33 |         }
34 |         fseek($tmp, 0);
35 | 
36 |         try {
37 |             $process = $processBuilder($path);
38 |             $process->run();
39 |         } finally {
40 |             fclose($tmp);
41 |         }
42 | 
43 |         return $process;
44 |     }
45 | }
46 | 


--------------------------------------------------------------------------------
/src/Runner/Ci/CiDetector.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Runner\Ci;
 6 | 
 7 | use OndraM\CiDetector\CiDetector as RealCiDetector;
 8 | 
 9 | class CiDetector
10 | {
11 |     /**
12 |      * @var RealCiDetector
13 |      */
14 |     private $ciDetector;
15 | 
16 |     /**
17 |      * @var ?bool
18 |      */
19 |     private $ciDetected;
20 | 
21 |     public function __construct(RealCiDetector $ciDetector)
22 |     {
23 |         $this->ciDetector = $ciDetector;
24 |     }
25 | 
26 |     public function isCiDetected(): bool
27 |     {
28 |         if (null === $this->ciDetected) {
29 |             $this->ciDetected = $this->ciDetector->isCiDetected();
30 |         }
31 | 
32 |         return $this->ciDetected;
33 |     }
34 | }
35 | 


--------------------------------------------------------------------------------
/src/Runner/MemoizedTaskResultMap.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Runner;
 6 | 
 7 | final class MemoizedTaskResultMap
 8 | {
 9 |     /**
10 |      * @var array<string, TaskResultInterface>
11 |      */
12 |     private $resultMap = [];
13 | 
14 |     public function onResult(TaskResultInterface $result): void
15 |     {
16 |         $this->resultMap[$result->getTask()->getConfig()->getName()] = $result;
17 |     }
18 | 
19 |     public function contains(string $taskName): bool
20 |     {
21 |         return array_key_exists($taskName, $this->resultMap);
22 |     }
23 | 
24 |     public function get(string $taskName): ?TaskResultInterface
25 |     {
26 |         return $this->resultMap[$taskName] ?? null;
27 |     }
28 | }
29 | 


--------------------------------------------------------------------------------
/src/Runner/Middleware/EventDispatchingRunnerMiddleware.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Runner\Middleware;
 6 | 
 7 | use GrumPHP\Collection\TaskResultCollection;
 8 | use GrumPHP\Event\Dispatcher\EventDispatcherInterface;
 9 | use GrumPHP\Event\RunnerEvent;
10 | use GrumPHP\Event\RunnerEvents;
11 | use GrumPHP\Event\RunnerFailedEvent;
12 | use GrumPHP\Runner\TaskRunnerContext;
13 | 
14 | class EventDispatchingRunnerMiddleware implements RunnerMiddlewareInterface
15 | {
16 |     /**
17 |      * @var EventDispatcherInterface
18 |      */
19 |     private $eventDispatcher;
20 | 
21 |     public function __construct(EventDispatcherInterface $eventDispatcher)
22 |     {
23 |         $this->eventDispatcher = $eventDispatcher;
24 |     }
25 | 
26 |     public function handle(TaskRunnerContext $context, callable $next): TaskResultCollection
27 |     {
28 |         $this->eventDispatcher->dispatch(
29 |             new RunnerEvent($context->getTasks(), $context->getTaskContext(), new TaskResultCollection()),
30 |             RunnerEvents::RUNNER_RUN
31 |         );
32 | 
33 |         /** @var TaskResultCollection $results */
34 |         $results = $next($context);
35 | 
36 |         if ($results->isFailed()) {
37 |             $this->eventDispatcher->dispatch(
38 |                 new RunnerFailedEvent($context->getTasks(), $context->getTaskContext(), $results),
39 |                 RunnerEvents::RUNNER_FAILED
40 |             );
41 | 
42 |             return $results;
43 |         }
44 | 
45 |         $this->eventDispatcher->dispatch(
46 |             new RunnerEvent($context->getTasks(), $context->getTaskContext(), $results),
47 |             RunnerEvents::RUNNER_COMPLETE
48 |         );
49 | 
50 |         return $results;
51 |     }
52 | }
53 | 


--------------------------------------------------------------------------------
/src/Runner/Middleware/FixCodeMiddleware.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Runner\Middleware;
 6 | 
 7 | use GrumPHP\Collection\TaskResultCollection;
 8 | use GrumPHP\Fixer\FixerUpper;
 9 | use GrumPHP\Runner\TaskRunnerContext;
10 | 
11 | class FixCodeMiddleware implements RunnerMiddlewareInterface
12 | {
13 |     /**
14 |      * @var FixerUpper
15 |      */
16 |     private $fixerUpper;
17 | 
18 |     public function __construct(FixerUpper $fixerUpper)
19 |     {
20 |         $this->fixerUpper = $fixerUpper;
21 |     }
22 | 
23 |     public function handle(TaskRunnerContext $context, callable $next): TaskResultCollection
24 |     {
25 |         /** @var TaskResultCollection $results */
26 |         $results = $next($context);
27 | 
28 |         $this->fixerUpper->fix($results);
29 | 
30 |         return $results;
31 |     }
32 | }
33 | 


--------------------------------------------------------------------------------
/src/Runner/Middleware/GroupByPriorityMiddleware.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Runner\Middleware;
 6 | 
 7 | use GrumPHP\Collection\TaskResultCollection;
 8 | use GrumPHP\Configuration\Model\RunnerConfig;
 9 | use GrumPHP\IO\IOInterface;
10 | use GrumPHP\Runner\TaskRunnerContext;
11 | 
12 | class GroupByPriorityMiddleware implements RunnerMiddlewareInterface
13 | {
14 |     /**
15 |      * @var IOInterface
16 |      */
17 |     private $IO;
18 | 
19 |     /**
20 |      * @var RunnerConfig
21 |      */
22 |     private $config;
23 | 
24 |     public function __construct(IOInterface $IO, RunnerConfig $config)
25 |     {
26 |         $this->IO = $IO;
27 |         $this->config = $config;
28 |     }
29 | 
30 |     public function handle(TaskRunnerContext $context, callable $next): TaskResultCollection
31 |     {
32 |         $results = new TaskResultCollection();
33 |         $grouped = $context->getTasks()
34 |            ->sortByPriority()
35 |            ->groupByPriority();
36 | 
37 |         foreach ($grouped as $priority => $tasks) {
38 |             $this->IO->startGroup('Running tasks with priority '.$priority.'!');
39 |             $results = new TaskResultCollection(array_merge(
40 |                 $results->toArray(),
41 |                 $next($context->withTasks($tasks))->toArray()
42 |             ));
43 |             $this->IO->endGroup();
44 | 
45 |             // Stop on failure:
46 |             if ($this->config->stopOnFailure() && $results->isFailed()) {
47 |                 return $results;
48 |             }
49 |         }
50 | 
51 |         return $results;
52 |     }
53 | }
54 | 


--------------------------------------------------------------------------------
/src/Runner/Middleware/ReportingRunnerMiddleware.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Runner\Middleware;
 6 | 
 7 | use GrumPHP\Collection\TaskResultCollection;
 8 | use GrumPHP\Runner\Reporting\RunnerReporter;
 9 | use GrumPHP\Runner\TaskRunnerContext;
10 | 
11 | class ReportingRunnerMiddleware implements RunnerMiddlewareInterface
12 | {
13 |     /**
14 |      * @var RunnerReporter
15 |      */
16 |     private $reporter;
17 | 
18 |     public function __construct(RunnerReporter $reporter)
19 |     {
20 |         $this->reporter = $reporter;
21 |     }
22 | 
23 |     public function handle(TaskRunnerContext $context, callable $next): TaskResultCollection
24 |     {
25 |         $this->reporter->start($context);
26 |         $results = $next($context);
27 |         $this->reporter->finish($context, $results);
28 | 
29 |         return $results;
30 |     }
31 | }
32 | 


--------------------------------------------------------------------------------
/src/Runner/Middleware/ReportingTasksSectionRunnerMiddleware.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Runner\Middleware;
 6 | 
 7 | use GrumPHP\Collection\TaskResultCollection;
 8 | use GrumPHP\Runner\Reporting\TaskResultsReporter;
 9 | use GrumPHP\Runner\TaskRunnerContext;
10 | 
11 | class ReportingTasksSectionRunnerMiddleware implements RunnerMiddlewareInterface
12 | {
13 |     /**
14 |      * @var TaskResultsReporter
15 |      */
16 |     private $reporter;
17 | 
18 |     public function __construct(TaskResultsReporter $reporter)
19 |     {
20 |         $this->reporter = $reporter;
21 |     }
22 | 
23 |     public function handle(TaskRunnerContext $context, callable $next): TaskResultCollection
24 |     {
25 |         return $this->reporter->runInSection(
26 |             /**
27 |              * @return TaskResultCollection
28 |              */
29 |             function () use ($context, $next): TaskResultCollection {
30 |                 $this->reporter->report($context);
31 | 
32 |                 return $next($context);
33 |             }
34 |         );
35 |     }
36 | }
37 | 


--------------------------------------------------------------------------------
/src/Runner/Middleware/RunnerMiddlewareInterface.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Runner\Middleware;
 6 | 
 7 | use GrumPHP\Collection\TaskResultCollection;
 8 | use GrumPHP\Runner\TaskRunnerContext;
 9 | 
10 | interface RunnerMiddlewareInterface
11 | {
12 |     /**
13 |      * @param callable(TaskRunnerContext $info): TaskResultCollection $next
14 |      */
15 |     public function handle(TaskRunnerContext $context, callable $next): TaskResultCollection;
16 | }
17 | 


--------------------------------------------------------------------------------
/src/Runner/Middleware/TasksFilteringRunnerMiddleware.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Runner\Middleware;
 6 | 
 7 | use GrumPHP\Collection\TaskResultCollection;
 8 | use GrumPHP\Collection\TasksCollection;
 9 | use GrumPHP\Runner\TaskRunnerContext;
10 | 
11 | class TasksFilteringRunnerMiddleware implements RunnerMiddlewareInterface
12 | {
13 |     public function handle(TaskRunnerContext $context, callable $next): TaskResultCollection
14 |     {
15 |         return $next(
16 |             $context->withTasks(
17 |                 (new TasksCollection($context->getTasks()->toArray()))
18 |                     ->filterByContext($context->getTaskContext())
19 |                     ->filterByTestSuite($context->getTestSuite())
20 |                     ->filterByTaskNames($context->getTaskNames())
21 |             )
22 |         );
23 |     }
24 | }
25 | 


--------------------------------------------------------------------------------
/src/Runner/Parallel/PoolFactory.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Runner\Parallel;
 6 | 
 7 | use Amp\Parallel\Worker\ContextWorkerPool;
 8 | use Amp\Parallel\Worker\WorkerPool;
 9 | use GrumPHP\Configuration\Model\ParallelConfig;
10 | 
11 | class PoolFactory
12 | {
13 |     private ParallelConfig $config;
14 |     private ?WorkerPool $pool = null;
15 | 
16 |     public function __construct(ParallelConfig $config)
17 |     {
18 |         $this->config = $config;
19 |     }
20 | 
21 |     public function createShared(): WorkerPool
22 |     {
23 |         if (!$this->pool) {
24 |             $this->pool = new ContextWorkerPool(
25 |                 $this->config->getMaxWorkers()
26 |             );
27 |         }
28 | 
29 |         return $this->pool;
30 |     }
31 | }
32 | 


--------------------------------------------------------------------------------
/src/Runner/Parallel/SerializedClosureTask.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | declare(strict_types=1);
 3 | 
 4 | namespace GrumPHP\Runner\Parallel;
 5 | 
 6 | use Amp\Cancellation;
 7 | use Amp\Parallel\Worker\Task;
 8 | use Amp\Sync\Channel;
 9 | use Laravel\SerializableClosure\SerializableClosure;
10 | 
11 | /**
12 |  * @template-covariant TResult
13 |  * @template TReceive
14 |  * @template TSend
15 |  *
16 |  * @implements Task<TResult, TReceive, TSend>
17 |  */
18 | class SerializedClosureTask implements Task
19 | {
20 |     /**
21 |      * @param (\Closure(): TResult) $closure
22 |      */
23 |     private function __construct(
24 |         private string $serializedClosure
25 |     ) {
26 |     }
27 | 
28 |     /**
29 |      * @template CResult
30 |      * @template CReceive
31 |      * @template CSend
32 |      *
33 |      * @param (\Closure(): CResult) $closure
34 |      * @return self<CResult, CReceive, CSend>
35 |      */
36 |     public static function fromClosure(\Closure $closure): self
37 |     {
38 |         return new self(serialize(new SerializableClosure($closure)));
39 |     }
40 | 
41 |     /**
42 |      * @return TResult
43 |      */
44 |     public function run(Channel $channel, Cancellation $cancellation): mixed
45 |     {
46 |         $unserialized = \unserialize($this->serializedClosure, ['allowed_classes' => true]);
47 | 
48 |         if ($unserialized instanceof \__PHP_Incomplete_Class) {
49 |             throw new \Error(
50 |                 'When using a class instance as a callable, the class must be autoloadable'
51 |             );
52 |         }
53 | 
54 |         if (!$unserialized instanceof SerializableClosure) {
55 |             throw new \Error(
56 |                 'This task can only deal with serialized closures. You passed '.get_debug_type($unserialized)
57 |             );
58 |         }
59 | 
60 |         $closure = $unserialized->getClosure();
61 | 
62 |         return $closure();
63 |     }
64 | }
65 | 


--------------------------------------------------------------------------------
/src/Runner/StopOnFailure.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | declare(strict_types=1);
 3 | 
 4 | namespace GrumPHP\Runner;
 5 | 
 6 | use Amp\Cancellation;
 7 | use Amp\DeferredCancellation;
 8 | use GrumPHP\Configuration\Model\RunnerConfig;
 9 | 
10 | final class StopOnFailure
11 | {
12 |     private function __construct(
13 |         private DeferredCancellation $cancellation,
14 |         private bool $enabled
15 |     ) {
16 |     }
17 | 
18 |     public static function dummy(): self
19 |     {
20 |         return new self(
21 |             new DeferredCancellation(),
22 |             false
23 |         );
24 |     }
25 | 
26 |     public static function createFromConfig(RunnerConfig $config): self
27 |     {
28 |         return new self(
29 |             new DeferredCancellation(),
30 |             $config->stopOnFailure()
31 |         );
32 |     }
33 | 
34 |     public function cancellation(): Cancellation
35 |     {
36 |         return $this->cancellation->getCancellation();
37 |     }
38 | 
39 |     public function decideForResult(TaskResultInterface $result): void
40 |     {
41 |         if ($result->hasFailed() && $result->isBlocking()) {
42 |             $this->stop();
43 |         }
44 |     }
45 | 
46 |     public function stop(): void
47 |     {
48 |         if ($this->enabled) {
49 |             $this->cancellation->cancel();
50 |         }
51 |     }
52 | }
53 | 


--------------------------------------------------------------------------------
/src/Runner/TaskHandler/Middleware/ErrorHandlingTaskHandlerMiddleware.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Runner\TaskHandler\Middleware;
 6 | 
 7 | use GrumPHP\Runner\StopOnFailure;
 8 | use function Amp\async;
 9 | use Amp\Future;
10 | use GrumPHP\Exception\PlatformException;
11 | use GrumPHP\Runner\TaskResult;
12 | use GrumPHP\Runner\TaskResultInterface;
13 | use GrumPHP\Runner\TaskRunnerContext;
14 | use GrumPHP\Task\TaskInterface;
15 | 
16 | class ErrorHandlingTaskHandlerMiddleware implements TaskHandlerMiddlewareInterface
17 | {
18 |     public function handle(
19 |         TaskInterface $task,
20 |         TaskRunnerContext $runnerContext,
21 |         StopOnFailure $stopOnFailure,
22 |         callable $next
23 |     ): Future {
24 |         return async(
25 |             static function () use ($task, $runnerContext): TaskResultInterface {
26 |                 $taskContext = $runnerContext->getTaskContext();
27 |                 try {
28 |                     $result = $task->run($taskContext);
29 |                 } catch (PlatformException $e) {
30 |                     return TaskResult::createSkipped($task, $taskContext);
31 |                 } catch (\Throwable $e) {
32 |                     return TaskResult::createFailed($task, $taskContext, $e->getMessage());
33 |                 }
34 | 
35 |                 return $result;
36 |             }
37 |         );
38 |     }
39 | }
40 | 


--------------------------------------------------------------------------------
/src/Runner/TaskHandler/Middleware/MemoizedResultsTaskHandlerMiddleware.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Runner\TaskHandler\Middleware;
 6 | 
 7 | use GrumPHP\Runner\StopOnFailure;
 8 | use function Amp\async;
 9 | use Amp\Future;
10 | use GrumPHP\Runner\MemoizedTaskResultMap;
11 | use GrumPHP\Runner\TaskResult;
12 | use GrumPHP\Runner\TaskResultInterface;
13 | use GrumPHP\Runner\TaskRunnerContext;
14 | use GrumPHP\Task\TaskInterface;
15 | 
16 | class MemoizedResultsTaskHandlerMiddleware implements TaskHandlerMiddlewareInterface
17 | {
18 |     /**
19 |      * @var MemoizedTaskResultMap
20 |      */
21 |     private $resultMap;
22 | 
23 |     public function __construct(MemoizedTaskResultMap $resultMap)
24 |     {
25 |         $this->resultMap = $resultMap;
26 |     }
27 | 
28 |     public function handle(
29 |         TaskInterface $task,
30 |         TaskRunnerContext $runnerContext,
31 |         StopOnFailure $stopOnFailure,
32 |         callable $next
33 |     ): Future {
34 |         return async(
35 |             function () use ($task, $runnerContext, $stopOnFailure, $next) : TaskResultInterface {
36 |                 try {
37 |                     $result = $next($task, $runnerContext, $stopOnFailure)->await();
38 |                 } catch (\Throwable $error) {
39 |                     $result = TaskResult::createFailed($task, $runnerContext->getTaskContext(), $error->getMessage());
40 |                 }
41 | 
42 |                 $this->resultMap->onResult($result);
43 | 
44 |                 return $result;
45 |             }
46 |         );
47 |     }
48 | }
49 | 


--------------------------------------------------------------------------------
/src/Runner/TaskHandler/Middleware/NonBlockingTaskHandlerMiddleware.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Runner\TaskHandler\Middleware;
 6 | 
 7 | use GrumPHP\Runner\StopOnFailure;
 8 | use function Amp\async;
 9 | use Amp\Future;
10 | use GrumPHP\Runner\TaskResult;
11 | use GrumPHP\Runner\TaskResultInterface;
12 | use GrumPHP\Runner\TaskRunnerContext;
13 | use GrumPHP\Task\TaskInterface;
14 | 
15 | class NonBlockingTaskHandlerMiddleware implements TaskHandlerMiddlewareInterface
16 | {
17 |     public function handle(
18 |         TaskInterface $task,
19 |         TaskRunnerContext $runnerContext,
20 |         StopOnFailure $stopOnFailure,
21 |         callable $next
22 |     ): Future {
23 |         return async(
24 |             static function () use ($task, $runnerContext, $next, $stopOnFailure): TaskResultInterface {
25 |                 $result = $next($task, $runnerContext, $stopOnFailure)->await();
26 | 
27 |                 if ($result->isPassed() || $result->isSkipped() || $task->getConfig()->getMetadata()->isBlocking()) {
28 |                     return $result;
29 |                 }
30 | 
31 |                 return TaskResult::createNonBlockingFailed(
32 |                     $result->getTask(),
33 |                     $result->getContext(),
34 |                     $result->getMessage()
35 |                 );
36 |             }
37 |         );
38 |     }
39 | }
40 | 


--------------------------------------------------------------------------------
/src/Runner/TaskHandler/Middleware/ReportingTaskHandlerMiddleware.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Runner\TaskHandler\Middleware;
 6 | 
 7 | use GrumPHP\Runner\StopOnFailure;
 8 | use function Amp\async;
 9 | use Amp\Future;
10 | use GrumPHP\Runner\Reporting\TaskResultsReporter;
11 | use GrumPHP\Runner\TaskResultInterface;
12 | use GrumPHP\Runner\TaskRunnerContext;
13 | use GrumPHP\Task\TaskInterface;
14 | 
15 | class ReportingTaskHandlerMiddleware implements TaskHandlerMiddlewareInterface
16 | {
17 |     /**
18 |      * @var TaskResultsReporter
19 |      */
20 |     private $reporter;
21 | 
22 |     public function __construct(TaskResultsReporter $reporter)
23 |     {
24 |         $this->reporter = $reporter;
25 |     }
26 | 
27 |     public function handle(
28 |         TaskInterface $task,
29 |         TaskRunnerContext $runnerContext,
30 |         StopOnFailure $stopOnFailure,
31 |         callable $next
32 |     ): Future {
33 |         return async(
34 |             function () use ($task, $runnerContext, $stopOnFailure, $next): TaskResultInterface {
35 |                 $result = $next($task, $runnerContext, $stopOnFailure)->await();
36 | 
37 |                 $this->reporter->report($runnerContext);
38 | 
39 |                 return $result;
40 |             }
41 |         );
42 |     }
43 | }
44 | 


--------------------------------------------------------------------------------
/src/Runner/TaskHandler/Middleware/StopOnFailureTaskHandlerMiddleware.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Runner\TaskHandler\Middleware;
 6 | 
 7 | use GrumPHP\Runner\StopOnFailure;
 8 | use function Amp\async;
 9 | use Amp\Future;
10 | use GrumPHP\Runner\TaskResultInterface;
11 | use GrumPHP\Runner\TaskRunnerContext;
12 | use GrumPHP\Task\TaskInterface;
13 | 
14 | class StopOnFailureTaskHandlerMiddleware implements TaskHandlerMiddlewareInterface
15 | {
16 |     public function handle(
17 |         TaskInterface $task,
18 |         TaskRunnerContext $runnerContext,
19 |         StopOnFailure $stopOnFailure,
20 |         callable $next
21 |     ): Future {
22 |         return async(
23 |             static function () use ($task, $runnerContext, $stopOnFailure, $next): TaskResultInterface {
24 |                 $result = $next($task, $runnerContext, $stopOnFailure)->await();
25 | 
26 |                 $stopOnFailure->decideForResult($result);
27 | 
28 |                 return $result;
29 |             }
30 |         );
31 |     }
32 | }
33 | 


--------------------------------------------------------------------------------
/src/Runner/TaskHandler/Middleware/TaskHandlerMiddlewareInterface.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Runner\TaskHandler\Middleware;
 6 | 
 7 | use Amp\Future;
 8 | use GrumPHP\Runner\StopOnFailure;
 9 | use GrumPHP\Runner\TaskResultInterface;
10 | use GrumPHP\Runner\TaskRunnerContext;
11 | use GrumPHP\Task\TaskInterface;
12 | 
13 | interface TaskHandlerMiddlewareInterface
14 | {
15 |     /**
16 |      * @param callable(TaskInterface, TaskRunnerContext, StopOnFailure): Future<TaskResultInterface> $next
17 |      * @return Future<TaskResultInterface>
18 |      */
19 |     public function handle(
20 |         TaskInterface $task,
21 |         TaskRunnerContext $runnerContext,
22 |         StopOnFailure $stopOnFailure,
23 |         callable $next
24 |     ): Future;
25 | }
26 | 


--------------------------------------------------------------------------------
/src/Runner/TaskResultInterface.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Runner;
 6 | 
 7 | use GrumPHP\Task\Context\ContextInterface;
 8 | use GrumPHP\Task\TaskInterface;
 9 | 
10 | /**
11 |  * @psalm-readonly
12 |  */
13 | interface TaskResultInterface
14 | {
15 |     const SKIPPED = -100;
16 |     const PASSED = 0;
17 |     const NONBLOCKING_FAILED = 90;
18 |     const FAILED = 99;
19 | 
20 |     public function getTask(): TaskInterface;
21 | 
22 |     public function getResultCode(): int;
23 | 
24 |     public function isPassed(): bool;
25 | 
26 |     public function hasFailed(): bool;
27 | 
28 |     public function isSkipped(): bool;
29 | 
30 |     public function isBlocking(): bool;
31 | 
32 |     public function getMessage(): string;
33 | 
34 |     public function getContext(): ContextInterface;
35 | 
36 |     public function withContext(ContextInterface $context): static;
37 | 
38 |     public function withAppendedMessage(string $message): self;
39 | }
40 | 


--------------------------------------------------------------------------------
/src/Runner/TaskRunner.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Runner;
 6 | 
 7 | use GrumPHP\Collection\TaskResultCollection;
 8 | use GrumPHP\Collection\TasksCollection;
 9 | 
10 | class TaskRunner
11 | {
12 |     /**
13 |      * @var TasksCollection
14 |      */
15 |     private $tasks;
16 | 
17 |     /**
18 |      * @var MiddlewareStack
19 |      */
20 |     private $middleware;
21 | 
22 |     public function __construct(TasksCollection $tasks, MiddlewareStack $middleware)
23 |     {
24 |         $this->tasks = $tasks;
25 |         $this->middleware = $middleware;
26 |     }
27 | 
28 |     public function run(TaskRunnerContext $runnerContext): TaskResultCollection
29 |     {
30 |         return $this->middleware->handle(
31 |             $runnerContext->withTasks($this->tasks)
32 |         );
33 |     }
34 | }
35 | 


--------------------------------------------------------------------------------
/src/Task/AbstractExternalTask.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Task;
 6 | 
 7 | use GrumPHP\Formatter\ProcessFormatterInterface;
 8 | use GrumPHP\Process\ProcessBuilder;
 9 | use GrumPHP\Task\Config\EmptyTaskConfig;
10 | use GrumPHP\Task\Config\TaskConfigInterface;
11 | 
12 | /**
13 |  * @template-covariant Formatter extends ProcessFormatterInterface
14 |  */
15 | abstract class AbstractExternalTask implements TaskInterface
16 | {
17 |     /**
18 |      * @var TaskConfigInterface
19 |      */
20 |     protected $config;
21 | 
22 |     /**
23 |      * @var ProcessBuilder
24 |      */
25 |     protected $processBuilder;
26 | 
27 |     /**
28 |      * @var Formatter
29 |      */
30 |     protected $formatter;
31 | 
32 |     /**
33 |      * @param Formatter $formatter
34 |      */
35 |     public function __construct(ProcessBuilder $processBuilder, ProcessFormatterInterface $formatter)
36 |     {
37 |         $this->config = new EmptyTaskConfig();
38 |         $this->processBuilder = $processBuilder;
39 |         $this->formatter = $formatter;
40 |     }
41 | 
42 |     public function getConfig(): TaskConfigInterface
43 |     {
44 |         return $this->config;
45 |     }
46 | 
47 |     public function withConfig(TaskConfigInterface $config): TaskInterface
48 |     {
49 |         $new = clone $this;
50 |         $new->config = $config;
51 | 
52 |         return $new;
53 |     }
54 | }
55 | 


--------------------------------------------------------------------------------
/src/Task/Config/ConfigOptionsResolver.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | declare(strict_types=1);
 3 | 
 4 | namespace GrumPHP\Task\Config;
 5 | 
 6 | use Symfony\Component\OptionsResolver\OptionsResolver;
 7 | 
 8 | final class ConfigOptionsResolver
 9 | {
10 |     /**
11 |      * @var \Closure(array): array
12 |      */
13 |     private \Closure $resolver;
14 | 
15 |     /**
16 |      * @param \Closure(array): array $resolver
17 |      */
18 |     private function __construct(\Closure $resolver)
19 |     {
20 |         $this->resolver = $resolver;
21 |     }
22 | 
23 |     public static function fromOptionsResolver(OptionsResolver $optionsResolver): self
24 |     {
25 |         return self::fromClosure(
26 |             static fn (array $options): array => $optionsResolver->resolve($options)
27 |         );
28 |     }
29 | 
30 |     /**
31 |      * @param \Closure(array): array $closure
32 |      */
33 |     public static function fromClosure(\Closure $closure): self
34 |     {
35 |         return new self($closure);
36 |     }
37 | 
38 |     public function resolve(array $data): array
39 |     {
40 |         return ($this->resolver)($data);
41 |     }
42 | }
43 | 


--------------------------------------------------------------------------------
/src/Task/Config/EmptyTaskConfig.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Task\Config;
 6 | 
 7 | class EmptyTaskConfig implements TaskConfigInterface
 8 | {
 9 |     public function getName(): string
10 |     {
11 |         return '';
12 |     }
13 | 
14 |     public function getOptions(): array
15 |     {
16 |         return [];
17 |     }
18 | 
19 |     public function getMetadata(): Metadata
20 |     {
21 |         return new Metadata([]);
22 |     }
23 | }
24 | 


--------------------------------------------------------------------------------
/src/Task/Config/LazyTaskConfig.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Task\Config;
 6 | 
 7 | class LazyTaskConfig implements TaskConfigInterface
 8 | {
 9 |     /**
10 |      * @var callable() : TaskConfigInterface
11 |      */
12 |     private $configFactory;
13 | 
14 |     /**
15 |      * @var null|TaskConfigInterface
16 |      */
17 |     private $config;
18 | 
19 |     /**
20 |      * @param callable() : TaskConfigInterface $configFactory
21 |      */
22 |     public function __construct(callable $configFactory)
23 |     {
24 |         $this->configFactory = $configFactory;
25 |     }
26 | 
27 |     public function getName(): string
28 |     {
29 |         return $this->proxy()->getName();
30 |     }
31 | 
32 |     public function getOptions(): array
33 |     {
34 |         return $this->proxy()->getOptions();
35 |     }
36 | 
37 |     public function getMetadata(): Metadata
38 |     {
39 |         return $this->proxy()->getMetadata();
40 |     }
41 | 
42 |     private function proxy(): TaskConfigInterface
43 |     {
44 |         if (!$this->config) {
45 |             $this->config = ($this->configFactory)();
46 |         }
47 | 
48 |         return $this->config;
49 |     }
50 | }
51 | 


--------------------------------------------------------------------------------
/src/Task/Config/Metadata.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Task\Config;
 6 | 
 7 | use Symfony\Component\OptionsResolver\OptionsResolver;
 8 | 
 9 | class Metadata
10 | {
11 |     /**
12 |      * @var array
13 |      */
14 |     private $metadata;
15 | 
16 |     public function __construct(array $metadata)
17 |     {
18 |         $this->metadata = self::getConfigurableOptions()->resolve($metadata);
19 |     }
20 | 
21 |     public static function getConfigurableOptions(): \GrumPHP\Task\Config\ConfigOptionsResolver
22 |     {
23 |         static $resolver;
24 |         if (null === $resolver) {
25 |             $resolver = new OptionsResolver();
26 |             $resolver->setDefaults([
27 |                 'priority' => 0,
28 |                 'blocking' => true,
29 |                 'enabled' => true,
30 |                 'task' => '',
31 |                 'label' => '',
32 |             ]);
33 |         }
34 | 
35 |         return ConfigOptionsResolver::fromOptionsResolver($resolver);
36 |     }
37 | 
38 |     public function priority(): int
39 |     {
40 |         return (int) $this->metadata['priority'];
41 |     }
42 | 
43 |     public function isBlocking(): bool
44 |     {
45 |         return (bool) $this->metadata['blocking'];
46 |     }
47 | 
48 |     public function isEnabled(): bool
49 |     {
50 |         return (bool) $this->metadata['enabled'];
51 |     }
52 | 
53 |     public function task(): string
54 |     {
55 |         return (string) $this->metadata['task'];
56 |     }
57 | 
58 |     public function label(): string
59 |     {
60 |         return (string) $this->metadata['label'];
61 |     }
62 | 
63 |     public function toArray(): array
64 |     {
65 |         return $this->metadata;
66 |     }
67 | }
68 | 


--------------------------------------------------------------------------------
/src/Task/Config/TaskConfig.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Task\Config;
 6 | 
 7 | class TaskConfig implements TaskConfigInterface
 8 | {
 9 |     /**
10 |      * @var string
11 |      */
12 |     private $name;
13 | 
14 |     /**
15 |      * @var array
16 |      */
17 |     private $options;
18 | 
19 |     /**
20 |      * @var Metadata
21 |      */
22 |     private $metadata;
23 | 
24 |     public function __construct(string $name, array $options, Metadata $metadata)
25 |     {
26 |         $this->name = $name;
27 |         $this->options = $options;
28 |         $this->metadata = $metadata;
29 |     }
30 | 
31 |     public function getName(): string
32 |     {
33 |         return $this->name;
34 |     }
35 | 
36 |     public function getOptions(): array
37 |     {
38 |         return $this->options;
39 |     }
40 | 
41 |     public function getMetadata(): Metadata
42 |     {
43 |         return $this->metadata;
44 |     }
45 | }
46 | 


--------------------------------------------------------------------------------
/src/Task/Config/TaskConfigInterface.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | namespace GrumPHP\Task\Config;
 4 | 
 5 | interface TaskConfigInterface
 6 | {
 7 |     public function getName(): string;
 8 | 
 9 |     public function getOptions(): array;
10 | 
11 |     public function getMetadata(): Metadata;
12 | }
13 | 


--------------------------------------------------------------------------------
/src/Task/Context/ContextInterface.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Task\Context;
 6 | 
 7 | use GrumPHP\Collection\FilesCollection;
 8 | 
 9 | interface ContextInterface
10 | {
11 |     public function getFiles(): FilesCollection;
12 | }
13 | 


--------------------------------------------------------------------------------
/src/Task/Context/GitCommitMsgContext.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Task\Context;
 6 | 
 7 | use GrumPHP\Collection\FilesCollection;
 8 | 
 9 | class GitCommitMsgContext implements ContextInterface
10 | {
11 |     private $files;
12 |     private $commitMessage;
13 |     private $userName;
14 |     private $userEmail;
15 | 
16 |     public function __construct(FilesCollection $files, string $commitMessage, string $userName, string $userEmail)
17 |     {
18 |         $this->files = $files;
19 |         $this->commitMessage = $commitMessage;
20 |         $this->userName = $userName;
21 |         $this->userEmail = $userEmail;
22 |     }
23 | 
24 |     public function getCommitMessage(): string
25 |     {
26 |         return $this->commitMessage;
27 |     }
28 | 
29 |     public function getUserName(): string
30 |     {
31 |         return $this->userName;
32 |     }
33 | 
34 |     public function getUserEmail(): string
35 |     {
36 |         return $this->userEmail;
37 |     }
38 | 
39 |     public function getFiles(): FilesCollection
40 |     {
41 |         return $this->files;
42 |     }
43 | }
44 | 


--------------------------------------------------------------------------------
/src/Task/Context/GitPreCommitContext.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Task\Context;
 6 | 
 7 | use GrumPHP\Collection\FilesCollection;
 8 | 
 9 | class GitPreCommitContext implements ContextInterface
10 | {
11 |     private $files;
12 | 
13 |     public function __construct(FilesCollection $files)
14 |     {
15 |         $this->files = $files;
16 |     }
17 | 
18 |     public function getFiles(): FilesCollection
19 |     {
20 |         return $this->files;
21 |     }
22 | }
23 | 


--------------------------------------------------------------------------------
/src/Task/Context/RunContext.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Task\Context;
 6 | 
 7 | use GrumPHP\Collection\FilesCollection;
 8 | 
 9 | class RunContext implements ContextInterface
10 | {
11 |     private $files;
12 | 
13 |     public function __construct(FilesCollection $files)
14 |     {
15 |         $this->files = $files;
16 |     }
17 | 
18 |     public function getFiles(): FilesCollection
19 |     {
20 |         return $this->files;
21 |     }
22 | }
23 | 


--------------------------------------------------------------------------------
/src/Task/TaskInterface.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Task;
 6 | 
 7 | use GrumPHP\Runner\TaskResultInterface;
 8 | use GrumPHP\Task\Config\ConfigOptionsResolver;
 9 | use GrumPHP\Task\Config\TaskConfigInterface;
10 | use GrumPHP\Task\Context\ContextInterface;
11 | 
12 | interface TaskInterface
13 | {
14 |     public static function getConfigurableOptions(): ConfigOptionsResolver;
15 | 
16 |     public function canRunInContext(ContextInterface $context): bool;
17 | 
18 |     public function run(ContextInterface $context): TaskResultInterface;
19 | 
20 |     public function getConfig(): TaskConfigInterface;
21 | 
22 |     public function withConfig(TaskConfigInterface $config): TaskInterface;
23 | }
24 | 


--------------------------------------------------------------------------------
/src/Test/Runner/AbstractMiddlewareTestCase.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Test\Runner;
 6 | 
 7 | use GrumPHP\Collection\FilesCollection;
 8 | use GrumPHP\IO\IOInterface;
 9 | use GrumPHP\Runner\TaskRunnerContext;
10 | use GrumPHP\Task\Config\Metadata;
11 | use GrumPHP\Task\Config\TaskConfig;
12 | use GrumPHP\Task\Context\ContextInterface;
13 | use GrumPHP\Task\Context\RunContext;
14 | use GrumPHP\Task\TaskInterface;
15 | use PHPUnit\Framework\TestCase;
16 | use Prophecy\Argument;
17 | use Prophecy\Prophecy\ObjectProphecy;
18 | use Symfony\Component\Console\Style\StyleInterface;
19 | 
20 | class AbstractMiddlewareTestCase extends TestCase
21 | {
22 |     protected function createRunnerContext(): TaskRunnerContext
23 |     {
24 |         return new TaskRunnerContext(
25 |             new RunContext(new FilesCollection())
26 |         );
27 |     }
28 | 
29 |     protected function createNextShouldNotBeCalledCallback(): callable
30 |     {
31 |         return static function () {
32 |             throw new \RuntimeException('Expected next not to be called!');
33 |         };
34 |     }
35 | 
36 |     protected function mockIO(): IOInterface
37 |     {
38 |         /** @var ObjectProphecy|IOInterface $IO */
39 |         $IO = $this->prophesize(IOInterface::class);
40 |         $IO->isVerbose()->willReturn(false);
41 |         $IO->startGroup(Argument::any());
42 |         $IO->endGroup();
43 | 
44 |         return $IO->reveal();
45 |     }
46 | 
47 |     protected function mockTask(string $name, array $meta = []): TaskInterface
48 |     {
49 |         /** @var ObjectProphecy|TaskInterface $task */
50 |         $task = $this->prophesize(TaskInterface::class);
51 |         $task->getConfig()->willReturn(new TaskConfig($name, [], new Metadata($meta)));
52 | 
53 |         return $task->reveal();
54 |     }
55 | }
56 | 


--------------------------------------------------------------------------------
/src/Test/Runner/AbstractRunnerMiddlewareTestCase.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Test\Runner;
 6 | 
 7 | use Symfony\Component\Messenger\Envelope;
 8 | use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
 9 | use Symfony\Component\Messenger\Middleware\StackInterface;
10 | use Symfony\Component\Messenger\Middleware\StackMiddleware;
11 | 
12 | abstract class AbstractRunnerMiddlewareTestCase extends AbstractMiddlewareTestCase
13 | {
14 | }
15 | 


--------------------------------------------------------------------------------
/src/Test/Runner/AbstractTaskHandlerMiddlewareTestCase.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Test\Runner;
 6 | 
 7 | use Amp\Future;
 8 | use GrumPHP\Runner\TaskResultInterface;
 9 | use GrumPHP\Task\Config\Metadata;
10 | use GrumPHP\Task\Config\TaskConfig;
11 | use GrumPHP\Task\Context\ContextInterface;
12 | use GrumPHP\Task\TaskInterface;
13 | use Prophecy\Argument;
14 | use Prophecy\Prophecy\ObjectProphecy;
15 | 
16 | abstract class AbstractTaskHandlerMiddlewareTestCase extends AbstractMiddlewareTestCase
17 | {
18 |     protected function createNextResultCallback(TaskResultInterface $taskResult): callable
19 |     {
20 |         return static function () use ($taskResult) {
21 |             return Future::complete($taskResult);
22 |         };
23 |     }
24 | 
25 |     protected function createExceptionCallback(\Throwable $exception): callable
26 |     {
27 |         return static function () use ($exception) {
28 |             return Future::error($exception);
29 |         };
30 |     }
31 | 
32 |     protected function resolve(Future $promise): TaskResultInterface
33 |     {
34 |         return $promise->await();
35 |     }
36 | 
37 |     protected function mockTaskRun(string $name, callable $runWillDo): TaskInterface
38 |     {
39 |         /** @var ObjectProphecy|TaskInterface $task */
40 |         $task = $this->prophesize(TaskInterface::class);
41 |         $task->getConfig()->willReturn(new TaskConfig($name, [], new Metadata([])));
42 |         $task->run(Argument::type(ContextInterface::class))->will($runWillDo);
43 | 
44 |         return $task->reveal();
45 |     }
46 | }
47 | 


--------------------------------------------------------------------------------
/src/TestSuite/TestSuite.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\TestSuite;
 6 | 
 7 | class TestSuite implements TestSuiteInterface
 8 | {
 9 |     /**
10 |      * @var string
11 |      */
12 |     private $name;
13 | 
14 |     /**
15 |      * @var array
16 |      */
17 |     private $taskNames = [];
18 | 
19 |     /**
20 |      * TestSuite constructor.
21 |      */
22 |     public function __construct(string $name, array $taskNames)
23 |     {
24 |         $this->taskNames = $taskNames;
25 |         $this->name = $name;
26 |     }
27 | 
28 |     public function getName(): string
29 |     {
30 |         return $this->name;
31 |     }
32 | 
33 |     public function getTaskNames(): array
34 |     {
35 |         return $this->taskNames;
36 |     }
37 | }
38 | 


--------------------------------------------------------------------------------
/src/TestSuite/TestSuiteInterface.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\TestSuite;
 6 | 
 7 | interface TestSuiteInterface
 8 | {
 9 |     public function getName(): string;
10 | 
11 |     public function getTaskNames(): array;
12 | }
13 | 


--------------------------------------------------------------------------------
/src/Util/ComposerFile.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Util;
 6 | 
 7 | class ComposerFile
 8 | {
 9 |     /**
10 |      * @var array
11 |      */
12 |     private $configuration;
13 | 
14 |     /**
15 |      * @var string
16 |      */
17 |     private $path;
18 | 
19 |     public function __construct(string $path, array $configuration)
20 |     {
21 |         $this->path = $path;
22 |         $this->configuration = $configuration;
23 |     }
24 | 
25 |     public function getBinDir(): string
26 |     {
27 |         $binDir = $this->configuration['config']['bin-dir'] ?? null;
28 | 
29 |         if (null !== $binDir) {
30 |             return (string) $binDir;
31 |         }
32 | 
33 |         $vendorDir = $this->configuration['config']['vendor-dir'] ?? null;
34 | 
35 |         if (null !== $vendorDir) {
36 |             return $vendorDir . '/bin';
37 |         }
38 | 
39 |         return 'vendor/bin';
40 |     }
41 | 
42 |     public function getConfigDefaultPath(): ?string
43 |     {
44 |         return $this->configuration['extra']['grumphp']['config-default-path'] ?? null;
45 |     }
46 | 
47 |     public function getProjectPath(): ?string
48 |     {
49 |         return $this->configuration['extra']['grumphp']['project-path'] ?? null;
50 |     }
51 | 
52 |     public function getPath(): string
53 |     {
54 |         return $this->path;
55 |     }
56 | }
57 | 


--------------------------------------------------------------------------------
/src/Util/PhpVersion.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Util;
 6 | 
 7 | class PhpVersion
 8 | {
 9 |     private $versions;
10 | 
11 |     public function __construct(array $versions)
12 |     {
13 |         $this->versions = $versions;
14 |     }
15 | 
16 |     /**
17 |      * @see https://secure.php.net/supported-versions.php for a list of currently supported versions
18 |      */
19 |     public function isSupportedVersion(string $currentVersion): bool
20 |     {
21 |         $now = new \DateTime();
22 |         foreach ($this->versions as $number => $eol) {
23 |             $eol = new \DateTime($eol);
24 |             if ($now < $eol && version_compare($currentVersion, $number) >= 0) {
25 |                 return true;
26 |             }
27 |         }
28 | 
29 |         return false;
30 |     }
31 | 
32 |     public function isSupportedProjectVersion(string $currentVersion, string $projectVersion): bool
33 |     {
34 |         return version_compare($currentVersion, $projectVersion) >= 0;
35 |     }
36 | }
37 | 


--------------------------------------------------------------------------------
/src/Util/Platform.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Util;
 6 | 
 7 | class Platform
 8 | {
 9 |     /**
10 |      * Windows has a limit on command line input strings.
11 |      * This one is causing external commands to fail with exit code 1 without any error.
12 |      * More information:.
13 |      *
14 |      * @see https://support.microsoft.com/en-us/kb/830473
15 |      */
16 |     const WINDOWS_COMMANDLINE_STRING_LIMITATION = 8191;
17 | 
18 |     public static function isWindows(): bool
19 |     {
20 |         return \defined('PHP_WINDOWS_VERSION_BUILD');
21 |     }
22 | }
23 | 


--------------------------------------------------------------------------------
/src/Util/Str.php:
--------------------------------------------------------------------------------
 1 | <?php
 2 | 
 3 | declare(strict_types=1);
 4 | 
 5 | namespace GrumPHP\Util;
 6 | 
 7 | final class Str
 8 | {
 9 |     /**
10 |      * String contains one of the provided needles
11 |      */
12 |     public static function containsOneOf(string $haystack, array $needles): bool
13 |     {
14 |         foreach ($needles as $needle) {
15 |             if ($needle !== '' && mb_strpos($haystack, $needle) !== false) {
16 |                 return true;
17 |             }
18 |         }
19 | 
20 |         return false;
21 |     }
22 | 
23 |     /**
24 |      * Split $value on ",", trim the individual parts and
25 |      * de-deduplicate the remaining values
26 |      *
27 |      * @param non-empty-string $delimiter
28 |      */
29 |     public static function explodeWithCleanup(string $delimiter, string $value): array
30 |     {
31 |         return array_unique(array_map(function (string $value) {
32 |             return trim($value);
33 |         }, array_filter(explode($delimiter, $value))));
34 |     }
35 | }
36 | 


--------------------------------------------------------------------------------