diff --git a/Installer.php b/Installer.php
deleted file mode 100644
index a91dedf..0000000
--- a/Installer.php
+++ /dev/null
@@ -1,148 +0,0 @@
-<?php
-namespace Codeception\c3;
-use Composer\Composer;
-use Composer\DependencyResolver\Operation\InstallOperation;
-use Composer\DependencyResolver\Operation\UninstallOperation;
-use Composer\DependencyResolver\Operation\UpdateOperation;
-use Composer\EventDispatcher\EventSubscriberInterface;
-use Composer\Installer\PackageEvent;
-use Composer\Installer\PackageEvents;
-use Composer\IO\IOInterface;
-use Composer\Plugin\PluginInterface;
-use Composer\EventDispatcher\Event;
-use Composer\Script\ScriptEvents;
-use Composer\Semver\Comparator;
-
-class Installer implements PluginInterface, EventSubscriberInterface
-{
-    /**
-     * @var IOInterface
-     */
-    private $io;
-
-    public function activate(Composer $composer, IOInterface $io)
-    {
-        $this->io = $io;
-    }
-
-    public function deactivate(Composer $composer, IOInterface $io)
-    {
-    }
-
-    public function uninstall(Composer $composer, IOInterface $io) {
-        $this->deleteFile();
-    }
-
-    protected function isOperationOnC3(PackageEvent $event)
-    {
-        if (static::composerV2()) {
-            return true;
-        }
-        $name = '';
-
-        if ($event->getOperation() instanceof InstallOperation) {
-            list(, $name) = explode('/', $event->getOperation()->getPackage()->getName());
-        } elseif ($event->getOperation() instanceof UpdateOperation) {
-            list(, $name) = explode('/', $event->getOperation()->getTargetPackage()->getName());
-        } elseif ($event->getOperation() instanceof UninstallOperation) {
-            list(, $name) = explode('/', $event->getOperation()->getPackage()->getName());
-        }
-
-        return $name === 'c3';
-    }
-    
-    public static function getSubscribedEvents()
-    {
-        if (static::composerV2()) {
-            return [
-                ScriptEvents::POST_INSTALL_CMD => [
-                    ['copyC3V2', 0]
-                ],
-                ScriptEvents::POST_UPDATE_CMD => [
-                    ['askForUpdateV2', 0]
-                ],
-            ];
-        }
-        return [
-            PackageEvents::POST_PACKAGE_INSTALL => [
-                ['copyC3', 0]
-            ],
-            PackageEvents::POST_PACKAGE_UPDATE => [
-                ['askForUpdate', 0]
-            ],
-            PackageEvents::POST_PACKAGE_UNINSTALL => [
-                ['deleteC3', 0]
-            ]
-        ];
-    }
-
-    public function copyC3(PackageEvent $event)
-    {
-        if (!$this->isOperationOnC3($event)) {
-            return;
-        }
-
-        $this->copyC3V2($event);
-    }
-
-    public function copyC3V2(Event $event)
-    {
-        if ($this->c3NotChanged()) {
-            $this->io->write("<comment>[codeception/c3]</comment> c3.php is already up-to-date");
-            return;
-        }
-        if (file_exists(getcwd() . DIRECTORY_SEPARATOR . 'c3.php')) {
-            $replace = $this->io->askConfirmation("<warning>c3.php has changed</warning> Do you want to replace c3.php with latest version?", false);
-            if (!$replace) {
-                return;
-            }
-        }
-
-        $this->io->write("<comment>[codeception/c3]</comment> Copying c3.php to the root of your project...");
-        copy(__DIR__ . DIRECTORY_SEPARATOR . 'c3.php', getcwd() . DIRECTORY_SEPARATOR.'c3.php');
-        $this->io->write("<comment>[codeception/c3]</comment> Include c3.php into index.php in order to collect codecoverage from server scripts");
-    }
-
-    public function askForUpdate(PackageEvent $event)
-    {
-        if (!$this->isOperationOnC3($event) || $this->c3NotChanged()) {
-            return;
-        }
-        $this->copyC3($event);
-    }
-
-    public function askForUpdateV2(Event $event)
-    {
-        if ($this->c3NotChanged()) {
-            return;
-        }
-        $this->copyC3V2($event);
-    }
-
-    private function c3NotChanged()
-    {
-        return file_exists(getcwd() . DIRECTORY_SEPARATOR . 'c3.php') &&
-            md5_file(__DIR__ . DIRECTORY_SEPARATOR . 'c3.php') === md5_file(getcwd() . DIRECTORY_SEPARATOR . 'c3.php');
-    }
-
-    public function deleteC3(PackageEvent $event)
-    {
-        if (!$this->isOperationOnC3($event)) {
-            return;
-        }
-        $this->deleteFile();
-    }
-
-    private function deleteFile() {
-        if (file_exists(getcwd() . DIRECTORY_SEPARATOR . 'c3.php')) {
-            $this->io->write("<comment>[codeception/c3]</comment> Deleting c3.php from the root of your project...");
-            unlink(getcwd() . DIRECTORY_SEPARATOR . 'c3.php');
-        }
-    }
-
-    private static function composerV2()
-    {
-        return Comparator::greaterThanOrEqualTo(PluginInterface::PLUGIN_API_VERSION, '2.0.0');
-    }
-
-}
diff --git a/README.md b/README.md
index 410a220..b207bae 100644
--- a/README.md
+++ b/README.md
@@ -2,18 +2,18 @@
 
 ## Remote CodeCoverage for Codeception [![Build Status](https://travis-ci.org/Codeception/c3.svg?branch=2.0)](https://travis-ci.org/Codeception/c3)
 
-This file `c3.php` should be included into the application you are testing in the very first line.
+C3 should be included and run into the application you are testing in the very first line.
 It will start remote code coverage collection. Coverage data will be stored to disk and retrieved by `codeception` when tests from the suite are finished.
 This file won't affect your application in any way. It is executed **only** when a special header `X-Codeception-CodeCoverage` is sent. Alternatively, if you use Selenium, special cookie `CODECEPTION_CODECOVERAGE` is used. In any other case your application run as usually with no overheads.
 
 ### Local Code Coverage
 
-If you don't run tests on remote server but use a webserver (Apache, Nginx, PhpWebserver) you need `c3.php` installed just the same way.
+If you don't run tests on remote server but use a webserver (Apache, Nginx, PhpWebserver) you need C3 included just the same way.
 In this case coverage result will be merged with local code coverage.
 
 ### Installation
 
-File `c3.php` should be put in project root, into the same directory where `codeception.yml` config is located.
+`codeception/c3` should be available in your project as a dev dependency.
 Also, make sure Codeception is available on remote server either in phar/pear/composer packages.
 
 #### Via Composer
@@ -22,30 +22,24 @@ Add to `composer.json`:
 
 ```
 "require-dev": {
-    "codeception/codeception": "3.*",
-    "codeception/c3": "2.*"
+    "codeception/codeception": "5.*",
+    "codeception/c3": "3.*"
 }
 ```
 
-C3 installer will copy `c3.php` to the project root.
-
-#### Manually
-
-```
-wget https://raw.github.com/Codeception/c3/2.0/c3.php
-```
-
 ### Setup
 
-Now you should include c3.php in your front script, like `index.php`.
+Now you should run c3 in your front script, like `index.php`.
 
 Example file: `web/index.php`:
 
 ``` php
 <?php
 
-define('C3_CODECOVERAGE_ERROR_LOG_FILE', '/path/to/c3_error.log'); //Optional (if not set the default c3 output dir will be used)
-include '/../c3.php';
+require __DIR__ . '/vendor/autoload.php';
+
+$c3 = new Codeception\C3(__DIR__); // pass the directory where `codeception.yml` config is located.
+$c3->run();
 
 define('MY_APP_STARTED', true);
 // App::start();
diff --git a/c3.php b/c3.php
index ee4230c..1b35605 100644
--- a/c3.php
+++ b/c3.php
@@ -8,420 +8,463 @@
  * @author tiger
  */
 
-// $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_DEBUG'] = 1;
+namespace Codeception;
 
 use SebastianBergmann\CodeCoverage\CodeCoverage;
 use SebastianBergmann\CodeCoverage\Driver\Driver;
 use SebastianBergmann\CodeCoverage\Driver\Selector;
 use SebastianBergmann\CodeCoverage\Filter as CodeCoverageFilter;
+use Codeception\Configuration;
+use PHP_CodeCoverage;
 
-if (isset($_COOKIE['CODECEPTION_CODECOVERAGE'])) {
-    $cookie = json_decode($_COOKIE['CODECEPTION_CODECOVERAGE'], true);
+// $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_DEBUG'] = 1;
 
-    // fix for improperly encoded JSON in Code Coverage cookie with WebDriver.
-    // @see https://github.com/Codeception/Codeception/issues/874
-    if (!is_array($cookie)) {
-        $cookie = json_decode($cookie, true);
-    }
+class C3 {
 
-    if ($cookie) {
-        foreach ($cookie as $key => $value) {
-            if (!empty($value)) {
-                $_SERVER["HTTP_X_CODECEPTION_" . strtoupper($key)] = $value;
-            }
-        }
-    }
-}
+    /**
+     * @var string
+     */
+    private $configDir;
 
-if (!array_key_exists('HTTP_X_CODECEPTION_CODECOVERAGE', $_SERVER)) {
-    return;
-}
+    /**
+     * @var string
+     */
+    private $autoloadRootDir;
 
-if (!function_exists('__c3_error')) {
-    function __c3_error($message)
+    /**
+     * @param string $configDir where to look for codeception.yml and codeception.dist.yml
+     */
+    public function __construct($configDir)
     {
-        $errorLogFile = defined('C3_CODECOVERAGE_ERROR_LOG_FILE') ?
-            C3_CODECOVERAGE_ERROR_LOG_FILE :
-            C3_CODECOVERAGE_MEDIATE_STORAGE . DIRECTORY_SEPARATOR . 'error.txt';
-        if (is_writable($errorLogFile)) {
-            file_put_contents($errorLogFile, $message);
-        } else {
-            $message = "Could not write error to log file ($errorLogFile), original message: $message";
-        }
-        if (!headers_sent()) {
-            header('X-Codeception-CodeCoverage-Error: ' . str_replace("\n", ' ', $message), true, 500);
-            setcookie('CODECEPTION_CODECOVERAGE_ERROR', $message);
-        }
+        $this->configDir = realpath($configDir);
+        $this->autoloadRootDir = $this->configDir;
     }
-}
 
-// Autoload Codeception classes
-if (!class_exists('\\Codeception\\Codecept') || !function_exists('codecept_is_path_absolute')) {
-    if (file_exists(__DIR__ . '/codecept.phar')) {
-        require_once 'phar://' . __DIR__ . '/codecept.phar/autoload.php';
-    } elseif (stream_resolve_include_path(__DIR__ . '/vendor/autoload.php')) {
-        require_once __DIR__ . '/vendor/autoload.php';
-        // Required to load some methods only available at codeception/autoload.php
-        if (stream_resolve_include_path(__DIR__ . '/vendor/codeception/codeception/autoload.php')) {
-            require_once __DIR__ . '/vendor/codeception/codeception/autoload.php';
-        }
-    } elseif (stream_resolve_include_path('Codeception/autoload.php')) {
-        require_once 'Codeception/autoload.php';
-    } else {
-        __c3_error('Codeception is not loaded. Please check that either PHAR or Composer package can be used');
+    /**
+     * @param string $dir directory from where to attempt autoloading Codeception
+     */
+    public function setAutoloadRootDir($dir)
+    {
+        $this->autoloadRootDir = $dir;
     }
-}
 
-// phpunit codecoverage shimming
-if (!class_exists('PHP_CodeCoverage') and class_exists('SebastianBergmann\CodeCoverage\CodeCoverage')) {
-    class_alias('SebastianBergmann\CodeCoverage\CodeCoverage', 'PHP_CodeCoverage');
-    class_alias('SebastianBergmann\CodeCoverage\Report\Text', 'PHP_CodeCoverage_Report_Text');
-    class_alias('SebastianBergmann\CodeCoverage\Report\PHP', 'PHP_CodeCoverage_Report_PHP');
-    class_alias('SebastianBergmann\CodeCoverage\Report\Clover', 'PHP_CodeCoverage_Report_Clover');
-    class_alias('SebastianBergmann\CodeCoverage\Report\Crap4j', 'PHP_CodeCoverage_Report_Crap4j');
-    class_alias('SebastianBergmann\CodeCoverage\Report\Html\Facade', 'PHP_CodeCoverage_Report_HTML');
-    class_alias('SebastianBergmann\CodeCoverage\Report\Xml\Facade', 'PHP_CodeCoverage_Report_XML');
-    class_alias('SebastianBergmann\CodeCoverage\Exception', 'PHP_CodeCoverage_Exception');
-}
-// phpunit version
-if (!class_exists('PHPUnit_Runner_Version') && class_exists('PHPUnit\Runner\Version')) {
-    class_alias('PHPUnit\Runner\Version', 'PHPUnit_Runner_Version');
-}
+    /**
+     * Run Codeception C3 Coverage collection.
+     *
+     * @throws Exception\ConfigurationException
+     */
+    public function run()
+    {
+        if (isset($_COOKIE['CODECEPTION_CODECOVERAGE'])) {
+            $cookie = json_decode($_COOKIE['CODECEPTION_CODECOVERAGE'], true);
 
-// Load Codeception Config
-$configDistFile = realpath(__DIR__) . DIRECTORY_SEPARATOR . 'codeception.dist.yml';
-$configFile = realpath(__DIR__) . DIRECTORY_SEPARATOR . 'codeception.yml';
+            // fix for improperly encoded JSON in Code Coverage cookie with WebDriver.
+            // @see https://github.com/Codeception/Codeception/issues/874
+            if (!is_array($cookie)) {
+                $cookie = json_decode($cookie, true);
+            }
 
-if (isset($_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_CONFIG'])) {
-    $configFile = realpath(__DIR__) . DIRECTORY_SEPARATOR . $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_CONFIG'];
-}
-if (file_exists($configFile)) {
-    // Use codeception.yml for configuration.
-} elseif (file_exists($configDistFile)) {
-    // Use codeception.dist.yml for configuration.
-    $configFile = $configDistFile;
-} else {
-    __c3_error(sprintf("Codeception config file '%s' not found", $configFile));
-}
-try {
-    \Codeception\Configuration::config($configFile);
-} catch (\Exception $e) {
-    __c3_error($e->getMessage());
-}
+            if ($cookie) {
+                foreach ($cookie as $key => $value) {
+                    if (!empty($value)) {
+                        $_SERVER["HTTP_X_CODECEPTION_" . strtoupper($key)] = $value;
+                    }
+                }
+            }
+        }
 
-if (!defined('C3_CODECOVERAGE_MEDIATE_STORAGE')) {
+        if (!array_key_exists('HTTP_X_CODECEPTION_CODECOVERAGE', $_SERVER)) {
+            return;
+        }
 
-    // workaround for 'zend_mm_heap corrupted' problem
-    gc_disable();
+        if (!function_exists('__c3_error')) {
+            function __c3_error($message)
+            {
+                $errorLogFile = defined('C3_CODECOVERAGE_ERROR_LOG_FILE') ?
+                    C3_CODECOVERAGE_ERROR_LOG_FILE :
+                    C3_CODECOVERAGE_MEDIATE_STORAGE . DIRECTORY_SEPARATOR . 'error.txt';
+                if (is_writable($errorLogFile)) {
+                    file_put_contents($errorLogFile, $message);
+                } else {
+                    $message = "Could not write error to log file ($errorLogFile), original message: $message";
+                }
+                if (!headers_sent()) {
+                    header('X-Codeception-CodeCoverage-Error: ' . str_replace("\n", ' ', $message), true, 500);
+                    setcookie('CODECEPTION_CODECOVERAGE_ERROR', $message);
+                }
+            }
+        }
 
-    $memoryLimit = ini_get('memory_limit');
-    $requiredMemory = '384M';
-    if ((substr($memoryLimit, -1) === 'M' && (int)$memoryLimit < (int)$requiredMemory)
-        || (substr($memoryLimit, -1) === 'K' && (int)$memoryLimit < (int)$requiredMemory * 1024)
-        || (ctype_digit($memoryLimit) && (int)$memoryLimit < (int)$requiredMemory * 1024 * 1024)
-    ) {
-        ini_set('memory_limit', $requiredMemory);
-    }
+        // Autoload Codeception classes
+        if (!class_exists('\\Codeception\\Codecept') || !function_exists('codecept_is_path_absolute')) {
+            if (file_exists($this->autoloadRootDir . '/codecept.phar')) {
+                require_once 'phar://' . $this->autoloadRootDir . '/codecept.phar/autoload.php';
+            } elseif (stream_resolve_include_path($this->autoloadRootDir . '/vendor/autoload.php')) {
+                require_once $this->autoloadRootDir . '/vendor/autoload.php';
+                // Required to load some methods only available at codeception/autoload.php
+                if (stream_resolve_include_path($this->autoloadRootDir . '/vendor/codeception/codeception/autoload.php')) {
+                    require_once $this->autoloadRootDir . '/vendor/codeception/codeception/autoload.php';
+                }
+            } elseif (stream_resolve_include_path('Codeception/autoload.php')) {
+                require_once 'Codeception/autoload.php';
+            } else {
+                __c3_error('Codeception is not loaded. Please check that either PHAR or Composer package can be used');
+            }
+        }
 
-    define('C3_CODECOVERAGE_MEDIATE_STORAGE', Codeception\Configuration::outputDir() . 'c3tmp');
-    define('C3_CODECOVERAGE_PROJECT_ROOT', Codeception\Configuration::projectDir());
-    define('C3_CODECOVERAGE_TESTNAME', $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE']);
+        // phpunit codecoverage shimming
+        if (!class_exists('PHP_CodeCoverage') and class_exists('SebastianBergmann\CodeCoverage\CodeCoverage')) {
+            class_alias('SebastianBergmann\CodeCoverage\CodeCoverage', 'PHP_CodeCoverage');
+            class_alias('SebastianBergmann\CodeCoverage\Report\Text', 'PHP_CodeCoverage_Report_Text');
+            class_alias('SebastianBergmann\CodeCoverage\Report\PHP', 'PHP_CodeCoverage_Report_PHP');
+            class_alias('SebastianBergmann\CodeCoverage\Report\Clover', 'PHP_CodeCoverage_Report_Clover');
+            class_alias('SebastianBergmann\CodeCoverage\Report\Crap4j', 'PHP_CodeCoverage_Report_Crap4j');
+            class_alias('SebastianBergmann\CodeCoverage\Report\Html\Facade', 'PHP_CodeCoverage_Report_HTML');
+            class_alias('SebastianBergmann\CodeCoverage\Report\Xml\Facade', 'PHP_CodeCoverage_Report_XML');
+            class_alias('SebastianBergmann\CodeCoverage\Exception', 'PHP_CodeCoverage_Exception');
+        }
+        // phpunit version
+        if (!class_exists('PHPUnit_Runner_Version') && class_exists('PHPUnit\Runner\Version')) {
+            class_alias('PHPUnit\Runner\Version', 'PHPUnit_Runner_Version');
+        }
 
-    function __c3_build_html_report(PHP_CodeCoverage $codeCoverage, $path)
-    {
-        $writer = new PHP_CodeCoverage_Report_HTML();
-        $writer->process($codeCoverage, $path . 'html');
+        // Load Codeception Config
+        $configDistFile = $this->configDir . DIRECTORY_SEPARATOR . 'codeception.dist.yml';
+        $configFile = $this->configDir . DIRECTORY_SEPARATOR . 'codeception.yml';
 
-        if (file_exists($path . '.tar')) {
-            unlink($path . '.tar');
+        if (isset($_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_CONFIG'])) {
+            $configFile = $this->configDir . DIRECTORY_SEPARATOR . $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_CONFIG'];
+        }
+        if (file_exists($configFile)) {
+            // Use codeception.yml for configuration.
+        } elseif (file_exists($configDistFile)) {
+            // Use codeception.dist.yml for configuration.
+            $configFile = $configDistFile;
+        } else {
+            __c3_error(sprintf("Codeception config file '%s' not found", $configFile));
+        }
+        try {
+            Configuration::config($configFile);
+        } catch (\Exception $e) {
+            __c3_error($e->getMessage());
         }
 
-        $phar = new PharData($path . '.tar');
-        $phar->setSignatureAlgorithm(Phar::SHA1);
-        $files = $phar->buildFromDirectory($path . 'html');
-        array_map('unlink', $files);
+        if (!defined('C3_CODECOVERAGE_MEDIATE_STORAGE')) {
 
-        if (in_array('GZ', Phar::getSupportedCompression())) {
-            if (file_exists($path . '.tar.gz')) {
-                unlink($path . '.tar.gz');
+            // workaround for 'zend_mm_heap corrupted' problem
+            gc_disable();
+
+            $memoryLimit = ini_get('memory_limit');
+            $requiredMemory = '384M';
+            if ((substr($memoryLimit, -1) === 'M' && (int)$memoryLimit < (int)$requiredMemory)
+                || (substr($memoryLimit, -1) === 'K' && (int)$memoryLimit < (int)$requiredMemory * 1024)
+                || (ctype_digit($memoryLimit) && (int)$memoryLimit < (int)$requiredMemory * 1024 * 1024)
+            ) {
+                ini_set('memory_limit', $requiredMemory);
             }
 
-            $phar->compress(\Phar::GZ);
+            define('C3_CODECOVERAGE_MEDIATE_STORAGE', Configuration::outputDir() . 'c3tmp');
+            define('C3_CODECOVERAGE_PROJECT_ROOT', Configuration::projectDir());
+            define('C3_CODECOVERAGE_TESTNAME', $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE']);
 
-            // close the file so that we can rename it
-            unset($phar);
+            function __c3_build_html_report(PHP_CodeCoverage $codeCoverage, $path)
+            {
+                $writer = new PHP_CodeCoverage_Report_HTML();
+                $writer->process($codeCoverage, $path . 'html');
 
-            unlink($path . '.tar');
-            rename($path . '.tar.gz', $path . '.tar');
-        }
+                if (file_exists($path . '.tar')) {
+                    unlink($path . '.tar');
+                }
 
-        return $path . '.tar';
-    }
+                $phar = new PharData($path . '.tar');
+                $phar->setSignatureAlgorithm(Phar::SHA1);
+                $files = $phar->buildFromDirectory($path . 'html');
+                array_map('unlink', $files);
 
-    function __c3_build_clover_report(PHP_CodeCoverage $codeCoverage, $path)
-    {
-        $writer = new PHP_CodeCoverage_Report_Clover();
-        $writer->process($codeCoverage, $path . '.clover.xml');
+                if (in_array('GZ', Phar::getSupportedCompression())) {
+                    if (file_exists($path . '.tar.gz')) {
+                        unlink($path . '.tar.gz');
+                    }
 
-        return $path . '.clover.xml';
-    }
+                    $phar->compress(\Phar::GZ);
 
-    function __c3_build_crap4j_report(PHP_CodeCoverage $codeCoverage, $path)
-    {
-        $writer = new PHP_CodeCoverage_Report_Crap4j();
-        $writer->process($codeCoverage, $path . '.crap4j.xml');
+                    // close the file so that we can rename it
+                    unset($phar);
 
-        return $path . '.crap4j.xml';
-    }
+                    unlink($path . '.tar');
+                    rename($path . '.tar.gz', $path . '.tar');
+                }
 
-    function __c3_build_cobertura_report(PHP_CodeCoverage $codeCoverage, $path)
-    {
-        if (!class_exists(\SebastianBergmann\CodeCoverage\Report\Cobertura::class)) {
-            throw new Exception("Cobertura report requires php-code-coverage >= 9.2");
-        }
-        $writer = new \SebastianBergmann\CodeCoverage\Report\Cobertura();
-        $writer->process($codeCoverage, $path . '.cobertura.xml');
+                return $path . '.tar';
+            }
 
-        return $path . '.cobertura.xml';
-    }
+            function __c3_build_clover_report(PHP_CodeCoverage $codeCoverage, $path)
+            {
+                $writer = new PHP_CodeCoverage_Report_Clover();
+                $writer->process($codeCoverage, $path . '.clover.xml');
 
-    function __c3_build_phpunit_report(PHP_CodeCoverage $codeCoverage, $path)
-    {
-        $writer = new PHP_CodeCoverage_Report_XML(\PHPUnit_Runner_Version::id());
-        $writer->process($codeCoverage, $path . 'phpunit');
+                return $path . '.clover.xml';
+            }
 
-        if (file_exists($path . '.tar')) {
-            unlink($path . '.tar');
-        }
+            function __c3_build_crap4j_report(PHP_CodeCoverage $codeCoverage, $path)
+            {
+                $writer = new PHP_CodeCoverage_Report_Crap4j();
+                $writer->process($codeCoverage, $path . '.crap4j.xml');
 
-        $phar = new PharData($path . '.tar');
-        $phar->setSignatureAlgorithm(Phar::SHA1);
-        $files = $phar->buildFromDirectory($path . 'phpunit');
-        array_map('unlink', $files);
+                return $path . '.crap4j.xml';
+            }
+
+            function __c3_build_cobertura_report(PHP_CodeCoverage $codeCoverage, $path)
+            {
+                if (!class_exists(\SebastianBergmann\CodeCoverage\Report\Cobertura::class)) {
+                    throw new Exception("Cobertura report requires php-code-coverage >= 9.2");
+                }
+                $writer = new \SebastianBergmann\CodeCoverage\Report\Cobertura();
+                $writer->process($codeCoverage, $path . '.cobertura.xml');
 
-        if (in_array('GZ', Phar::getSupportedCompression())) {
-            if (file_exists($path . '.tar.gz')) {
-                unlink($path . '.tar.gz');
+                return $path . '.cobertura.xml';
             }
 
-            $phar->compress(\Phar::GZ);
+            function __c3_build_phpunit_report(PHP_CodeCoverage $codeCoverage, $path)
+            {
+                $writer = new PHP_CodeCoverage_Report_XML(\PHPUnit_Runner_Version::id());
+                $writer->process($codeCoverage, $path . 'phpunit');
 
-            // close the file so that we can rename it
-            unset($phar);
+                if (file_exists($path . '.tar')) {
+                    unlink($path . '.tar');
+                }
 
-            unlink($path . '.tar');
-            rename($path . '.tar.gz', $path . '.tar');
-        }
+                $phar = new PharData($path . '.tar');
+                $phar->setSignatureAlgorithm(Phar::SHA1);
+                $files = $phar->buildFromDirectory($path . 'phpunit');
+                array_map('unlink', $files);
 
-        return $path . '.tar';
-    }
+                if (in_array('GZ', Phar::getSupportedCompression())) {
+                    if (file_exists($path . '.tar.gz')) {
+                        unlink($path . '.tar.gz');
+                    }
 
-    function __c3_send_file($filename)
-    {
-        if (!headers_sent()) {
-            readfile($filename);
-        }
+                    $phar->compress(\Phar::GZ);
 
-        return __c3_exit();
-    }
+                    // close the file so that we can rename it
+                    unset($phar);
 
-    /**
-     * @param $filename
-     * @param bool $lock Lock the file for writing?
-     * @return [null|PHP_CodeCoverage|\SebastianBergmann\CodeCoverage\CodeCoverage, resource]
-     */
-    function __c3_factory($filename, $lock = false)
-    {
-        $file = null;
-        if ($filename !== null && is_readable($filename)) {
-            if ($lock) {
-                $file = fopen($filename, 'r+');
-                if (flock($file, LOCK_EX)) {
-                    $phpCoverage = unserialize(stream_get_contents($file));
-                } else {
-                    __c3_error("Failed to acquire write-lock for $filename");
+                    unlink($path . '.tar');
+                    rename($path . '.tar.gz', $path . '.tar');
                 }
-            } else {
-                $phpCoverage = unserialize(file_get_contents($filename));
+
+                return $path . '.tar';
             }
 
-            return [$phpCoverage, $file];
-        }
+            function __c3_send_file($filename)
+            {
+                if (!headers_sent()) {
+                    readfile($filename);
+                }
 
-        if (isset($_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_SUITE'])) {
-            $suite = $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_SUITE'];
-            try {
-                $settings = \Codeception\Configuration::suiteSettings($suite, \Codeception\Configuration::config());
-            } catch (Exception $e) {
-                __c3_error($e->getMessage());
-                $settings = [];
+                return __c3_exit();
             }
-        } else {
-            $settings = \Codeception\Configuration::config();
-        }
 
-        $pathCoverage = false;
-        if (isset($settings['coverage']['path_coverage'])) {
-            $pathCoverage = (bool)$settings['coverage']['path_coverage'];
-        }
+            /**
+             * @param $filename
+             * @param bool $lock Lock the file for writing?
+             * @return [null|PHP_CodeCoverage|\SebastianBergmann\CodeCoverage\CodeCoverage, resource]
+             */
+            function __c3_factory($filename, $lock = false)
+            {
+                $file = null;
+                if ($filename !== null && is_readable($filename)) {
+                    if ($lock) {
+                        $file = fopen($filename, 'r+');
+                        if (flock($file, LOCK_EX)) {
+                            $phpCoverage = unserialize(stream_get_contents($file));
+                        } else {
+                            __c3_error("Failed to acquire write-lock for $filename");
+                        }
+                    } else {
+                        $phpCoverage = unserialize(file_get_contents($filename));
+                    }
 
-        if (class_exists(Selector::class)) {
-            //php-code-coverage >= 9.1.10
-            $filter = new CodeCoverageFilter();
-            if ($pathCoverage) {
-                $driver = (new Selector())->forLineAndPathCoverage($filter);
-            } else {
-                $driver = (new Selector())->forLineCoverage($filter);
-            }
-            $phpCoverage = new CodeCoverage($driver, $filter);
-        } elseif (method_exists(Driver::class, 'forLineCoverage')) {
-            //php-code-coverage 9.0.0 - 9.1.9
-            $filter = new CodeCoverageFilter();
-            if ($pathCoverage) {
-                $driver = Driver::forLineAndPathCoverage($filter);
-            } else {
-                $driver = Driver::forLineCoverage($filter);
-            }
-            $phpCoverage = new CodeCoverage($driver, $filter);
-        } else {
-            //php-code-coverage 8 or older
-            $phpCoverage = new PHP_CodeCoverage();
-        }
+                    return [$phpCoverage, $file];
+                }
 
-        try {
-            \Codeception\Coverage\Filter::setup($phpCoverage)
-                ->whiteList($settings)
-                ->blackList($settings);
-        } catch (Exception $e) {
-            __c3_error($e->getMessage());
-        }
+                if (isset($_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_SUITE'])) {
+                    $suite = $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_SUITE'];
+                    try {
+                        $settings = Configuration::suiteSettings($suite, Configuration::config());
+                    } catch (Exception $e) {
+                        __c3_error($e->getMessage());
+                        $settings = [];
+                    }
+                } else {
+                    $settings = Configuration::config();
+                }
 
-        return [$phpCoverage, $file];
-    }
+                $pathCoverage = false;
+                if (isset($settings['coverage']['path_coverage'])) {
+                    $pathCoverage = (bool)$settings['coverage']['path_coverage'];
+                }
 
-    function __c3_exit()
-    {
-        if (!isset($_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_DEBUG'])) {
-            exit;
-        }
-        return null;
-    }
+                if (class_exists(Selector::class)) {
+                    //php-code-coverage >= 9.1.10
+                    $filter = new CodeCoverageFilter();
+                    if ($pathCoverage) {
+                        $driver = (new Selector())->forLineAndPathCoverage($filter);
+                    } else {
+                        $driver = (new Selector())->forLineCoverage($filter);
+                    }
+                    $phpCoverage = new CodeCoverage($driver, $filter);
+                } elseif (method_exists(Driver::class, 'forLineCoverage')) {
+                    //php-code-coverage 9.0.0 - 9.1.9
+                    $filter = new CodeCoverageFilter();
+                    if ($pathCoverage) {
+                        $driver = Driver::forLineAndPathCoverage($filter);
+                    } else {
+                        $driver = Driver::forLineCoverage($filter);
+                    }
+                    $phpCoverage = new CodeCoverage($driver, $filter);
+                } else {
+                    //php-code-coverage 8 or older
+                    $phpCoverage = new PHP_CodeCoverage();
+                }
 
-    function __c3_clear()
-    {
-        \Codeception\Util\FileSystem::doEmptyDir(C3_CODECOVERAGE_MEDIATE_STORAGE);
-    }
-}
+                try {
+                    \Codeception\Coverage\Filter::setup($phpCoverage)
+                        ->whiteList($settings)
+                        ->blackList($settings);
+                } catch (Exception $e) {
+                    __c3_error($e->getMessage());
+                }
 
-if (!is_dir(C3_CODECOVERAGE_MEDIATE_STORAGE)) {
-    if (mkdir(C3_CODECOVERAGE_MEDIATE_STORAGE, 0777, true) === false) {
-        __c3_error('Failed to create directory "' . C3_CODECOVERAGE_MEDIATE_STORAGE . '"');
-    }
-}
+                return [$phpCoverage, $file];
+            }
 
-// evaluate base path for c3-related files
-$path = realpath(C3_CODECOVERAGE_MEDIATE_STORAGE) . DIRECTORY_SEPARATOR . 'codecoverage';
+            function __c3_exit()
+            {
+                if (!isset($_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_DEBUG'])) {
+                    exit;
+                }
+                return null;
+            }
 
-$requestedC3Report = (strpos($_SERVER['REQUEST_URI'], 'c3/report') !== false);
+            function __c3_clear()
+            {
+                \Codeception\Util\FileSystem::doEmptyDir(C3_CODECOVERAGE_MEDIATE_STORAGE);
+            }
+        }
 
-$completeReport = $currentReport = $path . '.serialized';
-if ($requestedC3Report) {
-    set_time_limit(0);
+        if (!is_dir(C3_CODECOVERAGE_MEDIATE_STORAGE)) {
+            if (mkdir(C3_CODECOVERAGE_MEDIATE_STORAGE, 0777, true) === false) {
+                __c3_error('Failed to create directory "' . C3_CODECOVERAGE_MEDIATE_STORAGE . '"');
+            }
+        }
 
-    $route = ltrim(strrchr(rtrim($_SERVER['REQUEST_URI'], '/'), '/'), '/');
+        // evaluate base path for c3-related files
+        $path = realpath(C3_CODECOVERAGE_MEDIATE_STORAGE) . DIRECTORY_SEPARATOR . 'codecoverage';
 
-    if ($route === 'clear') {
-        __c3_clear();
-        return __c3_exit();
-    }
+        $requestedC3Report = (strpos($_SERVER['REQUEST_URI'], 'c3/report') !== false);
 
-    list($codeCoverage, ) = __c3_factory($completeReport);
+        $completeReport = $currentReport = $path . '.serialized';
+        if ($requestedC3Report) {
+            set_time_limit(0);
 
-    switch ($route) {
-        case 'html':
-            try {
-                __c3_send_file(__c3_build_html_report($codeCoverage, $path));
-            } catch (Exception $e) {
-                __c3_error($e->getMessage());
-            }
-            return __c3_exit();
-        case 'clover':
-            try {
-                __c3_send_file(__c3_build_clover_report($codeCoverage, $path));
-            } catch (Exception $e) {
-                __c3_error($e->getMessage());
-            }
-            return __c3_exit();
-        case 'crap4j':
-            try {
-                __c3_send_file(__c3_build_crap4j_report($codeCoverage, $path));
-            } catch (Exception $e) {
-                __c3_error($e->getMessage());
-            }
-            return __c3_exit();
-        case 'serialized':
-            try {
-                __c3_send_file($completeReport);
-            } catch (Exception $e) {
-                __c3_error($e->getMessage());
-            }
-            return __c3_exit();
-        case 'phpunit':
-            try {
-                __c3_send_file(__c3_build_phpunit_report($codeCoverage, $path));
-            } catch (Exception $e) {
-                __c3_error($e->getMessage());
+            $route = ltrim(strrchr(rtrim($_SERVER['REQUEST_URI'], '/'), '/'), '/');
+
+            if ($route === 'clear') {
+                __c3_clear();
+                return __c3_exit();
             }
-            return __c3_exit();
-        case 'cobertura':
-            try {
-                __c3_send_file(__c3_build_cobertura_report($codeCoverage, $path));
-            } catch (Exception $e) {
-                __c3_error($e->getMessage());
+
+            list($codeCoverage, ) = __c3_factory($completeReport);
+
+            switch ($route) {
+                case 'html':
+                    try {
+                        __c3_send_file(__c3_build_html_report($codeCoverage, $path));
+                    } catch (Exception $e) {
+                        __c3_error($e->getMessage());
+                    }
+                    return __c3_exit();
+                case 'clover':
+                    try {
+                        __c3_send_file(__c3_build_clover_report($codeCoverage, $path));
+                    } catch (Exception $e) {
+                        __c3_error($e->getMessage());
+                    }
+                    return __c3_exit();
+                case 'crap4j':
+                    try {
+                        __c3_send_file(__c3_build_crap4j_report($codeCoverage, $path));
+                    } catch (Exception $e) {
+                        __c3_error($e->getMessage());
+                    }
+                    return __c3_exit();
+                case 'serialized':
+                    try {
+                        __c3_send_file($completeReport);
+                    } catch (Exception $e) {
+                        __c3_error($e->getMessage());
+                    }
+                    return __c3_exit();
+                case 'phpunit':
+                    try {
+                        __c3_send_file(__c3_build_phpunit_report($codeCoverage, $path));
+                    } catch (Exception $e) {
+                        __c3_error($e->getMessage());
+                    }
+                    return __c3_exit();
+                case 'cobertura':
+                    try {
+                        __c3_send_file(__c3_build_cobertura_report($codeCoverage, $path));
+                    } catch (Exception $e) {
+                        __c3_error($e->getMessage());
+                    }
+                    return __c3_exit();
             }
-            return __c3_exit();
-    }
-} else {
-    list($codeCoverage, ) = __c3_factory(null);
-    $codeCoverage->start(C3_CODECOVERAGE_TESTNAME);
-    if (!array_key_exists('HTTP_X_CODECEPTION_CODECOVERAGE_DEBUG', $_SERVER)) {
-        register_shutdown_function(
-            function () use ($codeCoverage, $currentReport) {
-                $codeCoverage->stop();
-                if (!file_exists(dirname($currentReport))) { // verify directory exists
-                    if (!mkdir(dirname($currentReport), 0777, true)) {
-                        __c3_error("Can't write CodeCoverage report into $currentReport");
+        } else {
+            list($codeCoverage, ) = __c3_factory(null);
+            $codeCoverage->start(C3_CODECOVERAGE_TESTNAME);
+            if (!array_key_exists('HTTP_X_CODECEPTION_CODECOVERAGE_DEBUG', $_SERVER)) {
+                register_shutdown_function(
+                    function () use ($codeCoverage, $currentReport) {
+                        $codeCoverage->stop();
+                        if (!file_exists(dirname($currentReport))) { // verify directory exists
+                            if (!mkdir(dirname($currentReport), 0777, true)) {
+                                __c3_error("Can't write CodeCoverage report into $currentReport");
+                            }
+                        }
+
+                        // This will either lock the existing report for writing and return it along with a file pointer,
+                        // or return a fresh PHP_CodeCoverage object without a file pointer. We'll merge the current request
+                        // into that coverage object, write it to disk, and release the lock. By doing this in the end of
+                        // the request, we avoid this scenario, where Request 2 overwrites the changes from Request 1:
+                        //
+                        //             Time ->
+                        // Request 1 [ <read>               <write>          ]
+                        // Request 2 [         <read>                <write> ]
+                        //
+                        // In addition, by locking the file for exclusive writing, we make sure no other request try to
+                        // read/write to the file at the same time as this request (leading to a corrupt file). flock() is a
+                        // blocking call, so it waits until an exclusive lock can be acquired before continuing.
+
+                        list($existingCodeCoverage, $file) = __c3_factory($currentReport, true);
+                        $existingCodeCoverage->merge($codeCoverage);
+
+                        if ($file === null) {
+                            file_put_contents($currentReport, serialize($existingCodeCoverage), LOCK_EX);
+                        } else {
+                            fseek($file, 0);
+                            fwrite($file, serialize($existingCodeCoverage));
+                            fflush($file);
+                            flock($file, LOCK_UN);
+                            fclose($file);
+                        }
                     }
-                }
-
-                // This will either lock the existing report for writing and return it along with a file pointer,
-                // or return a fresh PHP_CodeCoverage object without a file pointer. We'll merge the current request
-                // into that coverage object, write it to disk, and release the lock. By doing this in the end of
-                // the request, we avoid this scenario, where Request 2 overwrites the changes from Request 1:
-                //
-                //             Time ->
-                // Request 1 [ <read>               <write>          ]
-                // Request 2 [         <read>                <write> ]
-                //
-                // In addition, by locking the file for exclusive writing, we make sure no other request try to
-                // read/write to the file at the same time as this request (leading to a corrupt file). flock() is a
-                // blocking call, so it waits until an exclusive lock can be acquired before continuing.
-
-                list($existingCodeCoverage, $file) = __c3_factory($currentReport, true);
-                $existingCodeCoverage->merge($codeCoverage);
-
-                if ($file === null) {
-                    file_put_contents($currentReport, serialize($existingCodeCoverage), LOCK_EX);
-                } else {
-                    fseek($file, 0);
-                    fwrite($file, serialize($existingCodeCoverage));
-                    fflush($file);
-                    flock($file, LOCK_UN);
-                    fclose($file);
-                }
+                );
             }
-        );
+        }
     }
+
 }
 
 // @codeCoverageIgnoreEnd
diff --git a/composer.json b/composer.json
index d16287d..d2c9bfd 100644
--- a/composer.json
+++ b/composer.json
@@ -3,7 +3,7 @@
     "description":"CodeCoverage collector for Codeception",
     "keywords":["code coverage", "CodeCoverage"],
     "homepage":"http://codeception.com/",
-    "type": "composer-plugin",
+    "type": "library",
     "minimum-stability": "dev",
     "prefer-stable": true,
     "license":"MIT",
@@ -19,18 +19,9 @@
         }
     ],
     "require": {
-        "php": ">=5.5.0",
-        "composer-plugin-api": "^1.0 || ^2.0"
-    },
-    "require-dev": {
-        "composer/composer": "^1.0 || ^2.0"
-    },
-    "extra": {
-        "class": "Codeception\\c3\\Installer"
+        "php": ">=5.5.0"
     },
     "autoload": {
-        "psr-4": {
-            "Codeception\\c3\\": "."
-        }
+        "classmap": ["c3.php"]
     }
 }