2 // +----------------------------------------------------------------------+
\r
3 // | PHP version 4.0 |
\r
4 // +----------------------------------------------------------------------+
\r
5 // | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
\r
6 // +----------------------------------------------------------------------+
\r
7 // | This source file is subject to version 2.0 of the PHP license, |
\r
8 // | that is bundled with this package in the file LICENSE, and is |
\r
9 // | available at through the world-wide-web at |
\r
10 // | http://www.php.net/license/2_02.txt. |
\r
11 // | If you did not receive a copy of the PHP license and are unable to |
\r
12 // | obtain it through the world-wide-web, please send a note to |
\r
13 // | license@php.net so we can mail you a copy immediately. |
\r
14 // +----------------------------------------------------------------------+
\r
15 // | Authors: Stanislav Okhvat <stanis@ngs.ru> |
\r
16 // | Co-authored by : CVH, Chris Hansel <chris@cpi-service.com> |
\r
17 // +----------------------------------------------------------------------+
\r
19 // $Id: UnitConvertor.php,v 1.00 2002/02/20 11:40:00 stasokhvat Exp $
\r
22 * UnitConvertor is able to convert between different units and currencies.
\r
24 * @author Stanislav Okhvat <stanis@sibfair.nsk.su, stanis@ngs.ru>
\r
25 * @version $Id: UnitConvertor.php,v 1.00 2002/03/01 17:00:00 stasokhvat Exp $
\r
26 * @package UnitConvertor
\r
28 * @history 01.03.2002 Implemented the code for regular and offset-based
\r
32 * By Chris Hansel (CVH): changed getConvSpecs in order to have it look up
\r
33 * intermediary conversions (also see comments in check_key).
\r
35 * Intermediary conversions are useful when no conversion ratio is specified
\r
36 * between two units when we calculate between the two. For example, we want
\r
37 * to convert between Fahrenheit and Kelvin, and we have only
\r
38 * specified how to convert Centigrade<->Fahrenheit and
\r
39 * Centigrade<->Kelvin. While a direct (Fahrenheit->Kelvin) or
\r
40 * reverse (Kelvin->Fahrenheit) lookups fail, looking for an intermediary
\r
41 * unit linking the two (Centigrade) helps us do the conversion.
\r
44 * Chris Hansel (CVH): $to_array argument of addConversion method can now
\r
45 * contain units as 'unit1/unit2/unit3', when all units stand for the same
\r
46 * thing. See examples in unitconv.php
\r
51 * Stores conversion ratios.
\r
56 var $conversion_table = array();
\r
59 * Decimal point character (default is "." - American - set in constructor).
\r
67 * Thousands separator (default is "," - American - set in constructor).
\r
72 var $thousand_separator;
\r
80 var $bases = array();
\r
83 * Constructor. Initializes the UnitConvertor object with the most important
\r
86 * @param string decimal point character
\r
87 * @param string thousand separator character
\r
91 function UnitConvertor($dec_point = '.', $thousand_sep = ',')
\r
93 $this->decimal_point = $dec_point;
\r
94 $this->thousand_separator = $thousand_sep;
\r
96 } // end func UnitConvertor
\r
99 * Adds a conversion ratio to the conversion table.
\r
101 * @param string the name of unit from which to convert
\r
102 * @param array array(
\r
103 * "pound"=>array("ratio"=>'', "offset"=>'')
\r
105 * "pound" - name of unit to set conversion ration to
\r
106 * "ratio" - 'double' conversion ratio which, when
\r
107 * multiplied by the number of $from_unit units produces
\r
109 * "offset" - an offset from 0 which will be added to
\r
110 * the result when converting (needed for temperature
\r
111 * conversions and defaults to 0).
\r
112 * @return boolean true if successful, false otherwise
\r
115 function addConversion($from_unit, $to_array)
\r
117 if (!isset($this->conversion_table[$from_unit])) {
\r
118 while(list($key, $val) = each($to_array))
\r
120 if (strstr($key, '/'))
\r
122 $to_units = explode('/', $key);
\r
123 foreach ($to_units as $to_unit)
\r
125 $this->bases[$from_unit][] = $to_unit;
\r
127 if (!is_array($val))
\r
129 $this->conversion_table[$from_unit."_".$to_unit] = array("ratio"=>$val, "offset"=>0);
\r
133 $this->conversion_table[$from_unit."_".$to_unit] =
\r
135 "ratio"=>$val['ratio'],
\r
136 "offset"=>(isset($val['offset']) ? $val['offset'] : 0)
\r
143 $this->bases[$from_unit][] = $key;
\r
145 if (!is_array($val))
\r
147 $this->conversion_table[$from_unit."_".$key] = array("ratio"=>$val, "offset"=>0);
\r
151 $this->conversion_table[$from_unit."_".$key] =
\r
153 "ratio"=>$val['ratio'],
\r
154 "offset"=>(isset($val['offset']) ? $val['offset'] : 0)
\r
163 } // end func addConversion
\r
166 * Converts from one unit to another using specified precision.
\r
168 * @param double value to convert
\r
169 * @param string name of the source unit from which to convert
\r
170 * @param string name of the target unit to which we are converting
\r
171 * @param integer double precision of the end result
\r
175 function convert($value, $from_unit, $to_unit, $precision)
\r
177 if ($this->getConvSpecs($from_unit, $to_unit, $value, $converted ))
\r
179 return number_format($converted , (int)$precision, $this->decimal_point, $this->thousand_separator);
\r
186 * CVH : changed this Function getConvSpecs in order to have it look up
\r
187 * intermediary Conversions from the
\r
188 * "base" unit being that one that has the highest hierarchical order in one
\r
189 * "logical" Conversion_Array
\r
190 * when taking $conv->addConversion('km',
\r
191 * array('meter'=>1000, 'dmeter'=>10000, 'centimeter'=>100000,
\r
192 * 'millimeter'=>1000000, 'mile'=>0.62137, 'naut.mile'=>0.53996,
\r
193 * 'inch(es)/zoll'=>39370, 'ft/foot/feet'=>3280.8, 'yd/yard'=>1093.6));
\r
194 * "km" would be the logical base unit for all units of dinstance, thus,
\r
195 * if the function fails to find a direct or reverse conversion in the table
\r
196 * it is only logical to suspect that if there is a chance
\r
197 * converting the value it only is via the "base" unit, and so
\r
198 * there is not even a need for a recursive search keeping the perfomance
\r
199 * acceptable and the ressource small...
\r
201 * CVH check_key checks for a key in the Conversiontable and returns a value
\r
203 function check_key( $key) {
\r
204 if ( array_key_exists ($key,$this->conversion_table)) {
\r
205 if (! empty($this->conversion_table[$key])) {
\r
206 return $this->conversion_table[$key];
\r
213 * Key function. Finds the conversion ratio and offset from one unit to another.
\r
215 * @param string name of the source unit from which to convert
\r
216 * @param string name of the target unit to which we are converting
\r
217 * @param double conversion ratio found. Returned by reference.
\r
218 * @param double offset which needs to be added (or subtracted, if negative)
\r
219 * to the result to convert correctly.
\r
220 * For temperature or some scientific conversions,
\r
221 * i.e. Fahrenheit -> Celcius
\r
222 * @return boolean true if ratio and offset are found for the supplied
\r
223 * units, false otherwise
\r
226 function getConvSpecs($from_unit, $to_unit, $value, &$converted)
\r
228 $key = $from_unit."_".$to_unit;
\r
229 $revkey = $to_unit."_".$from_unit;
\r
231 if ($ct_arr = $this->check_key($key)) {
\r
232 // Conversion Specs found directly
\r
233 $ratio = (double)$ct_arr['ratio'];
\r
234 $offset = $ct_arr['offset'];
\r
235 $converted = (double)(($value * $ratio)+ $offset);
\r
238 } // not found in direct order, try reverse order
\r
239 elseif ($ct_arr = $this->check_key($revkey)) {
\r
240 $ratio = (double)(1/$ct_arr['ratio']);
\r
241 $offset = -$ct_arr['offset'];
\r
242 $converted = (double)(($value + $offset) * $ratio);
\r
245 } // not found test for intermediary conversion
\r
247 // return ratio = 1 if keyparts match
\r
248 if ($key == $revkey) {
\r
251 $converted = $value;
\r
254 // otherwise search intermediary
\r
255 reset($this->conversion_table);
\r
256 while (list($convk, $i1_value) = each($this->conversion_table)) {
\r
257 // split the key into parts
\r
258 $keyparts = preg_split("/_/",$convk);
\r
259 // return ratio = 1 if keyparts match
\r
261 // Now test if either part matches the from or to unit
\r
262 if ($keyparts[1] == $to_unit && ($i2_value = $this->check_key($keyparts[0]."_".$from_unit))) {
\r
263 // an intermediary $keyparts[0] was found
\r
264 // now let us put things together intermediary 1 and 2
\r
265 $converted = (double)(((($value - $i2_value['offset']) / $i2_value['ratio']) * $i1_value['ratio'])+ $i1_value['offset']);
\r
269 } elseif ($keyparts[1] == $from_unit && ($i2_value = $this->check_key($keyparts[0]."_".$to_unit))) {
\r
270 // an intermediary $keyparts[0] was found
\r
271 // now let us put things together intermediary 2 and 1
\r
272 $converted = (double)(((($value - $i1_value['offset']) / $i1_value['ratio']) + $i2_value['offset']) * $i2_value['ratio']);
\r
280 } // end func getConvSpecs
\r
282 } // end class UnitConvertor
\r