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

Freegle / iznik-server / 418053ef-1b85-44d6-aeef-204bc752e075

26 Jun 2024 12:25PM UTC coverage: 94.239% (-0.6%) from 94.811%
418053ef-1b85-44d6-aeef-204bc752e075

push

circleci

edwh
Test fixes.

0 of 1 new or added line in 1 file covered. (0.0%)

225 existing lines in 5 files now uncovered.

25190 of 26730 relevant lines covered (94.24%)

31.51 hits per line

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

91.96
/include/API.php
1
<?php
2

3
namespace Freegle\Iznik;
4

5

6
require_once(IZNIK_BASE . '/include/db.php');
1✔
7
global $dbhr, $dbhm;
1✔
8

9
use GeoIp2\Database\Reader;
10

11
class API
12
{
13
    public static function headers() {
14
        $call = array_key_exists('call', $_REQUEST) ? $_REQUEST['call'] : NULL;
313✔
15
        $type = array_key_exists('type', $_REQUEST) ? $_REQUEST['type'] : 'GET';
313✔
16

17
        // We allow anyone to use our API.
18
        //
19
        // Suppress errors on the header command for UT
20
        if (!(($call == 'image' || $call == 'profile') && $type == 'GET')) {
313✔
21
            # For images we'll set the content type later.
22
            @header('Content-type: application/json');
312✔
23
        }
24

25
        // Access-Control-Allow-Origin not now added by nginx.
26
        @header('Access-Control-Allow-Origin: *');
313✔
27
        @header('Access-Control-Allow-Headers: ' . (array_key_exists('HTTP_ACCESS_CONTROL_REQUEST_HEADERS', $_SERVER) ? $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'] : "Origin, X-Requested-With, Content-Type, Accept, Authorization, X-Iznik-PHP-Session")); // X-HTTP-Method-Override not needed
313✔
28
        @header('Access-Control-Allow-Credentials: true');
313✔
29
        @header('P3P:CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"');
313✔
30
    }
31

32
    public static function call()
33
    {
34
        global $dbhr, $dbhm;
313✔
35

36
        $scriptstart = microtime(true);
313✔
37

38
        $entityBody = file_get_contents('php://input');
313✔
39

40
        if ($entityBody) {
313✔
41
            $parms = json_decode($entityBody, true);
×
42
            if (json_last_error() == JSON_ERROR_NONE) {
×
43
                # We have been passed parameters in JSON.
44
                foreach ($parms as $parm => $val) {
×
45
                    $_REQUEST[$parm] = $val;
×
46
                }
47
            } else {
48
                # In some environments (e.g. PHP-FPM 7.2) parameters passed as an encoded form for PUT aren't parsed correctly,
49
                # probably because we have a rewrite that adds a parameter to the URL, and PHP-FPM doesn't want to have both
50
                # URL and form parameters.
51
                #
52
                # This may be a bug or a feature, but it messes us up.  So decode anything we can find that has not already
53
                # been decoded by our interpreter (if it did it, it's likely to be better).
54
                #
55
                # We needed this code when the app didn't contain the use of HTTP_X_HTTP_METHOD_OVERRIDE, and it's useful
56
                # anyway in case the client forgets.
57
                parse_str($entityBody, $params);
×
58
                foreach ($params as $key => $val) {
×
59
                    if (!array_key_exists($key, $_REQUEST)) {
×
60
                        $_REQUEST[$key] = $val;
×
61
                    }
62
                }
63
            }
64
        }
65

66
        if (array_key_exists('REQUEST_METHOD', $_SERVER)) {
313✔
67
            $_SERVER['REQUEST_METHOD'] = strtoupper($_SERVER['REQUEST_METHOD']);
313✔
68
            $_REQUEST['type'] = $_SERVER['REQUEST_METHOD'];
313✔
69
        }
70

71
        if (array_key_exists('HTTP_X_HTTP_METHOD_OVERRIDE', $_SERVER)) {
313✔
72
            # Used by Backbone's emulateHTTP to work around servers which don't handle verbs like PATCH very well.
73
            #
74
            # We use this because when we issue a PATCH we don't seem to be able to get the body parameters.
75
            $_REQUEST['type'] = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
1✔
76
            #error_log("Request method override to {$_REQUEST['type']}");
77
        }
78

79
        // Record timing.
80
        list ($tusage, $rusage) = API::requestStart();
313✔
81

82
        API::headers();
313✔
83

84
        // @codeCoverageIgnoreStart
85
        if (file_exists(IZNIK_BASE . '/http/maintenance_on.html')) {
86
            echo json_encode(array('ret' => 111, 'status' => 'Down for maintenance'));
87
            exit(0);
88
        }
89
        // @codeCoverageIgnoreEnd
90

91
        $includetime = microtime(true) - $scriptstart;
313✔
92

93
        # All API calls come through here.
94
        #error_log("Request " . var_export($_REQUEST, TRUE));
95
        #error_log("Server " . var_export($_SERVER, TRUE));
96

97
        if (Utils::pres('HTTP_X_REAL_IP', $_SERVER)) {
313✔
98
            # We jump through hoops to get the real IP address. This is one of them.
99
            $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_REAL_IP'];
×
100
        }
101

102
        if (array_key_exists('model', $_REQUEST)) {
313✔
103
            # Used by Backbone's emulateJSON to work around servers which don't handle requests encoded as
104
            # application/json.
105
            $_REQUEST = array_merge($_REQUEST, json_decode($_REQUEST['model'], true));
1✔
106
            unset($_REQUEST['model']);
1✔
107
        }
108

109
        # Include the API call
110
        $call = Utils::pres('call', $_REQUEST);
313✔
111

112
        if ($call) {
313✔
113
            $fn = IZNIK_BASE . '/http/api/' . $call . '.php';
313✔
114
            if (file_exists($fn)) {
313✔
115
                require_once($fn);
306✔
116
            }
117
        }
118

119
        if (Utils::presdef('type', $_REQUEST, null) == 'OPTIONS') {
313✔
120
            # We don't bother returning different values for different calls.
121
            http_response_code(204);
1✔
122
            @header('Allow: POST, GET, DELETE, PUT');
1✔
123
            @header('Access-Control-Allow-Methods:  POST, GET, DELETE, PUT');
1✔
124
        } else {
125
            # Actual API calls
126
            $ret = array('ret' => 1000, 'status' => 'Invalid API call');
312✔
127
            $t = microtime(true);
312✔
128

129
            # We wrap the whole request in a retry handler.  This is so that we can deal with errors caused by
130
            # conflicts within the Percona cluster.
131
            $apicallretries = 0;
312✔
132

133
            # This is an optimisation for User.php.
134
            if (session_status() !== PHP_SESSION_NONE) {
312✔
135
                $_SESSION['modorowner'] = Utils::presdef('modorowner', $_SESSION, []);
312✔
136
            }
137

138
            $encoded_ret = NULL;
312✔
139

140
            do {
141
                if (Utils::presdef('type', $_REQUEST, null) != 'GET') {
312✔
142
                    # Check that we're not posting from a blocked country.
143
                    try {
144
                        $reader = new Reader(MMDB);
242✔
145
                        $ip = Utils::presdef('REMOTE_ADDR', $_SERVER, null);
242✔
146

147
                        if ($ip) {
242✔
148
                            $record = $reader->country($ip);
233✔
149
                            $country = $record->country->name;
×
150
                            # Failed to look it up.
151
                            $countries = $dbhr->preQuery("SELECT * FROM spam_countries WHERE country = ?;", [$country]);
×
152
                            foreach ($countries as $country) {
9✔
153
                                error_log("Block post from {$country['country']} " . var_export($_REQUEST, true));
×
154
                                $encoded_ret = json_encode(array('ret' => 0, 'status' => 'Success'));
×
155
                                echo $encoded_ret;
×
156
                                break 2;
×
157
                            }
158
                        }
159
                    } catch (\Exception $e) {
233✔
160
                        // Don't log to Sentry - this can happen validly for some IPs (e.g. on CircleCI).
161
                    }
162
                }
163

164
                # Duplicate protection.  We upload multiple images so don't protect against those.
165
                if ((DUPLICATE_POST_PROTECTION > 0) &&
312✔
166
                    array_key_exists('REQUEST_METHOD', $_SERVER) &&
312✔
167
                    ((Utils::presdef('type', $_REQUEST, null) == 'POST' || Utils::presdef('type', $_REQUEST, null) == 'PUT')) &&
312✔
168
                    $call != 'image') {
312✔
169
                    # We want to make sure that we don't get duplicate POST requests within the same session.  We can't do this
170
                    # using information stored in the session because when Redis is used as the session handler, there is
171
                    # no session locking, and therefore two requests in quick succession could be allowed.  So instead
172
                    # we use Redis directly with a roll-your-own mutex.  We use the IP address as a key - if there are
173
                    # multiple sessions from the same IP then we might get false positives/negatives.
174
                    $reqData = $_REQUEST;
197✔
175
                    unset($reqData['requestid']);
197✔
176
                    $reqData['call'] = preg_replace('/(\?|&)requestid=[0-9]+?/', '', $reqData['call']);
197✔
177
                    $url = preg_replace('/(\?|&)requestid=[0-9]+?/', '', $_SERVER['REQUEST_URI']);
197✔
178
                    $req = $url . serialize($reqData);
197✔
179
                    $ip = Utils::presdef('REMOTE_ADDR', $_SERVER, NULL);
197✔
180
                    $lockkey = "POST_LOCK_$ip";
197✔
181
                    $datakey = "POST_DATA_$ip";
197✔
182
                    $uid = uniqid('', true);
197✔
183
                    $predis = new \Redis();
197✔
184
                    $predis->pconnect(REDIS_CONNECT);
197✔
185

186
                    # Get a lock.
187
                    $start = time();
197✔
188
                    $count = 0;
197✔
189

190
                    do {
191
                        $rc = $predis->setNx($lockkey, $uid);
197✔
192

193
                        if ($rc) {
197✔
194
                            # We managed to set it.  Ideally we would set an expiry time to make sure that if we got
195
                            # killed right now, this session wouldn't hang.  But that's an extra round trip on each
196
                            # API call, and the worst case would be a single session hanging, which we can live with.
197

198
                            # Sound out the last POST.
199
                            $last = $predis->get($datakey);
197✔
200

201
                            # Some actions are ok, so we exclude those.
202
                            if (!in_array($call, ['session', 'correlate', 'chatrooms', 'upload']) &&
197✔
203
                                $last ==  $req) {
197✔
204
                                # The last POST request was the same.  So this is a duplicate.
205
                                $predis->del($lockkey);
1✔
206
                                $ret = array(
1✔
207
                                    'ret' => 999,
1✔
208
                                    'text' => 'Duplicate request - rejected.',
1✔
209
                                    'data' => $_REQUEST
1✔
210
                                );
1✔
211
                                $encoded_ret = json_encode($ret);
1✔
212
                                echo $encoded_ret;
1✔
213
                                break 2;
1✔
214
                            }
215

216
                            # The last request wasn't the same.  Save this one.
217
                            $predis->set($datakey, $req);
197✔
218
                            $predis->expire($datakey, DUPLICATE_POST_PROTECTION);
197✔
219

220
                            # We're good to go - release the lock.
221
                            $predis->del($lockkey);
197✔
222
                            break;
197✔
223
                            // @codeCoverageIgnoreStart
224
                        } else {
225
                            # We didn't get the lock - another request for this session must have it.
226
                            $count++;
227
                            #error_log("Sleep for duplicate POST lock key $lockkey attempt $count, waited " . (time() - $start));
228
                            usleep(100000);
229
                        }
230
                    } while (time() < $start + 45);
231
                    // @codeCoverageIgnoreEnd
232
                }
233

234
                try {
235
                    # Each call is inside a file with a suitable name.
236
                    #
237
                    # call_user_func doesn't scale well on multicores with some versions of PHP, so we need can't figure out the function from
238
                    # the call name - use a switch instead.
239
                    switch ($call) {
240
                        case 'abtest':
312✔
241
                            $ret = abtest();
1✔
242
                            break;
1✔
243
                        case 'activity':
311✔
244
                            $ret = activity();
1✔
245
                            break;
1✔
246
                        case 'authority':
310✔
247
                            $ret = authority();
1✔
248
                            break;
1✔
249
                        case 'address':
309✔
250
                            $ret = address();
3✔
251
                            break;
3✔
252
                        case 'alert':
307✔
253
                            $ret = alert();
1✔
254
                            break;
1✔
255
                        case 'adview':
306✔
256
                            # Remove after 2021-05-05.
257
                            $ret = [
×
258
                                'ret' => 0,
×
259
                                'status' => 'Success',
×
260
                                'data' => []
×
261
                            ];
×
262
                            break;
×
263
                        case 'admin':
306✔
264
                            $ret = admin();
3✔
265
                            break;
3✔
266
                        case 'config':
304✔
267
                            $ret = config();
1✔
268
                            break;
1✔
269
                        case 'changes':
303✔
270
                            $ret = changes();
1✔
271
                            break;
1✔
272
                        case 'dashboard':
303✔
273
                            $ret = dashboard();
5✔
274
                            break;
5✔
275
                        case 'error':
300✔
276
                            $ret = error();
1✔
277
                            break;
1✔
278
                        case 'export':
299✔
279
                            $ret = export();
1✔
280
                            break;
1✔
281
                        case 'exception':
298✔
282
                            # For UT
283
                            throw new \Exception();
1✔
284
                        case 'image':
298✔
285
                            $ret = image();
26✔
286
                            break;
26✔
287
                        case 'isochrone':
278✔
288
                            $ret = isochrone();
2✔
289
                            break;
2✔
290
                        case 'jobs':
278✔
291
                            $ret = jobs();
1✔
292
                            break;
1✔
293
                        case 'profile':
277✔
294
                            $ret = profile();
1✔
295
                            break;
1✔
296
                        case 'socialactions':
276✔
297
                            $ret = socialactions();
4✔
298
                            break;
4✔
299
                        case 'messages':
272✔
300
                            $ret = messages();
23✔
301
                            break;
23✔
302
                        case 'message':
259✔
303
                            $ret = message();
72✔
304
                            break;
72✔
305
                        case 'invitation':
200✔
306
                            $ret = invitation();
1✔
307
                            break;
1✔
308
                        case 'item':
199✔
309
                            $ret = item();
4✔
310
                            break;
4✔
311
                        case 'usersearch':
195✔
312
                            $ret = usersearch();
1✔
313
                            break;
1✔
314
                        case 'memberships':
194✔
315
                            $ret = memberships();
30✔
316
                            break;
30✔
317
                        case 'merge':
169✔
318
                            $ret = merge();
2✔
319
                            break;
2✔
320
                        case 'spammers':
167✔
321
                            $ret = spammers();
4✔
322
                            break;
4✔
323
                        case 'session':
164✔
324
                            $ret = session();
48✔
325
                            break;
48✔
326
                        case 'group':
130✔
327
                            $ret = group();
9✔
328
                            break;
9✔
329
                        case 'groups':
121✔
330
                            $ret = groups();
1✔
331
                            break;
1✔
332
                        case 'communityevent':
120✔
333
                            $ret = communityevent();
2✔
334
                            break;
2✔
335
                        case 'domains':
118✔
336
                            $ret = domains();
1✔
337
                            break;
1✔
338
                        case 'locations':
117✔
339
                            $ret = locations();
5✔
340
                            break;
5✔
341
                        case 'logo':
112✔
342
                            $ret = logo();
1✔
343
                            break;
1✔
344
                        case 'modconfig':
111✔
345
                            $ret = modconfig();
10✔
346
                            break;
10✔
347
                        case 'stdmsg':
108✔
348
                            $ret = stdmsg();
3✔
349
                            break;
3✔
350
                        case 'bulkop':
105✔
351
                            $ret = bulkop();
4✔
352
                            break;
4✔
353
                        case 'comment':
101✔
354
                            $ret = comment();
4✔
355
                            break;
4✔
356
                        case 'user':
97✔
357
                            $ret = user();
27✔
358
                            break;
27✔
359
                        case 'chatrooms':
74✔
360
                            $ret = chatrooms();
18✔
361
                            break;
18✔
362
                        case 'chatmessages':
68✔
363
                            $ret = chatmessages();
14✔
364
                            break;
14✔
365
                        case 'poll':
54✔
366
                            $ret = poll();
2✔
367
                            break;
2✔
368
                        case 'request':
52✔
369
                            $ret = request();
1✔
370
                            break;
1✔
371
                        case 'shortlink':
51✔
372
                            $ret = shortlink();
2✔
373
                            break;
2✔
374
                        case 'stories':
49✔
375
                            $ret = stories();
3✔
376
                            break;
3✔
377
                        case 'donations':
47✔
378
                            $ret = donations();
2✔
379
                            break;
2✔
380
                        case 'giftaid':
45✔
381
                            $ret = giftaid();
4✔
382
                            break;
4✔
383
                        case 'status':
41✔
384
                            $ret = status();
1✔
385
                            break;
1✔
386
                        case 'volunteering':
40✔
387
                            $ret = volunteering();
4✔
388
                            break;
4✔
389
                        case 'logs':
36✔
390
                            $ret = logs();
1✔
391
                            break;
1✔
392
                        case 'newsfeed':
35✔
393
                            $ret = newsfeed();
14✔
394
                            break;
14✔
395
                        case 'noticeboard':
23✔
396
                            $ret = noticeboard();
3✔
397
                            break;
3✔
398
                        case 'notification':
21✔
399
                            $ret = notification();
3✔
400
                            break;
3✔
401
                        case 'mentions':
18✔
402
                            $ret = mentions();
1✔
403
                            break;
1✔
404
                        case 'microvolunteering':
17✔
405
                            $ret = microvolunteering();
4✔
406
                            break;
4✔
407
                        case 'team':
13✔
408
                            $ret = team();
1✔
409
                            break;
1✔
410
                        case 'tryst':
12✔
411
                            $ret = tryst();
4✔
412
                            break;
4✔
413
                        case 'src':
8✔
414
                            $ret = src();
1✔
415
                            break;
1✔
416
                        case 'visualise':
7✔
417
                            $ret = visualise();
1✔
418
                            break;
1✔
419
                        case 'echo':
6✔
420
                            $ret = array_merge($_REQUEST, $_SERVER);
2✔
421
                            break;
2✔
422
                        case 'DBexceptionWork':
4✔
423
                            # For UT
424
                            if ($apicallretries < 2) {
1✔
425
                                error_log("Fail DBException $apicallretries");
1✔
426
                                throw new DBException();
1✔
427
                            }
428

429
                            break;
1✔
430
                        case 'DBexceptionFail':
4✔
431
                            # For UT
432
                            throw new DBException();
1✔
433
                        case 'DBleaveTrans':
3✔
434
                            # For UT
435
                            $dbhm->beginTransaction();
1✔
436

437
                            break;
1✔
438
                    }
439

440
                    # If we get here, everything worked.
441
                    if ($call == 'upload') {
312✔
442
                        # Output is handled within the lib.
443
                    } else {
444
                        if (Utils::pres('redirectto', $ret)) {
312✔
445
                            header("Location: {$ret['redirectto']}", true, 302);
2✔
446
                        } else if (Utils::pres('img', $ret)) {
312✔
447
                            # This is an image we want to output.  Can cache forever - if an image changes it would get a new id
UNCOV
448
                            @header('Content-Type: image/jpeg');
×
UNCOV
449
                            @header('Content-Length: ' . strlen($ret['img']));
×
UNCOV
450
                            @header('Cache-Control: max-age=5360000');
×
UNCOV
451
                            print $ret['img'];
×
452
                        } else {
453
                            # This is a normal API call.  Add profiling info.
454
                            $duration = (microtime(true) - $scriptstart);
312✔
455
                            $ret['call'] = $call;
312✔
456
                            $ret['type'] = Utils::presdef('type', $_REQUEST, null);
312✔
457
                            $ret['session'] = session_id();
312✔
458
                            $ret['duration'] = $duration;
312✔
459
                            $ret['cpucost'] = API::getCpuUsage($tusage, $rusage);
312✔
460
                            $ret['dbwaittime'] = $dbhr->getWaitTime() + $dbhm->getWaitTime();
312✔
461
                            $ret['includetime'] = $includetime;
312✔
462
                            //                $ret['remoteaddr'] = Utils::presdef('REMOTE_ADDR', $_SERVER, '-');
463
                            //                $ret['_server'] = $_SERVER;
464

465
                            Utils::filterResult($ret);
312✔
466

467
                            $encoded_ret = json_encode($ret, JSON_PARTIAL_OUTPUT_ON_ERROR);
312✔
468
                            echo $encoded_ret;
312✔
469

470
                            if ($duration > 5000) {
312✔
471
                                # Slow call.
472
                                $stamp = microtime(true);
×
473
                                error_log("Slow API call for user " . Utils::presdef('id', $_SESSION, NULL) . " $call stamp $stamp");
×
474
                                file_put_contents("/tmp/iznik.slowapi.$stamp", "User # " . Utils::presdef('id', $_SESSION, NULL) . " request " . var_export($_REQUEST, true) . " response " . var_export($ret, TRUE));
×
475
                            }
476
                        }
477
                    }
478

479
                    if ($apicallretries > 0) {
312✔
480
                        error_log("API call $call worked after $apicallretries");
1✔
481
                    }
482

483
                    break;
312✔
484
                } catch (\Exception $e) {
1✔
485
                    # This is our retry handler.
486
                    if ($e instanceof DBException) {
1✔
487
                        # This is a DBException.  We want to retry, which means we just go round the loop
488
                        # again.
489
                        error_log(
1✔
490
                            "DB Exception try $apicallretries," . $e->getMessage() . ", " . $e->getTraceAsString()
1✔
491
                        );
1✔
492
                        $apicallretries++;
1✔
493

494
                        if ($apicallretries >= API_RETRIES) {
1✔
495
                            if ($call != 'DBexceptionFail') {
1✔
496
                                # Don't log deliberate exceptions in UT.
497
                                \Sentry\captureException($e);
×
498
                            }
499

500
                            $ret = [
1✔
501
                                'ret' => 997,
1✔
502
                                'status' => 'DB operation failed after retry',
1✔
503
                                'exception' => $e->getMessage()
1✔
504
                            ];
1✔
505
                            $encoded_ret = json_encode($ret);
1✔
506
                            echo $encoded_ret;
1✔
507
                        }
508
                    } else {
509
                        # Something else.
510
                        if ($call != 'exception') {
1✔
511
                            # Don't log deliberate exceptions in UT.
512
                            \Sentry\captureException($e);
×
513
                        }
514

515
                        error_log(
1✔
516
                            "Uncaught exception at " . $e->getFile() . " line " . $e->getLine() . " " . $e->getMessage()
1✔
517
                        );
1✔
518
                        $ret = ['ret' => 998, 'status' => 'Unexpected error', 'exception' => $e->getMessage()];
1✔
519
                        $encoded_ret = json_encode($ret);
1✔
520
                        echo $encoded_ret;
1✔
521
                        break;
1✔
522
                    }
523

524
                    # Make sure the duplicate POST detection doesn't throw us.
525
                    $_REQUEST['retry'] = uniqid('', true);
1✔
526
                }
527
            } while ($apicallretries < API_RETRIES);
1✔
528

529
            if (BROWSERTRACKING && (Utils::presdef('type', $_REQUEST, null) != 'GET') &&
312✔
530
                (gettype($ret) == 'array' && !array_key_exists('nolog', $ret))) {
312✔
531
                # Save off the API call and result, except for the (very frequent) event tracking calls.  Don't
532
                # save GET calls as they don't change the DB and there are a lot of them.
533
                #
534
                # Beanstalk has a limit on the size of job that it accepts; no point trying to log absurdly large
535
                # API requests.
536
                $r = $_REQUEST;
240✔
537
                $headers = Session::getallheaders();
240✔
538
                $r['headers'] = $headers;
240✔
539
                $req = json_encode($r);
240✔
540
                $rsp = $encoded_ret;
240✔
541

542
                if (strlen($req) + strlen($rsp) > 180000) {
240✔
543
                    $req = substr($req, 0, 1000);
1✔
544
                    $rsp = substr($rsp, 0, 1000);
1✔
545
                }
546

547
                $sql = "INSERT INTO logs_api (`userid`, `ip`, `session`, `request`, `response`) VALUES (" .
240✔
548
                    (session_status() !== PHP_SESSION_NONE ? Utils::presdef('id', $_SESSION,'NULL') : 'NULL') .
240✔
549
                    ", '" . Utils::presdef('REMOTE_ADDR', $_SERVER, '') . "', " . $dbhr->quote(session_id()) .
240✔
550
                    ", " . $dbhr->quote($req) . ", " . $dbhr->quote($rsp) . ");";
240✔
551
                $dbhm->background($sql);
240✔
552
            }
553

554
            # Any outstanding transaction is a bug; force a rollback to avoid locks lasting beyond this call.
555
            if ($dbhm->inTransaction()) {
312✔
556
                $dbhm->rollBack();
1✔
557
            }
558

559
            if (session_status() !== PHP_SESSION_NONE) {
312✔
560
                if (Utils::presdef('type', $_REQUEST, null) != 'GET') {
312✔
561
                    # This might have changed things.
562
                    $_SESSION['modorowner'] = [];
242✔
563
                }
564

565
                # Update our last access time for this user.  his is used to return our
566
                # roster status in ChatRoom.php, and also for spotting idle members.
567
                #
568
                # Do this here, as we might not be logged in at the start if we had a persistent token but no PHP session.
569
                $id = Utils::pres('id', $_SESSION);
312✔
570
                $last = intval(Utils::presdef('lastaccessupdate', $_SESSION, 0));
312✔
571
                if ($id && (abs(time() - $last) > 600)) {
312✔
572
                    $dbhm->background("UPDATE users SET lastaccess = NOW() WHERE id = $id;");
239✔
573
                    $_SESSION['lastaccessupdate'] = time();
239✔
574
                }
575
            }
576
        }
577
    }
578

579
    public static function requestStart() {
580
        $dat = getrusage();
313✔
581
        return ([ microtime(true), $dat["ru_utime.tv_sec"]*1e6+$dat["ru_utime.tv_usec"] ]);
313✔
582
    }
583

584
    public static function getCpuUsage($tusage, $rusage) {
585
        $dat = getrusage();
312✔
586
        $dat["ru_utime.tv_usec"] = ($dat["ru_utime.tv_sec"]*1e6 + $dat["ru_utime.tv_usec"]) - $rusage;
312✔
587
        $time = (microtime(true) - $tusage) * 1000000;
312✔
588

589
        // cpu per request
590
        $cpu = $time > 0 ? $dat["ru_utime.tv_usec"] / $time / 1000 : 0;
312✔
591

592
        return $cpu;
312✔
593
    }
594
}
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

© 2025 Coveralls, Inc