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.
1515
1616namespace 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}
0 commit comments