Whether you're trying to develop web applications with a truly global appeal, or you're one of the roughly 300 million United States residents mired in a maddening cacophony of conflicting measurement standards, chances are you'll need to regularly convert data from one standard to another. Of course, performing even relatively simple conversion calculations such as from pounds to kilograms from memory can be fairly difficult, let alone calculating more esoteric conversions such as from square meters to acres. To automate this conversion process, I used the Zend Framework to create a calculator capable of easily migrating data among the most commonly used formats. In this tutorial, I'll show you how you can use the Zend Framework's Zend_Measure component to create your own measurement-conversion calculator.

Introducing the Zend_Measure Component

True to the Zend Framework team's stated goal of "focusing on the most commonly needed functionality," they have taken great pains to provide readily accessible solutions for carrying out commonplace tasks that developers around the globe face. The Zend_Measure component implementation is a prime example of this pragmatic approach.
Capable of converting between an enormous number of measurement formats, Zend_Measure is a useful tool for carrying out not only typical conversions such as between miles and kilometers, but also between a wide variety of scientific formats such as torque, frequency, force, illumination, current, and density. For the purpose of demonstration, I'll stick to some of the more common measurement units, but I invite you to investigate the other supported measurement types on the Zend_Measure documentation page, or perhaps better, browse the Zend_Measure source code directly.
To use the Zend_Measure component, start by defining the originating unit type and value, and then call the convertTo() method to convert the value to the desired destination unit type. Consider a simple example that converts between centimeters and inches:
$locale = new Zend_Locale('en');
$unit = new Zend_Measure_Length(36, Zend_Measure_Length::CENTIMETER, $locale);
echo "CONVERT: ".$unit->convertTo(Zend_Measure_Length::INCH); 
Executing this example will produce the following output:
CONVERT: 14.0 in
Therefore, to convert a value from one standard to another, you statically call the respective class constants, in this case Zend_Measure_Length::CENTIMETER and Zend_Measure_Length::INCH. Again, the easiest way to determine which standards are available is by examining the source code. Each Zend_Measure class (Zend_Measure_Length, Zend_Measure_Volume, Zend_Measure_Area, etc.) contains a well-organized list of class constants at the top of the file. In fact, understanding precisely what these constants are will play an important role later in this tutorial when we use reflection to dynamically retrieve the class constant values, depending upon the desired conversion task.
To drive home the syntax pattern, consider another example. This time it converts between acres and square meters:
$locale = new Zend_Locale('en');
$unit = new Zend_Measure_Area(1, Zend_Measure_Area::ACRE, $locale);
echo "CONVERT: ".$unit->convertTo(Zend_Measure_Area::SQUARE_METER); 
Executing this example will tell you that 1 acre is equivalent to 4,047 m2.

Creating a Simple Conversion Calculator

Now that you understand how the conversion process works, let's build a simple calculator using the Zend Framework. Incidentally, you can experiment with the conversion calculator by navigating to converter.wjgilmore.com. I'll start with the conversion form, which for reasons of convenience I've created within the Index controller's index method view:
<h3>Cooking Conversion Calculator</h3>

<?php if (isset($this->convertedValue)) { ?>
  <p><b>Conversion: <?= $this->convertedValue; ?></b></p>
<?php } ?>

<form action="/" method="post">

<p>
From:<br />
<input type="text" name="source_val" value="" size="3" />
<select name="source_type">
<option value="CUP_US">Cups</option>
<option value="GALLON_US">Gallons</option>
<option value="OUNCE_US">Ounces</option>
<option value="PINT_US">Pints</option>
<option value="QUART_US">Quarts</option>
<option value="TABLESPOON_US">Tablespoons</option>
<option value="TEASPOON_US">Teaspoons</option>
</select>
</p>

<p>
To:<br />
<select name="destination_type">
<option value="CUP_US">Cups</option>
<option value="GALLON_US">Gallons</option>
<option value="OUNCE_US">Ounces</option>
<option value="PINT_US">Pints</option>
<option value="QUART_US">Quarts</option>
<option value="TABLESPOON_US">Tablespoons</option>
<option value="TEASPOON_US">Teaspoons</option>
</select>
</p>

<p>
<input type="submit" name="submit" value="Convert!" />
</p>

</form>
Figure 1 presents the conversion form as it is rendered within the browser. I've purposefully kept the design layout simple in order to ensure proper rendering within mobile browsers, a destination where I've found such a calculator to be quite useful.


Figure 1. A Conversion Calculator for Cooking Measurements

Performing the Conversion Calculation

With the calculator form in place, all that's left to do is write the code, which takes advantage of Zend_Measure in order to perform the calculation. To stay on message, I'm eschewing standard development practice by omitting the user input validation. However, I of course strongly encourage you to add the necessary safeguards if you choose to use this code in a production environment:
public function indexAction()
{
  
  if ($this->getRequest()->isPost()) {       
   
    $locale = new Zend_Locale('en');
    $className = "Zend_Measure_Volume";
   
    // Retrieve the measurement type and value
    $sourceType = $this->_request->getPost('source_type');
    $sourceValue = $this->_request->getPost('source_val');

    // Retrieve the desired conversion type
    $destinationType = $this->_request->getPost('destination_type');
   
    // Reflect the Zend_Measure_Volume class
    $refl = new ReflectionClass('Zend_Measure_Volume');
     
    // Retrieve the appropriate class constants
    $sourceConstant = $refl->getConstant($sourceType);
    $destinationConstant = $refl->getConstant($destinationType);
     
    // Define the source measurement
    $unit = new Zend_Measure_Volume($sourceValue, $sourceConstant, $locale);

    // Convert the value to the desired measurement
    $this->view->convertedValue = $unit->convertTo($destinationConstant);       

  }
  
} 
All of the code in this action should be pretty straightforward, except perhaps the section that deals with reflection. As I mentioned previously, each Zend_Measure class contains a list of supported conversion types, each defined as a class constant. For instance, here's a snippet of several class constants found in the Zend_Measure_Volume class:
  const ACRE_FOOT           = 'ACRE_FOOT';
  const ACRE_FOOT_SURVEY    = 'ACRE_FOOT_SURVEY';
  const ACRE_INCH           = 'ACRE_INCH';
  const BARREL_WINE         = 'BARREL_WINE';
  const BARREL              = 'BARREL';
Because we're using the form to dynamically assign the conversion types (for instance, OUNCE_US and GALLON_US), we require a means to dynamically retrieve the appropriate class constant. Using the reflection class's getConstant() method serves this need nicely.

Converting Roman Numerals to Decimal Equivalents

As I mentioned, the Zend_Measure component is capable of converting much more than the typical volume, weight, and length values that we regularly encounter. Roman numerals offer one particularly interesting example of this component's flexibility. An avid watcher of Bugs Bunny cartoons as a child, I often took wild guesses at the meaning of the Roman numerals located below the Warner Bros. shield in the opening credits. I rarely managed to decipher many beyond the most commonly used numbers such as I, V, and X. Armed with a browser and the Zend_Measure component, today's toddlers can make such conversions with ease. Suppose you want to convert the Roman number MCMLXIV to the Arabic equivalent:
$locale = new Zend_Locale('en');
$unit = new Zend_Measure_Number('MCMLXIV', Zend_Measure_Number::ROMAN, $locale);
echo "CONVERT: ".str_replace(",", "", $unit->convertTo(Zend_Measure_Number::DECIMAL)); 

Conclusion

The Zend_Measure component is illustrative of the considerable conveniences the Zend Framework offers. It can abstract a tedious, error-prone conversion process into an easily accessible set of classes. I'd love to see links to your own conversion solutions involving this powerful component!

About the Author

W. Jason Gilmore is founder of EasyPHPWebsites.com. He is the author of several popular books, including "Easy PHP web sites with the Zend Framework," "Easy PayPal with PHP," and "Beginning PHP and MySQL, Third Edition." to e-mail him.