Es un pequeño proyecto que tengo desde hace tiempo y utilizo en mis desarrollos de php. Parece una tontería, pero ahorra tiempo a la hora de convertir unidades dentro de un programa. Esto es una parte importante a la hora de proporcionar una experiencia de usuario, ya que a la hora de expresar tamaño de archivos, si decimos que el archivo ocupa 1602392 bytes, al usuario final lo dejamos igual, es más, con cara de tonto frente al ordenador mientras cuenta los números y echando la cuenta de la abuela, dice 1.6Mb (ya no quiero ver lo que hará ese usuario cuando vea 1Tb en bytes); es un detalle para nuestro programa que nos diga: 1.52Mb por ejemplo.
Lo mismo pasará con el tiempo, si medimos nuestro tiempo en segundos, es horrible dejar al usuario con calculadora en mano haciendo cuentas para verlo en una unidad que pueda manejar fácilmente (1314000 segundos es un año), lo mismo pasará a la hora de introducir la información: no se le puede preguntar a un ciudadano de a pié que nos diga en segundos cuánto tiempo hace que vive en un lugar determinado.
Por otra parte tenemos la necesidad de traducir nuestros programas, por lo que tenemos que tener en cuenta que un inglés nos diga «5 days» y un francés «5 jours», mientras que en español, debemos aceptar «día, días, dia y días» porque no todo el mundo le pone la tilde. Además, si medimos distancias, debemos aceptar «metros, metro y m», es decir, las unidades de medida tendrás alias y dependiendo del idioma debemos aceptar unos u otros.
1 2 3 4 5 6 7 8 9 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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 | <?php /** * Unit converter v0.1 * Copyright (C) 2012 Gaspar Fernández <gaspar.fernandez@totaki.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /******** UTILIDADES *********/ /* Extrae una clave de un valor, sólo si existe, si no, devuelve $default */ function get_akey(&$array, $key, $defaultValue=null) { return (isset($array[$key]))?$array[$key]:$defaultValue; } class UnitConverter { private $timeUnitName="time"; private $lengthUnitName="length"; private $storageUnitName="storage"; protected $units=array(); protected $validUnitTypes=array(); function __construct() { /* format: $this->validUnitTypes[UNITNAME]=FILLCALLBACK */ $this->validUnitTypes[$this->timeUnitName]=array($this, 'fillTimeUnits'); $this->validUnitTypes[$this->lengthUnitName]=array($this, 'fillLengthUnits'); $this->validUnitTypes[$this->storageUnitName]=array($this, 'fillStorageUnits'); if (method_exists($this, 'afterConstruct')) call_user_func(array($this, 'afterConstruct')); } /** * Insertar unidad y razón * * @param $type Tipo de unidad (unit type) * @param $name Nombre de la unidad * @param $aliases Alias de la unidad * @param $ratio Razón de conversión con la unidad anterior */ private function insertUnit($type, $name, $aliases, $ratio) { $this->units[$type][]=array('name' => $name, 'aliases' => explode(',', $aliases), 'ratio'=>$ratio); } /** * Rellena el array de unidades de tiempo */ protected function fillTimeUnits() { $unit = $this->timeUnitName; if (empty($this->units[$unit])) { $this->insertUnit($unit, 'seconds', _('seconds,second,sec,secs,s'), 0); $this->insertUnit($unit, 'minutes', _('minutes,minute,min,mins,m'), 60); $this->insertUnit($unit, 'hours', _('hours,hour,hr,hrs,h'), 60); $this->insertUnit($unit, 'days', _('days,day,d'), 24); $this->insertUnit($unit, 'weeks', _('weeks,week,w'), 7); $this->insertUnit($unit, 'months', _('months,month,mth,M'), 30); $this->insertUnit($unit, 'years', _('years,year,yrs,y'), 365); return true; } else return false; } /** * Rellena el array de unidades de longitud */ protected function fillLengthUnits() { $unit = $this->lengthUnitName; if (empty($this->units[$unit])) { $this->insertUnit($unit, 'millimeter', _('milimeter,mm'), 0); $this->insertUnit($unit, 'centimeter', _('centimeter,cm'), 10); $this->insertUnit($unit, 'decimeter', _('decimeter,dm'), 10); $this->insertUnit($unit, 'meter', _('meter,m'), 10); $this->insertUnit($unit, 'decameter', _('decameter,Dm'), 10); $this->insertUnit($unit, 'hectometer', _('hectometer,hmM'), 10); $this->insertUnit($unit, 'kilometer', _('kilometer,km'), 10); $this->insertUnit($unit, 'megameter', _('megameter,MM'), 1000); return true; } else return false; } /** * Rellena el array de unidades de almacenamiento */ protected function fillStorageUnits() { $unit = $this->storageUnitName; if (empty($this->units[$unit])) { $this->insertUnit($unit, 'bit', _('bit,bits'), 0); $this->insertUnit($unit, 'nibble', _('nibble'), 4); $this->insertUnit($unit, 'byte', _('byte'), 2); $this->insertUnit($unit, 'kilobyte', _('kiloByte,Kb'), 1024); $this->insertUnit($unit, 'megabyte', _('megaByte,Mb'), 1024); $this->insertUnit($unit, 'gigabyte', _('gigaByte,Gb'), 1024); $this->insertUnit($unit, 'terabyte', _('teraByte,Tb'), 1024); $this->insertUnit($unit, 'petabyte', _('petaByte,Pb'), 1024); return true; } else return false; } /** * Rellena todas las unidades de un tipo de unidad dado * * @param $unitType Tipo de unidad a rellenar */ private function fillUnit($unitType) { if ($this->validUnitType($unitType)) { call_user_func($this->validUnitTypes[$unitType]); return true; } else return false; } /** * Valida un tipo de unidad * * @param $unitType Tipo de unidad a probar */ private function validUnitType($unitType) { return (get_akey($this->validUnitTypes, $unitType)!=null); } /** * Mira en los alias de un tipo de unidad * * @param $unitInfo Array de información sobre la unidad * @param $unitName Nombre de la unidad * * @return true if unitName is in aliases */ private function aliasMatch($unitInfo, $unitName) { foreach ($unitInfo['aliases'] as $alias) { if ($unitName==$alias) return true; } } /** * Encuentra un nombre de unidad o un alias * * @param $type Tipo de unidad * @param $unitName Nombre de la unidad * @param $validUnits Unidades que son válidas para nosotros (puede que no queramos convertir a todas las unidades) * * @return Índice dentro del array donde está la unidad (si se encuentra) */ private function findUnit($type, $unitName, $validUnits=null) { if (!$this->fillUnit($type)) return false; $units=$this->units[$type]; $nunits=count($units); for ($i=0; $i<$nunits; $i++) { if (($validUnits!=null) && (!array_search($units[$i]['name'], $validUnits) )) continue; if ( ($units[$i]['name']==$unitName) || ($this->aliasMatch($units[$i], $unitName) ) ) return $i; } return false; } /** * Convierte unidades * * @param $type Tipo de unidad * @param $qty Cantidad a convertir * @param $fromUnit Unidad de origen * @param $toUnit Unidad de destino * * @return Resultado o falso (si hay error) */ public function convert($type, $qty, $fromUnit, $toUnit) { if (!$this->fillUnit($type)) return false; $fromUnitNdx=$this->findUnit($type, $fromUnit); $toUnitNdx=$this->findUnit($type, $toUnit); if ( ($fromUnitNdx===false) || ($toUnitNdx===false) ) return false; /* It wont be ever possible, but maybe it is useful for debugging */ if ( ($fromUnitNdx<0) || ($toUnitNdx>=count($this->units[$type]) ) ) return false; /* if ($fromUnitNdx==$toUnitNdx) */ if ($fromUnitNdx>$toUnitNdx) { /* Convert Down */ for ($i=$fromUnitNdx; $i>$toUnitNdx; $i--) $qty*=$this->units[$type][$i]['ratio']; } else { /* Convert up */ for ($i=$fromUnitNdx; $i<$toUnitNdx; $i++) $qty/=$this->units[$type][$i+1]['ratio']; } return $qty; } /** * Obtiene la cantidad expresada en la unidad $fromUnit en la unidad más grande * ej: 86400 segundos = 1 day (y no 0.03 months) * * @param $type Tipo de unidad * @param $qty Cantidad * @param $fromUnit Unidad de origen * * @return array(cantidad nueva, unidad nueva) */ public function getMaxUnit($type, $qty, $fromUnit) { if (!$this->fillUnit($type)) return false; $fromUnitNdx=$this->findUnit($type, $fromUnit); $toUnitNdx=count($this->units[$type]); $currentUnit=$fromUnit; /* Convert up */ for ($i=$fromUnitNdx; $i<$toUnitNdx-1; $i++) { $tmp=$qty/$this->units[$type][$i+1]['ratio']; if (!is_int($tmp)) break; $qty=$tmp; $currentUnit=$i+1; } return array($qty, $this->units[$type][$currentUnit]['aliases'][0]); } /** * Valida una unidad dada en cualquiera de sus alias * * @param $type Tipo de unidad * @param $unit Unidad * @param $validUnits Array de unidades válidas * * @return bool true if unit is right */ public function validateUnit($type, $unit, $validUnits=null) { return ($this->findUnit($type, $unit, $validUnits)!==false); } /** * Obtiene una lista de los tipos de unidad válidos * Útil para depuración * * @return array de tipos de unidades */ public function getValidUnitTypes() { $types=array(); foreach ($types as $name => $filler) { $types[]=$name; } return $types; } } |
En definitiva, resumo un poco sus características:
- Convierte entre unidades de medida siempre que haya una razón entre ellas
- Soporta tipos de unidades en varios idiomas, con gettext (se puede cambiar si cambiamos la función __())
- Soporta alias en las unidades de medida (segundo, segundos, seg, segs, s)
- A la hora de convertir, la unidades pueden estar expresadas de cualquier forma, con el nombre o con algún alias
- Soporta transformación de una cantidad en la unidad entera más grande
Ahora vemos un ejemplo:
1 2 3 4 5 6 7 8 | <?php require_once('unitConverter.class.php'); $converter=new UnitConverter(); echo $converter->convert('storage', 22, 'Mb', 'Kb'); ?> |
Esto nos devolverá: 22528
Y si por ejemplo hacemos:
1 2 3 4 5 6 7 8 | <?php require_once('unitConverter.class.php'); $converter=new UnitConverter(); print_r($converter->getMaxUnit('time', 86400, 's')); ?> |
Esto devolverá:
Array
(
[0] => 1
[1] => days
)
Pingback: Bitacoras.com /
Excelente Blog! muchos saludos desde Colombia 😉
@Mauricio
Muchas gracias por tu comentario! Y por echarme un cable! 🙂
Excellent information providing by your Article thank you for taking the time to share with us such a nice article.