Skip to end of metadata
Go to start of metadata

Zend Framework: Zend_Measure Component Proposal

Proposed Component Name Zend_Measure
Developer Notes http://framework.zend.com/wiki/display/ZFDEV/Zend_Measure
Proposers Thomas Weidner
Gavin (Zend-liaison)
Revision 4.0 - 25 Jan 2007: API rework, simpler useage
3.1 - 23 July 2006: Corrections based on actual work
3.0 - 18 July 2006: Class redesign based on actual work and comments from Zend
2.0 - 26 June 2006: Complete redesign of class skeleton, added new functions
1.1 - 20 June 2006: Changed class to be static
1.0 - 19 June 2006: Initial release extracted from Zend_Locale Proposal V1.0. (wiki revision: 28)

Table of Contents

1. Overview

Zend_Measure is a locale aware Class which can convert measurement units.
Hint: The name Zend_Measure was taken because Zend_Convert implements to convert between different file-types (Text->Excel, Windows->Unix,...) and not measurement units.

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • Lightweight and fast implementation
  • Simple use for ZF-user
  • Handling of different input formats

4. Dependencies on Other Framework Components

5. Theory of Operation

Zend_Measure can convert measurement units between different locales. The Conversion can be done for

  • Length
  • Weight
  • Temperature
  • Area
  • Power
  • Energy
  • and much more...

6. Milestones / Tasks

zone: Missing {zone-data:milestones}

7. Class Index

  • Zend_Measure_Exception
  • Zend_Measure

8. Use Cases

Get output of Kelvin in actual locale

Get formatted output in different locale

Different Input Types which would be recognized

Convert Fahrenheit to Celsius

Convert Kilo to Tonne with other locale

Maths with different units

9. Class Skeletons

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Jun 20, 2006

    This seems like it would be better implemented at as a library of helper function rather than one big class – especially given that the locale is passed to each method. If it was a library of functions it would be infinitely extensible without needing to include the whole giant class.

    1. Jun 20, 2006

      The locale is not passed explicit.
      When no locale is defined, the standard locale will be taken.

      BUT you can define that the returned result is displayed in a different locale.
      So all locale-parameters are optional as you will see at the first usecase.

      How would the "library of helper functions" look like and be implemented in your thought ?

      Btw: This class is extensible, as its complete PUBLIC.
      Btw2: There are only 11 functions so I don't think thas this is a BIG CLASS.

      1. Jun 20, 2006

        It seems like the methods would be better implemented as static methods, since they wouldn't vary between instances of the original class (though would if the class was extended, in which case it would have a different name anyway and it wouldn't matter if they were static). Beyond that, I think the class is well-designed. I would suggest adding a method to set an override for the default locale and have all methods use that locale unless a different one is specified in calls to them.

        EXAMPLE 1:

        /* Return value would use the default locale */
        Zend_Unit.toWeight($units);

        EXAMPLE 2:

        Zend_Unit.setLocale('fr');

        /* Return value would use the 'fr' locale instead of the default */
        Zend_Unit.toWeight($units);

        /* Return value would use the 'en' locale instead of the 'fr' locale */
        Zend_Unit.toWeight($units, $format, 'en');

        Also, this site as a reference, as it likely has all the information you'll need in one place and possibly even other conversions you haven't thought to include yet.

        1. Jun 20, 2006

          I like the staticification of Zend_Unit and will change this with next wiki-update.

      2. Jun 20, 2006

        I am not clear how it would look, but ultimately there are hundreds of units and the class would be HUGE in order to be complete. Also what happens with:

        $unit = Zend_Unit('25742.54 kg',Zend_Unit::KILOGRAM);
        echo $unit->toPressure(Zend_Unit::WATT,"n.2",de_DE); // outputs ?

        Maybe break it up into individual static classes for each type of unit like:

        class Zend_Units_Weight
        class Zend_Units_Length
        class Zend_Units_Volume
        ...

        1. Jun 20, 2006

          Sure there are hundreds of units.
          This was the reason why I split them in types.

          There are only a few types like weight, lenght, temperature and so on.
          These recognise their known types.

          The example you gave would throw 2 exeptions :

          • WATT is no unit of type Pressure
          • KILOGRAM can not be converted to WATT

          Conversion can only done between same types.

          When you give an type-less input all works right :

          When split into individual classes each class will have 1 function... I don't think this is nessecary.

          1. Jun 20, 2006

            "When split into individual classes each class will have 1 function... I don't think this is nessecary."

            They would be polymorphic if the all had the same interface with a convert() method. That might be handy for something.

    2. Jun 22, 2006

      I really, really agree with this. Each helper class can identify itself as a weight, volume, or length, and then the conversion process can be delegated down.

      1. Jun 22, 2006

        So you want to have

        and then call for example

        .....

        I don't think this is handy. From the site of object orientation is makes sense, but for the user not. More tipping and less useability.

        But again...

        "How do you want the class to look like ?"

        1. Jun 23, 2006

          I've thought more about it. Scratch the delegating down part.

          > "How do you want the class to look like ?"

          Each unit type class would be measured (internally only) against a benchmark unit. For length it could be the meter, for volume the liter, and for weight the gram, and so on.

          The design goals should be:

          • Zend_Unit is easy to extend
          • Zend_Unit includes Zend_Unit_Length, Zend_Unit_Weight, etc.
          • Users do not have to include string indicators within the number, and do not have to depend on Zend_Locale. The number is an integer or float. The return value is an integer or float. The mechanism for conversion is indicated by parameters.

          Given that, here's how I would implement it:

          Ta-da!

          This is a more object-oriented approach than in the proposal, as well. Here's a complete class skeleton:

          1. Jun 24, 2006

            Yep, sounds very neat and tidy. Add a vote from me.

          2. Jun 24, 2006

            Seperating the Types is a good approach.

            Looks very handy
            I will integrate this with my next reworking.

            BUT - as you wrote with your 3rd design goal

            The Zend_Unit Class was extracted from Zend_Locale, so the basic handling is the internal knowledge of locales and their formatting as these are defined in the LDML.
            My approach was that the user doesn't have to be aware of something. He doesn't have to preformat the input befor he could use it within our class.
            Otherwise there will be some problems. For example:

            • In german the comma seperation is with "," on english with "."
            • Working with circular values the user will have an input like 24°35'24''.

            So I will have to be aware of

            • Different Input depending on locales by using Zend_Locale_Format
            • Format the Input and the Output properly
            • The user could wish to use a different Locale or Formatting for In- and/or Output

            To ignore this and only use real/integer values and ignore locales and formatting would only mean that all work has to be done by the user and the framework will only do the maths ignoring the needs of the users.
            Of course my class could be used without handling from locales and/or formatting by the user and it will then use the default ones.

            Btw:
            Internally I will use some kind of benchmark unit. But maybe these must be handled different (not fixed to one unit).
            Think of length:
            Inputs in "PICOMETER" and Inputs in "LIGHTYEARS". They can not be measured against meters, as the difference is to big !! We would have rounding errors.

            So to summarize for now :

            • I will rework my proposal for use of type-classes (with one abstract one)
            • I don't know if object or static approach is better... (will let this static for now)
            • I will not strip the optional locale and formatting usage
            • Let's discuss after rework

            PS:
            Everyone comments Zend_Unit and Zend_Currency but not one comment about the base class Zend_Locale from which are all classes extracted

            1. Jun 24, 2006

              Of course my class could be used without handling from locales and/or formatting by the user and it will then use the default ones.

              I really think this is the way to go. Programming languages in general expect values in US (British) format, don't they? What happens when you pass 23,3 to is_numeric?

              When creating a class, I look at the core of what the class is supposed to do. Usually, I find that the things I want to do that don't directly contribute instead belong in an independent class. I think this is the case with Zend_Locale. A user should be able to pass in a value and get a value, but the class should have no knowledge of whether they're in the US or Germany, because units are units are units. If their notation is different, then they should preformat in Zend_Locale.

              Otherwise there will be some problems. For example ... Working with circular values the user will have an input like 24°35'24''.

              I think this conversion is outside the scope of this class. Also, it seems to be the only one that requires input like this, and I would really not want to see the class be completely altered for one conversion that is easy to perform manually and requires uncommon characters (the degree symbol). You'll notice that most online forms that do this conversion have three separate fields to avoid use of the degree symbol.

              Inputs in "PICOMETER" and Inputs in "LIGHTYEARS". They can not be measured against meters, as the difference is to big !! We would have rounding errors.

              Did you test it?

              Works for me.

              1. Jun 24, 2006

                bg. Programming languages in general expect values in US (British) format, don't they?

                • But my user inputs 14.411,23 as he is german... bad user
                • But my web-app/class known he is an german user (as the bowser sends his locale) so my class could process the
                  input properly. Good class

                bg. What happens when you pass 23,3 to is_numeric?

                • is_numeric doesn't know that my user is chinese and his input is different than from an english user.
                • is_numeric is not locale-aware... the basic of my class is, that it is locale-aware.
                • Just because the standard-php doesn't support this, doesn't mean that it is not usefull to users of the ZF.

                So you would have to parse the input format it properly so that the function can understand it.

                I would send the input to my function and would get the expected output without parsing or formating
                by my own.

                bg. Did you test it?
                bg. Works for me.

                Then it also works for me and the class will be simpler

                As you may mention all the classes I proposed are locale-aware.
                All these classes are often/normally used in a multi-langual situation.

                That was the reason for my proposal.
                When I would have to strip the usefull features as locale and formatting
                the classes would have no use in my eyes.
                But maybe I'm only to depressed today

                1. Jun 25, 2006

                  Ugh. I replied to this yesterday, but it disappeared--I am 100% positive I hit the Post button and didn't just hit Preview. It was a long post and I'm a little annoyed...

                  Basically, the gist was that I apparently misunderstood your purpose for using the class, and that it could be used for dynamic measurements based on the browser locale. For example, instead of saying "Such and such is 24,000 meters long", if the user was in America it might dynamically say "78,740 feet" instead. In this way, a CMS built on Zend Framework might automatically detect all units in text and automatically convert them into Zend_Unit locale-aware units by providing an additional layer on top.

                  So extending my previous idea:

                  You might also write:

                  Here's a new class skeleton along these lines. Some important functionality has changed, especially return values:

  2. Jun 20, 2006

    A concern I have is the name "Zend_Unit", my first thought went to Unit testing, What is a "Unit"

    Since this properly formats a "Unit" would it not make more sense for it to be part of Zend_Locale?

    Zend_Locale_Unit makes more sense?

    1. Jun 20, 2006

      The first approach was to do a "Zend_Locale_Unit".

      But as the other classes like Zend_Date and Zend_Currency which make extend use of Zend_Locale were all explicit extracted from Zend_Locale, it would make no sense to let the Unit Handling in Locale.

      Zend_Locale should ONLY handle direct Locale and Translation Issues.

      Conversion between Types and Type-Like-Formatting which is done by Zend_Unit has nothing to do with Locale Handling. This was the reason why to do a own Class / Proposal.

      1. Jun 21, 2006

        In the spirit of it-does-what-it-says, why not simply Zend_Unit_Converter or Zend_Convert?

        $unit = Zend_Convert::toUnit('86,7°F',Zend_Unit::FAHRENHEIT);
        echo Zend_Convert::toTemperature($unit,Zend_Unit::CELSIUS);

        1. Jun 21, 2006

          Zend_Converter sounds handy

          But it doesn't only convert, it also handles locale-formatting.

          I would let the desicion for changing the Class name to our Zend_Boys
          I like both.

  3. Jul 17, 2006

    Zend Feedback
    Zend_Unit is conditionally accepted subject to the
    conventions, stipulations, requirements, and changes listed below.

    A ZF default locale object should be used in most places where a
    component or function expects an optional locale object.
    The default locale object should be constructed from an instance of
    Zend_Config shared by all ZF components.

    Parsing, normalization, conversion, and formatting function names
    could benefit from sharing common looking names with other locale-related
    classes.

    Why should rounding be performed during instantiation, instead of formatting?

    If the unit type is specified (e.g. Zend_Unit::KILOGRAM), can Zend_Unit
    do the "right" thing when given a raw number like "1.250" or "12,5"?
    A couple use case would help clarify this.

    A large majority of the world uses Celsius. Should Zend_Unit::CELSIUS
    be required, if the default locale belongs to the majority?

    The name of the class needs clarification, probably by changing it to one of:

    • Zend_UnitConversion
    • Zend_UnitConvert
    • Zend_MeasurementConverter (most accurate, but terrible to type)
    • Zend_Convert
    • Zend_Converter
    • Zend_Measure
    • suggestions?

    Are the Zend_Unit constants, like Zend_Unit::SOME_UNIT_TYPE, absolute,
    precise, unambiguously unique designators of units, or can they represent
    something more abstract? For example, "ton" has several meanings,
    depending on the locale. If the units are unambigous, then what is the reason
    for having both a parameter for Zend_Unit::CONSTANT_UNIT_TYPE and a
    parameter for a locale in the convertTo() method?

    Fundamentally, we have data input, parsing, and normalization in the context
    of a particular locale, followed by formatting for output according to the
    conventions of optionally, a different locale.
    Methods should not combine input parsing, normalization, and output
    formatting all in one step. Instead, parsing and normalization could be
    encapsulated by a constructor. Then a "convertTo($someLocale)" can be
    applied to the instance, possibly more than once, if the same unit must
    be shown in different locales. Thus, __constructor()'s methods return
    normalized instances, not converted instances.

    Instances should be serializable.

    1. Jul 18, 2006

      A ZF default locale object should be used in most places where a
      component or function expects an optional locale object.
      The default locale object should be constructed from an instance of
      Zend_Config shared by all ZF components.

       All parameters witch expect a locale object are optional.
      When the parameter is left empty the function will take the standard locale
      which is set or recognized by ZF automatically.
      Therefor all "locale" parameters are marked OPTIONAL

      Why should rounding be performed during instantiation, instead of formatting?

      There will be no rounding or formatting by instantiation.
      By instantiation the value will be extracted and stored internally in the standard type.
      Formatting will only be done when requesting the output string.

      If the unit type is specified (e.g. Zend_Unit::KILOGRAM), can Zend_Unit
      do the "right" thing when given a raw number like "1.250" or "12,5"?
      A couple use case would help clarify this.

      I added some new use-cases to make this clear.
      Generally an integer, float or string can be given by instantiation
      and the first recognised "value" will be taken but locale-aware.
      That means a english '12.5' is identical with an german '12,5'.

      A large majority of the world uses Celsius. Should Zend_Unit::CELSIUS
      be required, if the default locale belongs to the majority?

      I reworked this.
      You can make Zend_Measure::TEMPERATURE which will take the standard for
      temperature measurements which is Celsius.
      Zend_Measure_Temperature::CELSIUS is identically with Zend_Measure::TEMPERATURE.

      The name of the class needs clarification, probably by changing it to one of:

      • Zend_UnitConversion
      • Zend_UnitConvert
      • Zend_MeasurementConverter (most accurate, but terrible to type)
      • Zend_Convert
      • Zend_Converter
      • Zend_Measure
      • suggestions?

      In my eyes Zend_Measure makes all clear. Handling of all measurement related things as
      getting value out of string, conversion, adding and so on.

      Are the Zend_Unit constants, like Zend_Unit::SOME_UNIT_TYPE, absolute,
      precise, unambiguously unique designators of units, or can they represent
      something more abstract? For example, "ton" has several meanings,
      depending on the locale. If the units are unambigous, then what is the reason
      for having both a parameter for Zend_Unit::CONSTANT_UNIT_TYPE and a
      parameter for a locale in the convertTo() method?

      The constants are absolute and precise units.
      There are no double units.
      When there are locale dependent units as feet the class will have
      FEET, FEET_FRENCH, FEET_EGYPT, FEET_IRAQ
      as all feet's are calculated different. But there will always an international standard FEET unit.

      To mention:
      Zend_Measure can only recognize input values.
      It does not know "Celsius" and maybe "Celsian".
      It only knows "1234" as this is locale independant.
      When given "1234 Celsius" only 1234 would be recognised,
      so you have to say which expected type you are instantiating.

      The type parameter in the convertTo method can be used to change the
      unit type, so instead of METER we can produce LIGHT_SECOND output
      of the object value. So 100,000,000 meter would become 1 light second.

      Otherwise the locale parameter is used to know how a string has to be parsed.
      Grouping in english ','  in german '.'
      Decimalpoint in english '.'    in german ','
      Also different number formats can be defined in the locale.
      For example there are slangs which group by 5 instead of 3 as it will be done in english.
      So 14,000,000 would become 140,00000.

      Fundamentally, we have data input, parsing, and normalization in the context
      of a particular locale, followed by formatting for output according to the
      conventions of optionally, a different locale.
      Methods should not combine input parsing, normalization, and output
      formatting all in one step. Instead, parsing and normalization could be
      encapsulated by a constructor. Then a "convertTo($someLocale)" can be
      applied to the instance, possibly more than once, if the same unit must
      be shown in different locales. Thus, __constructor()'s methods return
      normalized instances, not converted instances.

      Extraction of the right value and normalization to the standardunit will be done in an extra function "extractValue".
      As normalization is different for different types the encapsulating would be a problem, as each type must be normalized in an other way.
      Length "12,345678m"    Circular "12'34"5678°"    Currency "$ 12.345,678"

      Internally the values are always stored in standard unit.

      Instances should be serializable.

      I added this in my proposal.


      I hope I did'nt forgot something, but I think all questions should be answered.

    2. Jul 18, 2006

      1 thing i forgot to mention:

      The method "extractValue" makes use of Zend_Locale_Format->getNumber
      to extract the proper value.

      But special values as circular must be parsed locally.
      Currency on the other hand can be pased using Zend_Locale_Format->getNumber
      as there is no difference in the parsing algorithmus.

      Hope this clears the point of confusion

      1. Jul 19, 2006

        Looks good. Yes, I see that convertTo() must take 3 parameters, so that the number can be parsed correctly, and the unit of measurement must be specified independently of the data and number format (locale).

  4. Aug 08, 2006

    Hi all,

    I had some questions over Zend_Measure I thought to share:

    Zend_Measure_Capacity - Is "capacity" really a de facto synonym of
    "capacitance?" Despite the answer, I would prefer the name
    Zend_Measure_Capacitance, so as to avoid ambiguity - when I first read
    "Capacity," I assumed it was for volume, until I saw Zend_Measure_Volume.

    Zend_Measure_Flow_* - has a class for Mass; will Mass be included in
    Zend_Measure (as well as Zend_Measure_Flow)? I didn't see one in the
    proposal...

    Zend_Measure_(Illumination|Lightness) - I don't recall from physics what
    the difference here might be. I did notice that several of the units
    seem to be very similar across the two classes (e.g.,
    Lightness::CANDELA_PER_SQUARE_METER and
    Illumination::LUMEN_PER_SQUARE_METER).

    Best regards,
    Darby

    1. Aug 08, 2006

      Zend_Measure_Capacity - Is "capacity" really a de facto synonym of
      "capacitance?" Despite the answer, I would prefer the name
      Zend_Measure_Capacitance, so as to avoid ambiguity - when I first read
      "Capacity," I assumed it was for volume, until I saw Zend_Measure_Volume.

      Checked
      I will change this soon

      Zend_Measure_Flow_* - has a class for Mass; will Mass be included in
      Zend_Measure (as well as Zend_Measure_Flow)? I didn't see one in the
      proposal...

      Fluidicy is measured in
      Flow of mass (f.e. flowing of sand / non fluid)
      Flow of mole (includes density of flowing material)
      Flow of Volume (f.e. flowing of water / fluids)

      As mass and volume could be seen as weight and volume I had to make a own subclass to make things clear.

      Zend_Measure_(Illumination|Lightness) - I don't recall from physics what
      the difference here might be. I did notice that several of the units
      seem to be very similar across the two classes (e.g.,
      Lightness::CANDELA_PER_SQUARE_METER and
      Illumination::LUMEN_PER_SQUARE_METER).

      I didn't see any relationship in this 2 classes.
      Lightness - f.e. Candela/m² measures the density of light.
      Illumination - f.e. Lumen/m² measures the energy of light

      Otherwise described:

      Lightness is measured in Lightpower per Square meter... how much power of light per room-angle is found.
      Illumination is measured in Lightenergy per Square meter... how much energy of light per room-angle is found.
      Only because both units are measured per room-angle doesn't mean that they can be converted into each other without the knowledge of a third parameter.

      To get Lumen from Candela (Lightness to Illumination) you would have to multiply candela with
      sterad (which is area in m² per square angle in m²).

      1. Aug 09, 2006

        Thanks for the clarifications, Thomas!

  5. Sep 09, 2006

    Great work on putting everything together and getting it approved, Thomas!

    One comment: Zend_Measure::sub() should just be written out as Zend_Measure::subtract(). "Sub" is probably needlessly short and obfuscated.

    -Matt

    1. Sep 09, 2006

      Thanks

      Regarding to sub():
      In all locale aware classes the mathematical functions are abbreviated to 3 letters:

      add() for addition and sub() for substraction.

      Changing sub to substract would mean

      • more typing for user
      • change all other locale related classes...

      Speaking for the Zend_Date class I'm actual coding that all subxxx functions will change. And there are much of them

      subDay would become substractDay
      subYear would become substractYear

      and so on...
      I think for "add" and "sub" the abbreviation can be used as the meaning is clear.

      Greetings
      Thomas

  6. Jan 25, 2007

    Zend Feedback
    Hi Thomas,

    This change has been approved. The complications are resolved,
    if users simply use the needed Measure class directly.

    We will need to update the proposal and documentation.

    Cheers,
    Gavin

    > -------- Original Message --------
    > Subject: Re: [fw-i18n] RE: [fw-general] Zend_Measure constants wrong?
    > Date: Wed, 24 Jan 2007 00:15:00 +0100
    > From: Thomas Weidner <thomas.weidner@gmx.at>
    >
    > Hy Andi and Team,
    >
    > .
    > .
    >
    > We could also negate the base class...
    > So a user would have to use Zend_Measure_Length direct instead of Zend_Measure...
    > No base class, no problems
    > Would be a good solution in my eyes... it's only the question if this is coding standard conform...
    >
    >
    > .
    > .