Skip to content

Commit 6886665

Browse files
committed
Add Countable support.
Changelog excerpt: - Added Countable support to the cache handler, the delayed file IO class, the events orchestrator, the YAML handler, and the L10N handler.
1 parent bde4b76 commit 6886665

File tree

9 files changed

+215
-37
lines changed

9 files changed

+215
-37
lines changed

Changelog.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ found at:
1919

2020
- [2025.11.21]: Added PHP 8.5 to workflows.
2121

22+
- [2025.12.24]: Added Countable support to the cache handler, the delayed file
23+
IO class, the events orchestrator, the YAML handler, and the L10N handler.
24+
2225
=== Version/Release 2.14.2 ===
2326
PATCH RELEASE.
2427

_docs/Cache.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ All public methods provided by Cache, along with relevant instructions, are list
137137
- [offsetGet method.](#offsetget-method)
138138
- [offsetSet method.](#offsetset-method)
139139
- [offsetUnset method.](#offsetunset-method)
140+
- [count method.](#count-method)
140141

141142
#### __construct method.
142143

@@ -340,8 +341,18 @@ Invoked by attempting to unset cache entries by using the Cache object instance
340341
public function offsetUnset($Offset): void;
341342
```
342343

344+
#### count method.
345+
346+
Invoked via the `\Countable` interface implementation by calling the PHP count function with the object instance supplied to the call's parameter.
347+
348+
```PHP
349+
public function count(): int;
350+
```
351+
352+
Returns the number of cache entries attached to the current instance.
353+
343354

344355
---
345356

346357

347-
Last Updated: 7 November 2025 (2025.11.07).
358+
Last Updated: 24 December 2025 (2025.12.24).

_docs/DelayedIO.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ If we were to duplicate that `foreach()` some several times, to perform several
150150

151151
- [readFile method.](#generatemarkers-method)
152152
- [writeFile method.](#iterateclosure-method)
153+
- [count method.](#count-method)
153154

154155
#### readFile method.
155156

@@ -193,7 +194,17 @@ The method returns true when the target file is writable, or false otherwise. Wh
193194

194195
In order to be able to compare the file's original data with the data associated with the queued rewrite operation, `readFile()` must've been called for the file in question prior to calling `writeFile()` for the file in question. When that has been satisfied, the rewrite operation for the file in question won't occur when its original data and its new data is the same. When that hasn't been satisfied, the rewrite operation will always occur regardless, as long as the new data isn't empty (if not satisfied, and the new data is also empty, the rewrite operation won't occur).
195196

197+
#### count method.
198+
199+
Invoked via the `\Countable` interface implementation by calling the PHP count function with the object instance supplied to the call's parameter.
200+
201+
```PHP
202+
public function count(): int;
203+
```
204+
205+
Returns the number of files read already.
206+
196207
---
197208

198209

199-
Last Updated: 2 July 2025 (2025.07.02).
210+
Last Updated: 24 December 2025 (2025.12.24).

_docs/Events.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ The class is utilised by CIDRAM and phpMussel to provide a means by which users
2121
- [destroyEvent method.](#destroyevent-method)
2222
- [fireEvent method.](#fireevent-method)
2323
- [assigned method.](#assigned-method)
24+
- [count method.](#count-method)
2425

2526
#### addHandler method.
2627

@@ -76,6 +77,16 @@ public function assigned(string $Event): bool;
7677

7778
This can be useful in cases where data needs to be processed prior to calling fireEvent.
7879

80+
#### count method.
81+
82+
Invoked via the `\Countable` interface implementation by calling the PHP count function with the object instance supplied to the call's parameter.
83+
84+
```PHP
85+
public function count(): int;
86+
```
87+
88+
Returns the number of unique events registered.
89+
7990
---
8091

8192

@@ -108,4 +119,4 @@ $Events->fireEvent('aHypotheticalEvent');
108119
---
109120

110121

111-
Last Updated: 2 July 2025 (2025.07.02).
122+
Last Updated: 24 December 2025 (2025.12.24).

src/Cache.php

Lines changed: 128 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22
/**
3-
* A simple, unified cache handler (last modified: 2025.11.07).
3+
* A simple, unified cache handler (last modified: 2025.12.24).
44
*
55
* This file is a part of the "common classes package", utilised by a number of
66
* packages and projects, including CIDRAM and phpMussel.
@@ -15,7 +15,7 @@
1515

1616
namespace Maikuolan\Common;
1717

18-
class Cache extends CommonAbstract implements \ArrayAccess
18+
class Cache extends CommonAbstract implements \ArrayAccess, \Countable
1919
{
2020
/**
2121
* @var bool Whether to try using APCu.
@@ -166,6 +166,41 @@ class Cache extends CommonAbstract implements \ArrayAccess
166166
*/
167167
public const GET_ALL_QUERY = 'SELECT * FROM `Cache` WHERE 1';
168168

169+
/**
170+
* @var string Prepared get all query for SQLite with PDO using configured prefix.
171+
*/
172+
public const GET_ALL_QUERY_WPREFIX_DPIPE = 'SELECT * FROM `Cache` WHERE `Key` LIKE :prefix || \'%\'';
173+
174+
/**
175+
* @var string Prepared get all query for MySQL/MariaDB/PostgreSQL/SQLSrv with PDO using configured prefix.
176+
*/
177+
public const GET_ALL_QUERY_WPREFIX_CONCAT = 'SELECT * FROM `Cache` WHERE `Key` LIKE CONCAT(:prefix, \'%\')';
178+
179+
/**
180+
* @var string Prepared get all query for 4D with PDO using configured prefix.
181+
*/
182+
public const GET_ALL_QUERY_WPREFIX_PLUS = 'SELECT * FROM `Cache` WHERE `Key` LIKE :prefix + \'%\'';
183+
184+
/**
185+
* @var string Prepared count all query for PDO.
186+
*/
187+
public const COUNT_ALL_QUERY = 'SELECT COUNT(*) FROM `Cache` WHERE 1';
188+
189+
/**
190+
* @var string Prepared count all query for SQLite with PDO using configured prefix.
191+
*/
192+
public const COUNT_ALL_QUERY_WPREFIX_DPIPE = 'SELECT COUNT(*) FROM `Cache` WHERE `Key` LIKE :prefix || \'%\'';
193+
194+
/**
195+
* @var string Prepared count all query for MySQL/MariaDB/PostgreSQL/SQLSrv with PDO using configured prefix.
196+
*/
197+
public const COUNT_ALL_QUERY_WPREFIX_CONCAT = 'SELECT COUNT(*) FROM `Cache` WHERE `Key` LIKE CONCAT(:prefix, \'%\')';
198+
199+
/**
200+
* @var string Prepared count all query for 4D with PDO using configured prefix.
201+
*/
202+
public const COUNT_ALL_QUERY_WPREFIX_PLUS = 'SELECT COUNT(*) FROM `Cache` WHERE `Key` LIKE :prefix + \'%\'';
203+
169204
/**
170205
* @var int Number of seconds to try flocking a resource before giving up.
171206
*/
@@ -825,10 +860,7 @@ public function getAllEntries(): array
825860
$Keys = $this->WorkingData->keys($this->Prefix . '*') ?: [];
826861
}
827862
foreach ($Keys as $Key) {
828-
if (
829-
strlen($Key) > self::KEY_SIZE_LIMIT ||
830-
($PrefixLen && substr($Key, 0, $PrefixLen) !== $this->Prefix)
831-
) {
863+
if (strlen($Key) > self::KEY_SIZE_LIMIT || ($PrefixLen && substr($Key, 0, $PrefixLen) !== $this->Prefix)) {
832864
continue;
833865
}
834866
$TTL = $this->WorkingData->ttl($Key);
@@ -839,20 +871,23 @@ public function getAllEntries(): array
839871
}
840872
if ($this->Using === 'PDO') {
841873
$this->clearExpiredPDO();
842-
$PDO = $this->WorkingData->prepare(self::GET_ALL_QUERY);
843-
if ($PDO !== false && $PDO->execute()) {
874+
if ($this->Prefix === '') {
875+
$PDO = $this->WorkingData->prepare(self::GET_ALL_QUERY);
876+
} elseif (preg_match('~^(?:ibm|mysql|odbc|pgsql|sqlsrv):~i', $this->PDOdsn)) {
877+
$PDO = $this->WorkingData->prepare(self::GET_ALL_QUERY_WPREFIX_CONCAT);
878+
} elseif (preg_match('~^4d:~i', $this->PDOdsn)) {
879+
$PDO = $this->WorkingData->prepare(self::GET_ALL_QUERY_WPREFIX_PLUS);
880+
} else {
881+
$PDO = $this->WorkingData->prepare(self::GET_ALL_QUERY_WPREFIX_DPIPE);
882+
}
883+
if ($PDO !== false && ($this->Prefix === '' ? $PDO->execute() : $PDO->execute([':prefix' => $this->Prefix]))) {
844884
$Data = $PDO->fetchAll();
845885
if (!is_array($Data)) {
846886
return [];
847887
}
848888
$Output = [];
849889
foreach ($Data as $Entry) {
850-
if (
851-
!is_array($Entry) ||
852-
!isset($Entry['Key'], $Entry['Data'], $Entry['Time']) ||
853-
strlen($Entry['Key']) > self::KEY_SIZE_LIMIT ||
854-
($PrefixLen && substr($Entry['Key'], 0, $PrefixLen) !== $this->Prefix)
855-
) {
890+
if (!is_array($Entry) || !isset($Entry['Key'], $Entry['Data'], $Entry['Time']) || strlen($Entry['Key']) > self::KEY_SIZE_LIMIT) {
856891
continue;
857892
}
858893
$Key = substr($Entry['Key'], $PrefixLen);
@@ -944,10 +979,7 @@ public function getAllEntriesWhere(string $Pattern, string $Replacement = '', ?c
944979
$Keys = $this->WorkingData->keys($this->Prefix . '*') ?: [];
945980
}
946981
foreach ($Keys as $Key) {
947-
if (
948-
strlen($Key) > self::KEY_SIZE_LIMIT ||
949-
($PrefixLen && substr($Key, 0, $PrefixLen) !== $this->Prefix)
950-
) {
982+
if (strlen($Key) > self::KEY_SIZE_LIMIT || ($PrefixLen && substr($Key, 0, $PrefixLen) !== $this->Prefix)) {
951983
continue;
952984
}
953985
$Index = substr($Key, $PrefixLen);
@@ -961,19 +993,22 @@ public function getAllEntriesWhere(string $Pattern, string $Replacement = '', ?c
961993
unset($Keys);
962994
} elseif ($this->Using === 'PDO') {
963995
$this->clearExpiredPDO();
964-
$PDO = $this->WorkingData->prepare(self::GET_ALL_QUERY);
965-
if ($PDO !== false && $PDO->execute()) {
996+
if ($this->Prefix === '') {
997+
$PDO = $this->WorkingData->prepare(self::GET_ALL_QUERY);
998+
} elseif (preg_match('~^(?:ibm|mysql|odbc|pgsql|sqlsrv):~i', $this->PDOdsn)) {
999+
$PDO = $this->WorkingData->prepare(self::GET_ALL_QUERY_WPREFIX_CONCAT);
1000+
} elseif (preg_match('~^4d:~i', $this->PDOdsn)) {
1001+
$PDO = $this->WorkingData->prepare(self::GET_ALL_QUERY_WPREFIX_PLUS);
1002+
} else {
1003+
$PDO = $this->WorkingData->prepare(self::GET_ALL_QUERY_WPREFIX_DPIPE);
1004+
}
1005+
if ($PDO !== false && ($this->Prefix === '' ? $PDO->execute() : $PDO->execute([':prefix' => $this->Prefix]))) {
9661006
$Data = $PDO->fetchAll();
9671007
if (!is_array($Data)) {
9681008
return [];
9691009
}
9701010
foreach ($Data as $Entry) {
971-
if (
972-
!is_array($Entry) ||
973-
!isset($Entry['Key'], $Entry['Data'], $Entry['Time']) ||
974-
strlen($Entry['Key']) > self::KEY_SIZE_LIMIT ||
975-
($PrefixLen && substr($Entry['Key'], 0, $PrefixLen) !== $this->Prefix)
976-
) {
1011+
if (!is_array($Entry) || !isset($Entry['Key'], $Entry['Data'], $Entry['Time']) || strlen($Entry['Key']) > self::KEY_SIZE_LIMIT) {
9771012
continue;
9781013
}
9791014
$Key = substr($Entry['Key'], $PrefixLen);
@@ -1271,4 +1306,71 @@ private function tryEnforcePermissions(string $Directory): bool
12711306
}
12721307
return chmod($Directory, 0755);
12731308
}
1309+
1310+
/**
1311+
* Count cache entries.
1312+
*
1313+
* @return int The number of cache entries attached to the current instance.
1314+
*/
1315+
public function count(): int
1316+
{
1317+
if ($this->Using === 'Memcached') {
1318+
return count($this->Indexes);
1319+
}
1320+
$Output = 0;
1321+
$PrefixLen = strlen($this->Prefix);
1322+
if ($this->Using === 'APCu') {
1323+
$Data = apcu_cache_info();
1324+
if (empty($Data['cache_list'])) {
1325+
return $Output;
1326+
}
1327+
foreach ($Data['cache_list'] as $Entry) {
1328+
if (!empty($Entry['info']) && is_string($Entry['info']) && $PrefixLen !== 0 && substr($Entry['info'], 0, $PrefixLen) === $this->Prefix) {
1329+
$Output++;
1330+
}
1331+
}
1332+
return $Output;
1333+
}
1334+
if ($this->Using === 'Redis') {
1335+
if ($PrefixLen === 0 || preg_match('~[^\dA-Za-z_]~', $this->Prefix)) {
1336+
$Keys = $this->WorkingData->keys('*') ?: [];
1337+
} else {
1338+
$Keys = $this->WorkingData->keys($this->Prefix . '*') ?: [];
1339+
}
1340+
foreach ($Keys as $Key) {
1341+
if (strlen($Key) <= self::KEY_SIZE_LIMIT && $PrefixLen !== 0 && substr($Key, 0, $PrefixLen) === $this->Prefix) {
1342+
$Output++;
1343+
}
1344+
}
1345+
return $Output;
1346+
}
1347+
if ($this->Using === 'PDO') {
1348+
$this->clearExpiredPDO();
1349+
if ($this->Prefix === '') {
1350+
$PDO = $this->WorkingData->prepare(self::COUNT_ALL_QUERY);
1351+
} elseif (preg_match('~^(?:ibm|mysql|odbc|pgsql|sqlsrv):~i', $this->PDOdsn)) {
1352+
$PDO = $this->WorkingData->prepare(self::COUNT_ALL_QUERY_WPREFIX_CONCAT);
1353+
} elseif (preg_match('~^4d:~i', $this->PDOdsn)) {
1354+
$PDO = $this->WorkingData->prepare(self::COUNT_ALL_QUERY_WPREFIX_PLUS);
1355+
} else {
1356+
$PDO = $this->WorkingData->prepare(self::COUNT_ALL_QUERY_WPREFIX_DPIPE);
1357+
}
1358+
if ($PDO !== false && ($this->Prefix === '' ? $PDO->execute() : $PDO->execute([':prefix' => $this->Prefix]))) {
1359+
$Data = $PDO->fetchAll();
1360+
return isset($Data[0][0]) && is_int($Data[0][0]) ? $Data[0][0] : 0;
1361+
}
1362+
return 0;
1363+
}
1364+
if ($Arr = $this->exposeWorkingDataArray()) {
1365+
foreach ($Arr as $Key => $Entry) {
1366+
if ($PrefixLen) {
1367+
if (substr($Key, 0, $PrefixLen) !== $this->Prefix) {
1368+
continue;
1369+
}
1370+
}
1371+
$Output++;
1372+
}
1373+
}
1374+
return $Output;
1375+
}
12741376
}

src/DelayedIO.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22
/**
3-
* Delayed file IO class (last modified: 2023.09.14).
3+
* Delayed file IO class (last modified: 2025.12.24).
44
*
55
* This file is a part of the "common classes package", utilised by a number of
66
* packages and projects, including CIDRAM and phpMussel.
@@ -15,7 +15,7 @@
1515

1616
namespace Maikuolan\Common;
1717

18-
class DelayedIO extends CommonAbstract
18+
class DelayedIO extends CommonAbstract implements \Countable
1919
{
2020
/**
2121
* @var array Old data for the files being read/written.
@@ -160,4 +160,14 @@ public function writeFile(string $File = '', string $Data = '', int $Lock = 0):
160160
}
161161
return true;
162162
}
163+
164+
/**
165+
* Count how many files have been read already.
166+
*
167+
* @return int The number of files read already.
168+
*/
169+
public function count(): int
170+
{
171+
return count($this->OldData);
172+
}
163173
}

src/Events.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22
/**
3-
* Events orchestrator (last modified: 2023.09.14).
3+
* Events orchestrator (last modified: 2025.12.24).
44
*
55
* This file is a part of the "common classes package", utilised by a number of
66
* packages and projects, including CIDRAM and phpMussel.
@@ -15,7 +15,7 @@
1515

1616
namespace Maikuolan\Common;
1717

18-
class Events extends CommonAbstract
18+
class Events extends CommonAbstract implements \Countable
1919
{
2020
/**
2121
* @var array Event handlers.
@@ -115,4 +115,14 @@ public function assigned(string $Event): bool
115115
{
116116
return isset($this->Handlers[$Event], $this->Status[$Event]);
117117
}
118+
119+
/**
120+
* Count the number of unique events registered.
121+
*
122+
* @return int The number of unique events registered.
123+
*/
124+
public function count(): int
125+
{
126+
return count($this->Handlers);
127+
}
118128
}

0 commit comments

Comments
 (0)