--- /dev/null
+<?php\r
+// +----------------------------------------------------------------------+\r
+// | PHP version 4.0 |\r
+// +----------------------------------------------------------------------+\r
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |\r
+// +----------------------------------------------------------------------+\r
+// | This source file is subject to version 2.0 of the PHP license, |\r
+// | that is bundled with this package in the file LICENSE, and is |\r
+// | available at through the world-wide-web at |\r
+// | http://www.php.net/license/2_02.txt. |\r
+// | If you did not receive a copy of the PHP license and are unable to |\r
+// | obtain it through the world-wide-web, please send a note to |\r
+// | license@php.net so we can mail you a copy immediately. |\r
+// +----------------------------------------------------------------------+\r
+// | Authors: Stanislav Okhvat <stanis@ngs.ru> |\r
+// | Co-authored by : CVH, Chris Hansel <chris@cpi-service.com> |\r
+// +----------------------------------------------------------------------+\r
+//\r
+// $Id: UnitConvertor.php,v 1.00 2002/02/20 11:40:00 stasokhvat Exp $\r
+\r
+/**\r
+* UnitConvertor is able to convert between different units and currencies.\r
+*\r
+* @author Stanislav Okhvat <stanis@sibfair.nsk.su, stanis@ngs.ru>\r
+* @version $Id: UnitConvertor.php,v 1.00 2002/03/01 17:00:00 stasokhvat Exp $\r
+* @package UnitConvertor\r
+* @access public\r
+* @history 01.03.2002 Implemented the code for regular and offset-based\r
+* conversions\r
+*\r
+* 13.12.2004\r
+* By Chris Hansel (CVH): changed getConvSpecs in order to have it look up \r
+* intermediary conversions (also see comments in check_key).\r
+*\r
+* Intermediary conversions are useful when no conversion ratio is specified\r
+* between two units when we calculate between the two. For example, we want\r
+* to convert between Fahrenheit and Kelvin, and we have only\r
+* specified how to convert Centigrade<->Fahrenheit and\r
+* Centigrade<->Kelvin. While a direct (Fahrenheit->Kelvin) or\r
+* reverse (Kelvin->Fahrenheit) lookups fail, looking for an intermediary\r
+* unit linking the two (Centigrade) helps us do the conversion.\r
+*\r
+* 13.12.2004\r
+* Chris Hansel (CVH): $to_array argument of addConversion method can now\r
+* contain units as 'unit1/unit2/unit3', when all units stand for the same\r
+* thing. See examples in unitconv.php\r
+*/\r
+class UnitConvertor\r
+{\r
+ /**\r
+ * Stores conversion ratios.\r
+ *\r
+ * @var array\r
+ * @access private\r
+ */\r
+ var $conversion_table = array();\r
+\r
+ /**\r
+ * Decimal point character (default is "." - American - set in constructor).\r
+ *\r
+ * @var string\r
+ * @access private\r
+ */\r
+ var $decimal_point;\r
+\r
+ /**\r
+ * Thousands separator (default is "," - American - set in constructor).\r
+ *\r
+ * @var string\r
+ * @access private\r
+ */\r
+ var $thousand_separator;\r
+\r
+ /**\r
+ * For future use\r
+ *\r
+ * @var array\r
+ * @access private\r
+ */\r
+ var $bases = array();\r
+\r
+ /**\r
+ * Constructor. Initializes the UnitConvertor object with the most important\r
+ * properties.\r
+ *\r
+ * @param string decimal point character\r
+ * @param string thousand separator character\r
+ * @return void\r
+ * @access public\r
+ */\r
+ function UnitConvertor($dec_point = '.', $thousand_sep = ',')\r
+ {\r
+ $this->decimal_point = $dec_point;\r
+ $this->thousand_separator = $thousand_sep;\r
+\r
+ } // end func UnitConvertor\r
+\r
+ /**\r
+ * Adds a conversion ratio to the conversion table.\r
+ *\r
+ * @param string the name of unit from which to convert\r
+ * @param array array(\r
+ * "pound"=>array("ratio"=>'', "offset"=>'')\r
+ * )\r
+ * "pound" - name of unit to set conversion ration to\r
+ * "ratio" - 'double' conversion ratio which, when\r
+ * multiplied by the number of $from_unit units produces\r
+ * the result\r
+ * "offset" - an offset from 0 which will be added to\r
+ * the result when converting (needed for temperature\r
+ * conversions and defaults to 0).\r
+ * @return boolean true if successful, false otherwise\r
+ * @access public\r
+ */\r
+ function addConversion($from_unit, $to_array)\r
+ {\r
+ if (!isset($this->conversion_table[$from_unit])) {\r
+ while(list($key, $val) = each($to_array))\r
+ {\r
+ if (strstr($key, '/'))\r
+ {\r
+ $to_units = explode('/', $key);\r
+ foreach ($to_units as $to_unit)\r
+ {\r
+ $this->bases[$from_unit][] = $to_unit;\r
+\r
+ if (!is_array($val))\r
+ {\r
+ $this->conversion_table[$from_unit."_".$to_unit] = array("ratio"=>$val, "offset"=>0);\r
+ }\r
+ else\r
+ {\r
+ $this->conversion_table[$from_unit."_".$to_unit] =\r
+ array(\r
+ "ratio"=>$val['ratio'],\r
+ "offset"=>(isset($val['offset']) ? $val['offset'] : 0)\r
+ );\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ $this->bases[$from_unit][] = $key;\r
+\r
+ if (!is_array($val))\r
+ {\r
+ $this->conversion_table[$from_unit."_".$key] = array("ratio"=>$val, "offset"=>0);\r
+ }\r
+ else\r
+ {\r
+ $this->conversion_table[$from_unit."_".$key] =\r
+ array(\r
+ "ratio"=>$val['ratio'],\r
+ "offset"=>(isset($val['offset']) ? $val['offset'] : 0)\r
+ );\r
+ }\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+ return false;\r
+\r
+ } // end func addConversion\r
+\r
+ /**\r
+ * Converts from one unit to another using specified precision.\r
+ *\r
+ * @param double value to convert\r
+ * @param string name of the source unit from which to convert\r
+ * @param string name of the target unit to which we are converting\r
+ * @param integer double precision of the end result\r
+ * @return void\r
+ * @access public\r
+ */\r
+ function convert($value, $from_unit, $to_unit, $precision)\r
+ {\r
+ if ($this->getConvSpecs($from_unit, $to_unit, $value, $converted ))\r
+ {\r
+ return number_format($converted , (int)$precision, $this->decimal_point, $this->thousand_separator);\r
+ } else {\r
+ return false;\r
+ }\r
+ } // end func\r
+\r
+ /**\r
+ * CVH : changed this Function getConvSpecs in order to have it look up \r
+ * intermediary Conversions from the \r
+ * "base" unit being that one that has the highest hierarchical order in one\r
+ * "logical" Conversion_Array\r
+ * when taking $conv->addConversion('km',\r
+ * array('meter'=>1000, 'dmeter'=>10000, 'centimeter'=>100000,\r
+ * 'millimeter'=>1000000, 'mile'=>0.62137, 'naut.mile'=>0.53996,\r
+ * 'inch(es)/zoll'=>39370, 'ft/foot/feet'=>3280.8, 'yd/yard'=>1093.6));\r
+ * "km" would be the logical base unit for all units of dinstance, thus, \r
+ * if the function fails to find a direct or reverse conversion in the table \r
+ * it is only logical to suspect that if there is a chance \r
+ * converting the value it only is via the "base" unit, and so \r
+ * there is not even a need for a recursive search keeping the perfomance \r
+ * acceptable and the ressource small...\r
+ *\r
+ * CVH check_key checks for a key in the Conversiontable and returns a value\r
+ */\r
+ function check_key( $key) {\r
+ if ( array_key_exists ($key,$this->conversion_table)) {\r
+ if (! empty($this->conversion_table[$key])) {\r
+ return $this->conversion_table[$key];\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Key function. Finds the conversion ratio and offset from one unit to another.\r
+ *\r
+ * @param string name of the source unit from which to convert\r
+ * @param string name of the target unit to which we are converting\r
+ * @param double conversion ratio found. Returned by reference.\r
+ * @param double offset which needs to be added (or subtracted, if negative)\r
+ * to the result to convert correctly. \r
+ * For temperature or some scientific conversions,\r
+ * i.e. Fahrenheit -> Celcius\r
+ * @return boolean true if ratio and offset are found for the supplied\r
+ * units, false otherwise\r
+ * @access private\r
+ */\r
+ function getConvSpecs($from_unit, $to_unit, $value, &$converted)\r
+ {\r
+ $key = $from_unit."_".$to_unit;\r
+ $revkey = $to_unit."_".$from_unit;\r
+ $found = false;\r
+ if ($ct_arr = $this->check_key($key)) {\r
+ // Conversion Specs found directly\r
+ $ratio = (double)$ct_arr['ratio'];\r
+ $offset = $ct_arr['offset']; \r
+ $converted = (double)(($value * $ratio)+ $offset);\r
+ \r
+ return true;\r
+ } // not found in direct order, try reverse order\r
+ elseif ($ct_arr = $this->check_key($revkey)) {\r
+ $ratio = (double)(1/$ct_arr['ratio']);\r
+ $offset = -$ct_arr['offset'];\r
+ $converted = (double)(($value + $offset) * $ratio);\r
+ \r
+ return true;\r
+ } // not found test for intermediary conversion\r
+ else {\r
+ // return ratio = 1 if keyparts match\r
+ if ($key == $revkey) {\r
+ $ratio = 1;\r
+ $offset = 0;\r
+ $converted = $value;\r
+ return true;\r
+ } \r
+ // otherwise search intermediary\r
+ reset($this->conversion_table);\r
+ while (list($convk, $i1_value) = each($this->conversion_table)) {\r
+ // split the key into parts\r
+ $keyparts = preg_split("/_/",$convk);\r
+ // return ratio = 1 if keyparts match\r
+ \r
+ // Now test if either part matches the from or to unit\r
+ if ($keyparts[1] == $to_unit && ($i2_value = $this->check_key($keyparts[0]."_".$from_unit))) {\r
+ // an intermediary $keyparts[0] was found\r
+ // now let us put things together intermediary 1 and 2\r
+ $converted = (double)(((($value - $i2_value['offset']) / $i2_value['ratio']) * $i1_value['ratio'])+ $i1_value['offset']);\r
+\r
+ $found = true;\r
+ \r
+ } elseif ($keyparts[1] == $from_unit && ($i2_value = $this->check_key($keyparts[0]."_".$to_unit))) {\r
+ // an intermediary $keyparts[0] was found\r
+ // now let us put things together intermediary 2 and 1\r
+ $converted = (double)(((($value - $i1_value['offset']) / $i1_value['ratio']) + $i2_value['offset']) * $i2_value['ratio']);\r
+\r
+ $found = true; \r
+ } \r
+ }\r
+ return $found; \r
+ }\r
+\r
+ } // end func getConvSpecs\r
+ \r
+} // end class UnitConvertor\r
+?>
\ No newline at end of file