Los organismos internaciones se han vuelto a poner de acuerdo para poner unificar los números de cuenta y gracias a ello todos los programadores y DBAs del mundo ya estamos enfrascados en la tarea de adaptar programas, validaciones, campos de las BBDD y un largo etcétera.

En cada país el código de cuenta corriente puede variar y a través del IBAN se intenta unificar la forma de esa cuenta de tal forma que sea validable. Este artículo de la wikipedia lo explica bastante bien, incluyendo los pasos tanto para generar un IBAN a partir de una CC como para validarlo

Hay una pequeña pega, la validación exige una operación mod sobre un número tan grande que es probable que muchos lenguajes no puedan llevarla a cabo. En la red existen ya muchas soluciones, esta es una. Ojo, aparte de que este ejemplo solo se ha probado con cuentas en el formato de 20 dígitos esto no ha sido más que una diversión. Tienes implementaciones mejores como php-iban

NOTA:Para evitar problemas con el copy paste o posibles fallos con algún caracter, puedes descargarte esto del repositorio de git

<?php
/**
* Iban
* Simple utility class to generate and validate IBAN account numbers
* Based on http://www.lawebdelprogramador.com/foros/Visual_Basic/1409866-Calculo_IBAN.html
* @author Pello Xabier Altadill Izura
* @version    $Id:$
* @link       http://www.pello.info
* @since      Available since 2014-01-17
* @greetz	Iban (Zabalza)
*  ... and you too
*/

class Iban {

   private static $LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	private static $COUNTRIES = array("DE","EN","IT","NO","FR","PL","RO","SR","BG","SL","NL","RU","HU","LT","TR","CA","SV","ZH","EL","ES","SQ","PT","CS","FI","SK","DA","HR","LV","ET","JA","KA","MN");


   /**
   * Given a 20 digit account number,
   * generates and returns IBAN account
   * needs refactoring
   */
	public static function generate ($accountNumber, $countryCode, $ibanPrefix=false,$spaces=true) {

		$longAccount = "";
		$remainder = 0;
		$prefix = ($ibanPrefix)?'IBAN':'';

		// Remove space chars, if any
		$accountNumber = self::clear($accountNumber);

		if (self::invalidCountryCode($countryCode)) {
			return 'Invalid country code: ' . $countryCode;
		}

		if (self::invalidAccountNumber($accountNumber)) {
			return 'Invalid account Number: ' . $accountNumber;
		}


		$longAccount =  self::rearrangeAccount($accountNumber,$countryCode);

		$remainder = self::calculateRemainder($longAccount);

		if ($spaces) {
	   	return self::addSpaces($prefix . $countryCode . sprintf('%02d',(98-$remainder)) . $accountNumber);
	   } else {
	   	return $prefix . $countryCode . sprintf('%02d',(98-$remainder)) . $accountNumber;
	   }
	}

	/**
	* validates an IBAN account
	*
	*/
	public static function validate ($ibanAccount) {
		$longAccount = '';

		$longAccount = self::clearIban($ibanAccount);

		$longAccount = self::rearrangeIban($longAccount);

		$longAccount =	substr($longAccount,2,strlen($longAccount) -2) . substr($longAccount,0, 2) ;


     	return (self::calculateRemainder($longAccount) == 1);
	}


	/**
	* checks for invalid countryCode in format or existence
	*/
	private function invalidCountryCode ($countryCode) {

		if (!preg_match('/^[A-Z]{2}$/i', $countryCode)) {
			return true;
		}

		// Maybe it could be faster a regex like DE|EN|IT|.. ?
		if (!in_array($countryCode,self::$COUNTRIES)) {
			return true;
		}

		return false;
	}

	/**
	* rearrangeAccount
	*/
	private function rearrangeAccount ($accountNumber,$countryCode) {
					return $accountNumber .
								self::letterPosition(substr($countryCode,0, 1)) .
                           self::letterPosition(substr($countryCode,1, 1)). "00";
	}

	/**
	* rearrangeIban
	*/
	private function rearrangeIban ($ibanAccount) {
					return 	substr($ibanAccount,2,strlen($ibanAccount) -2) .
		                         self::letterPosition(substr($ibanAccount,0, 1)) .
		                         	self::letterPosition(substr($ibanAccount,1, 1));
	}

	/**
	* workaround to perform mod operation with a big number.
	*/
	private function calculateRemainder($longNumber) {
		$division = 0;
		$remainder = 0;

     for ($i=0;$i

Si quieres probar esto, tendrías que escribir algo así: incluir el fichero anterior y usar directamente los métdoos estáticos de generar y validar. Hay un par de cuentas de ejemplo. Generar tiene dos parámetros opcionales. Uno booleano para decirle si queremos sacar la cuenta con el prefijo IBAN (false por defecto), y otro para decirle si queremos que saque el IBAN con separaciones (true por defecto).

<?php

include_once 'Iban.php';

//$example='2103 0166 32 1234567890';
//$expected_iban = 'ES5021030166321234567890';

$example='2100 0813 61 0123456789';
$expected_iban = 'ES79 2100 0813 6101 2345 6789';

echo Iban::generate($example,'ES') . ' == ';
echo $expected_iban;

if (Iban::validate('ES5021030166321234567890')) {
	echo 'valid IBAN';
	} else {
	echo 'invalid IBAN';
}

Habrás notado que el php no lo cierro con ?> . Eso es porque voy de superdelisto y de extrakwel. Pero en el fondo esto no ha sido más que un post con una rueda reinventada. Hay formas mucho más abreviadas de hacer esto.