Completed
Push — development ( 9c238f...56a7e3 )
by Thomas
21s
created

cache::getListingOutdatedRelativeToLog()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 28
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 14
nc 2
nop 1
dl 0
loc 28
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/***************************************************************************
3
 * for license information see LICENSE.md
4
 *   get/set has to be committed with save
5
 *   add/remove etc. is executed instantly
6
 ***************************************************************************/
7
8
require_once __DIR__ . '/logtypes.inc.php';
9
10
class cache
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
11
{
12
    public $nCacheId = 0;
13
14
    public $reCache;
15
16
    /**
17
     * @param $wp
18
     * @return int|null
19
     */
20
    public static function cacheIdFromWP($wp)
21
    {
22
        if (mb_strtoupper(mb_substr($wp, 0, 2)) === 'GC') {
23
            $rs = sql("SELECT `cache_id` FROM `caches` WHERE `wp_gc_maintained`='&1'", $wp);
24
            if (sql_num_rows($rs) !== 1) {
25
                sql_free_result($rs);
26
27
                return null;
28
            }
29
            $r = sql_fetch_assoc($rs);
30
            sql_free_result($rs);
31
32
            $cacheId = $r['cache_id'];
33
        } else {
34
            $cacheId = sql_value("SELECT `cache_id` FROM `caches` WHERE `wp_oc`='&1'", 0, $wp);
35
        }
36
37
        return $cacheId;
38
    }
39
40
    /**
41
     * @param $wp
42
     * @return cache|null
43
     */
44
    public static function fromWP($wp)
45
    {
46
        $cacheId = self::cacheIdFromWP($wp);
47
        if ($cacheId === 0) {
48
            return null;
49
        }
50
51
        return new cache($cacheId);
52
    }
53
54
    /**
55
     * @param $uuid
56
     * @return mixed
57
     */
58
    public static function cacheIdFromUUID($uuid)
59
    {
60
        $cacheId = sql_value("SELECT `cache_id` FROM `caches` WHERE `uuid`='&1'", 0, $uuid);
61
62
        return $cacheId;
63
    }
64
65
    /**
66
     * @param $uuid
67
     * @return cache|null
68
     */
69
    public static function fromUUID($uuid)
70
    {
71
        $cacheId = self::cacheIdFromUUID($uuid);
72
        if ($cacheId === 0) {
73
            return null;
74
        }
75
76
        return new cache($cacheId);
77
    }
78
79
    public function __construct($nNewCacheId = ID_NEW)
80
    {
81
        $this->reCache = new rowEditor('caches');
82
        $this->reCache->addPKInt('cache_id', null, false, RE_INSERT_AUTOINCREMENT);
83
        $this->reCache->addString('uuid', '', false, RE_INSERT_AUTOUUID);
84
        $this->reCache->addInt('node', 0, false);
85
        $this->reCache->addDate('date_created', time(), true, RE_INSERT_IGNORE);
86
        $this->reCache->addDate('last_modified', time(), true, RE_INSERT_IGNORE);
87
        $this->reCache->addDate('listing_last_modified', time(), true, RE_INSERT_IGNORE);
88
        $this->reCache->addInt('user_id', 0, false);
89
        $this->reCache->addString('name', '', false);
90
        $this->reCache->addDouble('longitude', 0, false);
91
        $this->reCache->addDouble('latitude', 0, false);
92
        $this->reCache->addInt('type', 1, false);
93
        $this->reCache->addInt('status', 5, false);
94
        $this->reCache->addString('country', '', false);
95
        $this->reCache->addDate('date_hidden', time(), false);
96
        $this->reCache->addInt('size', 1, false);
97
        $this->reCache->addFloat('difficulty', 1, false);
98
        $this->reCache->addFloat('terrain', 1, false);
99
        $this->reCache->addString('logpw', '', false);
100
        $this->reCache->addFloat('search_time', 0, false);
101
        $this->reCache->addFloat('way_length', 0, false);
102
        $this->reCache->addString('wp_oc', null, true);
103
        $this->reCache->addString('wp_gc', '', false);
104
        $this->reCache->addString('wp_gc_maintained', '', false);
105
        $this->reCache->addString('wp_nc', '', false);
106
        $this->reCache->addString('desc_languages', '', false, RE_INSERT_IGNORE);
107
        $this->reCache->addString('default_desclang', '', false);
108
        $this->reCache->addDate('date_activate', null, true);
109
        $this->reCache->addInt('need_npa_recalc', 1, false, RE_INSERT_IGNORE);
110
        $this->reCache->addInt('show_cachelists', 1, false);
111
        $this->reCache->addInt('protect_old_coords', 0, false);
112
        $this->reCache->addInt('needs_maintenance', 0, false);
113
        $this->reCache->addInt('listing_outdated', 0, false);
114
        $this->reCache->addDate('flags_last_modified', '0000-00-00 00:00:00', false);
115
116
        $this->nCacheId = $nNewCacheId + 0;
117
118
        if ($nNewCacheId == ID_NEW) {
119
            $this->reCache->addNew(null);
120
        } else {
121
            $this->reCache->load($this->nCacheId);
122
        }
123
    }
124
125
    /**
126
     * @return bool
127
     */
128
    public function exist()
129
    {
130
        return $this->reCache->exist();
131
    }
132
133
    /**
134
     * @return int
135
     */
136
    public function getCacheId()
137
    {
138
        return $this->nCacheId;
139
    }
140
141
    public function getStatus()
142
    {
143
        return $this->reCache->getValue('status');
144
    }
145
146
    public function getType()
147
    {
148
        return $this->reCache->getValue('type');
149
    }
150
151
    public function getName()
152
    {
153
        return $this->reCache->getValue('name');
154
    }
155
156
    public function getLongitude()
157
    {
158
        return $this->reCache->getValue('longitude');
159
    }
160
161
    public function getLatitude()
162
    {
163
        return $this->reCache->getValue('latitude');
164
    }
165
166
    public function getUserId()
167
    {
168
        return $this->reCache->getValue('user_id');
169
    }
170
171
    public function getUsername()
172
    {
173
        return sql_value("SELECT `username` FROM `user` WHERE `user_id`='&1'", '', $this->getUserId());
174
    }
175
176
    public function getWPOC()
177
    {
178
        return $this->reCache->getValue('wp_oc');
179
    }
180
181
    public function getWPGC()
182
    {
183
        return $this->reCache->getValue('wp_gc');
184
    }
185
186
    public function getWPGC_maintained()
187
    {
188
        return $this->reCache->getValue('wp_gc_maintained');
189
    }
190
191
    public function getUUID()
192
    {
193
        return $this->reCache->getValue('uuid');
194
    }
195
196
    public function getDateCreated()
197
    {
198
        return $this->reCache->getValue('date_created');
199
    }
200
201
    public function getLastModified()
202
    {
203
        return $this->reCache->getValue('last_modified');
204
    }
205
206
    public function getListingLastModified()
207
    {
208
        return $this->reCache->getValue('listing_last_modified');
209
    }
210
211
    public function getNode()
212
    {
213
        return $this->reCache->getValue('node');
214
    }
215
216
    public function setNode($value)
217
    {
218
        return $this->reCache->setValue('node', $value);
219
    }
220
221
    public function setStatus($value)
222
    {
223
        global $login;
224
        if (sql_value("SELECT COUNT(*) FROM `cache_status` WHERE `id`='&1'", 0, $value) == 1) {
225
            sql("SET @STATUS_CHANGE_USER_ID='&1'", $login->userid);
226
227
            return $this->reCache->setValue('status', $value);
228
        } else {
229
            return false;
230
        }
231
    }
232
233
    public function getDescLanguages()
234
    {
235
        return explode($this->reCache->getValue('desc_languages'), ',');
236
    }
237
238
    public function getDefaultDescLanguage()
239
    {
240
        return $this->reCache->getValue('default_desclang');
241
    }
242
243
    public function getProtectOldCoords()
244
    {
245
        return $this->reCache->getValue('protect_old_coords');
246
    }
247
248
    public function setProtectOldCoords($value)
249
    {
250
        return $this->reCache->setValue('protect_old_coords', $value);
251
    }
252
253
    // cache condition flags
254
    public function getNeedsMaintenance()
255
    {
256
        return $this->reCache->getValue('needs_maintenance');
257
    }
258
259
    public function setNeedsMaintenance($value)
260
    {
261
        return $this->reCache->setValue('needs_maintenance', $value);
262
    }
263
264
    public function getListingOutdated()
265
    {
266
        return $this->reCache->getValue('listing_outdated');
267
    }
268
269
    public function getListingOutdatedLogUrl()
270
    {
271
        $url = '';
272
        $rs = sql(
273
            "SELECT `id`, `listing_outdated`
274
             FROM `cache_logs`
275
             WHERE `cache_id`='&1'
276
             AND `listing_outdated`>0
277
             ORDER BY `order_date` DESC, `date_created` DESC, `id` DESC",
278
            // same sorting order as in DB function sp_update_logstat()
279
            $this->getCacheId()
280
        );
281
        if ($r = sql_fetch_assoc($rs)) {
282
            if ($r['listing_outdated'] == 2) {
283
                $url = 'viewlogs.php?cacheid=' . $this->getCacheId() . '#log' . $r['id'];
284
            }
285
        }
286
        sql_free_result($rs);
287
288
        return $url;
289
    }
290
291
    // test if af the time of insertion of an existing log the listing
292
    // was outdated and there is no newer listing-[not]-outdated-log
293
294
    public function getListingOutdatedRelativeToLog($logId)
295
    {
296
        $logDate = sql_value("SELECT `order_date` FROM `cache_logs` WHERE `id`='&1'", '', $logId);
297
298
        return
299
            sql_value(  // Is there a newer log with LO flag?
300
                "SELECT 1
301
                 FROM `cache_logs`
302
                 WHERE `cache_id`='&1'
303
                 AND `order_date`>'&2'
304
                 AND `listing_outdated`>0
305
                 LIMIT 1",
306
                0,
307
                $this->getCacheId(),
308
                $logDate
309
            ) == 0 && sql_value(  // Was the listing outdated before the log was posted?
310
                "SELECT `listing_outdated`
311
                 FROM `cache_logs`
312
                 WHERE `cache_id`='&1'
313
                 AND `listing_outdated`>0
314
                 AND `id`!='&2'
315
                 ORDER BY `order_date` DESC, `date_created` DESC, `id` DESC
316
                 LIMIT 1",
317
                0,
318
                $this->getCacheId(),
319
                $logId
320
            ) == 2;
321
    }
322
323
    public function setListingOutdated($value)
324
    {
325
        return $this->reCache->setValue('listing_outdated', $value);
326
    }
327
328
    public function getConditionHistory()
329
    {
330
        $cache_id = $this->nCacheId;
331
        $rs = sql(
332
            "SELECT `date`, `needs_maintenance`, `listing_outdated`
333
         FROM `cache_logs`
334
         WHERE `cache_id`='&1' AND (`needs_maintenance` IS NOT NULL OR `listing_outdated` IS NOT NULL)
335
         ORDER BY `date`, `id`",
336
            $cache_id
337
        );
338
        $nm = $lo = 0;
339
        $cond = [
340
            [
341
                'needs_maintenance' => $nm,
342
                'listing_outdated' => $lo,
343
                'date' => '0000-00-00 00:00:00'
344
            ]
345
        ];
346
        while ($r = sql_fetch_assoc($rs)) {
347
            if ($r['needs_maintenance'] != $nm || $r['listing_outdated'] != $lo) {
348
                $nm = $r['needs_maintenance'];
349
                $lo = $r['listing_outdated'];
350
                $cond[] = [
351
                    'needs_maintenance' => $nm,
352
                    'listing_outdated' => $lo,
353
                    'date' => $r['date']
354
                ];
355
            }
356
        }
357
        sql_free_result($rs);
358
        $cond[] = [
359
            'needs_maintenance' => $nm,
360
            'listing_outdated' => $lo,
361
            'date' => '9999-12-31 23:59:59'
362
        ];
363
364
        return $cond;
365
    }
366
367
    /**
368
     * @param int $attr_id
369
     * @return bool
370
     */
371
    public function hasAttribute($attr_id)
372
    {
373
        return sql_value(
374
            "SELECT 1
375
             FROM `caches_attributes` `ca`
376
             WHERE `ca`.`cache_id`='&1'
377
             AND `attrib_id`='&2'",
378
            0,
379
            $this->nCacheId,
380
            $attr_id
381
        ) == 1;
382
    }
383
384
    // other
385
    public function getAnyChanged()
386
    {
387
        return $this->reCache->getAnyChanged();
388
    }
389
390
    // return if successful (with insert)
391
    public function save()
392
    {
393
        if ($this->reCache->save()) {
394
            sql_slave_exclude();
395
396
            return true;
397
        } else {
398
            return false;
399
        }
400
    }
401
402
    public function requireLogPW()
403
    {
404
        return $this->reCache->getValue('logpw') != '';
405
    }
406
407
    // TODO: use prepared one way hash
408
    public function validateLogPW($nLogType, $sLogPW)
409
    {
410
        if (!$this->requireLogPW()) {
411
            return true;
412
        }
413
414
        if (sql_value("SELECT `require_password` FROM `log_types` WHERE `id`='&1'", 0, $nLogType) == 0) {
415
            return true;
416
        }
417
418
        return ($sLogPW == $this->reCache->getValue('logpw'));
419
    }
420
421
    /**
422
     * @param $nVisitUserId
423
     * @param $sRemoteAddr
424
     * @param $nCacheId
425
     * @return void
426
     */
427
    public static function visitCounter($nVisitUserId, $sRemoteAddr, $nCacheId)
0 ignored issues
show
Coding Style introduced by
visitCounter uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
428
    {
429
        global $opt, $_SERVER;
430
431
        // delete cache_visits older 1 day 60*60*24 = 86400
432
        sql(
433
            "DELETE FROM `cache_visits`
434
             WHERE `cache_id`='&1'
435
             AND `user_id_ip`!='0'
436
             AND NOW()-`last_modified`>86400",
437
            $nCacheId
438
        );
439
440
        if ($nVisitUserId == 0) {
441
            $se = explode(';', $opt['logic']['search_engines']);
442
            $ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : "";
443
            foreach ($se as $s) {
444
                if (strpos($ua, $s) !== false) {
445
                    return;
446
                }
447
            }   // do not count search engine views
448
449
            $sIdentifier = $sRemoteAddr;
450
        } else {
451
            $sIdentifier = $nVisitUserId;
452
        }
453
454
        // note the visit of this user
455
        sql(
456
            "INSERT INTO `cache_visits` (`cache_id`, `user_id_ip`, `count`)
457
             VALUES (&1, '&2', 1)
458
             ON DUPLICATE KEY UPDATE `count`=`count`+1",
459
            $nCacheId,
460
            $sIdentifier
461
        );
462
463
        // if the previous statement does an INSERT, it was the first visit for this user today
464
        if (sql_affected_rows() == 1) {
465
            if ($nVisitUserId != sql_value("SELECT `user_id` FROM `caches` WHERE `cache_id`='&1'", 0, $nCacheId)) {
466
                // increment the counter for this cache
467
                sql(
468
                    "INSERT INTO `cache_visits` (`cache_id`, `user_id_ip`, `count`)
469
                     VALUES (&1, '0', 1)
470
                     ON DUPLICATE KEY UPDATE `count`=`count`+1",
471
                    $nCacheId
472
                );
473
            }
474
        }
475
    }
476
477
    /**
478
     * @param $cacheId
479
     * @return array
480
     */
481
    public static function getLogsCount($cacheId)
482
    {
483
        //prepare the logs
484
        $rsLogs = sql("SELECT COUNT(*) FROM `cache_logs` WHERE `cache_id`='&1'", $cacheId);
485
        $rLog = sql_fetch_assoc($rsLogs);
486
        sql_free_result($rsLogs);
487
488
        return $rLog;
489
    }
490
491
    /**
492
     * @param integer $start
493
     * @param integer $count
494
     * @param bool $deleted
495
     * @param bool $protect_old_coords
496
     * @return array
497
     */
498
    public function getLogsArray($start, $count, $deleted = false, $protect_old_coords = false)
499
    {
500
        global $login, $translate;
501
502
        // negative or abornally high numbers like 1.0E+15 can crash the LIMIT statement
503
        if ($count <= 0 || $count > 10000) {
504
            return [];
505
        }
506
507
        $coords = [];
508
        if ($this->getType() != 7 && $this->getType() != 8 &&  // quiz cache
509
            $this->hasAttribute(61) != true &&                 // safari cache
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
510
            $this->getStatus() != 5                            // unpublished cache
511
        ) {
512
            $rsCoords = sql(
513
                "SELECT
514
                    `date_created` `date`,
515
                    `date_created` - INTERVAL 4 SECOND AS `adjusted_date_created`,
516
                     /*
517
                        The first cache_coordinates entry is created immediately after
518
                        creating inserting cache into caches table. This usually takes
519
                        a few milliseconds. We apply a 4 seconds 'safety margin' for
520
                        detecting the original coords. In the extremly unlikely case that
521
                        it took more than 4 seconds AND the owner did a coordinate change
522
                        before publish AND someone posts a log entry with date inbetween
523
                        the coord change and the publish, the coords before the change
524
                        will be exposed (which would be unpleasant but not critical).
525
526
                        Increasing the 4 seconds lag would increase the chance that
527
                        original coords which were set immediately before publish are
528
                        hidden (which would be unpleasant but not critical).
529
530
                        4 seconds was chosen as estimated shortest interval someone will
531
                        need to change coordinates of an unpublished cache AND publish
532
                        it, minus 1 second.
533
                     */
534
                    `latitude`,
535
                    `longitude`
536
                 FROM `cache_coordinates`
537
                 WHERE `cache_id`='&1'
538
                 ORDER BY `date_created` DESC",
539
                $this->nCacheId
540
            );
541
            $publish_date = $this->getDateCreated();
542
            while ($rCoord = sql_fetch_assoc($rsCoords)) {
543
                $coords[] = $rCoord;
544
                if (strtotime($rCoord['adjusted_date_created']) <= $publish_date) {
545
                    // This are the cache coords at publish time, which we will
546
                    // show as "original coords". Older coordinate changes
547
                    // (before publish) are discarded.
548
                    break;
549
                }
550
            }
551
            sql_free_result($rsCoords);
552
        }
553
554
        if ($deleted && ($login->admin && ADMIN_USER) > 0) {
555
            // admins may view owner-deleted logs
556
            $table = 'cache_logs_archived';
557
            $delfields = 'IFNULL(`u2`.`username`,"") AS `deleted_by_name`, `deletion_date`, "1" AS `deleted`';
558
            $addjoin = 'LEFT JOIN `user` `u2` ON `u2`.`user_id`=`cache_logs`.`deleted_by`';
559
        } else {
560
            $table = 'cache_logs';
561
            $delfields = '"" AS `deleted_by_name`, NULL AS `deletion_date`, "0" AS `deleted`';
562
            $addjoin = '';
563
        }
564
565
        $rsLogs = sql(
566
            'SELECT `cache_logs`.`user_id` AS `userid`,
567
                    `cache_logs`.`id` AS `id`,
568
                    `cache_logs`.`uuid` AS `uuid`,
569
                    `cache_logs`.`date` AS `date`,
570
                    `cache_logs`.`order_date` AS `order_date`,
571
                    `cache_logs`.`entry_last_modified`,
572
                    DATEDIFF(`cache_logs`.`entry_last_modified`, `cache_logs`.`date_created`) >= 1 AS `late_modified`,
573
                    substr(`cache_logs`.`date`,12) AS `time`,  /* 00:00:01 = 00:00 logged, 00:00:00 = no time */
574
                    `cache_logs`.`type` AS `type`,
575
                    `cache_logs`.`oc_team_comment` AS `oc_team_comment`,
576
                    `cache_logs`.`needs_maintenance` AS `needs_maintenance`,
577
                    `cache_logs`.`listing_outdated` AS `listing_outdated`,
578
                    `cache_logs`.`text` AS `text`,
579
                    `cache_logs`.`text_html` AS `texthtml`,
580
                    `cache_logs`.`picture`,
581
                    ' . $delfields . ",
582
                    `user`.`username` AS `username`,
583
                    IF(ISNULL(`cache_rating`.`cache_id`), 0, `cache_logs`.`type` IN (1,7)) AS `recommended`,
584
                    FALSE AS `cache_moved`
585
             FROM $table AS `cache_logs`
586
             INNER JOIN `user`
587
                 ON `user`.`user_id` = `cache_logs`.`user_id`
588
             LEFT JOIN `cache_rating`
589
                 ON `cache_logs`.`cache_id`=`cache_rating`.`cache_id`
590
                 AND `cache_logs`.`user_id`=`cache_rating`.`user_id`
591
                 AND `cache_logs`.`date`=`cache_rating`.`rating_date`
592
             " . $addjoin . "
593
             WHERE `cache_logs`.`cache_id`='&1'
594
             ORDER BY `cache_logs`.`order_date` DESC, `cache_logs`.`date_created` DESC, `id` DESC
595
             LIMIT &2, &3",
596
            $this->nCacheId,
597
            $start + 0,
598
            $count + 0
599
        );
600
601
        $logs = [];
602
        $coord_changes = (count($coords) > 1);
603
        if ($coord_changes) {
604
            $coordpos = 0;
605
            $current_coord = new coordinate($coords[0]['latitude'], $coords[0]['longitude']);
606
        }
607
608
        while ($rLog = sql_fetch_assoc($rsLogs)) {
609
            $pictures = [];
610
            $rsPictures = sql(
611
                "SELECT `url`, `title`, `uuid`, `id`, `spoiler`
612
                 FROM `pictures`
613
                 WHERE `object_id`='&1'
614
                 AND `object_type`=1
615
                 ORDER BY `seq`",
616
                $rLog['id']
617
            );
618
            while ($rPicture = sql_fetch_assoc($rsPictures)) {
619
                if (trim($rPicture['title']) == '') {
620
                    $rPicture['title'] = $translate->t('Picture', '', '', 0) . ' ' . (count($pictures) + 1);
621
                }
622
                $pictures[] = $rPicture;
623
            }
624
            sql_free_result($rsPictures);
625
            $rLog['pictures'] = $pictures;
626
            $rLog['text'] = use_current_protocol_in_html($rLog['text']);
627
628
            if ($coord_changes) {
629
                $newcoord = false;
630
                while ($coordpos < count($coords) && $coords[$coordpos]['date'] > $rLog['order_date']) {
0 ignored issues
show
Bug introduced by
The variable $coordpos does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
631
                    if (!$newcoord) {
632
                        $newcoord = new coordinate($coords[$coordpos]['latitude'], $coords[$coordpos]['longitude']);
633
                    }
634
                    ++$coordpos;
635
                }
636
                if ($newcoord) {
637
                    if ($coordpos < count($coords)) {
638
                        $distance = geomath::calcDistance(
639
                            $newcoord->nLat,
640
                            $newcoord->nLon,
641
                            $coords[$coordpos]['latitude'],
642
                            $coords[$coordpos]['longitude']
643
                        );
644
                        if (abs($distance) > 0.005) {
645
                            $rLog['newcoord'] = $newcoord->getDecimalMinutes($protect_old_coords && $new != $current_coord);
0 ignored issues
show
Bug introduced by
The variable $new does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $current_coord does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
646
                            if ($protect_old_coords) {
647
                                $rLog['movedbykm'] = false;
648
                            } elseif ($distance <= 1) {
649
                                $rLog['movedbym'] = floor($distance * 1000);
650
                            } elseif ($distance < 10) {
651
                                $rLog['movedbykm'] = sprintf('%1.1f', $distance);
652
                            } else {
653
                                $rLog['movedbykm'] = round($distance);
654
                            }
655
                            $rLog['cache_moved'] = true;
656
                        }
657
                    } else {
658
                        // This is the original coord of the cache.
659
                        $rLog['newcoord'] = $newcoord->getDecimalMinutes($protect_old_coords);
660
                        $rLog['cache_moved'] = false;
661
                    }
662
                }
663
            }
664
665
            $logs[] = $rLog;
666
        }
667
        sql_free_result($rsLogs);
668
669
        // Append a dummy log entry for the original coordinate, if it was
670
        // not added to a a real log entry because there are logs older than the
671
        // OC cache listing (cmp. https://redmine.opencaching.de/issues/1102):
672
673
        if ($coord_changes && $coordpos < count($coords) && count($logs) > 0) {
674
            $coord = new coordinate($coords[$coordpos]['latitude'], $coords[$coordpos]['longitude']);
675
            $logs[] = [
676
                'newcoord' => $coord->getDecimalMinutes($protect_old_coords),
677
                'cache_moved' => false,
678
                'type' => false
679
            ];
680
        }
681
682
        return $logs;
683
    }
684
685
    /**
686
     * @param $userId
687
     * @param $reportReason
688
     * @param $reportNote
689
     * @return bool
690
     */
691
    public function report($userId, $reportReason, $reportNote)
692
    {
693
        sql(
694
            "INSERT INTO cache_reports (`cacheid`, `userid`, `reason`, `note`)
695
             VALUES(&1, &2, &3, '&4')",
696
            $this->nCacheId,
697
            $userId,
698
            $reportReason,
699
            $reportNote
700
        );
701
702
        return true;
703
    }
704
705
    /**
706
     * @param $userId
707
     * @return bool|string
708
     */
709
    public function addAdoption($userId)
710
    {
711
        if ($this->allowEdit() == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
712
            return "noaccess";
713
        }
714
715
        if (sql_value("SELECT COUNT(*) FROM `user` WHERE `user_id`='&1'", 0, $userId) == 0) {
716
            return "userunknown";
717
        }
718
719
        if (sql_value("SELECT COUNT(*) FROM `user` WHERE `user_id`='&1' AND `is_active_flag`=1", 0, $userId) == 0) {
720
            return "userdisabled";
721
        }
722
723
        // same user?
724
        if ($this->getUserId() == $userId) {
725
            return "self";
726
        }
727
728
        sql(
729
            "INSERT IGNORE INTO `cache_adoption` (`cache_id`, `user_id`)
730
             VALUES ('&1', '&2')",
731
            $this->nCacheId,
732
            $userId
733
        );
734
735
        return true;
736
    }
737
738
    /**
739
     * @param int $userId
740
     * @return bool
741
     */
742
    public function cancelAdoption($userId)
743
    {
744
        global $login;
745
746
        if ($this->allowEdit() == false && $login->userid != $userId) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
747
            return false;
748
        }
749
750
        sql(
751
            "DELETE FROM `cache_adoption`
752
             WHERE `user_id`='&1'
753
             AND `cache_id`='&2'",
754
            $userId,
755
            $this->nCacheId
756
        );
757
758
        return true;
759
    }
760
761
    /**
762
     * @param int $userId
763
     * @return bool
764
     */
765
    public function commitAdoption($userId)
766
    {
767
        global $login;
768
769
        // cache_adoption exists?
770
        if (sql_value(
771
                "SELECT COUNT(*) FROM `cache_adoption` WHERE `cache_id`='&1' AND `user_id`='&2'",
772
                0,
773
                $this->nCacheId,
774
                $userId
775
            ) == 0
776
        ) {
777
            return false;
778
        }
779
780
        // new user active?
781
        if (sql_value("SELECT `is_active_flag` FROM `user` WHERE `user_id`='&1'", 0, $userId) != 1) {
782
            return false;
783
        }
784
785
        sql(
786
            "INSERT INTO `logentries` (`module`, `eventid`, `userid`, `objectid1`, `objectid2`, `logtext`)
787
             VALUES ('cache', 5, '&1', '&2', '&3', '&4')",
788
            $login->userid,
789
            $this->nCacheId,
790
            0,
791
            'Cache ' . sql_escape($this->nCacheId) . ' has changed the owner from userid ' . sql_escape(
792
                $this->getUserId()
793
            ) . ' to ' . sql_escape($userId) . ' by ' . sql_escape($login->userid)
794
        );
795
        // Adoptions now are recorded by trigger in cache_adoptions table.
796
        // Recording adoptions in 'logentries' may be discarded after ensuring that the
797
        // log entries are not used anywhere.
798
        sql("UPDATE `caches` SET `user_id`='&1' WHERE `cache_id`='&2'", $userId, $this->nCacheId);
799
        sql("DELETE FROM `cache_adoption` WHERE `cache_id`='&1'", $this->nCacheId);
800
801
        $this->reCache->setValue('user_id', $userId);
802
803
        return true;
804
    }
805
806
    // checks if $userId has adopted this cache
807
    public function hasAdopted($userId)
808
    {
809
        // cache_adoption exists?
810
        return sql_value(
811
            "SELECT COUNT(*)
812
             FROM `cache_adoption`
813
             WHERE `cache_id`='&1'
814
             AND `user_id`='&2'",
815
            0,
816
            $this->nCacheId,
817
            $userId
818
        ) != 0;
819
    }
820
821
    // true if anyone can view the cache
822
    public function isPublic()
823
    {
824
        return (sql_value("SELECT `allow_user_view` FROM `cache_status` WHERE `id`='&1'", 0, $this->getStatus()) == 1);
825
    }
826
827
    public function allowView()
828
    {
829
        global $login;
830
831
        if ($this->isPublic()) {
832
            return true;
833
        }
834
835
        $login->verify();
836
837
        if (($login->admin & ADMIN_USER) == ADMIN_USER) {
838
            return true;
839
        } elseif ($this->getUserId() == $login->userid) {
840
            return true;
841
        }
842
843
        return false;
844
    }
845
846 View Code Duplication
    public function allowEdit()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
847
    {
848
        global $login;
849
850
        $login->verify();
851
        if ($this->getUserId() == $login->userid) {
852
            return true;
853
        }
854
855
        return false;
856
    }
857
858
    public function allowLog()
859
    {
860
        global $login;
861
862
        $login->verify();
863
        if ($this->getUserId() == $login->userid || $login->hasAdminPriv(ADMIN_USER)) {
864
            return true;
865
        }
866
867
        return (sql_value("SELECT `allow_user_log` FROM `cache_status` WHERE `id`='&1'", 0, $this->getStatus()) == 1);
868
    }
869
870
    public function isRecommendedByUser($nUserId)
871
    {
872
        return sql_value(
873
            "SELECT COUNT(*) FROM `cache_rating` WHERE `cache_id`='&1' AND `user_id`='&2'",
874
            0,
875
            $this->nCacheId,
876
            $nUserId
877
        ) > 0;
878
    }
879
880
    public function addRecommendation($nUserId, $logdate)
881
    {
882
        // rating_date will be set to NOW() by Insert-trigger
883
        sql(
884
            "INSERT IGNORE INTO `cache_rating` (`cache_id`, `user_id`, `rating_date`) VALUES ('&1', '&2', '&3')",
885
            $this->nCacheId,
886
            $nUserId,
887
            $logdate
888
        );
889
    }
890
891
    public function removeRecommendation($nUserId)
892
    {
893
        sql("DELETE FROM `cache_rating` WHERE `cache_id`='&1' AND `user_id`='&2'", $this->nCacheId, $nUserId);
894
    }
895
896
897
    // retrieves admin cache history data and stores it to template variables
898
    // for display by adminhistory.tpl and adminreports.tpl
899
    public function setTplHistoryData($exclude_report_id)
900
    {
901
        global $opt, $tpl;
902
903
        // (other) reports for this cache
904
        $rs = sql(
905
            "SELECT `cr`.`id`,
906
                    `cr`.`date_created`,
907
                    `cr`.`lastmodified`,
908
                    `cr`.`userid`,
909
                    `cr`.`adminid`,
910
                    `users`.`username` AS `usernick`,
911
                    `admins`.`username` AS `adminnick`,
912
                    IFNULL(`tt`.`text`, `crs`.`name`) AS `status`,
913
                    IFNULL(`tt2`.`text`, `crr`.`name`) AS `reason`
914
             FROM `cache_reports` AS `cr`
915
             LEFT JOIN `cache_report_reasons` AS `crr`
916
                 ON `cr`.`reason`=`crr`.`id`
917
             LEFT JOIN `user` AS `users`
918
                 ON `users`.`user_id`=`cr`.`userid`
919
             LEFT JOIN `user` AS `admins`
920
                 ON `admins`.`user_id`=`cr`.`adminid`
921
             LEFT JOIN `cache_report_status` AS `crs`
922
                 ON `cr`.`status`=`crs`.`id`
923
             LEFT JOIN `sys_trans_text` AS `tt`
924
                 ON `crs`.`trans_id`=`tt`.`trans_id`
925
                 AND `tt`.`lang`='&2'
926
             LEFT JOIN `sys_trans_text` AS `tt2`
927
                 ON `crr`.`trans_id`=`tt2`.`trans_id`
928
                 AND `tt2`.`lang`='&2'
929
             WHERE `cr`.`cacheid`='&1'
930
                 AND `cr`.`id`<>'&3'
931
             ORDER BY `cr`.`status`,`cr`.`id` DESC",
932
            $this->getCacheId(),
933
            $opt['template']['locale'],
934
            $exclude_report_id
935
        );
936
        $tpl->assign_rs('reports', $rs);
937
        sql_free_result($rs);
938
939
        // user; deleted logs
940
        $rs = sql("SELECT * FROM `caches` WHERE `cache_id`='&1'", $this->getCacheId());
941
        $rCache = sql_fetch_array($rs);
942
        $tpl->assign('cache', $rCache);
943
        sql_free_result($rs);
944
        $tpl->assign(
945
            'ownername',
946
            sql_value("SELECT `username` FROM `user` WHERE `user_id`='&1'", "", $rCache['user_id'])
947
        );
948
949
        $tpl->assign('deleted_logs', $this->getLogsArray($this->getCacheId(), 0, 1000, true));
0 ignored issues
show
Documentation introduced by
1000 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
950
951
        // status changes
952
        $rs = sql(
953
            "SELECT `csm`.`date_modified`,
954
                    `csm`.`old_state` AS `old_status_id`,
955
                    `csm`.`new_state` AS `new_status_id`,
956
                    `user`.`username`,
957
                    `user`.`user_id`,
958
                    IFNULL(`stt_old`.`text`,`cs_old`.`name`) AS `old_status`,
959
                    IFNULL(`stt_new`.`text`,`cs_new`.`name`) AS `new_status`
960
            FROM `cache_status_modified` `csm`
961
            LEFT JOIN `cache_status` `cs_old`
962
                ON `cs_old`.`id`=`csm`.`old_state`
963
            LEFT JOIN `sys_trans_text` `stt_old`
964
                ON `stt_old`.`trans_id`=`cs_old`.`trans_id`
965
                AND `stt_old`.`lang`='&2'
966
            LEFT JOIN `cache_status` `cs_new`
967
                ON `cs_new`.`id`=`csm`.`new_state`
968
            LEFT JOIN `sys_trans_text` `stt_new`
969
                ON `stt_new`.`trans_id`=`cs_new`.`trans_id`
970
                AND `stt_new`.`lang`='&2'
971
            LEFT JOIN `user`
972
                ON `user`.`user_id`=`csm`.`user_id`
973
            WHERE `cache_id`='&1'
974
            ORDER BY `date_modified` DESC",
975
            $this->getCacheId(),
976
            $opt['template']['locale']
977
        );
978
        $tpl->assign_rs('status_changes', $rs);
979
        sql_free_result($rs);
980
981
        // coordinate changes
982
        $rs = sql(
983
            "SELECT `cc`.`date_created`, `cc`.`longitude`, `cc`.`latitude`,
984
                          IFNULL(`admin`.`user_id`, `owner`.`user_id`) AS `user_id`,
985
                          IFNULL(`admin`.`username`, `owner`.`username`) AS `username`
986
                     FROM `cache_coordinates` `cc`
987
                LEFT JOIN `caches` ON `caches`.`cache_id`=`cc`.`cache_id`
988
                LEFT JOIN `user` `owner` ON `owner`.`user_id`=`caches`.`user_id`
989
                LEFT JOIN `user` `admin` ON `admin`.`user_id`=`cc`.`restored_by`
990
                    WHERE `cc`.`cache_id`='&1'
991
                 ORDER BY `cc`.`date_created` DESC",
992
            $this->getCacheId()
993
        );
994
        $coords = [];
995
        while ($rCoord = sql_fetch_assoc($rs)) {
996
            $coord = new coordinate($rCoord['latitude'], $rCoord['longitude']);
997
            $coords[] = [
998
                'date' => $rCoord['date_created'],
999
                'coord' => $coord->getDecimalMinutes(),
1000
                'user_id' => $rCoord['user_id'],
1001
                'username' => $rCoord['username']
1002
            ];
1003
        }
1004
        sql_free_result($rs);
1005
        $tpl->assign('coordinates', $coords);
1006
1007
        // Adoptions
1008
        $rs = sql(
1009
            "SELECT `cache_adoptions`.`date`,
1010
                          `cache_adoptions`.`from_user_id`,
1011
                          `cache_adoptions`.`to_user_id`,
1012
                          `from_user`.`username` AS `from_username`,
1013
                          `to_user`.`username` AS `to_username`
1014
                     FROM `cache_adoptions`
1015
                LEFT JOIN `user` `from_user` ON `from_user`.`user_id`=`from_user_id`
1016
                LEFT JOIN `user` `to_user` ON `to_user`.`user_id`=`to_user_id`
1017
                    WHERE `cache_id`='&1'
1018
                 ORDER BY `cache_adoptions`.`date`, `cache_adoptions`.`id`",
1019
            $this->getCacheId()
1020
        );
1021
        $tpl->assign_rs('adoptions', $rs);
1022
        sql_free_result($rs);
1023
    }
1024
1025
1026
    public function logTypeAllowed($logType, $oldLogType = 0)
1027
    {
1028
        // check if given logType is valid for this cache type
1029
        return logtype_ok($this->getCacheId(), $logType, $oldLogType);
1030
    }
1031
1032
1033
    public function updateCacheStatus($logType)
1034
    {
1035
        // get cache status
1036
        $cacheStatus = sql_value(
1037
            "SELECT `cache_status` FROM `log_types` WHERE `id`='&1'",
1038
            0,
1039
            $logType
1040
        );
1041
        // set status, if not 0
1042
        if ($cacheStatus != 0) {
1043
            $this->setStatus($cacheStatus);
1044
        }
1045
    }
1046
1047
1048
    // $userLogType:
1049
    //   Logtype selected by the user, or null if not applicable
1050
1051
    public function getUserLogTypes($userLogType, $oldLogType = 0, $statusLogs = true)
1052
    {
1053
        $logTypes = [];
1054
1055
        $logtypeNames = get_logtype_names();
1056
        $allowedLogtypes = get_cache_log_types($this->getCacheId(), $oldLogType, $statusLogs);
1057
        $defaultLogType = $userLogType;
1058
        if (!logtype_ok($this->getCacheId(), $defaultLogType, $oldLogType, $statusLogs)) {
1059
            $defaultLogType = $allowedLogtypes[0];
1060
        }
1061
1062
        // prepare array
1063
        $i = 0;
1064
        foreach ($allowedLogtypes as $logtype) {
1065
            $logTypes[$i]['selected'] = ($logtype == $defaultLogType) ? true : false;
1066
            $logTypes[$i]['name'] = $logtypeNames[$logtype];
1067
            $logTypes[$i]['id'] = $logtype;
1068
            $i++;
1069
        }
1070
1071
        // return
1072
        return $logTypes;
1073
    }
1074
1075
    public function teamcommentAllowed($logType, $oldTeamComment = false)
1076
    {
1077
        // checks if teamcomment is allowed
1078
        return teamcomment_allowed($this->getCacheId(), $logType, $oldTeamComment);
1079
    }
1080
1081
    public function statusUserLogAllowed()
1082
    {
1083
        return sql_value(
1084
            "SELECT `cache_status`.`allow_user_log`
1085
             FROM `cache_status`,`caches`
1086
             WHERE `caches`.`status`=`cache_status`.`id`
1087
             AND `caches`.`cache_id`='&1'",
1088
            0,
1089
            $this->getCacheId()
1090
        ) == 1;
1091
    }
1092
1093
    /**
1094
     * @param $logId
1095
     * @return bool
1096
     */
1097
    public function isLatestLog($logId)
1098
    {
1099
        $latestLogId = sql_value(
1100
            "SELECT `id` FROM `cache_logs`
1101
             WHERE `cache_id`='&1'
1102
             ORDER BY `order_date` DESC, `date_created` DESC, `id` DESC
1103
             LIMIT 1",
1104
            0,
1105
            $this->nCacheId
1106
        );
1107
1108
        return ($logId == $latestLogId);
1109
    }
1110
}
1111