\n\n\n```\n\n## Integración con Laravel\n\n### Regla de validación personalizada:\n\n```php\nmessage;\n }\n}\n\n// app/Providers/AppServiceProvider.php\nnamespace AppProviders;\n\nuse IlluminateSupportServiceProvider;\nuse IlluminateSupportFacadesValidator;\n\nclass AppServiceProvider extends ServiceProvider\n{\n public function boot()\n {\n // Registrar validación personalizada\n Validator::extend('rut', function ($attribute, $value, $parameters, $validator) {\n return RutValidator::validar($value);\n });\n \n // Mensaje personalizado\n Validator::replacer('rut', function ($message, $attribute, $rule, $parameters) {\n return str_replace(':attribute', $attribute, 'El campo :attribute debe ser un RUT válido.');\n });\n }\n}\n\n// Uso en controlador\nnamespace AppHttpControllers;\n\nuse AppRulesRutChileno;\nuse IlluminateHttpRequest;\n\nclass ClienteController extends Controller\n{\n public function store(Request $request)\n {\n $validated = $request->validate([\n 'nombre' => 'required|string|max:255',\n 'rut' => ['required', new RutChileno],\n 'email' => 'required|email'\n ]);\n \n // Formatear RUT antes de guardar\n $validated['rut'] = RutValidator::formatear($validated['rut']);\n \n $cliente = Cliente::create($validated);\n \n return response()->json([\n 'message' => 'Cliente creado exitosamente',\n 'cliente' => $cliente\n ], 201);\n }\n}\n\n// Modelo con mutador\nnamespace AppModels;\n\nuse IlluminateDatabaseEloquentModel;\n\nclass Cliente extends Model\n{\n protected $fillable = ['nombre', 'rut', 'email'];\n \n /**\n * Mutador para formatear RUT al guardar\n */\n public function setRutAttribute($value)\n {\n $this->attributes['rut'] = RutValidator::formatear($value);\n }\n \n /**\n * Accessor para obtener info del RUT\n */\n public function getRutInfoAttribute()\n {\n return RutValidator::extraerInfo($this->rut);\n }\n}\n```\n\n## Integración con Symfony\n\n### Constraint personalizado para Symfony:\n\n```php\ncontext->buildViolation($constraint->message)\n ->setParameter('{{ value }}', $value)\n ->addViolation();\n }\n }\n}\n\n// src/Entity/Cliente.php\nnamespace AppEntity;\n\nuse AppValidatorConstraints as AppAssert;\nuse DoctrineORMMapping as ORM;\nuse SymfonyComponentValidatorConstraints as Assert;\n\n/**\n * @ORMEntity\n * @ORMTable(name=\"clientes\")\n */\nclass Cliente\n{\n /**\n * @ORMId\n * @ORMGeneratedValue\n * @ORMColumn(type=\"integer\")\n */\n private $id;\n \n /**\n * @ORMColumn(type=\"string\", length=100)\n * @AssertNotBlank\n * @AssertLength(max=100)\n */\n private $nombre;\n \n /**\n * @ORMColumn(type=\"string\", length=12, unique=true)\n * @AssertNotBlank\n * @AppAssertRutChileno\n */\n private $rut;\n \n /**\n * @ORMColumn(type=\"string\", length=255)\n * @AssertNotBlank\n * @AssertEmail\n */\n private $email;\n \n // Getters y setters...\n \n public function setRut(string $rut): self\n {\n $this->rut = RutValidator::formatear($rut);\n return $this;\n }\n}\n```\n\n## API REST con validación\n\n### Endpoint completo con manejo de errores:\n\n```php\n true,\n 'message' => 'RUT es requerido'\n ]);\n }\n \n $rut = $input['rut'];\n $esValido = RutValidator::validar($rut);\n \n $response = [\n 'valido' => $esValido,\n 'rut_original' => $rut,\n 'rut_limpio' => RutValidator::limpiar($rut)\n ];\n \n if ($esValido) {\n $info = RutValidator::extraerInfo($rut);\n $response = array_merge($response, [\n 'rut_formateado' => $info['formateado'],\n 'numero' => $info['numero'],\n 'digito_verificador' => $info['dv'],\n 'tipo' => $info['tipo']\n ]);\n }\n \n echo json_encode($response);\n \n } catch (Exception $e) {\n http_response_code(500);\n echo json_encode([\n 'error' => true,\n 'message' => 'Error interno del servidor'\n ]);\n }\n }\n \n /**\n * Valida múltiples RUTs\n * POST /api/rut/validar-lote\n */\n public function validarLote()\n {\n header('Content-Type: application/json');\n \n try {\n $input = json_decode(file_get_contents('php://input'), true);\n \n if (!isset($input['ruts']) || !is_array($input['ruts'])) {\n http_response_code(400);\n return json_encode([\n 'error' => true,\n 'message' => 'Se requiere un array de RUTs'\n ]);\n }\n \n $resultados = [];\n $validos = 0;\n $invalidos = 0;\n \n foreach ($input['ruts'] as $rut) {\n $esValido = RutValidator::validar($rut);\n \n $resultado = [\n 'rut' => $rut,\n 'valido' => $esValido\n ];\n \n if ($esValido) {\n $resultado['formateado'] = RutValidator::formatear($rut);\n $validos++;\n } else {\n $invalidos++;\n }\n \n $resultados[] = $resultado;\n }\n \n echo json_encode([\n 'total' => count($input['ruts']),\n 'validos' => $validos,\n 'invalidos' => $invalidos,\n 'porcentaje_validos' => count($input['ruts']) > 0 \n ? round(($validos / count($input['ruts'])) * 100, 2) \n : 0,\n 'resultados' => $resultados\n ]);\n \n } catch (Exception $e) {\n http_response_code(500);\n echo json_encode([\n 'error' => true,\n 'message' => 'Error procesando lote'\n ]);\n }\n }\n \n /**\n * Calcula dígito verificador\n * GET /api/rut/calcular-dv/{numero}\n */\n public function calcularDV($numero)\n {\n header('Content-Type: application/json');\n \n if (!is_numeric($numero) || $numero < 1000000 || $numero > 99999999) {\n http_response_code(400);\n echo json_encode([\n 'error' => true,\n 'message' => 'Número de RUT inválido'\n ]);\n return;\n }\n \n $dv = RutValidator::calcularDV($numero);\n \n echo json_encode([\n 'numero' => (int)$numero,\n 'digito_verificador' => $dv,\n 'rut_completo' => RutValidator::formatear($numero . $dv)\n ]);\n }\n}\n```\n\n## Procesamiento masivo de archivos\n\n### Script para procesar CSV con RUTs:\n\n```php\n 0,\n 'validos' => 0,\n 'invalidos' => 0,\n 'procesados' => 0,\n 'errores' => []\n ];\n \n /**\n * Procesa un archivo CSV con RUTs\n * \n * @param string $archivoEntrada Path del archivo CSV\n * @param string $archivoSalida Path del archivo de salida\n * @param int $columnaRut Índice de la columna con RUTs (0-based)\n * @return array Estadísticas del procesamiento\n */\n public function procesarCSV($archivoEntrada, $archivoSalida, $columnaRut = 0)\n {\n if (!file_exists($archivoEntrada)) {\n throw new Exception(\"Archivo no encontrado: $archivoEntrada\");\n }\n \n $entrada = fopen($archivoEntrada, 'r');\n $salida = fopen($archivoSalida, 'w');\n \n if (!$entrada || !$salida) {\n throw new Exception(\"Error abriendo archivos\");\n }\n \n // Procesar encabezados\n $encabezados = fgetcsv($entrada);\n if ($encabezados) {\n // Agregar columnas nuevas\n $encabezados[] = 'rut_valido';\n $encabezados[] = 'rut_formateado';\n $encabezados[] = 'rut_tipo';\n fputcsv($salida, $encabezados);\n }\n \n // Procesar datos\n while (($fila = fgetcsv($entrada)) !== false) {\n $this->stats['total']++;\n \n try {\n if (isset($fila[$columnaRut])) {\n $rut = $fila[$columnaRut];\n $esValido = RutValidator::validar($rut);\n \n if ($esValido) {\n $this->stats['validos']++;\n $info = RutValidator::extraerInfo($rut);\n $fila[] = 'SI';\n $fila[] = $info['formateado'];\n $fila[] = $info['tipo'];\n } else {\n $this->stats['invalidos']++;\n $fila[] = 'NO';\n $fila[] = '';\n $fila[] = '';\n }\n } else {\n $this->stats['errores'][] = \"Fila {$this->stats['total']}: columna RUT no encontrada\";\n $fila[] = 'ERROR';\n $fila[] = '';\n $fila[] = '';\n }\n \n fputcsv($salida, $fila);\n $this->stats['procesados']++;\n \n // Mostrar progreso cada 1000 registros\n if ($this->stats['procesados'] % 1000 === 0) {\n echo \"Procesados: {$this->stats['procesados']}\\r\";\n }\n \n } catch (Exception $e) {\n $this->stats['errores'][] = \"Fila {$this->stats['total']}: \" . $e->getMessage();\n }\n }\n \n fclose($entrada);\n fclose($salida);\n \n return $this->stats;\n }\n \n /**\n * Genera reporte de procesamiento\n * \n * @return string Reporte formateado\n */\n public function generarReporte()\n {\n $porcentajeValidos = $this->stats['total'] > 0 \n ? round(($this->stats['validos'] / $this->stats['total']) * 100, 2) \n : 0;\n \n $reporte = \"\\n=== REPORTE DE PROCESAMIENTO ===\\n\";\n $reporte .= \"Total de registros: {$this->stats['total']}\\n\";\n $reporte .= \"RUTs válidos: {$this->stats['validos']} ($porcentajeValidos%)\\n\";\n $reporte .= \"RUTs inválidos: {$this->stats['invalidos']}\\n\";\n $reporte .= \"Registros procesados: {$this->stats['procesados']}\\n\";\n \n if (count($this->stats['errores']) > 0) {\n $reporte .= \"\\nErrores encontrados:\\n\";\n foreach ($this->stats['errores'] as $error) {\n $reporte .= \"- $error\\n\";\n }\n }\n \n return $reporte;\n }\n}\n\n// Script principal\nif (php_sapi_name() === 'cli') {\n if ($argc < 2) {\n echo \"Uso: php procesar_ruts.php [archivo_salida.csv] [columna_rut]\\n\";\n exit(1);\n }\n \n $archivoEntrada = $argv[1];\n $archivoSalida = $argv[2] ?? 'ruts_validados.csv';\n $columnaRut = isset($argv[3]) ? (int)$argv[3] : 0;\n \n try {\n require_once 'RutValidator.php';\n \n $processor = new RutProcessor();\n echo \"Procesando archivo: $archivoEntrada\\n\";\n \n $stats = $processor->procesarCSV($archivoEntrada, $archivoSalida, $columnaRut);\n \n echo $processor->generarReporte();\n echo \"\\nArchivo de salida: $archivoSalida\\n\";\n \n } catch (Exception $e) {\n echo \"Error: \" . $e->getMessage() . \"\\n\";\n exit(1);\n }\n}\n?>\n```\n\n## Testing con PHPUnit\n\n### Suite completa de pruebas:\n\n```php\nassertEquals($esperado, RutValidator::validar($rut));\n }\n \n public function rutValidosProvider()\n {\n return [\n ['12.345.678-5', true],\n ['12345678-5', true],\n ['123456785', true],\n ['11.111.111-1', true],\n ['15.834.661-K', true],\n ['15834661-k', true], // k minúscula\n ['5.126.663-3', true],\n ];\n }\n \n /**\n * @dataProvider rutInvalidosProvider\n */\n public function testValidarRutsIncorrectos($rut)\n {\n $this->assertFalse(RutValidator::validar($rut));\n }\n \n public function rutInvalidosProvider()\n {\n return [\n ['12.345.678-9'],\n ['11.111.111-2'],\n ['00000000-0'],\n ['abc123'],\n [''],\n ['1'],\n ['12.345.678'],\n ['12345678-'],\n ];\n }\n \n /**\n * @dataProvider digitoVerificadorProvider\n */\n public function testCalcularDigitoVerificador($numero, $dvEsperado)\n {\n $this->assertEquals($dvEsperado, RutValidator::calcularDV($numero));\n }\n \n public function digitoVerificadorProvider()\n {\n return [\n [12345678, '5'],\n [11111111, '1'],\n [22222222, '2'],\n [15834660, '0'],\n [15834661, 'K'],\n [8942487, '6'],\n ];\n }\n \n public function testFormatearRut()\n {\n $this->assertEquals('12.345.678-5', RutValidator::formatear('123456785'));\n $this->assertEquals('1.111.111-1', RutValidator::formatear('11111111'));\n $this->assertEquals('1.234.567-K', RutValidator::formatear('1234567K'));\n }\n \n public function testLimpiarRut()\n {\n $this->assertEquals('123456785', RutValidator::limpiar('12.345.678-5'));\n $this->assertEquals('123456785', RutValidator::limpiar('12 345 678-5'));\n $this->assertEquals('12345678K', RutValidator::limpiar('12.345.678-k'));\n }\n \n public function testExtraerInfo()\n {\n $info = RutValidator::extraerInfo('12.345.678-5');\n \n $this->assertIsArray($info);\n $this->assertEquals(12345678, $info['numero']);\n $this->assertEquals('5', $info['dv']);\n $this->assertEquals('12.345.678-5', $info['formateado']);\n $this->assertTrue($info['es_persona']);\n $this->assertFalse($info['es_empresa']);\n \n // Test con RUT inválido\n $this->assertNull(RutValidator::extraerInfo('12.345.678-9'));\n }\n \n public function testCacheFunciona()\n {\n RutValidator::limpiarCache();\n \n $rut = '12.345.678-5';\n \n // Primera validación (sin cache)\n $resultado1 = RutValidator::validar($rut);\n \n // Segunda validación (con cache)\n $resultado2 = RutValidator::validar($rut);\n \n $this->assertEquals($resultado1, $resultado2);\n }\n \n public function testRendimientoMasivo()\n {\n $inicio = microtime(true);\n \n // Validar 1000 RUTs\n for ($i = 0; $i < 1000; $i++) {\n RutValidator::validar('12.345.678-5');\n }\n \n $tiempo = microtime(true) - $inicio;\n \n // Debería tomar menos de 1 segundo con cache\n $this->assertLessThan(1.0, $tiempo);\n }\n}\n?>\n```\n\n## Consideraciones de seguridad\n\n### Mejores prácticas para producción:\n\n```php\n 100) { // Máximo 100 intentos por hora\n return [\n 'error' => true,\n 'message' => 'Demasiados intentos. Intente más tarde.'\n ];\n }\n \n apcu_store($cacheKey, $attempts + 1, 3600); // TTL 1 hora\n \n // Sanitizar entrada\n $rut = self::sanitizar($rut);\n \n // Validar\n $esValido = parent::validar($rut);\n \n // Log para auditoría\n self::logValidacion($rut, $ip, $esValido);\n \n return [\n 'valido' => $esValido,\n 'rut_sanitizado' => $rut\n ];\n }\n \n /**\n * Sanitiza entrada para prevenir XSS\n * \n * @param string $input Entrada del usuario\n * @return string Entrada sanitizada\n */\n private static function sanitizar($input)\n {\n // Remover tags HTML\n $input = strip_tags($input);\n \n // Remover caracteres de control\n $input = preg_replace('/[\u0000-\u001f]/u', '', $input);\n \n // Limitar longitud\n $input = substr($input, 0, 20);\n \n return $input;\n }\n \n /**\n * Registra validaciones para auditoría\n * \n * @param string $rut RUT validado\n * @param string $ip IP del cliente\n * @param bool $esValido Resultado de validación\n */\n private static function logValidacion($rut, $ip, $esValido)\n {\n $log = sprintf(\n \"[%s] IP: %s, RUT: %s, Válido: %s\\n\",\n date('Y-m-d H:i:s'),\n $ip,\n substr($rut, 0, -4) . '****', // Ocultar últimos 4 caracteres\n $esValido ? 'SI' : 'NO'\n );\n \n error_log($log, 3, '/var/log/rut_validations.log');\n }\n}\n\n// Middleware para aplicaciones web\nclass RutValidationMiddleware\n{\n public function handle($request, $next)\n {\n if ($request->has('rut')) {\n $ip = $request->ip();\n $rut = $request->input('rut');\n \n $resultado = SecureRutValidator::validarConLimite($rut, $ip);\n \n if (isset($resultado['error'])) {\n return response()->json($resultado, 429); // Too Many Requests\n }\n \n $request->merge(['rut_validado' => $resultado]);\n }\n \n return $next($request);\n }\n}\n?>\n```\n\n## Conclusión\n\nPHP ofrece una implementación robusta y eficiente para la validación de RUTs chilenos. Las soluciones presentadas cubren desde scripts simples hasta integraciones empresariales complejas:\n\n- **Funciones básicas**: Para validaciones rápidas y scripts simples\n- **Clases OOP**: Para aplicaciones estructuradas y reutilizables\n- **Integraciones con frameworks**: Laravel y Symfony para aplicaciones modernas\n- **APIs REST**: Para servicios web escalables\n- **Procesamiento masivo**: Para manejar grandes volúmenes de datos\n- **Seguridad**: Implementaciones seguras para producción\n\nLa clave está en elegir la implementación adecuada según tus necesidades, siempre manteniendo las mejores prácticas de seguridad y rendimiento.\n\n---\n\n### Recursos adicionales\n\n- **PHP.net**: Documentación oficial del lenguaje\n- **Packagist**: Busca paquetes como `freshwork/chilean-bundle`\n- **GitHub**: Repositorios con implementaciones de RUT para PHP\n- **Comunidad PHP Chile**: Foros y grupos de desarrolladores chilenos\n","keywords":"PHP, RUT, programación, validación, backend","articleSection":"Desarrollo"}
Volver al blog
Desarrollo

Validar RUT en PHP: Implementación del Algoritmo Módulo 11

9 min de lectura
PHP
RUT
programación
validación
backend

Validar RUT en PHP: Implementación del Algoritmo Módulo 11

PHP en el contexto empresarial chileno

PHP sigue siendo uno de los lenguajes más utilizados en aplicaciones web chilenas, especialmente en sistemas de facturación, e-commerce y plataformas empresariales. La validación correcta del RUT es crítica para cumplir con las normativas del SII y evitar rechazos en documentos tributarios electrónicos.

Este tutorial te mostrará cómo implementar una validación robusta y segura del RUT en PHP, desde funciones básicas hasta integraciones con frameworks modernos como Laravel y Symfony.

Función básica para calcular el dígito verificador

Implementación directa del algoritmo:

PHP
1<?php
2/**
3 * Calcula el dígito verificador de un RUT chileno
4 * 
5 * @param int|string $rutNumero Número del RUT sin dígito verificador
6 * @return string Dígito verificador (0-9 o K)
7 */
8function calcularDigitoVerificador($rutNumero) {
9    // Asegurar que sea string y tenga 8 dígitos
10    $rut = str_pad((string)$rutNumero, 8, '0', STR_PAD_LEFT);
11    
12    // Multiplicadores según algoritmo módulo 11
13    $multiplicadores = [2, 3, 4, 5, 6, 7];
14    
15    // Calcular suma ponderada
16    $suma = 0;
17    $rutReverso = strrev($rut);
18    
19    for ($i = 0; $i < strlen($rutReverso); $i++) {
20        $digito = (int)$rutReverso[$i];
21        $multiplicador = $multiplicadores[$i % 6];
22        $suma += $digito * $multiplicador;
23    }
24    
25    // Calcular dígito verificador
26    $resto = $suma % 11;
27    $digitoVerificador = 11 - $resto;
28    
29    // Casos especiales
30    if ($digitoVerificador == 11) {
31        return '0';
32    } elseif ($digitoVerificador == 10) {
33        return 'K';
34    } else {
35        return (string)$digitoVerificador;
36    }
37}
38
39// Ejemplos de uso
40echo calcularDigitoVerificador(12345678);  // 5
41echo calcularDigitoVerificador(11111111);  // 1
42echo calcularDigitoVerificador(15834661);  // K
43?>

Clase completa para manejo de RUT

Implementación orientada a objetos con mejores prácticas:

PHP
1<?php
2/**
3 * Clase para validar y formatear RUTs chilenos
4 * 
5 * @author Tu Nombre
6 * @version 1.0.0
7 */
8class RutValidator
9{
10    /**
11     * Cache de validaciones para mejorar rendimiento
12     * @var array
13     */
14    private static $cache = [];
15    
16    /**
17     * Tamaño máximo del cache
18     * @var int
19     */
20    private const MAX_CACHE_SIZE = 1000;
21    
22    /**
23     * Limpia el RUT removiendo puntos, guiones y espacios
24     * 
25     * @param string $rut RUT en cualquier formato
26     * @return string RUT limpio
27     */
28    public static function limpiar($rut)
29    {
30        // Remover caracteres no deseados y convertir a mayúsculas
31        return strtoupper(preg_replace('/[^0-9kK]/', '', $rut));
32    }
33    
34    /**
35     * Formatea el RUT con puntos y guión
36     * 
37     * @param string $rut RUT sin formato
38     * @return string RUT formateado (ej: 12.345.678-9)
39     */
40    public static function formatear($rut)
41    {
42        $rutLimpio = self::limpiar($rut);
43        
44        if (strlen($rutLimpio) < 2) {
45            return $rutLimpio;
46        }
47        
48        // Separar número y dígito verificador
49        $numero = substr($rutLimpio, 0, -1);
50        $dv = substr($rutLimpio, -1);
51        
52        // Formatear número con puntos
53        $numeroFormateado = '';
54        $contador = 0;
55        
56        for ($i = strlen($numero) - 1; $i >= 0; $i--) {
57            if ($contador > 0 && $contador % 3 === 0) {
58                $numeroFormateado = '.' . $numeroFormateado;
59            }
60            $numeroFormateado = $numero[$i] . $numeroFormateado;
61            $contador++;
62        }
63        
64        return $numeroFormateado . '-' . $dv;
65    }
66    
67    /**
68     * Calcula el dígito verificador
69     * 
70     * @param int|string $rutNumero Número del RUT sin DV
71     * @return string Dígito verificador
72     */
73    public static function calcularDV($rutNumero)
74    {
75        // Asegurar string de 8 dígitos
76        $rut = str_pad((string)$rutNumero, 8, '0', STR_PAD_LEFT);
77        
78        // Calcular suma ponderada usando array_reduce
79        $suma = array_reduce(
80            array_map(
81                function($i) use ($rut) {
82                    $digito = (int)$rut[strlen($rut) - 1 - $i];
83                    $factor = (($i % 6) + 2);
84                    return $digito * $factor;
85                },
86                range(0, strlen($rut) - 1)
87            ),
88            function($carry, $item) {
89                return $carry + $item;
90            },
91            0
92        );
93        
94        // Obtener dígito verificador
95        $dv = 11 - ($suma % 11);
96        
97        if ($dv == 11) {
98            return '0';
99        } elseif ($dv == 10) {
100            return 'K';
101        } else {
102            return (string)$dv;
103        }
104    }
105    
106    /**
107     * Valida un RUT completo
108     * 
109     * @param string $rut RUT completo con o sin formato
110     * @return bool True si es válido
111     */
112    public static function validar($rut)
113    {
114        $rutLimpio = self::limpiar($rut);
115        
116        // Verificar cache
117        if (isset(self::$cache[$rutLimpio])) {
118            return self::$cache[$rutLimpio];
119        }
120        
121        // Validar largo mínimo
122        if (strlen($rutLimpio) < 2) {
123            return self::actualizarCache($rutLimpio, false);
124        }
125        
126        // Separar número y DV
127        $numero = substr($rutLimpio, 0, -1);
128        $dvIngresado = substr($rutLimpio, -1);
129        
130        // Validar que el número contenga solo dígitos
131        if (!ctype_digit($numero)) {
132            return self::actualizarCache($rutLimpio, false);
133        }
134        
135        // Calcular DV esperado y comparar
136        $dvCalculado = self::calcularDV($numero);
137        $esValido = $dvIngresado === $dvCalculado;
138        
139        return self::actualizarCache($rutLimpio, $esValido);
140    }
141    
142    /**
143     * Actualiza el cache con límite de tamaño
144     * 
145     * @param string $rut RUT limpio
146     * @param bool $esValido Resultado de validación
147     * @return bool El resultado de validación
148     */
149    private static function actualizarCache($rut, $esValido)
150    {
151        // Limitar tamaño del cache
152        if (count(self::$cache) >= self::MAX_CACHE_SIZE) {
153            // Eliminar el primer elemento (FIFO)
154            array_shift(self::$cache);
155        }
156        
157        self::$cache[$rut] = $esValido;
158        return $esValido;
159    }
160    
161    /**
162     * Extrae información detallada del RUT
163     * 
164     * @param string $rut RUT a analizar
165     * @return array|null Array con información o null si es inválido
166     */
167    public static function extraerInfo($rut)
168    {
169        if (!self::validar($rut)) {
170            return null;
171        }
172        
173        $rutLimpio = self::limpiar($rut);
174        $numero = (int)substr($rutLimpio, 0, -1);
175        
176        return [
177            'numero' => $numero,
178            'dv' => substr($rutLimpio, -1),
179            'formateado' => self::formatear($rutLimpio),
180            'sin_formato' => $rutLimpio,
181            'es_empresa' => $numero >= 50000000,
182            'es_persona' => $numero < 50000000,
183            'tipo' => $numero >= 50000000 ? 'Empresa' : 'Persona Natural'
184        ];
185    }
186    
187    /**
188     * Limpia el cache de validaciones
189     * 
190     * @return void
191     */
192    public static function limpiarCache()
193    {
194        self::$cache = [];
195    }
196}
197
198// Ejemplos de uso
199$ruts = ['12.345.678-5', '11111111-1', '12.345.678-9', '15.834.661-K'];
200
201foreach ($ruts as $rut) {
202    if (RutValidator::validar($rut)) {
203        echo "✓ {$rut} es válido - " . RutValidator::formatear($rut) . PHP_EOL;
204    } else {
205        echo "✗ {$rut} es inválido" . PHP_EOL;
206    }
207}
208
209// Extraer información
210$info = RutValidator::extraerInfo('12.345.678-5');
211print_r($info);
212?>

Integración con formularios web

Validación AJAX en tiempo real:

PHP
1<?php
2// api/validar-rut.php
3header('Content-Type: application/json');
4header('Access-Control-Allow-Origin: *');
5
6// Incluir clase RutValidator
7require_once 'RutValidator.php';
8
9// Obtener RUT desde POST o GET
10$rut = $_REQUEST['rut'] ?? '';
11
12// Validar
13$esValido = RutValidator::validar($rut);
14$response = [
15    'valido' => $esValido,
16    'rut_original' => $rut,
17    'rut_limpio' => RutValidator::limpiar($rut),
18    'mensaje' => $esValido ? 'RUT válido' : 'RUT inválido'
19];
20
21if ($esValido) {
22    $response['rut_formateado'] = RutValidator::formatear($rut);
23    $response['info'] = RutValidator::extraerInfo($rut);
24}
25
26echo json_encode($response);
27?>

Formulario HTML con validación en tiempo real:

HTML
1<!DOCTYPE html>
2<html lang="es">
3<head>
4    <meta charset="UTF-8">
5    <title>Validador de RUT PHP</title>
6    <style>
7        .form-control { 
8            padding: 10px; 
9            font-size: 16px; 
10            border: 2px solid #ddd; 
11            border-radius: 4px; 
12            width: 300px;
13        }
14        .is-valid { border-color: #28a745; }
15        .is-invalid { border-color: #dc3545; }
16        .feedback { margin-top: 5px; font-size: 14px; }
17        .valid-feedback { color: #28a745; }
18        .invalid-feedback { color: #dc3545; }
19        .loading { color: #6c757d; }
20    </style>
21</head>
22<body>
23    <h2>Validador de RUT en PHP</h2>
24    
25    <form id="rutForm">
26        <div class="form-group">
27            <label for="rut">Ingrese RUT:</label>
28            <input 
29                type="text" 
30                id="rut" 
31                name="rut" 
32                class="form-control" 
33                placeholder="12.345.678-9"
34                maxlength="12"
35            >
36            <div class="feedback"></div>
37        </div>
38        
39        <button type="submit">Validar</button>
40    </form>
41    
42    <div id="resultado"></div>
43
44    <script>
45        const rutInput = document.getElementById('rut');
46        const feedback = document.querySelector('.feedback');
47        const resultado = document.getElementById('resultado');
48        let timeoutId;
49        
50        // Validación en tiempo real
51        rutInput.addEventListener('input', function(e) {
52            clearTimeout(timeoutId);
53            const rut = e.target.value;
54            
55            if (rut.length < 3) {
56                rutInput.classList.remove('is-valid', 'is-invalid');
57                feedback.textContent = '';
58                return;
59            }
60            
61            feedback.textContent = 'Validando...';
62            feedback.className = 'feedback loading';
63            
64            // Debounce de 500ms
65            timeoutId = setTimeout(() => {
66                validarRut(rut);
67            }, 500);
68        });
69        
70        // Función para validar RUT
71        async function validarRut(rut) {
72            try {
73                const response = await fetch('api/validar-rut.php', {
74                    method: 'POST',
75                    headers: {
76                        'Content-Type': 'application/x-www-form-urlencoded',
77                    },
78                    body: 'rut=' + encodeURIComponent(rut)
79                });
80                
81                const data = await response.json();
82                
83                if (data.valido) {
84                    rutInput.classList.remove('is-invalid');
85                    rutInput.classList.add('is-valid');
86                    feedback.className = 'feedback valid-feedback';
87                    feedback.textContent = '✓ ' + data.mensaje;
88                    
89                    // Actualizar input con formato correcto
90                    if (data.rut_formateado && rutInput.value !== data.rut_formateado) {
91                        rutInput.value = data.rut_formateado;
92                    }
93                } else {
94                    rutInput.classList.remove('is-valid');
95                    rutInput.classList.add('is-invalid');
96                    feedback.className = 'feedback invalid-feedback';
97                    feedback.textContent = '✗ ' + data.mensaje;
98                }
99                
100            } catch (error) {
101                feedback.className = 'feedback invalid-feedback';
102                feedback.textContent = 'Error al validar';
103            }
104        }
105        
106        // Prevenir envío del formulario
107        document.getElementById('rutForm').addEventListener('submit', function(e) {
108            e.preventDefault();
109            const rut = rutInput.value;
110            if (rut) {
111                validarRut(rut);
112            }
113        });
114    </script>
115</body>
116</html>

Integración con Laravel

Regla de validación personalizada:

PHP
1<?php
2// app/Rules/RutChileno.php
3namespace AppRules;
4
5use IlluminateContractsValidationRule;
6
7class RutChileno implements Rule
8{
9    /**
10     * Mensaje de error personalizado
11     *
12     * @var string
13     */
14    protected $message = 'El :attribute no es un RUT válido.';
15    
16    /**
17     * Determina si la validación pasa
18     *
19     * @param  string  $attribute
20     * @param  mixed  $value
21     * @return bool
22     */
23    public function passes($attribute, $value)
24    {
25        return RutValidator::validar($value);
26    }
27    
28    /**
29     * Obtiene el mensaje de error
30     *
31     * @return string
32     */
33    public function message()
34    {
35        return $this->message;
36    }
37}
38
39// app/Providers/AppServiceProvider.php
40namespace AppProviders;
41
42use IlluminateSupportServiceProvider;
43use IlluminateSupportFacadesValidator;
44
45class AppServiceProvider extends ServiceProvider
46{
47    public function boot()
48    {
49        // Registrar validación personalizada
50        Validator::extend('rut', function ($attribute, $value, $parameters, $validator) {
51            return RutValidator::validar($value);
52        });
53        
54        // Mensaje personalizado
55        Validator::replacer('rut', function ($message, $attribute, $rule, $parameters) {
56            return str_replace(':attribute', $attribute, 'El campo :attribute debe ser un RUT válido.');
57        });
58    }
59}
60
61// Uso en controlador
62namespace AppHttpControllers;
63
64use AppRulesRutChileno;
65use IlluminateHttpRequest;
66
67class ClienteController extends Controller
68{
69    public function store(Request $request)
70    {
71        $validated = $request->validate([
72            'nombre' => 'required|string|max:255',
73            'rut' => ['required', new RutChileno],
74            'email' => 'required|email'
75        ]);
76        
77        // Formatear RUT antes de guardar
78        $validated['rut'] = RutValidator::formatear($validated['rut']);
79        
80        $cliente = Cliente::create($validated);
81        
82        return response()->json([
83            'message' => 'Cliente creado exitosamente',
84            'cliente' => $cliente
85        ], 201);
86    }
87}
88
89// Modelo con mutador
90namespace AppModels;
91
92use IlluminateDatabaseEloquentModel;
93
94class Cliente extends Model
95{
96    protected $fillable = ['nombre', 'rut', 'email'];
97    
98    /**
99     * Mutador para formatear RUT al guardar
100     */
101    public function setRutAttribute($value)
102    {
103        $this->attributes['rut'] = RutValidator::formatear($value);
104    }
105    
106    /**
107     * Accessor para obtener info del RUT
108     */
109    public function getRutInfoAttribute()
110    {
111        return RutValidator::extraerInfo($this->rut);
112    }
113}

Integración con Symfony

Constraint personalizado para Symfony:

PHP
1<?php
2// src/Validator/Constraints/RutChileno.php
3namespace AppValidatorConstraints;
4
5use SymfonyComponentValidatorConstraint;
6
7/**
8 * @Annotation
9 */
10class RutChileno extends Constraint
11{
12    public $message = 'El RUT "{{ value }}" no es válido.';
13}
14
15// src/Validator/Constraints/RutChilenoValidator.php
16namespace AppValidatorConstraints;
17
18use SymfonyComponentValidatorConstraint;
19use SymfonyComponentValidatorConstraintValidator;
20use SymfonyComponentValidatorExceptionUnexpectedTypeException;
21
22class RutChilenoValidator extends ConstraintValidator
23{
24    public function validate($value, Constraint $constraint)
25    {
26        if (!$constraint instanceof RutChileno) {
27            throw new UnexpectedTypeException($constraint, RutChileno::class);
28        }
29        
30        if (null === $value || '' === $value) {
31            return;
32        }
33        
34        if (!RutValidator::validar($value)) {
35            $this->context->buildViolation($constraint->message)
36                ->setParameter('{{ value }}', $value)
37                ->addViolation();
38        }
39    }
40}
41
42// src/Entity/Cliente.php
43namespace AppEntity;
44
45use AppValidatorConstraints as AppAssert;
46use DoctrineORMMapping as ORM;
47use SymfonyComponentValidatorConstraints as Assert;
48
49/**
50 * @ORMEntity
51 * @ORMTable(name="clientes")
52 */
53class Cliente
54{
55    /**
56     * @ORMId
57     * @ORMGeneratedValue
58     * @ORMColumn(type="integer")
59     */
60    private $id;
61    
62    /**
63     * @ORMColumn(type="string", length=100)
64     * @AssertNotBlank
65     * @AssertLength(max=100)
66     */
67    private $nombre;
68    
69    /**
70     * @ORMColumn(type="string", length=12, unique=true)
71     * @AssertNotBlank
72     * @AppAssertRutChileno
73     */
74    private $rut;
75    
76    /**
77     * @ORMColumn(type="string", length=255)
78     * @AssertNotBlank
79     * @AssertEmail
80     */
81    private $email;
82    
83    // Getters y setters...
84    
85    public function setRut(string $rut): self
86    {
87        $this->rut = RutValidator::formatear($rut);
88        return $this;
89    }
90}

API REST con validación

Endpoint completo con manejo de errores:

PHP
1<?php
2// api/RutController.php
3class RutController
4{
5    /**
6     * Valida un RUT individual
7     * POST /api/rut/validar
8     */
9    public function validar()
10    {
11        header('Content-Type: application/json');
12        
13        try {
14            // Obtener datos de entrada
15            $input = json_decode(file_get_contents('php://input'), true);
16            
17            if (!isset($input['rut'])) {
18                http_response_code(400);
19                return json_encode([
20                    'error' => true,
21                    'message' => 'RUT es requerido'
22                ]);
23            }
24            
25            $rut = $input['rut'];
26            $esValido = RutValidator::validar($rut);
27            
28            $response = [
29                'valido' => $esValido,
30                'rut_original' => $rut,
31                'rut_limpio' => RutValidator::limpiar($rut)
32            ];
33            
34            if ($esValido) {
35                $info = RutValidator::extraerInfo($rut);
36                $response = array_merge($response, [
37                    'rut_formateado' => $info['formateado'],
38                    'numero' => $info['numero'],
39                    'digito_verificador' => $info['dv'],
40                    'tipo' => $info['tipo']
41                ]);
42            }
43            
44            echo json_encode($response);
45            
46        } catch (Exception $e) {
47            http_response_code(500);
48            echo json_encode([
49                'error' => true,
50                'message' => 'Error interno del servidor'
51            ]);
52        }
53    }
54    
55    /**
56     * Valida múltiples RUTs
57     * POST /api/rut/validar-lote
58     */
59    public function validarLote()
60    {
61        header('Content-Type: application/json');
62        
63        try {
64            $input = json_decode(file_get_contents('php://input'), true);
65            
66            if (!isset($input['ruts']) || !is_array($input['ruts'])) {
67                http_response_code(400);
68                return json_encode([
69                    'error' => true,
70                    'message' => 'Se requiere un array de RUTs'
71                ]);
72            }
73            
74            $resultados = [];
75            $validos = 0;
76            $invalidos = 0;
77            
78            foreach ($input['ruts'] as $rut) {
79                $esValido = RutValidator::validar($rut);
80                
81                $resultado = [
82                    'rut' => $rut,
83                    'valido' => $esValido
84                ];
85                
86                if ($esValido) {
87                    $resultado['formateado'] = RutValidator::formatear($rut);
88                    $validos++;
89                } else {
90                    $invalidos++;
91                }
92                
93                $resultados[] = $resultado;
94            }
95            
96            echo json_encode([
97                'total' => count($input['ruts']),
98                'validos' => $validos,
99                'invalidos' => $invalidos,
100                'porcentaje_validos' => count($input['ruts']) > 0 
101                    ? round(($validos / count($input['ruts'])) * 100, 2) 
102                    : 0,
103                'resultados' => $resultados
104            ]);
105            
106        } catch (Exception $e) {
107            http_response_code(500);
108            echo json_encode([
109                'error' => true,
110                'message' => 'Error procesando lote'
111            ]);
112        }
113    }
114    
115    /**
116     * Calcula dígito verificador
117     * GET /api/rut/calcular-dv/{numero}
118     */
119    public function calcularDV($numero)
120    {
121        header('Content-Type: application/json');
122        
123        if (!is_numeric($numero) || $numero < 1000000 || $numero > 99999999) {
124            http_response_code(400);
125            echo json_encode([
126                'error' => true,
127                'message' => 'Número de RUT inválido'
128            ]);
129            return;
130        }
131        
132        $dv = RutValidator::calcularDV($numero);
133        
134        echo json_encode([
135            'numero' => (int)$numero,
136            'digito_verificador' => $dv,
137            'rut_completo' => RutValidator::formatear($numero . $dv)
138        ]);
139    }
140}

Procesamiento masivo de archivos

Script para procesar CSV con RUTs:

PHP
1<?php
2/**
3 * Procesador de RUTs desde archivo CSV
4 * Uso: php procesar_ruts.php archivo.csv
5 */
6
7class RutProcessor
8{
9    private $stats = [
10        'total' => 0,
11        'validos' => 0,
12        'invalidos' => 0,
13        'procesados' => 0,
14        'errores' => []
15    ];
16    
17    /**
18     * Procesa un archivo CSV con RUTs
19     * 
20     * @param string $archivoEntrada Path del archivo CSV
21     * @param string $archivoSalida Path del archivo de salida
22     * @param int $columnaRut Índice de la columna con RUTs (0-based)
23     * @return array Estadísticas del procesamiento
24     */
25    public function procesarCSV($archivoEntrada, $archivoSalida, $columnaRut = 0)
26    {
27        if (!file_exists($archivoEntrada)) {
28            throw new Exception("Archivo no encontrado: $archivoEntrada");
29        }
30        
31        $entrada = fopen($archivoEntrada, 'r');
32        $salida = fopen($archivoSalida, 'w');
33        
34        if (!$entrada || !$salida) {
35            throw new Exception("Error abriendo archivos");
36        }
37        
38        // Procesar encabezados
39        $encabezados = fgetcsv($entrada);
40        if ($encabezados) {
41            // Agregar columnas nuevas
42            $encabezados[] = 'rut_valido';
43            $encabezados[] = 'rut_formateado';
44            $encabezados[] = 'rut_tipo';
45            fputcsv($salida, $encabezados);
46        }
47        
48        // Procesar datos
49        while (($fila = fgetcsv($entrada)) !== false) {
50            $this->stats['total']++;
51            
52            try {
53                if (isset($fila[$columnaRut])) {
54                    $rut = $fila[$columnaRut];
55                    $esValido = RutValidator::validar($rut);
56                    
57                    if ($esValido) {
58                        $this->stats['validos']++;
59                        $info = RutValidator::extraerInfo($rut);
60                        $fila[] = 'SI';
61                        $fila[] = $info['formateado'];
62                        $fila[] = $info['tipo'];
63                    } else {
64                        $this->stats['invalidos']++;
65                        $fila[] = 'NO';
66                        $fila[] = '';
67                        $fila[] = '';
68                    }
69                } else {
70                    $this->stats['errores'][] = "Fila {$this->stats['total']}: columna RUT no encontrada";
71                    $fila[] = 'ERROR';
72                    $fila[] = '';
73                    $fila[] = '';
74                }
75                
76                fputcsv($salida, $fila);
77                $this->stats['procesados']++;
78                
79                // Mostrar progreso cada 1000 registros
80                if ($this->stats['procesados'] % 1000 === 0) {
81                    echo "Procesados: {$this->stats['procesados']}\r";
82                }
83                
84            } catch (Exception $e) {
85                $this->stats['errores'][] = "Fila {$this->stats['total']}: " . $e->getMessage();
86            }
87        }
88        
89        fclose($entrada);
90        fclose($salida);
91        
92        return $this->stats;
93    }
94    
95    /**
96     * Genera reporte de procesamiento
97     * 
98     * @return string Reporte formateado
99     */
100    public function generarReporte()
101    {
102        $porcentajeValidos = $this->stats['total'] > 0 
103            ? round(($this->stats['validos'] / $this->stats['total']) * 100, 2) 
104            : 0;
105        
106        $reporte = "\n=== REPORTE DE PROCESAMIENTO ===\n";
107        $reporte .= "Total de registros: {$this->stats['total']}\n";
108        $reporte .= "RUTs válidos: {$this->stats['validos']} ($porcentajeValidos%)\n";
109        $reporte .= "RUTs inválidos: {$this->stats['invalidos']}\n";
110        $reporte .= "Registros procesados: {$this->stats['procesados']}\n";
111        
112        if (count($this->stats['errores']) > 0) {
113            $reporte .= "\nErrores encontrados:\n";
114            foreach ($this->stats['errores'] as $error) {
115                $reporte .= "- $error\n";
116            }
117        }
118        
119        return $reporte;
120    }
121}
122
123// Script principal
124if (php_sapi_name() === 'cli') {
125    if ($argc < 2) {
126        echo "Uso: php procesar_ruts.php <archivo_entrada.csv> [archivo_salida.csv] [columna_rut]\n";
127        exit(1);
128    }
129    
130    $archivoEntrada = $argv[1];
131    $archivoSalida = $argv[2] ?? 'ruts_validados.csv';
132    $columnaRut = isset($argv[3]) ? (int)$argv[3] : 0;
133    
134    try {
135        require_once 'RutValidator.php';
136        
137        $processor = new RutProcessor();
138        echo "Procesando archivo: $archivoEntrada\n";
139        
140        $stats = $processor->procesarCSV($archivoEntrada, $archivoSalida, $columnaRut);
141        
142        echo $processor->generarReporte();
143        echo "\nArchivo de salida: $archivoSalida\n";
144        
145    } catch (Exception $e) {
146        echo "Error: " . $e->getMessage() . "\n";
147        exit(1);
148    }
149}
150?>

Testing con PHPUnit

Suite completa de pruebas:

PHP
1<?php
2use PHPUnitFrameworkTestCase;
3
4class RutValidatorTest extends TestCase
5{
6    /**
7     * @dataProvider rutValidosProvider
8     */
9    public function testValidarRutsCorrectos($rut, $esperado)
10    {
11        $this->assertEquals($esperado, RutValidator::validar($rut));
12    }
13    
14    public function rutValidosProvider()
15    {
16        return [
17            ['12.345.678-5', true],
18            ['12345678-5', true],
19            ['123456785', true],
20            ['11.111.111-1', true],
21            ['15.834.661-K', true],
22            ['15834661-k', true], // k minúscula
23            ['5.126.663-3', true],
24        ];
25    }
26    
27    /**
28     * @dataProvider rutInvalidosProvider
29     */
30    public function testValidarRutsIncorrectos($rut)
31    {
32        $this->assertFalse(RutValidator::validar($rut));
33    }
34    
35    public function rutInvalidosProvider()
36    {
37        return [
38            ['12.345.678-9'],
39            ['11.111.111-2'],
40            ['00000000-0'],
41            ['abc123'],
42            [''],
43            ['1'],
44            ['12.345.678'],
45            ['12345678-'],
46        ];
47    }
48    
49    /**
50     * @dataProvider digitoVerificadorProvider
51     */
52    public function testCalcularDigitoVerificador($numero, $dvEsperado)
53    {
54        $this->assertEquals($dvEsperado, RutValidator::calcularDV($numero));
55    }
56    
57    public function digitoVerificadorProvider()
58    {
59        return [
60            [12345678, '5'],
61            [11111111, '1'],
62            [22222222, '2'],
63            [15834660, '0'],
64            [15834661, 'K'],
65            [8942487, '6'],
66        ];
67    }
68    
69    public function testFormatearRut()
70    {
71        $this->assertEquals('12.345.678-5', RutValidator::formatear('123456785'));
72        $this->assertEquals('1.111.111-1', RutValidator::formatear('11111111'));
73        $this->assertEquals('1.234.567-K', RutValidator::formatear('1234567K'));
74    }
75    
76    public function testLimpiarRut()
77    {
78        $this->assertEquals('123456785', RutValidator::limpiar('12.345.678-5'));
79        $this->assertEquals('123456785', RutValidator::limpiar('12 345 678-5'));
80        $this->assertEquals('12345678K', RutValidator::limpiar('12.345.678-k'));
81    }
82    
83    public function testExtraerInfo()
84    {
85        $info = RutValidator::extraerInfo('12.345.678-5');
86        
87        $this->assertIsArray($info);
88        $this->assertEquals(12345678, $info['numero']);
89        $this->assertEquals('5', $info['dv']);
90        $this->assertEquals('12.345.678-5', $info['formateado']);
91        $this->assertTrue($info['es_persona']);
92        $this->assertFalse($info['es_empresa']);
93        
94        // Test con RUT inválido
95        $this->assertNull(RutValidator::extraerInfo('12.345.678-9'));
96    }
97    
98    public function testCacheFunciona()
99    {
100        RutValidator::limpiarCache();
101        
102        $rut = '12.345.678-5';
103        
104        // Primera validación (sin cache)
105        $resultado1 = RutValidator::validar($rut);
106        
107        // Segunda validación (con cache)
108        $resultado2 = RutValidator::validar($rut);
109        
110        $this->assertEquals($resultado1, $resultado2);
111    }
112    
113    public function testRendimientoMasivo()
114    {
115        $inicio = microtime(true);
116        
117        // Validar 1000 RUTs
118        for ($i = 0; $i < 1000; $i++) {
119            RutValidator::validar('12.345.678-5');
120        }
121        
122        $tiempo = microtime(true) - $inicio;
123        
124        // Debería tomar menos de 1 segundo con cache
125        $this->assertLessThan(1.0, $tiempo);
126    }
127}
128?>

Consideraciones de seguridad

Mejores prácticas para producción:

PHP
1<?php
2/**
3 * Versión segura con validaciones adicionales
4 */
5class SecureRutValidator extends RutValidator
6{
7    /**
8     * Valida RUT con límite de intentos por IP
9     * 
10     * @param string $rut RUT a validar
11     * @param string $ip IP del cliente
12     * @return array Resultado con información adicional
13     */
14    public static function validarConLimite($rut, $ip)
15    {
16        // Implementar rate limiting
17        $cacheKey = "rut_attempts_" . md5($ip);
18        $attempts = apcu_fetch($cacheKey) ?: 0;
19        
20        if ($attempts > 100) { // Máximo 100 intentos por hora
21            return [
22                'error' => true,
23                'message' => 'Demasiados intentos. Intente más tarde.'
24            ];
25        }
26        
27        apcu_store($cacheKey, $attempts + 1, 3600); // TTL 1 hora
28        
29        // Sanitizar entrada
30        $rut = self::sanitizar($rut);
31        
32        // Validar
33        $esValido = parent::validar($rut);
34        
35        // Log para auditoría
36        self::logValidacion($rut, $ip, $esValido);
37        
38        return [
39            'valido' => $esValido,
40            'rut_sanitizado' => $rut
41        ];
42    }
43    
44    /**
45     * Sanitiza entrada para prevenir XSS
46     * 
47     * @param string $input Entrada del usuario
48     * @return string Entrada sanitizada
49     */
50    private static function sanitizar($input)
51    {
52        // Remover tags HTML
53        $input = strip_tags($input);
54        
55        // Remover caracteres de control
56        $input = preg_replace('/[-]/u', '', $input);
57        
58        // Limitar longitud
59        $input = substr($input, 0, 20);
60        
61        return $input;
62    }
63    
64    /**
65     * Registra validaciones para auditoría
66     * 
67     * @param string $rut RUT validado
68     * @param string $ip IP del cliente
69     * @param bool $esValido Resultado de validación
70     */
71    private static function logValidacion($rut, $ip, $esValido)
72    {
73        $log = sprintf(
74            "[%s] IP: %s, RUT: %s, Válido: %s\n",
75            date('Y-m-d H:i:s'),
76            $ip,
77            substr($rut, 0, -4) . '****', // Ocultar últimos 4 caracteres
78            $esValido ? 'SI' : 'NO'
79        );
80        
81        error_log($log, 3, '/var/log/rut_validations.log');
82    }
83}
84
85// Middleware para aplicaciones web
86class RutValidationMiddleware
87{
88    public function handle($request, $next)
89    {
90        if ($request->has('rut')) {
91            $ip = $request->ip();
92            $rut = $request->input('rut');
93            
94            $resultado = SecureRutValidator::validarConLimite($rut, $ip);
95            
96            if (isset($resultado['error'])) {
97                return response()->json($resultado, 429); // Too Many Requests
98            }
99            
100            $request->merge(['rut_validado' => $resultado]);
101        }
102        
103        return $next($request);
104    }
105}
106?>

Conclusión

PHP ofrece una implementación robusta y eficiente para la validación de RUTs chilenos. Las soluciones presentadas cubren desde scripts simples hasta integraciones empresariales complejas:

  • Funciones básicas: Para validaciones rápidas y scripts simples
  • Clases OOP: Para aplicaciones estructuradas y reutilizables
  • Integraciones con frameworks: Laravel y Symfony para aplicaciones modernas
  • APIs REST: Para servicios web escalables
  • Procesamiento masivo: Para manejar grandes volúmenes de datos
  • Seguridad: Implementaciones seguras para producción

La clave está en elegir la implementación adecuada según tus necesidades, siempre manteniendo las mejores prácticas de seguridad y rendimiento.

---

Recursos adicionales

  • PHP.net: Documentación oficial del lenguaje
  • Packagist: Busca paquetes como freshwork/chilean-bundle
  • GitHub: Repositorios con implementaciones de RUT para PHP
  • Comunidad PHP Chile: Foros y grupos de desarrolladores chilenos

Sobre el autor

CF

Catalina Fuentes

Desarrolladora Backend

Programadora backend con amor por Python y el código limpio. Practica yoga y es mentora en comunidades tech.

¿Necesitas generar o validar un RUT?

Usa nuestras herramientas gratuitas para generar y validar RUT chilenos al instante.