xoops_setcookie()   F
last analyzed

Complexity

Conditions 16
Paths 577

Size

Total Lines 67
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 16
eloc 34
c 1
b 0
f 0
nc 577
nop 8
dl 0
loc 67
rs 1.9875

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/*
3
 You may not change or alter any portion of this comment or credits of supporting
4
 developers from this source code or any supporting source code which is considered
5
 copyrighted (c) material of the original comment or credit authors.
6
7
 This program is distributed in the hope that it will be useful,
8
 but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
 */
11
12
/**
13
 * Safe, modern cookie setter with RFC 6265 domain validation and SameSite support.
14
 *
15
 * This function replaces the legacy func_get_args() version with an explicit
16
 * signature, making it more robust and easier for static analysis to validate.
17
 *
18
 * @copyright       Copyright 2021-2025 The XOOPS Project https://xoops.org
19
 * @license         GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
20
 * @author          Richard Griffith <[email protected]>
21
 *
22
 * @param string $name The name of the cookie.
23
 * @param string|null $value The value of the cookie.
24
 * @param int $expire The time the cookie expires. This is a Unix timestamp.
25
 * @param string|null $path The path on the server in which the cookie will be available on.
26
 * @param string|null $domain The (sub)domain that the cookie is available to.
27
 * @param bool|null $secure Indicates that the cookie should only be transmitted over a secure HTTPS connection.
28
 * If null, it will be auto-detected.
29
 * @param bool $httponly When TRUE the cookie will be made accessible only through the HTTP protocol.
30
 * @param string $samesite The SameSite attribute ('Lax', 'Strict', 'None').
31
 * @return bool True on success, false on failure.
32
 */
33
function xoops_setcookie(
34
    string $name,
35
    ?string $value = '',
36
    int $expire = 0,
37
    ?string $path = '/',
38
    ?string $domain = '',
39
    ?bool $secure = null,
40
    bool $httponly = true,
41
    string $samesite = 'Lax'
42
): bool {
43
    if (headers_sent()) {
44
        return false;
45
    }
46
47
    // Convert null values to empty string for compatibility with setcookie.
48
    $value = $value ?? '';
49
    $host = parse_url(XOOPS_URL, PHP_URL_HOST);
50
    if (!is_string($host)) {
0 ignored issues
show
introduced by
The condition is_string($host) is always true.
Loading history...
51
        $host = ''; // Fallback for invalid XOOPS_URL
52
    }
53
    if (!is_string($domain)) {
54
        $domain = ''; // Fallback for invalid domain
55
    }
56
57
    // Validate the domain BEFORE using it.
58
    if (class_exists('\Xoops\RegDom\RegisteredDomain')) {
59
        if (!\Xoops\RegDom\RegisteredDomain::domainMatches($host, $domain)) {
60
            $originalDomain = $domain;
61
            $domain = ''; // Auto-correct to a safe, host-only cookie
62
63
            if (defined('XOOPS_DEBUG_MODE') && XOOPS_DEBUG_MODE) {
0 ignored issues
show
Bug introduced by
The constant XOOPS_DEBUG_MODE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
64
                error_log(
65
                    sprintf(
66
                        '[XOOPS Cookie] Invalid domain "%s" for host "%s" (cookie: %s) - using host-only.',
67
                        $originalDomain,
68
                        $host,
69
                        $name
70
                    )
71
                );
72
            }
73
        }
74
    }
75
76
    // Auto-detect 'secure' flag if not explicitly set
77
    if ($secure === null) {
78
        $secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
79
            || (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443)
80
            || (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https');
81
    }
82
83
    // Use modern array syntax for PHP 7.3+
84
    if (PHP_VERSION_ID >= 70300) {
85
        $options = [
86
            'expires'  => $expire,
87
            'path'     => $path,
88
            'secure'   => $secure,
89
            'httponly' => $httponly,
90
            'samesite' => $samesite,
91
        ];
92
        if ($domain !== '') {
93
            $options['domain'] = $domain;
94
        }
95
        return setcookie($name, $value, $options);
96
    }
97
98
    // Fallback for older PHP versions
99
    return setcookie($name, $value, $expire, $path, $domain, $secure, $httponly);
100
}
101
102
/**
103
 * @param array $args 'name', 'value' and 'options' corresponding to php 7.3 arguments to setcookie()
104
 *
105
 * @return string
106
 */
107
function xoops_buildCookieHeader($args)
108
{
109
    //$optionsKeys = array('expires', 'path', 'domain', 'secure', 'httponly', 'samesite');
110
    $options = $args['options'];
111
112
    $header = 'Set-Cookie: ' . $args['name'] . '=' . rawurlencode($args['value']) . ' ';
113
114
    if (isset($options['expires']) && 0 !== $options['expires']) {
115
        $dateTime = new DateTime();
116
        if (time() >= $options['expires']) {
117
            $dateTime->setTimestamp(0);
118
            $header = 'Set-Cookie: ' . $args['name'] . '=deleted ; expires=' . $dateTime->format(DateTime::COOKIE) . ' ; Max-Age=0 ';
119
        } else {
120
            $dateTime->setTimestamp($options['expires']);
121
            $header .= '; expires=' . $dateTime->format(DateTime::COOKIE) . ' ';
122
        }
123
    }
124
125
    if (isset($options['path']) && '' !== $options['path']) {
126
        $header .= '; path=' . $options['path'] . ' ';
127
    }
128
129
    if (isset($options['domain']) && '' !== $options['domain']) {
130
        $header .= '; domain=' . $options['domain'] . ' ';
131
    }
132
133
    if (isset($options['secure']) && true === (bool) $options['secure']) {
134
        $header .= '; Secure ';
135
    }
136
137
    if (isset($options['httponly']) && true === (bool) $options['httponly']) {
138
        $header .= '; HttpOnly ';
139
    }
140
141
    if (isset($options['samesite']) && '' !== $options['samesite']) {
142
        $header .= '; samesite=' . $options['samesite'] . ' ';
143
    }
144
145
    return $header;
146
}
147