OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

PhpConstantsTest.php

4 KB
<?php

declare(strict_types=1);

/**
 * Deckt ab, dass jede verwendete PHP-Kernkonstante auch existiert.
 *
 * Ein vertippter Konstantenname fällt beim Lesen nicht auf und beim Start
 * nicht ebenfalls – er schlägt erst zu, wenn genau die Zeile ausgeführt wird,
 * und dann als Abbruch mit internem Fehler. Geprüft werden nur Konstanten aus
 * dem PHP-Kern, die auf jeder Installation vorhanden sind; Konstanten aus
 * optionalen Erweiterungen bleiben außen vor.
 */
return static function (): void {

    /** Präfixe, deren Konstanten der PHP-Kern immer mitbringt. */
    $praefixe = ['LIBXML_', 'JSON_', 'PREG_', 'ENT_', 'PATHINFO_', 'UPLOAD_ERR_',
                 'FILTER_', 'SORT_', 'STR_PAD_', 'ARRAY_FILTER_', 'COUNT_', 'SEEK_', 'LOCK_'];

    /** Sammelt alle als Konstante gelesenen Bezeichner einer Datei. */
    $konstantenIn = static function (string $datei) use ($praefixe): array {
        $tokens = @token_get_all((string)file_get_contents($datei));
        $namen  = [];
        $vorher = null;
        foreach ($tokens as $t) {
            if (is_array($t) && in_array($t[0], [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], true)) {
                continue;
            }
            if (is_array($t) && $t[0] === T_STRING) {
                $name = $t[1];
                // Eigenschafts- und Klassenzugriffe sind keine Konstanten.
                $istZugriff = is_array($vorher)
                    && in_array($vorher[0], [T_OBJECT_OPERATOR, T_DOUBLE_COLON, T_FUNCTION, T_NEW, T_CONST], true);
                if (!$istZugriff) {
                    foreach ($praefixe as $p) {
                        if (str_starts_with($name, $p)) {
                            $namen[$name] = true;
                            break;
                        }
                    }
                }
            }
            $vorher = $t;
        }
        return array_keys($namen);
    };

    /** Alle PHP-Dateien des Projekts (ohne Fremdcode). */
    $dateien = static function (): array {
        $wurzel = dirname(__DIR__);
        $liste  = [];
        foreach (['src', 'bin', 'public', 'templates'] as $ordner) {
            $pfad = $wurzel . '/' . $ordner;
            if (!is_dir($pfad)) {
                continue;
            }
            $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($pfad));
            foreach ($it as $f) {
                if ($f->isFile() && $f->getExtension() === 'php') {
                    $liste[] = $f->getPathname();
                }
            }
        }
        return $liste;
    };

    test('Jede verwendete PHP-Kernkonstante existiert', function () use ($dateien, $konstantenIn) {
        $fehlend = [];
        foreach ($dateien() as $datei) {
            foreach ($konstantenIn($datei) as $name) {
                if (!defined($name)) {
                    $fehlend[] = str_replace(dirname(__DIR__) . '/', '', $datei) . ': ' . $name;
                }
            }
        }
        assertSame([], $fehlend, "Unbekannte Konstante(n):\n  " . implode("\n  ", $fehlend));
    });

    test('Der Netzwerkplan prueft das XML mit einer echten Konstante', function () {
        // Ein unbekannter Name würde hier erst beim Hochladen auffallen.
        $quelle = (string)file_get_contents(__DIR__ . '/../src/Controllers/NetworkDiagramController.php');
        assertTrue(str_contains($quelle, 'LIBXML_NONET | LIBXML_NOERROR'),
            'Die Kennzeichen für die XML-Prüfung stimmen nicht');
        assertFalse(str_contains($quelle, 'LIBXML_NOERR '), 'Der verkürzte Name steht noch im Code');
        // LIBXML_NONET hält die Auflösung externer Entitäten vom Netz fern.
        assertTrue(defined('LIBXML_NONET') && defined('LIBXML_NOERROR'));
    });
};