diff --git a/CHANGELOG.md b/CHANGELOG.md index e7541ec241..51bfce4e77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This is a log of major user-visible changes in each phpMyFAQ release. ### phpMyFAQ v4.0.0-dev - 2023- +- changed PHP requirement to PHP 8.2.0 or later (Thorsten) - changed rewrite rules for Apache and nginx as mandatory requirement (Thorsten) - changed folder structure (Thorsten, Jan Harms) - added experimental support for PHP 8.3 (Thorsten) diff --git a/phpmyfaq/admin/upgrade.php b/phpmyfaq/admin/upgrade.php index 95f9fecb73..cea115e246 100644 --- a/phpmyfaq/admin/upgrade.php +++ b/phpmyfaq/admin/upgrade.php @@ -45,7 +45,7 @@ 'buttonCheckUpdates' => Translation::get('buttonCheckUpdates'), 'headerDownloadPackage' => Translation::get('headerDownloadPackage'), 'isOnNightlies' => $faqConfig->get('upgrade.releaseEnvironment') === ReleaseType::NIGHTLY->value, - 'releaseEnvironment' => ucfirst($faqConfig->get('upgrade.releaseEnvironment')), + 'releaseEnvironment' => ucfirst((string) $faqConfig->get('upgrade.releaseEnvironment')), 'dateLastChecked' => $faqConfig->get('upgrade.dateLastChecked') ]; diff --git a/phpmyfaq/api.php b/phpmyfaq/api.php index 0bbf9d2fa6..7d7d1bb9a5 100644 --- a/phpmyfaq/api.php +++ b/phpmyfaq/api.php @@ -359,7 +359,7 @@ $categoryId = $categoryIdFound; } - if ($faq->hasTitleAHash($question) { + if ($faq->hasTitleAHash($question)) { $response->setStatusCode(400); $result = [ 'stored' => false, diff --git a/phpmyfaq/assets/src/api/autocomplete.js b/phpmyfaq/assets/src/api/autocomplete.js index 9f1e03af6d..d093f7c005 100644 --- a/phpmyfaq/assets/src/api/autocomplete.js +++ b/phpmyfaq/assets/src/api/autocomplete.js @@ -8,7 +8,7 @@ * @package phpMyFAQ * @author Thorsten Rinne * @copyright 2014-2023 phpMyFAQ Team - * @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 + * @license https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 * @link https://www.phpmyfaq.de * @since 2014-11-23 */ diff --git a/phpmyfaq/assets/src/api/bookmarks.js b/phpmyfaq/assets/src/api/bookmarks.js index 0ba15f8b78..ab24612cea 100644 --- a/phpmyfaq/assets/src/api/bookmarks.js +++ b/phpmyfaq/assets/src/api/bookmarks.js @@ -8,7 +8,7 @@ * @package phpMyFAQ * @author Thorsten Rinne * @copyright 2023 phpMyFAQ Team - * @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 + * @license https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 * @link https://www.phpmyfaq.de * @since 2023-09-19 */ diff --git a/phpmyfaq/assets/src/api/forms.js b/phpmyfaq/assets/src/api/forms.js index d1679597be..35aa265cec 100644 --- a/phpmyfaq/assets/src/api/forms.js +++ b/phpmyfaq/assets/src/api/forms.js @@ -8,7 +8,7 @@ * @package phpMyFAQ * @author Thorsten Rinne * @copyright 2023 phpMyFAQ Team - * @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 + * @license https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 * @link https://www.phpmyfaq.de * @since 2023-01-06 */ diff --git a/phpmyfaq/assets/src/api/index.js b/phpmyfaq/assets/src/api/index.js index f266ddd280..65bb508046 100644 --- a/phpmyfaq/assets/src/api/index.js +++ b/phpmyfaq/assets/src/api/index.js @@ -1,3 +1,4 @@ -export * from './forms'; -export * from './bookmarks'; export * from './autocomplete'; +export * from './bookmarks'; +export * from './forms'; +export * from './setup'; diff --git a/phpmyfaq/assets/src/api/setup.js b/phpmyfaq/assets/src/api/setup.js new file mode 100644 index 0000000000..dc7a451812 --- /dev/null +++ b/phpmyfaq/assets/src/api/setup.js @@ -0,0 +1,16 @@ +/** + * Setup API functionality + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + * + * @package phpMyFAQ + * @author Thorsten Rinne + * @copyright 2023 phpMyFAQ Team + * @license https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 + * @link https://www.phpmyfaq.de + * @since 2023-10-18 + */ + +export const handleSetup = () => {}; diff --git a/phpmyfaq/setup/index.php b/phpmyfaq/setup/index.php index cda41dbedb..9edebd859e 100644 --- a/phpmyfaq/setup/index.php +++ b/phpmyfaq/setup/index.php @@ -22,11 +22,11 @@ */ use Composer\Autoload\ClassLoader; -use phpMyFAQ\Core\Exception; +use phpMyFAQ\Component\Alert;use phpMyFAQ\Core\Exception; use phpMyFAQ\Language\LanguageCodes; use phpMyFAQ\Setup\Installer; use phpMyFAQ\Strings; -use phpMyFAQ\System; +use phpMyFAQ\System;use phpMyFAQ\Translation; define('PMF_ROOT_DIR', dirname(__FILE__, 2)); const PMF_SRC_DIR = PMF_ROOT_DIR . '/src'; @@ -109,7 +109,8 @@ class="needs-validation" novalidate>

phpMyFAQ

- Did you already read our documentation + Did you already read our + documentation carefully before starting the phpMyFAQ setup?

@@ -127,9 +128,30 @@ class="needs-validation" novalidate> // Initialize static string wrapper // Strings::init(); + +// +// Set translation class +// +try { + Translation::create() + ->setLanguagesDir(PMF_LANGUAGE_DIR) + ->setDefaultLanguage('en') + ->setCurrentLanguage('en') + ->setMultiByteLanguage(); +} catch (Exception $e) { + echo 'Error: ' . $e->getMessage(); +} + $system = new System(); $installer = new Installer($system); -$installer->checkBasicStuff(); + +try { + $installer->checkBasicStuff(); +} catch (Exception $e) { + echo Alert::danger('ad_entryins_fail', $e->getMessage()); + System::renderFooter(); +} + $installer->checkFilesystemPermissions(); // not yet POSTed diff --git a/phpmyfaq/setup/update.php b/phpmyfaq/setup/update.php index fa565f0b4d..6508a0b314 100644 --- a/phpmyfaq/setup/update.php +++ b/phpmyfaq/setup/update.php @@ -48,10 +48,9 @@ $version = Filter::filterInput(INPUT_POST, 'version', FILTER_SANITIZE_SPECIAL_CHARS); $query = []; -if ( - !file_exists(PMF_ROOT_DIR . '/config/database.php') && - !file_exists(PMF_ROOT_DIR . '/content/core/config/database.php') -) { +$update = new Update(new System(), Configuration::getConfigurationInstance()); + +if (!$update->checkDatabaseFile()) { $redirect = new RedirectResponse('./index.php'); $redirect->send(); } @@ -108,7 +107,7 @@

phpMyFAQ

- Did you already read our documentation + Did you already read our documentation carefully before starting the phpMyFAQ setup?

@@ -125,11 +124,14 @@ $faqConfig = Configuration::getConfigurationInstance(); $version = $faqConfig->getVersion(); -$installer = new Installer($system); $update = new Update($system, $faqConfig); $update->setVersion(System::getVersion()); -$installer->checkPreUpgrade($dbConfig->getType()); +try { + $update->checkPreUpgrade($dbConfig->getType()); +} catch (Exception $e) { + echo Alert::danger('ad_entryins_fail', $e->getMessage()); +} if ($update->isConfigTableAvailable($faqConfig->getDb())) { echo Alert::danger('ad_entryins_fail'); diff --git a/phpmyfaq/src/phpMyFAQ/Controller/SetupController.php b/phpmyfaq/src/phpMyFAQ/Controller/SetupController.php index 7b530a9ec9..6060504170 100644 --- a/phpmyfaq/src/phpMyFAQ/Controller/SetupController.php +++ b/phpmyfaq/src/phpMyFAQ/Controller/SetupController.php @@ -19,29 +19,20 @@ use phpMyFAQ\Configuration; use phpMyFAQ\Core\Exception; -use phpMyFAQ\Filter; use phpMyFAQ\Setup\Update; use phpMyFAQ\System; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\StreamedResponse; class SetupController { - /** - * @throws \JsonException - */ - public function update(Request $request): StreamedResponse + public function update(): StreamedResponse { $update = new Update(new System(), Configuration::getConfigurationInstance()); $update->setVersion(System::getVersion()); - //$postBody = json_decode($request->getContent(), false, 512, JSON_THROW_ON_ERROR); - //$step = Filter::filterVar($postBody->step, FILTER_VALIDATE_INT); - $step = 1; - - return new StreamedResponse(function () use ($step, $update) { + return new StreamedResponse(function () use ($update) { $progressCallback = function ($progress) { - echo json_encode(['progress' => $progress]) . "\n"; + echo json_encode(['progress' => $progress], JSON_THROW_ON_ERROR) . "\n"; ob_flush(); flush(); }; @@ -51,7 +42,7 @@ public function update(Request $request): StreamedResponse echo json_encode(['message' => '✅ Database successfully updated.']); } } catch (Exception $e) { - echo json_encode(['message' => 'Update database failed: ' . $e->getMessage()]); + echo json_encode(['message' => 'Update database failed: ' . $e->getMessage()], JSON_THROW_ON_ERROR); } }); } diff --git a/phpmyfaq/src/phpMyFAQ/Database.php b/phpmyfaq/src/phpMyFAQ/Database.php index 9a65e5da92..066a23c1ad 100755 --- a/phpmyfaq/src/phpMyFAQ/Database.php +++ b/phpmyfaq/src/phpMyFAQ/Database.php @@ -66,9 +66,9 @@ public static function factory(string $type): ?DatabaseDriver self::$instance = new $class(); return self::$instance; - } else { - throw new Exception('Invalid Database Type: ' . $type); } + + throw new Exception('Invalid Database Type: ' . $type); } /** @@ -96,19 +96,13 @@ public static function getType(): string * Check if a table is filled with data. * * @param string $tableName Table name - * @return bool true, if table is empty, otherwise false + * @return bool true, if the table is empty, otherwise false */ public static function checkOnEmptyTable(string $tableName): bool { - if ( - self::$instance->numRows( - self::$instance->query('SELECT * FROM ' . self::getTablePrefix() . $tableName) - ) < 1 - ) { - return true; - } else { - return false; - } + return self::$instance->numRows( + self::$instance->query('SELECT * FROM ' . self::getTablePrefix() . $tableName) + ) < 1; } /** diff --git a/phpmyfaq/src/phpMyFAQ/Database/Mysqli.php b/phpmyfaq/src/phpMyFAQ/Database/Mysqli.php index fb4e6be5c9..379a0a47c0 100644 --- a/phpmyfaq/src/phpMyFAQ/Database/Mysqli.php +++ b/phpmyfaq/src/phpMyFAQ/Database/Mysqli.php @@ -169,9 +169,9 @@ public function numRows(mixed $result): int { if ($result instanceof mysqli_result) { return $result->num_rows; - } else { - return 0; } + + return 0; } /** @@ -280,13 +280,9 @@ public function nextId(string $table, string $id): int $table ); - $result = $this->query($select); + $mysqliresult = $this->query($select); - if ($result instanceof mysqli_result) { - $current = $result->fetch_row(); - } else { - $current = [0]; - } + $current = $mysqliresult instanceof mysqli_result ? $mysqliresult->fetch_row() : [0]; return $current[0] + 1; } diff --git a/phpmyfaq/src/phpMyFAQ/Database/Pgsql.php b/phpmyfaq/src/phpMyFAQ/Database/Pgsql.php index 56d2679638..e95e9b4a23 100644 --- a/phpmyfaq/src/phpMyFAQ/Database/Pgsql.php +++ b/phpmyfaq/src/phpMyFAQ/Database/Pgsql.php @@ -77,7 +77,7 @@ public function connect( $this->conn = pg_connect($connectionString); - if (empty($database) || !$this->conn) { + if ($database === '' || !$this->conn) { Database::errorPage(pg_last_error($this->conn)); die(); } @@ -238,11 +238,7 @@ public function nextId(string $table, string $id): int public function clientVersion(): string { $pg_version = pg_version($this->conn); - if (isset($pg_version['client'])) { - return $pg_version['client']; - } else { - return 'n/a'; - } + return $pg_version['client'] ?? 'n/a'; } /** @@ -251,11 +247,7 @@ public function clientVersion(): string public function serverVersion(): string { $pg_version = pg_version($this->conn); - if (isset($pg_version['server_version'])) { - return $pg_version['server_version']; - } else { - return 'n/a'; - } + return $pg_version['server_version'] ?? 'n/a'; } /** diff --git a/phpmyfaq/src/phpMyFAQ/Database/Sqlite3.php b/phpmyfaq/src/phpMyFAQ/Database/Sqlite3.php index 04bd72a5ec..1f0e45e3fd 100644 --- a/phpmyfaq/src/phpMyFAQ/Database/Sqlite3.php +++ b/phpmyfaq/src/phpMyFAQ/Database/Sqlite3.php @@ -205,7 +205,10 @@ public function fetchAssoc(mixed $result): array */ public function numRows(mixed $result): int { - !isset($result->fetchedByPMF) || !$result->fetchedByPMF || die(self::ERROR_MESSAGE); + if (isset($result->fetchedByPMF) && $result->fetchedByPMF) { + die(self::ERROR_MESSAGE); + } + $numberOfRows = 0; while ($result->fetchArray(SQLITE3_NUM)) { ++$numberOfRows; diff --git a/phpmyfaq/src/phpMyFAQ/Date.php b/phpmyfaq/src/phpMyFAQ/Date.php index c96dd61843..3d227875eb 100644 --- a/phpmyfaq/src/phpMyFAQ/Date.php +++ b/phpmyfaq/src/phpMyFAQ/Date.php @@ -31,7 +31,7 @@ class Date /** * Constructor. */ - public function __construct(private readonly Configuration $config) + public function __construct(private readonly Configuration $configuration) { } @@ -74,27 +74,25 @@ public static function getTrackingFileDate(string $file, bool $endOfDay = false) $year = Strings::substr($file, 12, 4); if (!$endOfDay) { - $time = mktime(0, 0, 0, (int) $month, (int) $day, (int) $year); - } else { - $time = mktime(23, 59, 59, (int) $month, (int) $day, (int) $year); + return mktime(0, 0, 0, (int)$month, (int)$day, (int)$year); } - return $time; - } else { - return -1; + return mktime(23, 59, 59, (int) $month, (int) $day, (int) $year); } + + return -1; } /** - * Returns date formatted according to user defined format. + * Returns date formatted according to user-defined format. */ public function format(string $unformattedDate): string { try { - $date = new DateTime($unformattedDate); - return $date->format($this->config->get('main.dateFormat')); + $dateTime = new DateTime($unformattedDate); + return $dateTime->format($this->configuration->get('main.dateFormat')); } catch (Exception $e) { - $this->config->getLogger()->error($e->getMessage()); + $this->configuration->getLogger()->error($e->getMessage()); return ''; } } diff --git a/phpmyfaq/src/phpMyFAQ/Encryption.php b/phpmyfaq/src/phpMyFAQ/Encryption.php index 3a4529bfbf..8cf6194633 100644 --- a/phpmyfaq/src/phpMyFAQ/Encryption.php +++ b/phpmyfaq/src/phpMyFAQ/Encryption.php @@ -47,7 +47,7 @@ class Encryption /** * Constructor. */ - public function __construct(protected ?Configuration $config) + public function __construct(protected ?Configuration $configuration) { } @@ -61,19 +61,19 @@ public function __construct(protected ?Configuration $config) * object without database access and with an error message. See the * of the error() method for further details. */ - public static function selectEnc(string $encType, Configuration $config): Encryption + public static function selectEnc(string $encType, Configuration $configuration): Encryption { - $enc = new self($config); + $self = new self($configuration); $encType = ucfirst(strtolower($encType)); $encClass = 'phpMyFAQ\\EncryptionTypes\\' . $encType; if (!class_exists($encClass)) { - $enc->errors[] = self::PMF_ERROR_USER_NO_ENCTYPE; + $self->errors[] = self::PMF_ERROR_USER_NO_ENCTYPE; - return $enc; + return $self; } - return new $encClass($config); + return new $encClass($configuration); } /** @@ -110,7 +110,7 @@ public function error(): string */ public function setSalt(string $login): Encryption { - $this->salt = $this->config->get('security.salt') . $login; + $this->salt = $this->configuration->get('security.salt') . $login; return $this; } diff --git a/phpmyfaq/src/phpMyFAQ/Entity/CommentType.php b/phpmyfaq/src/phpMyFAQ/Entity/CommentType.php index e61f91c544..493b784430 100644 --- a/phpmyfaq/src/phpMyFAQ/Entity/CommentType.php +++ b/phpmyfaq/src/phpMyFAQ/Entity/CommentType.php @@ -24,5 +24,6 @@ class CommentType { final public const FAQ = 'faq'; + final public const NEWS = 'news'; } diff --git a/phpmyfaq/src/phpMyFAQ/Entity/FaqEntity.php b/phpmyfaq/src/phpMyFAQ/Entity/FaqEntity.php index 75682720d0..66d9f265c7 100644 --- a/phpmyfaq/src/phpMyFAQ/Entity/FaqEntity.php +++ b/phpmyfaq/src/phpMyFAQ/Entity/FaqEntity.php @@ -206,9 +206,9 @@ public function getValidFrom(): DateTime { if ($this->validFrom instanceof DateTime) { return $this->validFrom; - } else { - return $this->validFrom = new DateTime(); } + + return $this->validFrom = new DateTime(); } public function setValidFrom(DateTime $validFrom): FaqEntity @@ -221,14 +221,14 @@ public function getValidTo(): DateTime { if ($this->validTo instanceof DateTime) { return $this->validTo; - } else { - return $this->validTo = new DateTime('99991231235959'); } + + return $this->validTo = new DateTime('99991231235959'); } - public function setValidTo(DateTime $validTo): FaqEntity + public function setValidTo(DateTime $dateTime): FaqEntity { - $this->validTo = $validTo; + $this->validTo = $dateTime; return $this; } @@ -236,9 +236,9 @@ public function getCreatedDate(): DateTime { if ($this->createdDate instanceof DateTime) { return $this->createdDate; - } else { - return $this->createdDate = new DateTime(); } + + return $this->createdDate = new DateTime(); } public function setCreatedDate(DateTime $createdDate): FaqEntity @@ -251,9 +251,9 @@ public function getUpdatedDate(): DateTime { if ($this->updatedDate instanceof DateTime) { return $this->updatedDate; - } else { - return $this->updatedDate = new DateTime(); } + + return $this->updatedDate = new DateTime(); } public function setUpdatedDate(DateTime $updatedDate): FaqEntity diff --git a/phpmyfaq/src/phpMyFAQ/Export.php b/phpmyfaq/src/phpMyFAQ/Export.php index 999d8a420d..dc24d23e7c 100644 --- a/phpmyfaq/src/phpMyFAQ/Export.php +++ b/phpmyfaq/src/phpMyFAQ/Export.php @@ -38,22 +38,18 @@ class Export /** * Factory. - * - * @param Faq $faq - * @param Category $category - * @return Pdf|Html5|Json * @throws Exception */ public static function create( Faq $faq, Category $category, - Configuration $config, + Configuration $configuration, string $mode = 'pdf' ): Pdf|Html5|Json { return match ($mode) { - 'json' => new Json($faq, $category, $config), - 'pdf' => new Pdf($faq, $category, $config), - 'html5' => new Html5($faq, $category, $config), + 'json' => new Json($faq, $category, $configuration), + 'pdf' => new Pdf($faq, $category, $configuration), + 'html5' => new Html5($faq, $category, $configuration), default => throw new Exception('Export not implemented!'), }; } diff --git a/phpmyfaq/src/phpMyFAQ/Export/Html5.php b/phpmyfaq/src/phpMyFAQ/Export/Html5.php index ec4558899e..4a6cdacbf9 100644 --- a/phpmyfaq/src/phpMyFAQ/Export/Html5.php +++ b/phpmyfaq/src/phpMyFAQ/Export/Html5.php @@ -41,17 +41,15 @@ class Html5 extends Export /** * Constructor. * - * @param Faq $faq FaqHelper object - * @param Category $category CategoryHelper object - * @param Configuration $config Configuration - * return - * PMF_Export_Xhtml + * @param Faq $faq FaqHelper object + * @param Category $category CategoryHelper object + * @param Configuration $configuration Configuration */ - public function __construct(Faq $faq, Category $category, Configuration $config) + public function __construct(Faq $faq, Category $category, Configuration $configuration) { $this->faq = $faq; $this->category = $category; - $this->config = $config; + $this->config = $configuration; $this->xml = new XMLWriter(); $this->xml->openMemory(); @@ -90,14 +88,14 @@ public function generate(int $categoryId = 0, bool $downwards = true, string $la $this->xml->endElement(); $this->xml->startElement('meta'); $this->xml->writeAttribute('http-equiv', 'Content-Security-Policy'); - $this->xml->writeAttribute('content', 'default-src \'self\'; img-src https://*; child-src \'none\';'); + $this->xml->writeAttribute('content', "default-src 'self'; img-src https://*; child-src 'none';"); $this->xml->endElement(); $this->xml->endElement(); // $this->xml->startElement('body'); $this->xml->writeAttribute('dir', Translation::get('dir')); - if (is_countable($faqData) ? count($faqData) : 0) { + if ((is_countable($faqData) ? count($faqData) : 0) !== 0) { $lastCategory = 0; foreach ($faqData as $data) { if ($data['category_id'] != $lastCategory) { diff --git a/phpmyfaq/src/phpMyFAQ/Export/Json.php b/phpmyfaq/src/phpMyFAQ/Export/Json.php index b4635fe238..1be8004279 100644 --- a/phpmyfaq/src/phpMyFAQ/Export/Json.php +++ b/phpmyfaq/src/phpMyFAQ/Export/Json.php @@ -34,15 +34,15 @@ class Json extends Export /** * Constructor. * - * @param Faq $faq FaqHelper object - * @param Category $category Entity object - * @param Configuration $config Configuration + * @param Faq $faq FaqHelper object + * @param Category $category Entity object + * @param Configuration $configuration Configuration */ - public function __construct(Faq $faq, Category $category, Configuration $config) + public function __construct(Faq $faq, Category $category, Configuration $configuration) { $this->faq = $faq; $this->category = $category; - $this->config = $config; + $this->config = $configuration; } /** @@ -62,21 +62,19 @@ public function generate(int $categoryId = 0, bool $downwards = true, string $la $faqData = $this->faq->get(FAQ_QUERY_TYPE_EXPORT_XML, $categoryId, $downwards, $language); - if (count($faqData)) { - foreach ($faqData as $data) { - $generated[] = [ - 'faq' => [ - 'id' => $data['id'], - 'language' => $data['lang'], - 'category' => $this->category->getPath($data['category_id'], ' >> '), - 'keywords' => $data['keywords'], - 'question' => strip_tags((string) $data['topic']), - 'answer' => Strings::htmlspecialchars($data['content']), - 'author' => $data['author_name'], - 'last_modified' => Date::createIsoDate($data['lastmodified']) - ] - ]; - } + foreach ($faqData as $data) { + $generated[] = [ + 'faq' => [ + 'id' => $data['id'], + 'language' => $data['lang'], + 'category' => $this->category->getPath($data['category_id'], ' >> '), + 'keywords' => $data['keywords'], + 'question' => strip_tags((string) $data['topic']), + 'answer' => Strings::htmlspecialchars($data['content']), + 'author' => $data['author_name'], + 'last_modified' => Date::createIsoDate($data['lastmodified']) + ] + ]; } header('Content-type: application/json'); diff --git a/phpmyfaq/src/phpMyFAQ/Export/Pdf.php b/phpmyfaq/src/phpMyFAQ/Export/Pdf.php index 805c123c95..6f532a3acc 100644 --- a/phpmyfaq/src/phpMyFAQ/Export/Pdf.php +++ b/phpmyfaq/src/phpMyFAQ/Export/Pdf.php @@ -43,20 +43,20 @@ class Pdf extends Export private ?Tags $tags = null; - private readonly ?ParsedownExtra $parsedown; + private readonly ?ParsedownExtra $parsedownExtra; /** * Constructor. * - * @param Faq $faq FaqHelper object - * @param Category $category Entity object - * @param Configuration $config Configuration + * @param Faq $faq FaqHelper object + * @param Category $category Entity object + * @param Configuration $configuration Configuration */ - public function __construct(Faq $faq, Category $category, Configuration $config) + public function __construct(Faq $faq, Category $category, Configuration $configuration) { $this->faq = $faq; $this->category = $category; - $this->config = $config; + $this->config = $configuration; $this->pdf = new Wrapper(); $this->pdf->setConfig($this->config); @@ -68,7 +68,7 @@ public function __construct(Faq $faq, Category $category, Configuration $config) $this->pdf->SetHeaderMargin(PDF_MARGIN_HEADER); $this->pdf->SetFooterMargin(PDF_MARGIN_FOOTER); - $this->parsedown = new ParsedownExtra(); + $this->parsedownExtra = new ParsedownExtra(); } /** @@ -103,7 +103,7 @@ public function generate(int $categoryId = 0, bool $downwards = true, string $la if ($currentCategory !== $this->category->categoryName[$faq['category_id']]['id']) { $this->pdf->Bookmark( html_entity_decode( - (string) $this->category->categoryName[$faq['category_id']]['name'], + $this->category->categoryName[$faq['category_id']]['name'], ENT_QUOTES, 'utf-8' ), @@ -135,7 +135,7 @@ public function generate(int $categoryId = 0, bool $downwards = true, string $la $this->pdf->SetFont($this->pdf->getCurrentFont(), '', 10); if ($this->config->get('main.enableMarkdownEditor')) { - $this->pdf->WriteHTML(trim((string) $this->parsedown->text($faq['content']))); + $this->pdf->WriteHTML(trim((string) $this->parsedownExtra->text($faq['content']))); } else { $this->pdf->WriteHTML(trim((string) $faq['content'])); } @@ -176,8 +176,8 @@ public function generate(int $categoryId = 0, bool $downwards = true, string $la public function generateFile(array $faqData, string $filename = null): string { // Default filename: FAQ--.pdf - if (empty($filename)) { - $filename = "FAQ-{$faqData['id']}-{$faqData['lang']}.pdf"; + if ($filename === null || $filename === '') { + $filename = sprintf('FAQ-%s-%s.pdf', $faqData['id'], $faqData['lang']); } $this->pdf->setFaq($faqData); @@ -198,7 +198,7 @@ public function generateFile(array $faqData, string $filename = null): string $this->pdf->Ln(); if ($this->config->get('main.enableMarkdownEditor')) { - $this->pdf->WriteHTML(str_replace('../', '', (string) $this->parsedown->text($faqData['content']))); + $this->pdf->WriteHTML(str_replace('../', '', (string) $this->parsedownExtra->text($faqData['content']))); } else { $this->pdf->WriteHTML(str_replace('../', '', (string) $faqData['content'])); } @@ -209,12 +209,8 @@ public function generateFile(array $faqData, string $filename = null): string $this->pdf->Write(5, Translation::get('ad_entry_solution_id') . ': #' . $faqData['solution_id']); // Check if the author name should be visible, according to the GDPR option - $user = new CurrentUser($this->config); - if ($user->getUserVisibilityByEmail($faqData['email'])) { - $author = $faqData['author']; - } else { - $author = 'n/a'; - } + $currentUser = new CurrentUser($this->config); + $author = $currentUser->getUserVisibilityByEmail($faqData['email']) ? $faqData['author'] : 'n/a'; $this->pdf->SetAuthor($author); $this->pdf->Ln(); diff --git a/phpmyfaq/src/phpMyFAQ/Export/Pdf/Wrapper.php b/phpmyfaq/src/phpMyFAQ/Export/Pdf/Wrapper.php index bcbc64e556..a44ef57703 100644 --- a/phpmyfaq/src/phpMyFAQ/Export/Pdf/Wrapper.php +++ b/phpmyfaq/src/phpMyFAQ/Export/Pdf/Wrapper.php @@ -103,7 +103,7 @@ define('PDF_HEADER_LOGO_WIDTH', 30); /* - * document unit of measure [pt=point, mm=millimeter, cm=centimeter, in=inch] + * document unit of measure [pt=point, mm=millimeter, cm=centimeter, in=inch] */ define('PDF_UNIT', 'mm'); @@ -218,14 +218,17 @@ class Wrapper extends TCPDF * The current faq. */ public array $faq = []; + /** * Configuration. */ protected ?Configuration $config = null; + /** * Question. */ private string $question = ''; + /** * Font files. */ @@ -292,13 +295,13 @@ public function setCategory(int $category): void * * @param string $question Question */ - public function setQuestion(string $question = '') + public function setQuestion(string $question = ''): void { $this->question = $question; } /** - * Setter for categories array. + * Setter for a category array. * * @param array $categories Categories */ @@ -307,9 +310,9 @@ public function setCategories(array $categories): void $this->categories = $categories; } - public function setConfig(Configuration $config): void + public function setConfig(Configuration $configuration): void { - $this->config = $config; + $this->config = $configuration; } /** @@ -320,11 +323,7 @@ public function Header(): void // phpcs:ignore // Set custom header and footer $this->setCustomHeader(); - if (array_key_exists($this->category, $this->categories)) { - $title = $this->categories[$this->category]['name']; - } else { - $title = ''; - } + $title = array_key_exists($this->category, $this->categories) ? $this->categories[$this->category]['name'] : ''; $this->SetTextColor(0, 0, 0); $this->SetFont($this->currentFont, 'B', 18); @@ -400,11 +399,11 @@ public function Footer(): void // phpcs:ignore $this->SetY(-20); $this->SetFont($this->currentFont, 'B', 8); $this->Cell(0, 10, $footer, 0, 1, 'C'); - if ($this->enableBookmarks == false) { + if (!$this->enableBookmarks) { $this->SetY(-15); $this->SetFont($this->currentFont, '', 8); $baseUrl = 'index.php'; - if (is_array($this->faq) && !empty($this->faq)) { + if ($this->faq !== []) { $baseUrl .= '?action=faq&'; if (array_key_exists($this->category, $this->categories)) { $baseUrl .= 'cat=' . $this->categories[$this->category]['id']; @@ -415,9 +414,9 @@ public function Footer(): void // phpcs:ignore $baseUrl .= '&artlang=' . $this->faq['lang']; } $url = $this->config->getDefaultUrl() . $baseUrl; - $urlObj = new Link($url, $this->config); - $urlObj->itemTitle = $this->question; - $_url = str_replace('&', '&', $urlObj->toString()); + $link = new Link($url, $this->config); + $link->itemTitle = $this->question; + $_url = str_replace('&', '&', $link->toString()); $this->Cell(0, 10, 'URL: ' . $_url, 0, 1, 'C', 0, $_url); } $this->TextColor = $currentTextColor; @@ -426,7 +425,7 @@ public function Footer(): void // phpcs:ignore /** * Sets custom footer. */ - public function setCustomFooter() + public function setCustomFooter(): void { $this->customFooter = $this->config->get('main.customPdfFooter'); } @@ -434,7 +433,7 @@ public function setCustomFooter() /** * Adds a table of content for exports of the complete FAQ. */ - public function addFaqToc() + public function addFaqToc(): void { $this->addTOCPage(); @@ -476,32 +475,32 @@ public function setFaq(array $faq) * @param string $file Name of the file containing the image or a '@' character followed by the image data * string. To link an image without embedding it on the document, set an asterisk * character before the URL (i.e.: '*http://www.example.com/image.jpg'). - * @param string $x Abscissa of the upper-left corner (LTR) or upper-right corner (RTL). - * @param string $y Ordinate of the upper-left corner (LTR) or upper-right corner (RTL). - * @param int $w Width of the image in the page. If not specified or equal to zero, it is automatically + * @param float|null $x Abscissa of the upper-left corner (LTR) or upper-right corner (RTL). + * @param float|null $y Ordinate of the upper-left corner (LTR) or upper-right corner (RTL). + * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically * calculated. - * @param int $h Height of the image in the page. If not specified or equal to zero, it is automatically + * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically * calculated. - * @param string $type Image format. Possible values are (case insensitive): JPEG and PNG (whitout GD library) + * @param string $type Image format. Possible values are (case-insensitive): JPEG and PNG (without a GD library) * and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;. If * not specified, the type is inferred from the file extension. * @param string $link URL or identifier returned by AddLink(). * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. - * @param bool $resize If true resize (reduce) the image to fit $w and $h (requires GD or ImageMagick library); - * if false do not resize; if 2 force resize in all cases (upscaling and downscaling). + * @param bool $resize If true resizes (reduce) the image to fit $w and $h (requires a GD or ImageMagick library); + * if false do not resize; if two force resize in all cases (upscaling and downscaling). * @param int $dpi dot-per-inch resolution used on resize - * @param string $palign Allows to center or align the image on the current line. + * @param string $palign Allows centering or aligning the image on the current line. * @param bool $ismask true if this image is a mask, false otherwise * @param mixed $imgmask Image object returned by this function or false * @param int $border Indicates if borders must be drawn around the cell. - * @param mixed $fitbox If not false scale image dimensions proportionally to fit within the ($w, $h) box. - * $fitbox can be true or a 2 characters string indicating the image alignment inside - * the box. The first character indicate the horizontal alignment (L = left, C = - * center, R = right) the second character indicate the vertical algnment (T = top, M + * @param mixed $fitbox If not, false scale image dimensions proportionally to fit within the ($w, $h) box. + * $fitbox can be true or a 2-character string indicating the image alignment inside + * the box. The first character indicates the horizontal alignment (L = left, C = + * center, R = right) the second character indicates the vertical algnment (T = top, M * = middle, B = bottom). - * @param bool $hidden If true do not display the image. - * @param bool $fitonpage If true the image is resized to not exceed page dimensions. - * @param bool $alt If true the image will be added as alternative and not directly printed (the ID of the + * @param bool $hidden If true, do not display the image. + * @param bool $fitonpage If true, the image is resized to not exceed page dimensions. + * @param bool $alt If true, the image will be added as alternative and not directly printed (the ID of the * image will be returned). * @param array $altimgs Array of alternate images IDs. Each alternative image must be an array with two values: * an integer representing the image ID (the value returned by the Image method) and a @@ -509,8 +508,8 @@ public function setFaq(array $faq) */ public function Image(// phpcs:ignore $file, - $x = '', - $y = '', + $x = null, + $y = null, $w = 0, $h = 0, $type = '', @@ -528,7 +527,7 @@ public function Image(// phpcs:ignore $alt = false, $altimgs = [] ): void { - if (!strpos($file, 'data:image/png;base64,') === false) { + if (strpos($file, 'data:image/png;base64,')) { $file = '@' . base64_decode( chunk_split(str_replace(' ', '+', str_replace('data:image/png;base64,', '', $file))) ); diff --git a/phpmyfaq/src/phpMyFAQ/Instance/Client.php b/phpmyfaq/src/phpMyFAQ/Instance/Client.php index 1d5638c240..e02fd16821 100644 --- a/phpmyfaq/src/phpMyFAQ/Instance/Client.php +++ b/phpmyfaq/src/phpMyFAQ/Instance/Client.php @@ -86,7 +86,6 @@ public function createClientFolder(string $hostname): bool * Creates all tables with the given table prefix from the primary tables. * * @param string $prefix SQL table prefix - * @return void */ public function createClientTables(string $prefix): void { @@ -148,7 +147,6 @@ public function copyConstantsFile(string $destination): bool * * @param string $destination Destination folder * @param string $templateDir Template folder - * @return void * @throws Exception */ public function copyTemplateFolder(string $destination, string $templateDir = 'default'): void diff --git a/phpmyfaq/src/phpMyFAQ/Language/LanguageCodes.php b/phpmyfaq/src/phpMyFAQ/Language/LanguageCodes.php index 7b0b775a46..ecf397c4fe 100644 --- a/phpmyfaq/src/phpMyFAQ/Language/LanguageCodes.php +++ b/phpmyfaq/src/phpMyFAQ/Language/LanguageCodes.php @@ -294,11 +294,7 @@ public static function getAll(): array */ public static function get(string $key): ?string { - if (isset(static::$languageCodes[strtolower($key)])) { - return static::$languageCodes[strtolower($key)]; - } - - return null; + return static::$languageCodes[strtolower($key)] ?? null; } /** @@ -308,11 +304,7 @@ public static function get(string $key): ?string */ public static function getSupported(string $key): ?string { - if (isset(static::$supportedLanguageCodes[strtolower($key)])) { - return static::$supportedLanguageCodes[strtolower($key)]; - } - - return null; + return static::$supportedLanguageCodes[strtolower($key)] ?? null; } public static function getKey(string $value): ?string diff --git a/phpmyfaq/src/phpMyFAQ/Setup.php b/phpmyfaq/src/phpMyFAQ/Setup.php index bead5b40bd..f9a13c4db5 100644 --- a/phpmyfaq/src/phpMyFAQ/Setup.php +++ b/phpmyfaq/src/phpMyFAQ/Setup.php @@ -17,6 +17,8 @@ namespace phpMyFAQ; +use phpMyFAQ\Core\Exception; + abstract class Setup { public function __construct(protected System $system) @@ -31,4 +33,55 @@ public function checkMinimumPhpVersion(): bool { return version_compare(PHP_VERSION, System::VERSION_MINIMUM_PHP) > 0; } + + /** + * Checks if the database file exists. + * @return bool + */ + public function checkDatabaseFile(): bool + { + if ( + !file_exists(PMF_ROOT_DIR . '/config/database.php') && + !file_exists(PMF_ROOT_DIR . '/content/core/config/database.php') + ) { + return false; + } + + return true; + } + + /** + * Checks for the minimum PHP requirement and if the database credentials file is readable. + * @throws Exception + */ + public function checkPreUpgrade(string $databaseType): void + { + $database = null; + if (!$this->checkMinimumPhpVersion()) { + throw new Exception( + sprintf('Sorry, but you need PHP %s or later!', System::VERSION_MINIMUM_PHP) + ); + } + + if (!is_readable(PMF_ROOT_DIR . '/content/core/config/database.php')) { + throw new Exception( + 'Sorry, but the database configuration file is not readable. Please check the permissions.' + ); + } + + if ('' !== $databaseType) { + $databaseFound = false; + foreach ($this->system->getSupportedDatabases() as $database => $values) { + if ($database === $databaseType) { + $databaseFound = true; + break; + } + } + if (!$databaseFound) { + throw new Exception( + sprintf('Sorry, but the database %s is not supported!', ucfirst($database)) + ); + } + } + } } diff --git a/phpmyfaq/src/phpMyFAQ/Setup/Installer.php b/phpmyfaq/src/phpMyFAQ/Setup/Installer.php index a8be723db7..39242a3719 100644 --- a/phpmyfaq/src/phpMyFAQ/Setup/Installer.php +++ b/phpmyfaq/src/phpMyFAQ/Setup/Installer.php @@ -26,7 +26,6 @@ use phpMyFAQ\Configuration\ElasticsearchConfiguration; use phpMyFAQ\Core\Exception; use phpMyFAQ\Database; -use phpMyFAQ\Database\DatabaseDriver; use phpMyFAQ\Entity\InstanceEntity; use phpMyFAQ\Enums\ReleaseType; use phpMyFAQ\Filter; @@ -468,65 +467,44 @@ public function __construct(protected System $system) /** * Check absolutely necessary stuff and die. + * @throws Exception */ public function checkBasicStuff(): void { if (!$this->checkMinimumPhpVersion()) { - Alert::danger( - 'ad_entryins_fail', + throw new Exception( sprintf('Sorry, but you need PHP %s or later!', System::VERSION_MINIMUM_PHP) ); - System::renderFooter(); } if (!function_exists('date_default_timezone_set')) { - Alert::danger( - 'ad_entryins_fail', + throw new Exception( 'Sorry, but setting a default timezone does not work in your environment!' ); - echo '

Sorry, but setting a default timezone does not work in your ' . - 'environment!

'; - System::renderFooter(); } if (!$this->system->checkDatabase()) { - echo '

No supported database detected! Please install one of the following' . - ' database systems and enable the corresponding PHP extension in php.ini:

'; - echo '
    '; - foreach ($this->system->getSupportedDatabases() as $database) { - printf('
  • %s
  • ', $database[1]); - } - echo '
'; - System::renderFooter(); + throw new Exception( + 'No supported database detected!' + ); } if (!$this->system->checkRequiredExtensions()) { - echo '

The following extensions are missing! Please enable the PHP ' . - 'extension(s) in php.ini.

'; - echo '
    '; - foreach ($this->system->getMissingExtensions() as $extension) { - printf('
  • ext/%s
  • ', $extension); - } - echo '
'; - System::renderFooter(); + throw new Exception( + sprintf( + 'Some required PHP extensions are missing: %s', + implode(', ', $this->system->getMissingExtensions()) + ) + ); } if (!$this->system->checkInstallation()) { - echo '

The setup script found the file config/database.php. It ' . - 'looks like you\'re already running a version of phpMyFAQ. Please run the update' . - ' script.

'; - System::renderFooter(); + throw new Exception( + 'phpMyFAQ is already installed! Please use the update.' + ); } } - /** - * Checks the minimum required PHP version, defined in System. - */ - public function checkMinimumPhpVersion(): bool - { - return true; - } - /** * Checks for the minimum PHP requirement and if the database credentials file is readable. * @@ -1069,4 +1047,13 @@ public function startInstall(array $setup = null): void $faqInstanceElasticsearch->createIndex(); } } + + /** + * Checks the minimum required PHP version, defined in System class. + * Returns true if it's okay. + */ + public function checkMinimumPhpVersion(): bool + { + return version_compare(PHP_VERSION, System::VERSION_MINIMUM_PHP) > 0; + } } diff --git a/phpmyfaq/src/phpMyFAQ/Setup/Upgrade.php b/phpmyfaq/src/phpMyFAQ/Setup/Upgrade.php index 96ef8ff88f..661ae92767 100644 --- a/phpmyfaq/src/phpMyFAQ/Setup/Upgrade.php +++ b/phpmyfaq/src/phpMyFAQ/Setup/Upgrade.php @@ -36,7 +36,7 @@ class Upgrade extends Setup { - public const GITHUB_PATH = 'thorsten/phpMyFAQ/releases/download/development-nightly-%s/'; + final public const GITHUB_PATH = 'thorsten/phpMyFAQ/releases/download/development-nightly-%s/'; private const GITHUB_FILENAME = 'phpMyFAQ-nightly-%s.zip'; private const PHPMYFAQ_FILENAME = 'phpMyFAQ-%s.zip'; private const PMF_UPGRADE_DIR = PMF_CONTENT_DIR . '/upgrades'; @@ -52,7 +52,6 @@ public function __construct(protected System $system, private readonly Configura /** * Method to check if the filesystem is ready for the upgrade * - * @return bool * @throws Exception */ public function checkFilesystem(): bool @@ -106,7 +105,6 @@ public function checkFilesystem(): bool * Method to download a phpMyFAQ package, returns false if it doesn't work * * @param string $version - * @return string * @throws Exception * @todo handle possible proxy servers */ @@ -143,7 +141,6 @@ public function downloadPackage(string $version): string * * @param string $path | Path to zip file * @param string $version | Version to verify - * @return bool * @throws TransportExceptionInterface|ClientExceptionInterface|RedirectionExceptionInterface|ServerExceptionInterface|JsonException */ public function verifyPackage(string $path, string $version): bool @@ -179,7 +176,6 @@ public function verifyPackage(string $path, string $version): bool * * @param string $path | Path of the package * @param callable $progressCallback - * @return bool * @throws Exception */ public function extractPackage(string $path, callable $progressCallback): bool @@ -210,7 +206,6 @@ public function extractPackage(string $path, callable $progressCallback): bool * * @param string $backupName | Name of the created backup * @param callable $progressCallback - * @return bool * @throws Exception */ public function createTemporaryBackup(string $backupName, callable $progressCallback): bool @@ -257,7 +252,6 @@ public function createTemporaryBackup(string $backupName, callable $progressCall * Method to delete the temporary created backup. * * @param string $backupName | Name of the created backup - * @return bool */ public function deleteTemporaryBackup(string $backupName): bool { @@ -270,8 +264,6 @@ public function deleteTemporaryBackup(string $backupName): bool /** * Method to restore from the temporary backup - * - * @return void */ public function restoreTemporaryBackup() { @@ -281,7 +273,6 @@ public function restoreTemporaryBackup() * Method to install the package * * @param callable $progressCallback - * @return bool */ public function installPackage(callable $progressCallback): bool { @@ -317,7 +308,6 @@ public function installPackage(callable $progressCallback): bool /** * Returns the host for download packages, so either github.com or download.phpmyfaq.de - * @return string */ public function getDownloadHost(): string { @@ -330,7 +320,6 @@ public function getDownloadHost(): string /** * Returns the path to the download package, it's an empty string for development and production releases - * @return string */ public function getPath(): string { @@ -344,7 +333,6 @@ public function getPath(): string /** * Returns the filename of the download package * @param string $version - * @return string */ public function getFilename(string $version): string { @@ -355,9 +343,6 @@ public function getFilename(string $version): string return sprintf(self::PHPMYFAQ_FILENAME, $version); } - /** - * @return bool - */ public function isNightly(): bool { return $this->isNightly; diff --git a/phpmyfaq/src/phpMyFAQ/System.php b/phpmyfaq/src/phpMyFAQ/System.php index 651dc3b5de..2ae8ddebb5 100644 --- a/phpmyfaq/src/phpMyFAQ/System.php +++ b/phpmyfaq/src/phpMyFAQ/System.php @@ -61,7 +61,7 @@ class System /** * Minimum required PHP version. */ - final public const VERSION_MINIMUM_PHP = '8.1.0'; + final public const VERSION_MINIMUM_PHP = '8.2.0'; /** * phpMyFAQ homepage URL diff --git a/phpmyfaq/src/phpMyFAQ/Template/TwigWrapper.php b/phpmyfaq/src/phpMyFAQ/Template/TwigWrapper.php index 30d663b50c..626ad4cdb0 100644 --- a/phpmyfaq/src/phpMyFAQ/Template/TwigWrapper.php +++ b/phpmyfaq/src/phpMyFAQ/Template/TwigWrapper.php @@ -27,7 +27,7 @@ class TwigWrapper { - private Environment $twig; + private readonly Environment $twig; public function __construct(string $templatePath) { diff --git a/phpmyfaq/src/phpMyFAQ/User.php b/phpmyfaq/src/phpMyFAQ/User.php index 1ee516ab7f..81715b2df6 100644 --- a/phpmyfaq/src/phpMyFAQ/User.php +++ b/phpmyfaq/src/phpMyFAQ/User.php @@ -203,10 +203,7 @@ public function addPerm(Permission $perm): bool */ public function getAuthSource(string $key): ?string { - if (isset($this->authData['authSource'][$key])) { - return $this->authData['authSource'][$key]; - } - return null; + return $this->authData['authSource'][$key] ?? null; } public function getUserAuthSource(): string @@ -219,10 +216,7 @@ public function getUserAuthSource(): string */ public function getAuthData(string $key): mixed { - if (isset($this->authData[$key])) { - return $this->authData[$key]; - } - return null; + return $this->authData[$key] ?? null; } /** @@ -1055,7 +1049,6 @@ public function setSuperAdmin(bool $isSuperAdmin): bool /** * Terminates the session ID of user - * @return bool */ public function terminateSessionId(): bool { diff --git a/rector.php b/rector.php index f1b5f0f15a..1083399b42 100644 --- a/rector.php +++ b/rector.php @@ -24,9 +24,14 @@ // define sets of rules $rectorConfig->sets([ - LevelSetList::UP_TO_PHP_81, + LevelSetList::UP_TO_PHP_82, SetList::DEAD_CODE, - SymfonySetList::SYMFONY_62, + SetList::CODE_QUALITY, + SetList::CODING_STYLE, + SetList::NAMING, + SetList::EARLY_RETURN, + SetList::PHP_82, + SymfonySetList::SYMFONY_63, SymfonySetList::SYMFONY_CODE_QUALITY, SymfonySetList::SYMFONY_CONSTRUCTOR_INJECTION, PHPUnitSetList::PHPUNIT_100,