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
- Zend_Exception
- Zend_Locale_Data
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
29 Comments
comments.show.hideJun 20, 2006
Christopher Thompson
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.
Jun 20, 2006
Thomas Weidner
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.
Jun 20, 2006
Matthew Turland
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.
Jun 20, 2006
Thomas Weidner
I like the staticification of Zend_Unit and will change this with next wiki-update.
Jun 20, 2006
Christopher Thompson
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
...
Jun 20, 2006
Thomas Weidner
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 :
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.
Jun 20, 2006
Christopher Thompson
"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.
Jun 22, 2006
Matthew Ratzloff
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.
Jun 22, 2006
Thomas Weidner
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 ?"
Jun 23, 2006
Matthew Ratzloff
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:
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:
Jun 24, 2006
Simon Mundy
Yep, sounds very neat and tidy. Add a vote from me.
Jun 24, 2006
Thomas Weidner
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:
So I will have to be aware of
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 :
PS:
Everyone comments Zend_Unit and Zend_Currency but not one comment about the base class Zend_Locale from which are all classes extracted
Jun 24, 2006
Matthew Ratzloff
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.
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.
Did you test it?
Works for me.
Jun 24, 2006
Thomas Weidner
bg. Programming languages in general expect values in US (British) format, don't they?
input properly. Good class
bg. What happens when you pass 23,3 to is_numeric?
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
Jun 25, 2006
Matthew Ratzloff
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:
Jun 20, 2006
Richard Thomas
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?
Jun 20, 2006
Thomas Weidner
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.
Jun 21, 2006
Simon Mundy
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);
Jun 21, 2006
Thomas Weidner
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.
Jul 17, 2006
Gavin
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:
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.
Jul 18, 2006
Thomas Weidner
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
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.
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'.
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.
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.
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.
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.
I added this in my proposal.
I hope I did'nt forgot something, but I think all questions should be answered.
Jul 18, 2006
Thomas Weidner
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
Jul 19, 2006
Gavin
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).
Aug 08, 2006
Darby Felton
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
Aug 08, 2006
Thomas Weidner
Checked
I will change this soon
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.
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²).
Aug 09, 2006
Darby Felton
Thanks for the clarifications, Thomas!
Sep 09, 2006
Matthew Ratzloff
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
Sep 09, 2006
Thomas Weidner
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
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
Jan 25, 2007
Gavin
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...
>
>
> .
> .