<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

/*
 * Declares every class in the bundles' `src/`, so that a declaration PHP refuses
 * to accept — most typically a method that shadows one of Symfony's
 * AbstractController helpers (`file()`, `stream()`, `render()`, …) with an
 * incompatible signature — is discovered here rather than by `cache:clear` in
 * the application, after a release.
 *
 * Such an error is raised at compile time and cannot be caught, so this runs as
 * its own process, and writes the class it is about to declare to the progress
 * file named by `argv[2]` — after the process dies, that file holds the culprit.
 * (STDERR would not do: the fatal's own message and stack trace land there too.)
 * The caller then re-runs with that class in `argv[1]`, a comma-separated skip
 * list, to reach the classes behind it. See ClassDeclarationTest, which runs it.
 */

$root = \dirname(__DIR__, 3);

require $root.'/vendor/autoload.php';

$skip = array_flip(array_filter(explode(',', $argv[1] ?? '')));
$progress = $argv[2] ?? null;

$prefixes = [
    'admin-bundle' => 'Ferienpass\\AdminBundle\\',
    'api-bundle' => 'Ferienpass\\ApiBundle\\',
    'cms-bundle' => 'Ferienpass\\CmsBundle\\',
    'core-bundle' => 'Ferienpass\\CoreBundle\\',
];

$count = 0;

foreach ($prefixes as $bundle => $prefix) {
    $dir = $root.'/'.$bundle.'/src';
    if (!is_dir($dir)) {
        continue;
    }

    $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS));
    foreach ($files as $file) {
        if ('php' !== $file->getExtension()) {
            continue;
        }

        $class = $prefix.str_replace('/', '\\', substr($file->getPathname(), \strlen($dir) + 1, -4));

        if (isset($skip[$class])) {
            continue;
        }

        // Recorded BEFORE the attempt: an uncatchable compile error leaves its
        // culprit behind as the file's content.
        if (null !== $progress) {
            file_put_contents($progress, $class);
        }

        try {
            class_exists($class);
        } catch (Throwable) {
            // A class extending a dependency this repository does not install on
            // its own (they come with the application). Missing, not broken.
            continue;
        }

        ++$count;
    }
}

fwrite(\STDOUT, 'DECLARED '.$count."\n");
