Skip to content

Commit fc92fd1

Browse files
author
Pascal MARTINEZ
committed
CHANGE: code refactoring, new UserSessionManager class with clean() and killAll() public methods.
1 parent eb18618 commit fc92fd1

4 files changed

Lines changed: 186 additions & 118 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
# CHANGE LOG: User sessions (z4m_usersessions)
22

3+
## Version 1.1, 2025-06-10
4+
- CHANGE: code refactoring, new `UserSessionManager` class with `clean()` and `killAll()` public methods.
5+
36
## Version 1.0, 2025-04-21
47
First version.

mod/UserSessionManager.php

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
<?php
2+
3+
/*
4+
* ZnetDK, Starter Web Application for rapid & easy development
5+
* See official website https://www.znetdk.fr
6+
* Copyright (C) 2025 Pascal MARTINEZ (contact@znetdk.fr)
7+
* License GNU GPL http://www.gnu.org/licenses/gpl-3.0.html GNU GPL
8+
* --------------------------------------------------------------------
9+
* This program is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU General Public License as published by
11+
* the Free Software Foundation, either version 3 of the License, or
12+
* (at your option) any later version.
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU General Public License for more details.
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
* --------------------------------------------------------------------
20+
* ZnetDK 4 Mobile User sessions class
21+
*
22+
* File version: 1.0
23+
* Last update: 06/10/2025
24+
*/
25+
26+
namespace z4m_usersessions\mod;
27+
28+
/**
29+
* Management of user sessions
30+
*/
31+
class UserSessionManager {
32+
33+
/**
34+
* Clean expired user sessions by calling the PHP session_gc() function.
35+
* @param boolean $isCurrentSessionDestroyed When set to TRUE, the current
36+
* user session is destroyed (PHP session_destroy() function) after session
37+
* cleaning.
38+
* @return string Message indicating the number of cleaned sessions.
39+
*/
40+
static public function clean($isCurrentSessionDestroyed) {
41+
$count = session_gc();
42+
if ($isCurrentSessionDestroyed) {
43+
session_destroy();
44+
}
45+
return \General::getFilledMessage(MOD_Z4M_USERSESSIONS_ACTION_CLEAN_SUCCESS, $count);
46+
}
47+
48+
/**
49+
* Kill all user sessions.
50+
* @param boolean $preserveCurrentSession When TRUE, current session is not
51+
* killed.
52+
* @param boolean $doesWriteAndCloseSession When TRUE, the current session
53+
* data is stored and closed (session_write_close() PHP function) before
54+
* killing the sessions.
55+
* @param boolean $silent When TRUE, no Exception is thrown and the error
56+
* message is returned by the function.
57+
* @return string Message indicating the number of killed sessions.
58+
* @throws \Exception Session killing failed
59+
*/
60+
static public function killAll($preserveCurrentSession, $doesWriteAndCloseSession, $silent) {
61+
if ($doesWriteAndCloseSession === TRUE) {
62+
session_write_close();
63+
}
64+
try {
65+
$count = self::killUserSessions(NULL, NULL, $preserveCurrentSession);
66+
return \General::getFilledMessage(MOD_Z4M_USERSESSIONS_ACTION_KILL_SUCCESS, $count);
67+
} catch (\Exception $ex) {
68+
\General::writeErrorLog(__METHOD__, $ex->getMessage());
69+
if ($silent === TRUE) {
70+
return MOD_Z4M_USERSESSIONS_ACTION_KILL_FAILED;
71+
}
72+
throw new \Exception(MOD_Z4M_USERSESSIONS_ACTION_KILL_FAILED);
73+
}
74+
}
75+
76+
/**
77+
* Extracts session data from session files and convert them to datatable
78+
* rows for display.
79+
* @param array $allSessionsData the datatable rows matching the session
80+
* data found.
81+
* @param string|NULL $loginName Optionally, the login name that the session
82+
* files should match.
83+
* @param string|NULL $applicationKey Optionally, the application key that
84+
* the session files should match.
85+
* @param boolean $withFilePath If TRUE, the session file path is added to
86+
* the returned rows.
87+
* @return bool Value TRUE if session files can be read within the directory
88+
* where session files are stored. FALSE otherwise.
89+
*/
90+
static public function getSessionDataFromFiles(array &$allSessionsData, $loginName = NULL, $applicationKey = NULL, $withFilePath = FALSE) {
91+
$sessionFiles = self::getSessionFiles();
92+
$isSessionSavePathReadable = TRUE;
93+
if (count($sessionFiles) === 0) {
94+
$sessionFiles = [self::getCurrentSessionFile()];
95+
$isSessionSavePathReadable = FALSE;
96+
}
97+
foreach ($sessionFiles as $filepath) {
98+
$sessionFile = new Z4MUserSessionFile($filepath, $loginName, $applicationKey);
99+
$rows = $sessionFile->convertSessionDataToDatalistRows($withFilePath);
100+
if (count($rows) > 0) {
101+
$allSessionsData = array_merge($allSessionsData, $rows);
102+
}
103+
}
104+
self::sortByModificationTime($allSessionsData);
105+
return $isSessionSavePathReadable;
106+
}
107+
108+
/**
109+
* Kills user sessions.
110+
* @param string $loginName Optional, the login name that session file
111+
* should match to be removed.
112+
* @param string $applicationKey Optional, the application key that session
113+
* file should match to be removed.
114+
* @param boolean $preserveCurrentSession When TRUE, the current user
115+
* session is preserved and not killed.
116+
* @return int Number of session files removed.
117+
* @throws \Exception No session file to remove for the specified login name
118+
* and application key.
119+
*/
120+
static public function killUserSessions($loginName = NULL, $applicationKey = NULL, $preserveCurrentSession = TRUE) {
121+
$rows = [];
122+
self::getSessionDataFromFiles($rows, $loginName, $applicationKey, TRUE);
123+
if (count($rows) === 0 && !is_null($loginName) && !is_null($applicationKey)) {
124+
throw new \Exception("No session to kill for user 'login_name={$loginName}' and 'application_key={$applicationKey}'.");
125+
}
126+
$count = 0;
127+
foreach ($rows as $row) {
128+
$sessionFile = new Z4MUserSessionFile($row['file_path']);
129+
if ($preserveCurrentSession === TRUE && $sessionFile->doesMatchCurrentUserSession()) {
130+
continue;
131+
}
132+
$sessionFile->remove();
133+
$count++;
134+
}
135+
return $count;
136+
}
137+
138+
/**
139+
* Returns the PHP session files.
140+
* @return array The absolute file path of the session files found in the
141+
* the directory set to the PHP session.save_path parameter.
142+
*/
143+
static protected function getSessionFiles() {
144+
$sessionSavePath = session_save_path();
145+
return glob($sessionSavePath . DIRECTORY_SEPARATOR . 'sess_*');
146+
}
147+
148+
/**
149+
* Returns the file path of the current user's session
150+
* @return string Session file path
151+
*/
152+
static protected function getCurrentSessionFile() {
153+
$currentSessionFile = new Z4MUserSessionFile();
154+
return $currentSessionFile->getFilePath();
155+
}
156+
157+
/**
158+
* Sorts the specified rows by session file modification time in reverse
159+
* order.
160+
* @param array $rows The rows to sort.
161+
*/
162+
static protected function sortByModificationTime(&$rows) {
163+
usort($rows, function ($a, $b){
164+
return $b['session_timestamp'] - $a['session_timestamp'];
165+
});
166+
}
167+
168+
}

mod/config.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
* --------------------------------------------------------------------
1919
* Parameters of the ZnetDK 4 Mobile User sessions module
2020
*
21-
* File version: 1.0
22-
* Last update: 04/22/2025
21+
* File version: 1.1
22+
* Last update: 06/10/2025
2323
*/
2424

2525

@@ -45,9 +45,9 @@
4545
* Module version number
4646
* @return string Version
4747
*/
48-
define('MOD_Z4M_USERSESSIONS_VERSION_NUMBER','1.0');
48+
define('MOD_Z4M_USERSESSIONS_VERSION_NUMBER','1.1');
4949
/**
5050
* Module version date
5151
* @return string Date in W3C format
5252
*/
53-
define('MOD_Z4M_USERSESSIONS_VERSION_DATE','2025-04-22');
53+
define('MOD_Z4M_USERSESSIONS_VERSION_DATE','2025-06-10');

mod/controller/Z4MUserSessionsCtrl.php

Lines changed: 11 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@
1919
* --------------------------------------------------------------------
2020
* ZnetDK 4 Mobile User sessions module Controller class
2121
*
22-
* File version: 1.0
23-
* Last update: 04/25/2025
22+
* File version: 1.1
23+
* Last update: 06/10/2025
2424
*/
2525

2626
namespace z4m_usersessions\mod\controller;
2727

28-
use \z4m_usersessions\mod\Z4MUserSessionFile;
28+
use \z4m_usersessions\mod\UserSessionManager;
2929

3030
class Z4MUserSessionsCtrl extends \AppController {
3131

@@ -66,7 +66,7 @@ static protected function action_all() {
6666
$rowCount = $request->count;
6767
$response = new \Response();
6868
$rows = [];
69-
$isSessionSavePathReadable = self::getSessionDataFromFiles($rows);
69+
$isSessionSavePathReadable = UserSessionManager::getSessionDataFromFiles($rows);
7070
if (!$isSessionSavePathReadable) {
7171
$sessionSavePath = session_save_path();
7272
$rows[0]['summary'] = MOD_Z4M_USERSESSIONS_SETTINGS_PHP_ROW;
@@ -86,11 +86,9 @@ static protected function action_all() {
8686
static protected function action_clean() {
8787
$method = \Request::getMethod();
8888
$response = new \Response($method !== 'GET');
89-
$count = session_gc();
90-
$message = \General::getFilledMessage(MOD_Z4M_USERSESSIONS_ACTION_CLEAN_SUCCESS, $count);
89+
$message = UserSessionManager::clean($method === 'GET');
9190
if ($method === 'GET') {
92-
session_destroy();
93-
$response->setCustomContent($message);
91+
$response->setCustomContent($message . PHP_EOL);
9492
} else {
9593
$response->setSuccessMessage(NULL, $message);
9694
}
@@ -109,7 +107,7 @@ static protected function action_kill() {
109107
return $response;
110108
}
111109
try {
112-
$count = self::killUserSessions($request->login_name, $request->application_key, FALSE);
110+
$count = UserSessionManager::killUserSessions($request->login_name, $request->application_key, FALSE);
113111
$response->setSuccessMessage(NULL, \General::getFilledMessage(MOD_Z4M_USERSESSIONS_ACTION_KILL_SUCCESS, $count));
114112
} catch (\Exception $ex) {
115113
\General::writeErrorLog(__METHOD__, $ex->getMessage());
@@ -127,119 +125,18 @@ static protected function action_kill() {
127125
static protected function action_killall() {
128126
$request = new \Request();
129127
$response = new \Response();
130-
if ($request::getMethod()=== 'GET') {
131-
// Call by web service: session data are saved before killing sessions
132-
session_write_close();
133-
}
134128
try {
135-
$count = self::killUserSessions(NULL, NULL, $request->preserve_current_session);
136-
$message = \General::getFilledMessage(MOD_Z4M_USERSESSIONS_ACTION_KILL_SUCCESS, $count);
129+
$message = UserSessionManager::killAll($request->preserve_current_session,
130+
$request::getMethod()=== 'GET', $request::getMethod()=== 'GET');
137131
if ($request::getMethod()=== 'GET') {
138-
$response->setCustomContent($message);
132+
$response->setCustomContent($message . PHP_EOL);
139133
} else {
140134
$response->setSuccessMessage(NULL, $message);
141135
}
142136
} catch (\Exception $ex) {
143-
\General::writeErrorLog(__METHOD__, $ex->getMessage());
144-
if ($request::getMethod()=== 'GET') {
145-
$response->setCustomContent(MOD_Z4M_USERSESSIONS_ACTION_KILL_FAILED);
146-
} else {
147-
$response->setFailedMessage(NULL, MOD_Z4M_USERSESSIONS_ACTION_KILL_FAILED);
148-
}
137+
$response->setFailedMessage(NULL, $ex->getMessage());
149138
}
150139
return $response;
151140
}
152141

153-
/**
154-
* Returns the PHP session files.
155-
* @return array The absolute file path of the session files found in the
156-
* the directory set to the PHP session.save_path parameter.
157-
*/
158-
static protected function getSessionFiles() {
159-
$sessionSavePath = session_save_path();
160-
return glob($sessionSavePath . DIRECTORY_SEPARATOR . 'sess_*');
161-
}
162-
163-
/**
164-
* Returns the file path of the current user's session
165-
* @return string Session file path
166-
*/
167-
static protected function getCurrentSessionFile() {
168-
$currentSessionFile = new Z4MUserSessionFile();
169-
return $currentSessionFile->getFilePath();
170-
}
171-
172-
/**
173-
* Extracts session data from session files and convert them to datatable
174-
* rows for display.
175-
* @param array $allSessionsData the datatable rows matching the session
176-
* data found.
177-
* @param string|NULL $loginName Optionally, the login name that the session
178-
* files should match.
179-
* @param string|NULL $applicationKey Optionally, the application key that
180-
* the session files should match.
181-
* @param boolean $withFilePath If TRUE, the session file path is added to
182-
* the returned rows.
183-
* @return bool Value TRUE if session files can be read within the directory
184-
* where session files are stored. FALSE otherwise.
185-
*/
186-
static protected function getSessionDataFromFiles(array &$allSessionsData, $loginName = NULL, $applicationKey = NULL, $withFilePath = FALSE) {
187-
$sessionFiles = self::getSessionFiles();
188-
$isSessionSavePathReadable = TRUE;
189-
if (count($sessionFiles) === 0) {
190-
$sessionFiles = [self::getCurrentSessionFile()];
191-
$isSessionSavePathReadable = FALSE;
192-
}
193-
foreach ($sessionFiles as $filepath) {
194-
$sessionFile = new Z4MUserSessionFile($filepath, $loginName, $applicationKey);
195-
$rows = $sessionFile->convertSessionDataToDatalistRows($withFilePath);
196-
if (count($rows) > 0) {
197-
$allSessionsData = array_merge($allSessionsData, $rows);
198-
}
199-
}
200-
self::sortByModificationTime($allSessionsData);
201-
return $isSessionSavePathReadable;
202-
}
203-
204-
/**
205-
* Sorts the specified rows by session file modification time in reverse
206-
* order.
207-
* @param array $rows The rows to sort.
208-
*/
209-
static protected function sortByModificationTime(&$rows) {
210-
usort($rows, function ($a, $b){
211-
return $b['session_timestamp'] - $a['session_timestamp'];
212-
});
213-
}
214-
215-
/**
216-
* Kills user sessions.
217-
* @param string $loginName Optional, the login name that session file
218-
* should match to be removed.
219-
* @param string $applicationKey Optional, the application key that session
220-
* file should match to be removed.
221-
* @param boolean $preserveCurrentSession When TRUE, the current user
222-
* session is preserved and not killed.
223-
* @return int Number of session files removed.
224-
* @throws \Exception No session file to remove for the specified login name
225-
* and application key.
226-
*/
227-
static protected function killUserSessions($loginName = NULL, $applicationKey = NULL, $preserveCurrentSession = TRUE) {
228-
$rows = [];
229-
self::getSessionDataFromFiles($rows, $loginName, $applicationKey, TRUE);
230-
if (count($rows) === 0 && !is_null($loginName) && !is_null($applicationKey)) {
231-
throw new \Exception("No session to kill for user 'login_name={$loginName}' and 'application_key={$applicationKey}'.");
232-
}
233-
$count = 0;
234-
foreach ($rows as $row) {
235-
$sessionFile = new Z4MUserSessionFile($row['file_path']);
236-
if ($preserveCurrentSession && $sessionFile->doesMatchCurrentUserSession()) {
237-
continue;
238-
}
239-
$sessionFile->remove();
240-
$count++;
241-
}
242-
return $count;
243-
}
244-
245142
}

0 commit comments

Comments
 (0)