Auf einem Hetzner-Webhosting mit PHP 8.5 brach die Anwendung beim ersten Aufruf ab. Ausloeser war session.sid_length: seit PHP 8.4 als veraltet gemarkt. Die Meldung wurde ausgegeben, damit waren Header raus - und danach liess sich weder eine ini-Einstellung aendern noch eine Session starten. Aus einer Randnotiz wurde so ein Totalausfall. Behoben auf drei Ebenen: 1. Die veralteten Aufrufe sind weg. session.sid_length und session.sid_bits_per_character werden nicht mehr gesetzt; die Voreinstellungen liefern mit 128 Bit ohnehin genug Entropie. Ebenso entfallen curl_close(), imagedestroy() und finfo_close() - seit PHP 8.0 bzw. 8.5 wirkungslos und als veraltet gemeldet. 2. public/index.php puffert die Ausgabe von der ersten Zeile an. Viele Hoster erzwingen display_errors und lassen sich per ini_set() nicht davon abbringen. Gepuffert laesst sich Fremdausgabe vor dem Senden verwerfen und ins Fehlerprotokoll umleiten, statt die Antwort zu zerstoeren. Das schuetzt auch Binaerdownloads: eine einzelne Meldung haette bisher jedes PNG beschaedigt. Session::start() meldet ausserdem klar, wenn doch schon Ausgaben raus sind, statt eine Kaskade aus Warnungen zu erzeugen. 3. endroid/qr-code von 5.1 auf 6.0 angehoben. Version 5 markiert Parameter implizit als nullable, was PHP 8.4 bemaengelt - bei jedem erzeugten QR-Code. Version 6.1 waere ebenfalls verfuegbar, verlangt aber PHP 8.4+ und wuerde Hoster mit 8.2 oder 8.3 ausschliessen; 6.0 laeuft ab 8.1. Getestet wird jetzt gegen PHP 8.3, 8.4 und 8.5, jeweils mit display_errors=On und error_reporting=E_ALL, damit Deprecations sichtbar werden statt still im Log zu landen. Alle 232 Tests laufen auf allen drei Versionen ohne eine einzige Meldung durch. Zusaetzlich ist das Release-Paket in einer PHP-8.5-Umgebung mit fremdem Webroot geprueft: Ersteinrichtung, Kurzlinks, Weiterleitung, QR-Codes und Statistik, ohne Streuausgabe in Seiten oder Downloads.
101 lines
3.1 KiB
PHP
101 lines
3.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests;
|
|
|
|
use App\Support\Punycode;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class PunycodeTest extends TestCase
|
|
{
|
|
public function testAsciiHostsBleibenUnveraendert(): void
|
|
{
|
|
foreach (['example.com', 'sub.domain.co.uk', 'intranet', 'xn--mller-kva.de', ''] as $host) {
|
|
self::assertSame($host, Punycode::encodeDomain($host));
|
|
}
|
|
}
|
|
|
|
public function testIsAscii(): void
|
|
{
|
|
self::assertTrue(Punycode::isAscii('example.com'));
|
|
self::assertTrue(Punycode::isAscii(''));
|
|
self::assertFalse(Punycode::isAscii('müller.de'));
|
|
self::assertFalse(Punycode::isAscii('日本語.jp'));
|
|
}
|
|
|
|
/**
|
|
* @return array<string,array{0:string,1:string}>
|
|
*/
|
|
public static function domainProvider(): array
|
|
{
|
|
return [
|
|
'muellerde' => ['müller.de', 'xn--mller-kva.de'],
|
|
'buecherde' => ['bücher.de', 'xn--bcher-kva.de'],
|
|
'muenchende' => ['münchen.de', 'xn--mnchen-3ya.de'],
|
|
'nur ein Label' => ['münchen', 'xn--mnchen-3ya'],
|
|
'mehrere Umlaute' => ['grüße.example.com', 'xn--gre-6ka8l.example.com'],
|
|
'Subdomain umlautfrei' => ['www.müller.de', 'www.xn--mller-kva.de'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider domainProvider
|
|
*/
|
|
public function testEncodeDomain(string $unicode, string $expected): void
|
|
{
|
|
self::assertSame($expected, Punycode::encodeDomain($unicode));
|
|
}
|
|
|
|
/**
|
|
* Die eigene Implementierung muss dasselbe Ergebnis liefern wie die
|
|
* Referenz aus ext-intl. Ohne intl entfaellt die Gegenprobe.
|
|
*/
|
|
public function testStimmtMitIntlUeberein(): void
|
|
{
|
|
if (!function_exists('idn_to_ascii')) {
|
|
self::markTestSkipped('ext-intl ist nicht verfuegbar.');
|
|
}
|
|
|
|
$domains = [
|
|
'müller.de',
|
|
'bücher.de',
|
|
'münchen.de',
|
|
'grüße.example.com',
|
|
'www.müller.de',
|
|
'österreich.at',
|
|
'köln.de',
|
|
'schöne-grüße.de',
|
|
'日本語.jp',
|
|
'мосва.рф',
|
|
'ελλάδα.gr',
|
|
];
|
|
|
|
foreach ($domains as $domain) {
|
|
$reference = idn_to_ascii($domain, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46);
|
|
self::assertIsString($reference, $domain);
|
|
|
|
// Die eigene Implementierung direkt aufrufen, nicht den
|
|
// intl-Weg innerhalb von encodeDomain().
|
|
self::assertSame($reference, self::encodeWithoutIntl($domain), $domain);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ruft den Rueckfallweg ueber Reflection auf, damit die Gegenprobe
|
|
* nicht wieder bei idn_to_ascii landet.
|
|
*/
|
|
private static function encodeWithoutIntl(string $host): string
|
|
{
|
|
// Kein setAccessible(): seit PHP 8.1 wirkungslos, seit 8.5 veraltet.
|
|
$method = new \ReflectionMethod(Punycode::class, 'encodeLabel');
|
|
|
|
$labels = array_map(
|
|
static fn (string $label): string => (string) $method->invoke(null, $label),
|
|
explode('.', $host),
|
|
);
|
|
|
|
return implode('.', $labels);
|
|
}
|
|
}
|