mediawiki/vendor (main)

sourcepatches
From b62e7640ca0daf8ed4b139d7a871ab96f4450b61 Mon Sep 17 00:00:00 2001
From: libraryupgrader <tools.libraryupgrader@tools.wmflabs.org>
Date: Thu, 25 Apr 2024 21:07:24 +0000
Subject: [PATCH] build: Updating php-parallel-lint/php-parallel-lint to 1.4.0

Change-Id: Iab8e18cf4e65821d806234b9623baff21ff2ae1f
---
 composer.json                                 |   2 +-
 composer.lock                                 |  22 +-
 composer/ClassLoader.php                      |  96 +--
 composer/LICENSE                              |   2 -
 composer/autoload_classmap.php                |  36 ++
 composer/autoload_static.php                  |  36 ++
 composer/installed.json                       |  70 ++-
 composer/installed.php                        |  27 +-
 .../php-parallel-lint/CHANGELOG.md            | 256 ++++++++
 php-parallel-lint/php-parallel-lint/LICENSE   |  26 +
 php-parallel-lint/php-parallel-lint/README.md | 122 ++++
 .../php-parallel-lint/bin/skip-linting.php    |  25 +
 .../php-parallel-lint/composer.json           |  55 ++
 .../php-parallel-lint/parallel-lint           |  84 +++
 .../php-parallel-lint/src/Application.php     | 129 ++++
 .../src/Contracts/SyntaxErrorCallback.php     |  13 +
 .../php-parallel-lint/src/Error.php           | 242 +++++++
 .../php-parallel-lint/src/ErrorFormatter.php  | 141 +++++
 .../php-parallel-lint/src/Manager.php         | 293 +++++++++
 .../php-parallel-lint/src/Output.php          | 594 ++++++++++++++++++
 .../php-parallel-lint/src/ParallelLint.php    | 286 +++++++++
 .../src/Process/GitBlameProcess.php           | 147 +++++
 .../src/Process/LintProcess.php               | 137 ++++
 .../src/Process/PhpExecutable.php             | 128 ++++
 .../src/Process/PhpProcess.php                |  35 ++
 .../php-parallel-lint/src/Process/Process.php | 153 +++++
 .../src/Process/SkipLintProcess.php           |  91 +++
 .../php-parallel-lint/src/Result.php          | 171 +++++
 .../php-parallel-lint/src/Settings.php        | 247 ++++++++
 .../php-parallel-lint/src/exceptions.php      |  93 +++
 .../php-parallel-lint/src/polyfill.php        |  15 +
 31 files changed, 3712 insertions(+), 62 deletions(-)
 create mode 100644 php-parallel-lint/php-parallel-lint/CHANGELOG.md
 create mode 100644 php-parallel-lint/php-parallel-lint/LICENSE
 create mode 100644 php-parallel-lint/php-parallel-lint/README.md
 create mode 100644 php-parallel-lint/php-parallel-lint/bin/skip-linting.php
 create mode 100644 php-parallel-lint/php-parallel-lint/composer.json
 create mode 100755 php-parallel-lint/php-parallel-lint/parallel-lint
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Application.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Contracts/SyntaxErrorCallback.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Error.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/ErrorFormatter.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Manager.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Output.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/ParallelLint.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Process/GitBlameProcess.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Process/LintProcess.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Process/PhpExecutable.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Process/PhpProcess.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Process/Process.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Process/SkipLintProcess.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Result.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Settings.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/exceptions.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/polyfill.php

diff --git a/composer.json b/composer.json
index 9d1e5d9..e7b47ed 100644
--- a/composer.json
+++ b/composer.json
@@ -155,7 +155,7 @@
 		"symfony/polyfill-mbstring": "1.99"
 	},
 	"require-dev": {
-		"php-parallel-lint/php-parallel-lint": "1.3.2"
+		"php-parallel-lint/php-parallel-lint": "1.4.0"
 	},
 	"scripts": {
 		"test": "parallel-lint --exclude composer/autoload_static.php --exclude symfony/console/Attribute/AsCommand.php --exclude symfony/polyfill-php83/bootstrap81.php --exclude ruflin/elastica/src/Query/Match.php ."
diff --git a/composer.lock b/composer.lock
index 280de5b..f100c82 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "014a86445ee735eb32d5e3fd16221b4d",
+    "content-hash": "5d43f427827d94274ff65182f6435dbc",
     "packages": [
         {
             "name": "bacon/bacon-qr-code",
@@ -8769,16 +8769,16 @@
     "packages-dev": [
         {
             "name": "php-parallel-lint/php-parallel-lint",
-            "version": "v1.3.2",
+            "version": "v1.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-parallel-lint/PHP-Parallel-Lint.git",
-                "reference": "6483c9832e71973ed29cf71bd6b3f4fde438a9de"
+                "reference": "6db563514f27e19595a19f45a4bf757b6401194e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-parallel-lint/PHP-Parallel-Lint/zipball/6483c9832e71973ed29cf71bd6b3f4fde438a9de",
-                "reference": "6483c9832e71973ed29cf71bd6b3f4fde438a9de",
+                "url": "https://api.github.com/repos/php-parallel-lint/PHP-Parallel-Lint/zipball/6db563514f27e19595a19f45a4bf757b6401194e",
+                "reference": "6db563514f27e19595a19f45a4bf757b6401194e",
                 "shasum": ""
             },
             "require": {
@@ -8816,13 +8816,17 @@
                     "email": "ahoj@jakubonderka.cz"
                 }
             ],
-            "description": "This tool check syntax of PHP files about 20x faster than serial check.",
+            "description": "This tool checks the syntax of PHP files about 20x faster than serial check.",
             "homepage": "https://github.com/php-parallel-lint/PHP-Parallel-Lint",
+            "keywords": [
+                "lint",
+                "static analysis"
+            ],
             "support": {
                 "issues": "https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues",
-                "source": "https://github.com/php-parallel-lint/PHP-Parallel-Lint/tree/v1.3.2"
+                "source": "https://github.com/php-parallel-lint/PHP-Parallel-Lint/tree/v1.4.0"
             },
-            "time": "2022-02-21T12:50:22+00:00"
+            "time": "2024-03-27T12:14:49+00:00"
         }
     ],
     "aliases": [
@@ -8845,5 +8849,5 @@
         "composer-plugin-api": "^2.0"
     },
     "platform-dev": [],
-    "plugin-api-version": "2.6.0"
+    "plugin-api-version": "2.3.0"
 }
diff --git a/composer/ClassLoader.php b/composer/ClassLoader.php
index 7824d8f..a72151c 100644
--- a/composer/ClassLoader.php
+++ b/composer/ClassLoader.php
@@ -45,34 +45,35 @@ class ClassLoader
     /** @var \Closure(string):void */
     private static $includeFile;
 
-    /** @var string|null */
+    /** @var ?string */
     private $vendorDir;
 
     // PSR-4
     /**
-     * @var array<string, array<string, int>>
+     * @var array[]
+     * @psalm-var array<string, array<string, int>>
      */
     private $prefixLengthsPsr4 = array();
     /**
-     * @var array<string, list<string>>
+     * @var array[]
+     * @psalm-var array<string, array<int, string>>
      */
     private $prefixDirsPsr4 = array();
     /**
-     * @var list<string>
+     * @var array[]
+     * @psalm-var array<string, string>
      */
     private $fallbackDirsPsr4 = array();
 
     // PSR-0
     /**
-     * List of PSR-0 prefixes
-     *
-     * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
-     *
-     * @var array<string, array<string, list<string>>>
+     * @var array[]
+     * @psalm-var array<string, array<string, string[]>>
      */
     private $prefixesPsr0 = array();
     /**
-     * @var list<string>
+     * @var array[]
+     * @psalm-var array<string, string>
      */
     private $fallbackDirsPsr0 = array();
 
@@ -80,7 +81,8 @@ class ClassLoader
     private $useIncludePath = false;
 
     /**
-     * @var array<string, string>
+     * @var string[]
+     * @psalm-var array<string, string>
      */
     private $classMap = array();
 
@@ -88,20 +90,21 @@ class ClassLoader
     private $classMapAuthoritative = false;
 
     /**
-     * @var array<string, bool>
+     * @var bool[]
+     * @psalm-var array<string, bool>
      */
     private $missingClasses = array();
 
-    /** @var string|null */
+    /** @var ?string */
     private $apcuPrefix;
 
     /**
-     * @var array<string, self>
+     * @var self[]
      */
     private static $registeredLoaders = array();
 
     /**
-     * @param string|null $vendorDir
+     * @param ?string $vendorDir
      */
     public function __construct($vendorDir = null)
     {
@@ -110,7 +113,7 @@ class ClassLoader
     }
 
     /**
-     * @return array<string, list<string>>
+     * @return string[]
      */
     public function getPrefixes()
     {
@@ -122,7 +125,8 @@ class ClassLoader
     }
 
     /**
-     * @return array<string, list<string>>
+     * @return array[]
+     * @psalm-return array<string, array<int, string>>
      */
     public function getPrefixesPsr4()
     {
@@ -130,7 +134,8 @@ class ClassLoader
     }
 
     /**
-     * @return list<string>
+     * @return array[]
+     * @psalm-return array<string, string>
      */
     public function getFallbackDirs()
     {
@@ -138,7 +143,8 @@ class ClassLoader
     }
 
     /**
-     * @return list<string>
+     * @return array[]
+     * @psalm-return array<string, string>
      */
     public function getFallbackDirsPsr4()
     {
@@ -146,7 +152,8 @@ class ClassLoader
     }
 
     /**
-     * @return array<string, string> Array of classname => path
+     * @return string[] Array of classname => path
+     * @psalm-return array<string, string>
      */
     public function getClassMap()
     {
@@ -154,7 +161,8 @@ class ClassLoader
     }
 
     /**
-     * @param array<string, string> $classMap Class to filename map
+     * @param string[] $classMap Class to filename map
+     * @psalm-param array<string, string> $classMap
      *
      * @return void
      */
@@ -171,25 +179,24 @@ class ClassLoader
      * Registers a set of PSR-0 directories for a given prefix, either
      * appending or prepending to the ones previously set for this prefix.
      *
-     * @param string              $prefix  The prefix
-     * @param list<string>|string $paths   The PSR-0 root directories
-     * @param bool                $prepend Whether to prepend the directories
+     * @param string          $prefix  The prefix
+     * @param string[]|string $paths   The PSR-0 root directories
+     * @param bool            $prepend Whether to prepend the directories
      *
      * @return void
      */
     public function add($prefix, $paths, $prepend = false)
     {
-        $paths = (array) $paths;
         if (!$prefix) {
             if ($prepend) {
                 $this->fallbackDirsPsr0 = array_merge(
-                    $paths,
+                    (array) $paths,
                     $this->fallbackDirsPsr0
                 );
             } else {
                 $this->fallbackDirsPsr0 = array_merge(
                     $this->fallbackDirsPsr0,
-                    $paths
+                    (array) $paths
                 );
             }
 
@@ -198,19 +205,19 @@ class ClassLoader
 
         $first = $prefix[0];
         if (!isset($this->prefixesPsr0[$first][$prefix])) {
-            $this->prefixesPsr0[$first][$prefix] = $paths;
+            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
 
             return;
         }
         if ($prepend) {
             $this->prefixesPsr0[$first][$prefix] = array_merge(
-                $paths,
+                (array) $paths,
                 $this->prefixesPsr0[$first][$prefix]
             );
         } else {
             $this->prefixesPsr0[$first][$prefix] = array_merge(
                 $this->prefixesPsr0[$first][$prefix],
-                $paths
+                (array) $paths
             );
         }
     }
@@ -219,9 +226,9 @@ class ClassLoader
      * Registers a set of PSR-4 directories for a given namespace, either
      * appending or prepending to the ones previously set for this namespace.
      *
-     * @param string              $prefix  The prefix/namespace, with trailing '\\'
-     * @param list<string>|string $paths   The PSR-4 base directories
-     * @param bool                $prepend Whether to prepend the directories
+     * @param string          $prefix  The prefix/namespace, with trailing '\\'
+     * @param string[]|string $paths   The PSR-4 base directories
+     * @param bool            $prepend Whether to prepend the directories
      *
      * @throws \InvalidArgumentException
      *
@@ -229,18 +236,17 @@ class ClassLoader
      */
     public function addPsr4($prefix, $paths, $prepend = false)
     {
-        $paths = (array) $paths;
         if (!$prefix) {
             // Register directories for the root namespace.
             if ($prepend) {
                 $this->fallbackDirsPsr4 = array_merge(
-                    $paths,
+                    (array) $paths,
                     $this->fallbackDirsPsr4
                 );
             } else {
                 $this->fallbackDirsPsr4 = array_merge(
                     $this->fallbackDirsPsr4,
-                    $paths
+                    (array) $paths
                 );
             }
         } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
@@ -250,18 +256,18 @@ class ClassLoader
                 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
             }
             $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
-            $this->prefixDirsPsr4[$prefix] = $paths;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
         } elseif ($prepend) {
             // Prepend directories for an already registered namespace.
             $this->prefixDirsPsr4[$prefix] = array_merge(
-                $paths,
+                (array) $paths,
                 $this->prefixDirsPsr4[$prefix]
             );
         } else {
             // Append directories for an already registered namespace.
             $this->prefixDirsPsr4[$prefix] = array_merge(
                 $this->prefixDirsPsr4[$prefix],
-                $paths
+                (array) $paths
             );
         }
     }
@@ -270,8 +276,8 @@ class ClassLoader
      * Registers a set of PSR-0 directories for a given prefix,
      * replacing any others previously set for this prefix.
      *
-     * @param string              $prefix The prefix
-     * @param list<string>|string $paths  The PSR-0 base directories
+     * @param string          $prefix The prefix
+     * @param string[]|string $paths  The PSR-0 base directories
      *
      * @return void
      */
@@ -288,8 +294,8 @@ class ClassLoader
      * Registers a set of PSR-4 directories for a given namespace,
      * replacing any others previously set for this namespace.
      *
-     * @param string              $prefix The prefix/namespace, with trailing '\\'
-     * @param list<string>|string $paths  The PSR-4 base directories
+     * @param string          $prefix The prefix/namespace, with trailing '\\'
+     * @param string[]|string $paths  The PSR-4 base directories
      *
      * @throws \InvalidArgumentException
      *
@@ -475,9 +481,9 @@ class ClassLoader
     }
 
     /**
-     * Returns the currently registered loaders keyed by their corresponding vendor directories.
+     * Returns the currently registered loaders indexed by their corresponding vendor directories.
      *
-     * @return array<string, self>
+     * @return self[]
      */
     public static function getRegisteredLoaders()
     {
diff --git a/composer/LICENSE b/composer/LICENSE
index f27399a..62ecfd8 100644
--- a/composer/LICENSE
+++ b/composer/LICENSE
@@ -1,4 +1,3 @@
-
 Copyright (c) Nils Adermann, Jordi Boggiano
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -18,4 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
-
diff --git a/composer/autoload_classmap.php b/composer/autoload_classmap.php
index 6bddcf0..6ed1c3f 100644
--- a/composer/autoload_classmap.php
+++ b/composer/autoload_classmap.php
@@ -1374,6 +1374,41 @@ return array(
     'GuzzleHttp\\Utils' => $vendorDir . '/guzzlehttp/guzzle/src/Utils.php',
     'HtmlFormatter\\HtmlFormatter' => $vendorDir . '/wikimedia/html-formatter/src/HtmlFormatter.php',
     'Image_XMP' => $vendorDir . '/james-heinrich/getid3/getid3/module.tag.xmp.php',
+    'JakubOnderka\\PhpParallelLint\\Application' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Application.php',
+    'JakubOnderka\\PhpParallelLint\\ArrayIterator' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Settings.php',
+    'JakubOnderka\\PhpParallelLint\\Blame' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Error.php',
+    'JakubOnderka\\PhpParallelLint\\CheckstyleOutput' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\ConsoleWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\Contracts\\SyntaxErrorCallback' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Contracts/SyntaxErrorCallback.php',
+    'JakubOnderka\\PhpParallelLint\\Error' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Error.php',
+    'JakubOnderka\\PhpParallelLint\\ErrorFormatter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/ErrorFormatter.php',
+    'JakubOnderka\\PhpParallelLint\\Exception' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+    'JakubOnderka\\PhpParallelLint\\FileWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\GitLabOutput' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\IWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\InvalidArgumentException' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+    'JakubOnderka\\PhpParallelLint\\JsonOutput' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\Manager' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Manager.php',
+    'JakubOnderka\\PhpParallelLint\\MultipleWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\NotExistsClassException' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+    'JakubOnderka\\PhpParallelLint\\NotExistsPathException' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+    'JakubOnderka\\PhpParallelLint\\NotImplementCallbackException' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+    'JakubOnderka\\PhpParallelLint\\NullWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\Output' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\ParallelLint' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/ParallelLint.php',
+    'JakubOnderka\\PhpParallelLint\\Process\\GitBlameProcess' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/GitBlameProcess.php',
+    'JakubOnderka\\PhpParallelLint\\Process\\LintProcess' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/LintProcess.php',
+    'JakubOnderka\\PhpParallelLint\\Process\\PhpExecutable' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/PhpExecutable.php',
+    'JakubOnderka\\PhpParallelLint\\Process\\PhpProcess' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/PhpProcess.php',
+    'JakubOnderka\\PhpParallelLint\\Process\\Process' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/Process.php',
+    'JakubOnderka\\PhpParallelLint\\Process\\SkipLintProcess' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/SkipLintProcess.php',
+    'JakubOnderka\\PhpParallelLint\\RecursiveDirectoryFilterIterator' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Manager.php',
+    'JakubOnderka\\PhpParallelLint\\Result' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Result.php',
+    'JakubOnderka\\PhpParallelLint\\RunTimeException' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+    'JakubOnderka\\PhpParallelLint\\Settings' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Settings.php',
+    'JakubOnderka\\PhpParallelLint\\SyntaxError' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Error.php',
+    'JakubOnderka\\PhpParallelLint\\TextOutput' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\TextOutputColored' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
     'JsonException' => $vendorDir . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
     'JsonSchema\\Constraints\\BaseConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/BaseConstraint.php',
     'JsonSchema\\Constraints\\CollectionConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/CollectionConstraint.php',
@@ -1418,6 +1453,7 @@ return array(
     'JsonSchema\\Uri\\UriResolver' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Uri/UriResolver.php',
     'JsonSchema\\Uri\\UriRetriever' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php',
     'JsonSchema\\Validator' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Validator.php',
+    'JsonSerializable' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/polyfill.php',
     'Lcobucci\\Clock\\Clock' => $vendorDir . '/lcobucci/clock/src/Clock.php',
     'Lcobucci\\Clock\\FrozenClock' => $vendorDir . '/lcobucci/clock/src/FrozenClock.php',
     'Lcobucci\\Clock\\SystemClock' => $vendorDir . '/lcobucci/clock/src/SystemClock.php',
diff --git a/composer/autoload_static.php b/composer/autoload_static.php
index 4d801fd..9dd8a0b 100644
--- a/composer/autoload_static.php
+++ b/composer/autoload_static.php
@@ -2180,6 +2180,41 @@ class ComposerStaticInit_mediawiki_vendor
         'GuzzleHttp\\Utils' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Utils.php',
         'HtmlFormatter\\HtmlFormatter' => __DIR__ . '/..' . '/wikimedia/html-formatter/src/HtmlFormatter.php',
         'Image_XMP' => __DIR__ . '/..' . '/james-heinrich/getid3/getid3/module.tag.xmp.php',
+        'JakubOnderka\\PhpParallelLint\\Application' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Application.php',
+        'JakubOnderka\\PhpParallelLint\\ArrayIterator' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Settings.php',
+        'JakubOnderka\\PhpParallelLint\\Blame' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Error.php',
+        'JakubOnderka\\PhpParallelLint\\CheckstyleOutput' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\ConsoleWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\Contracts\\SyntaxErrorCallback' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Contracts/SyntaxErrorCallback.php',
+        'JakubOnderka\\PhpParallelLint\\Error' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Error.php',
+        'JakubOnderka\\PhpParallelLint\\ErrorFormatter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/ErrorFormatter.php',
+        'JakubOnderka\\PhpParallelLint\\Exception' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+        'JakubOnderka\\PhpParallelLint\\FileWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\GitLabOutput' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\IWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\InvalidArgumentException' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+        'JakubOnderka\\PhpParallelLint\\JsonOutput' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\Manager' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Manager.php',
+        'JakubOnderka\\PhpParallelLint\\MultipleWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\NotExistsClassException' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+        'JakubOnderka\\PhpParallelLint\\NotExistsPathException' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+        'JakubOnderka\\PhpParallelLint\\NotImplementCallbackException' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+        'JakubOnderka\\PhpParallelLint\\NullWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\Output' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\ParallelLint' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/ParallelLint.php',
+        'JakubOnderka\\PhpParallelLint\\Process\\GitBlameProcess' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/GitBlameProcess.php',
+        'JakubOnderka\\PhpParallelLint\\Process\\LintProcess' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/LintProcess.php',
+        'JakubOnderka\\PhpParallelLint\\Process\\PhpExecutable' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/PhpExecutable.php',
+        'JakubOnderka\\PhpParallelLint\\Process\\PhpProcess' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/PhpProcess.php',
+        'JakubOnderka\\PhpParallelLint\\Process\\Process' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/Process.php',
+        'JakubOnderka\\PhpParallelLint\\Process\\SkipLintProcess' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/SkipLintProcess.php',
+        'JakubOnderka\\PhpParallelLint\\RecursiveDirectoryFilterIterator' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Manager.php',
+        'JakubOnderka\\PhpParallelLint\\Result' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Result.php',
+        'JakubOnderka\\PhpParallelLint\\RunTimeException' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+        'JakubOnderka\\PhpParallelLint\\Settings' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Settings.php',
+        'JakubOnderka\\PhpParallelLint\\SyntaxError' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Error.php',
+        'JakubOnderka\\PhpParallelLint\\TextOutput' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\TextOutputColored' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
         'JsonException' => __DIR__ . '/..' . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
         'JsonSchema\\Constraints\\BaseConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/BaseConstraint.php',
         'JsonSchema\\Constraints\\CollectionConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/CollectionConstraint.php',
@@ -2224,6 +2259,7 @@ class ComposerStaticInit_mediawiki_vendor
         'JsonSchema\\Uri\\UriResolver' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Uri/UriResolver.php',
         'JsonSchema\\Uri\\UriRetriever' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php',
         'JsonSchema\\Validator' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Validator.php',
+        'JsonSerializable' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/polyfill.php',
         'Lcobucci\\Clock\\Clock' => __DIR__ . '/..' . '/lcobucci/clock/src/Clock.php',
         'Lcobucci\\Clock\\FrozenClock' => __DIR__ . '/..' . '/lcobucci/clock/src/FrozenClock.php',
         'Lcobucci\\Clock\\SystemClock' => __DIR__ . '/..' . '/lcobucci/clock/src/SystemClock.php',
diff --git a/composer/installed.json b/composer/installed.json
index 2055a88..d4841cf 100644
--- a/composer/installed.json
+++ b/composer/installed.json
@@ -3911,6 +3911,70 @@
             },
             "install-path": "../pear/pear_exception"
         },
+        {
+            "name": "php-parallel-lint/php-parallel-lint",
+            "version": "v1.4.0",
+            "version_normalized": "1.4.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-parallel-lint/PHP-Parallel-Lint.git",
+                "reference": "6db563514f27e19595a19f45a4bf757b6401194e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-parallel-lint/PHP-Parallel-Lint/zipball/6db563514f27e19595a19f45a4bf757b6401194e",
+                "reference": "6db563514f27e19595a19f45a4bf757b6401194e",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "php": ">=5.3.0"
+            },
+            "replace": {
+                "grogy/php-parallel-lint": "*",
+                "jakub-onderka/php-parallel-lint": "*"
+            },
+            "require-dev": {
+                "nette/tester": "^1.3 || ^2.0",
+                "php-parallel-lint/php-console-highlighter": "0.* || ^1.0",
+                "squizlabs/php_codesniffer": "^3.6"
+            },
+            "suggest": {
+                "php-parallel-lint/php-console-highlighter": "Highlight syntax in code snippet"
+            },
+            "time": "2024-03-27T12:14:49+00:00",
+            "bin": [
+                "parallel-lint"
+            ],
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "classmap": [
+                    "./src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-2-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Jakub Onderka",
+                    "email": "ahoj@jakubonderka.cz"
+                }
+            ],
+            "description": "This tool checks the syntax of PHP files about 20x faster than serial check.",
+            "homepage": "https://github.com/php-parallel-lint/PHP-Parallel-Lint",
+            "keywords": [
+                "lint",
+                "static analysis"
+            ],
+            "support": {
+                "issues": "https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues",
+                "source": "https://github.com/php-parallel-lint/PHP-Parallel-Lint/tree/v1.4.0"
+            },
+            "install-path": "../php-parallel-lint/php-parallel-lint"
+        },
         {
             "name": "pimple/pimple",
             "version": "v3.5.0",
@@ -9146,6 +9210,8 @@
             "install-path": "../zordius/lightncandy"
         }
     ],
-    "dev": false,
-    "dev-package-names": []
+    "dev": true,
+    "dev-package-names": [
+        "php-parallel-lint/php-parallel-lint"
+    ]
 }
diff --git a/composer/installed.php b/composer/installed.php
index 4e55570..b4717a6 100644
--- a/composer/installed.php
+++ b/composer/installed.php
@@ -3,17 +3,17 @@
         'name' => '__root__',
         'pretty_version' => 'dev-master',
         'version' => 'dev-master',
-        'reference' => 'f94adaaa03ba6fe9bddc2ac498db50955dd381de',
+        'reference' => 'fd4498bc6a5f80a8b56b03e0be446600188e86f0',
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'dev' => false,
+        'dev' => true,
     ),
     'versions' => array(
         '__root__' => array(
             'pretty_version' => 'dev-master',
             'version' => 'dev-master',
-            'reference' => 'f94adaaa03ba6fe9bddc2ac498db50955dd381de',
+            'reference' => 'fd4498bc6a5f80a8b56b03e0be446600188e86f0',
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
@@ -235,6 +235,12 @@
             'aliases' => array(),
             'dev_requirement' => false,
         ),
+        'grogy/php-parallel-lint' => array(
+            'dev_requirement' => true,
+            'replaced' => array(
+                0 => '*',
+            ),
+        ),
         'guzzlehttp/guzzle' => array(
             'pretty_version' => '7.7.1',
             'version' => '7.7.1.0',
@@ -277,6 +283,12 @@
             'aliases' => array(),
             'dev_requirement' => false,
         ),
+        'jakub-onderka/php-parallel-lint' => array(
+            'dev_requirement' => true,
+            'replaced' => array(
+                0 => '*',
+            ),
+        ),
         'james-heinrich/getid3' => array(
             'pretty_version' => 'v1.9.23',
             'version' => '1.9.23.0',
@@ -556,6 +568,15 @@
             'aliases' => array(),
             'dev_requirement' => false,
         ),
+        'php-parallel-lint/php-parallel-lint' => array(
+            'pretty_version' => 'v1.4.0',
+            'version' => '1.4.0.0',
+            'reference' => '6db563514f27e19595a19f45a4bf757b6401194e',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../php-parallel-lint/php-parallel-lint',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
         'pimple/pimple' => array(
             'pretty_version' => 'v3.5.0',
             'version' => '3.5.0.0',
diff --git a/php-parallel-lint/php-parallel-lint/CHANGELOG.md b/php-parallel-lint/php-parallel-lint/CHANGELOG.md
new file mode 100644
index 0000000..3902e57
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/CHANGELOG.md
@@ -0,0 +1,256 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
+
+## [Unreleased]
+
+[Unreleased]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/compare/v1.3.2...HEAD
+
+
+## [1.4.0] - 2024-03-27
+
+### Added
+- The "skip linting" feature can now be used in PHP files starting with a shebang, [#146] from [@xaben].
+
+### Fixed
+- PHP 8.4 deprecation notice, [#154] from [@Ayesh] and [@jrfnl].
+- Bug fix: the PHP version check in the application bootstrap did not work on PHP < 5.3, [#100] from [@jrfnl], fixes [#62].
+- Bug fix: files containing the `~` character in their name can now be processed correctly, [#118] from [@jrfnl].
+- Bug fix: error message sometimes displayed on last line of code snippet, [#98] from [@jrfnl], fixes [#93].
+- Bug fix: error message would sometimes contain duplicate information, [#117] from [@jrfnl].
+- Bug fix: the "in file .. on line part" text did not always get cleaned correctly from the error message, [#118] from [@jrfnl].
+
+### Changed
+- The percentage output in the progress report is now aligned, [#140] from [@robertology].
+- The error message displayed when the PHP version is too low for the application to run is now more informative, [#100] from [@jrfnl].
+- Composer: The package will now identify itself as a static analysis/linting tool, [#134] from [@staabm].
+- Composer: fix grammar error, [#139] from [@TravisCarden].
+- README: improvement to the install instructions, [#99] from [@samsonasik], fixes [#96].
+- README: move screenshot, [#97] from [@jrfnl].
+- README: fix typos, [#124] from [@krsriq].
+- Docs: code style consistency, [#137] from [@lens0021].
+
+### Internal
+- Prevent PHAR not being compatible with PHP < 7.0, [#116] from [@jrfnl].
+- GH Actions: update used actions, [#109], [#158] from [@jrfnl].
+- GH Actions: updates for box 4.x, [#121] from [@jrfnl].
+- GH Actions: fix download URL for box, [#125] from [@jrfnl].
+- GH Actions: use fail-fast with setup-php when creating the binaries, [#131], [#132] from [@jrfnl].
+- GH Actions: update PHP version for PHAR boxing, [#152] from [@jrfnl].
+- GH Actions: harden the workflow against PHPCS ruleset errors, [#128] from [@jrfnl].
+- GH Actions: bust the cache semi-regularly, [#129] from [@jrfnl].
+- GH Actions: update PHP versions in workflows, [#130] from [@jrfnl].
+- GH Actions: update for the release of PHP 8.3, [#150], [#151] from [@jrfnl].
+- GH Actions: fix duplicate release, [#159] from [@jrfnl].
+- SettingsParseArgumentsTest: fix bug in test, [#102] from [@jrfnl].
+- OutputTest: fix risky test, [#156] from [@jrfnl].
+- Tests: fix issue with Nette Tester 1.x, [#141] from [@grogy].
+- Add dependabot configuration file, [#148] from [@jrfnl].
+
+[1.4.0]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/compare/v1.3.2...v1.4.0
+
+[#62]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues/62
+[#93]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues/93
+[#96]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues/96
+[#97]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/97
+[#98]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/98
+[#99]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/99
+[#100]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/100
+[#102]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/102
+[#109]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/109
+[#116]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/116
+[#117]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/117
+[#118]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/118
+[#121]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/121
+[#124]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/124
+[#125]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/125
+[#128]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/128
+[#129]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/129
+[#130]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/130
+[#131]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/131
+[#132]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/132
+[#134]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/134
+[#137]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/137
+[#139]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/139
+[#140]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/140
+[#141]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/141
+[#146]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/146
+[#148]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/148
+[#150]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/150
+[#151]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/151
+[#152]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/152
+[#154]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/154
+[#156]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/156
+[#158]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/158
+[#159]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/159
+
+
+## [1.3.2] - 2022-02-19
+
+### Added
+
+- Support for PHP Console Highlighter 1.0.0, which comes with PHP Console Color 1.0.1, [#92] from [@jrfnl].
+
+### Fixed
+
+- Bug fix: make Phar file run independently of project under scan [#63] from [@jrfnl], fixes [#61].
+- Bug fix: checkstyle report could contain invalid XML due to insufficient output escaping [#73] from [@gmazzap], fixes [#72].
+- Fix Phar building [#70] from [@jrfnl]. This fixes PHP 8.1 compatibility for the Phar file.
+- Documentation fix: the `--show-deprecated` option was missing in both the README as well as the CLI `help` [#84] from [@jrfnl], fixes [#81] reported by [@stronk7].
+
+### Changed
+
+- README: updated information about PHAR availability [#77] from [@jrfnl].
+- README: updated CLI example [#80] from [@jrfnl].
+- README: added documentation on how to exclude files from a scan based on the PHP version used [#80] from [@jrfnl].
+- Composer autoload improvement [#88] from [@jrfnl] with thanks to [@mfn].
+
+### Internal
+
+- Welcome [@jrfnl] as a new maintainer [#32].
+- GH Actions: set error reporting to E_ALL [#65], [#76] from [@jrfnl].
+- GH Actions: fix failing tests on PHP 5.3-5.5 [#71] from [@jrfnl] and [@villfa].
+- GH Actions: auto-cancel concurrent builds [#76] from [@jrfnl].
+- GH Actions: testing against PHP 8.2 [#74] from [@grogy].
+- GH Actions: release testing against PHP 5.3 [#79] from [@jrfnl].
+- GH Actions: update used actions [#82] from [@jrfnl].
+- Release checklist can now be found in the `.github` folder [#78] from [@jrfnl].
+
+[1.3.2]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/compare/v1.3.1...v1.3.2
+
+[#32]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues/32
+[#61]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues/61
+[#63]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/63
+[#65]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/65
+[#70]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/70
+[#71]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/71
+[#72]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues/72
+[#73]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/73
+[#74]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/74
+[#76]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/76
+[#77]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/77
+[#78]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/78
+[#79]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/79
+[#80]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/80
+[#81]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues/81
+[#82]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/82
+[#84]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/84
+[#88]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/88
+[#89]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/89
+[#92]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/92
+
+
+## [1.3.1] - 2021-08-13
+
+### Added
+
+- Extend by the Code Climate output format [#50] from [@lukas9393]. 
+
+### Fixed
+
+- PHP 8.1: silence the deprecation notices about missing return types [#64] from [@jrfnl].
+
+### Internal
+
+- Reformat changelog to use reflinks in changelog entries [#58] from [@glensc].
+
+[1.3.1]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/compare/v1.3.0...v1.3.1
+
+[#50]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/50
+[#58]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/58
+[#64]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/64
+
+## [1.3.0] - 2021-04-07
+
+### Added
+
+- Allow for multi-part file extensions to be passed using -e (like `-e php,php.dist`) from [@jrfnl].
+- Added syntax error callback [#30] from [@arxeiss].
+- Ignore PHP startup errors [#34] from [@jrfnl].
+- Restore php 5.3 support [#51] from [@glensc].
+
+### Fixed
+
+- Determine skip lint process failure by status code instead of stderr content [#48] from [@jankonas].
+
+### Changed
+
+- Improve wording in the readme [#52] from [@glensc].
+
+### Internal
+
+- Normalized composer.json from [@OndraM].
+- Updated PHPCS dependency from [@jrfnl].
+- Cleaned coding style from [@jrfnl].
+- Provide one true way to run the test suite [#37] from [@mfn].
+- Travis: add build against PHP 8.0 and fix failing test [#41] from [@jrfnl].
+- GitHub Actions for testing, and automatic phar creation [#46] from [@roelofr].
+- Add .github folder to .gitattributes export-ignore [#54] from [@reedy].
+- Suggest to curl composer install via HTTPS [#53] from [@reedy].
+- GH Actions: allow for manually triggering a workflow [#55] from [@jrfnl].
+- GH Actions: fix phar creation [#55] from [@jrfnl].
+- GH Actions: run the tests against all supported PHP versions [#55] from [@jrfnl].
+- GH Actions: report CS violations in the PR [#55] from [@jrfnl].
+
+[1.3.0]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/compare/v1.2.0...v1.3.0
+[#30]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/30
+[#34]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/34
+[#37]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/37
+[#41]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/41
+[#46]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/46
+[#48]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/48
+[#51]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/51
+[#52]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/52
+[#53]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/53
+[#54]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/54
+[#55]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/55
+
+## [1.2.0] - 2020-04-04
+
+### Added
+
+- Added changelog.
+
+### Fixed
+
+- Fixed vendor location for running from other folder from [@Erkens].
+
+### Internal
+
+- Added a .gitattributes file from [@jrfnl], thanks for issue to [@ondrejmirtes].
+- Fixed incorrect unit tests from [@jrfnl].
+- Fixed minor grammatical errors from [@jrfnl].
+- Added Travis: test against nightly (= PHP 8) from [@jrfnl].
+- Travis: removed sudo from [@jrfnl].
+- Added info about installing like not a dependency.
+- Cleaned readme - new organization from previous package.
+- Added checklist for new version from [@szepeviktor].
+
+[1.2.0]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/compare/v1.1.0...v1.2.0
+
+[@arxeiss]:      https://github.com/arxeiss
+[@Ayesh]:        https://github.com/Ayesh
+[@Erkens]:       https://github.com/Erkens
+[@glensc]:       https://github.com/glensc
+[@gmazzap]:      https://github.com/gmazzap
+[@grogy]:        https://github.com/grogy
+[@jankonas]:     https://github.com/jankonas
+[@jrfnl]:        https://github.com/jrfnl
+[@krsriq]:       https://github.com/krsriq
+[@lens0021]:     https://github.com/lens0021
+[@lukas9393]:    https://github.com/lukas9393
+[@mfn]:          https://github.com/mfn
+[@OndraM]:       https://github.com/OndraM
+[@ondrejmirtes]: https://github.com/ondrejmirtes
+[@reedy]:        https://github.com/reedy
+[@robertology]:  https://github.com/robertology
+[@roelofr]:      https://github.com/roelofr
+[@samsonasik]:   https://github.com/samsonasik
+[@staabm]:       https://github.com/staabm
+[@stronk7]:      https://github.com/stronk7
+[@szepeviktor]:  https://github.com/szepeviktor
+[@TravisCarden]: https://github.com/TravisCarden
+[@villfa]:       https://github.com/villfa
+[@xaben]:        https://github.com/xaben
diff --git a/php-parallel-lint/php-parallel-lint/LICENSE b/php-parallel-lint/php-parallel-lint/LICENSE
new file mode 100644
index 0000000..09429bb
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/LICENSE
@@ -0,0 +1,26 @@
+Copyright (c) 2012, Jakub Onderka
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those
+of the authors and should not be interpreted as representing official policies,
+either expressed or implied, of the FreeBSD Project.
diff --git a/php-parallel-lint/php-parallel-lint/README.md b/php-parallel-lint/php-parallel-lint/README.md
new file mode 100644
index 0000000..4449f06
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/README.md
@@ -0,0 +1,122 @@
+# PHP Parallel Lint
+
+[![Downloads this Month](https://img.shields.io/packagist/dm/php-parallel-lint/php-parallel-lint.svg)](https://packagist.org/packages/php-parallel-lint/php-parallel-lint)
+[![Build Status](https://github.com/php-parallel-lint/PHP-Parallel-Lint/actions/workflows/test.yml/badge.svg)](https://github.com/php-parallel-lint/PHP-Parallel-Lint/actions/workflows/test.yml)
+[![License](https://poser.pugx.org/php-parallel-lint/php-parallel-lint/license.svg)](https://packagist.org/packages/php-parallel-lint/php-parallel-lint)
+
+This application checks the syntax of PHP files in parallel.
+It can output in plain text, colored text, json and checksyntax formats.
+Additionally `blame` can be used to show commits that introduced the breakage.
+
+Running parallel jobs in PHP is inspired by Nette framework tests.
+
+The application is officially supported for use with PHP 5.3 to 8.3.
+
+## Table of contents
+
+1. [Installation](#installation)
+2. [Example output](#example-output)
+3. [History](#history)
+4. [Command line options](#command-line-options)
+5. [Recommended excludes for Symfony framework](#recommended-excludes-for-symfony-framework)
+6. [Excluding files from a scan based on the PHP version used](#excluding-files-from-a-scan-based-on-the-php-version-used)
+7. [How to upgrade](#how-to-upgrade)
+
+## Installation
+
+Install with `composer` as development dependency:
+
+    composer require --dev php-parallel-lint/php-parallel-lint
+
+Alternatively you can install as a standalone `composer` project:
+
+    composer create-project php-parallel-lint/php-parallel-lint /path/to/folder/php-parallel-lint --no-dev
+    /path/to/folder/php-parallel-lint/parallel-lint # running tool
+
+For colored output, install the suggested package `php-parallel-lint/php-console-highlighter`:
+
+    composer require --dev php-parallel-lint/php-console-highlighter
+
+Since v1.3.0, a PHAR file is also made available for each release.
+This PHAR file is published as an asset for each release and can be found on the [Releases](https://github.com/php-parallel-lint/PHP-Parallel-Lint/releases) page.
+
+## Example output
+
+![Example use of tool with error](/doc/use-error.png?raw=true "Example use of tool with error")
+
+
+## History
+
+This project was originally created by [@JakubOnderka] and released as
+[jakub-onderka/php-parallel-lint].
+
+Since then, Jakub has moved on to other interests and as of January 2020, the
+second most active maintainer [@grogy] has taken over maintenance of the project
+and given the project - and related dependencies - a new home in the PHP
+Parallel Lint organisation.
+
+It is strongly recommended for existing users of the (unmaintained)
+[jakub-onderka/php-parallel-lint] package to switch their dependency to
+[php-parallel-lint/php-parallel-lint], see [How to upgrade](#how-to-upgrade) below.
+
+[php-parallel-lint/php-parallel-lint]: https://github.com/php-parallel-lint/PHP-Parallel-Lint
+[grogy/php-parallel-lint]: https://github.com/grogy/PHP-Parallel-Lint
+[jakub-onderka/php-parallel-lint]: https://github.com/JakubOnderka/PHP-Parallel-Lint
+[@JakubOnderka]: https://github.com/JakubOnderka
+[@grogy]: https://github.com/grogy
+
+## Command line options
+
+- `-p <php>`                Specify PHP-CGI executable to run (default: 'php').
+- `-s`, `--short`           Set short_open_tag to On (default: Off).
+- `-a`, `--asp`             Set asp_tags to On (default: Off).
+- `-e <ext>`                Check only files with selected extensions separated by comma. (default: php,php3,php4,php5,phtml,phpt)
+- `-j <num>`                Run <num> jobs in parallel (default: 10).
+- `--exclude`               Exclude a file or directory. If you want to exclude multiple items, use multiple exclude parameters.
+- `--colors`                Enable colors in console output. (disables auto detection of color support)
+- `--no-colors`             Disable colors in console output.
+- `--no-progress`           Disable progress in console output.
+- `--checkstyle`            Output results as Checkstyle XML.
+- `--json`                  Output results as JSON string (requires PHP 5.4).
+- `--gitlab`                Output results for the GitLab Code Quality Widget (requires PHP 5.4), see more in [Code Quality](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html) documentation..
+- `--blame`                 Try to show git blame for row with error.
+- `--git <git>`             Path to Git executable to show blame message (default: 'git').
+- `--stdin`                 Load files and folder to test from standard input.
+- `--ignore-fails`          Ignore failed tests.
+- `--show-deprecated`       Show deprecations (default: Off).
+- `--syntax-error-callback` File with syntax error callback for ability to modify error, see more in [example](doc/syntax-error-callback.md).
+- `-h`, `--help`            Print this help.
+- `-V`, `--version`         Display the application version
+
+
+## Recommended excludes for Symfony framework
+
+To run from the command line:
+
+    vendor/bin/parallel-lint --exclude .git --exclude app --exclude vendor .
+
+
+## Excluding files from a scan based on the PHP version used
+
+Sometimes a particular file in a project may not comply with the project-wide minimum PHP version, like a file which is conditionally included in the project and contains PHP syntax which needs a higher PHP version to run.
+
+This can make it complicated to run Parallel Lint in a CI context, as the `exclude`s used in the command would have to be adjusted based on the PHP version on which the scan is being run.
+
+PHP Parallel Lint offers a straight-forward way around this, as files can define their own minimum PHP version like so:
+```php
+<?php // lint >= 7.4
+
+// Code which contains PHP 7.4 syntax.
+```
+
+With this comment in place, the file will be automatically skipped when PHP Parallel Lint is run on a PHP version lower than PHP 7.4.
+
+Note: The `// lint >= 7.4` comment has to be only the first line of the file and must directly follow the PHP open tag.
+
+
+## How to upgrade
+
+Are you using the `jakub-onderka/php-parallel-lint` package? You can switch to `php-parallel-lint/php-parallel-lint` using:
+
+    composer remove --dev jakub-onderka/php-parallel-lint
+    composer require --dev php-parallel-lint/php-parallel-lint
diff --git a/php-parallel-lint/php-parallel-lint/bin/skip-linting.php b/php-parallel-lint/php-parallel-lint/bin/skip-linting.php
new file mode 100644
index 0000000..9cb552b
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/bin/skip-linting.php
@@ -0,0 +1,25 @@
+<?php
+$stdin = fopen('php://stdin', 'r');
+$input = stream_get_contents($stdin);
+fclose($stdin);
+
+foreach (explode(PHP_EOL, $input) as $file) {
+    $skip = false;
+    $f = @fopen($file, 'r');
+    if ($f) {
+        $firstLine = fgets($f);
+
+        // ignore shebang line
+        if (strpos($firstLine, '#!') === 0) {
+            $firstLine = fgets($f);
+        }
+
+        @fclose($f);
+
+        if (preg_match('~<?php\\s*\\/\\/\s*lint\s*([^\d\s]+)\s*([^\s]+)\s*~i', $firstLine, $m)) {
+            $skip = version_compare(PHP_VERSION, $m[2], $m[1]) === false;
+        }
+    }
+
+    echo $file . ';' . ($skip ? '1' : '0') . PHP_EOL;
+}
diff --git a/php-parallel-lint/php-parallel-lint/composer.json b/php-parallel-lint/php-parallel-lint/composer.json
new file mode 100644
index 0000000..cdec2a0
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/composer.json
@@ -0,0 +1,55 @@
+{
+    "name": "php-parallel-lint/php-parallel-lint",
+    "description": "This tool checks the syntax of PHP files about 20x faster than serial check.",
+    "homepage": "https://github.com/php-parallel-lint/PHP-Parallel-Lint",
+    "license": "BSD-2-Clause",
+    "keywords": [
+        "lint",
+        "static analysis"
+    ],
+    "authors": [
+        {
+            "name": "Jakub Onderka",
+            "email": "ahoj@jakubonderka.cz"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.0",
+        "ext-json": "*"
+    },
+    "replace": {
+        "grogy/php-parallel-lint": "*",
+        "jakub-onderka/php-parallel-lint": "*"
+    },
+    "require-dev": {
+        "nette/tester": "^1.3 || ^2.0",
+        "php-parallel-lint/php-console-highlighter": "0.* || ^1.0",
+        "squizlabs/php_codesniffer": "^3.6"
+    },
+    "suggest": {
+        "php-parallel-lint/php-console-highlighter": "Highlight syntax in code snippet"
+    },
+    "config": {
+        "sort-packages": true
+    },
+    "autoload": {
+        "classmap": [
+            "./src/"
+        ]
+    },
+    "autoload-dev": {
+        "classmap": [
+            "./tests/"
+        ]
+    },
+    "bin": [
+        "parallel-lint"
+    ],
+    "scripts": {
+        "test": "@php vendor/bin/tester -C -p php tests",
+        "testphp5": "@php vendor/bin/tester -c tests/php5.3-5.5.ini -p php tests"
+    },
+    "scripts-descriptions": {
+        "test": "Run all tests!"
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/parallel-lint b/php-parallel-lint/php-parallel-lint/parallel-lint
new file mode 100755
index 0000000..4e05f11
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/parallel-lint
@@ -0,0 +1,84 @@
+#!/usr/bin/env php
+<?php
+
+/*
+Copyright (c) 2014, Jakub Onderka
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those
+of the authors and should not be interpreted as representing official policies,
+either expressed or implied, of the FreeBSD Project.
+ */
+
+if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 50300) {
+    fwrite(
+        STDERR,
+        sprintf(
+            'PHP Parallel Lint requires PHP 5.3.0 or newer.' . PHP_EOL
+            . 'You are using PHP %s (%s).' . PHP_EOL,
+            PHP_VERSION,
+            PHP_BINDIR
+        )
+    );
+    exit(254);
+}
+
+$autoloadLocations = array(
+    getcwd() . '/vendor/autoload.php',
+    getcwd() . '/../../autoload.php',
+    __DIR__ . '/vendor/autoload.php',
+    __DIR__ . '/../vendor/autoload.php',
+    __DIR__ . '/../../../autoload.php',
+    __DIR__ . '/../../autoload.php',
+);
+
+if (class_exists('Phar') && Phar::running() !== '') {
+    // Running from a phar file. Prevent loading - potentially blocking - project autoload file.
+    $autoloadLocations = array(
+        __DIR__ . '/vendor/autoload.php',
+    );
+}
+
+$loaded = false;
+foreach ($autoloadLocations as $autoload) {
+    if (is_file($autoload)) {
+        require_once($autoload);
+        $loaded = true;
+    }
+}
+
+if (!$loaded) {
+    fwrite(STDERR,
+        'You must set up the project dependencies, run the following commands:' . PHP_EOL .
+        'curl -s https://getcomposer.org/installer | php' . PHP_EOL .
+        'php composer.phar install' . PHP_EOL
+    );
+    exit(254);
+}
+
+require_once __DIR__ . '/src/polyfill.php';
+
+// Prevent parse error on PHP < 5.3 so the version check above can work.
+$className = 'JakubOnderka\PhpParallelLint\Application';
+$app = new $className();
+exit($app->run());
diff --git a/php-parallel-lint/php-parallel-lint/src/Application.php b/php-parallel-lint/php-parallel-lint/src/Application.php
new file mode 100644
index 0000000..0cbab11
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Application.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace JakubOnderka\PhpParallelLint;
+
+class Application
+{
+    const VERSION = '1.4.0';
+
+    // Return codes
+    const SUCCESS = 0,
+        WITH_ERRORS = 1,
+        FAILED = 254; // Error code 255 is reserved for PHP itself
+
+    /**
+     * Run the application
+     * @return int Return code
+     */
+    public function run()
+    {
+        if (in_array('proc_open', explode(',', ini_get('disable_functions')))) {
+            echo "Function 'proc_open' is required, but it is disabled by the 'disable_functions' ini setting.", PHP_EOL;
+            return self::FAILED;
+        }
+
+        if (in_array('-h', $_SERVER['argv']) || in_array('--help', $_SERVER['argv'])) {
+            $this->showUsage();
+            return self::SUCCESS;
+        }
+
+        if (in_array('-V', $_SERVER['argv']) || in_array('--version', $_SERVER['argv'])) {
+            $this->showVersion();
+            return self::SUCCESS;
+        }
+
+        try {
+            $settings = Settings::parseArguments($_SERVER['argv']);
+            if ($settings->stdin) {
+                $settings->addPaths(Settings::getPathsFromStdIn());
+            }
+            if (empty($settings->paths)) {
+                $this->showUsage();
+                return self::FAILED;
+            }
+            $manager = new Manager;
+            $result = $manager->run($settings);
+            if ($settings->ignoreFails) {
+                return $result->hasSyntaxError() ? self::WITH_ERRORS : self::SUCCESS;
+            } else {
+                return $result->hasError() ? self::WITH_ERRORS : self::SUCCESS;
+            }
+
+        } catch (InvalidArgumentException $e) {
+            echo "Invalid option {$e->getArgument()}", PHP_EOL, PHP_EOL;
+            $this->showOptions();
+            return self::FAILED;
+
+        } catch (Exception $e) {
+            if (isset($settings) && $settings->format === Settings::FORMAT_JSON) {
+                echo json_encode($e);
+            } else {
+                echo $e->getMessage(), PHP_EOL;
+            }
+            return self::FAILED;
+
+        } catch (\Exception $e) {
+            echo $e->getMessage(), PHP_EOL;
+            return self::FAILED;
+        }
+    }
+
+    /**
+     * Outputs the options
+     */
+    private function showOptions()
+    {
+        echo <<<HELP
+Options:
+    -p <php>                Specify PHP-CGI executable to run (default: 'php').
+    -s, --short             Set short_open_tag to On (default: Off).
+    -a, --asp               Set asp_tags to On (default: Off).
+    -e <ext>                Check only files with selected extensions separated by comma.
+                            (default: php,php3,php4,php5,phtml,phpt)
+    -j <num>                Run <num> jobs in parallel (default: 10).
+    --exclude               Exclude a file or directory. If you want exclude multiple items,
+                            use multiple exclude parameters.
+    --colors                Enable colors in console output.
+                            (disables auto detection of color support)
+    --no-colors             Disable colors in console output.
+    --no-progress           Disable progress in console output.
+    --checkstyle            Output results as Checkstyle XML.
+    --json                  Output results as JSON string
+                            (requires PHP 5.4).
+    --gitlab                Output results for the GitLab Code Quality Widget
+                            (requires PHP 5.4).
+    --blame                 Try to show git blame for row with error.
+    --git <git>             Path to Git executable to show blame message (default: 'git').
+    --stdin                 Load files and folder to test from standard input.
+    --ignore-fails          Ignore failed tests.
+    --show-deprecated       Show deprecations (default: Off).
+    --syntax-error-callback File with syntax error callback for ability to modify error
+    -h, --help              Print this help.
+    -V, --version           Display the application version
+
+HELP;
+    }
+
+    /**
+     * Outputs the current version
+     */
+    private function showVersion()
+    {
+        echo 'PHP Parallel Lint version ' . self::VERSION.PHP_EOL;
+    }
+
+    /**
+     * Shows usage
+     */
+    private function showUsage()
+    {
+        $this->showVersion();
+        echo <<<USAGE
+-------------------------------
+Usage:
+parallel-lint [sa] [-p php] [-e ext] [-j num] [--exclude dir] [files or directories]
+
+USAGE;
+        $this->showOptions();
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Contracts/SyntaxErrorCallback.php b/php-parallel-lint/php-parallel-lint/src/Contracts/SyntaxErrorCallback.php
new file mode 100644
index 0000000..0db055f
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Contracts/SyntaxErrorCallback.php
@@ -0,0 +1,13 @@
+<?php
+namespace JakubOnderka\PhpParallelLint\Contracts;
+
+use JakubOnderka\PhpParallelLint\SyntaxError;
+
+interface SyntaxErrorCallback
+{
+    /**
+     * @param SyntaxError $error
+     * @return SyntaxError
+     */
+    public function errorFound(SyntaxError $error);
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Error.php b/php-parallel-lint/php-parallel-lint/src/Error.php
new file mode 100644
index 0000000..61cace9
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Error.php
@@ -0,0 +1,242 @@
+<?php
+namespace JakubOnderka\PhpParallelLint;
+
+use ReturnTypeWillChange;
+
+class Error implements \JsonSerializable
+{
+    /** @var string */
+    protected $filePath;
+
+    /** @var string */
+    protected $message;
+
+    /**
+     * @param string $filePath
+     * @param string $message
+     */
+    public function __construct($filePath, $message)
+    {
+        $this->filePath = $filePath;
+        $this->message = rtrim($message);
+    }
+
+    /**
+     * @return string
+     */
+    public function getMessage()
+    {
+        return $this->message;
+    }
+
+    /**
+     * @return string
+     */
+    public function getFilePath()
+    {
+        return $this->filePath;
+    }
+
+    /**
+     * @return string
+     */
+    public function getShortFilePath()
+    {
+        $cwd = getcwd();
+
+        if ($cwd === '/') {
+            // For root directory in unix, do not modify path
+            return $this->filePath;
+        }
+
+        return preg_replace('/' . preg_quote($cwd, '/') . '/', '', $this->filePath, 1);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.4.0)<br/>
+     * Specify data which should be serialized to JSON
+     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
+     * @return mixed data which can be serialized by <b>json_encode</b>,
+     * which is a value of any type other than a resource.
+     */
+    #[ReturnTypeWillChange]
+    public function jsonSerialize()
+    {
+        return array(
+            'type' => 'error',
+            'file' => $this->getFilePath(),
+            'message' => $this->getMessage(),
+        );
+    }
+}
+
+class Blame implements \JsonSerializable
+{
+    public $name;
+
+    public $email;
+
+    /** @var \DateTime */
+    public $datetime;
+
+    public $commitHash;
+
+    public $summary;
+
+    /**
+     * (PHP 5 &gt;= 5.4.0)<br/>
+     * Specify data which should be serialized to JSON
+     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
+     * @return mixed data which can be serialized by <b>json_encode</b>,
+     * which is a value of any type other than a resource.
+     */
+    #[ReturnTypeWillChange]
+    function jsonSerialize()
+    {
+        return array(
+            'name' => $this->name,
+            'email' => $this->email,
+            'datetime' => $this->datetime,
+            'commitHash' => $this->commitHash,
+            'summary' => $this->summary,
+        );
+    }
+
+
+}
+
+class SyntaxError extends Error
+{
+    const IN_ON_REGEX = '~ in %s on line [0-9]+$~';
+
+    /** @var Blame */
+    private $blame;
+
+    /**
+     * @return int|null
+     */
+    public function getLine()
+    {
+        preg_match('~on line ([0-9]+)$~', $this->message, $matches);
+
+        if ($matches && isset($matches[1])) {
+            $onLine = (int) $matches[1];
+            return $onLine;
+        }
+
+        return null;
+    }
+
+    /**
+     * @param bool $translateTokens
+     * @return mixed|string
+     */
+    public function getNormalizedMessage($translateTokens = false)
+    {
+        $message  = preg_replace('~^(Parse|Fatal) error: (syntax error, )?~', '', $this->message);
+        $baseName = basename($this->filePath);
+        $regex    = sprintf(self::IN_ON_REGEX, preg_quote($baseName, '~'));
+        $message  = preg_replace($regex, '', $message, -1, $count);
+
+        if ($count === 0 && strpos($baseName, '\\') !== false) {
+            $baseName = ltrim(strrchr($this->filePath, '\\'), '\\');
+            $regex    = sprintf(self::IN_ON_REGEX, preg_quote($baseName, '~'));
+            $message  = preg_replace($regex, '', $message, -1, $count);
+        }
+
+        if ($count === 0) {
+            $regex   = sprintf(self::IN_ON_REGEX, preg_quote($this->filePath, '~'));
+            $message = preg_replace($regex, '', $message);
+        }
+
+        $message = ucfirst($message);
+
+        if ($translateTokens) {
+            $message = $this->translateTokens($message);
+        }
+
+        return $message;
+    }
+
+    /**
+     * @param Blame $blame
+     */
+    public function setBlame(Blame $blame)
+    {
+        $this->blame = $blame;
+    }
+
+    /**
+     * @return Blame
+     */
+    public function getBlame()
+    {
+        return $this->blame;
+    }
+
+    /**
+     * @param string $message
+     * @return string
+     */
+    protected function translateTokens($message)
+    {
+        static $translateTokens = array(
+            'T_FILE' => '__FILE__',
+            'T_FUNC_C' => '__FUNCTION__',
+            'T_HALT_COMPILER' => '__halt_compiler()',
+            'T_INC' => '++',
+            'T_IS_EQUAL' => '==',
+            'T_IS_GREATER_OR_EQUAL' => '>=',
+            'T_IS_IDENTICAL' => '===',
+            'T_IS_NOT_IDENTICAL' => '!==',
+            'T_IS_SMALLER_OR_EQUAL' => '<=',
+            'T_LINE' => '__LINE__',
+            'T_METHOD_C' => '__METHOD__',
+            'T_MINUS_EQUAL' => '-=',
+            'T_MOD_EQUAL' => '%=',
+            'T_MUL_EQUAL' => '*=',
+            'T_NS_C' => '__NAMESPACE__',
+            'T_NS_SEPARATOR' => '\\',
+            'T_OBJECT_OPERATOR' => '->',
+            'T_OR_EQUAL' => '|=',
+            'T_PAAMAYIM_NEKUDOTAYIM' => '::',
+            'T_PLUS_EQUAL' => '+=',
+            'T_SL' => '<<',
+            'T_SL_EQUAL' => '<<=',
+            'T_SR' => '>>',
+            'T_SR_EQUAL' => '>>=',
+            'T_START_HEREDOC' => '<<<',
+            'T_XOR_EQUAL' => '^=',
+            'T_ECHO' => 'echo'
+        );
+
+        return preg_replace_callback('~(?<!\()T_([A-Z_]*)(?!\))~', function ($matches) use ($translateTokens) {
+            list($tokenName) = $matches;
+            if (isset($translateTokens[$tokenName])) {
+                $operator = $translateTokens[$tokenName];
+                return "$operator ($tokenName)";
+            }
+
+            return $tokenName;
+        }, $message);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.4.0)<br/>
+     * Specify data which should be serialized to JSON
+     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
+     * @return mixed data which can be serialized by <b>json_encode</b>,
+     * which is a value of any type other than a resource.
+     */
+    public function jsonSerialize()
+    {
+        return array(
+            'type' => 'syntaxError',
+            'file' => $this->getFilePath(),
+            'line' => $this->getLine(),
+            'message' => $this->getMessage(),
+            'normalizeMessage' => $this->getNormalizedMessage(),
+            'blame' => $this->blame,
+        );
+    }
+}
\ No newline at end of file
diff --git a/php-parallel-lint/php-parallel-lint/src/ErrorFormatter.php b/php-parallel-lint/php-parallel-lint/src/ErrorFormatter.php
new file mode 100644
index 0000000..ff8757b
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/ErrorFormatter.php
@@ -0,0 +1,141 @@
+<?php
+namespace JakubOnderka\PhpParallelLint;
+
+use JakubOnderka\PhpConsoleColor\ConsoleColor as OldConsoleColor;
+use JakubOnderka\PhpConsoleHighlighter\Highlighter as OldHighlighter;
+use PHP_Parallel_Lint\PhpConsoleColor\ConsoleColor;
+use PHP_Parallel_Lint\PhpConsoleHighlighter\Highlighter;
+
+class ErrorFormatter
+{
+    /** @var string */
+    private $useColors;
+
+    /** @var bool */
+    private $forceColors;
+
+    /** @var bool */
+    private $translateTokens;
+
+    public function __construct($useColors = Settings::AUTODETECT, $translateTokens = false, $forceColors = false)
+    {
+        $this->useColors = $useColors;
+        $this->forceColors = $forceColors;
+        $this->translateTokens = $translateTokens;
+    }
+
+    /**
+     * @param Error $error
+     * @return string
+     */
+    public function format(Error $error)
+    {
+        if ($error instanceof SyntaxError) {
+            return $this->formatSyntaxErrorMessage($error);
+        } else {
+            if ($error->getMessage()) {
+                return $error->getMessage();
+            } else {
+                return "Unknown error for file '{$error->getFilePath()}'.";
+            }
+        }
+    }
+
+    /**
+     * @param SyntaxError $error
+     * @param bool $withCodeSnipped
+     * @return string
+     */
+    public function formatSyntaxErrorMessage(SyntaxError $error, $withCodeSnipped = true)
+    {
+        $string = "Parse error: {$error->getShortFilePath()}";
+
+        if ($error->getLine()) {
+            $onLine = $error->getLine();
+            $string .= ":$onLine" . PHP_EOL;
+
+            if ($withCodeSnipped) {
+                if ($this->useColors !== Settings::DISABLED) {
+                    $string .= $this->getColoredCodeSnippet($error->getFilePath(), $onLine);
+                } else {
+                    $string .= $this->getCodeSnippet($error->getFilePath(), $onLine);
+                }
+                $string = rtrim($string) . PHP_EOL;
+            }
+        }
+
+        $string .= $error->getNormalizedMessage($this->translateTokens);
+
+        if ($error->getBlame()) {
+            $blame = $error->getBlame();
+            $shortCommitHash = substr($blame->commitHash, 0, 8);
+            $dateTime = $blame->datetime->format('c');
+            $string .= PHP_EOL . "Blame {$blame->name} <{$blame->email}>, commit '$shortCommitHash' from $dateTime";
+        }
+
+        return $string;
+    }
+
+    /**
+     * @param string $filePath
+     * @param int $lineNumber
+     * @param int $linesBefore
+     * @param int $linesAfter
+     * @return string
+     */
+    protected function getCodeSnippet($filePath, $lineNumber, $linesBefore = 2, $linesAfter = 2)
+    {
+        $lines = file($filePath);
+
+        $offset = $lineNumber - $linesBefore - 1;
+        $offset = max($offset, 0);
+        $length = $linesAfter + $linesBefore + 1;
+        $lines = array_slice($lines, $offset, $length, $preserveKeys = true);
+
+        end($lines);
+        $lineStrlen = strlen(key($lines) + 1);
+
+        $snippet = '';
+        foreach ($lines as $i => $line) {
+            $snippet .= ($lineNumber === $i + 1 ? '  > ' : '    ');
+            $snippet .= str_pad($i + 1, $lineStrlen, ' ', STR_PAD_LEFT) . '| ' . rtrim($line) . PHP_EOL;
+        }
+
+        return $snippet;
+    }
+
+    /**
+     * @param string $filePath
+     * @param int $lineNumber
+     * @param int $linesBefore
+     * @param int $linesAfter
+     * @return string
+     */
+    protected function getColoredCodeSnippet($filePath, $lineNumber, $linesBefore = 2, $linesAfter = 2)
+    {
+        if (
+            class_exists('\PHP_Parallel_Lint\PhpConsoleHighlighter\Highlighter')
+            && class_exists('\PHP_Parallel_Lint\PhpConsoleColor\ConsoleColor')
+        ) {
+            // Highlighter and ConsoleColor 1.0+.
+            $colors = new ConsoleColor();
+            $colors->setForceStyle($this->forceColors);
+            $highlighter = new Highlighter($colors);
+        } else if (
+            class_exists('\JakubOnderka\PhpConsoleHighlighter\Highlighter')
+            && class_exists('\JakubOnderka\PhpConsoleColor\ConsoleColor')
+        ) {
+            // Highlighter and ConsoleColor < 1.0.
+            $colors = new OldConsoleColor();
+            $colors->setForceStyle($this->forceColors);
+            $highlighter = new OldHighlighter($colors);
+        }
+
+        if (isset($colors, $highlighter) === false) {
+            return $this->getCodeSnippet($filePath, $lineNumber, $linesBefore, $linesAfter);
+        }
+
+        $fileContent = file_get_contents($filePath);
+        return $highlighter->getCodeSnippet($fileContent, $lineNumber, $linesBefore, $linesAfter);
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Manager.php b/php-parallel-lint/php-parallel-lint/src/Manager.php
new file mode 100644
index 0000000..31d26ad
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Manager.php
@@ -0,0 +1,293 @@
+<?php
+namespace JakubOnderka\PhpParallelLint;
+
+use JakubOnderka\PhpParallelLint\Contracts\SyntaxErrorCallback;
+use JakubOnderka\PhpParallelLint\Process\GitBlameProcess;
+use JakubOnderka\PhpParallelLint\Process\PhpExecutable;
+use ReturnTypeWillChange;
+
+class Manager
+{
+    /** @var Output */
+    protected $output;
+
+    /**
+     * @param null|Settings $settings
+     * @return Result
+     * @throws Exception
+     * @throws \Exception
+     */
+    public function run($settings = null)
+    {
+        $settings = ($settings instanceof Settings) ? $settings : new Settings();
+        $output = $this->output ?: $this->getDefaultOutput($settings);
+
+        $phpExecutable = PhpExecutable::getPhpExecutable($settings->phpExecutable);
+        $olderThanPhp54 = $phpExecutable->getVersionId() < 50400; // From PHP version 5.4 are tokens translated by default
+        $translateTokens = $phpExecutable->isIsHhvmType() || $olderThanPhp54;
+
+        $output->writeHeader($phpExecutable->getVersionId(), $settings->parallelJobs, $phpExecutable->getHhvmVersion());
+
+        $files = $this->getFilesFromPaths($settings->paths, $settings->extensions, $settings->excluded);
+
+        if (empty($files)) {
+            throw new Exception('No file found to check.');
+        }
+
+        $output->setTotalFileCount(count($files));
+
+        $parallelLint = new ParallelLint($phpExecutable, $settings->parallelJobs);
+        $parallelLint->setAspTagsEnabled($settings->aspTags);
+        $parallelLint->setShortTagEnabled($settings->shortTag);
+        $parallelLint->setShowDeprecated($settings->showDeprecated);
+        $parallelLint->setSyntaxErrorCallback($this->createSyntaxErrorCallback($settings));
+
+        $parallelLint->setProcessCallback(function ($status, $file) use ($output) {
+            if ($status === ParallelLint::STATUS_OK) {
+                $output->ok();
+            } else if ($status === ParallelLint::STATUS_SKIP) {
+                $output->skip();
+            } else if ($status === ParallelLint::STATUS_ERROR) {
+                $output->error();
+            } else {
+                $output->fail();
+            }
+        });
+
+        $result = $parallelLint->lint($files);
+
+        if ($settings->blame) {
+            $this->gitBlame($result, $settings);
+        }
+
+        $output->writeResult($result, new ErrorFormatter($settings->colors, $translateTokens), $settings->ignoreFails);
+
+        return $result;
+    }
+
+    /**
+     * @param Output $output
+     */
+    public function setOutput(Output $output)
+    {
+        $this->output = $output;
+    }
+
+    /**
+     * @param Settings $settings
+     * @return Output
+     */
+    protected function getDefaultOutput(Settings $settings)
+    {
+        $writer = new ConsoleWriter;
+        switch ($settings->format) {
+            case Settings::FORMAT_JSON:
+                return new JsonOutput($writer);
+            case Settings::FORMAT_GITLAB:
+                return new GitLabOutput($writer);
+            case Settings::FORMAT_CHECKSTYLE:
+                return new CheckstyleOutput($writer);
+        }
+
+        if ($settings->colors === Settings::DISABLED) {
+            $output = new TextOutput($writer);
+        } else {
+            $output = new TextOutputColored($writer, $settings->colors);
+        }
+
+        $output->showProgress = $settings->showProgress;
+
+        return $output;
+    }
+
+    /**
+     * @param Result $result
+     * @param Settings $settings
+     * @throws Exception
+     */
+    protected function gitBlame(Result $result, Settings $settings)
+    {
+        if (!GitBlameProcess::gitExists($settings->gitExecutable)) {
+            return;
+        }
+
+        foreach ($result->getErrors() as $error) {
+            if ($error instanceof SyntaxError) {
+                $process = new GitBlameProcess($settings->gitExecutable, $error->getFilePath(), $error->getLine());
+                $process->waitForFinish();
+
+                if ($process->isSuccess()) {
+                    $blame = new Blame;
+                    $blame->name = $process->getAuthor();
+                    $blame->email = $process->getAuthorEmail();
+                    $blame->datetime = $process->getAuthorTime();
+                    $blame->commitHash = $process->getCommitHash();
+                    $blame->summary = $process->getSummary();
+
+                    $error->setBlame($blame);
+                }
+            }
+        }
+    }
+
+    /**
+     * @param array $paths
+     * @param array $extensions
+     * @param array $excluded
+     * @return array
+     * @throws NotExistsPathException
+     */
+    protected function getFilesFromPaths(array $paths, array $extensions, array $excluded = array())
+    {
+        $extensions = array_map('preg_quote', $extensions, array_fill(0, count($extensions), '`'));
+        $regex = '`\.(?:' . implode('|', $extensions) . ')$`iD';
+        $files = array();
+
+        foreach ($paths as $path) {
+            if (is_file($path)) {
+                $files[] = $path;
+            } else if (is_dir($path)) {
+                $iterator = new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS);
+                if (!empty($excluded)) {
+                    $iterator = new RecursiveDirectoryFilterIterator($iterator, $excluded);
+                }
+                $iterator = new \RecursiveIteratorIterator(
+                    $iterator,
+                    \RecursiveIteratorIterator::LEAVES_ONLY,
+                    \RecursiveIteratorIterator::CATCH_GET_CHILD
+                );
+
+                $iterator = new \RegexIterator($iterator, $regex);
+
+                /** @var \SplFileInfo[] $iterator */
+                foreach ($iterator as $directoryFile) {
+                    $files[] = (string) $directoryFile;
+                }
+            } else {
+                throw new NotExistsPathException($path);
+            }
+        }
+
+        $files = array_unique($files);
+
+        return $files;
+    }
+
+    protected function createSyntaxErrorCallback(Settings $settings)
+    {
+        if ($settings->syntaxErrorCallbackFile === null) {
+            return null;
+        }
+
+        $fullFilePath = realpath($settings->syntaxErrorCallbackFile);
+        if ($fullFilePath === false) {
+            throw new NotExistsPathException($settings->syntaxErrorCallbackFile);
+        }
+
+        require_once $fullFilePath;
+
+        $expectedClassName = basename($fullFilePath, '.php');
+        if (!class_exists($expectedClassName)) {
+            throw new NotExistsClassException($expectedClassName, $settings->syntaxErrorCallbackFile);
+        }
+
+        $callbackInstance = new $expectedClassName;
+
+        if (!($callbackInstance instanceof SyntaxErrorCallback)) {
+            throw new NotImplementCallbackException($expectedClassName);
+        }
+
+        return $callbackInstance;
+    }
+}
+
+class RecursiveDirectoryFilterIterator extends \RecursiveFilterIterator
+{
+    /** @var \RecursiveDirectoryIterator */
+    private $iterator;
+
+    /** @var array */
+    private $excluded = array();
+
+    /**
+     * @param \RecursiveDirectoryIterator $iterator
+     * @param array $excluded
+     */
+    public function __construct(\RecursiveDirectoryIterator $iterator, array $excluded)
+    {
+        parent::__construct($iterator);
+        $this->iterator = $iterator;
+        $this->excluded = array_map(array($this, 'getPathname'), $excluded);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.1.0)<br/>
+     * Check whether the current element of the iterator is acceptable
+     *
+     * @link http://php.net/manual/en/filteriterator.accept.php
+     * @return bool true if the current element is acceptable, otherwise false.
+     */
+    #[ReturnTypeWillChange]
+    public function accept()
+    {
+        $current = $this->current()->getPathname();
+        $current = $this->normalizeDirectorySeparator($current);
+
+        if ('.' . DIRECTORY_SEPARATOR !== $current[0] . $current[1]) {
+            $current = '.' . DIRECTORY_SEPARATOR . $current;
+        }
+
+        return !in_array($current, $this->excluded);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.1.0)<br/>
+     * Check whether the inner iterator's current element has children
+     *
+     * @link http://php.net/manual/en/recursivefilteriterator.haschildren.php
+     * @return bool true if the inner iterator has children, otherwise false
+     */
+    #[ReturnTypeWillChange]
+    public function hasChildren()
+    {
+        return $this->iterator->hasChildren();
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.1.0)<br/>
+     * Return the inner iterator's children contained in a RecursiveFilterIterator
+     *
+     * @link http://php.net/manual/en/recursivefilteriterator.getchildren.php
+     * @return \RecursiveFilterIterator containing the inner iterator's children.
+     */
+    #[ReturnTypeWillChange]
+    public function getChildren()
+    {
+        return new self($this->iterator->getChildren(), $this->excluded);
+    }
+
+    /**
+     * @param string $file
+     * @return string
+     */
+    private function getPathname($file)
+    {
+        $file = $this->normalizeDirectorySeparator($file);
+
+        if ('.' . DIRECTORY_SEPARATOR !== $file[0] . $file[1]) {
+            $file = '.' . DIRECTORY_SEPARATOR . $file;
+        }
+
+        $directoryFile = new \SplFileInfo($file);
+        return $directoryFile->getPathname();
+    }
+
+    /**
+     * @param string $file
+     * @return string
+     */
+    private function normalizeDirectorySeparator($file)
+    {
+        return str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $file);
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Output.php b/php-parallel-lint/php-parallel-lint/src/Output.php
new file mode 100644
index 0000000..dc2c94a
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Output.php
@@ -0,0 +1,594 @@
+<?php
+namespace JakubOnderka\PhpParallelLint;
+
+interface Output
+{
+    public function __construct(IWriter $writer);
+
+    public function ok();
+
+    public function skip();
+
+    public function error();
+
+    public function fail();
+
+    public function setTotalFileCount($count);
+
+    public function writeHeader($phpVersion, $parallelJobs, $hhvmVersion = null);
+
+    public function writeResult(Result $result, ErrorFormatter $errorFormatter, $ignoreFails);
+}
+
+class JsonOutput implements Output
+{
+    /** @var IWriter */
+    protected $writer;
+
+    /** @var int */
+    protected $phpVersion;
+
+    /** @var int */
+    protected $parallelJobs;
+
+    /** @var string */
+    protected $hhvmVersion;
+
+    /**
+     * @param IWriter $writer
+     */
+    public function __construct(IWriter $writer)
+    {
+        $this->writer = $writer;
+    }
+
+    public function ok()
+    {
+
+    }
+
+    public function skip()
+    {
+
+    }
+
+    public function error()
+    {
+
+    }
+
+    public function fail()
+    {
+
+    }
+
+    public function setTotalFileCount($count)
+    {
+
+    }
+
+    public function writeHeader($phpVersion, $parallelJobs, $hhvmVersion = null)
+    {
+        $this->phpVersion = $phpVersion;
+        $this->parallelJobs = $parallelJobs;
+        $this->hhvmVersion = $hhvmVersion;
+    }
+
+    public function writeResult(Result $result, ErrorFormatter $errorFormatter, $ignoreFails)
+    {
+        echo json_encode(array(
+            'phpVersion' => $this->phpVersion,
+            'hhvmVersion' => $this->hhvmVersion,
+            'parallelJobs' => $this->parallelJobs,
+            'results' => $result,
+        ));
+    }
+}
+
+class GitLabOutput implements Output
+{
+    /** @var IWriter */
+    protected $writer;
+
+    /**
+     * @param IWriter $writer
+     */
+    public function __construct(IWriter $writer)
+    {
+        $this->writer = $writer;
+    }
+
+    public function ok()
+    {
+
+    }
+
+    public function skip()
+    {
+
+    }
+
+    public function error()
+    {
+
+    }
+
+    public function fail()
+    {
+
+    }
+
+    public function setTotalFileCount($count)
+    {
+
+    }
+
+    public function writeHeader($phpVersion, $parallelJobs, $hhvmVersion = null)
+    {
+
+    }
+
+    public function writeResult(Result $result, ErrorFormatter $errorFormatter, $ignoreFails)
+    {
+        $errors = array();
+        foreach ($result->getErrors() as $error) {
+            $message = $error->getMessage();
+            $line = 1;
+            if ($error instanceof SyntaxError) {
+                $line = $error->getLine();
+            }
+            $filePath = $error->getFilePath();
+            $result = array(
+                'type' => 'issue',
+                'check_name' => 'Parse error',
+                'description' => $message,
+                'categories' => 'Style',
+                'fingerprint' => md5($filePath . $message . $line),
+                'severity' => 'minor',
+                'location' => array(
+                    'path' => $filePath,
+                    'lines' => array(
+                        'begin' => $line,
+                    ),
+                ),
+            );
+            array_push($errors, $result);
+        }
+
+        $string = json_encode($errors) . PHP_EOL;
+        $this->writer->write($string);
+    }
+}
+
+class TextOutput implements Output
+{
+    const TYPE_DEFAULT = 'default',
+        TYPE_SKIP = 'skip',
+        TYPE_ERROR = 'error',
+        TYPE_FAIL = 'fail',
+        TYPE_OK = 'ok';
+
+    /** @var int */
+    public $filesPerLine = 60;
+
+    /** @var bool */
+    public $showProgress = true;
+
+    /** @var int */
+    protected $checkedFiles;
+
+    /** @var int */
+    protected $totalFileCount;
+
+    /** @var IWriter */
+    protected $writer;
+
+    /**
+     * @param IWriter $writer
+     */
+    public function __construct(IWriter $writer)
+    {
+        $this->writer = $writer;
+    }
+
+    public function ok()
+    {
+        $this->writeMark(self::TYPE_OK);
+    }
+
+    public function skip()
+    {
+        $this->writeMark(self::TYPE_SKIP);
+    }
+
+    public function error()
+    {
+        $this->writeMark(self::TYPE_ERROR);
+    }
+
+    public function fail()
+    {
+        $this->writeMark(self::TYPE_FAIL);
+    }
+
+    /**
+     * @param string $string
+     * @param string $type
+     */
+    public function write($string, $type = self::TYPE_DEFAULT)
+    {
+        $this->writer->write($string);
+    }
+
+    /**
+     * @param string|null $line
+     * @param string $type
+     */
+    public function writeLine($line = null, $type = self::TYPE_DEFAULT)
+    {
+        $this->write($line, $type);
+        $this->writeNewLine();
+    }
+
+    /**
+     * @param int $count
+     */
+    public function writeNewLine($count = 1)
+    {
+        $this->write(str_repeat(PHP_EOL, $count));
+    }
+
+    /**
+     * @param int $count
+     */
+    public function setTotalFileCount($count)
+    {
+        $this->totalFileCount = $count;
+    }
+
+    /**
+     * @param int $phpVersion
+     * @param int $parallelJobs
+     * @param string $hhvmVersion
+     */
+    public function writeHeader($phpVersion, $parallelJobs, $hhvmVersion = null)
+    {
+        $this->write("PHP {$this->phpVersionIdToString($phpVersion)} | ");
+
+        if ($hhvmVersion) {
+            $this->write("HHVM $hhvmVersion | ");
+        }
+
+        if ($parallelJobs === 1) {
+            $this->writeLine("1 job");
+        } else {
+            $this->writeLine("{$parallelJobs} parallel jobs");
+        }
+    }
+
+    /**
+     * @param Result $result
+     * @param ErrorFormatter $errorFormatter
+     * @param bool $ignoreFails
+     */
+    public function writeResult(Result $result, ErrorFormatter $errorFormatter, $ignoreFails)
+    {
+        if ($this->showProgress) {
+            if ($this->checkedFiles % $this->filesPerLine !== 0) {
+                $rest = $this->filesPerLine - ($this->checkedFiles % $this->filesPerLine);
+                $this->write(str_repeat(' ', $rest));
+                $this->writePercent();
+            }
+
+            $this->writeNewLine(2);
+        }
+
+        $testTime = round($result->getTestTime(), 1);
+        $message = "Checked {$result->getCheckedFilesCount()} files in $testTime ";
+        $message .= $testTime == 1 ? 'second' : 'seconds';
+
+        if ($result->getSkippedFilesCount() > 0) {
+            $message .= ", skipped {$result->getSkippedFilesCount()} ";
+            $message .= ($result->getSkippedFilesCount() === 1 ? 'file' : 'files');
+        }
+
+        $this->writeLine($message);
+
+        if (!$result->hasSyntaxError()) {
+            $message = "No syntax error found";
+        } else {
+            $message = "Syntax error found in {$result->getFilesWithSyntaxErrorCount()} ";
+            $message .= ($result->getFilesWithSyntaxErrorCount() === 1 ? 'file' : 'files');
+        }
+
+        if ($result->hasFilesWithFail()) {
+            $message .= ", failed to check {$result->getFilesWithFailCount()} ";
+            $message .= ($result->getFilesWithFailCount() === 1 ? 'file' : 'files');
+
+            if ($ignoreFails) {
+                $message .= ' (ignored)';
+            }
+        }
+
+        $hasError = $ignoreFails ? $result->hasSyntaxError() : $result->hasError();
+        $this->writeLine($message, $hasError ? self::TYPE_ERROR : self::TYPE_OK);
+
+        if ($result->hasError()) {
+            $this->writeNewLine();
+            foreach ($result->getErrors() as $error) {
+                $this->writeLine(str_repeat('-', 60));
+                $this->writeLine($errorFormatter->format($error));
+            }
+        }
+    }
+
+    protected function writeMark($type)
+    {
+        ++$this->checkedFiles;
+
+        if ($this->showProgress) {
+            if ($type === self::TYPE_OK) {
+                $this->writer->write('.');
+
+            } else if ($type === self::TYPE_SKIP) {
+                $this->write('S', self::TYPE_SKIP);
+
+            } else if ($type === self::TYPE_ERROR) {
+                $this->write('X', self::TYPE_ERROR);
+
+            } else if ($type === self::TYPE_FAIL) {
+                $this->writer->write('-');
+            }
+
+            if ($this->checkedFiles % $this->filesPerLine === 0) {
+                $this->writePercent();
+            }
+        }
+    }
+
+    protected function writePercent()
+    {
+        $percent = $this->stringWidth(floor($this->checkedFiles / $this->totalFileCount * 100), 3);
+        $current = $this->stringWidth($this->checkedFiles, strlen($this->totalFileCount));
+        $this->writeLine(" $current/$this->totalFileCount ($percent%)");
+    }
+
+    /**
+     * @param string $input
+     * @param int $width
+     * @return string
+     */
+    protected function stringWidth($input, $width = 3)
+    {
+        $multiplier = $width - strlen($input);
+        return str_repeat(' ', $multiplier > 0 ? $multiplier : 0) . $input;
+    }
+
+    /**
+     * @param int $phpVersionId
+     * @return string
+     */
+    protected function phpVersionIdToString($phpVersionId)
+    {
+        $releaseVersion = (int) substr($phpVersionId, -2, 2);
+        $minorVersion = (int) substr($phpVersionId, -4, 2);
+        $majorVersion = (int) substr($phpVersionId, 0, strlen($phpVersionId) - 4);
+
+        return "$majorVersion.$minorVersion.$releaseVersion";
+    }
+}
+
+class CheckstyleOutput implements Output
+{
+    private $writer;
+
+    public function __construct(IWriter $writer)
+    {
+        $this->writer = $writer;
+    }
+
+    public function ok()
+    {
+    }
+
+    public function skip()
+    {
+    }
+
+    public function error()
+    {
+    }
+
+    public function fail()
+    {
+    }
+
+    public function setTotalFileCount($count)
+    {
+    }
+
+    public function writeHeader($phpVersion, $parallelJobs, $hhvmVersion = null)
+    {
+        $this->writer->write('<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL);
+    }
+
+    public function writeResult(Result $result, ErrorFormatter $errorFormatter, $ignoreFails)
+    {
+        $this->writer->write('<checkstyle>' . PHP_EOL);
+        $errors = array();
+
+        foreach ($result->getErrors() as $error) {
+            $message = $error->getMessage();
+            if ($error instanceof SyntaxError) {
+                $line = $error->getLine();
+                $source = "Syntax Error";
+            } else {
+                $line = 1;
+                $source = "Linter Error";
+            }
+
+            $errors[$error->getShortFilePath()][] = array(
+                'message' => $message,
+                'line' => $line,
+                'source' => $source
+            );
+        }
+
+        foreach ($errors as $file => $fileErrors) {
+            $this->writer->write(sprintf('    <file name="%s">', $file) . PHP_EOL);
+            foreach ($fileErrors as $fileError) {
+                $this->writer->write(
+                    sprintf(
+                        '        <error line="%d" severity="ERROR" message="%s" source="%s" />',
+                        $fileError['line'],
+                        htmlspecialchars($fileError['message'], ENT_COMPAT, 'UTF-8'),
+                        $fileError['source']
+                    ) .
+                    PHP_EOL
+                );
+            }
+            $this->writer->write('    </file>' . PHP_EOL);
+        }
+
+        $this->writer->write('</checkstyle>' . PHP_EOL);
+    }
+}
+
+class TextOutputColored extends TextOutput
+{
+    /** @var \PHP_Parallel_Lint\PhpConsoleColor\ConsoleColor|\JakubOnderka\PhpConsoleColor\ConsoleColor */
+    private $colors;
+
+    public function __construct(IWriter $writer, $colors = Settings::AUTODETECT)
+    {
+        parent::__construct($writer);
+
+        if (class_exists('\PHP_Parallel_Lint\PhpConsoleColor\ConsoleColor')) {
+            $this->colors = new \PHP_Parallel_Lint\PhpConsoleColor\ConsoleColor();
+            $this->colors->setForceStyle($colors === Settings::FORCED);
+        } else if (class_exists('\JakubOnderka\PhpConsoleColor\ConsoleColor')) {
+            $this->colors = new \JakubOnderka\PhpConsoleColor\ConsoleColor();
+            $this->colors->setForceStyle($colors === Settings::FORCED);
+        }
+    }
+
+    /**
+     * @param string $string
+     * @param string $type
+     * @throws \PHP_Parallel_Lint\PhpConsoleColor\InvalidStyleException|\JakubOnderka\PhpConsoleColor\InvalidStyleException
+     */
+    public function write($string, $type = self::TYPE_DEFAULT)
+    {
+        if (
+            !$this->colors instanceof \PHP_Parallel_Lint\PhpConsoleColor\ConsoleColor
+            && !$this->colors instanceof \JakubOnderka\PhpConsoleColor\ConsoleColor
+        ) {
+            parent::write($string, $type);
+        } else {
+            switch ($type) {
+                case self::TYPE_OK:
+                    parent::write($this->colors->apply('bg_green', $string));
+                    break;
+
+                case self::TYPE_SKIP:
+                    parent::write($this->colors->apply('bg_yellow', $string));
+                    break;
+
+                case self::TYPE_ERROR:
+                    parent::write($this->colors->apply('bg_red', $string));
+                    break;
+
+                default:
+                    parent::write($string);
+            }
+        }
+    }
+}
+
+interface IWriter
+{
+    /**
+     * @param string $string
+     */
+    public function write($string);
+}
+
+class NullWriter implements IWriter
+{
+    /**
+     * @param string $string
+     */
+    public function write($string)
+    {
+
+    }
+}
+
+class ConsoleWriter implements IWriter
+{
+    /**
+     * @param string $string
+     */
+    public function write($string)
+    {
+        echo $string;
+    }
+}
+
+class FileWriter implements IWriter
+{
+    /** @var string */
+    protected $logFile;
+
+    /** @var string */
+    protected $buffer;
+
+    public function __construct($logFile)
+    {
+        $this->logFile = $logFile;
+    }
+
+    public function write($string)
+    {
+        $this->buffer .= $string;
+    }
+
+    public function __destruct()
+    {
+        file_put_contents($this->logFile, $this->buffer);
+    }
+}
+
+class MultipleWriter implements IWriter
+{
+    /** @var IWriter[] */
+    protected $writers;
+
+    /**
+     * @param IWriter[] $writers
+     */
+    public function __construct(array $writers)
+    {
+        foreach ($writers as $writer) {
+            $this->addWriter($writer);
+        }
+    }
+
+    /**
+     * @param IWriter $writer
+     */
+    public function addWriter(IWriter $writer)
+    {
+        $this->writers[] = $writer;
+    }
+
+    /**
+     * @param $string
+     */
+    public function write($string)
+    {
+        foreach ($this->writers as $writer) {
+            $writer->write($string);
+        }
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/ParallelLint.php b/php-parallel-lint/php-parallel-lint/src/ParallelLint.php
new file mode 100644
index 0000000..b1825f3
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/ParallelLint.php
@@ -0,0 +1,286 @@
+<?php
+namespace JakubOnderka\PhpParallelLint;
+
+use JakubOnderka\PhpParallelLint\Contracts\SyntaxErrorCallback;
+use JakubOnderka\PhpParallelLint\Process\LintProcess;
+use JakubOnderka\PhpParallelLint\Process\PhpExecutable;
+use JakubOnderka\PhpParallelLint\Process\SkipLintProcess;
+
+class ParallelLint
+{
+    const STATUS_OK = 'ok',
+        STATUS_SKIP = 'skip',
+        STATUS_FAIL = 'fail',
+        STATUS_ERROR = 'error';
+
+    /** @var int */
+    private $parallelJobs;
+
+    /** @var PhpExecutable */
+    private $phpExecutable;
+
+    /** @var bool */
+    private $aspTagsEnabled = false;
+
+    /** @var bool */
+    private $shortTagEnabled = false;
+
+    /** @var callable */
+    private $processCallback;
+
+    /** @var bool */
+    private $showDeprecated = false;
+
+    /** @var SyntaxErrorCallback|null */
+    private $syntaxErrorCallback = null;
+
+    public function __construct(PhpExecutable $phpExecutable, $parallelJobs = 10)
+    {
+        $this->phpExecutable = $phpExecutable;
+        $this->parallelJobs = $parallelJobs;
+    }
+
+    /**
+     * @param array $files
+     * @return Result
+     * @throws \Exception
+     */
+    public function lint(array $files)
+    {
+        $startTime = microtime(true);
+
+        $skipLintProcess = new SkipLintProcess($this->phpExecutable, $files);
+
+        $processCallback = is_callable($this->processCallback) ? $this->processCallback : function () {
+        };
+
+        /**
+         * @var LintProcess[] $running
+         * @var LintProcess[] $waiting
+         */
+        $errors = $running = $waiting = array();
+        $skippedFiles = $checkedFiles = array();
+
+        while ($files || $running) {
+            for ($i = count($running); $files && $i < $this->parallelJobs; $i++) {
+                $file = array_shift($files);
+
+                if ($skipLintProcess->isSkipped($file) === true) {
+                    $skippedFiles[] = $file;
+                    $processCallback(self::STATUS_SKIP, $file);
+                } else {
+                    $running[$file] = new LintProcess(
+                        $this->phpExecutable,
+                        $file,
+                        $this->aspTagsEnabled,
+                        $this->shortTagEnabled,
+                        $this->showDeprecated
+                    );
+                }
+            }
+
+            $skipLintProcess->getChunk();
+            usleep(100);
+
+            foreach ($running as $file => $process) {
+                if ($process->isFinished()) {
+                    unset($running[$file]);
+
+                    $skipStatus = $skipLintProcess->isSkipped($file);
+                    if ($skipStatus === null) {
+                        $waiting[$file] = $process;
+
+                    } else if ($skipStatus === true) {
+                        $skippedFiles[] = $file;
+                        $processCallback(self::STATUS_SKIP, $file);
+
+                    } else if ($process->containsError()) {
+                        $checkedFiles[] = $file;
+                        $errors[] = $this->triggerSyntaxErrorCallback(new SyntaxError($file, $process->getSyntaxError()));
+                        $processCallback(self::STATUS_ERROR, $file);
+
+                    } else if ($process->isSuccess()) {
+                        $checkedFiles[] = $file;
+                        $processCallback(self::STATUS_OK, $file);
+
+
+                    } else {
+                        $errors[] = new Error($file, $process->getOutput());
+                        $processCallback(self::STATUS_FAIL, $file);
+                    }
+                }
+            }
+        }
+
+        if (!empty($waiting)) {
+            $skipLintProcess->waitForFinish();
+
+            if ($skipLintProcess->isFail()) {
+                $message = "Error in skip-linting.php process\nError output: {$skipLintProcess->getErrorOutput()}";
+                throw new \Exception($message);
+            }
+
+            foreach ($waiting as $file => $process) {
+                $skipStatus = $skipLintProcess->isSkipped($file);
+                if ($skipStatus === null) {
+                    throw new \Exception("File $file has empty skip status. Please contact the author of PHP Parallel Lint.");
+
+                } else if ($skipStatus === true) {
+                    $skippedFiles[] = $file;
+                    $processCallback(self::STATUS_SKIP, $file);
+
+                } else if ($process->isSuccess()) {
+                    $checkedFiles[] = $file;
+                    $processCallback(self::STATUS_OK, $file);
+
+                } else if ($process->containsError()) {
+                    $checkedFiles[] = $file;
+                    $errors[] = $this->triggerSyntaxErrorCallback(new SyntaxError($file, $process->getSyntaxError()));
+                    $processCallback(self::STATUS_ERROR, $file);
+
+                } else {
+                    $errors[] = new Error($file, $process->getOutput());
+                    $processCallback(self::STATUS_FAIL, $file);
+                }
+            }
+        }
+
+        $testTime = microtime(true) - $startTime;
+
+        return new Result($errors, $checkedFiles, $skippedFiles, $testTime);
+    }
+
+    /**
+     * @return int
+     */
+    public function getParallelJobs()
+    {
+        return $this->parallelJobs;
+    }
+
+    /**
+     * @param int $parallelJobs
+     * @return ParallelLint
+     */
+    public function setParallelJobs($parallelJobs)
+    {
+        $this->parallelJobs = $parallelJobs;
+
+        return $this;
+    }
+
+    /**
+     * @return string
+     */
+    public function getPhpExecutable()
+    {
+        return $this->phpExecutable;
+    }
+
+    /**
+     * @param string $phpExecutable
+     * @return ParallelLint
+     */
+    public function setPhpExecutable($phpExecutable)
+    {
+        $this->phpExecutable = $phpExecutable;
+
+        return $this;
+    }
+
+    /**
+     * @return callable
+     */
+    public function getProcessCallback()
+    {
+        return $this->processCallback;
+    }
+
+    /**
+     * @param callable $processCallback
+     * @return ParallelLint
+     */
+    public function setProcessCallback($processCallback)
+    {
+        $this->processCallback = $processCallback;
+
+        return $this;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isAspTagsEnabled()
+    {
+        return $this->aspTagsEnabled;
+    }
+
+    /**
+     * @param boolean $aspTagsEnabled
+     * @return ParallelLint
+     */
+    public function setAspTagsEnabled($aspTagsEnabled)
+    {
+        $this->aspTagsEnabled = $aspTagsEnabled;
+
+        return $this;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isShortTagEnabled()
+    {
+        return $this->shortTagEnabled;
+    }
+
+    /**
+     * @param boolean $shortTagEnabled
+     * @return ParallelLint
+     */
+    public function setShortTagEnabled($shortTagEnabled)
+    {
+        $this->shortTagEnabled = $shortTagEnabled;
+
+        return $this;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isShowDeprecated()
+    {
+        return $this->showDeprecated;
+    }
+
+    /**
+     * @param $showDeprecated
+     * @return ParallelLint
+     */
+    public function setShowDeprecated($showDeprecated)
+    {
+        $this->showDeprecated = $showDeprecated;
+
+        return $this;
+    }
+
+    public function triggerSyntaxErrorCallback($syntaxError)
+    {
+        if ($this->syntaxErrorCallback === null) {
+            return $syntaxError;
+        }
+
+        return $this->syntaxErrorCallback->errorFound($syntaxError);
+    }
+
+    /**
+     * @param SyntaxErrorCallback|null $syntaxErrorCallback
+     * @return ParallelLint
+     */
+    public function setSyntaxErrorCallback($syntaxErrorCallback)
+    {
+        $this->syntaxErrorCallback = $syntaxErrorCallback;
+
+        return $this;
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Process/GitBlameProcess.php b/php-parallel-lint/php-parallel-lint/src/Process/GitBlameProcess.php
new file mode 100644
index 0000000..2d675b8
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Process/GitBlameProcess.php
@@ -0,0 +1,147 @@
+<?php
+namespace JakubOnderka\PhpParallelLint\Process;
+
+use JakubOnderka\PhpParallelLint\RunTimeException;
+
+class GitBlameProcess extends Process
+{
+    /**
+     * @param string $gitExecutable
+     * @param string $file
+     * @param int $line
+     * @throws RunTimeException
+     */
+    public function __construct($gitExecutable, $file, $line)
+    {
+        $arguments = array('blame', '-p', '-L', "$line,+1", $file);
+        parent::__construct($gitExecutable, $arguments);
+    }
+
+    /**
+     * @return bool
+     * @throws RunTimeException
+     */
+    public function isSuccess()
+    {
+        return $this->getStatusCode() === 0;
+    }
+
+    /**
+     * @return string
+     * @throws RunTimeException
+     */
+    public function getAuthor()
+    {
+        if (!$this->isSuccess()) {
+            throw new RunTimeException("Author can only be retrieved for successful process output.");
+        }
+
+        $output = $this->getOutput();
+        preg_match('~^author (.*)~m', $output, $matches);
+        return $matches[1];
+    }
+
+    /**
+     * @return string
+     * @throws RunTimeException
+     */
+    public function getAuthorEmail()
+    {
+        if (!$this->isSuccess()) {
+            throw new RunTimeException("Author e-mail can only be retrieved for successful process output.");
+        }
+
+        $output = $this->getOutput();
+        preg_match('~^author-mail <(.*)>~m', $output, $matches);
+        return $matches[1];
+    }
+
+    /**
+     * @return \DateTime
+     * @throws RunTimeException
+     */
+    public function getAuthorTime()
+    {
+        if (!$this->isSuccess()) {
+            throw new RunTimeException("Author time can only be retrieved for successful process output.");
+        }
+
+        $output = $this->getOutput();
+
+        preg_match('~^author-time (.*)~m', $output, $matches);
+        $time = $matches[1];
+
+        preg_match('~^author-tz (.*)~m', $output, $matches);
+        $zone = $matches[1];
+
+        return $this->getDateTime($time, $zone);
+    }
+
+    /**
+     * @return string
+     * @throws RunTimeException
+     */
+    public function getCommitHash()
+    {
+        if (!$this->isSuccess()) {
+            throw new RunTimeException("Commit hash can only be retrieved for successful process output.");
+        }
+
+        return substr($this->getOutput(), 0, strpos($this->getOutput(), ' '));
+    }
+
+    /**
+     * @return string
+     * @throws RunTimeException
+     */
+    public function getSummary()
+    {
+        if (!$this->isSuccess()) {
+            throw new RunTimeException("Commit summary can only be retrieved for successful process output.");
+        }
+
+        $output = $this->getOutput();
+        preg_match('~^summary (.*)~m', $output, $matches);
+        return $matches[1];
+    }
+
+    /**
+     * @param string $gitExecutable
+     * @return bool
+     * @throws RunTimeException
+     */
+    public static function gitExists($gitExecutable)
+    {
+        $process = new Process($gitExecutable, array('--version'));
+        $process->waitForFinish();
+        return $process->getStatusCode() === 0;
+    }
+
+    /**
+     * This harakiri method is required to correct support time zone in PHP 5.4
+     *
+     * @param int $time
+     * @param string $zone
+     * @return \DateTime
+     * @throws \Exception
+     */
+    protected function getDateTime($time, $zone)
+    {
+        $utcTimeZone = new \DateTimeZone('UTC');
+        $datetime = \DateTime::createFromFormat('U', $time, $utcTimeZone);
+
+        $way = substr($zone, 0, 1);
+        $hours = (int) substr($zone, 1, 2);
+        $minutes = (int) substr($zone, 3, 2);
+
+        $interval = new \DateInterval("PT{$hours}H{$minutes}M");
+
+        if ($way === '+') {
+            $datetime->add($interval);
+        } else {
+            $datetime->sub($interval);
+        }
+
+        return new \DateTime($datetime->format('Y-m-d\TH:i:s') . $zone, $utcTimeZone);
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Process/LintProcess.php b/php-parallel-lint/php-parallel-lint/src/Process/LintProcess.php
new file mode 100644
index 0000000..e2e6b2d
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Process/LintProcess.php
@@ -0,0 +1,137 @@
+<?php
+namespace JakubOnderka\PhpParallelLint\Process;
+
+use JakubOnderka\PhpParallelLint\RunTimeException;
+
+class LintProcess extends PhpProcess
+{
+    const FATAL_ERROR = 'Fatal error';
+    const PARSE_ERROR = 'Parse error';
+    const DEPRECATED_ERROR = 'Deprecated:';
+
+    /**
+     * @var bool
+     */
+    private $showDeprecatedErrors;
+
+    /**
+     * @param PhpExecutable $phpExecutable
+     * @param string $fileToCheck Path to file to check
+     * @param bool $aspTags
+     * @param bool $shortTag
+     * @param bool $deprecated
+     * @throws RunTimeException
+     */
+    public function __construct(PhpExecutable $phpExecutable, $fileToCheck, $aspTags = false, $shortTag = false, $deprecated = false)
+    {
+        if (empty($fileToCheck)) {
+            throw new \InvalidArgumentException("File to check must be set.");
+        }
+
+        $parameters = array(
+            '-d asp_tags=' . ($aspTags ? 'On' : 'Off'),
+            '-d short_open_tag=' . ($shortTag ? 'On' : 'Off'),
+            '-d error_reporting=E_ALL',
+            '-n',
+            '-l',
+            $fileToCheck,
+        );
+
+        $this->showDeprecatedErrors = $deprecated;
+        parent::__construct($phpExecutable, $parameters);
+    }
+
+    /**
+     * @return bool
+     * @throws
+     */
+    public function containsError()
+    {
+        return $this->containsParserError($this->getOutput()) ||
+            $this->containsFatalError($this->getOutput()) ||
+            $this->containsDeprecatedError($this->getOutput());
+    }
+
+    /**
+     * @return string
+     * @throws RunTimeException
+     */
+    public function getSyntaxError()
+    {
+        if ($this->containsError()) {
+            // Look for fatal errors first
+            foreach (explode("\n", $this->getOutput()) as $line) {
+                if ($this->containsFatalError($line)) {
+                    return $line;
+                }
+            }
+
+            // Look for parser errors second
+            foreach (explode("\n", $this->getOutput()) as $line) {
+                if ($this->containsParserError($line)) {
+                    return $line;
+                }
+            }
+
+            // Look for deprecated errors third
+            foreach (explode("\n", $this->getOutput()) as $line) {
+                if ($this->containsDeprecatedError($line)) {
+                    return $line;
+                }
+            }
+
+            throw new RunTimeException("The output '{$this->getOutput()}' does not contain Parse or Syntax errors");
+        }
+
+        return false;
+    }
+
+    /**
+     * @return bool
+     * @throws RunTimeException
+     */
+    public function isFail()
+    {
+        return defined('PHP_WINDOWS_VERSION_MAJOR') ? $this->getStatusCode() === 1 : parent::isFail();
+    }
+
+    /**
+     * @return bool
+     * @throws RunTimeException
+     */
+    public function isSuccess()
+    {
+        return $this->getStatusCode() === 0;
+    }
+
+    /**
+     * @param string $string
+     * @return bool
+     */
+    private function containsParserError($string)
+    {
+        return strpos($string, self::PARSE_ERROR) !== false;
+    }
+
+    /**
+     * @param string $string
+     * @return bool
+     */
+    private function containsFatalError($string)
+    {
+        return strpos($string, self::FATAL_ERROR) !== false;
+    }
+
+    /**
+     * @param string $string
+     * @return bool
+     */
+    private function containsDeprecatedError($string)
+    {
+        if ($this->showDeprecatedErrors === false) {
+            return false;
+        }
+
+        return strpos($string, self::DEPRECATED_ERROR) !== false;
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Process/PhpExecutable.php b/php-parallel-lint/php-parallel-lint/src/Process/PhpExecutable.php
new file mode 100644
index 0000000..001b1e9
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Process/PhpExecutable.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace JakubOnderka\PhpParallelLint\Process;
+
+use JakubOnderka\PhpParallelLint\RunTimeException;
+
+class PhpExecutable
+{
+    /** @var string */
+    private $path;
+
+    /**
+     * Version as PHP_VERSION_ID constant
+     * @var int
+     */
+    private $versionId;
+
+    /** @var string */
+    private $hhvmVersion;
+
+    /** @var bool */
+    private $isHhvmType;
+
+    /**
+     * @param string $path
+     * @param int $versionId
+     * @param string $hhvmVersion
+     * @param bool $isHhvmType
+     */
+    public function __construct($path, $versionId, $hhvmVersion, $isHhvmType)
+    {
+        $this->path = $path;
+        $this->versionId = $versionId;
+        $this->hhvmVersion = $hhvmVersion;
+        $this->isHhvmType = $isHhvmType;
+    }
+
+    /**
+     * @return string
+     */
+    public function getHhvmVersion()
+    {
+        return $this->hhvmVersion;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isIsHhvmType()
+    {
+        return $this->isHhvmType;
+    }
+
+    /**
+     * @return string
+     */
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    /**
+     * @return int
+     */
+    public function getVersionId()
+    {
+        return $this->versionId;
+    }
+
+    /**
+     * @param string $phpExecutable
+     * @return PhpExecutable
+     * @throws \Exception
+     */
+    public static function getPhpExecutable($phpExecutable)
+    {
+        $codeToExecute = <<<PHP
+echo 'PHP;', PHP_VERSION_ID, ';', defined('HPHP_VERSION') ? HPHP_VERSION : null;
+PHP;
+
+        $process = new Process($phpExecutable, array('-n', '-r', $codeToExecute));
+        $process->waitForFinish();
+
+        try {
+            if ($process->getStatusCode() !== 0 && $process->getStatusCode() !== 255) {
+                throw new RunTimeException("Unable to execute '{$phpExecutable}'.");
+            }
+
+            return self::getPhpExecutableFromOutput($phpExecutable, $process->getOutput());
+
+        } catch (RunTimeException $e) {
+            // Try HHVM type
+            $process = new Process($phpExecutable, array('--php', '-r', $codeToExecute));
+            $process->waitForFinish();
+
+            if ($process->getStatusCode() !== 0 && $process->getStatusCode() !== 255) {
+                throw new RunTimeException("Unable to execute '{$phpExecutable}'.");
+            }
+
+            return self::getPhpExecutableFromOutput($phpExecutable, $process->getOutput(), $isHhvmType = true);
+        }
+    }
+
+    /**
+     * @param string $phpExecutable
+     * @param string $output
+     * @param bool $isHhvmType
+     * @return PhpExecutable
+     * @throws RunTimeException
+     */
+    private static function getPhpExecutableFromOutput($phpExecutable, $output, $isHhvmType = false)
+    {
+        $parts = explode(';', $output);
+
+        if ($parts[0] !== 'PHP' || !preg_match('~([0-9]+)~', $parts[1], $matches)) {
+            throw new RunTimeException("'{$phpExecutable}' is not valid PHP binary.");
+        }
+
+        $hhvmVersion = isset($parts[2]) ? $parts[2] : false;
+
+        return new PhpExecutable(
+            $phpExecutable,
+            intval($matches[1]),
+            $hhvmVersion,
+            $isHhvmType
+        );
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Process/PhpProcess.php b/php-parallel-lint/php-parallel-lint/src/Process/PhpProcess.php
new file mode 100644
index 0000000..0df293a
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Process/PhpProcess.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace JakubOnderka\PhpParallelLint\Process;
+
+class PhpProcess extends Process
+{
+    /**
+     * @param PhpExecutable $phpExecutable
+     * @param array $parameters
+     * @param string|null $stdIn
+     * @throws \JakubOnderka\PhpParallelLint\RunTimeException
+     */
+    public function __construct(PhpExecutable $phpExecutable, array $parameters = array(), $stdIn = null)
+    {
+        $constructedParameters = $this->constructParameters($parameters, $phpExecutable->isIsHhvmType());
+        parent::__construct($phpExecutable->getPath(), $constructedParameters, $stdIn);
+    }
+
+    /**
+     * @param array $parameters
+     * @param bool $isHhvm
+     * @return array
+     */
+    private function constructParameters(array $parameters, $isHhvm)
+    {
+        // Always ignore PHP startup errors ("Unable to load library...") in sub-processes.
+        array_unshift($parameters, '-d display_startup_errors=0');
+
+        if ($isHhvm) {
+            array_unshift($parameters, '-php');
+        }
+
+        return $parameters;
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Process/Process.php b/php-parallel-lint/php-parallel-lint/src/Process/Process.php
new file mode 100644
index 0000000..190511e
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Process/Process.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace JakubOnderka\PhpParallelLint\Process;
+
+use JakubOnderka\PhpParallelLint\RunTimeException;
+
+class Process
+{
+    const STDIN = 0,
+        STDOUT = 1,
+        STDERR = 2;
+
+    const READ = 'r',
+        WRITE = 'w';
+
+    /** @var resource */
+    protected $process;
+
+    /** @var resource */
+    protected $stdout;
+
+    /** @var resource */
+    protected $stderr;
+
+    /** @var string */
+    private $output;
+
+    /** @var string */
+    private $errorOutput;
+
+    /** @var int */
+    private $statusCode;
+
+    /**
+     * @param string $executable
+     * @param string[] $arguments
+     * @param string $stdInInput
+     * @throws RunTimeException
+     */
+    public function __construct($executable, array $arguments = array(), $stdInInput = null)
+    {
+        $descriptors = array(
+            self::STDIN  => array('pipe', self::READ),
+            self::STDOUT => array('pipe', self::WRITE),
+            self::STDERR => array('pipe', self::WRITE),
+        );
+
+        $cmdLine = $executable . ' ' . implode(' ', array_map('escapeshellarg', $arguments));
+        $this->process = proc_open($cmdLine, $descriptors, $pipes, null, null, array('bypass_shell' => true));
+
+        if ($this->process === false || $this->process === null) {
+            throw new RunTimeException("Cannot create new process $cmdLine");
+        }
+
+        list($stdin, $this->stdout, $this->stderr) = $pipes;
+
+        if ($stdInInput) {
+            fwrite($stdin, $stdInInput);
+        }
+
+        fclose($stdin);
+    }
+
+    /**
+     * @return bool
+     */
+    public function isFinished()
+    {
+        if ($this->statusCode !== null) {
+            return true;
+        }
+
+        $status = proc_get_status($this->process);
+
+        if ($status['running']) {
+            return false;
+        } else if ($this->statusCode === null) {
+            $this->statusCode = (int) $status['exitcode'];
+        }
+
+        // Process outputs
+        $this->output = stream_get_contents($this->stdout);
+        fclose($this->stdout);
+
+        $this->errorOutput = stream_get_contents($this->stderr);
+        fclose($this->stderr);
+
+        $statusCode = proc_close($this->process);
+
+        if ($this->statusCode === null) {
+            $this->statusCode = $statusCode;
+        }
+
+        $this->process = null;
+
+        return true;
+    }
+
+    public function waitForFinish()
+    {
+        while (!$this->isFinished()) {
+            usleep(100);
+        }
+    }
+
+    /**
+     * @return string
+     * @throws RunTimeException
+     */
+    public function getOutput()
+    {
+        if (!$this->isFinished()) {
+            throw new RunTimeException("Cannot get output for running process");
+        }
+
+        return $this->output;
+    }
+
+    /**
+     * @return string
+     * @throws RunTimeException
+     */
+    public function getErrorOutput()
+    {
+        if (!$this->isFinished()) {
+            throw new RunTimeException("Cannot get error output for running process");
+        }
+
+        return $this->errorOutput;
+    }
+
+    /**
+     * @return int
+     * @throws RunTimeException
+     */
+    public function getStatusCode()
+    {
+        if (!$this->isFinished()) {
+            throw new RunTimeException("Cannot get status code for running process");
+        }
+
+        return $this->statusCode;
+    }
+
+    /**
+     * @return bool
+     * @throws RunTimeException
+     */
+    public function isFail()
+    {
+        return $this->getStatusCode() === 1;
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Process/SkipLintProcess.php b/php-parallel-lint/php-parallel-lint/src/Process/SkipLintProcess.php
new file mode 100644
index 0000000..52f4e76
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Process/SkipLintProcess.php
@@ -0,0 +1,91 @@
+<?php
+namespace JakubOnderka\PhpParallelLint\Process;
+
+use JakubOnderka\PhpParallelLint\RunTimeException;
+
+class SkipLintProcess extends PhpProcess
+{
+    /** @var array */
+    private $skipped = array();
+
+    /** @var bool */
+    private $done = false;
+
+    /** @var string */
+    private $endLastChunk = '';
+
+    /**
+     * @param PhpExecutable $phpExecutable
+     * @param array $filesToCheck
+     * @throws RunTimeException
+     */
+    public function __construct(PhpExecutable $phpExecutable, array $filesToCheck)
+    {
+        $scriptPath = __DIR__ . '/../../bin/skip-linting.php';
+        $script = file_get_contents($scriptPath);
+
+        if (!$script) {
+            throw new RunTimeException("skip-linting.php script not found in '$scriptPath'.");
+        }
+
+        $script = str_replace('<?php', '', $script);
+
+        $parameters = array('-d', 'display_errors=stderr', '-r', $script);
+        parent::__construct($phpExecutable, $parameters, implode(PHP_EOL, $filesToCheck));
+    }
+
+    /**
+     * @throws RunTimeException
+     */
+    public function getChunk()
+    {
+        if (!$this->isFinished()) {
+            $this->processLines(fread($this->stdout, 8192));
+        }
+    }
+
+    /**
+     * @return bool
+     * @throws \JakubOnderka\PhpParallelLint\RunTimeException
+     */
+    public function isFinished()
+    {
+        $isFinished = parent::isFinished();
+        if ($isFinished && !$this->done) {
+            $this->done = true;
+            $output = $this->getOutput();
+            $this->processLines($output);
+        }
+
+        return $isFinished;
+    }
+
+    /**
+     * @param string $file
+     * @return bool|null
+     */
+    public function isSkipped($file)
+    {
+        if (isset($this->skipped[$file])) {
+            return $this->skipped[$file];
+        }
+
+        return null;
+    }
+
+    /**
+     * @param string $content
+     */
+    private function processLines($content)
+    {
+        if (!empty($content)) {
+            $lines = explode(PHP_EOL, $this->endLastChunk . $content);
+            $this->endLastChunk = array_pop($lines);
+            foreach ($lines as $line) {
+                $parts = explode(';', $line);
+                list($file, $status) = $parts;
+                $this->skipped[$file] = $status === '1' ? true : false;
+            }
+        }
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Result.php b/php-parallel-lint/php-parallel-lint/src/Result.php
new file mode 100644
index 0000000..9f6a368
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Result.php
@@ -0,0 +1,171 @@
+<?php
+namespace JakubOnderka\PhpParallelLint;
+
+use ReturnTypeWillChange;
+
+class Result implements \JsonSerializable
+{
+    /** @var Error[] */
+    private $errors;
+
+    /** @var array */
+    private $checkedFiles;
+
+    /** @var array */
+    private $skippedFiles;
+
+    /** @var float */
+    private $testTime;
+
+    /**
+     * @param Error[] $errors
+     * @param array $checkedFiles
+     * @param array $skippedFiles
+     * @param float $testTime
+     */
+    public function __construct(array $errors, array $checkedFiles, array $skippedFiles, $testTime)
+    {
+        $this->errors = $errors;
+        $this->checkedFiles = $checkedFiles;
+        $this->skippedFiles = $skippedFiles;
+        $this->testTime = $testTime;
+    }
+
+    /**
+     * @return Error[]
+     */
+    public function getErrors()
+    {
+        return $this->errors;
+    }
+
+    /**
+     * @return bool
+     */
+    public function hasError()
+    {
+        return !empty($this->errors);
+    }
+
+    /**
+     * @return array
+     */
+    public function getFilesWithFail()
+    {
+        $filesWithFail = array();
+        foreach ($this->errors as $error) {
+            if (!$error instanceof SyntaxError) {
+                $filesWithFail[] = $error->getFilePath();
+            }
+        }
+
+        return $filesWithFail;
+    }
+
+    /**
+     * @return int
+     */
+    public function getFilesWithFailCount()
+    {
+        return count($this->getFilesWithFail());
+    }
+
+    /**
+     * @return bool
+     */
+    public function hasFilesWithFail()
+    {
+        return $this->getFilesWithFailCount() !== 0;
+    }
+
+    /**
+     * @return array
+     */
+    public function getCheckedFiles()
+    {
+        return $this->checkedFiles;
+    }
+
+    /**
+     * @return int
+     */
+    public function getCheckedFilesCount()
+    {
+        return count($this->checkedFiles);
+    }
+
+    /**
+     * @return array
+     */
+    public function getSkippedFiles()
+    {
+        return $this->skippedFiles;
+    }
+
+    /**
+     * @return int
+     */
+    public function getSkippedFilesCount()
+    {
+        return count($this->skippedFiles);
+    }
+
+    /**
+     * @return array
+     */
+    public function getFilesWithSyntaxError()
+    {
+        $filesWithSyntaxError = array();
+        foreach ($this->errors as $error) {
+            if ($error instanceof SyntaxError) {
+                $filesWithSyntaxError[] = $error->getFilePath();
+            }
+        }
+
+        return $filesWithSyntaxError;
+    }
+
+    /**
+     * @return int
+     */
+    public function getFilesWithSyntaxErrorCount()
+    {
+        return count($this->getFilesWithSyntaxError());
+    }
+
+    /**
+     * @return bool
+     */
+    public function hasSyntaxError()
+    {
+        return $this->getFilesWithSyntaxErrorCount() !== 0;
+    }
+
+    /**
+     * @return float
+     */
+    public function getTestTime()
+    {
+        return $this->testTime;
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.4.0)<br/>
+     * Specify data which should be serialized to JSON
+     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
+     * @return mixed data which can be serialized by <b>json_encode</b>,
+     * which is a value of any type other than a resource.
+     */
+    #[ReturnTypeWillChange]
+    function jsonSerialize()
+    {
+        return array(
+            'checkedFiles' => $this->getCheckedFiles(),
+            'filesWithSyntaxError' => $this->getFilesWithSyntaxError(),
+            'skippedFiles' => $this->getSkippedFiles(),
+            'errors' => $this->getErrors(),
+        );
+    }
+
+
+}
\ No newline at end of file
diff --git a/php-parallel-lint/php-parallel-lint/src/Settings.php b/php-parallel-lint/php-parallel-lint/src/Settings.php
new file mode 100644
index 0000000..4622910
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Settings.php
@@ -0,0 +1,247 @@
+<?php
+namespace JakubOnderka\PhpParallelLint;
+
+class Settings
+{
+
+    /**
+     * constants for enum settings
+     */
+    const FORCED = 'FORCED';
+    const DISABLED = 'DISABLED';
+    const AUTODETECT = 'AUTODETECT';
+
+    const FORMAT_TEXT = 'text';
+    const FORMAT_JSON = 'json';
+    const FORMAT_GITLAB = 'gitlab';
+    const FORMAT_CHECKSTYLE = 'checkstyle';
+
+    /**
+     * Path to PHP executable
+     * @var string
+     */
+    public $phpExecutable = 'php';
+
+    /**
+     * Check code inside PHP opening short tag <? or <?= in PHP 5.3
+     * @var bool
+     */
+    public $shortTag = false;
+
+    /**
+     * Check PHP code inside ASP-style <% %> tags.
+     * @var bool
+     */
+    public $aspTags = false;
+
+    /**
+     * Number of jobs running in same time
+     * @var int
+     */
+    public $parallelJobs = 10;
+
+    /**
+     * If path contains directory, only file with these extensions are checked
+     * @var array
+     */
+    public $extensions = array('php', 'phtml', 'php3', 'php4', 'php5', 'phpt');
+
+    /**
+     * Array of file or directories to check
+     * @var array
+     */
+    public $paths = array();
+
+    /**
+     * Don't check files or directories
+     * @var array
+     */
+    public $excluded = array();
+
+    /**
+     * Mode for color detection. Possible values: self::FORCED, self::DISABLED and self::AUTODETECT
+     * @var string
+     */
+    public $colors = self::AUTODETECT;
+
+    /**
+     * Show progress in text output
+     * @var bool
+     */
+    public $showProgress = true;
+
+    /**
+     * Output format (see FORMAT_* constants)
+     * @var string
+     */
+    public $format = self::FORMAT_TEXT;
+
+    /**
+     * Read files and folder to tests from standard input (blocking)
+     * @var bool
+     */
+    public $stdin = false;
+
+    /**
+     * Try to show git blame for row with error
+     * @var bool
+     */
+    public $blame = false;
+
+    /**
+     * Path to git executable for blame
+     * @var string
+     */
+    public $gitExecutable = 'git';
+
+    /**
+     * @var bool
+     */
+    public $ignoreFails = false;
+
+    /**
+     * @var bool
+     */
+    public $showDeprecated = false;
+
+    /**
+     * Path to a file with syntax error callback
+     * @var string|null
+     */
+    public $syntaxErrorCallbackFile = null;
+
+    /**
+     * @param array $paths
+     */
+    public function addPaths(array $paths)
+    {
+        $this->paths = array_merge($this->paths, $paths);
+    }
+
+    /**
+     * @param array $arguments
+     * @return Settings
+     * @throws InvalidArgumentException
+     */
+    public static function parseArguments(array $arguments)
+    {
+        $arguments = new ArrayIterator(array_slice($arguments, 1));
+        $settings = new self;
+
+        // Use the currently invoked php as the default if possible
+        if (defined('PHP_BINARY')) {
+            $settings->phpExecutable = PHP_BINARY;
+        }
+
+        foreach ($arguments as $argument) {
+            if ($argument[0] !== '-') {
+                $settings->paths[] = $argument;
+            } else {
+                switch ($argument) {
+                    case '-p':
+                        $settings->phpExecutable = $arguments->getNext();
+                        break;
+
+                    case '-s':
+                    case '--short':
+                        $settings->shortTag = true;
+                        break;
+
+                    case '-a':
+                    case '--asp':
+                        $settings->aspTags = true;
+                        break;
+
+                    case '-e':
+                        $settings->extensions = array_map('trim', explode(',', $arguments->getNext()));
+                        break;
+
+                    case '-j':
+                        $settings->parallelJobs = max((int) $arguments->getNext(), 1);
+                        break;
+
+                    case '--exclude':
+                        $settings->excluded[] = $arguments->getNext();
+                        break;
+
+                    case '--colors':
+                        $settings->colors = self::FORCED;
+                        break;
+
+                    case '--no-colors':
+                        $settings->colors = self::DISABLED;
+                        break;
+
+                    case '--no-progress':
+                        $settings->showProgress = false;
+                        break;
+
+                    case '--checkstyle':
+                        $settings->format = self::FORMAT_CHECKSTYLE;
+                        break;
+
+                    case '--json':
+                        $settings->format = self::FORMAT_JSON;
+                        break;
+
+                    case '--gitlab':
+                        $settings->format = self::FORMAT_GITLAB;
+                        break;
+
+                    case '--git':
+                        $settings->gitExecutable = $arguments->getNext();
+                        break;
+
+                    case '--stdin':
+                        $settings->stdin = true;
+                        break;
+
+                    case '--blame':
+                        $settings->blame = true;
+                        break;
+
+                    case '--ignore-fails':
+                        $settings->ignoreFails = true;
+                        break;
+
+                    case '--show-deprecated':
+                        $settings->showDeprecated = true;
+                        break;
+
+                    case '--syntax-error-callback':
+                        $settings->syntaxErrorCallbackFile = $arguments->getNext();
+                        break;
+
+                    default:
+                        throw new InvalidArgumentException($argument);
+                }
+            }
+        }
+
+        return $settings;
+    }
+
+    /**
+     * @return array
+     */
+    public static function getPathsFromStdIn()
+    {
+        $content = stream_get_contents(STDIN);
+
+        if (empty($content)) {
+            return array();
+        }
+
+        $lines = explode("\n", rtrim($content));
+        return array_map('rtrim', $lines);
+    }
+}
+
+class ArrayIterator extends \ArrayIterator
+{
+    public function getNext()
+    {
+        $this->next();
+        return $this->current();
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/exceptions.php b/php-parallel-lint/php-parallel-lint/src/exceptions.php
new file mode 100644
index 0000000..b96f28a
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/exceptions.php
@@ -0,0 +1,93 @@
+<?php
+namespace JakubOnderka\PhpParallelLint;
+
+use ReturnTypeWillChange;
+
+class Exception extends \Exception implements \JsonSerializable
+{
+    #[ReturnTypeWillChange]
+    public function jsonSerialize()
+    {
+        return array(
+            'type' => get_class($this),
+            'message' => $this->getMessage(),
+            'code' => $this->getCode(),
+        );
+    }
+}
+
+class RunTimeException extends Exception
+{
+
+}
+
+class InvalidArgumentException extends Exception
+{
+    protected $argument;
+
+    public function __construct($argument)
+    {
+        $this->argument = $argument;
+        $this->message = "Invalid argument $argument";
+    }
+
+    public function getArgument()
+    {
+        return $this->argument;
+    }
+}
+
+class NotExistsPathException extends Exception
+{
+    protected $path;
+
+    public function __construct($path)
+    {
+        $this->path = $path;
+        $this->message = "Path '$path' not found";
+    }
+
+    public function getPath()
+    {
+        return $this->path;
+    }
+}
+
+class NotExistsClassException extends Exception
+{
+    protected $className;
+    protected $fileName;
+
+    public function __construct($className, $fileName)
+    {
+        $this->className = $className;
+        $this->fileName = $fileName;
+        $this->message = "Class with name '$className' does not exists in file '$fileName'";
+    }
+
+    public function getClassName()
+    {
+        return $this->className;
+    }
+
+    public function getFileName()
+    {
+        return $this->fileName;
+    }
+}
+
+class NotImplementCallbackException extends Exception
+{
+    protected $className;
+
+    public function __construct($className)
+    {
+        $this->className = $className;
+        $this->message = "Class '$className' does not implement SyntaxErrorCallback interface.";
+    }
+
+    public function getClassName()
+    {
+        return $this->className;
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/polyfill.php b/php-parallel-lint/php-parallel-lint/src/polyfill.php
new file mode 100644
index 0000000..79b3049
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/polyfill.php
@@ -0,0 +1,15 @@
+<?php
+
+/**
+ * Polyfill for PHP < 5.4
+ */
+if (!interface_exists('JsonSerializable', false)) {
+    interface JsonSerializable
+    {
+        /**
+         * @param void
+         * @return mixed
+         */
+        function jsonSerialize();
+    }
+}
-- 
2.39.2

$ date
--- stdout ---
Thu Apr 25 21:06:53 UTC 2024

--- end ---
$ git clone file:///srv/git/mediawiki-vendor.git repo --depth=1 -b master
--- stderr ---
Cloning into 'repo'...
Updating files:  97% (6228/6381)
Updating files:  98% (6254/6381)
Updating files:  99% (6318/6381)
Updating files: 100% (6381/6381)
Updating files: 100% (6381/6381), done.
--- stdout ---

--- end ---
$ git config user.name libraryupgrader
--- stdout ---

--- end ---
$ git config user.email tools.libraryupgrader@tools.wmflabs.org
--- stdout ---

--- end ---
$ git submodule update --init
--- stdout ---

--- end ---
$ grr init
--- stdout ---
Installed commit-msg hook.

--- end ---
$ git show-ref refs/heads/master
--- stdout ---
fd4498bc6a5f80a8b56b03e0be446600188e86f0 refs/heads/master

--- end ---
Upgrading c:php-parallel-lint/php-parallel-lint from 1.3.2 -> 1.4.0
$ /usr/bin/composer update
--- stderr ---
Loading composer repositories with package information
                                                      Updating dependencies
Lock file operations: 0 installs, 1 update, 0 removals
  - Upgrading php-parallel-lint/php-parallel-lint (v1.3.2 => v1.4.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Installing php-parallel-lint/php-parallel-lint (v1.4.0): Extracting archive
Package fgrosse/phpasn1 is abandoned, you should avoid using it. No replacement was suggested.
Generating optimized autoload files
40 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
No security vulnerability advisories found
--- stdout ---

--- end ---
$ /usr/bin/composer install
--- stderr ---
Installing dependencies from lock file (including require-dev)
Verifying lock file contents can be installed on current platform.
Nothing to install, update or remove
Package fgrosse/phpasn1 is abandoned, you should avoid using it. No replacement was suggested.
Generating optimized autoload files
40 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
--- stdout ---

--- end ---
$ /usr/bin/composer test
--- stderr ---
> parallel-lint --exclude composer/autoload_static.php --exclude symfony/console/Attribute/AsCommand.php --exclude symfony/polyfill-php83/bootstrap81.php --exclude ruflin/elastica/src/Query/Match.php .
--- stdout ---
PHP 8.2.7 | 10 parallel jobs
............................................................   60/5123 (  1%)
............................................................  120/5123 (  2%)
............................................................  180/5123 (  3%)
............................................................  240/5123 (  4%)
............................................................  300/5123 (  5%)
............................................................  360/5123 (  7%)
............................................................  420/5123 (  8%)
............................................................  480/5123 (  9%)
............................................................  540/5123 ( 10%)
............................................................  600/5123 ( 11%)
............................................................  660/5123 ( 12%)
............................................................  720/5123 ( 14%)
............................................................  780/5123 ( 15%)
............................................................  840/5123 ( 16%)
............................................................  900/5123 ( 17%)
............................................................  960/5123 ( 18%)
............................................................ 1020/5123 ( 19%)
............................................................ 1080/5123 ( 21%)
............................................................ 1140/5123 ( 22%)
............................................................ 1200/5123 ( 23%)
............................................................ 1260/5123 ( 24%)
............................................................ 1320/5123 ( 25%)
............................................................ 1380/5123 ( 26%)
............................................................ 1440/5123 ( 28%)
............................................................ 1500/5123 ( 29%)
............................................................ 1560/5123 ( 30%)
............................................................ 1620/5123 ( 31%)
............................................................ 1680/5123 ( 32%)
............................................................ 1740/5123 ( 33%)
............................................................ 1800/5123 ( 35%)
............................................................ 1860/5123 ( 36%)
............................................................ 1920/5123 ( 37%)
............................................................ 1980/5123 ( 38%)
............................................................ 2040/5123 ( 39%)
............................................................ 2100/5123 ( 40%)
............................................................ 2160/5123 ( 42%)
............................................................ 2220/5123 ( 43%)
............................................................ 2280/5123 ( 44%)
............................................................ 2340/5123 ( 45%)
............................................................ 2400/5123 ( 46%)
............................................................ 2460/5123 ( 48%)
............................................................ 2520/5123 ( 49%)
............................................................ 2580/5123 ( 50%)
............................................................ 2640/5123 ( 51%)
............................................................ 2700/5123 ( 52%)
............................................................ 2760/5123 ( 53%)
............................................................ 2820/5123 ( 55%)
............................................................ 2880/5123 ( 56%)
............................................................ 2940/5123 ( 57%)
............................................................ 3000/5123 ( 58%)
............................................................ 3060/5123 ( 59%)
............................................................ 3120/5123 ( 60%)
............................................................ 3180/5123 ( 62%)
............................................................ 3240/5123 ( 63%)
............................................................ 3300/5123 ( 64%)
............................................................ 3360/5123 ( 65%)
............................................................ 3420/5123 ( 66%)
............................................................ 3480/5123 ( 67%)
............................................................ 3540/5123 ( 69%)
............................................................ 3600/5123 ( 70%)
............................................................ 3660/5123 ( 71%)
............................................................ 3720/5123 ( 72%)
............................................................ 3780/5123 ( 73%)
............................................................ 3840/5123 ( 74%)
............................................................ 3900/5123 ( 76%)
............................................................ 3960/5123 ( 77%)
............................................................ 4020/5123 ( 78%)
............................................................ 4080/5123 ( 79%)
............................................................ 4140/5123 ( 80%)
............................................................ 4200/5123 ( 81%)
............................................................ 4260/5123 ( 83%)
............................................................ 4320/5123 ( 84%)
............................................................ 4380/5123 ( 85%)
............................................................ 4440/5123 ( 86%)
............................................................ 4500/5123 ( 87%)
............................................................ 4560/5123 ( 89%)
............................................................ 4620/5123 ( 90%)
............................................................ 4680/5123 ( 91%)
............................................................ 4740/5123 ( 92%)
............................................................ 4800/5123 ( 93%)
............................................................ 4860/5123 ( 94%)
............................................................ 4920/5123 ( 96%)
............................................................ 4980/5123 ( 97%)
............................................................ 5040/5123 ( 98%)
............................................................ 5100/5123 ( 99%)
.......................                                      5123/5123 (100%)


Checked 5123 files in 18.2 seconds
No syntax error found

--- end ---
build: Updating php-parallel-lint/php-parallel-lint to 1.4.0

$ git add .
--- stdout ---

--- end ---
$ git commit -F /tmp/tmpm5y8zsiv
--- stdout ---
[master b62e764] build: Updating php-parallel-lint/php-parallel-lint to 1.4.0
 31 files changed, 3712 insertions(+), 62 deletions(-)
 create mode 100644 php-parallel-lint/php-parallel-lint/CHANGELOG.md
 create mode 100644 php-parallel-lint/php-parallel-lint/LICENSE
 create mode 100644 php-parallel-lint/php-parallel-lint/README.md
 create mode 100644 php-parallel-lint/php-parallel-lint/bin/skip-linting.php
 create mode 100644 php-parallel-lint/php-parallel-lint/composer.json
 create mode 100755 php-parallel-lint/php-parallel-lint/parallel-lint
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Application.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Contracts/SyntaxErrorCallback.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Error.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/ErrorFormatter.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Manager.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Output.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/ParallelLint.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Process/GitBlameProcess.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Process/LintProcess.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Process/PhpExecutable.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Process/PhpProcess.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Process/Process.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Process/SkipLintProcess.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Result.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Settings.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/exceptions.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/polyfill.php

--- end ---
$ git format-patch HEAD~1 --stdout
--- stdout ---
From b62e7640ca0daf8ed4b139d7a871ab96f4450b61 Mon Sep 17 00:00:00 2001
From: libraryupgrader <tools.libraryupgrader@tools.wmflabs.org>
Date: Thu, 25 Apr 2024 21:07:24 +0000
Subject: [PATCH] build: Updating php-parallel-lint/php-parallel-lint to 1.4.0

Change-Id: Iab8e18cf4e65821d806234b9623baff21ff2ae1f
---
 composer.json                                 |   2 +-
 composer.lock                                 |  22 +-
 composer/ClassLoader.php                      |  96 +--
 composer/LICENSE                              |   2 -
 composer/autoload_classmap.php                |  36 ++
 composer/autoload_static.php                  |  36 ++
 composer/installed.json                       |  70 ++-
 composer/installed.php                        |  27 +-
 .../php-parallel-lint/CHANGELOG.md            | 256 ++++++++
 php-parallel-lint/php-parallel-lint/LICENSE   |  26 +
 php-parallel-lint/php-parallel-lint/README.md | 122 ++++
 .../php-parallel-lint/bin/skip-linting.php    |  25 +
 .../php-parallel-lint/composer.json           |  55 ++
 .../php-parallel-lint/parallel-lint           |  84 +++
 .../php-parallel-lint/src/Application.php     | 129 ++++
 .../src/Contracts/SyntaxErrorCallback.php     |  13 +
 .../php-parallel-lint/src/Error.php           | 242 +++++++
 .../php-parallel-lint/src/ErrorFormatter.php  | 141 +++++
 .../php-parallel-lint/src/Manager.php         | 293 +++++++++
 .../php-parallel-lint/src/Output.php          | 594 ++++++++++++++++++
 .../php-parallel-lint/src/ParallelLint.php    | 286 +++++++++
 .../src/Process/GitBlameProcess.php           | 147 +++++
 .../src/Process/LintProcess.php               | 137 ++++
 .../src/Process/PhpExecutable.php             | 128 ++++
 .../src/Process/PhpProcess.php                |  35 ++
 .../php-parallel-lint/src/Process/Process.php | 153 +++++
 .../src/Process/SkipLintProcess.php           |  91 +++
 .../php-parallel-lint/src/Result.php          | 171 +++++
 .../php-parallel-lint/src/Settings.php        | 247 ++++++++
 .../php-parallel-lint/src/exceptions.php      |  93 +++
 .../php-parallel-lint/src/polyfill.php        |  15 +
 31 files changed, 3712 insertions(+), 62 deletions(-)
 create mode 100644 php-parallel-lint/php-parallel-lint/CHANGELOG.md
 create mode 100644 php-parallel-lint/php-parallel-lint/LICENSE
 create mode 100644 php-parallel-lint/php-parallel-lint/README.md
 create mode 100644 php-parallel-lint/php-parallel-lint/bin/skip-linting.php
 create mode 100644 php-parallel-lint/php-parallel-lint/composer.json
 create mode 100755 php-parallel-lint/php-parallel-lint/parallel-lint
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Application.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Contracts/SyntaxErrorCallback.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Error.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/ErrorFormatter.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Manager.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Output.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/ParallelLint.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Process/GitBlameProcess.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Process/LintProcess.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Process/PhpExecutable.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Process/PhpProcess.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Process/Process.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Process/SkipLintProcess.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Result.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/Settings.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/exceptions.php
 create mode 100644 php-parallel-lint/php-parallel-lint/src/polyfill.php

diff --git a/composer.json b/composer.json
index 9d1e5d9..e7b47ed 100644
--- a/composer.json
+++ b/composer.json
@@ -155,7 +155,7 @@
 		"symfony/polyfill-mbstring": "1.99"
 	},
 	"require-dev": {
-		"php-parallel-lint/php-parallel-lint": "1.3.2"
+		"php-parallel-lint/php-parallel-lint": "1.4.0"
 	},
 	"scripts": {
 		"test": "parallel-lint --exclude composer/autoload_static.php --exclude symfony/console/Attribute/AsCommand.php --exclude symfony/polyfill-php83/bootstrap81.php --exclude ruflin/elastica/src/Query/Match.php ."
diff --git a/composer.lock b/composer.lock
index 280de5b..f100c82 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "014a86445ee735eb32d5e3fd16221b4d",
+    "content-hash": "5d43f427827d94274ff65182f6435dbc",
     "packages": [
         {
             "name": "bacon/bacon-qr-code",
@@ -8769,16 +8769,16 @@
     "packages-dev": [
         {
             "name": "php-parallel-lint/php-parallel-lint",
-            "version": "v1.3.2",
+            "version": "v1.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-parallel-lint/PHP-Parallel-Lint.git",
-                "reference": "6483c9832e71973ed29cf71bd6b3f4fde438a9de"
+                "reference": "6db563514f27e19595a19f45a4bf757b6401194e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-parallel-lint/PHP-Parallel-Lint/zipball/6483c9832e71973ed29cf71bd6b3f4fde438a9de",
-                "reference": "6483c9832e71973ed29cf71bd6b3f4fde438a9de",
+                "url": "https://api.github.com/repos/php-parallel-lint/PHP-Parallel-Lint/zipball/6db563514f27e19595a19f45a4bf757b6401194e",
+                "reference": "6db563514f27e19595a19f45a4bf757b6401194e",
                 "shasum": ""
             },
             "require": {
@@ -8816,13 +8816,17 @@
                     "email": "ahoj@jakubonderka.cz"
                 }
             ],
-            "description": "This tool check syntax of PHP files about 20x faster than serial check.",
+            "description": "This tool checks the syntax of PHP files about 20x faster than serial check.",
             "homepage": "https://github.com/php-parallel-lint/PHP-Parallel-Lint",
+            "keywords": [
+                "lint",
+                "static analysis"
+            ],
             "support": {
                 "issues": "https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues",
-                "source": "https://github.com/php-parallel-lint/PHP-Parallel-Lint/tree/v1.3.2"
+                "source": "https://github.com/php-parallel-lint/PHP-Parallel-Lint/tree/v1.4.0"
             },
-            "time": "2022-02-21T12:50:22+00:00"
+            "time": "2024-03-27T12:14:49+00:00"
         }
     ],
     "aliases": [
@@ -8845,5 +8849,5 @@
         "composer-plugin-api": "^2.0"
     },
     "platform-dev": [],
-    "plugin-api-version": "2.6.0"
+    "plugin-api-version": "2.3.0"
 }
diff --git a/composer/ClassLoader.php b/composer/ClassLoader.php
index 7824d8f..a72151c 100644
--- a/composer/ClassLoader.php
+++ b/composer/ClassLoader.php
@@ -45,34 +45,35 @@ class ClassLoader
     /** @var \Closure(string):void */
     private static $includeFile;
 
-    /** @var string|null */
+    /** @var ?string */
     private $vendorDir;
 
     // PSR-4
     /**
-     * @var array<string, array<string, int>>
+     * @var array[]
+     * @psalm-var array<string, array<string, int>>
      */
     private $prefixLengthsPsr4 = array();
     /**
-     * @var array<string, list<string>>
+     * @var array[]
+     * @psalm-var array<string, array<int, string>>
      */
     private $prefixDirsPsr4 = array();
     /**
-     * @var list<string>
+     * @var array[]
+     * @psalm-var array<string, string>
      */
     private $fallbackDirsPsr4 = array();
 
     // PSR-0
     /**
-     * List of PSR-0 prefixes
-     *
-     * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
-     *
-     * @var array<string, array<string, list<string>>>
+     * @var array[]
+     * @psalm-var array<string, array<string, string[]>>
      */
     private $prefixesPsr0 = array();
     /**
-     * @var list<string>
+     * @var array[]
+     * @psalm-var array<string, string>
      */
     private $fallbackDirsPsr0 = array();
 
@@ -80,7 +81,8 @@ class ClassLoader
     private $useIncludePath = false;
 
     /**
-     * @var array<string, string>
+     * @var string[]
+     * @psalm-var array<string, string>
      */
     private $classMap = array();
 
@@ -88,20 +90,21 @@ class ClassLoader
     private $classMapAuthoritative = false;
 
     /**
-     * @var array<string, bool>
+     * @var bool[]
+     * @psalm-var array<string, bool>
      */
     private $missingClasses = array();
 
-    /** @var string|null */
+    /** @var ?string */
     private $apcuPrefix;
 
     /**
-     * @var array<string, self>
+     * @var self[]
      */
     private static $registeredLoaders = array();
 
     /**
-     * @param string|null $vendorDir
+     * @param ?string $vendorDir
      */
     public function __construct($vendorDir = null)
     {
@@ -110,7 +113,7 @@ class ClassLoader
     }
 
     /**
-     * @return array<string, list<string>>
+     * @return string[]
      */
     public function getPrefixes()
     {
@@ -122,7 +125,8 @@ class ClassLoader
     }
 
     /**
-     * @return array<string, list<string>>
+     * @return array[]
+     * @psalm-return array<string, array<int, string>>
      */
     public function getPrefixesPsr4()
     {
@@ -130,7 +134,8 @@ class ClassLoader
     }
 
     /**
-     * @return list<string>
+     * @return array[]
+     * @psalm-return array<string, string>
      */
     public function getFallbackDirs()
     {
@@ -138,7 +143,8 @@ class ClassLoader
     }
 
     /**
-     * @return list<string>
+     * @return array[]
+     * @psalm-return array<string, string>
      */
     public function getFallbackDirsPsr4()
     {
@@ -146,7 +152,8 @@ class ClassLoader
     }
 
     /**
-     * @return array<string, string> Array of classname => path
+     * @return string[] Array of classname => path
+     * @psalm-return array<string, string>
      */
     public function getClassMap()
     {
@@ -154,7 +161,8 @@ class ClassLoader
     }
 
     /**
-     * @param array<string, string> $classMap Class to filename map
+     * @param string[] $classMap Class to filename map
+     * @psalm-param array<string, string> $classMap
      *
      * @return void
      */
@@ -171,25 +179,24 @@ class ClassLoader
      * Registers a set of PSR-0 directories for a given prefix, either
      * appending or prepending to the ones previously set for this prefix.
      *
-     * @param string              $prefix  The prefix
-     * @param list<string>|string $paths   The PSR-0 root directories
-     * @param bool                $prepend Whether to prepend the directories
+     * @param string          $prefix  The prefix
+     * @param string[]|string $paths   The PSR-0 root directories
+     * @param bool            $prepend Whether to prepend the directories
      *
      * @return void
      */
     public function add($prefix, $paths, $prepend = false)
     {
-        $paths = (array) $paths;
         if (!$prefix) {
             if ($prepend) {
                 $this->fallbackDirsPsr0 = array_merge(
-                    $paths,
+                    (array) $paths,
                     $this->fallbackDirsPsr0
                 );
             } else {
                 $this->fallbackDirsPsr0 = array_merge(
                     $this->fallbackDirsPsr0,
-                    $paths
+                    (array) $paths
                 );
             }
 
@@ -198,19 +205,19 @@ class ClassLoader
 
         $first = $prefix[0];
         if (!isset($this->prefixesPsr0[$first][$prefix])) {
-            $this->prefixesPsr0[$first][$prefix] = $paths;
+            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
 
             return;
         }
         if ($prepend) {
             $this->prefixesPsr0[$first][$prefix] = array_merge(
-                $paths,
+                (array) $paths,
                 $this->prefixesPsr0[$first][$prefix]
             );
         } else {
             $this->prefixesPsr0[$first][$prefix] = array_merge(
                 $this->prefixesPsr0[$first][$prefix],
-                $paths
+                (array) $paths
             );
         }
     }
@@ -219,9 +226,9 @@ class ClassLoader
      * Registers a set of PSR-4 directories for a given namespace, either
      * appending or prepending to the ones previously set for this namespace.
      *
-     * @param string              $prefix  The prefix/namespace, with trailing '\\'
-     * @param list<string>|string $paths   The PSR-4 base directories
-     * @param bool                $prepend Whether to prepend the directories
+     * @param string          $prefix  The prefix/namespace, with trailing '\\'
+     * @param string[]|string $paths   The PSR-4 base directories
+     * @param bool            $prepend Whether to prepend the directories
      *
      * @throws \InvalidArgumentException
      *
@@ -229,18 +236,17 @@ class ClassLoader
      */
     public function addPsr4($prefix, $paths, $prepend = false)
     {
-        $paths = (array) $paths;
         if (!$prefix) {
             // Register directories for the root namespace.
             if ($prepend) {
                 $this->fallbackDirsPsr4 = array_merge(
-                    $paths,
+                    (array) $paths,
                     $this->fallbackDirsPsr4
                 );
             } else {
                 $this->fallbackDirsPsr4 = array_merge(
                     $this->fallbackDirsPsr4,
-                    $paths
+                    (array) $paths
                 );
             }
         } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
@@ -250,18 +256,18 @@ class ClassLoader
                 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
             }
             $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
-            $this->prefixDirsPsr4[$prefix] = $paths;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
         } elseif ($prepend) {
             // Prepend directories for an already registered namespace.
             $this->prefixDirsPsr4[$prefix] = array_merge(
-                $paths,
+                (array) $paths,
                 $this->prefixDirsPsr4[$prefix]
             );
         } else {
             // Append directories for an already registered namespace.
             $this->prefixDirsPsr4[$prefix] = array_merge(
                 $this->prefixDirsPsr4[$prefix],
-                $paths
+                (array) $paths
             );
         }
     }
@@ -270,8 +276,8 @@ class ClassLoader
      * Registers a set of PSR-0 directories for a given prefix,
      * replacing any others previously set for this prefix.
      *
-     * @param string              $prefix The prefix
-     * @param list<string>|string $paths  The PSR-0 base directories
+     * @param string          $prefix The prefix
+     * @param string[]|string $paths  The PSR-0 base directories
      *
      * @return void
      */
@@ -288,8 +294,8 @@ class ClassLoader
      * Registers a set of PSR-4 directories for a given namespace,
      * replacing any others previously set for this namespace.
      *
-     * @param string              $prefix The prefix/namespace, with trailing '\\'
-     * @param list<string>|string $paths  The PSR-4 base directories
+     * @param string          $prefix The prefix/namespace, with trailing '\\'
+     * @param string[]|string $paths  The PSR-4 base directories
      *
      * @throws \InvalidArgumentException
      *
@@ -475,9 +481,9 @@ class ClassLoader
     }
 
     /**
-     * Returns the currently registered loaders keyed by their corresponding vendor directories.
+     * Returns the currently registered loaders indexed by their corresponding vendor directories.
      *
-     * @return array<string, self>
+     * @return self[]
      */
     public static function getRegisteredLoaders()
     {
diff --git a/composer/LICENSE b/composer/LICENSE
index f27399a..62ecfd8 100644
--- a/composer/LICENSE
+++ b/composer/LICENSE
@@ -1,4 +1,3 @@
-
 Copyright (c) Nils Adermann, Jordi Boggiano
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -18,4 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
-
diff --git a/composer/autoload_classmap.php b/composer/autoload_classmap.php
index 6bddcf0..6ed1c3f 100644
--- a/composer/autoload_classmap.php
+++ b/composer/autoload_classmap.php
@@ -1374,6 +1374,41 @@ return array(
     'GuzzleHttp\\Utils' => $vendorDir . '/guzzlehttp/guzzle/src/Utils.php',
     'HtmlFormatter\\HtmlFormatter' => $vendorDir . '/wikimedia/html-formatter/src/HtmlFormatter.php',
     'Image_XMP' => $vendorDir . '/james-heinrich/getid3/getid3/module.tag.xmp.php',
+    'JakubOnderka\\PhpParallelLint\\Application' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Application.php',
+    'JakubOnderka\\PhpParallelLint\\ArrayIterator' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Settings.php',
+    'JakubOnderka\\PhpParallelLint\\Blame' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Error.php',
+    'JakubOnderka\\PhpParallelLint\\CheckstyleOutput' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\ConsoleWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\Contracts\\SyntaxErrorCallback' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Contracts/SyntaxErrorCallback.php',
+    'JakubOnderka\\PhpParallelLint\\Error' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Error.php',
+    'JakubOnderka\\PhpParallelLint\\ErrorFormatter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/ErrorFormatter.php',
+    'JakubOnderka\\PhpParallelLint\\Exception' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+    'JakubOnderka\\PhpParallelLint\\FileWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\GitLabOutput' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\IWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\InvalidArgumentException' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+    'JakubOnderka\\PhpParallelLint\\JsonOutput' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\Manager' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Manager.php',
+    'JakubOnderka\\PhpParallelLint\\MultipleWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\NotExistsClassException' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+    'JakubOnderka\\PhpParallelLint\\NotExistsPathException' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+    'JakubOnderka\\PhpParallelLint\\NotImplementCallbackException' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+    'JakubOnderka\\PhpParallelLint\\NullWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\Output' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\ParallelLint' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/ParallelLint.php',
+    'JakubOnderka\\PhpParallelLint\\Process\\GitBlameProcess' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/GitBlameProcess.php',
+    'JakubOnderka\\PhpParallelLint\\Process\\LintProcess' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/LintProcess.php',
+    'JakubOnderka\\PhpParallelLint\\Process\\PhpExecutable' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/PhpExecutable.php',
+    'JakubOnderka\\PhpParallelLint\\Process\\PhpProcess' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/PhpProcess.php',
+    'JakubOnderka\\PhpParallelLint\\Process\\Process' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/Process.php',
+    'JakubOnderka\\PhpParallelLint\\Process\\SkipLintProcess' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/SkipLintProcess.php',
+    'JakubOnderka\\PhpParallelLint\\RecursiveDirectoryFilterIterator' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Manager.php',
+    'JakubOnderka\\PhpParallelLint\\Result' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Result.php',
+    'JakubOnderka\\PhpParallelLint\\RunTimeException' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+    'JakubOnderka\\PhpParallelLint\\Settings' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Settings.php',
+    'JakubOnderka\\PhpParallelLint\\SyntaxError' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Error.php',
+    'JakubOnderka\\PhpParallelLint\\TextOutput' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+    'JakubOnderka\\PhpParallelLint\\TextOutputColored' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
     'JsonException' => $vendorDir . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
     'JsonSchema\\Constraints\\BaseConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/BaseConstraint.php',
     'JsonSchema\\Constraints\\CollectionConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/CollectionConstraint.php',
@@ -1418,6 +1453,7 @@ return array(
     'JsonSchema\\Uri\\UriResolver' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Uri/UriResolver.php',
     'JsonSchema\\Uri\\UriRetriever' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php',
     'JsonSchema\\Validator' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Validator.php',
+    'JsonSerializable' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/polyfill.php',
     'Lcobucci\\Clock\\Clock' => $vendorDir . '/lcobucci/clock/src/Clock.php',
     'Lcobucci\\Clock\\FrozenClock' => $vendorDir . '/lcobucci/clock/src/FrozenClock.php',
     'Lcobucci\\Clock\\SystemClock' => $vendorDir . '/lcobucci/clock/src/SystemClock.php',
diff --git a/composer/autoload_static.php b/composer/autoload_static.php
index 4d801fd..9dd8a0b 100644
--- a/composer/autoload_static.php
+++ b/composer/autoload_static.php
@@ -2180,6 +2180,41 @@ class ComposerStaticInit_mediawiki_vendor
         'GuzzleHttp\\Utils' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Utils.php',
         'HtmlFormatter\\HtmlFormatter' => __DIR__ . '/..' . '/wikimedia/html-formatter/src/HtmlFormatter.php',
         'Image_XMP' => __DIR__ . '/..' . '/james-heinrich/getid3/getid3/module.tag.xmp.php',
+        'JakubOnderka\\PhpParallelLint\\Application' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Application.php',
+        'JakubOnderka\\PhpParallelLint\\ArrayIterator' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Settings.php',
+        'JakubOnderka\\PhpParallelLint\\Blame' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Error.php',
+        'JakubOnderka\\PhpParallelLint\\CheckstyleOutput' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\ConsoleWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\Contracts\\SyntaxErrorCallback' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Contracts/SyntaxErrorCallback.php',
+        'JakubOnderka\\PhpParallelLint\\Error' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Error.php',
+        'JakubOnderka\\PhpParallelLint\\ErrorFormatter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/ErrorFormatter.php',
+        'JakubOnderka\\PhpParallelLint\\Exception' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+        'JakubOnderka\\PhpParallelLint\\FileWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\GitLabOutput' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\IWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\InvalidArgumentException' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+        'JakubOnderka\\PhpParallelLint\\JsonOutput' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\Manager' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Manager.php',
+        'JakubOnderka\\PhpParallelLint\\MultipleWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\NotExistsClassException' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+        'JakubOnderka\\PhpParallelLint\\NotExistsPathException' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+        'JakubOnderka\\PhpParallelLint\\NotImplementCallbackException' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+        'JakubOnderka\\PhpParallelLint\\NullWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\Output' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\ParallelLint' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/ParallelLint.php',
+        'JakubOnderka\\PhpParallelLint\\Process\\GitBlameProcess' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/GitBlameProcess.php',
+        'JakubOnderka\\PhpParallelLint\\Process\\LintProcess' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/LintProcess.php',
+        'JakubOnderka\\PhpParallelLint\\Process\\PhpExecutable' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/PhpExecutable.php',
+        'JakubOnderka\\PhpParallelLint\\Process\\PhpProcess' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/PhpProcess.php',
+        'JakubOnderka\\PhpParallelLint\\Process\\Process' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/Process.php',
+        'JakubOnderka\\PhpParallelLint\\Process\\SkipLintProcess' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/SkipLintProcess.php',
+        'JakubOnderka\\PhpParallelLint\\RecursiveDirectoryFilterIterator' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Manager.php',
+        'JakubOnderka\\PhpParallelLint\\Result' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Result.php',
+        'JakubOnderka\\PhpParallelLint\\RunTimeException' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
+        'JakubOnderka\\PhpParallelLint\\Settings' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Settings.php',
+        'JakubOnderka\\PhpParallelLint\\SyntaxError' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Error.php',
+        'JakubOnderka\\PhpParallelLint\\TextOutput' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
+        'JakubOnderka\\PhpParallelLint\\TextOutputColored' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
         'JsonException' => __DIR__ . '/..' . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
         'JsonSchema\\Constraints\\BaseConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/BaseConstraint.php',
         'JsonSchema\\Constraints\\CollectionConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/CollectionConstraint.php',
@@ -2224,6 +2259,7 @@ class ComposerStaticInit_mediawiki_vendor
         'JsonSchema\\Uri\\UriResolver' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Uri/UriResolver.php',
         'JsonSchema\\Uri\\UriRetriever' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php',
         'JsonSchema\\Validator' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Validator.php',
+        'JsonSerializable' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/polyfill.php',
         'Lcobucci\\Clock\\Clock' => __DIR__ . '/..' . '/lcobucci/clock/src/Clock.php',
         'Lcobucci\\Clock\\FrozenClock' => __DIR__ . '/..' . '/lcobucci/clock/src/FrozenClock.php',
         'Lcobucci\\Clock\\SystemClock' => __DIR__ . '/..' . '/lcobucci/clock/src/SystemClock.php',
diff --git a/composer/installed.json b/composer/installed.json
index 2055a88..d4841cf 100644
--- a/composer/installed.json
+++ b/composer/installed.json
@@ -3911,6 +3911,70 @@
             },
             "install-path": "../pear/pear_exception"
         },
+        {
+            "name": "php-parallel-lint/php-parallel-lint",
+            "version": "v1.4.0",
+            "version_normalized": "1.4.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-parallel-lint/PHP-Parallel-Lint.git",
+                "reference": "6db563514f27e19595a19f45a4bf757b6401194e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-parallel-lint/PHP-Parallel-Lint/zipball/6db563514f27e19595a19f45a4bf757b6401194e",
+                "reference": "6db563514f27e19595a19f45a4bf757b6401194e",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "php": ">=5.3.0"
+            },
+            "replace": {
+                "grogy/php-parallel-lint": "*",
+                "jakub-onderka/php-parallel-lint": "*"
+            },
+            "require-dev": {
+                "nette/tester": "^1.3 || ^2.0",
+                "php-parallel-lint/php-console-highlighter": "0.* || ^1.0",
+                "squizlabs/php_codesniffer": "^3.6"
+            },
+            "suggest": {
+                "php-parallel-lint/php-console-highlighter": "Highlight syntax in code snippet"
+            },
+            "time": "2024-03-27T12:14:49+00:00",
+            "bin": [
+                "parallel-lint"
+            ],
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "classmap": [
+                    "./src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-2-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Jakub Onderka",
+                    "email": "ahoj@jakubonderka.cz"
+                }
+            ],
+            "description": "This tool checks the syntax of PHP files about 20x faster than serial check.",
+            "homepage": "https://github.com/php-parallel-lint/PHP-Parallel-Lint",
+            "keywords": [
+                "lint",
+                "static analysis"
+            ],
+            "support": {
+                "issues": "https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues",
+                "source": "https://github.com/php-parallel-lint/PHP-Parallel-Lint/tree/v1.4.0"
+            },
+            "install-path": "../php-parallel-lint/php-parallel-lint"
+        },
         {
             "name": "pimple/pimple",
             "version": "v3.5.0",
@@ -9146,6 +9210,8 @@
             "install-path": "../zordius/lightncandy"
         }
     ],
-    "dev": false,
-    "dev-package-names": []
+    "dev": true,
+    "dev-package-names": [
+        "php-parallel-lint/php-parallel-lint"
+    ]
 }
diff --git a/composer/installed.php b/composer/installed.php
index 4e55570..b4717a6 100644
--- a/composer/installed.php
+++ b/composer/installed.php
@@ -3,17 +3,17 @@
         'name' => '__root__',
         'pretty_version' => 'dev-master',
         'version' => 'dev-master',
-        'reference' => 'f94adaaa03ba6fe9bddc2ac498db50955dd381de',
+        'reference' => 'fd4498bc6a5f80a8b56b03e0be446600188e86f0',
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'dev' => false,
+        'dev' => true,
     ),
     'versions' => array(
         '__root__' => array(
             'pretty_version' => 'dev-master',
             'version' => 'dev-master',
-            'reference' => 'f94adaaa03ba6fe9bddc2ac498db50955dd381de',
+            'reference' => 'fd4498bc6a5f80a8b56b03e0be446600188e86f0',
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
@@ -235,6 +235,12 @@
             'aliases' => array(),
             'dev_requirement' => false,
         ),
+        'grogy/php-parallel-lint' => array(
+            'dev_requirement' => true,
+            'replaced' => array(
+                0 => '*',
+            ),
+        ),
         'guzzlehttp/guzzle' => array(
             'pretty_version' => '7.7.1',
             'version' => '7.7.1.0',
@@ -277,6 +283,12 @@
             'aliases' => array(),
             'dev_requirement' => false,
         ),
+        'jakub-onderka/php-parallel-lint' => array(
+            'dev_requirement' => true,
+            'replaced' => array(
+                0 => '*',
+            ),
+        ),
         'james-heinrich/getid3' => array(
             'pretty_version' => 'v1.9.23',
             'version' => '1.9.23.0',
@@ -556,6 +568,15 @@
             'aliases' => array(),
             'dev_requirement' => false,
         ),
+        'php-parallel-lint/php-parallel-lint' => array(
+            'pretty_version' => 'v1.4.0',
+            'version' => '1.4.0.0',
+            'reference' => '6db563514f27e19595a19f45a4bf757b6401194e',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../php-parallel-lint/php-parallel-lint',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
         'pimple/pimple' => array(
             'pretty_version' => 'v3.5.0',
             'version' => '3.5.0.0',
diff --git a/php-parallel-lint/php-parallel-lint/CHANGELOG.md b/php-parallel-lint/php-parallel-lint/CHANGELOG.md
new file mode 100644
index 0000000..3902e57
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/CHANGELOG.md
@@ -0,0 +1,256 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
+
+## [Unreleased]
+
+[Unreleased]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/compare/v1.3.2...HEAD
+
+
+## [1.4.0] - 2024-03-27
+
+### Added
+- The "skip linting" feature can now be used in PHP files starting with a shebang, [#146] from [@xaben].
+
+### Fixed
+- PHP 8.4 deprecation notice, [#154] from [@Ayesh] and [@jrfnl].
+- Bug fix: the PHP version check in the application bootstrap did not work on PHP < 5.3, [#100] from [@jrfnl], fixes [#62].
+- Bug fix: files containing the `~` character in their name can now be processed correctly, [#118] from [@jrfnl].
+- Bug fix: error message sometimes displayed on last line of code snippet, [#98] from [@jrfnl], fixes [#93].
+- Bug fix: error message would sometimes contain duplicate information, [#117] from [@jrfnl].
+- Bug fix: the "in file .. on line part" text did not always get cleaned correctly from the error message, [#118] from [@jrfnl].
+
+### Changed
+- The percentage output in the progress report is now aligned, [#140] from [@robertology].
+- The error message displayed when the PHP version is too low for the application to run is now more informative, [#100] from [@jrfnl].
+- Composer: The package will now identify itself as a static analysis/linting tool, [#134] from [@staabm].
+- Composer: fix grammar error, [#139] from [@TravisCarden].
+- README: improvement to the install instructions, [#99] from [@samsonasik], fixes [#96].
+- README: move screenshot, [#97] from [@jrfnl].
+- README: fix typos, [#124] from [@krsriq].
+- Docs: code style consistency, [#137] from [@lens0021].
+
+### Internal
+- Prevent PHAR not being compatible with PHP < 7.0, [#116] from [@jrfnl].
+- GH Actions: update used actions, [#109], [#158] from [@jrfnl].
+- GH Actions: updates for box 4.x, [#121] from [@jrfnl].
+- GH Actions: fix download URL for box, [#125] from [@jrfnl].
+- GH Actions: use fail-fast with setup-php when creating the binaries, [#131], [#132] from [@jrfnl].
+- GH Actions: update PHP version for PHAR boxing, [#152] from [@jrfnl].
+- GH Actions: harden the workflow against PHPCS ruleset errors, [#128] from [@jrfnl].
+- GH Actions: bust the cache semi-regularly, [#129] from [@jrfnl].
+- GH Actions: update PHP versions in workflows, [#130] from [@jrfnl].
+- GH Actions: update for the release of PHP 8.3, [#150], [#151] from [@jrfnl].
+- GH Actions: fix duplicate release, [#159] from [@jrfnl].
+- SettingsParseArgumentsTest: fix bug in test, [#102] from [@jrfnl].
+- OutputTest: fix risky test, [#156] from [@jrfnl].
+- Tests: fix issue with Nette Tester 1.x, [#141] from [@grogy].
+- Add dependabot configuration file, [#148] from [@jrfnl].
+
+[1.4.0]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/compare/v1.3.2...v1.4.0
+
+[#62]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues/62
+[#93]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues/93
+[#96]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues/96
+[#97]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/97
+[#98]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/98
+[#99]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/99
+[#100]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/100
+[#102]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/102
+[#109]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/109
+[#116]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/116
+[#117]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/117
+[#118]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/118
+[#121]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/121
+[#124]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/124
+[#125]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/125
+[#128]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/128
+[#129]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/129
+[#130]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/130
+[#131]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/131
+[#132]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/132
+[#134]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/134
+[#137]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/137
+[#139]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/139
+[#140]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/140
+[#141]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/141
+[#146]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/146
+[#148]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/148
+[#150]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/150
+[#151]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/151
+[#152]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/152
+[#154]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/154
+[#156]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/156
+[#158]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/158
+[#159]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/159
+
+
+## [1.3.2] - 2022-02-19
+
+### Added
+
+- Support for PHP Console Highlighter 1.0.0, which comes with PHP Console Color 1.0.1, [#92] from [@jrfnl].
+
+### Fixed
+
+- Bug fix: make Phar file run independently of project under scan [#63] from [@jrfnl], fixes [#61].
+- Bug fix: checkstyle report could contain invalid XML due to insufficient output escaping [#73] from [@gmazzap], fixes [#72].
+- Fix Phar building [#70] from [@jrfnl]. This fixes PHP 8.1 compatibility for the Phar file.
+- Documentation fix: the `--show-deprecated` option was missing in both the README as well as the CLI `help` [#84] from [@jrfnl], fixes [#81] reported by [@stronk7].
+
+### Changed
+
+- README: updated information about PHAR availability [#77] from [@jrfnl].
+- README: updated CLI example [#80] from [@jrfnl].
+- README: added documentation on how to exclude files from a scan based on the PHP version used [#80] from [@jrfnl].
+- Composer autoload improvement [#88] from [@jrfnl] with thanks to [@mfn].
+
+### Internal
+
+- Welcome [@jrfnl] as a new maintainer [#32].
+- GH Actions: set error reporting to E_ALL [#65], [#76] from [@jrfnl].
+- GH Actions: fix failing tests on PHP 5.3-5.5 [#71] from [@jrfnl] and [@villfa].
+- GH Actions: auto-cancel concurrent builds [#76] from [@jrfnl].
+- GH Actions: testing against PHP 8.2 [#74] from [@grogy].
+- GH Actions: release testing against PHP 5.3 [#79] from [@jrfnl].
+- GH Actions: update used actions [#82] from [@jrfnl].
+- Release checklist can now be found in the `.github` folder [#78] from [@jrfnl].
+
+[1.3.2]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/compare/v1.3.1...v1.3.2
+
+[#32]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues/32
+[#61]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues/61
+[#63]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/63
+[#65]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/65
+[#70]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/70
+[#71]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/71
+[#72]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues/72
+[#73]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/73
+[#74]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/74
+[#76]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/76
+[#77]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/77
+[#78]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/78
+[#79]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/79
+[#80]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/80
+[#81]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues/81
+[#82]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/82
+[#84]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/84
+[#88]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/88
+[#89]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/89
+[#92]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/92
+
+
+## [1.3.1] - 2021-08-13
+
+### Added
+
+- Extend by the Code Climate output format [#50] from [@lukas9393]. 
+
+### Fixed
+
+- PHP 8.1: silence the deprecation notices about missing return types [#64] from [@jrfnl].
+
+### Internal
+
+- Reformat changelog to use reflinks in changelog entries [#58] from [@glensc].
+
+[1.3.1]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/compare/v1.3.0...v1.3.1
+
+[#50]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/50
+[#58]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/58
+[#64]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/64
+
+## [1.3.0] - 2021-04-07
+
+### Added
+
+- Allow for multi-part file extensions to be passed using -e (like `-e php,php.dist`) from [@jrfnl].
+- Added syntax error callback [#30] from [@arxeiss].
+- Ignore PHP startup errors [#34] from [@jrfnl].
+- Restore php 5.3 support [#51] from [@glensc].
+
+### Fixed
+
+- Determine skip lint process failure by status code instead of stderr content [#48] from [@jankonas].
+
+### Changed
+
+- Improve wording in the readme [#52] from [@glensc].
+
+### Internal
+
+- Normalized composer.json from [@OndraM].
+- Updated PHPCS dependency from [@jrfnl].
+- Cleaned coding style from [@jrfnl].
+- Provide one true way to run the test suite [#37] from [@mfn].
+- Travis: add build against PHP 8.0 and fix failing test [#41] from [@jrfnl].
+- GitHub Actions for testing, and automatic phar creation [#46] from [@roelofr].
+- Add .github folder to .gitattributes export-ignore [#54] from [@reedy].
+- Suggest to curl composer install via HTTPS [#53] from [@reedy].
+- GH Actions: allow for manually triggering a workflow [#55] from [@jrfnl].
+- GH Actions: fix phar creation [#55] from [@jrfnl].
+- GH Actions: run the tests against all supported PHP versions [#55] from [@jrfnl].
+- GH Actions: report CS violations in the PR [#55] from [@jrfnl].
+
+[1.3.0]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/compare/v1.2.0...v1.3.0
+[#30]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/30
+[#34]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/34
+[#37]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/37
+[#41]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/41
+[#46]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/46
+[#48]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/48
+[#51]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/51
+[#52]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/52
+[#53]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/53
+[#54]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/54
+[#55]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/55
+
+## [1.2.0] - 2020-04-04
+
+### Added
+
+- Added changelog.
+
+### Fixed
+
+- Fixed vendor location for running from other folder from [@Erkens].
+
+### Internal
+
+- Added a .gitattributes file from [@jrfnl], thanks for issue to [@ondrejmirtes].
+- Fixed incorrect unit tests from [@jrfnl].
+- Fixed minor grammatical errors from [@jrfnl].
+- Added Travis: test against nightly (= PHP 8) from [@jrfnl].
+- Travis: removed sudo from [@jrfnl].
+- Added info about installing like not a dependency.
+- Cleaned readme - new organization from previous package.
+- Added checklist for new version from [@szepeviktor].
+
+[1.2.0]: https://github.com/php-parallel-lint/PHP-Parallel-Lint/compare/v1.1.0...v1.2.0
+
+[@arxeiss]:      https://github.com/arxeiss
+[@Ayesh]:        https://github.com/Ayesh
+[@Erkens]:       https://github.com/Erkens
+[@glensc]:       https://github.com/glensc
+[@gmazzap]:      https://github.com/gmazzap
+[@grogy]:        https://github.com/grogy
+[@jankonas]:     https://github.com/jankonas
+[@jrfnl]:        https://github.com/jrfnl
+[@krsriq]:       https://github.com/krsriq
+[@lens0021]:     https://github.com/lens0021
+[@lukas9393]:    https://github.com/lukas9393
+[@mfn]:          https://github.com/mfn
+[@OndraM]:       https://github.com/OndraM
+[@ondrejmirtes]: https://github.com/ondrejmirtes
+[@reedy]:        https://github.com/reedy
+[@robertology]:  https://github.com/robertology
+[@roelofr]:      https://github.com/roelofr
+[@samsonasik]:   https://github.com/samsonasik
+[@staabm]:       https://github.com/staabm
+[@stronk7]:      https://github.com/stronk7
+[@szepeviktor]:  https://github.com/szepeviktor
+[@TravisCarden]: https://github.com/TravisCarden
+[@villfa]:       https://github.com/villfa
+[@xaben]:        https://github.com/xaben
diff --git a/php-parallel-lint/php-parallel-lint/LICENSE b/php-parallel-lint/php-parallel-lint/LICENSE
new file mode 100644
index 0000000..09429bb
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/LICENSE
@@ -0,0 +1,26 @@
+Copyright (c) 2012, Jakub Onderka
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those
+of the authors and should not be interpreted as representing official policies,
+either expressed or implied, of the FreeBSD Project.
diff --git a/php-parallel-lint/php-parallel-lint/README.md b/php-parallel-lint/php-parallel-lint/README.md
new file mode 100644
index 0000000..4449f06
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/README.md
@@ -0,0 +1,122 @@
+# PHP Parallel Lint
+
+[![Downloads this Month](https://img.shields.io/packagist/dm/php-parallel-lint/php-parallel-lint.svg)](https://packagist.org/packages/php-parallel-lint/php-parallel-lint)
+[![Build Status](https://github.com/php-parallel-lint/PHP-Parallel-Lint/actions/workflows/test.yml/badge.svg)](https://github.com/php-parallel-lint/PHP-Parallel-Lint/actions/workflows/test.yml)
+[![License](https://poser.pugx.org/php-parallel-lint/php-parallel-lint/license.svg)](https://packagist.org/packages/php-parallel-lint/php-parallel-lint)
+
+This application checks the syntax of PHP files in parallel.
+It can output in plain text, colored text, json and checksyntax formats.
+Additionally `blame` can be used to show commits that introduced the breakage.
+
+Running parallel jobs in PHP is inspired by Nette framework tests.
+
+The application is officially supported for use with PHP 5.3 to 8.3.
+
+## Table of contents
+
+1. [Installation](#installation)
+2. [Example output](#example-output)
+3. [History](#history)
+4. [Command line options](#command-line-options)
+5. [Recommended excludes for Symfony framework](#recommended-excludes-for-symfony-framework)
+6. [Excluding files from a scan based on the PHP version used](#excluding-files-from-a-scan-based-on-the-php-version-used)
+7. [How to upgrade](#how-to-upgrade)
+
+## Installation
+
+Install with `composer` as development dependency:
+
+    composer require --dev php-parallel-lint/php-parallel-lint
+
+Alternatively you can install as a standalone `composer` project:
+
+    composer create-project php-parallel-lint/php-parallel-lint /path/to/folder/php-parallel-lint --no-dev
+    /path/to/folder/php-parallel-lint/parallel-lint # running tool
+
+For colored output, install the suggested package `php-parallel-lint/php-console-highlighter`:
+
+    composer require --dev php-parallel-lint/php-console-highlighter
+
+Since v1.3.0, a PHAR file is also made available for each release.
+This PHAR file is published as an asset for each release and can be found on the [Releases](https://github.com/php-parallel-lint/PHP-Parallel-Lint/releases) page.
+
+## Example output
+
+![Example use of tool with error](/doc/use-error.png?raw=true "Example use of tool with error")
+
+
+## History
+
+This project was originally created by [@JakubOnderka] and released as
+[jakub-onderka/php-parallel-lint].
+
+Since then, Jakub has moved on to other interests and as of January 2020, the
+second most active maintainer [@grogy] has taken over maintenance of the project
+and given the project - and related dependencies - a new home in the PHP
+Parallel Lint organisation.
+
+It is strongly recommended for existing users of the (unmaintained)
+[jakub-onderka/php-parallel-lint] package to switch their dependency to
+[php-parallel-lint/php-parallel-lint], see [How to upgrade](#how-to-upgrade) below.
+
+[php-parallel-lint/php-parallel-lint]: https://github.com/php-parallel-lint/PHP-Parallel-Lint
+[grogy/php-parallel-lint]: https://github.com/grogy/PHP-Parallel-Lint
+[jakub-onderka/php-parallel-lint]: https://github.com/JakubOnderka/PHP-Parallel-Lint
+[@JakubOnderka]: https://github.com/JakubOnderka
+[@grogy]: https://github.com/grogy
+
+## Command line options
+
+- `-p <php>`                Specify PHP-CGI executable to run (default: 'php').
+- `-s`, `--short`           Set short_open_tag to On (default: Off).
+- `-a`, `--asp`             Set asp_tags to On (default: Off).
+- `-e <ext>`                Check only files with selected extensions separated by comma. (default: php,php3,php4,php5,phtml,phpt)
+- `-j <num>`                Run <num> jobs in parallel (default: 10).
+- `--exclude`               Exclude a file or directory. If you want to exclude multiple items, use multiple exclude parameters.
+- `--colors`                Enable colors in console output. (disables auto detection of color support)
+- `--no-colors`             Disable colors in console output.
+- `--no-progress`           Disable progress in console output.
+- `--checkstyle`            Output results as Checkstyle XML.
+- `--json`                  Output results as JSON string (requires PHP 5.4).
+- `--gitlab`                Output results for the GitLab Code Quality Widget (requires PHP 5.4), see more in [Code Quality](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html) documentation..
+- `--blame`                 Try to show git blame for row with error.
+- `--git <git>`             Path to Git executable to show blame message (default: 'git').
+- `--stdin`                 Load files and folder to test from standard input.
+- `--ignore-fails`          Ignore failed tests.
+- `--show-deprecated`       Show deprecations (default: Off).
+- `--syntax-error-callback` File with syntax error callback for ability to modify error, see more in [example](doc/syntax-error-callback.md).
+- `-h`, `--help`            Print this help.
+- `-V`, `--version`         Display the application version
+
+
+## Recommended excludes for Symfony framework
+
+To run from the command line:
+
+    vendor/bin/parallel-lint --exclude .git --exclude app --exclude vendor .
+
+
+## Excluding files from a scan based on the PHP version used
+
+Sometimes a particular file in a project may not comply with the project-wide minimum PHP version, like a file which is conditionally included in the project and contains PHP syntax which needs a higher PHP version to run.
+
+This can make it complicated to run Parallel Lint in a CI context, as the `exclude`s used in the command would have to be adjusted based on the PHP version on which the scan is being run.
+
+PHP Parallel Lint offers a straight-forward way around this, as files can define their own minimum PHP version like so:
+```php
+<?php // lint >= 7.4
+
+// Code which contains PHP 7.4 syntax.
+```
+
+With this comment in place, the file will be automatically skipped when PHP Parallel Lint is run on a PHP version lower than PHP 7.4.
+
+Note: The `// lint >= 7.4` comment has to be only the first line of the file and must directly follow the PHP open tag.
+
+
+## How to upgrade
+
+Are you using the `jakub-onderka/php-parallel-lint` package? You can switch to `php-parallel-lint/php-parallel-lint` using:
+
+    composer remove --dev jakub-onderka/php-parallel-lint
+    composer require --dev php-parallel-lint/php-parallel-lint
diff --git a/php-parallel-lint/php-parallel-lint/bin/skip-linting.php b/php-parallel-lint/php-parallel-lint/bin/skip-linting.php
new file mode 100644
index 0000000..9cb552b
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/bin/skip-linting.php
@@ -0,0 +1,25 @@
+<?php
+$stdin = fopen('php://stdin', 'r');
+$input = stream_get_contents($stdin);
+fclose($stdin);
+
+foreach (explode(PHP_EOL, $input) as $file) {
+    $skip = false;
+    $f = @fopen($file, 'r');
+    if ($f) {
+        $firstLine = fgets($f);
+
+        // ignore shebang line
+        if (strpos($firstLine, '#!') === 0) {
+            $firstLine = fgets($f);
+        }
+
+        @fclose($f);
+
+        if (preg_match('~<?php\\s*\\/\\/\s*lint\s*([^\d\s]+)\s*([^\s]+)\s*~i', $firstLine, $m)) {
+            $skip = version_compare(PHP_VERSION, $m[2], $m[1]) === false;
+        }
+    }
+
+    echo $file . ';' . ($skip ? '1' : '0') . PHP_EOL;
+}
diff --git a/php-parallel-lint/php-parallel-lint/composer.json b/php-parallel-lint/php-parallel-lint/composer.json
new file mode 100644
index 0000000..cdec2a0
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/composer.json
@@ -0,0 +1,55 @@
+{
+    "name": "php-parallel-lint/php-parallel-lint",
+    "description": "This tool checks the syntax of PHP files about 20x faster than serial check.",
+    "homepage": "https://github.com/php-parallel-lint/PHP-Parallel-Lint",
+    "license": "BSD-2-Clause",
+    "keywords": [
+        "lint",
+        "static analysis"
+    ],
+    "authors": [
+        {
+            "name": "Jakub Onderka",
+            "email": "ahoj@jakubonderka.cz"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.0",
+        "ext-json": "*"
+    },
+    "replace": {
+        "grogy/php-parallel-lint": "*",
+        "jakub-onderka/php-parallel-lint": "*"
+    },
+    "require-dev": {
+        "nette/tester": "^1.3 || ^2.0",
+        "php-parallel-lint/php-console-highlighter": "0.* || ^1.0",
+        "squizlabs/php_codesniffer": "^3.6"
+    },
+    "suggest": {
+        "php-parallel-lint/php-console-highlighter": "Highlight syntax in code snippet"
+    },
+    "config": {
+        "sort-packages": true
+    },
+    "autoload": {
+        "classmap": [
+            "./src/"
+        ]
+    },
+    "autoload-dev": {
+        "classmap": [
+            "./tests/"
+        ]
+    },
+    "bin": [
+        "parallel-lint"
+    ],
+    "scripts": {
+        "test": "@php vendor/bin/tester -C -p php tests",
+        "testphp5": "@php vendor/bin/tester -c tests/php5.3-5.5.ini -p php tests"
+    },
+    "scripts-descriptions": {
+        "test": "Run all tests!"
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/parallel-lint b/php-parallel-lint/php-parallel-lint/parallel-lint
new file mode 100755
index 0000000..4e05f11
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/parallel-lint
@@ -0,0 +1,84 @@
+#!/usr/bin/env php
+<?php
+
+/*
+Copyright (c) 2014, Jakub Onderka
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those
+of the authors and should not be interpreted as representing official policies,
+either expressed or implied, of the FreeBSD Project.
+ */
+
+if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 50300) {
+    fwrite(
+        STDERR,
+        sprintf(
+            'PHP Parallel Lint requires PHP 5.3.0 or newer.' . PHP_EOL
+            . 'You are using PHP %s (%s).' . PHP_EOL,
+            PHP_VERSION,
+            PHP_BINDIR
+        )
+    );
+    exit(254);
+}
+
+$autoloadLocations = array(
+    getcwd() . '/vendor/autoload.php',
+    getcwd() . '/../../autoload.php',
+    __DIR__ . '/vendor/autoload.php',
+    __DIR__ . '/../vendor/autoload.php',
+    __DIR__ . '/../../../autoload.php',
+    __DIR__ . '/../../autoload.php',
+);
+
+if (class_exists('Phar') && Phar::running() !== '') {
+    // Running from a phar file. Prevent loading - potentially blocking - project autoload file.
+    $autoloadLocations = array(
+        __DIR__ . '/vendor/autoload.php',
+    );
+}
+
+$loaded = false;
+foreach ($autoloadLocations as $autoload) {
+    if (is_file($autoload)) {
+        require_once($autoload);
+        $loaded = true;
+    }
+}
+
+if (!$loaded) {
+    fwrite(STDERR,
+        'You must set up the project dependencies, run the following commands:' . PHP_EOL .
+        'curl -s https://getcomposer.org/installer | php' . PHP_EOL .
+        'php composer.phar install' . PHP_EOL
+    );
+    exit(254);
+}
+
+require_once __DIR__ . '/src/polyfill.php';
+
+// Prevent parse error on PHP < 5.3 so the version check above can work.
+$className = 'JakubOnderka\PhpParallelLint\Application';
+$app = new $className();
+exit($app->run());
diff --git a/php-parallel-lint/php-parallel-lint/src/Application.php b/php-parallel-lint/php-parallel-lint/src/Application.php
new file mode 100644
index 0000000..0cbab11
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Application.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace JakubOnderka\PhpParallelLint;
+
+class Application
+{
+    const VERSION = '1.4.0';
+
+    // Return codes
+    const SUCCESS = 0,
+        WITH_ERRORS = 1,
+        FAILED = 254; // Error code 255 is reserved for PHP itself
+
+    /**
+     * Run the application
+     * @return int Return code
+     */
+    public function run()
+    {
+        if (in_array('proc_open', explode(',', ini_get('disable_functions')))) {
+            echo "Function 'proc_open' is required, but it is disabled by the 'disable_functions' ini setting.", PHP_EOL;
+            return self::FAILED;
+        }
+
+        if (in_array('-h', $_SERVER['argv']) || in_array('--help', $_SERVER['argv'])) {
+            $this->showUsage();
+            return self::SUCCESS;
+        }
+
+        if (in_array('-V', $_SERVER['argv']) || in_array('--version', $_SERVER['argv'])) {
+            $this->showVersion();
+            return self::SUCCESS;
+        }
+
+        try {
+            $settings = Settings::parseArguments($_SERVER['argv']);
+            if ($settings->stdin) {
+                $settings->addPaths(Settings::getPathsFromStdIn());
+            }
+            if (empty($settings->paths)) {
+                $this->showUsage();
+                return self::FAILED;
+            }
+            $manager = new Manager;
+            $result = $manager->run($settings);
+            if ($settings->ignoreFails) {
+                return $result->hasSyntaxError() ? self::WITH_ERRORS : self::SUCCESS;
+            } else {
+                return $result->hasError() ? self::WITH_ERRORS : self::SUCCESS;
+            }
+
+        } catch (InvalidArgumentException $e) {
+            echo "Invalid option {$e->getArgument()}", PHP_EOL, PHP_EOL;
+            $this->showOptions();
+            return self::FAILED;
+
+        } catch (Exception $e) {
+            if (isset($settings) && $settings->format === Settings::FORMAT_JSON) {
+                echo json_encode($e);
+            } else {
+                echo $e->getMessage(), PHP_EOL;
+            }
+            return self::FAILED;
+
+        } catch (\Exception $e) {
+            echo $e->getMessage(), PHP_EOL;
+            return self::FAILED;
+        }
+    }
+
+    /**
+     * Outputs the options
+     */
+    private function showOptions()
+    {
+        echo <<<HELP
+Options:
+    -p <php>                Specify PHP-CGI executable to run (default: 'php').
+    -s, --short             Set short_open_tag to On (default: Off).
+    -a, --asp               Set asp_tags to On (default: Off).
+    -e <ext>                Check only files with selected extensions separated by comma.
+                            (default: php,php3,php4,php5,phtml,phpt)
+    -j <num>                Run <num> jobs in parallel (default: 10).
+    --exclude               Exclude a file or directory. If you want exclude multiple items,
+                            use multiple exclude parameters.
+    --colors                Enable colors in console output.
+                            (disables auto detection of color support)
+    --no-colors             Disable colors in console output.
+    --no-progress           Disable progress in console output.
+    --checkstyle            Output results as Checkstyle XML.
+    --json                  Output results as JSON string
+                            (requires PHP 5.4).
+    --gitlab                Output results for the GitLab Code Quality Widget
+                            (requires PHP 5.4).
+    --blame                 Try to show git blame for row with error.
+    --git <git>             Path to Git executable to show blame message (default: 'git').
+    --stdin                 Load files and folder to test from standard input.
+    --ignore-fails          Ignore failed tests.
+    --show-deprecated       Show deprecations (default: Off).
+    --syntax-error-callback File with syntax error callback for ability to modify error
+    -h, --help              Print this help.
+    -V, --version           Display the application version
+
+HELP;
+    }
+
+    /**
+     * Outputs the current version
+     */
+    private function showVersion()
+    {
+        echo 'PHP Parallel Lint version ' . self::VERSION.PHP_EOL;
+    }
+
+    /**
+     * Shows usage
+     */
+    private function showUsage()
+    {
+        $this->showVersion();
+        echo <<<USAGE
+-------------------------------
+Usage:
+parallel-lint [sa] [-p php] [-e ext] [-j num] [--exclude dir] [files or directories]
+
+USAGE;
+        $this->showOptions();
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Contracts/SyntaxErrorCallback.php b/php-parallel-lint/php-parallel-lint/src/Contracts/SyntaxErrorCallback.php
new file mode 100644
index 0000000..0db055f
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Contracts/SyntaxErrorCallback.php
@@ -0,0 +1,13 @@
+<?php
+namespace JakubOnderka\PhpParallelLint\Contracts;
+
+use JakubOnderka\PhpParallelLint\SyntaxError;
+
+interface SyntaxErrorCallback
+{
+    /**
+     * @param SyntaxError $error
+     * @return SyntaxError
+     */
+    public function errorFound(SyntaxError $error);
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Error.php b/php-parallel-lint/php-parallel-lint/src/Error.php
new file mode 100644
index 0000000..61cace9
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Error.php
@@ -0,0 +1,242 @@
+<?php
+namespace JakubOnderka\PhpParallelLint;
+
+use ReturnTypeWillChange;
+
+class Error implements \JsonSerializable
+{
+    /** @var string */
+    protected $filePath;
+
+    /** @var string */
+    protected $message;
+
+    /**
+     * @param string $filePath
+     * @param string $message
+     */
+    public function __construct($filePath, $message)
+    {
+        $this->filePath = $filePath;
+        $this->message = rtrim($message);
+    }
+
+    /**
+     * @return string
+     */
+    public function getMessage()
+    {
+        return $this->message;
+    }
+
+    /**
+     * @return string
+     */
+    public function getFilePath()
+    {
+        return $this->filePath;
+    }
+
+    /**
+     * @return string
+     */
+    public function getShortFilePath()
+    {
+        $cwd = getcwd();
+
+        if ($cwd === '/') {
+            // For root directory in unix, do not modify path
+            return $this->filePath;
+        }
+
+        return preg_replace('/' . preg_quote($cwd, '/') . '/', '', $this->filePath, 1);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.4.0)<br/>
+     * Specify data which should be serialized to JSON
+     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
+     * @return mixed data which can be serialized by <b>json_encode</b>,
+     * which is a value of any type other than a resource.
+     */
+    #[ReturnTypeWillChange]
+    public function jsonSerialize()
+    {
+        return array(
+            'type' => 'error',
+            'file' => $this->getFilePath(),
+            'message' => $this->getMessage(),
+        );
+    }
+}
+
+class Blame implements \JsonSerializable
+{
+    public $name;
+
+    public $email;
+
+    /** @var \DateTime */
+    public $datetime;
+
+    public $commitHash;
+
+    public $summary;
+
+    /**
+     * (PHP 5 &gt;= 5.4.0)<br/>
+     * Specify data which should be serialized to JSON
+     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
+     * @return mixed data which can be serialized by <b>json_encode</b>,
+     * which is a value of any type other than a resource.
+     */
+    #[ReturnTypeWillChange]
+    function jsonSerialize()
+    {
+        return array(
+            'name' => $this->name,
+            'email' => $this->email,
+            'datetime' => $this->datetime,
+            'commitHash' => $this->commitHash,
+            'summary' => $this->summary,
+        );
+    }
+
+
+}
+
+class SyntaxError extends Error
+{
+    const IN_ON_REGEX = '~ in %s on line [0-9]+$~';
+
+    /** @var Blame */
+    private $blame;
+
+    /**
+     * @return int|null
+     */
+    public function getLine()
+    {
+        preg_match('~on line ([0-9]+)$~', $this->message, $matches);
+
+        if ($matches && isset($matches[1])) {
+            $onLine = (int) $matches[1];
+            return $onLine;
+        }
+
+        return null;
+    }
+
+    /**
+     * @param bool $translateTokens
+     * @return mixed|string
+     */
+    public function getNormalizedMessage($translateTokens = false)
+    {
+        $message  = preg_replace('~^(Parse|Fatal) error: (syntax error, )?~', '', $this->message);
+        $baseName = basename($this->filePath);
+        $regex    = sprintf(self::IN_ON_REGEX, preg_quote($baseName, '~'));
+        $message  = preg_replace($regex, '', $message, -1, $count);
+
+        if ($count === 0 && strpos($baseName, '\\') !== false) {
+            $baseName = ltrim(strrchr($this->filePath, '\\'), '\\');
+            $regex    = sprintf(self::IN_ON_REGEX, preg_quote($baseName, '~'));
+            $message  = preg_replace($regex, '', $message, -1, $count);
+        }
+
+        if ($count === 0) {
+            $regex   = sprintf(self::IN_ON_REGEX, preg_quote($this->filePath, '~'));
+            $message = preg_replace($regex, '', $message);
+        }
+
+        $message = ucfirst($message);
+
+        if ($translateTokens) {
+            $message = $this->translateTokens($message);
+        }
+
+        return $message;
+    }
+
+    /**
+     * @param Blame $blame
+     */
+    public function setBlame(Blame $blame)
+    {
+        $this->blame = $blame;
+    }
+
+    /**
+     * @return Blame
+     */
+    public function getBlame()
+    {
+        return $this->blame;
+    }
+
+    /**
+     * @param string $message
+     * @return string
+     */
+    protected function translateTokens($message)
+    {
+        static $translateTokens = array(
+            'T_FILE' => '__FILE__',
+            'T_FUNC_C' => '__FUNCTION__',
+            'T_HALT_COMPILER' => '__halt_compiler()',
+            'T_INC' => '++',
+            'T_IS_EQUAL' => '==',
+            'T_IS_GREATER_OR_EQUAL' => '>=',
+            'T_IS_IDENTICAL' => '===',
+            'T_IS_NOT_IDENTICAL' => '!==',
+            'T_IS_SMALLER_OR_EQUAL' => '<=',
+            'T_LINE' => '__LINE__',
+            'T_METHOD_C' => '__METHOD__',
+            'T_MINUS_EQUAL' => '-=',
+            'T_MOD_EQUAL' => '%=',
+            'T_MUL_EQUAL' => '*=',
+            'T_NS_C' => '__NAMESPACE__',
+            'T_NS_SEPARATOR' => '\\',
+            'T_OBJECT_OPERATOR' => '->',
+            'T_OR_EQUAL' => '|=',
+            'T_PAAMAYIM_NEKUDOTAYIM' => '::',
+            'T_PLUS_EQUAL' => '+=',
+            'T_SL' => '<<',
+            'T_SL_EQUAL' => '<<=',
+            'T_SR' => '>>',
+            'T_SR_EQUAL' => '>>=',
+            'T_START_HEREDOC' => '<<<',
+            'T_XOR_EQUAL' => '^=',
+            'T_ECHO' => 'echo'
+        );
+
+        return preg_replace_callback('~(?<!\()T_([A-Z_]*)(?!\))~', function ($matches) use ($translateTokens) {
+            list($tokenName) = $matches;
+            if (isset($translateTokens[$tokenName])) {
+                $operator = $translateTokens[$tokenName];
+                return "$operator ($tokenName)";
+            }
+
+            return $tokenName;
+        }, $message);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.4.0)<br/>
+     * Specify data which should be serialized to JSON
+     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
+     * @return mixed data which can be serialized by <b>json_encode</b>,
+     * which is a value of any type other than a resource.
+     */
+    public function jsonSerialize()
+    {
+        return array(
+            'type' => 'syntaxError',
+            'file' => $this->getFilePath(),
+            'line' => $this->getLine(),
+            'message' => $this->getMessage(),
+            'normalizeMessage' => $this->getNormalizedMessage(),
+            'blame' => $this->blame,
+        );
+    }
+}
\ No newline at end of file
diff --git a/php-parallel-lint/php-parallel-lint/src/ErrorFormatter.php b/php-parallel-lint/php-parallel-lint/src/ErrorFormatter.php
new file mode 100644
index 0000000..ff8757b
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/ErrorFormatter.php
@@ -0,0 +1,141 @@
+<?php
+namespace JakubOnderka\PhpParallelLint;
+
+use JakubOnderka\PhpConsoleColor\ConsoleColor as OldConsoleColor;
+use JakubOnderka\PhpConsoleHighlighter\Highlighter as OldHighlighter;
+use PHP_Parallel_Lint\PhpConsoleColor\ConsoleColor;
+use PHP_Parallel_Lint\PhpConsoleHighlighter\Highlighter;
+
+class ErrorFormatter
+{
+    /** @var string */
+    private $useColors;
+
+    /** @var bool */
+    private $forceColors;
+
+    /** @var bool */
+    private $translateTokens;
+
+    public function __construct($useColors = Settings::AUTODETECT, $translateTokens = false, $forceColors = false)
+    {
+        $this->useColors = $useColors;
+        $this->forceColors = $forceColors;
+        $this->translateTokens = $translateTokens;
+    }
+
+    /**
+     * @param Error $error
+     * @return string
+     */
+    public function format(Error $error)
+    {
+        if ($error instanceof SyntaxError) {
+            return $this->formatSyntaxErrorMessage($error);
+        } else {
+            if ($error->getMessage()) {
+                return $error->getMessage();
+            } else {
+                return "Unknown error for file '{$error->getFilePath()}'.";
+            }
+        }
+    }
+
+    /**
+     * @param SyntaxError $error
+     * @param bool $withCodeSnipped
+     * @return string
+     */
+    public function formatSyntaxErrorMessage(SyntaxError $error, $withCodeSnipped = true)
+    {
+        $string = "Parse error: {$error->getShortFilePath()}";
+
+        if ($error->getLine()) {
+            $onLine = $error->getLine();
+            $string .= ":$onLine" . PHP_EOL;
+
+            if ($withCodeSnipped) {
+                if ($this->useColors !== Settings::DISABLED) {
+                    $string .= $this->getColoredCodeSnippet($error->getFilePath(), $onLine);
+                } else {
+                    $string .= $this->getCodeSnippet($error->getFilePath(), $onLine);
+                }
+                $string = rtrim($string) . PHP_EOL;
+            }
+        }
+
+        $string .= $error->getNormalizedMessage($this->translateTokens);
+
+        if ($error->getBlame()) {
+            $blame = $error->getBlame();
+            $shortCommitHash = substr($blame->commitHash, 0, 8);
+            $dateTime = $blame->datetime->format('c');
+            $string .= PHP_EOL . "Blame {$blame->name} <{$blame->email}>, commit '$shortCommitHash' from $dateTime";
+        }
+
+        return $string;
+    }
+
+    /**
+     * @param string $filePath
+     * @param int $lineNumber
+     * @param int $linesBefore
+     * @param int $linesAfter
+     * @return string
+     */
+    protected function getCodeSnippet($filePath, $lineNumber, $linesBefore = 2, $linesAfter = 2)
+    {
+        $lines = file($filePath);
+
+        $offset = $lineNumber - $linesBefore - 1;
+        $offset = max($offset, 0);
+        $length = $linesAfter + $linesBefore + 1;
+        $lines = array_slice($lines, $offset, $length, $preserveKeys = true);
+
+        end($lines);
+        $lineStrlen = strlen(key($lines) + 1);
+
+        $snippet = '';
+        foreach ($lines as $i => $line) {
+            $snippet .= ($lineNumber === $i + 1 ? '  > ' : '    ');
+            $snippet .= str_pad($i + 1, $lineStrlen, ' ', STR_PAD_LEFT) . '| ' . rtrim($line) . PHP_EOL;
+        }
+
+        return $snippet;
+    }
+
+    /**
+     * @param string $filePath
+     * @param int $lineNumber
+     * @param int $linesBefore
+     * @param int $linesAfter
+     * @return string
+     */
+    protected function getColoredCodeSnippet($filePath, $lineNumber, $linesBefore = 2, $linesAfter = 2)
+    {
+        if (
+            class_exists('\PHP_Parallel_Lint\PhpConsoleHighlighter\Highlighter')
+            && class_exists('\PHP_Parallel_Lint\PhpConsoleColor\ConsoleColor')
+        ) {
+            // Highlighter and ConsoleColor 1.0+.
+            $colors = new ConsoleColor();
+            $colors->setForceStyle($this->forceColors);
+            $highlighter = new Highlighter($colors);
+        } else if (
+            class_exists('\JakubOnderka\PhpConsoleHighlighter\Highlighter')
+            && class_exists('\JakubOnderka\PhpConsoleColor\ConsoleColor')
+        ) {
+            // Highlighter and ConsoleColor < 1.0.
+            $colors = new OldConsoleColor();
+            $colors->setForceStyle($this->forceColors);
+            $highlighter = new OldHighlighter($colors);
+        }
+
+        if (isset($colors, $highlighter) === false) {
+            return $this->getCodeSnippet($filePath, $lineNumber, $linesBefore, $linesAfter);
+        }
+
+        $fileContent = file_get_contents($filePath);
+        return $highlighter->getCodeSnippet($fileContent, $lineNumber, $linesBefore, $linesAfter);
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Manager.php b/php-parallel-lint/php-parallel-lint/src/Manager.php
new file mode 100644
index 0000000..31d26ad
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Manager.php
@@ -0,0 +1,293 @@
+<?php
+namespace JakubOnderka\PhpParallelLint;
+
+use JakubOnderka\PhpParallelLint\Contracts\SyntaxErrorCallback;
+use JakubOnderka\PhpParallelLint\Process\GitBlameProcess;
+use JakubOnderka\PhpParallelLint\Process\PhpExecutable;
+use ReturnTypeWillChange;
+
+class Manager
+{
+    /** @var Output */
+    protected $output;
+
+    /**
+     * @param null|Settings $settings
+     * @return Result
+     * @throws Exception
+     * @throws \Exception
+     */
+    public function run($settings = null)
+    {
+        $settings = ($settings instanceof Settings) ? $settings : new Settings();
+        $output = $this->output ?: $this->getDefaultOutput($settings);
+
+        $phpExecutable = PhpExecutable::getPhpExecutable($settings->phpExecutable);
+        $olderThanPhp54 = $phpExecutable->getVersionId() < 50400; // From PHP version 5.4 are tokens translated by default
+        $translateTokens = $phpExecutable->isIsHhvmType() || $olderThanPhp54;
+
+        $output->writeHeader($phpExecutable->getVersionId(), $settings->parallelJobs, $phpExecutable->getHhvmVersion());
+
+        $files = $this->getFilesFromPaths($settings->paths, $settings->extensions, $settings->excluded);
+
+        if (empty($files)) {
+            throw new Exception('No file found to check.');
+        }
+
+        $output->setTotalFileCount(count($files));
+
+        $parallelLint = new ParallelLint($phpExecutable, $settings->parallelJobs);
+        $parallelLint->setAspTagsEnabled($settings->aspTags);
+        $parallelLint->setShortTagEnabled($settings->shortTag);
+        $parallelLint->setShowDeprecated($settings->showDeprecated);
+        $parallelLint->setSyntaxErrorCallback($this->createSyntaxErrorCallback($settings));
+
+        $parallelLint->setProcessCallback(function ($status, $file) use ($output) {
+            if ($status === ParallelLint::STATUS_OK) {
+                $output->ok();
+            } else if ($status === ParallelLint::STATUS_SKIP) {
+                $output->skip();
+            } else if ($status === ParallelLint::STATUS_ERROR) {
+                $output->error();
+            } else {
+                $output->fail();
+            }
+        });
+
+        $result = $parallelLint->lint($files);
+
+        if ($settings->blame) {
+            $this->gitBlame($result, $settings);
+        }
+
+        $output->writeResult($result, new ErrorFormatter($settings->colors, $translateTokens), $settings->ignoreFails);
+
+        return $result;
+    }
+
+    /**
+     * @param Output $output
+     */
+    public function setOutput(Output $output)
+    {
+        $this->output = $output;
+    }
+
+    /**
+     * @param Settings $settings
+     * @return Output
+     */
+    protected function getDefaultOutput(Settings $settings)
+    {
+        $writer = new ConsoleWriter;
+        switch ($settings->format) {
+            case Settings::FORMAT_JSON:
+                return new JsonOutput($writer);
+            case Settings::FORMAT_GITLAB:
+                return new GitLabOutput($writer);
+            case Settings::FORMAT_CHECKSTYLE:
+                return new CheckstyleOutput($writer);
+        }
+
+        if ($settings->colors === Settings::DISABLED) {
+            $output = new TextOutput($writer);
+        } else {
+            $output = new TextOutputColored($writer, $settings->colors);
+        }
+
+        $output->showProgress = $settings->showProgress;
+
+        return $output;
+    }
+
+    /**
+     * @param Result $result
+     * @param Settings $settings
+     * @throws Exception
+     */
+    protected function gitBlame(Result $result, Settings $settings)
+    {
+        if (!GitBlameProcess::gitExists($settings->gitExecutable)) {
+            return;
+        }
+
+        foreach ($result->getErrors() as $error) {
+            if ($error instanceof SyntaxError) {
+                $process = new GitBlameProcess($settings->gitExecutable, $error->getFilePath(), $error->getLine());
+                $process->waitForFinish();
+
+                if ($process->isSuccess()) {
+                    $blame = new Blame;
+                    $blame->name = $process->getAuthor();
+                    $blame->email = $process->getAuthorEmail();
+                    $blame->datetime = $process->getAuthorTime();
+                    $blame->commitHash = $process->getCommitHash();
+                    $blame->summary = $process->getSummary();
+
+                    $error->setBlame($blame);
+                }
+            }
+        }
+    }
+
+    /**
+     * @param array $paths
+     * @param array $extensions
+     * @param array $excluded
+     * @return array
+     * @throws NotExistsPathException
+     */
+    protected function getFilesFromPaths(array $paths, array $extensions, array $excluded = array())
+    {
+        $extensions = array_map('preg_quote', $extensions, array_fill(0, count($extensions), '`'));
+        $regex = '`\.(?:' . implode('|', $extensions) . ')$`iD';
+        $files = array();
+
+        foreach ($paths as $path) {
+            if (is_file($path)) {
+                $files[] = $path;
+            } else if (is_dir($path)) {
+                $iterator = new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS);
+                if (!empty($excluded)) {
+                    $iterator = new RecursiveDirectoryFilterIterator($iterator, $excluded);
+                }
+                $iterator = new \RecursiveIteratorIterator(
+                    $iterator,
+                    \RecursiveIteratorIterator::LEAVES_ONLY,
+                    \RecursiveIteratorIterator::CATCH_GET_CHILD
+                );
+
+                $iterator = new \RegexIterator($iterator, $regex);
+
+                /** @var \SplFileInfo[] $iterator */
+                foreach ($iterator as $directoryFile) {
+                    $files[] = (string) $directoryFile;
+                }
+            } else {
+                throw new NotExistsPathException($path);
+            }
+        }
+
+        $files = array_unique($files);
+
+        return $files;
+    }
+
+    protected function createSyntaxErrorCallback(Settings $settings)
+    {
+        if ($settings->syntaxErrorCallbackFile === null) {
+            return null;
+        }
+
+        $fullFilePath = realpath($settings->syntaxErrorCallbackFile);
+        if ($fullFilePath === false) {
+            throw new NotExistsPathException($settings->syntaxErrorCallbackFile);
+        }
+
+        require_once $fullFilePath;
+
+        $expectedClassName = basename($fullFilePath, '.php');
+        if (!class_exists($expectedClassName)) {
+            throw new NotExistsClassException($expectedClassName, $settings->syntaxErrorCallbackFile);
+        }
+
+        $callbackInstance = new $expectedClassName;
+
+        if (!($callbackInstance instanceof SyntaxErrorCallback)) {
+            throw new NotImplementCallbackException($expectedClassName);
+        }
+
+        return $callbackInstance;
+    }
+}
+
+class RecursiveDirectoryFilterIterator extends \RecursiveFilterIterator
+{
+    /** @var \RecursiveDirectoryIterator */
+    private $iterator;
+
+    /** @var array */
+    private $excluded = array();
+
+    /**
+     * @param \RecursiveDirectoryIterator $iterator
+     * @param array $excluded
+     */
+    public function __construct(\RecursiveDirectoryIterator $iterator, array $excluded)
+    {
+        parent::__construct($iterator);
+        $this->iterator = $iterator;
+        $this->excluded = array_map(array($this, 'getPathname'), $excluded);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.1.0)<br/>
+     * Check whether the current element of the iterator is acceptable
+     *
+     * @link http://php.net/manual/en/filteriterator.accept.php
+     * @return bool true if the current element is acceptable, otherwise false.
+     */
+    #[ReturnTypeWillChange]
+    public function accept()
+    {
+        $current = $this->current()->getPathname();
+        $current = $this->normalizeDirectorySeparator($current);
+
+        if ('.' . DIRECTORY_SEPARATOR !== $current[0] . $current[1]) {
+            $current = '.' . DIRECTORY_SEPARATOR . $current;
+        }
+
+        return !in_array($current, $this->excluded);
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.1.0)<br/>
+     * Check whether the inner iterator's current element has children
+     *
+     * @link http://php.net/manual/en/recursivefilteriterator.haschildren.php
+     * @return bool true if the inner iterator has children, otherwise false
+     */
+    #[ReturnTypeWillChange]
+    public function hasChildren()
+    {
+        return $this->iterator->hasChildren();
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.1.0)<br/>
+     * Return the inner iterator's children contained in a RecursiveFilterIterator
+     *
+     * @link http://php.net/manual/en/recursivefilteriterator.getchildren.php
+     * @return \RecursiveFilterIterator containing the inner iterator's children.
+     */
+    #[ReturnTypeWillChange]
+    public function getChildren()
+    {
+        return new self($this->iterator->getChildren(), $this->excluded);
+    }
+
+    /**
+     * @param string $file
+     * @return string
+     */
+    private function getPathname($file)
+    {
+        $file = $this->normalizeDirectorySeparator($file);
+
+        if ('.' . DIRECTORY_SEPARATOR !== $file[0] . $file[1]) {
+            $file = '.' . DIRECTORY_SEPARATOR . $file;
+        }
+
+        $directoryFile = new \SplFileInfo($file);
+        return $directoryFile->getPathname();
+    }
+
+    /**
+     * @param string $file
+     * @return string
+     */
+    private function normalizeDirectorySeparator($file)
+    {
+        return str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $file);
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Output.php b/php-parallel-lint/php-parallel-lint/src/Output.php
new file mode 100644
index 0000000..dc2c94a
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Output.php
@@ -0,0 +1,594 @@
+<?php
+namespace JakubOnderka\PhpParallelLint;
+
+interface Output
+{
+    public function __construct(IWriter $writer);
+
+    public function ok();
+
+    public function skip();
+
+    public function error();
+
+    public function fail();
+
+    public function setTotalFileCount($count);
+
+    public function writeHeader($phpVersion, $parallelJobs, $hhvmVersion = null);
+
+    public function writeResult(Result $result, ErrorFormatter $errorFormatter, $ignoreFails);
+}
+
+class JsonOutput implements Output
+{
+    /** @var IWriter */
+    protected $writer;
+
+    /** @var int */
+    protected $phpVersion;
+
+    /** @var int */
+    protected $parallelJobs;
+
+    /** @var string */
+    protected $hhvmVersion;
+
+    /**
+     * @param IWriter $writer
+     */
+    public function __construct(IWriter $writer)
+    {
+        $this->writer = $writer;
+    }
+
+    public function ok()
+    {
+
+    }
+
+    public function skip()
+    {
+
+    }
+
+    public function error()
+    {
+
+    }
+
+    public function fail()
+    {
+
+    }
+
+    public function setTotalFileCount($count)
+    {
+
+    }
+
+    public function writeHeader($phpVersion, $parallelJobs, $hhvmVersion = null)
+    {
+        $this->phpVersion = $phpVersion;
+        $this->parallelJobs = $parallelJobs;
+        $this->hhvmVersion = $hhvmVersion;
+    }
+
+    public function writeResult(Result $result, ErrorFormatter $errorFormatter, $ignoreFails)
+    {
+        echo json_encode(array(
+            'phpVersion' => $this->phpVersion,
+            'hhvmVersion' => $this->hhvmVersion,
+            'parallelJobs' => $this->parallelJobs,
+            'results' => $result,
+        ));
+    }
+}
+
+class GitLabOutput implements Output
+{
+    /** @var IWriter */
+    protected $writer;
+
+    /**
+     * @param IWriter $writer
+     */
+    public function __construct(IWriter $writer)
+    {
+        $this->writer = $writer;
+    }
+
+    public function ok()
+    {
+
+    }
+
+    public function skip()
+    {
+
+    }
+
+    public function error()
+    {
+
+    }
+
+    public function fail()
+    {
+
+    }
+
+    public function setTotalFileCount($count)
+    {
+
+    }
+
+    public function writeHeader($phpVersion, $parallelJobs, $hhvmVersion = null)
+    {
+
+    }
+
+    public function writeResult(Result $result, ErrorFormatter $errorFormatter, $ignoreFails)
+    {
+        $errors = array();
+        foreach ($result->getErrors() as $error) {
+            $message = $error->getMessage();
+            $line = 1;
+            if ($error instanceof SyntaxError) {
+                $line = $error->getLine();
+            }
+            $filePath = $error->getFilePath();
+            $result = array(
+                'type' => 'issue',
+                'check_name' => 'Parse error',
+                'description' => $message,
+                'categories' => 'Style',
+                'fingerprint' => md5($filePath . $message . $line),
+                'severity' => 'minor',
+                'location' => array(
+                    'path' => $filePath,
+                    'lines' => array(
+                        'begin' => $line,
+                    ),
+                ),
+            );
+            array_push($errors, $result);
+        }
+
+        $string = json_encode($errors) . PHP_EOL;
+        $this->writer->write($string);
+    }
+}
+
+class TextOutput implements Output
+{
+    const TYPE_DEFAULT = 'default',
+        TYPE_SKIP = 'skip',
+        TYPE_ERROR = 'error',
+        TYPE_FAIL = 'fail',
+        TYPE_OK = 'ok';
+
+    /** @var int */
+    public $filesPerLine = 60;
+
+    /** @var bool */
+    public $showProgress = true;
+
+    /** @var int */
+    protected $checkedFiles;
+
+    /** @var int */
+    protected $totalFileCount;
+
+    /** @var IWriter */
+    protected $writer;
+
+    /**
+     * @param IWriter $writer
+     */
+    public function __construct(IWriter $writer)
+    {
+        $this->writer = $writer;
+    }
+
+    public function ok()
+    {
+        $this->writeMark(self::TYPE_OK);
+    }
+
+    public function skip()
+    {
+        $this->writeMark(self::TYPE_SKIP);
+    }
+
+    public function error()
+    {
+        $this->writeMark(self::TYPE_ERROR);
+    }
+
+    public function fail()
+    {
+        $this->writeMark(self::TYPE_FAIL);
+    }
+
+    /**
+     * @param string $string
+     * @param string $type
+     */
+    public function write($string, $type = self::TYPE_DEFAULT)
+    {
+        $this->writer->write($string);
+    }
+
+    /**
+     * @param string|null $line
+     * @param string $type
+     */
+    public function writeLine($line = null, $type = self::TYPE_DEFAULT)
+    {
+        $this->write($line, $type);
+        $this->writeNewLine();
+    }
+
+    /**
+     * @param int $count
+     */
+    public function writeNewLine($count = 1)
+    {
+        $this->write(str_repeat(PHP_EOL, $count));
+    }
+
+    /**
+     * @param int $count
+     */
+    public function setTotalFileCount($count)
+    {
+        $this->totalFileCount = $count;
+    }
+
+    /**
+     * @param int $phpVersion
+     * @param int $parallelJobs
+     * @param string $hhvmVersion
+     */
+    public function writeHeader($phpVersion, $parallelJobs, $hhvmVersion = null)
+    {
+        $this->write("PHP {$this->phpVersionIdToString($phpVersion)} | ");
+
+        if ($hhvmVersion) {
+            $this->write("HHVM $hhvmVersion | ");
+        }
+
+        if ($parallelJobs === 1) {
+            $this->writeLine("1 job");
+        } else {
+            $this->writeLine("{$parallelJobs} parallel jobs");
+        }
+    }
+
+    /**
+     * @param Result $result
+     * @param ErrorFormatter $errorFormatter
+     * @param bool $ignoreFails
+     */
+    public function writeResult(Result $result, ErrorFormatter $errorFormatter, $ignoreFails)
+    {
+        if ($this->showProgress) {
+            if ($this->checkedFiles % $this->filesPerLine !== 0) {
+                $rest = $this->filesPerLine - ($this->checkedFiles % $this->filesPerLine);
+                $this->write(str_repeat(' ', $rest));
+                $this->writePercent();
+            }
+
+            $this->writeNewLine(2);
+        }
+
+        $testTime = round($result->getTestTime(), 1);
+        $message = "Checked {$result->getCheckedFilesCount()} files in $testTime ";
+        $message .= $testTime == 1 ? 'second' : 'seconds';
+
+        if ($result->getSkippedFilesCount() > 0) {
+            $message .= ", skipped {$result->getSkippedFilesCount()} ";
+            $message .= ($result->getSkippedFilesCount() === 1 ? 'file' : 'files');
+        }
+
+        $this->writeLine($message);
+
+        if (!$result->hasSyntaxError()) {
+            $message = "No syntax error found";
+        } else {
+            $message = "Syntax error found in {$result->getFilesWithSyntaxErrorCount()} ";
+            $message .= ($result->getFilesWithSyntaxErrorCount() === 1 ? 'file' : 'files');
+        }
+
+        if ($result->hasFilesWithFail()) {
+            $message .= ", failed to check {$result->getFilesWithFailCount()} ";
+            $message .= ($result->getFilesWithFailCount() === 1 ? 'file' : 'files');
+
+            if ($ignoreFails) {
+                $message .= ' (ignored)';
+            }
+        }
+
+        $hasError = $ignoreFails ? $result->hasSyntaxError() : $result->hasError();
+        $this->writeLine($message, $hasError ? self::TYPE_ERROR : self::TYPE_OK);
+
+        if ($result->hasError()) {
+            $this->writeNewLine();
+            foreach ($result->getErrors() as $error) {
+                $this->writeLine(str_repeat('-', 60));
+                $this->writeLine($errorFormatter->format($error));
+            }
+        }
+    }
+
+    protected function writeMark($type)
+    {
+        ++$this->checkedFiles;
+
+        if ($this->showProgress) {
+            if ($type === self::TYPE_OK) {
+                $this->writer->write('.');
+
+            } else if ($type === self::TYPE_SKIP) {
+                $this->write('S', self::TYPE_SKIP);
+
+            } else if ($type === self::TYPE_ERROR) {
+                $this->write('X', self::TYPE_ERROR);
+
+            } else if ($type === self::TYPE_FAIL) {
+                $this->writer->write('-');
+            }
+
+            if ($this->checkedFiles % $this->filesPerLine === 0) {
+                $this->writePercent();
+            }
+        }
+    }
+
+    protected function writePercent()
+    {
+        $percent = $this->stringWidth(floor($this->checkedFiles / $this->totalFileCount * 100), 3);
+        $current = $this->stringWidth($this->checkedFiles, strlen($this->totalFileCount));
+        $this->writeLine(" $current/$this->totalFileCount ($percent%)");
+    }
+
+    /**
+     * @param string $input
+     * @param int $width
+     * @return string
+     */
+    protected function stringWidth($input, $width = 3)
+    {
+        $multiplier = $width - strlen($input);
+        return str_repeat(' ', $multiplier > 0 ? $multiplier : 0) . $input;
+    }
+
+    /**
+     * @param int $phpVersionId
+     * @return string
+     */
+    protected function phpVersionIdToString($phpVersionId)
+    {
+        $releaseVersion = (int) substr($phpVersionId, -2, 2);
+        $minorVersion = (int) substr($phpVersionId, -4, 2);
+        $majorVersion = (int) substr($phpVersionId, 0, strlen($phpVersionId) - 4);
+
+        return "$majorVersion.$minorVersion.$releaseVersion";
+    }
+}
+
+class CheckstyleOutput implements Output
+{
+    private $writer;
+
+    public function __construct(IWriter $writer)
+    {
+        $this->writer = $writer;
+    }
+
+    public function ok()
+    {
+    }
+
+    public function skip()
+    {
+    }
+
+    public function error()
+    {
+    }
+
+    public function fail()
+    {
+    }
+
+    public function setTotalFileCount($count)
+    {
+    }
+
+    public function writeHeader($phpVersion, $parallelJobs, $hhvmVersion = null)
+    {
+        $this->writer->write('<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL);
+    }
+
+    public function writeResult(Result $result, ErrorFormatter $errorFormatter, $ignoreFails)
+    {
+        $this->writer->write('<checkstyle>' . PHP_EOL);
+        $errors = array();
+
+        foreach ($result->getErrors() as $error) {
+            $message = $error->getMessage();
+            if ($error instanceof SyntaxError) {
+                $line = $error->getLine();
+                $source = "Syntax Error";
+            } else {
+                $line = 1;
+                $source = "Linter Error";
+            }
+
+            $errors[$error->getShortFilePath()][] = array(
+                'message' => $message,
+                'line' => $line,
+                'source' => $source
+            );
+        }
+
+        foreach ($errors as $file => $fileErrors) {
+            $this->writer->write(sprintf('    <file name="%s">', $file) . PHP_EOL);
+            foreach ($fileErrors as $fileError) {
+                $this->writer->write(
+                    sprintf(
+                        '        <error line="%d" severity="ERROR" message="%s" source="%s" />',
+                        $fileError['line'],
+                        htmlspecialchars($fileError['message'], ENT_COMPAT, 'UTF-8'),
+                        $fileError['source']
+                    ) .
+                    PHP_EOL
+                );
+            }
+            $this->writer->write('    </file>' . PHP_EOL);
+        }
+
+        $this->writer->write('</checkstyle>' . PHP_EOL);
+    }
+}
+
+class TextOutputColored extends TextOutput
+{
+    /** @var \PHP_Parallel_Lint\PhpConsoleColor\ConsoleColor|\JakubOnderka\PhpConsoleColor\ConsoleColor */
+    private $colors;
+
+    public function __construct(IWriter $writer, $colors = Settings::AUTODETECT)
+    {
+        parent::__construct($writer);
+
+        if (class_exists('\PHP_Parallel_Lint\PhpConsoleColor\ConsoleColor')) {
+            $this->colors = new \PHP_Parallel_Lint\PhpConsoleColor\ConsoleColor();
+            $this->colors->setForceStyle($colors === Settings::FORCED);
+        } else if (class_exists('\JakubOnderka\PhpConsoleColor\ConsoleColor')) {
+            $this->colors = new \JakubOnderka\PhpConsoleColor\ConsoleColor();
+            $this->colors->setForceStyle($colors === Settings::FORCED);
+        }
+    }
+
+    /**
+     * @param string $string
+     * @param string $type
+     * @throws \PHP_Parallel_Lint\PhpConsoleColor\InvalidStyleException|\JakubOnderka\PhpConsoleColor\InvalidStyleException
+     */
+    public function write($string, $type = self::TYPE_DEFAULT)
+    {
+        if (
+            !$this->colors instanceof \PHP_Parallel_Lint\PhpConsoleColor\ConsoleColor
+            && !$this->colors instanceof \JakubOnderka\PhpConsoleColor\ConsoleColor
+        ) {
+            parent::write($string, $type);
+        } else {
+            switch ($type) {
+                case self::TYPE_OK:
+                    parent::write($this->colors->apply('bg_green', $string));
+                    break;
+
+                case self::TYPE_SKIP:
+                    parent::write($this->colors->apply('bg_yellow', $string));
+                    break;
+
+                case self::TYPE_ERROR:
+                    parent::write($this->colors->apply('bg_red', $string));
+                    break;
+
+                default:
+                    parent::write($string);
+            }
+        }
+    }
+}
+
+interface IWriter
+{
+    /**
+     * @param string $string
+     */
+    public function write($string);
+}
+
+class NullWriter implements IWriter
+{
+    /**
+     * @param string $string
+     */
+    public function write($string)
+    {
+
+    }
+}
+
+class ConsoleWriter implements IWriter
+{
+    /**
+     * @param string $string
+     */
+    public function write($string)
+    {
+        echo $string;
+    }
+}
+
+class FileWriter implements IWriter
+{
+    /** @var string */
+    protected $logFile;
+
+    /** @var string */
+    protected $buffer;
+
+    public function __construct($logFile)
+    {
+        $this->logFile = $logFile;
+    }
+
+    public function write($string)
+    {
+        $this->buffer .= $string;
+    }
+
+    public function __destruct()
+    {
+        file_put_contents($this->logFile, $this->buffer);
+    }
+}
+
+class MultipleWriter implements IWriter
+{
+    /** @var IWriter[] */
+    protected $writers;
+
+    /**
+     * @param IWriter[] $writers
+     */
+    public function __construct(array $writers)
+    {
+        foreach ($writers as $writer) {
+            $this->addWriter($writer);
+        }
+    }
+
+    /**
+     * @param IWriter $writer
+     */
+    public function addWriter(IWriter $writer)
+    {
+        $this->writers[] = $writer;
+    }
+
+    /**
+     * @param $string
+     */
+    public function write($string)
+    {
+        foreach ($this->writers as $writer) {
+            $writer->write($string);
+        }
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/ParallelLint.php b/php-parallel-lint/php-parallel-lint/src/ParallelLint.php
new file mode 100644
index 0000000..b1825f3
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/ParallelLint.php
@@ -0,0 +1,286 @@
+<?php
+namespace JakubOnderka\PhpParallelLint;
+
+use JakubOnderka\PhpParallelLint\Contracts\SyntaxErrorCallback;
+use JakubOnderka\PhpParallelLint\Process\LintProcess;
+use JakubOnderka\PhpParallelLint\Process\PhpExecutable;
+use JakubOnderka\PhpParallelLint\Process\SkipLintProcess;
+
+class ParallelLint
+{
+    const STATUS_OK = 'ok',
+        STATUS_SKIP = 'skip',
+        STATUS_FAIL = 'fail',
+        STATUS_ERROR = 'error';
+
+    /** @var int */
+    private $parallelJobs;
+
+    /** @var PhpExecutable */
+    private $phpExecutable;
+
+    /** @var bool */
+    private $aspTagsEnabled = false;
+
+    /** @var bool */
+    private $shortTagEnabled = false;
+
+    /** @var callable */
+    private $processCallback;
+
+    /** @var bool */
+    private $showDeprecated = false;
+
+    /** @var SyntaxErrorCallback|null */
+    private $syntaxErrorCallback = null;
+
+    public function __construct(PhpExecutable $phpExecutable, $parallelJobs = 10)
+    {
+        $this->phpExecutable = $phpExecutable;
+        $this->parallelJobs = $parallelJobs;
+    }
+
+    /**
+     * @param array $files
+     * @return Result
+     * @throws \Exception
+     */
+    public function lint(array $files)
+    {
+        $startTime = microtime(true);
+
+        $skipLintProcess = new SkipLintProcess($this->phpExecutable, $files);
+
+        $processCallback = is_callable($this->processCallback) ? $this->processCallback : function () {
+        };
+
+        /**
+         * @var LintProcess[] $running
+         * @var LintProcess[] $waiting
+         */
+        $errors = $running = $waiting = array();
+        $skippedFiles = $checkedFiles = array();
+
+        while ($files || $running) {
+            for ($i = count($running); $files && $i < $this->parallelJobs; $i++) {
+                $file = array_shift($files);
+
+                if ($skipLintProcess->isSkipped($file) === true) {
+                    $skippedFiles[] = $file;
+                    $processCallback(self::STATUS_SKIP, $file);
+                } else {
+                    $running[$file] = new LintProcess(
+                        $this->phpExecutable,
+                        $file,
+                        $this->aspTagsEnabled,
+                        $this->shortTagEnabled,
+                        $this->showDeprecated
+                    );
+                }
+            }
+
+            $skipLintProcess->getChunk();
+            usleep(100);
+
+            foreach ($running as $file => $process) {
+                if ($process->isFinished()) {
+                    unset($running[$file]);
+
+                    $skipStatus = $skipLintProcess->isSkipped($file);
+                    if ($skipStatus === null) {
+                        $waiting[$file] = $process;
+
+                    } else if ($skipStatus === true) {
+                        $skippedFiles[] = $file;
+                        $processCallback(self::STATUS_SKIP, $file);
+
+                    } else if ($process->containsError()) {
+                        $checkedFiles[] = $file;
+                        $errors[] = $this->triggerSyntaxErrorCallback(new SyntaxError($file, $process->getSyntaxError()));
+                        $processCallback(self::STATUS_ERROR, $file);
+
+                    } else if ($process->isSuccess()) {
+                        $checkedFiles[] = $file;
+                        $processCallback(self::STATUS_OK, $file);
+
+
+                    } else {
+                        $errors[] = new Error($file, $process->getOutput());
+                        $processCallback(self::STATUS_FAIL, $file);
+                    }
+                }
+            }
+        }
+
+        if (!empty($waiting)) {
+            $skipLintProcess->waitForFinish();
+
+            if ($skipLintProcess->isFail()) {
+                $message = "Error in skip-linting.php process\nError output: {$skipLintProcess->getErrorOutput()}";
+                throw new \Exception($message);
+            }
+
+            foreach ($waiting as $file => $process) {
+                $skipStatus = $skipLintProcess->isSkipped($file);
+                if ($skipStatus === null) {
+                    throw new \Exception("File $file has empty skip status. Please contact the author of PHP Parallel Lint.");
+
+                } else if ($skipStatus === true) {
+                    $skippedFiles[] = $file;
+                    $processCallback(self::STATUS_SKIP, $file);
+
+                } else if ($process->isSuccess()) {
+                    $checkedFiles[] = $file;
+                    $processCallback(self::STATUS_OK, $file);
+
+                } else if ($process->containsError()) {
+                    $checkedFiles[] = $file;
+                    $errors[] = $this->triggerSyntaxErrorCallback(new SyntaxError($file, $process->getSyntaxError()));
+                    $processCallback(self::STATUS_ERROR, $file);
+
+                } else {
+                    $errors[] = new Error($file, $process->getOutput());
+                    $processCallback(self::STATUS_FAIL, $file);
+                }
+            }
+        }
+
+        $testTime = microtime(true) - $startTime;
+
+        return new Result($errors, $checkedFiles, $skippedFiles, $testTime);
+    }
+
+    /**
+     * @return int
+     */
+    public function getParallelJobs()
+    {
+        return $this->parallelJobs;
+    }
+
+    /**
+     * @param int $parallelJobs
+     * @return ParallelLint
+     */
+    public function setParallelJobs($parallelJobs)
+    {
+        $this->parallelJobs = $parallelJobs;
+
+        return $this;
+    }
+
+    /**
+     * @return string
+     */
+    public function getPhpExecutable()
+    {
+        return $this->phpExecutable;
+    }
+
+    /**
+     * @param string $phpExecutable
+     * @return ParallelLint
+     */
+    public function setPhpExecutable($phpExecutable)
+    {
+        $this->phpExecutable = $phpExecutable;
+
+        return $this;
+    }
+
+    /**
+     * @return callable
+     */
+    public function getProcessCallback()
+    {
+        return $this->processCallback;
+    }
+
+    /**
+     * @param callable $processCallback
+     * @return ParallelLint
+     */
+    public function setProcessCallback($processCallback)
+    {
+        $this->processCallback = $processCallback;
+
+        return $this;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isAspTagsEnabled()
+    {
+        return $this->aspTagsEnabled;
+    }
+
+    /**
+     * @param boolean $aspTagsEnabled
+     * @return ParallelLint
+     */
+    public function setAspTagsEnabled($aspTagsEnabled)
+    {
+        $this->aspTagsEnabled = $aspTagsEnabled;
+
+        return $this;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isShortTagEnabled()
+    {
+        return $this->shortTagEnabled;
+    }
+
+    /**
+     * @param boolean $shortTagEnabled
+     * @return ParallelLint
+     */
+    public function setShortTagEnabled($shortTagEnabled)
+    {
+        $this->shortTagEnabled = $shortTagEnabled;
+
+        return $this;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isShowDeprecated()
+    {
+        return $this->showDeprecated;
+    }
+
+    /**
+     * @param $showDeprecated
+     * @return ParallelLint
+     */
+    public function setShowDeprecated($showDeprecated)
+    {
+        $this->showDeprecated = $showDeprecated;
+
+        return $this;
+    }
+
+    public function triggerSyntaxErrorCallback($syntaxError)
+    {
+        if ($this->syntaxErrorCallback === null) {
+            return $syntaxError;
+        }
+
+        return $this->syntaxErrorCallback->errorFound($syntaxError);
+    }
+
+    /**
+     * @param SyntaxErrorCallback|null $syntaxErrorCallback
+     * @return ParallelLint
+     */
+    public function setSyntaxErrorCallback($syntaxErrorCallback)
+    {
+        $this->syntaxErrorCallback = $syntaxErrorCallback;
+
+        return $this;
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Process/GitBlameProcess.php b/php-parallel-lint/php-parallel-lint/src/Process/GitBlameProcess.php
new file mode 100644
index 0000000..2d675b8
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Process/GitBlameProcess.php
@@ -0,0 +1,147 @@
+<?php
+namespace JakubOnderka\PhpParallelLint\Process;
+
+use JakubOnderka\PhpParallelLint\RunTimeException;
+
+class GitBlameProcess extends Process
+{
+    /**
+     * @param string $gitExecutable
+     * @param string $file
+     * @param int $line
+     * @throws RunTimeException
+     */
+    public function __construct($gitExecutable, $file, $line)
+    {
+        $arguments = array('blame', '-p', '-L', "$line,+1", $file);
+        parent::__construct($gitExecutable, $arguments);
+    }
+
+    /**
+     * @return bool
+     * @throws RunTimeException
+     */
+    public function isSuccess()
+    {
+        return $this->getStatusCode() === 0;
+    }
+
+    /**
+     * @return string
+     * @throws RunTimeException
+     */
+    public function getAuthor()
+    {
+        if (!$this->isSuccess()) {
+            throw new RunTimeException("Author can only be retrieved for successful process output.");
+        }
+
+        $output = $this->getOutput();
+        preg_match('~^author (.*)~m', $output, $matches);
+        return $matches[1];
+    }
+
+    /**
+     * @return string
+     * @throws RunTimeException
+     */
+    public function getAuthorEmail()
+    {
+        if (!$this->isSuccess()) {
+            throw new RunTimeException("Author e-mail can only be retrieved for successful process output.");
+        }
+
+        $output = $this->getOutput();
+        preg_match('~^author-mail <(.*)>~m', $output, $matches);
+        return $matches[1];
+    }
+
+    /**
+     * @return \DateTime
+     * @throws RunTimeException
+     */
+    public function getAuthorTime()
+    {
+        if (!$this->isSuccess()) {
+            throw new RunTimeException("Author time can only be retrieved for successful process output.");
+        }
+
+        $output = $this->getOutput();
+
+        preg_match('~^author-time (.*)~m', $output, $matches);
+        $time = $matches[1];
+
+        preg_match('~^author-tz (.*)~m', $output, $matches);
+        $zone = $matches[1];
+
+        return $this->getDateTime($time, $zone);
+    }
+
+    /**
+     * @return string
+     * @throws RunTimeException
+     */
+    public function getCommitHash()
+    {
+        if (!$this->isSuccess()) {
+            throw new RunTimeException("Commit hash can only be retrieved for successful process output.");
+        }
+
+        return substr($this->getOutput(), 0, strpos($this->getOutput(), ' '));
+    }
+
+    /**
+     * @return string
+     * @throws RunTimeException
+     */
+    public function getSummary()
+    {
+        if (!$this->isSuccess()) {
+            throw new RunTimeException("Commit summary can only be retrieved for successful process output.");
+        }
+
+        $output = $this->getOutput();
+        preg_match('~^summary (.*)~m', $output, $matches);
+        return $matches[1];
+    }
+
+    /**
+     * @param string $gitExecutable
+     * @return bool
+     * @throws RunTimeException
+     */
+    public static function gitExists($gitExecutable)
+    {
+        $process = new Process($gitExecutable, array('--version'));
+        $process->waitForFinish();
+        return $process->getStatusCode() === 0;
+    }
+
+    /**
+     * This harakiri method is required to correct support time zone in PHP 5.4
+     *
+     * @param int $time
+     * @param string $zone
+     * @return \DateTime
+     * @throws \Exception
+     */
+    protected function getDateTime($time, $zone)
+    {
+        $utcTimeZone = new \DateTimeZone('UTC');
+        $datetime = \DateTime::createFromFormat('U', $time, $utcTimeZone);
+
+        $way = substr($zone, 0, 1);
+        $hours = (int) substr($zone, 1, 2);
+        $minutes = (int) substr($zone, 3, 2);
+
+        $interval = new \DateInterval("PT{$hours}H{$minutes}M");
+
+        if ($way === '+') {
+            $datetime->add($interval);
+        } else {
+            $datetime->sub($interval);
+        }
+
+        return new \DateTime($datetime->format('Y-m-d\TH:i:s') . $zone, $utcTimeZone);
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Process/LintProcess.php b/php-parallel-lint/php-parallel-lint/src/Process/LintProcess.php
new file mode 100644
index 0000000..e2e6b2d
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Process/LintProcess.php
@@ -0,0 +1,137 @@
+<?php
+namespace JakubOnderka\PhpParallelLint\Process;
+
+use JakubOnderka\PhpParallelLint\RunTimeException;
+
+class LintProcess extends PhpProcess
+{
+    const FATAL_ERROR = 'Fatal error';
+    const PARSE_ERROR = 'Parse error';
+    const DEPRECATED_ERROR = 'Deprecated:';
+
+    /**
+     * @var bool
+     */
+    private $showDeprecatedErrors;
+
+    /**
+     * @param PhpExecutable $phpExecutable
+     * @param string $fileToCheck Path to file to check
+     * @param bool $aspTags
+     * @param bool $shortTag
+     * @param bool $deprecated
+     * @throws RunTimeException
+     */
+    public function __construct(PhpExecutable $phpExecutable, $fileToCheck, $aspTags = false, $shortTag = false, $deprecated = false)
+    {
+        if (empty($fileToCheck)) {
+            throw new \InvalidArgumentException("File to check must be set.");
+        }
+
+        $parameters = array(
+            '-d asp_tags=' . ($aspTags ? 'On' : 'Off'),
+            '-d short_open_tag=' . ($shortTag ? 'On' : 'Off'),
+            '-d error_reporting=E_ALL',
+            '-n',
+            '-l',
+            $fileToCheck,
+        );
+
+        $this->showDeprecatedErrors = $deprecated;
+        parent::__construct($phpExecutable, $parameters);
+    }
+
+    /**
+     * @return bool
+     * @throws
+     */
+    public function containsError()
+    {
+        return $this->containsParserError($this->getOutput()) ||
+            $this->containsFatalError($this->getOutput()) ||
+            $this->containsDeprecatedError($this->getOutput());
+    }
+
+    /**
+     * @return string
+     * @throws RunTimeException
+     */
+    public function getSyntaxError()
+    {
+        if ($this->containsError()) {
+            // Look for fatal errors first
+            foreach (explode("\n", $this->getOutput()) as $line) {
+                if ($this->containsFatalError($line)) {
+                    return $line;
+                }
+            }
+
+            // Look for parser errors second
+            foreach (explode("\n", $this->getOutput()) as $line) {
+                if ($this->containsParserError($line)) {
+                    return $line;
+                }
+            }
+
+            // Look for deprecated errors third
+            foreach (explode("\n", $this->getOutput()) as $line) {
+                if ($this->containsDeprecatedError($line)) {
+                    return $line;
+                }
+            }
+
+            throw new RunTimeException("The output '{$this->getOutput()}' does not contain Parse or Syntax errors");
+        }
+
+        return false;
+    }
+
+    /**
+     * @return bool
+     * @throws RunTimeException
+     */
+    public function isFail()
+    {
+        return defined('PHP_WINDOWS_VERSION_MAJOR') ? $this->getStatusCode() === 1 : parent::isFail();
+    }
+
+    /**
+     * @return bool
+     * @throws RunTimeException
+     */
+    public function isSuccess()
+    {
+        return $this->getStatusCode() === 0;
+    }
+
+    /**
+     * @param string $string
+     * @return bool
+     */
+    private function containsParserError($string)
+    {
+        return strpos($string, self::PARSE_ERROR) !== false;
+    }
+
+    /**
+     * @param string $string
+     * @return bool
+     */
+    private function containsFatalError($string)
+    {
+        return strpos($string, self::FATAL_ERROR) !== false;
+    }
+
+    /**
+     * @param string $string
+     * @return bool
+     */
+    private function containsDeprecatedError($string)
+    {
+        if ($this->showDeprecatedErrors === false) {
+            return false;
+        }
+
+        return strpos($string, self::DEPRECATED_ERROR) !== false;
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Process/PhpExecutable.php b/php-parallel-lint/php-parallel-lint/src/Process/PhpExecutable.php
new file mode 100644
index 0000000..001b1e9
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Process/PhpExecutable.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace JakubOnderka\PhpParallelLint\Process;
+
+use JakubOnderka\PhpParallelLint\RunTimeException;
+
+class PhpExecutable
+{
+    /** @var string */
+    private $path;
+
+    /**
+     * Version as PHP_VERSION_ID constant
+     * @var int
+     */
+    private $versionId;
+
+    /** @var string */
+    private $hhvmVersion;
+
+    /** @var bool */
+    private $isHhvmType;
+
+    /**
+     * @param string $path
+     * @param int $versionId
+     * @param string $hhvmVersion
+     * @param bool $isHhvmType
+     */
+    public function __construct($path, $versionId, $hhvmVersion, $isHhvmType)
+    {
+        $this->path = $path;
+        $this->versionId = $versionId;
+        $this->hhvmVersion = $hhvmVersion;
+        $this->isHhvmType = $isHhvmType;
+    }
+
+    /**
+     * @return string
+     */
+    public function getHhvmVersion()
+    {
+        return $this->hhvmVersion;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isIsHhvmType()
+    {
+        return $this->isHhvmType;
+    }
+
+    /**
+     * @return string
+     */
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    /**
+     * @return int
+     */
+    public function getVersionId()
+    {
+        return $this->versionId;
+    }
+
+    /**
+     * @param string $phpExecutable
+     * @return PhpExecutable
+     * @throws \Exception
+     */
+    public static function getPhpExecutable($phpExecutable)
+    {
+        $codeToExecute = <<<PHP
+echo 'PHP;', PHP_VERSION_ID, ';', defined('HPHP_VERSION') ? HPHP_VERSION : null;
+PHP;
+
+        $process = new Process($phpExecutable, array('-n', '-r', $codeToExecute));
+        $process->waitForFinish();
+
+        try {
+            if ($process->getStatusCode() !== 0 && $process->getStatusCode() !== 255) {
+                throw new RunTimeException("Unable to execute '{$phpExecutable}'.");
+            }
+
+            return self::getPhpExecutableFromOutput($phpExecutable, $process->getOutput());
+
+        } catch (RunTimeException $e) {
+            // Try HHVM type
+            $process = new Process($phpExecutable, array('--php', '-r', $codeToExecute));
+            $process->waitForFinish();
+
+            if ($process->getStatusCode() !== 0 && $process->getStatusCode() !== 255) {
+                throw new RunTimeException("Unable to execute '{$phpExecutable}'.");
+            }
+
+            return self::getPhpExecutableFromOutput($phpExecutable, $process->getOutput(), $isHhvmType = true);
+        }
+    }
+
+    /**
+     * @param string $phpExecutable
+     * @param string $output
+     * @param bool $isHhvmType
+     * @return PhpExecutable
+     * @throws RunTimeException
+     */
+    private static function getPhpExecutableFromOutput($phpExecutable, $output, $isHhvmType = false)
+    {
+        $parts = explode(';', $output);
+
+        if ($parts[0] !== 'PHP' || !preg_match('~([0-9]+)~', $parts[1], $matches)) {
+            throw new RunTimeException("'{$phpExecutable}' is not valid PHP binary.");
+        }
+
+        $hhvmVersion = isset($parts[2]) ? $parts[2] : false;
+
+        return new PhpExecutable(
+            $phpExecutable,
+            intval($matches[1]),
+            $hhvmVersion,
+            $isHhvmType
+        );
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Process/PhpProcess.php b/php-parallel-lint/php-parallel-lint/src/Process/PhpProcess.php
new file mode 100644
index 0000000..0df293a
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Process/PhpProcess.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace JakubOnderka\PhpParallelLint\Process;
+
+class PhpProcess extends Process
+{
+    /**
+     * @param PhpExecutable $phpExecutable
+     * @param array $parameters
+     * @param string|null $stdIn
+     * @throws \JakubOnderka\PhpParallelLint\RunTimeException
+     */
+    public function __construct(PhpExecutable $phpExecutable, array $parameters = array(), $stdIn = null)
+    {
+        $constructedParameters = $this->constructParameters($parameters, $phpExecutable->isIsHhvmType());
+        parent::__construct($phpExecutable->getPath(), $constructedParameters, $stdIn);
+    }
+
+    /**
+     * @param array $parameters
+     * @param bool $isHhvm
+     * @return array
+     */
+    private function constructParameters(array $parameters, $isHhvm)
+    {
+        // Always ignore PHP startup errors ("Unable to load library...") in sub-processes.
+        array_unshift($parameters, '-d display_startup_errors=0');
+
+        if ($isHhvm) {
+            array_unshift($parameters, '-php');
+        }
+
+        return $parameters;
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Process/Process.php b/php-parallel-lint/php-parallel-lint/src/Process/Process.php
new file mode 100644
index 0000000..190511e
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Process/Process.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace JakubOnderka\PhpParallelLint\Process;
+
+use JakubOnderka\PhpParallelLint\RunTimeException;
+
+class Process
+{
+    const STDIN = 0,
+        STDOUT = 1,
+        STDERR = 2;
+
+    const READ = 'r',
+        WRITE = 'w';
+
+    /** @var resource */
+    protected $process;
+
+    /** @var resource */
+    protected $stdout;
+
+    /** @var resource */
+    protected $stderr;
+
+    /** @var string */
+    private $output;
+
+    /** @var string */
+    private $errorOutput;
+
+    /** @var int */
+    private $statusCode;
+
+    /**
+     * @param string $executable
+     * @param string[] $arguments
+     * @param string $stdInInput
+     * @throws RunTimeException
+     */
+    public function __construct($executable, array $arguments = array(), $stdInInput = null)
+    {
+        $descriptors = array(
+            self::STDIN  => array('pipe', self::READ),
+            self::STDOUT => array('pipe', self::WRITE),
+            self::STDERR => array('pipe', self::WRITE),
+        );
+
+        $cmdLine = $executable . ' ' . implode(' ', array_map('escapeshellarg', $arguments));
+        $this->process = proc_open($cmdLine, $descriptors, $pipes, null, null, array('bypass_shell' => true));
+
+        if ($this->process === false || $this->process === null) {
+            throw new RunTimeException("Cannot create new process $cmdLine");
+        }
+
+        list($stdin, $this->stdout, $this->stderr) = $pipes;
+
+        if ($stdInInput) {
+            fwrite($stdin, $stdInInput);
+        }
+
+        fclose($stdin);
+    }
+
+    /**
+     * @return bool
+     */
+    public function isFinished()
+    {
+        if ($this->statusCode !== null) {
+            return true;
+        }
+
+        $status = proc_get_status($this->process);
+
+        if ($status['running']) {
+            return false;
+        } else if ($this->statusCode === null) {
+            $this->statusCode = (int) $status['exitcode'];
+        }
+
+        // Process outputs
+        $this->output = stream_get_contents($this->stdout);
+        fclose($this->stdout);
+
+        $this->errorOutput = stream_get_contents($this->stderr);
+        fclose($this->stderr);
+
+        $statusCode = proc_close($this->process);
+
+        if ($this->statusCode === null) {
+            $this->statusCode = $statusCode;
+        }
+
+        $this->process = null;
+
+        return true;
+    }
+
+    public function waitForFinish()
+    {
+        while (!$this->isFinished()) {
+            usleep(100);
+        }
+    }
+
+    /**
+     * @return string
+     * @throws RunTimeException
+     */
+    public function getOutput()
+    {
+        if (!$this->isFinished()) {
+            throw new RunTimeException("Cannot get output for running process");
+        }
+
+        return $this->output;
+    }
+
+    /**
+     * @return string
+     * @throws RunTimeException
+     */
+    public function getErrorOutput()
+    {
+        if (!$this->isFinished()) {
+            throw new RunTimeException("Cannot get error output for running process");
+        }
+
+        return $this->errorOutput;
+    }
+
+    /**
+     * @return int
+     * @throws RunTimeException
+     */
+    public function getStatusCode()
+    {
+        if (!$this->isFinished()) {
+            throw new RunTimeException("Cannot get status code for running process");
+        }
+
+        return $this->statusCode;
+    }
+
+    /**
+     * @return bool
+     * @throws RunTimeException
+     */
+    public function isFail()
+    {
+        return $this->getStatusCode() === 1;
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Process/SkipLintProcess.php b/php-parallel-lint/php-parallel-lint/src/Process/SkipLintProcess.php
new file mode 100644
index 0000000..52f4e76
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Process/SkipLintProcess.php
@@ -0,0 +1,91 @@
+<?php
+namespace JakubOnderka\PhpParallelLint\Process;
+
+use JakubOnderka\PhpParallelLint\RunTimeException;
+
+class SkipLintProcess extends PhpProcess
+{
+    /** @var array */
+    private $skipped = array();
+
+    /** @var bool */
+    private $done = false;
+
+    /** @var string */
+    private $endLastChunk = '';
+
+    /**
+     * @param PhpExecutable $phpExecutable
+     * @param array $filesToCheck
+     * @throws RunTimeException
+     */
+    public function __construct(PhpExecutable $phpExecutable, array $filesToCheck)
+    {
+        $scriptPath = __DIR__ . '/../../bin/skip-linting.php';
+        $script = file_get_contents($scriptPath);
+
+        if (!$script) {
+            throw new RunTimeException("skip-linting.php script not found in '$scriptPath'.");
+        }
+
+        $script = str_replace('<?php', '', $script);
+
+        $parameters = array('-d', 'display_errors=stderr', '-r', $script);
+        parent::__construct($phpExecutable, $parameters, implode(PHP_EOL, $filesToCheck));
+    }
+
+    /**
+     * @throws RunTimeException
+     */
+    public function getChunk()
+    {
+        if (!$this->isFinished()) {
+            $this->processLines(fread($this->stdout, 8192));
+        }
+    }
+
+    /**
+     * @return bool
+     * @throws \JakubOnderka\PhpParallelLint\RunTimeException
+     */
+    public function isFinished()
+    {
+        $isFinished = parent::isFinished();
+        if ($isFinished && !$this->done) {
+            $this->done = true;
+            $output = $this->getOutput();
+            $this->processLines($output);
+        }
+
+        return $isFinished;
+    }
+
+    /**
+     * @param string $file
+     * @return bool|null
+     */
+    public function isSkipped($file)
+    {
+        if (isset($this->skipped[$file])) {
+            return $this->skipped[$file];
+        }
+
+        return null;
+    }
+
+    /**
+     * @param string $content
+     */
+    private function processLines($content)
+    {
+        if (!empty($content)) {
+            $lines = explode(PHP_EOL, $this->endLastChunk . $content);
+            $this->endLastChunk = array_pop($lines);
+            foreach ($lines as $line) {
+                $parts = explode(';', $line);
+                list($file, $status) = $parts;
+                $this->skipped[$file] = $status === '1' ? true : false;
+            }
+        }
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/Result.php b/php-parallel-lint/php-parallel-lint/src/Result.php
new file mode 100644
index 0000000..9f6a368
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Result.php
@@ -0,0 +1,171 @@
+<?php
+namespace JakubOnderka\PhpParallelLint;
+
+use ReturnTypeWillChange;
+
+class Result implements \JsonSerializable
+{
+    /** @var Error[] */
+    private $errors;
+
+    /** @var array */
+    private $checkedFiles;
+
+    /** @var array */
+    private $skippedFiles;
+
+    /** @var float */
+    private $testTime;
+
+    /**
+     * @param Error[] $errors
+     * @param array $checkedFiles
+     * @param array $skippedFiles
+     * @param float $testTime
+     */
+    public function __construct(array $errors, array $checkedFiles, array $skippedFiles, $testTime)
+    {
+        $this->errors = $errors;
+        $this->checkedFiles = $checkedFiles;
+        $this->skippedFiles = $skippedFiles;
+        $this->testTime = $testTime;
+    }
+
+    /**
+     * @return Error[]
+     */
+    public function getErrors()
+    {
+        return $this->errors;
+    }
+
+    /**
+     * @return bool
+     */
+    public function hasError()
+    {
+        return !empty($this->errors);
+    }
+
+    /**
+     * @return array
+     */
+    public function getFilesWithFail()
+    {
+        $filesWithFail = array();
+        foreach ($this->errors as $error) {
+            if (!$error instanceof SyntaxError) {
+                $filesWithFail[] = $error->getFilePath();
+            }
+        }
+
+        return $filesWithFail;
+    }
+
+    /**
+     * @return int
+     */
+    public function getFilesWithFailCount()
+    {
+        return count($this->getFilesWithFail());
+    }
+
+    /**
+     * @return bool
+     */
+    public function hasFilesWithFail()
+    {
+        return $this->getFilesWithFailCount() !== 0;
+    }
+
+    /**
+     * @return array
+     */
+    public function getCheckedFiles()
+    {
+        return $this->checkedFiles;
+    }
+
+    /**
+     * @return int
+     */
+    public function getCheckedFilesCount()
+    {
+        return count($this->checkedFiles);
+    }
+
+    /**
+     * @return array
+     */
+    public function getSkippedFiles()
+    {
+        return $this->skippedFiles;
+    }
+
+    /**
+     * @return int
+     */
+    public function getSkippedFilesCount()
+    {
+        return count($this->skippedFiles);
+    }
+
+    /**
+     * @return array
+     */
+    public function getFilesWithSyntaxError()
+    {
+        $filesWithSyntaxError = array();
+        foreach ($this->errors as $error) {
+            if ($error instanceof SyntaxError) {
+                $filesWithSyntaxError[] = $error->getFilePath();
+            }
+        }
+
+        return $filesWithSyntaxError;
+    }
+
+    /**
+     * @return int
+     */
+    public function getFilesWithSyntaxErrorCount()
+    {
+        return count($this->getFilesWithSyntaxError());
+    }
+
+    /**
+     * @return bool
+     */
+    public function hasSyntaxError()
+    {
+        return $this->getFilesWithSyntaxErrorCount() !== 0;
+    }
+
+    /**
+     * @return float
+     */
+    public function getTestTime()
+    {
+        return $this->testTime;
+    }
+
+    /**
+     * (PHP 5 &gt;= 5.4.0)<br/>
+     * Specify data which should be serialized to JSON
+     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
+     * @return mixed data which can be serialized by <b>json_encode</b>,
+     * which is a value of any type other than a resource.
+     */
+    #[ReturnTypeWillChange]
+    function jsonSerialize()
+    {
+        return array(
+            'checkedFiles' => $this->getCheckedFiles(),
+            'filesWithSyntaxError' => $this->getFilesWithSyntaxError(),
+            'skippedFiles' => $this->getSkippedFiles(),
+            'errors' => $this->getErrors(),
+        );
+    }
+
+
+}
\ No newline at end of file
diff --git a/php-parallel-lint/php-parallel-lint/src/Settings.php b/php-parallel-lint/php-parallel-lint/src/Settings.php
new file mode 100644
index 0000000..4622910
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/Settings.php
@@ -0,0 +1,247 @@
+<?php
+namespace JakubOnderka\PhpParallelLint;
+
+class Settings
+{
+
+    /**
+     * constants for enum settings
+     */
+    const FORCED = 'FORCED';
+    const DISABLED = 'DISABLED';
+    const AUTODETECT = 'AUTODETECT';
+
+    const FORMAT_TEXT = 'text';
+    const FORMAT_JSON = 'json';
+    const FORMAT_GITLAB = 'gitlab';
+    const FORMAT_CHECKSTYLE = 'checkstyle';
+
+    /**
+     * Path to PHP executable
+     * @var string
+     */
+    public $phpExecutable = 'php';
+
+    /**
+     * Check code inside PHP opening short tag <? or <?= in PHP 5.3
+     * @var bool
+     */
+    public $shortTag = false;
+
+    /**
+     * Check PHP code inside ASP-style <% %> tags.
+     * @var bool
+     */
+    public $aspTags = false;
+
+    /**
+     * Number of jobs running in same time
+     * @var int
+     */
+    public $parallelJobs = 10;
+
+    /**
+     * If path contains directory, only file with these extensions are checked
+     * @var array
+     */
+    public $extensions = array('php', 'phtml', 'php3', 'php4', 'php5', 'phpt');
+
+    /**
+     * Array of file or directories to check
+     * @var array
+     */
+    public $paths = array();
+
+    /**
+     * Don't check files or directories
+     * @var array
+     */
+    public $excluded = array();
+
+    /**
+     * Mode for color detection. Possible values: self::FORCED, self::DISABLED and self::AUTODETECT
+     * @var string
+     */
+    public $colors = self::AUTODETECT;
+
+    /**
+     * Show progress in text output
+     * @var bool
+     */
+    public $showProgress = true;
+
+    /**
+     * Output format (see FORMAT_* constants)
+     * @var string
+     */
+    public $format = self::FORMAT_TEXT;
+
+    /**
+     * Read files and folder to tests from standard input (blocking)
+     * @var bool
+     */
+    public $stdin = false;
+
+    /**
+     * Try to show git blame for row with error
+     * @var bool
+     */
+    public $blame = false;
+
+    /**
+     * Path to git executable for blame
+     * @var string
+     */
+    public $gitExecutable = 'git';
+
+    /**
+     * @var bool
+     */
+    public $ignoreFails = false;
+
+    /**
+     * @var bool
+     */
+    public $showDeprecated = false;
+
+    /**
+     * Path to a file with syntax error callback
+     * @var string|null
+     */
+    public $syntaxErrorCallbackFile = null;
+
+    /**
+     * @param array $paths
+     */
+    public function addPaths(array $paths)
+    {
+        $this->paths = array_merge($this->paths, $paths);
+    }
+
+    /**
+     * @param array $arguments
+     * @return Settings
+     * @throws InvalidArgumentException
+     */
+    public static function parseArguments(array $arguments)
+    {
+        $arguments = new ArrayIterator(array_slice($arguments, 1));
+        $settings = new self;
+
+        // Use the currently invoked php as the default if possible
+        if (defined('PHP_BINARY')) {
+            $settings->phpExecutable = PHP_BINARY;
+        }
+
+        foreach ($arguments as $argument) {
+            if ($argument[0] !== '-') {
+                $settings->paths[] = $argument;
+            } else {
+                switch ($argument) {
+                    case '-p':
+                        $settings->phpExecutable = $arguments->getNext();
+                        break;
+
+                    case '-s':
+                    case '--short':
+                        $settings->shortTag = true;
+                        break;
+
+                    case '-a':
+                    case '--asp':
+                        $settings->aspTags = true;
+                        break;
+
+                    case '-e':
+                        $settings->extensions = array_map('trim', explode(',', $arguments->getNext()));
+                        break;
+
+                    case '-j':
+                        $settings->parallelJobs = max((int) $arguments->getNext(), 1);
+                        break;
+
+                    case '--exclude':
+                        $settings->excluded[] = $arguments->getNext();
+                        break;
+
+                    case '--colors':
+                        $settings->colors = self::FORCED;
+                        break;
+
+                    case '--no-colors':
+                        $settings->colors = self::DISABLED;
+                        break;
+
+                    case '--no-progress':
+                        $settings->showProgress = false;
+                        break;
+
+                    case '--checkstyle':
+                        $settings->format = self::FORMAT_CHECKSTYLE;
+                        break;
+
+                    case '--json':
+                        $settings->format = self::FORMAT_JSON;
+                        break;
+
+                    case '--gitlab':
+                        $settings->format = self::FORMAT_GITLAB;
+                        break;
+
+                    case '--git':
+                        $settings->gitExecutable = $arguments->getNext();
+                        break;
+
+                    case '--stdin':
+                        $settings->stdin = true;
+                        break;
+
+                    case '--blame':
+                        $settings->blame = true;
+                        break;
+
+                    case '--ignore-fails':
+                        $settings->ignoreFails = true;
+                        break;
+
+                    case '--show-deprecated':
+                        $settings->showDeprecated = true;
+                        break;
+
+                    case '--syntax-error-callback':
+                        $settings->syntaxErrorCallbackFile = $arguments->getNext();
+                        break;
+
+                    default:
+                        throw new InvalidArgumentException($argument);
+                }
+            }
+        }
+
+        return $settings;
+    }
+
+    /**
+     * @return array
+     */
+    public static function getPathsFromStdIn()
+    {
+        $content = stream_get_contents(STDIN);
+
+        if (empty($content)) {
+            return array();
+        }
+
+        $lines = explode("\n", rtrim($content));
+        return array_map('rtrim', $lines);
+    }
+}
+
+class ArrayIterator extends \ArrayIterator
+{
+    public function getNext()
+    {
+        $this->next();
+        return $this->current();
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/exceptions.php b/php-parallel-lint/php-parallel-lint/src/exceptions.php
new file mode 100644
index 0000000..b96f28a
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/exceptions.php
@@ -0,0 +1,93 @@
+<?php
+namespace JakubOnderka\PhpParallelLint;
+
+use ReturnTypeWillChange;
+
+class Exception extends \Exception implements \JsonSerializable
+{
+    #[ReturnTypeWillChange]
+    public function jsonSerialize()
+    {
+        return array(
+            'type' => get_class($this),
+            'message' => $this->getMessage(),
+            'code' => $this->getCode(),
+        );
+    }
+}
+
+class RunTimeException extends Exception
+{
+
+}
+
+class InvalidArgumentException extends Exception
+{
+    protected $argument;
+
+    public function __construct($argument)
+    {
+        $this->argument = $argument;
+        $this->message = "Invalid argument $argument";
+    }
+
+    public function getArgument()
+    {
+        return $this->argument;
+    }
+}
+
+class NotExistsPathException extends Exception
+{
+    protected $path;
+
+    public function __construct($path)
+    {
+        $this->path = $path;
+        $this->message = "Path '$path' not found";
+    }
+
+    public function getPath()
+    {
+        return $this->path;
+    }
+}
+
+class NotExistsClassException extends Exception
+{
+    protected $className;
+    protected $fileName;
+
+    public function __construct($className, $fileName)
+    {
+        $this->className = $className;
+        $this->fileName = $fileName;
+        $this->message = "Class with name '$className' does not exists in file '$fileName'";
+    }
+
+    public function getClassName()
+    {
+        return $this->className;
+    }
+
+    public function getFileName()
+    {
+        return $this->fileName;
+    }
+}
+
+class NotImplementCallbackException extends Exception
+{
+    protected $className;
+
+    public function __construct($className)
+    {
+        $this->className = $className;
+        $this->message = "Class '$className' does not implement SyntaxErrorCallback interface.";
+    }
+
+    public function getClassName()
+    {
+        return $this->className;
+    }
+}
diff --git a/php-parallel-lint/php-parallel-lint/src/polyfill.php b/php-parallel-lint/php-parallel-lint/src/polyfill.php
new file mode 100644
index 0000000..79b3049
--- /dev/null
+++ b/php-parallel-lint/php-parallel-lint/src/polyfill.php
@@ -0,0 +1,15 @@
+<?php
+
+/**
+ * Polyfill for PHP < 5.4
+ */
+if (!interface_exists('JsonSerializable', false)) {
+    interface JsonSerializable
+    {
+        /**
+         * @param void
+         * @return mixed
+         */
+        function jsonSerialize();
+    }
+}
-- 
2.39.2


--- end ---

composer dependencies

Dependencies
Development dependencies

Logs

Source code is licensed under the AGPL.