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

codeigniter4 / settings / 13399286100

18 Feb 2025 08:14PM UTC coverage: 85.526%. Remained the same
13399286100

push

github

web-flow
chore: update dependencies to support PHP 8.1 - 8.4 (#145)

* move to PHP 8.1 and PHPUnit 10

* update tests

* cs fix

* update workflows

* update php version in the docs

* update rector config

* update psalm config

1 of 2 new or added lines in 2 files covered. (50.0%)

195 of 228 relevant lines covered (85.53%)

55.95 hits per line

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

94.83
/src/Handlers/DatabaseHandler.php
1
<?php
2

3
namespace CodeIgniter\Settings\Handlers;
4

5
use CodeIgniter\Database\BaseBuilder;
6
use CodeIgniter\Database\BaseConnection;
7
use CodeIgniter\I18n\Time;
8
use CodeIgniter\Settings\Config\Settings;
9
use RuntimeException;
10

11
/**
12
 * Provides database persistence for Settings.
13
 * Uses ArrayHandler for storage to minimize database calls.
14
 */
15
class DatabaseHandler extends ArrayHandler
16
{
17
    /**
18
     * The DB connection for the Settings.
19
     */
20
    private BaseConnection $db;
21

22
    /**
23
     * The Query Builder for the Settings table.
24
     */
25
    private BaseBuilder $builder;
26

27
    /**
28
     * Array of contexts that have been stored.
29
     *
30
     * @var list<null>|list<string>
31
     */
32
    private array $hydrated = [];
33

34
    private Settings $config;
35

36
    /**
37
     * Stores the configured database table.
38
     */
39
    public function __construct()
40
    {
41
        $this->config  = config('Settings');
90✔
42
        $this->db      = db_connect($this->config->database['group']);
90✔
43
        $this->builder = $this->db->table($this->config->database['table']);
90✔
44
    }
45

46
    /**
47
     * Checks whether this handler has a value set.
48
     */
49
    public function has(string $class, string $property, ?string $context = null): bool
50
    {
51
        $this->hydrate($context);
72✔
52

53
        return $this->hasStored($class, $property, $context);
72✔
54
    }
55

56
    /**
57
     * Attempt to retrieve a value from the database.
58
     * To boost performance, all of the values are
59
     * read and stored the first call for each contexts
60
     * and then retrieved from storage.
61
     *
62
     * @return mixed|null
63
     */
64
    public function get(string $class, string $property, ?string $context = null)
65
    {
66
        return $this->getStored($class, $property, $context);
48✔
67
    }
68

69
    /**
70
     * Stores values into the database for later retrieval.
71
     *
72
     * @param mixed $value
73
     *
74
     * @return void
75
     *
76
     * @throws RuntimeException For database failures
77
     */
78
    public function set(string $class, string $property, $value = null, ?string $context = null)
79
    {
80
        $time     = Time::now()->format('Y-m-d H:i:s');
72✔
81
        $type     = gettype($value);
72✔
82
        $prepared = $this->prepareValue($value);
72✔
83

84
        // If it was stored then we need to update
85
        if ($this->has($class, $property, $context)) {
72✔
86
            $result = $this->builder
12✔
87
                ->where('class', $class)
12✔
88
                ->where('key', $property)
12✔
89
                ->where('context', $context)
12✔
90
                ->update([
12✔
91
                    'value'      => $prepared,
12✔
92
                    'type'       => $type,
12✔
93
                    'context'    => $context,
12✔
94
                    'updated_at' => $time,
12✔
95
                ]);
12✔
96
            // ...otherwise insert it
97
        } else {
98
            $result = $this->builder
66✔
99
                ->insert([
66✔
100
                    'class'      => $class,
66✔
101
                    'key'        => $property,
66✔
102
                    'value'      => $prepared,
66✔
103
                    'type'       => $type,
66✔
104
                    'context'    => $context,
66✔
105
                    'created_at' => $time,
66✔
106
                    'updated_at' => $time,
66✔
107
                ]);
66✔
108
        }
109

110
        if ($result !== true) {
72✔
111
            throw new RuntimeException($this->db->error()['message'] ?? 'Error writing to the database.');
×
112
        }
113

114
        // Update storage
115
        $this->setStored($class, $property, $value, $context);
72✔
116
    }
117

118
    /**
119
     * Deletes the record from persistent storage, if found,
120
     * and from the local cache.
121
     *
122
     * @return void
123
     */
124
    public function forget(string $class, string $property, ?string $context = null)
125
    {
126
        $this->hydrate($context);
12✔
127

128
        // Delete from the database
129
        $result = $this->builder
12✔
130
            ->where('class', $class)
12✔
131
            ->where('key', $property)
12✔
132
            ->where('context', $context)
12✔
133
            ->delete();
12✔
134

135
        if (! $result) {
12✔
136
            throw new RuntimeException($this->db->error()['message'] ?? 'Error writing to the database.');
×
137
        }
138

139
        // Delete from local storage
140
        $this->forgetStored($class, $property, $context);
12✔
141
    }
142

143
    /**
144
     * Deletes all records from persistent storage, if found,
145
     * and from the local cache.
146
     *
147
     * @return void
148
     */
149
    public function flush()
150
    {
151
        $this->builder->truncate();
6✔
152

153
        parent::flush();
6✔
154
    }
155

156
    /**
157
     * Fetches values from the database in bulk to minimize calls.
158
     * General (null) is always fetched once, contexts are fetched
159
     * in their entirety for each new request.
160
     *
161
     * @throws RuntimeException For database failures
162
     */
163
    private function hydrate(?string $context): void
164
    {
165
        // Check for completion
166
        if (in_array($context, $this->hydrated, true)) {
84✔
167
            return;
54✔
168
        }
169

170
        if ($context === null) {
84✔
171
            $this->hydrated[] = null;
78✔
172

173
            $query = $this->builder->where('context', null);
78✔
174
        } else {
175
            $query = $this->builder->where('context', $context);
12✔
176

177
            // If general has not been hydrated we will do that at the same time
178
            if (! in_array(null, $this->hydrated, true)) {
12✔
179
                $this->hydrated[] = null;
6✔
180
                $query->orWhere('context', null);
6✔
181
            }
182

183
            $this->hydrated[] = $context;
12✔
184
        }
185

186
        if (is_bool($result = $query->get())) {
84✔
187
            throw new RuntimeException($this->db->error()['message'] ?? 'Error reading from database.');
×
188
        }
189

190
        foreach ($result->getResultObject() as $row) {
84✔
191
            $this->setStored($row->class, $row->key, $this->parseValue($row->value, $row->type), $row->context);
12✔
192
        }
193
    }
194
}
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