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

ericmann / sessionz / 24419782699

14 Apr 2026 07:52PM UTC coverage: 27.407% (+0.07%) from 27.333%
24419782699

push

github

ericmann
v0.5.0: modernize deps, commit lockfile, add PHP 7.4-8.5 CI matrix

- Bump minimum PHP to 7.4; bump defuse/php-encryption to ^2.4
- Replace abandoned PHPUnit ^5.6 with ^9.6 and satooshi/php-coveralls
  with php-coveralls/php-coveralls ^2.7 (closes open vulnerability)
- Commit composer.lock so Dependabot can raise dependency PRs
- Rewrite CI as a PHP 7.4-8.5 matrix using shivammathur/setup-php;
  CI now actually runs PHPUnit (previously only PHPStan ran)
- Split static analysis into its own job on PHP 8.3
- Annotate Manager session-handler methods with #[\ReturnTypeWillChange]
  to silence PHP 8.1+ tentative-return-type deprecations
- Drop .travis.yml; drop "version" field from composer.json

37 of 135 relevant lines covered (27.41%)

0.56 hits per line

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

0.0
/php/Manager.php
1
<?php
2
/**
3
 * Session Manager
4
 *
5
 * Configure the global mechanism that will manage all handler stacks
6
 * and route requests to/from the storage implementations as needed.
7
 *
8
 * @package Sessionz
9
 * @since 1.0.0
10
 */
11
namespace EAMann\Sessionz;
12

13
use EAMann\Sessionz\Handlers\BaseHandler;
14

15
/**
16
 * Implement PHP's native session handling in such a way as to pass requests
17
 * for information through a multi-layered stack of potential handlers in
18
 * a fashion similar to request middleware.
19
 */
20
class Manager implements \SessionHandlerInterface {
21
    protected static $manager;
22

23
    /**
24
     * @var array
25
     */
26
    protected $handlers;
27

28
    /**
29
     * @var array
30
     */
31
    protected $stacks;
32

33
    /**
34
     * Handler stack lock
35
     *
36
     * @var bool
37
     */
38
    protected $handlerLock;
39

40
    public function __construct()
41
    {
42

43
    }
×
44

45
    /**
46
     * Add a handler to the stack.
47
     *
48
     * @param Handler $handler
49
     *
50
     * @return static
51
     */
52
    public function addHandler($handler)
53
    {
54
        if ($this->handlerLock) {
×
55
            throw new \RuntimeException('Session handlers can’t be added once the stack is dequeuing');
×
56
        }
57
        if (is_null($this->handlers)) {
×
58
            $this->seedHandlerStack();
×
59
        }
60

61
        // DELETE
62
        $next_delete = $this->stacks['delete']->top();
×
63
        $this->stacks['delete'][] = function($session_id) use ($handler, $next_delete) {
64
            return call_user_func(array( $handler, 'delete'), $session_id, $next_delete);
×
65
        };
66

67
        // CLEAN
68
        $next_clean = $this->stacks['clean']->top();
×
69
        $this->stacks['clean'][] = function($lifetime) use ($handler, $next_clean) {
70
            return call_user_func(array( $handler, 'clean'), $lifetime, $next_clean);
×
71
        };
72

73
        // CREATE
74
        $next_create = $this->stacks['create']->top();
×
75
        $this->stacks['create'][] = function($path, $name) use ($handler, $next_create) {
76
            return call_user_func(array( $handler, 'create'), $path, $name, $next_create);
×
77
        };
78

79
        // READ
80
        $next_read = $this->stacks['read']->top();
×
81
        $this->stacks['read'][] = function($session_id) use ($handler, $next_read) {
82
            return call_user_func(array( $handler, 'read'), $session_id, $next_read);
×
83
        };
84

85
        // WRITE
86
        $next_write = $this->stacks['write']->top();
×
87
        $this->stacks['write'][] = function($session_id, $session_data) use ($handler, $next_write) {
88
            return call_user_func(array( $handler, 'write'), $session_id, $session_data, $next_write);
×
89
        };
90

91
        return $this;
×
92
    }
93

94
    /**
95
     * Seed handler stack with first callable
96
     *
97
     * @throws \RuntimeException if the stack is seeded more than once
98
     */
99
    protected function seedHandlerStack()
100
    {
101
        if (!is_null($this->handlers)) {
×
102
            throw new \RuntimeException('Handler stacks can only be seeded once.');
×
103
        }
104
        $this->stacks = [];
×
105
        $base = new BaseHandler();
×
106
        $this->handlers = [$base];
×
107

108
        $this->stacks['delete'] = new \SplStack();
×
109
        $this->stacks['clean'] = new \SplStack();
×
110
        $this->stacks['create'] = new \SplStack();
×
111
        $this->stacks['read'] = new \SplStack();
×
112
        $this->stacks['write'] = new \SplStack();
×
113

114
        foreach($this->stacks as $id => $stack) {
×
115
            $stack->setIteratorMode(\SplDoublyLinkedList::IT_MODE_LIFO | \SplDoublyLinkedList::IT_MODE_KEEP);
×
116
            $stack[] = array( $base, $id );
×
117
        }
118
    }
119

120
    /**
121
     * Initialize the session manager.
122
     *
123
     * Invoking this function multiple times will reset the manager itself
124
     * and purge any handlers already registered with the system.
125
     *
126
     * @return Manager
127
     */
128
    public static function initialize()
129
    {
130
        $manager = self::$manager = new self();
×
131
        $manager->seedHandlerStack();
×
132

133
        if( ! headers_sent() ){
×
134
            session_set_save_handler($manager);
×
135
        }
136

137
        return $manager;
×
138
    }
139

140
    /**
141
     * Close the current session.
142
     *
143
     * Will iterate through all handlers registered to the manager and
144
     * remove them from the stack. This has the effect of removing the
145
     * objects from scope and triggering their destructors. Any cleanup
146
     * should happen there.
147
     *
148
     * @return true
149
     */
150
    #[\ReturnTypeWillChange]
151
    public function close()
152
    {
153
        $this->handlerLock = true;
×
154

155
        while (count($this->handlers) > 0) {
×
156
            array_pop($this->handlers);
×
157
            $this->stacks['delete']->pop();
×
158
            $this->stacks['clean']->pop();
×
159
            $this->stacks['create']->pop();
×
160
            $this->stacks['read']->pop();
×
161
            $this->stacks['write']->pop();
×
162
        }
163

164
        $this->handlerLock = false;
×
165
        return true;
×
166
    }
167

168
    /**
169
     * Destroy a session by either invalidating it or forcibly removing
170
     * it from session storage.
171
     *
172
     * @param string $session_id ID of the session to destroy.
173
     *
174
     * @return bool
175
     */
176
    #[\ReturnTypeWillChange]
177
    public function destroy($session_id)
178
    {
179
        if (is_null($this->handlers)) {
×
180
            $this->seedHandlerStack();
×
181
        }
182

183
        /** @var callable $start */
184
        $start = $this->stacks['delete']->top();
×
185
        $this->handlerLock = true;
×
186
        $data = $start($session_id);
×
187
        $this->handlerLock = false;
×
188
        return $data;
×
189
    }
190

191
    /**
192
     * Clean up any potentially expired sessions (sessions with an age
193
     * greater than the specified maximum-allowed lifetime).
194
     *
195
     * @param int $maxlifetime Max number of seconds for which a session is valid.
196
     *
197
     * @return bool
198
     */
199
    #[\ReturnTypeWillChange]
200
    public function gc($maxlifetime)
201
    {
202
        if (is_null($this->handlers)) {
×
203
            $this->seedHandlerStack();
×
204
        }
205

206
        /** @var callable $start */
207
        $start = $this->stacks['clean']->top();
×
208
        $this->handlerLock = true;
×
209
        $data = $start($maxlifetime);
×
210
        $this->handlerLock = false;
×
211
        return $data;
×
212
    }
213

214
    /**
215
     * Create a new session storage.
216
     *
217
     * @param string $save_path File location/path where sessions should be written.
218
     * @param string $name      Unique name of the storage instance.
219
     *
220
     * @return bool
221
     */
222
    #[\ReturnTypeWillChange]
223
    public function open($save_path, $name)
224
    {
225
        if (is_null($this->handlers)) {
×
226
            $this->seedHandlerStack();
×
227
        }
228

229
        /** @var callable $start */
230
        $start = $this->stacks['create']->top();
×
231
        $this->handlerLock = true;
×
232
        $data = $start($save_path, $name);
×
233
        $this->handlerLock = false;
×
234
        return $data;
×
235
    }
236

237
    /**
238
     * Read data from the specified session.
239
     *
240
     * @param string $session_id ID of the session to read.
241
     *
242
     * @return string
243
     */
244
    #[\ReturnTypeWillChange]
245
    public function read($session_id)
246
    {
247
        if (is_null($this->handlers)) {
×
248
            $this->seedHandlerStack();
×
249
        }
250

251
        /** @var callable $start */
252
        $start = $this->stacks['read']->top();
×
253
        $this->handlerLock = true;
×
254
        $data = $start($session_id);
×
255
        $this->handlerLock = false;
×
256
        return $data;
×
257
    }
258

259
    /**
260
     * Write data to a specific session.
261
     *
262
     * @param string $session_id   ID of the session to write.
263
     * @param string $session_data Serialized string of session data.
264
     *
265
     * @return bool
266
     */
267
    #[\ReturnTypeWillChange]
268
    public function write($session_id, $session_data)
269
    {
270
        if (is_null($this->handlers)) {
×
271
            $this->seedHandlerStack();
×
272
        }
273

274
        /** @var callable $start */
275
        $start = $this->stacks['write']->top();
×
276
        $this->handlerLock = true;
×
277
        $data = $start($session_id, $session_data);
×
278
        $this->handlerLock = false;
×
279
        return $data;
×
280
    }
281
}
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