diff --git a/CHANGELOG.md b/CHANGELOG.md index 388603fe1..16fa66a34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ Full version-by-version history for Pinakes. The README shows only the latest release; everything older lives here. +## [0.8.8] + +### Fixed +- **The French loan-window sentences include the last day** ([#490](https://github.com/fabiodalez-dev/Pinakes/pull/490)). "retour avant le D" excluded the day the server still accepts as the return date; it now reads "retour au plus tard le D", as the Italian "entro il" does, in both the plural and the singular sentence. The book-club due date says "À rendre au plus tard le D" for the same reason. + +### Tests +- The two database-backed loan-window suites pin the default and maximum loan durations for their run and restore the installation's values afterwards, so they no longer depend on what the environment configures; the capped-loan browser spec restores the title's derived columns that an auto-approved request recomputes. + +No migration, no plugin change. 0.8.7 is still the release that carries the PHP security notice for CVE-2026-109618 and CVE-2026-109619 ([#491](https://github.com/fabiodalez-dev/Pinakes/issues/491)). + ## [0.8.7] ### Security diff --git a/README.md b/README.md index 7a439e9ca..7aa2a9409 100644 --- a/README.md +++ b/README.md @@ -25,17 +25,9 @@ Pinakes is a self-hosted, full-featured ILS for schools, municipalities, and pri [![Documentation](https://img.shields.io/badge/Documentazione-Docsify-4285f4?style=for-the-badge&logo=readthedocs&logoColor=white)](https://fabiodalez-dev.github.io/Pinakes/) [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://buymeacoffee.com/fabiodalez) -## What's New in v0.8.7 +## What's New in v0.8.8 -**A security notice for PHP, loan windows that explain themselves, and Desiderata codes that follow the book.** - -- **PHP security notice** ([#491](https://github.com/fabiodalez-dev/Pinakes/issues/491)): CVE-2026-109618 and CVE-2026-109619 are in the PHP runtime, not in Pinakes: they are fixed upstream in PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35, and some distributions backport the fixes to older version numbers, so check your provider's packages too. The dashboard and the updates page tell administrators when their server's PHP is older, with the interim `session.upload_progress.enabled = 0` (in `php.ini` or the PHP-FPM pool, not `.user.ini`). -- **Loan requests stop before a scheduled loan** ([#490](https://github.com/fabiodalez-dev/Pinakes/pull/490)) and say the longest possible loan, on the book page, the user area, the Mobile API and every admin form. -- **Renewals say until when they are possible** ([#493](https://github.com/fabiodalez-dev/Pinakes/pull/493)) and can renew until that day, also from the Mobile API and NCIP. -- **Desiderata copies get the book's inventory prefix** ([#489](https://github.com/fabiodalez-dev/Pinakes/issues/489), [#494](https://github.com/fabiodalez-dev/Pinakes/pull/494)) instead of `LIB-`. -- **Digital Library**: the "Whoever has the book on loan" visibility now says that authorized users open the file too ([#492](https://github.com/fabiodalez-dev/Pinakes/pull/492)). - -**No migration.** Desiderata 1.2.1 and Mobile API 1.8.2. Full details in **[CHANGELOG.md](CHANGELOG.md)**. +**A French sentence that now includes the last day of a loan.** The loan-window explanation added in 0.8.7 said "retour avant le D" in French, which left out the return day the server still accepts; it now says "retour au plus tard le D" ([#490](https://github.com/fabiodalez-dev/Pinakes/pull/490)). The loan-window test suites no longer depend on the loan durations an environment configures. No migration. If you are upgrading from 0.8.6 or earlier, read the 0.8.7 notes below too: they include a notice about the PHP security release of 21 October 2026. Full details in **[CHANGELOG.md](CHANGELOG.md)**. --- @@ -61,6 +53,10 @@ The showcase contains 50 catalogue books, 8 desiderata, 3 periodicals with 10 is Highlights of the 0.8 releases are below. The full version-by-version history lives in **[CHANGELOG.md](CHANGELOG.md)**. +### v0.8.7 + +**A security notice for PHP, loan windows that explain themselves, and Desiderata codes that follow the book.** CVE-2026-109618 and CVE-2026-109619 are in the PHP runtime, not in Pinakes: the dashboard and the updates page tell administrators when their server's PHP is older than 8.5.12, 8.4.27, 8.3.36 or 8.2.35 ([#491](https://github.com/fabiodalez-dev/Pinakes/issues/491)). Loan requests stop before a scheduled loan and say the longest possible loan ([#490](https://github.com/fabiodalez-dev/Pinakes/pull/490)), renewals say until when they are possible ([#493](https://github.com/fabiodalez-dev/Pinakes/pull/493)), and Desiderata copies get the book's inventory prefix ([#494](https://github.com/fabiodalez-dev/Pinakes/pull/494)). No migration; see **[CHANGELOG.md](CHANGELOG.md)**. + ### v0.8.6 **Who may open each digital file, files out of the web root, and custom roles.** Every Digital Library attachment can be public, for signed-in readers, for authorised staff, or for the current borrower (authorised users open every file too), and every download passes through Pinakes, also from the app with signed links; administrators can create roles with exactly the permissions they need ([#480](https://github.com/fabiodalez-dev/Pinakes/issues/480)). The upgrade heals every older `public/.htaccess`, keeps files uploaded on older releases working, and public pages are much lighter. One additive migration (`migrate_0.8.6-rc.1.sql`); see **[CHANGELOG.md](CHANGELOG.md)**. diff --git a/locale/fr_FR.json b/locale/fr_FR.json index b8fbd16ec..a1744f7b3 100644 --- a/locale/fr_FR.json +++ b/locale/fr_FR.json @@ -6540,7 +6540,7 @@ "Copia offerta al club.": "Exemplaire proposé au club.", "Copia segnata come restituita.": "Exemplaire marqué comme rendu.", "Copie offerte dai membri": "Exemplaires proposés par les membres", - "Da restituire entro il %s": "À rendre avant le %s", + "Da restituire entro il %s": "À rendre au plus tard le %s", "Data di riconsegna (facoltativa)": "Date de retour (facultative)", "Elimina bozza": "Supprimer le brouillon", "Eliminare questa bozza? Le domande andranno perse.": "Supprimer ce brouillon ? Les questions seront perdues.", @@ -8374,11 +8374,11 @@ "Se lo stesso file è allegato anche ad altre schede, vale la visibilità più aperta tra quelle schede.": "Si le même fichier est aussi joint à d’autres notices, la visibilité la plus ouverte parmi ces notices s’applique.", "Gestione allegati digitali comprende anche l’apertura degli allegati riservati: chi può cambiare la visibilità di un file può comunque aprirlo, quindi i due permessi vengono salvati insieme.": "La gestion des pièces jointes numériques comprend aussi l’ouverture des pièces jointes réservées : qui peut modifier la visibilité d’un fichier peut de toute façon l’ouvrir, les deux autorisations sont donc enregistrées ensemble.", "Diritti esistenti": "Droits existants", - "Durata massima del prestito: %1$d giorni (restituzione entro il %2$s). Dal %3$s il libro è già prenotato.": "Durée maximale du prêt : %1$d jours (retour avant le %2$s). À partir du %3$s, le livre est déjà réservé.", + "Durata massima del prestito: %1$d giorni (restituzione entro il %2$s). Dal %3$s il libro è già prenotato.": "Durée maximale du prêt : %1$d jours (retour au plus tard le %2$s). À partir du %3$s, le livre est déjà réservé.", "Giorno non disponibile (%1$s): %2$s": "Jour non disponible (%1$s) : %2$s", "Il periodo richiesto supera la durata massima consentita.": "La période demandée dépasse la durée maximale de prêt autorisée.", "Da questa data di inizio non è possibile alcun prestito: dal %s il libro è già prenotato. Scegli un'altra data.": "Aucun prêt n'est possible à partir de cette date : à partir du %s, le livre est déjà réservé. Choisissez une autre date.", - "Durata massima del prestito: 1 giorno (restituzione entro il %1$s). Dal %2$s il libro è già prenotato.": "Durée maximale du prêt : 1 jour (retour avant le %1$s). À partir du %2$s, le livre est déjà réservé.", + "Durata massima del prestito: 1 giorno (restituzione entro il %1$s). Dal %2$s il libro è già prenotato.": "Durée maximale du prêt : 1 jour (retour au plus tard le %1$s). À partir du %2$s, le livre est déjà réservé.", "Rinnovo non possibile: dal %s il libro è già prenotato.": "Aucune prolongation possible : à partir du %s, le livre est déjà réservé.", "Rinnovo possibile al massimo di 1 giorno (fino al %1$s): dal %2$s il libro è già prenotato.": "Prolongation possible d'au plus 1 jour (jusqu'au %1$s) : à partir du %2$s, le livre est déjà réservé.", "Rinnovo possibile al massimo di %1$d giorni (fino al %2$s): dal %3$s il libro è già prenotato.": "Prolongation possible d'au plus %1$d jours (jusqu'au %2$s) : à partir du %3$s, le livre est déjà réservé.", diff --git a/tests/archives-marc21-xsd.unit.php b/tests/archives-marc21-xsd.unit.php index 263174e0b..2abeb2313 100644 --- a/tests/archives-marc21-xsd.unit.php +++ b/tests/archives-marc21-xsd.unit.php @@ -80,7 +80,6 @@ $ref = new ReflectionClass($plugin); $call = static function (string $method, mixed ...$args) use ($ref, $plugin): mixed { $m = $ref->getMethod($method); - $m->setAccessible(true); return $m->invoke($plugin, ...$args); }; /** Render records inside a MARC Slim . */ diff --git a/tests/archives-plugin.unit.php b/tests/archives-plugin.unit.php index 757fceb39..3c3bdafcd 100644 --- a/tests/archives-plugin.unit.php +++ b/tests/archives-plugin.unit.php @@ -138,7 +138,6 @@ $xw->openMemory(); $xw->setIndent(true); $method = $reflection->getMethod($methodName); - $method->setAccessible(true); $method->invokeArgs($instance, array_merge([$xw], $args)); return $xw->outputMemory(); }; @@ -155,7 +154,6 @@ $check(str_contains($listRecordsSetXml, 'code="noSetHierarchy"'), 'ListRecords rejects set filters with noSetHierarchy'); $buildDc = $reflection->getMethod('buildDublinCoreXml'); -$buildDc->setAccessible(true); $dcXml = (string) $buildDc->invoke($instance, [ 'constructed_title' => 'Test collection', 'reference_code' => 'TEST-DC-001', @@ -168,7 +166,6 @@ $configReflection = new ReflectionClass(\App\Support\ConfigStore::class); $runtimeCache = $configReflection->getProperty('runtimeCache'); -$runtimeCache->setAccessible(true); $previousRuntimeCache = $runtimeCache->getValue(); $runtimeCache->setValue(null, ['app' => ['name' => 'Pinakes Test Repository']]); $dcFallbackXml = (string) $buildDc->invoke($instance, [ @@ -195,9 +192,7 @@ echo "\nOAI-PMH resumptionToken round-trip:\n"; $encode = $reflection->getMethod('encodeOaiResumptionToken'); -$encode->setAccessible(true); $decode = $reflection->getMethod('decodeOaiResumptionToken'); -$decode->setAccessible(true); $token = (string) $encode->invoke($instance, 100, 'ead3', '2026-05-01T00:00:00Z', '2026-05-02T12:34:56Z', 'series'); $decoded = $decode->invoke($instance, $token); $check(is_array($decoded), 'encoded token decodes'); diff --git a/tests/archives-ric-jsonld.unit.php b/tests/archives-ric-jsonld.unit.php index bcc26da87..33eb6484e 100644 --- a/tests/archives-ric-jsonld.unit.php +++ b/tests/archives-ric-jsonld.unit.php @@ -153,7 +153,6 @@ echo "\nURI scheme allow-list:\n"; $ric = new \ReflectionClass(\App\Plugins\Archives\RicJsonLdBuilder::class); $isValid = $ric->getMethod('isValidLodUri'); -$isValid->setAccessible(true); $uriCases = [ // Positive — every scheme in ALLOWED_URI_SCHEMES must pass. @@ -187,7 +186,6 @@ echo "\nxsd:gYear formatting:\n"; $buildDateRange = $ric->getMethod('buildDateRange'); -$buildDateRange->setAccessible(true); // Helper that returns the two formatted gYear strings (begin, end). $gyear = static function (?int $start, ?int $end) use ($builder, $buildDateRange): array { @@ -246,7 +244,6 @@ echo "\nARK identifier sanitiser:\n"; $sanitizeArk = $ric->getMethod('sanitizeArkIdentifier'); -$sanitizeArk->setAccessible(true); $arkCases = [ // Canonical form already prefixed — pass through. @@ -756,7 +753,6 @@ // Unknown CURIE prefix returns empty URI so the writer can fall back. $reflection = new \ReflectionClass($builder); $method = $reflection->getMethod('expandCurie'); -$method->setAccessible(true); $ret = $method->invoke($builder, 'unknownprefix:Foo', []); $check($ret[1] === 'Foo' && $ret[0]['uri'] === '', 'unknown prefix → empty namespace URI (falls back to rdf:Description on emit)'); @@ -766,9 +762,7 @@ // integer / float datatype emission, the typed-reference compact form, // and the `@id === '0'` survival without depending on a database row. $writeProp = $reflection->getMethod('writeRdfProperty'); -$writeProp->setAccessible(true); $writeSubject = $reflection->getMethod('writeRdfSubject'); -$writeSubject->setAccessible(true); $renderProp = static function (string $tag, mixed $value) use ($builder, $writeProp): string { $xw = new \XMLWriter(); diff --git a/tests/audit-fixes-p2-p4.unit.php b/tests/audit-fixes-p2-p4.unit.php index 35626fdd1..d2aaeef68 100644 --- a/tests/audit-fixes-p2-p4.unit.php +++ b/tests/audit-fixes-p2-p4.unit.php @@ -402,7 +402,6 @@ static function () use ($mkLoan, $bookP1, $copyP1, $borrower1, $picker1, $d): ar $notifUser = $mkUser(); [$bookN, [$copyN]] = $mkBook(1); $sendMethod = new \ReflectionMethod(ReservationManager::class, 'sendReservationNotification'); - $sendMethod->setAccessible(true); $manager = new ReservationManager($db); $userRow = $db->query("SELECT email, nome, cognome FROM utenti WHERE id = {$notifUser}")->fetch_assoc(); $mkPayload = static fn (int $resId, int $bookId) => [ diff --git a/tests/backup-generated-columns.unit.php b/tests/backup-generated-columns.unit.php index 2cf23f733..217362a1e 100644 --- a/tests/backup-generated-columns.unit.php +++ b/tests/backup-generated-columns.unit.php @@ -95,7 +95,6 @@ $manager = new \App\Support\BackupManager($db, $root); $method = new \ReflectionMethod(\App\Support\BackupManager::class, 'dumpDatabaseTo'); - $method->setAccessible(true); $method->invoke($manager, $dumpFile); $dump = (string) file_get_contents($dumpFile); diff --git a/tests/backup-retention.unit.php b/tests/backup-retention.unit.php index bfb89cb01..06db3b6f6 100644 --- a/tests/backup-retention.unit.php +++ b/tests/backup-retention.unit.php @@ -41,7 +41,6 @@ */ $prune = static function (BackupManager $manager, string $justWritten): void { $ref = new ReflectionMethod(BackupManager::class, 'pruneOldBackups'); - $ref->setAccessible(true); $ref->invoke($manager, $justWritten); }; @@ -89,7 +88,6 @@ $setRetention($retention); $manager = new BackupManager($db, $root); $prop = new ReflectionProperty(BackupManager::class, 'backupPath'); - $prop->setAccessible(true); $prop->setValue($manager, $dir); return $manager; }; @@ -282,7 +280,6 @@ } $nameFor = new ReflectionMethod(BackupManager::class, 'backupFileName'); -$nameFor->setAccessible(true); $manual = $tmp . '/' . $nameFor->invoke(null, '2025-01-01_000000', BackupManager::ORIGIN_MANUAL); $safety = $tmp . '/' . $nameFor->invoke(null, '2025-01-02_000000', BackupManager::ORIGIN_SAFETY); diff --git a/tests/bookclub-uq-bcbooks-and-isbn-checksum.unit.php b/tests/bookclub-uq-bcbooks-and-isbn-checksum.unit.php index bdea5ec9f..fb7d7430d 100644 --- a/tests/bookclub-uq-bcbooks-and-isbn-checksum.unit.php +++ b/tests/bookclub-uq-bcbooks-and-isbn-checksum.unit.php @@ -94,7 +94,6 @@ echo "A. normalizedIsbnParts validates the checksum\n"; $repo = new Repo($db); $rp = new ReflectionMethod(Repo::class, 'normalizedIsbnParts'); -$rp->setAccessible(true); $parts = static fn(string $isbn): array => $rp->invoke($repo, $isbn); // Sanity-check the fixtures against the validator so the assertions below are @@ -115,7 +114,6 @@ // ── Part B. uq_bcbooks self-heal + duplicate-safe refusal ──────────────────── echo "B. ensureBookclubBooksExternalSupport self-heals uq_bcbooks\n"; $rm = new ReflectionMethod(BookClubPlugin::class, 'ensureBookclubBooksExternalSupport'); -$rm->setAccessible(true); $runMigration = static function () use ($rm, $plugin): void { $rm->invoke($plugin); }; $dupClubId = null; $dupLibroId = null; diff --git a/tests/catalog-materialization.unit.php b/tests/catalog-materialization.unit.php index 349fceb15..20eb81dcc 100644 --- a/tests/catalog-materialization.unit.php +++ b/tests/catalog-materialization.unit.php @@ -62,9 +62,6 @@ $sentinelPathMethod = $projectionClass->getMethod('degradedSentinelPath'); $readDegradedMethod = $projectionClass->getMethod('readDegradedSet'); $clearDegradedMethod = $projectionClass->getMethod('clearDegradedFor'); -$sentinelPathMethod->setAccessible(true); -$readDegradedMethod->setAccessible(true); -$clearDegradedMethod->setAccessible(true); $sentinelPath = (string) $sentinelPathMethod->invoke(null); $sentinelLock = $sentinelPath . '.lock'; $sentinelExisted = is_file($sentinelPath); diff --git a/tests/circulation-sweep-audit.unit.php b/tests/circulation-sweep-audit.unit.php index e83cc66ef..bbbacf917 100644 --- a/tests/circulation-sweep-audit.unit.php +++ b/tests/circulation-sweep-audit.unit.php @@ -355,7 +355,6 @@ foreach (['runtimeCache' => null, 'dbSettingsCache' => null, 'sharedConnection' => null, 'connectionFailed' => false] as $cacheProp => $resetVal) { $rp = new ReflectionProperty(\App\Support\ConfigStore::class, $cacheProp); - $rp->setAccessible(true); $rp->setValue(null, $resetVal); } diff --git a/tests/contributor-roles-237.unit.php b/tests/contributor-roles-237.unit.php index ad079d37d..0a3006317 100644 --- a/tests/contributor-roles-237.unit.php +++ b/tests/contributor-roles-237.unit.php @@ -307,7 +307,6 @@ 'autori_ids' => [$authorId, $relatedCoauthorId], ]); $relatedMethod = new ReflectionMethod(FrontendController::class, 'getRelatedBooks'); - $relatedMethod->setAccessible(true); $relatedRows = $relatedMethod->invoke( new FrontendController(), $db, @@ -336,7 +335,6 @@ $db->query("INSERT INTO libri_autori (libro_id, autore_id, ruolo) VALUES ({$bookId}, {$apiCuratorId}, 'curatore')"); $db->query("INSERT INTO libri_autori (libro_id, autore_id, ruolo) VALUES ({$bookId}, {$apiIllustratorId}, 'illustratore')"); $findBooks = new ReflectionMethod(PublicApiController::class, 'findBooks'); - $findBooks->setAccessible(true); $apiContributorRows = $findBooks->invoke(new PublicApiController(), $db, null, null, null, $realName); $apiContributorBook = array_values(array_filter( is_array($apiContributorRows) ? $apiContributorRows : [], diff --git a/tests/csv-scraping-family-gate-380.unit.php b/tests/csv-scraping-family-gate-380.unit.php index dffd257a0..0e0f287ec 100644 --- a/tests/csv-scraping-family-gate-380.unit.php +++ b/tests/csv-scraping-family-gate-380.unit.php @@ -89,7 +89,6 @@ $controller = (new ReflectionClass(CsvImportController::class))->newInstanceWithoutConstructor(); $enrich = new ReflectionMethod(CsvImportController::class, 'enrichBookWithScrapedData'); -$enrich->setAccessible(true); // CSV row with EMPTY cells for the gated fields (the pre-fix trigger condition). $csvEmpty = [ diff --git a/tests/desiderata-catalogued-on-create.unit.php b/tests/desiderata-catalogued-on-create.unit.php index b96948d1e..91ead9c3c 100644 --- a/tests/desiderata-catalogued-on-create.unit.php +++ b/tests/desiderata-catalogued-on-create.unit.php @@ -164,7 +164,6 @@ function __(string $text, mixed ...$args): string ); $insertBook = new ReflectionMethod(\App\Controllers\CsvImportController::class, 'insertBook'); - $insertBook->setAccessible(true); $importer = (new ReflectionClass(\App\Controllers\CsvImportController::class))->newInstanceWithoutConstructor(); /** @return array{id:int, stamp:?string, wanted:int} */ diff --git a/tests/desiderata-csv-affirmative.unit.php b/tests/desiderata-csv-affirmative.unit.php index d3af05b64..568a8d89a 100644 --- a/tests/desiderata-csv-affirmative.unit.php +++ b/tests/desiderata-csv-affirmative.unit.php @@ -40,7 +40,6 @@ function __(string $text, mixed ...$args): string }; $parse = new ReflectionMethod(\App\Controllers\CsvImportController::class, 'parseCsvRow'); -$parse->setAccessible(true); $controller = (new ReflectionClass(\App\Controllers\CsvImportController::class))->newInstanceWithoutConstructor(); /** What the importer decides for a given cell value. */ @@ -98,7 +97,6 @@ function __(string $text, mixed ...$args): string // with its values, which is indistinguishable from an import that simply had // no such column. Both halves of the comparison must fold multibyte. $map = new ReflectionMethod(\App\Controllers\CsvImportController::class, 'mapColumnHeaders'); -$map->setAccessible(true); $headerCases = [ 'RECHERCHÉ' => 'is_desiderata', diff --git a/tests/desiderata-extended.integration.php b/tests/desiderata-extended.integration.php index 79dc36954..dc727f56e 100644 --- a/tests/desiderata-extended.integration.php +++ b/tests/desiderata-extended.integration.php @@ -639,10 +639,8 @@ public function sendEmail(string $to, string $subject, string $body, string $toN // The class statics the plugin memoises. Left alone, the installation's // plugin id and its libri_editori answer would be reused here. $resolvedId = new ReflectionProperty(DesiderataPlugin::class, 'resolvedPluginId'); - $resolvedId->setAccessible(true); $resolvedId->setValue(null, null); $junction = new ReflectionProperty(DesiderataPlugin::class, 'publisherJunction'); - $junction->setAccessible(true); $junction->setValue(null, null); // The sandbox is EMPTIED, not relieved of a named list. It is shared with @@ -856,10 +854,8 @@ public function sendEmail(string $to, string $subject, string $body, string $toN // Restore the memoised statics for whatever runs next in this process. try { $resolved = new ReflectionProperty(DesiderataPlugin::class, 'resolvedPluginId'); - $resolved->setAccessible(true); $resolved->setValue(null, null); $junctionMemo = new ReflectionProperty(DesiderataPlugin::class, 'publisherJunction'); - $junctionMemo->setAccessible(true); $junctionMemo->setValue(null, null); } catch (\Throwable) { // Reflection on a renamed property must not mask the real failure. diff --git a/tests/desiderata-recaptcha.unit.php b/tests/desiderata-recaptcha.unit.php index aa0433099..2e85cf4b6 100644 --- a/tests/desiderata-recaptcha.unit.php +++ b/tests/desiderata-recaptcha.unit.php @@ -314,7 +314,6 @@ public function sendEmail(string $to, string $subject, string $body, string $toN // cannot be confused with the markup of a view somebody may be restyling. $useSecret($secret); $verify = new ReflectionMethod($plugin, 'verifyRecaptcha'); - $verify->setAccessible(true); $expected = __('Verifica reCAPTCHA fallita. Riprova.'); $unusableTokens = [ 'missing altogether' => [], diff --git a/tests/desiderata-return-path.unit.php b/tests/desiderata-return-path.unit.php index 710177b1a..089b4c99a 100644 --- a/tests/desiderata-return-path.unit.php +++ b/tests/desiderata-return-path.unit.php @@ -46,7 +46,6 @@ function __(string $text, mixed ...$args): string }; $method = new ReflectionMethod(DesiderataPlugin::class, 'returnPath'); -$method->setAccessible(true); $returnPath = static fn(mixed $raw): mixed => $method->invoke(null, $raw); /** Readable rendering of control bytes, so a failure line is diagnosable. */ diff --git a/tests/desiderata-search-facets.unit.php b/tests/desiderata-search-facets.unit.php index 6c07f156e..05e8542b4 100644 --- a/tests/desiderata-search-facets.unit.php +++ b/tests/desiderata-search-facets.unit.php @@ -187,7 +187,6 @@ function __(string $text, mixed ...$args): string $controller = new FrontendController(); $compute = new ReflectionMethod(FrontendController::class, 'computeFilterOptions'); - $compute->setAccessible(true); /** How many books the publisher facet credits to our fixture publisher. */ $facetCount = static function (array $options) use ($MARK): ?int { diff --git a/tests/desiderata-thank-you.unit.php b/tests/desiderata-thank-you.unit.php index 292f08d24..2a79ece88 100644 --- a/tests/desiderata-thank-you.unit.php +++ b/tests/desiderata-thank-you.unit.php @@ -57,7 +57,6 @@ function __(string $text, mixed ...$args): string echo "\nB. The redirect carries the marker, whatever the donor came from\n"; $thankYou = new ReflectionMethod(DesiderataPlugin::class, 'thankYouUrl'); -$thankYou->setAccessible(true); $marker = DesiderataPlugin::THANK_YOU_MARKER; $cases = [ diff --git a/tests/desiderata-visibility.integration.php b/tests/desiderata-visibility.integration.php index e475225ff..59565011e 100644 --- a/tests/desiderata-visibility.integration.php +++ b/tests/desiderata-visibility.integration.php @@ -104,7 +104,6 @@ function __(string $text, mixed ...$args): string /** Call a private/protected method on an instance without changing its visibility. */ $callPrivate = static function (object $object, string $method, array $args = []): mixed { $ref = new ReflectionMethod($object, $method); - $ref->setAccessible(true); return $ref->invokeArgs($object, $args); }; @@ -124,7 +123,6 @@ function __(string $text, mixed ...$args): string if (class_exists($mobileClass)) { $mobile = (new ReflectionClass($mobileClass))->newInstanceWithoutConstructor(); $dbProp = new ReflectionProperty($mobileClass, 'db'); - $dbProp->setAccessible(true); $dbProp->setValue($mobile, $db); $core = $callPrivate($mobile, 'fetchBookCore', [$wantedId]); $check($core === null || $core === [] || $core === false, 'the app cannot open a requested book by id'); diff --git a/tests/emeroteca-export-140.unit.php b/tests/emeroteca-export-140.unit.php index d7217a281..88d1ef2d8 100644 --- a/tests/emeroteca-export-140.unit.php +++ b/tests/emeroteca-export-140.unit.php @@ -698,7 +698,6 @@ function check(bool $cond, string $desc): void // and this is exactly the input that used to destroy the cell — a value // reaching the exporter from a hook, an import or a legacy dump. $oneLine = new \ReflectionMethod(KbartExporter::class, 'oneLine'); - $oneLine->setAccessible(true); $latin1Title = "zz_Latin1 \xE8 rivista"; check( preg_match('//u', $latin1Title) !== 1, @@ -725,7 +724,6 @@ function check(bool $cond, string $desc): void // degraded SQL branch is the one that really runs. $forceProbe = static function (string $class, mysqli $handle, array $tables): void { $prop = new \ReflectionProperty($class, 'tableCache'); - $prop->setAccessible(true); $cache = $prop->getValue() ?? new \WeakMap(); $entries = $cache[$handle] ?? []; foreach ($tables as $table => $exists) { diff --git a/tests/emeroteca-interop-140.unit.php b/tests/emeroteca-interop-140.unit.php index 2bd6e74b4..d55975882 100644 --- a/tests/emeroteca-interop-140.unit.php +++ b/tests/emeroteca-interop-140.unit.php @@ -1060,11 +1060,9 @@ function check(bool $cond, string $desc): void $sruProbe = new \Z39Server\SRUServer($db, $sruSettings); $ref = new \ReflectionClass($sruProbe); $cacheProp = $ref->getProperty('columnProbeCache'); - $cacheProp->setAccessible(true); $cacheProp->setValue($sruProbe, ['libri.issn' => false]); $compile = $ref->getMethod('compileConditionClause'); - $compile->setAccessible(true); $anywhereSql = (string) $compile->invoke($sruProbe, 'cql.anywhere', '=', 'qualunque'); check( !str_contains($anywhereSql, 'l.issn'), @@ -1090,7 +1088,6 @@ function check(bool $cond, string $desc): void // render it as `a AND NOT b` — never as the non-SQL `a NOT b` — and must // still refuse an operator it cannot render. $serialBool = $ref->getMethod('buildSerialWhereClause'); - $serialBool->setAccessible(true); $notNode = [ 'type' => 'boolean', 'operator' => 'NOT', diff --git a/tests/emeroteca-schema-140.unit.php b/tests/emeroteca-schema-140.unit.php index b93310b96..2024a9102 100644 --- a/tests/emeroteca-schema-140.unit.php +++ b/tests/emeroteca-schema-140.unit.php @@ -367,7 +367,6 @@ function note(string $msg): void // mensole, re-attached by the upgrade. Reading the list from the plugin means a // core FK added later is covered here without editing this file. $coreFkDefs = new \ReflectionMethod(EmerotecaPlugin::class, 'coreForeignKeyDefs'); -$coreFkDefs->setAccessible(true); /** @var list $coreFkList */ $coreFkList = $coreFkDefs->invoke(null); foreach (array_unique(array_column($coreFkList, 'ref_table')) as $coreTable) { @@ -428,7 +427,6 @@ function note(string $msg): void /** @return array> table => (column => DDL fragment) */ $additiveDefs = static function (): array { $m = new \ReflectionMethod(EmerotecaPlugin::class, 'additiveColumnDefs'); - $m->setAccessible(true); /** @var array> $defs */ $defs = $m->invoke(null); return $defs; diff --git a/tests/helpers/branch-fix-harness.php b/tests/helpers/branch-fix-harness.php index 296069ad4..d2d61d9dd 100644 --- a/tests/helpers/branch-fix-harness.php +++ b/tests/helpers/branch-fix-harness.php @@ -253,11 +253,8 @@ function getLibraryThingMethods(): array { $controller = new LibraryThingImportController(); $parse = new ReflectionMethod($controller, 'parseLibraryThingRow'); - $parse->setAccessible(true); $insert = new ReflectionMethod($controller, 'insertBook'); - $insert->setAccessible(true); $update = new ReflectionMethod($controller, 'updateBook'); - $update->setAccessible(true); return [$controller, $parse, $insert, $update]; } @@ -492,9 +489,7 @@ function scenarioLibraryThingExportTranslatorRoundtrip(mysqli $db): array { [$controller, $parse] = getLibraryThingMethods(); $format = new ReflectionMethod($controller, 'formatLibraryThingRow'); - $format->setAccessible(true); $headersMethod = new ReflectionMethod($controller, 'getLibraryThingHeaders'); - $headersMethod->setAccessible(true); $row = $format->invoke($controller, [ 'titolo' => 'Roundtrip Test', @@ -530,9 +525,7 @@ function scenarioLibraryThingSecondaryAuthorRolePairing(mysqli $db): array { [$controller, $parse] = getLibraryThingMethods(); $format = new ReflectionMethod($controller, 'formatLibraryThingRow'); - $format->setAccessible(true); $headersMethod = new ReflectionMethod($controller, 'getLibraryThingHeaders'); - $headersMethod->setAccessible(true); $row = $format->invoke($controller, [ 'titolo' => 'Roundtrip Pairing Test', diff --git a/tests/import-upc-normalization-348.unit.php b/tests/import-upc-normalization-348.unit.php index 3022eb4fc..56784245a 100644 --- a/tests/import-upc-normalization-348.unit.php +++ b/tests/import-upc-normalization-348.unit.php @@ -22,7 +22,6 @@ $controller = (new ReflectionClass(CsvImportController::class))->newInstanceWithoutConstructor(); $method = new ReflectionMethod(CsvImportController::class, 'normalizeEan'); -$method->setAccessible(true); /** @return string|null */ $normalize = static fn (string $ean) => $method->invoke($controller, $ean); diff --git a/tests/language-derived-stats.unit.php b/tests/language-derived-stats.unit.php index ae12fe736..5bdd7a790 100644 --- a/tests/language-derived-stats.unit.php +++ b/tests/language-derived-stats.unit.php @@ -78,7 +78,6 @@ // Reflection handle for the private translatedKeyCount(). $ref = new ReflectionMethod(Language::class, 'translatedKeyCount'); -$ref->setAccessible(true); $translated = static fn (string $code, int $total): int => $ref->invoke($lang, $code, $total); echo "A. sourceKeyCount()\n"; @@ -141,7 +140,6 @@ $check($tmpTotal === 5, "injected source dir drives the denominator ({$tmpTotal} == 5)"); $refTmp = new ReflectionMethod(Language::class, 'translatedKeyCount'); - $refTmp->setAccessible(true); $tr = static fn (string $code): int => $refTmp->invoke($langTmp, $code, $tmpTotal); // 3 canonical translations + 2 empty canonical values → 3 translated. diff --git a/tests/librarything-search-index-conflicts.unit.php b/tests/librarything-search-index-conflicts.unit.php index ccd30f192..433b1bac9 100644 --- a/tests/librarything-search-index-conflicts.unit.php +++ b/tests/librarything-search-index-conflicts.unit.php @@ -113,7 +113,6 @@ function check(bool $cond, string $desc): void $controller = new LibraryThingImportController(); $upsert = new ReflectionMethod($controller, 'upsertBook'); -$upsert->setAccessible(true); $result = $upsert->invoke($controller, $db, [ 'id' => $targetId, 'titolo' => $targetTitle, diff --git a/tests/loan-auto-approval-301-reservation-path.unit.php b/tests/loan-auto-approval-301-reservation-path.unit.php index 68d5fc3ef..a1ac035b9 100644 --- a/tests/loan-auto-approval-301-reservation-path.unit.php +++ b/tests/loan-auto-approval-301-reservation-path.unit.php @@ -758,7 +758,6 @@ try { $faultController = new ReservationsController($deadDb); $faultMethod = new ReflectionMethod($faultController, 'autoApproveLoanRequest'); - $faultMethod->setAccessible(true); $faultResult = $faultMethod->invoke($faultController, $faultRequest, 12345); } catch (\Throwable $e) { $faultThrew = true; diff --git a/tests/loan-auto-approval-301.unit.php b/tests/loan-auto-approval-301.unit.php index 60c3be261..5e23170a4 100644 --- a/tests/loan-auto-approval-301.unit.php +++ b/tests/loan-auto-approval-301.unit.php @@ -177,7 +177,6 @@ // the test drives the exact code path the loan() request handler triggers. $controller = new UserActionsController(); $autoMethod = new ReflectionMethod($controller, 'autoApproveLoanRequest'); -$autoMethod->setAccessible(true); $autoApprove = static function (int $loanId) use ($controller, $autoMethod, $db): bool { $request = (new ServerRequestFactory())->createServerRequest('POST', '/prestito'); return (bool) $autoMethod->invoke($controller, $request, $db, $loanId); diff --git a/tests/loan-recall-360-behavior.unit.php b/tests/loan-recall-360-behavior.unit.php index c15d24754..e278f1dc8 100644 --- a/tests/loan-recall-360-behavior.unit.php +++ b/tests/loan-recall-360-behavior.unit.php @@ -60,7 +60,6 @@ // in the CI unit job). Inject the test connection so set()/get() are deterministic // against this database on every runner — same reflection seam archives-plugin uses. $sharedConn = new ReflectionProperty(ConfigStore::class, 'sharedConnection'); -$sharedConn->setAccessible(true); $sharedConn->setValue(null, $db); // The recall columns are self-healed at runtime by the app; ensure them here so diff --git a/tests/loan-request-capped-by-scheduled-loan.spec.js b/tests/loan-request-capped-by-scheduled-loan.spec.js index e6281f426..d75914fd7 100644 --- a/tests/loan-request-capped-by-scheduled-loan.spec.js +++ b/tests/loan-request-capped-by-scheduled-loan.spec.js @@ -84,6 +84,7 @@ test.describe('Book page: loan request capped by a scheduled loan', () => { let copyId = 0; let seededLoanId = 0; let borrowerId = 0; + let bookSnapshot = ''; let today = ''; const RUN = Date.now().toString(36).slice(-6); const BORROWER_EMAIL = `lw-cap-${RUN}@example.invalid`; @@ -111,6 +112,10 @@ test.describe('Book page: loan request capped by a scheduled loan', () => { ); if (!bookId) return; copyId = parseInt(dbQuery(`SELECT id FROM copie WHERE libro_id=${bookId} LIMIT 1`) || '0', 10); + // The request may be auto-approved (loans.auto_approve_requests), which + // recomputes the title's derived columns: restore them afterwards. + // '|' and not a tab: mysql -B writes a tab inside a value as the two characters \t. + bookSnapshot = dbQuery(`SELECT CONCAT(IFNULL(copie_disponibili,'NULL'),'|',IFNULL(stato,'')) FROM libri WHERE id=${bookId}`); // The application's clock (DateHelper::today(): app.timezone, default // Europe/Rome), not the database session's: near midnight they differ. const tz = dbQuery("SELECT setting_value FROM system_settings WHERE category='app' AND setting_key='timezone' LIMIT 1") || 'Europe/Rome'; @@ -142,6 +147,11 @@ test.describe('Book page: loan request capped by a scheduled loan', () => { dbQuery(`DELETE FROM prestiti WHERE id=${seededLoanId}`); dbQuery(`DELETE FROM prestiti WHERE libro_id=${bookId} AND utente_id=(SELECT id FROM utenti WHERE email='${ADMIN_EMAIL.replace(/'/g, "''")}' LIMIT 1) AND data_prestito>='${today}'`); dbQuery(`UPDATE copie SET stato='disponibile' WHERE id=${copyId}`); + const [avail, stato] = bookSnapshot.split('|'); + if (avail !== undefined && stato !== undefined) { + const availSql = avail === 'NULL' ? 'NULL' : String(parseInt(avail, 10) || 0); + dbQuery(`UPDATE libri SET copie_disponibili=${availSql}, stato='${stato.replace(/'/g, "''")}' WHERE id=${bookId}`); + } } if (borrowerId) { dbQuery(`DELETE FROM prestiti WHERE utente_id=${borrowerId}`); diff --git a/tests/loan-window-cap.unit.php b/tests/loan-window-cap.unit.php index 6b50b60dd..c63f96ef1 100644 --- a/tests/loan-window-cap.unit.php +++ b/tests/loan-window-cap.unit.php @@ -66,7 +66,17 @@ $bookIds = []; /** @var int[] $userIds */ $userIds = []; -$cleanup = static function () use ($db, &$bookIds, &$userIds): void { +/** @var array $savedLoanSettings */ +$savedLoanSettings = []; +$cleanup = static function () use ($db, &$bookIds, &$userIds, &$savedLoanSettings): void { + $settings = new SettingsRepository($db); + foreach ($savedLoanSettings as $key => $value) { + if ($value === null) { + $settings->delete('loans', $key); + } else { + $settings->set('loans', $key, $value); + } + } foreach ($bookIds as $id) { $db->query("DELETE FROM prestiti WHERE libro_id = {$id}"); $db->query("DELETE FROM prenotazioni WHERE libro_id = {$id}"); @@ -138,6 +148,13 @@ }; $settings = new SettingsRepository($db); +// The scenario needs a default longer than the conflict offset: pin the two +// durations for the run (the installation's values are restored by $cleanup). +foreach (['loan_duration_days', 'max_loan_duration_days'] as $key) { + $savedLoanSettings[$key] = $settings->get('loans', $key); +} +$settings->set('loans', 'loan_duration_days', '30'); +$settings->set('loans', 'max_loan_duration_days', '90'); $defaultDays = $settings->loanDurationDays(); $maxDays = max(1, (int) ($settings->get('loans', 'max_loan_duration_days', '90') ?? 90)); $defaultDays = min($defaultDays, $maxDays); diff --git a/tests/loan-window-surfaces.unit.php b/tests/loan-window-surfaces.unit.php index 3753fc00b..9d470fe20 100644 --- a/tests/loan-window-surfaces.unit.php +++ b/tests/loan-window-surfaces.unit.php @@ -170,6 +170,13 @@ }; $settings = new SettingsRepository($db); +// The scenario needs a default longer than the conflict offset: pin the two +// durations for the run (the installation's values are restored by $cleanup). +foreach (['loan_duration_days', 'max_loan_duration_days'] as $key) { + $savedLoanSettings[$key] = $settings->get('loans', $key); +} +$settings->set('loans', 'loan_duration_days', '30'); +$settings->set('loans', 'max_loan_duration_days', '90'); $maxDays = max(1, (int) ($settings->get('loans', 'max_loan_duration_days', '90') ?? 90)); $defaultDays = min($settings->loanDurationDays(), $maxDays); $offset = max(2, min(5, $defaultDays - 1)); @@ -405,9 +412,7 @@ // A lower configured maximum must govern every default, including Mobile API // requests without end_date. A commitment at +10 must not reject a valid 7-day // request just because the configured standard duration is 30 days. -foreach (['loan_duration_days', 'max_loan_duration_days'] as $key) { - $savedLoanSettings[$key] = $settings->get('loans', $key); -} +// The installation's values were saved at the top; $cleanup restores them. $settings->set('loans', 'loan_duration_days', '30'); $settings->set('loans', 'max_loan_duration_days', '7'); [$boundedBook, $boundedCopies] = $mkBook(1, 3); diff --git a/tests/locale-override-layer.unit.php b/tests/locale-override-layer.unit.php index 4b9ef9edc..ccb99cab4 100644 --- a/tests/locale-override-layer.unit.php +++ b/tests/locale-override-layer.unit.php @@ -50,7 +50,6 @@ $ref = new ReflectionClass(I18n::class); $setStatic = static function (string $prop, $value) use ($ref): void { $p = $ref->getProperty($prop); - $p->setAccessible(true); $p->setValue(null, $value); }; $reload = static function () use ($setStatic, $code): void { diff --git a/tests/mvcc-lockfirst-circulation.unit.php b/tests/mvcc-lockfirst-circulation.unit.php index 13c717bd3..f8a75c217 100644 --- a/tests/mvcc-lockfirst-circulation.unit.php +++ b/tests/mvcc-lockfirst-circulation.unit.php @@ -187,7 +187,6 @@ function check(bool $cond, string $desc): void $deferredNotifications = static function (ReservationManager $rm): array { $prop = new \ReflectionProperty(ReservationManager::class, 'deferredReservationNotifications'); - $prop->setAccessible(true); return (array) $prop->getValue($rm); }; diff --git a/tests/plugin-goodreads-cover.unit.php b/tests/plugin-goodreads-cover.unit.php index cbbce9397..366db4d74 100644 --- a/tests/plugin-goodreads-cover.unit.php +++ b/tests/plugin-goodreads-cover.unit.php @@ -31,9 +31,7 @@ $plugin = new OpenLibraryPlugin(); $extractM = new ReflectionMethod($plugin, 'extractGoodreadsCoverFromHtml'); -$extractM->setAccessible(true); $coverM = new ReflectionMethod($plugin, 'getGoodreadsCover'); -$coverM->setAccessible(true); $extract = static fn (string $html) => $extractM->invoke($plugin, $html); $cover = static fn (string $isbn) => $coverM->invoke($plugin, $isbn); diff --git a/tests/plugin-orphan-hook-selfheal.unit.php b/tests/plugin-orphan-hook-selfheal.unit.php index 6e36acf6a..5617b49aa 100644 --- a/tests/plugin-orphan-hook-selfheal.unit.php +++ b/tests/plugin-orphan-hook-selfheal.unit.php @@ -124,7 +124,6 @@ $pm = new PluginManager($db, new HookManager($db)); $disable = new ReflectionMethod($pm, 'disableOrphanHook'); -$disable->setAccessible(true); echo "disableOrphanHook — correctness, reversibility, idempotency, precision\n"; $check($loaderWouldLoad($orphanHook, 'zzMissingMethod'), '01 orphan seeded active (loader would load it)'); diff --git a/tests/pr166-167-coverage.spec.js b/tests/pr166-167-coverage.spec.js index 0d5e07638..39f11f874 100644 --- a/tests/pr166-167-coverage.spec.js +++ b/tests/pr166-167-coverage.spec.js @@ -886,7 +886,6 @@ $trigsBefore = (int)$db->query('SELECT COUNT(*) FROM information_schema.TRIGGER // (d) Call importViaPhp via ReflectionMethod on the SAME DB (content-identical => safe). $rm = new ReflectionMethod(\\App\\Support\\BackupManager::class, 'importViaPhp'); -$rm->setAccessible(true); try { $rm->invoke($bm, $tmpSql); } catch (\\Throwable $e) { @@ -994,7 +993,6 @@ $db->set_charset('utf8mb4'); $bm = new \\App\\Support\\BackupManager($db, $root); $rm = new ReflectionMethod(\\App\\Support\\BackupManager::class, 'cnfEscape'); -$rm->setAccessible(true); // Rule 1: single quote passes through unchanged — "pa'ss" => "pa'ss" $r1 = $rm->invoke($bm, "pa'ss"); diff --git a/tests/scrape-edition-editor-alignment.unit.php b/tests/scrape-edition-editor-alignment.unit.php index 33648dba0..f659fb239 100644 --- a/tests/scrape-edition-editor-alignment.unit.php +++ b/tests/scrape-edition-editor-alignment.unit.php @@ -37,7 +37,6 @@ function check(bool $ok, string $label): void function call(object $obj, string $method, mixed ...$args): mixed { $m = new ReflectionMethod($obj, $method); - $m->setAccessible(true); return $m->invoke($obj, ...$args); } function xpathOf(string $xml): DOMXPath diff --git a/tests/security-scan-findings.unit.php b/tests/security-scan-findings.unit.php index 42843ba31..0fdb2bf50 100644 --- a/tests/security-scan-findings.unit.php +++ b/tests/security-scan-findings.unit.php @@ -33,7 +33,6 @@ $invoke = static function (object $obj, string $method, ...$args) { $r = new ReflectionMethod($obj, $method); - $r->setAccessible(true); return $r->invoke($obj, ...$args); }; diff --git a/tests/sessionless-anonymous-387.unit.php b/tests/sessionless-anonymous-387.unit.php index 7e647df91..e181221b2 100644 --- a/tests/sessionless-anonymous-387.unit.php +++ b/tests/sessionless-anonymous-387.unit.php @@ -229,7 +229,6 @@ public function handle(ServerRequestInterface $request): ResponseInterface // Force advanced.private_mode=1 through ConfigStore's runtime cache (no DB). $configRef = new ReflectionClass(\App\Support\ConfigStore::class); $runtimeCacheProp = $configRef->getProperty('runtimeCache'); -$runtimeCacheProp->setAccessible(true); $previousRuntimeCache = $runtimeCacheProp->getValue(); $runtimeCacheProp->setValue(null, ['advanced' => ['private_mode' => '1']]); diff --git a/tests/update-pending-package.unit.php b/tests/update-pending-package.unit.php index f9b63893d..b79b75ca5 100644 --- a/tests/update-pending-package.unit.php +++ b/tests/update-pending-package.unit.php @@ -63,7 +63,6 @@ function __(string $s): string { return $s; } $updater = new App\Support\Updater(new mysqli()); $db = new mysqli(); $hold = new ReflectionMethod($controller, 'holdPendingPackage'); - $hold->setAccessible(true); $tmp = dirname(__DIR__) . '/storage/tmp'; if (!is_dir($tmp)) { mkdir($tmp, 0775, true); } diff --git a/tests/update-space-preflight.unit.php b/tests/update-space-preflight.unit.php index 02ae45726..9ca24906c 100644 --- a/tests/update-space-preflight.unit.php +++ b/tests/update-space-preflight.unit.php @@ -49,7 +49,6 @@ $call = static function (Updater $u, string $method, array $args = []) { $ref = new ReflectionMethod(Updater::class, $method); - $ref->setAccessible(true); return $ref->invoke($u, ...$args); }; @@ -157,7 +156,6 @@ $bareRef = new ReflectionClass(Updater::class); $bare = $bareRef->newInstanceWithoutConstructor(); $rootProp = $bareRef->getProperty('rootPath'); -$rootProp->setAccessible(true); $blockedRoot = sys_get_temp_dir() . '/zz_preflight_blocked_' . bin2hex(random_bytes(4)); mkdir($blockedRoot . '/storage', 0777, true); file_put_contents($blockedRoot . '/storage/tmp', 'not a directory'); diff --git a/tests/update-status-450.unit.php b/tests/update-status-450.unit.php index 55bc3d7e4..9e8e22adb 100644 --- a/tests/update-status-450.unit.php +++ b/tests/update-status-450.unit.php @@ -122,7 +122,6 @@ $saved = is_file($outcomeFile) ? file_get_contents($outcomeFile) : null; try { $record = new ReflectionMethod(Updater::class, 'recordUpdateOutcome'); - $record->setAccessible(true); $record->invoke($updater, ['success' => false, 'error' => 'no space ' . $marker]); $outcome = $updater->lastUpdateOutcome(); $check(($outcome['success'] ?? null) === false && ($outcome['error'] ?? '') === 'no space ' . $marker, 'a failed run is read back with its error'); @@ -166,9 +165,7 @@ try { @unlink($attemptsFile); $logStart = new ReflectionMethod(Updater::class, 'logUpdateStart'); - $logStart->setAccessible(true); $record = new ReflectionMethod(Updater::class, 'recordUpdateOutcome'); - $record->setAccessible(true); $mine = bin2hex(random_bytes(16)); $other = bin2hex(random_bytes(16)); diff --git a/tests/updater-custom-locale.unit.php b/tests/updater-custom-locale.unit.php index 6596f0894..d121fe202 100644 --- a/tests/updater-custom-locale.unit.php +++ b/tests/updater-custom-locale.unit.php @@ -34,7 +34,6 @@ function check(bool $cond, string $desc): void $ref = new \ReflectionClass(\App\Support\Updater::class); $updater = $ref->newInstanceWithoutConstructor(); $method = $ref->getMethod('isCustomLocalePath'); -$method->setAccessible(true); $isCustom = static fn(string $path): bool => (bool) $method->invoke($updater, $path); // --- Bundled locales must NOT be treated as custom (they keep updating). --- diff --git a/tests/updater-fatal-json.unit.php b/tests/updater-fatal-json.unit.php index 13cd38bf8..04700b782 100644 --- a/tests/updater-fatal-json.unit.php +++ b/tests/updater-fatal-json.unit.php @@ -27,7 +27,6 @@ function check(bool $ok, string $label): void if (!function_exists("__")) { function __(string $s): string { return $s; } } $controller = (new ReflectionClass(App\Controllers\UpdateController::class))->newInstanceWithoutConstructor(); $guard = new ReflectionMethod($controller, "answerJsonOnFatal"); -$guard->setAccessible(true); $guard->invoke($controller); ob_start(); echo "partial page"; diff --git a/tests/updater-hardening.unit.php b/tests/updater-hardening.unit.php index 4d387f2a1..ad4c7a525 100644 --- a/tests/updater-hardening.unit.php +++ b/tests/updater-hardening.unit.php @@ -32,7 +32,6 @@ $ref = new ReflectionClass(\App\Support\Updater::class); $updater = $ref->newInstanceWithoutConstructor(); // no DB needed for a pure method $isApiUrl = $ref->getMethod('isApiUrl'); -$isApiUrl->setAccessible(true); $call = static fn(string $url): bool => (bool) $isApiUrl->invoke($updater, $url); // 1. The API host is the only one that may carry the token. @@ -61,7 +60,6 @@ // --------------------------------------------------------------------------- $isValid = $ref->getMethod('isValidSha256'); -$isValid->setAccessible(true); $sha = static fn(string $h): bool => (bool) $isValid->invoke($updater, $h); $check($sha(str_repeat('a', 64)) === true, '64 lowercase hex => true'); @@ -97,7 +95,6 @@ protected function downloadPatchFile(string $url): ?string }; }; $fvMethod = new ReflectionMethod(\App\Support\Updater::class, 'fetchVerifiedReleaseAsset'); -$fvMethod->setAccessible(true); $fetch = static function (\App\Support\Updater $u, string $asset) use ($fvMethod) { return $fvMethod->invoke($u, '1.0.0', $asset); }; diff --git a/tests/updater-rc-channel.unit.php b/tests/updater-rc-channel.unit.php index f634e33db..a888a7c54 100644 --- a/tests/updater-rc-channel.unit.php +++ b/tests/updater-rc-channel.unit.php @@ -34,7 +34,6 @@ $invoke = static function (string $method, array $args) use ($ref, $updater) { $m = $ref->getMethod($method); - $m->setAccessible(true); return $m->invokeArgs($updater, $args); }; diff --git a/tests/updater-truncated-download.unit.php b/tests/updater-truncated-download.unit.php index d4d700d13..aa1a79645 100644 --- a/tests/updater-truncated-download.unit.php +++ b/tests/updater-truncated-download.unit.php @@ -63,9 +63,7 @@ function __(string $s): string { return $s; } }; $streamToFile = new ReflectionMethod($updater, 'streamToFile'); -$streamToFile->setAccessible(true); $streamWithPhp = new ReflectionMethod($updater, 'streamWithPhp'); -$streamWithPhp->setAccessible(true); $paths = [ 'cURL' => static fn(string $url, string $path) => $streamToFile->invoke($updater, $url, $path, false), diff --git a/tests/viaf-authority.unit.php b/tests/viaf-authority.unit.php index 528c6abb8..cd8a2de4b 100644 --- a/tests/viaf-authority.unit.php +++ b/tests/viaf-authority.unit.php @@ -81,7 +81,6 @@ $plugin = new ViafAuthorityPlugin($db, new \App\Support\HookManager($db)); $gate = new ReflectionMethod($plugin, 'requireAdminOrStaff'); -$gate->setAccessible(true); $ip = '198.51.100.' . random_int(1, 254); $rlId = 'viaf_basic:' . $ip; diff --git a/version.json b/version.json index f59714267..d5d10b756 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { "name": "Pinakes", - "version": "0.8.7", + "version": "0.8.8", "description": "Library Management System - Sistema di Gestione Bibliotecaria" }