Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
90.48% covered (success)
90.48%
19 / 21
85.71% covered (success)
85.71%
6 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Visits
90.48% covered (success)
90.48%
19 / 21
85.71% covered (success)
85.71%
6 / 7
10.09
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 logViews
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 add
66.67% covered (warning)
66.67%
4 / 6
0.00% covered (danger)
0.00%
0 / 1
2.15
 update
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getAllData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 resetAll
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getRequestTimestamp
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3/**
4 * Handles all the stuff for visits.
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 * @copyright 2009-2026 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link  https://www.phpmyfaq.de
15 * @since 2009-03-08
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ;
21
22use phpMyFAQ\Visits\VisitsRepository;
23use phpMyFAQ\Visits\VisitsRepositoryInterface;
24use Symfony\Component\HttpFoundation\Request;
25
26/**
27 * Class Visits
28 *
29 * @package phpMyFAQ
30 */
31readonly class Visits
32{
33    private VisitsRepositoryInterface $visitsRepository;
34
35    /**
36     * Constructor.
37     */
38    public function __construct(
39        private Configuration $configuration,
40    ) {
41        $this->visitsRepository = new VisitsRepository($configuration);
42    }
43
44    /**
45     * Counting the views of a FAQ record.
46     *
47     * @param int $faqId FAQ record ID
48     */
49    public function logViews(int $faqId): void
50    {
51        $language = $this->configuration->getLanguage()->getLanguage();
52        $visitCount = $this->visitsRepository->getVisitCount($faqId, $language);
53
54        if ($visitCount === 0) {
55            $this->add($faqId);
56            return;
57        }
58
59        $this->update($faqId);
60    }
61
62    /**
63     * Adds a new entry in the table "faqvisits".
64     *
65     * @param int $faqId Record ID
66     */
67    public function add(int $faqId): bool
68    {
69        $language = $this->configuration->getLanguage()->getLanguage();
70        $timestamp = $this->getRequestTimestamp();
71
72        // If a row already exists for this (id, lang), update it instead of inserting to avoid unique constraint errors
73        if ($this->visitsRepository->exists($faqId, $language)) {
74            $this->update($faqId);
75            return true;
76        }
77
78        return $this->visitsRepository->insert($faqId, $language, $timestamp);
79    }
80
81    /**
82     * Updates an entry in the table "faqvisits".
83     *
84     * @param int $faqId FAQ record ID
85     */
86    private function update(int $faqId): void
87    {
88        $language = $this->configuration->getLanguage()->getLanguage();
89        $timestamp = $this->getRequestTimestamp();
90
91        $this->visitsRepository->update($faqId, $language, $timestamp);
92    }
93
94    /**
95     * Get all the entries from the table "faqvisits".
96     *
97     * @return array<int, array<string, mixed>>
98     */
99    public function getAllData(): array
100    {
101        return $this->visitsRepository->getAll();
102    }
103
104    /**
105     * Resets all visits to the current date and one visit per FAQ.
106     */
107    public function resetAll(): bool
108    {
109        $timestamp = $this->getRequestTimestamp();
110        return $this->visitsRepository->resetAll($timestamp);
111    }
112
113    private function getRequestTimestamp(): int
114    {
115        $timestamp = Request::createFromGlobals()->server->get(key: 'REQUEST_TIME');
116
117        return is_int($timestamp) ? $timestamp : time();
118    }
119}