Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
94.44% |
34 / 36 |
|
75.00% |
3 / 4 |
CRAP | |
0.00% |
0 / 1 |
| Report | |
94.44% |
34 / 36 |
|
75.00% |
3 / 4 |
16.04 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getReportingData | |
100.00% |
23 / 23 |
|
100.00% |
1 / 1 |
8 | |||
| convertEncoding | |
77.78% |
7 / 9 |
|
0.00% |
0 / 1 |
5.27 | |||
| sanitize | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * The reporting class for simple report generation. |
| 5 | * |
| 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, |
| 7 | * v. 2.0. If a copy of the MPL was not distributed with this file, You can |
| 8 | * obtain one at https://mozilla.org/MPL/2.0/. |
| 9 | * |
| 10 | * @package phpMyFAQ |
| 11 | * @author Thorsten Rinne <thorsten@phpmyfaq.de> |
| 12 | * @author Gustavo Solt <gustavo.solt@mayflower.de> |
| 13 | * @copyright 2011-2026 phpMyFAQ Team |
| 14 | * @license https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 |
| 15 | * @link https://www.phpmyfaq.de |
| 16 | * @since 2011-02-04 |
| 17 | */ |
| 18 | |
| 19 | declare(strict_types=1); |
| 20 | |
| 21 | namespace phpMyFAQ\Administration; |
| 22 | |
| 23 | use phpMyFAQ\Configuration; |
| 24 | use phpMyFAQ\Date; |
| 25 | |
| 26 | /** |
| 27 | * Class Report |
| 28 | * |
| 29 | * @package phpMyFAQ |
| 30 | */ |
| 31 | readonly class Report |
| 32 | { |
| 33 | private ReportRepository $reportRepository; |
| 34 | |
| 35 | /** |
| 36 | * Constructor. |
| 37 | */ |
| 38 | public function __construct(Configuration $configuration) |
| 39 | { |
| 40 | $this->reportRepository = new ReportRepository($configuration); |
| 41 | } |
| 42 | |
| 43 | /** |
| 44 | * Generates a huge array for the report. |
| 45 | * |
| 46 | * @return array<int, array<string, mixed>> |
| 47 | */ |
| 48 | public function getReportingData(): array |
| 49 | { |
| 50 | $report = []; |
| 51 | $rows = $this->reportRepository->fetchAllReportData(); |
| 52 | |
| 53 | $lastId = 0; |
| 54 | foreach ($rows as $row) { |
| 55 | $rowId = (int) $row->id; |
| 56 | if ($rowId === $lastId) { |
| 57 | ++$report[$rowId]['faq_translations']; |
| 58 | } |
| 59 | |
| 60 | $report[$rowId] = [ |
| 61 | 'faq_id' => (int) $row->id, |
| 62 | 'faq_language' => (string) $row->lang, |
| 63 | 'category_id' => $row->category_id === null ? null : (int) $row->category_id, |
| 64 | 'category_parent' => $row->parent_id === null ? null : (int) $row->parent_id, |
| 65 | 'category_name' => $row->category_name === null ? null : (string) $row->category_name, |
| 66 | 'faq_translations' => 0, |
| 67 | 'faq_sticky' => (int) $row->sticky, |
| 68 | 'faq_question' => (string) $row->question, |
| 69 | 'faq_org_author' => (string) $row->original_author, |
| 70 | 'faq_updated' => Date::createIsoDate((string) $row->updated), |
| 71 | 'faq_visits' => $row->visits === null ? null : (int) $row->visits, |
| 72 | 'faq_last_author' => $row->last_author === null ? null : (string) $row->last_author, |
| 73 | ]; |
| 74 | |
| 75 | $lastId = $rowId; |
| 76 | } |
| 77 | |
| 78 | return $report; |
| 79 | } |
| 80 | |
| 81 | /** |
| 82 | * Convert string to the correct encoding and removes possible |
| 83 | * bad strings to avoid formula injection attacks. |
| 84 | * |
| 85 | * @param string $outputString String to encode. |
| 86 | * @return string Encoded string. |
| 87 | */ |
| 88 | public function convertEncoding(string $outputString = ''): string |
| 89 | { |
| 90 | $outputString = html_entity_decode($outputString, ENT_QUOTES, encoding: 'utf-8'); |
| 91 | $outputString = str_replace(search: ',', replace: ' ', subject: $outputString); |
| 92 | |
| 93 | if (extension_loaded(extension: 'mbstring')) { |
| 94 | $detected = mb_detect_encoding($outputString); |
| 95 | |
| 96 | if ($detected !== false && $detected !== 'ASCII') { |
| 97 | $converted = mb_convert_encoding($outputString, to_encoding: 'UTF-16', from_encoding: $detected); |
| 98 | $outputString = is_string($converted) ? $converted : $outputString; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | $toBeRemoved = ['=', '+', '-', 'HYPERLINK']; |
| 103 | return str_replace(search: $toBeRemoved, replace: '', subject: $outputString); |
| 104 | } |
| 105 | |
| 106 | /** |
| 107 | * Sanitizes input to avoid CSV injection. |
| 108 | */ |
| 109 | public static function sanitize(int|string $value): string|int |
| 110 | { |
| 111 | if (preg_match(pattern: '/[=\+\-\@\|]/', subject: (string) $value)) { |
| 112 | return '"' . str_replace(search: '"', replace: '""', subject: (string) $value) . '"'; |
| 113 | } |
| 114 | |
| 115 | return $value; |
| 116 | } |
| 117 | } |