• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

daycry / auth / 22527357078

28 Feb 2026 07:22PM UTC coverage: 63.267% (+0.7%) from 62.568%
22527357078

push

github

daycry
Remove PHP 8.1 from PHPUnit CI matrix

Update .github/workflows/phpunit.yml to drop PHP 8.1 from the test matrix. CI will now run PHPUnit only on PHP 8.2 and 8.3, reducing the matrix to current supported versions.

3064 of 4843 relevant lines covered (63.27%)

41.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.31
/src/Models/DeviceSessionModel.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of Daycry Auth.
7
 *
8
 * (c) Daycry <daycry9@proton.me>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace Daycry\Auth\Models;
15

16
use CodeIgniter\I18n\Time;
17
use Daycry\Auth\Entities\DeviceSession;
18
use Daycry\Auth\Entities\User;
19
use Symfony\Component\Uid\Uuid;
20

21
class DeviceSessionModel extends BaseModel
22
{
23
    protected $primaryKey     = 'id';
24
    protected $returnType     = DeviceSession::class;
25
    protected $useSoftDeletes = false;
26
    protected $allowedFields  = [
27
        'uuid',
28
        'user_id',
29
        'session_id',
30
        'device_name',
31
        'ip_address',
32
        'user_agent',
33
        'last_active',
34
        'logged_out_at',
35
    ];
36
    protected $useTimestamps = true;
37
    protected $createdField  = 'created_at';
38
    protected $updatedField  = 'updated_at';
39
    protected $beforeInsert  = ['generateUuid'];
40

41
    protected function initialize(): void
13✔
42
    {
43
        parent::initialize();
13✔
44

45
        $this->table = $this->tables['device_sessions'];
13✔
46
    }
47

48
    /**
49
     * Generates a UUID v7 for new device session records.
50
     *
51
     * Model event callback called by `beforeInsert`.
52
     *
53
     * @param array<string, mixed> $data
54
     *
55
     * @return array<string, mixed>
56
     */
57
    protected function generateUuid(array $data): array
13✔
58
    {
59
        if (empty($data['data']['uuid'])) {
13✔
60
            $data['data']['uuid'] = Uuid::v7()->toRfc4122();
13✔
61
        }
62

63
        return $data;
13✔
64
    }
65

66
    /**
67
     * Returns all active (not logged out) device sessions for the given user.
68
     *
69
     * @return list<DeviceSession>
70
     */
71
    public function getActiveForUser(User $user): array
8✔
72
    {
73
        return $this->where('user_id', $user->id)
8✔
74
            ->where('logged_out_at', null)
8✔
75
            ->orderBy('last_active', 'DESC')
8✔
76
            ->findAll();
8✔
77
    }
78

79
    /**
80
     * Returns all device sessions (active and terminated) for the given user.
81
     *
82
     * @return list<DeviceSession>
83
     */
84
    public function getAllForUser(User $user): array
×
85
    {
86
        return $this->where('user_id', $user->id)
×
87
            ->orderBy('last_active', 'DESC')
×
88
            ->findAll();
×
89
    }
90

91
    /**
92
     * Finds a device session by its PHP session ID.
93
     */
94
    public function findBySessionId(string $sessionId): ?DeviceSession
4✔
95
    {
96
        return $this->where('session_id', $sessionId)
4✔
97
            ->first();
4✔
98
    }
99

100
    /**
101
     * Creates a new device session record.
102
     */
103
    public function createSession(User $user, string $sessionId, string $ipAddress, ?string $userAgent = null): DeviceSession
13✔
104
    {
105
        $deviceName = DeviceSession::parseUserAgent($userAgent ?? '');
13✔
106

107
        $data = [
13✔
108
            'user_id'     => $user->id,
13✔
109
            'session_id'  => $sessionId,
13✔
110
            'device_name' => $deviceName,
13✔
111
            'ip_address'  => $ipAddress,
13✔
112
            'user_agent'  => $userAgent,
13✔
113
            'last_active' => Time::now()->format('Y-m-d H:i:s'),
13✔
114
        ];
13✔
115

116
        $id = $this->insert($data, true);
13✔
117

118
        /** @var DeviceSession|null $result */
119
        return $this->find($id);
13✔
120
    }
121

122
    /**
123
     * Updates the last_active timestamp for the given session.
124
     */
125
    public function touchSession(string $sessionId): void
1✔
126
    {
127
        $this->where('session_id', $sessionId)
1✔
128
            ->where('logged_out_at', null)
1✔
129
            ->set('last_active', Time::now()->format('Y-m-d H:i:s'))
1✔
130
            ->update();
1✔
131
    }
132

133
    /**
134
     * Marks the session as terminated by setting logged_out_at.
135
     */
136
    public function terminateSession(string $sessionId): void
3✔
137
    {
138
        $this->where('session_id', $sessionId)
3✔
139
            ->where('logged_out_at', null)
3✔
140
            ->set('logged_out_at', Time::now()->format('Y-m-d H:i:s'))
3✔
141
            ->update();
3✔
142
    }
143

144
    /**
145
     * Terminates all active sessions for a user, optionally keeping one session alive.
146
     *
147
     * @param string|null $exceptSessionId Session ID to keep active (e.g. current session)
148
     */
149
    public function terminateAllForUser(User $user, ?string $exceptSessionId = null): void
4✔
150
    {
151
        $builder = $this->where('user_id', $user->id)
4✔
152
            ->where('logged_out_at', null);
4✔
153

154
        if ($exceptSessionId !== null && $exceptSessionId !== '') {
4✔
155
            $builder = $builder->where('session_id !=', $exceptSessionId);
2✔
156
        }
157

158
        $builder->set('logged_out_at', Time::now()->format('Y-m-d H:i:s'))
4✔
159
            ->update();
4✔
160
    }
161

162
    /**
163
     * Removes old terminated sessions older than the given number of days.
164
     */
165
    public function purgeOldSessions(int $days = 30): void
1✔
166
    {
167
        $cutoff = Time::now()->subDays($days)->format('Y-m-d H:i:s');
1✔
168

169
        $this->where('logged_out_at <', $cutoff)
1✔
170
            ->delete();
1✔
171
    }
172
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc