@@ -280,20 +280,42 @@ public static function setLimit(int $newLimit): void
280280 */
281281 protected static function isValidFieldName (string $ fieldName ): bool
282282 {
283- // permite campos vacíos (se usan valores por defecto en algunos casos )
284- if (empty ( $ fieldName) ) {
283+ // permite campos vacíos (valores por defecto)
284+ if ($ fieldName === '' ) {
285285 return true ;
286286 }
287287
288- // permite lower() y upper() con un campo válido dentro
289- if (preg_match ('/^(lower|upper)\(([a-zA-Z0-9_\.]+)\)$/i ' , $ fieldName , $ matches )) {
288+ // Identificador: campo o tabla.campo (sin espacios, sin comillas)
289+ $ fieldName = trim ($ fieldName );
290+ $ ident = '[a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)? ' ;
291+
292+ // Campo directo
293+ if (preg_match ('/^ ' . $ ident . '$/ ' , $ fieldName )) {
290294 return true ;
291295 }
292296
293- // permite letras, números, guiones bajos y puntos (para tabla.campo)
294- return preg_match ('/^[a-zA-Z0-9_\.]+$/ ' , $ fieldName ) === 1 ;
295- }
297+ // lower(field) / upper(field)
298+ if (preg_match ('/^(lower|upper)\(( ' . $ ident . ')\)$/i ' , $ fieldName )) {
299+ return true ;
300+ }
301+
302+ // substring(field, start, len) con números
303+ if (preg_match ('/^substring\(( ' . $ ident . '),\s*(\d+)\s*,\s*(\d+)\s*\)$/i ' , $ fieldName , $ m )) {
304+ $ start = (int )$ m [2 ];
305+ $ len = (int )$ m [3 ];
306+ // límites razonables (ajusta a tu caso)
307+ return $ start >= 1 && $ len >= 1 && $ len <= 1000 ;
308+ }
296309
310+ // concat(arg1, arg2, ...) donde arg es un identificador o literal simple '...'(sin comillas internas o escapadas)
311+ $ arg = "(?: $ ident|'[^']*') " ;
312+ if (preg_match ('/^concat\(\s* ' . $ arg . '(?:\s*,\s* ' . $ arg . ')+\s*\)$/i ' , $ fieldName )) {
313+ return true ;
314+ }
315+
316+ return false ;
317+ }
318+
297319 protected static function db (): DataBase
298320 {
299321 if (self ::$ dataBase === null ) {
0 commit comments