Buscar en este blog

viernes, 1 de mayo de 2009

PHP. módulo de un "long". Calcular dígito control de IBAN.

Calculando el DC dígito de control de un número de cuenta IBAN me he encontrado un problema con el módulo o resto.
Al manejar un número que sobrepasa el integer, "no se exacta la barrera, pero supongamos que a partir de 2147483647 deja de ser nuestro integer de toda la vida", manual PHP. Enteros y al calcular el modulo de 97 de ese número long perdía la precisión y devolvía un resto incorrecto.

Hay varias soluciones a esta situación, yo opté por usar bcmod function bcmod ya que en PHP hay funciones para casi todo y sino en www.phpclasses.org podremos encontrar alguna clase que nos facilite el desarrollo.

Supongo que también se debe a la "anarquía" de PHP que no obliga a declarar el tipo de dato de la variable, aunque cada vez empiezo a encontrar más código en el que se hacen casting a las variables.

En el mundo java un int es un int, un String es un String y un long es un long y todo el día haciendo casting pero siempre tienes claro que hay en una variable.


$foo = (array)$foo;
$foo = (b)$foo;
$foo = (binary)$foo;
$foo = (bool)$foo;
$foo = (boolean)$foo;
$foo = (double)$foo;
$foo = (float)$foo;
$foo = (int)$foo;
$foo = (integer)$foo;
$foo = (object)$foo;
$foo = (real)$foo;
$foo = (string)$foo;

type-casting-php


En esta función que cálcula el dígito de control de un iban, recopilada de www.desarrolloweb.com me encontré con el problema devolviendome mal el resto el operador % y solucionandolo cuando apliqué la función bmod en lugar del operador % de siempre.

Al usar % me devolvía mal el resto cuando le pasaba un long 9999999999999999
//$modulo97 = $ibanConDC_ % 97;
Al usar bcmod todo a la perfección
$modulo97 = bcmod($ibanConDC_, 97);

La función es muy mejorable, pero siempre hay prisas con los proyectos:

/**
* Gernerar el DC, dígito control de IBAN, y devolver en nuevo IBAN con DC
*
* @link http://www.desarrolloweb.com/articulos/2484.php
* @param string $_iban
* @return $iban_
*/
function generarDCInToIban( $_iban ) {

$ibanConDC_ = -1;

// IBAN sin DC, DC = 00 : BE00999999999999
// IBAN con DC, DC = 89 : BE89999999999999

// Mover los cuatro primeros caracteres del número IBAN a la derecha:
$ibanConDC_ = substr($_iban,4)."".substr($_iban,0,4);


// Convertir las letras a números según la siguiente tabla:
// A=10 G=16 M=22 S=28 Y=34
// B=11 H=17 N=23 T=29 Z=35
// C=12 I=18 O=24 U=30
// D=13 J=19 P=25 V=31
// E=14 K=20 Q=26 W=32
// F=15 L=21 R=27 X=33
$letras_array = array("A","B","C","D","E","F","G","H","I","J","K","L",
"M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z");
$numeros_array = array("10","11","12","13","14","15","16","17","18","19","20","21",
"22","23","24","25","26","27","28","29","30","31","32","33","34","35");


$ibanConDC_ = str_replace( $letras_array, $numeros_array, $ibanConDC_);


// Aplicar la operación módulo 97 y restar al número 98, el valor obtenido.
// Si el resultado consta de sólo un dígito, insertar un cero a la izquierda.

//$modulo97 = intval($ibanConDC_) % 97;
//$modulo97 = $ibanConDC_ % 97;
$modulo97 = bcmod($ibanConDC_, 97);

$dc = 98 - $modulo97;

// insertar 0 a la izquierda si fuera menor de dos dígitos
$dc = sprintf("%02d",$dc);

// Sustituimos los dígitos 2 y 4 por el $dc
$ibanConDC_ = substr($_iban,0,2).$dc.substr($_iban,4);


return $ibanConDC_;
}




Esta función solo pasa de un IBAN sin DC dígito de control a un IBAN con dígito de control, si buscais directamente de CCC cuenta bancaria a IBAN, el tema se complica ya que cada pais tiene un prefijo de dos caracteres y cada pais tiene un sistema de CCC cuentas bancarias diferente, pero seguro que googleando lo podeis encontrar, pasarme el código cuando lo localiceis.

Yo os muestro una forma muy sencilla para los IBAN de cuentas bancarias españolas, (seguro que es mejorable se aceptan sugerencias):


/**
* Calcular IBAN
*
* @link https://empresas.bankinter.com/www/es-es/cgi/empresas+fichhtml?nombre=empresas/cmd_exterior/cmd_negocio_internacional/calcula_iban.html
* @param string $_entidad, $_sucursal, $_dc, $_cuenta
* @return $iban_
*/
function calcularIban( $_entidad, $_sucursal, $_dc, $_cuenta ) {

$iban_ = -1;

// CCC : 01280010120123456789
// IBAN : ES7001280010120123456789

$codPais = "ES";
$dc = "00"; // No sabemos el dígito de control del IBAN, ponemos 00

$iban_ = $codPais."".$dc."".$_entidad."".$_sucursal."".$_dc."".$_cuenta;

return $iban_;
}


Con la CCC cuenta bancaria hallamos el IBAN sin DC calcularIban(...)
y con el generarDCInToIban(...) podemos obtener el DC de la cuenta IBAN, modificando la función también nos sirve directamente para validar el IBAN.

Se aceptan sugerencias y mejoras de las funciones.

Si quereis calcular el DC dígito de control de una CCC cuenta bancaría bulma tiene publicada la función en PHP. No os asusteis que también escriben en castellano Cómo calcular el dígito de control de una cuenta corriente

Espero que os sirva de ayuda

2 comentarios:

ddanone dijo...

Más sencillo:

- en una sola línea:
sprintf("%02d", 98-bcmod($ccc.(ord($pais[0])-55).ord($pais[1])-55.'00',97) );

y más sencillo si sólo es para una cuenta española:
sprintf("%02d", 98-bcmod($ccc.'142800',97) );

mugedoc dijo...

Está muy bien la solución