function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
anthony_idanthony_id 

FromCharArray returns different results than PHP chr() - How do I get them to match?

I've been racking my brain on this one and I've not been able to figure out why php Chr() returns a different result than fromCharArray for very large integers.  I need to replicate some code to generate a token for a third party integration and have been wildly unsuccessful.

PHP Code snippet:
$nExpires = 1370359009; //this number is generated via epoch time + some additional values
$sExpByte1 = chr($nExpires & 0xff);

Apex Code
Integer TokenExpires = 1370359009; 
String expByte1 = String.fromCharArray(new integer[] {TokenExpires});

If I echo $sExpByte (or convert using Ord() or base64Encode()) I get a different result than when I output expByte1 in a test method using a string representation in assertEquals()

 

What do I need to do to get the Apex to match the PHP?

 

Thanks!

 

 

Best Answer chosen by Admin (Salesforce Developers) 
sfdcfoxsfdcfox

Masking in Apex Code is identical to Java, with a single caveat: Apex Code doesn't support octal, hexadecimal, or binary notations in source code. So, you can do this:

 

Integer tokenExpires = 1370359009;
String expByte1 = String.fromCharArray(new Integer[] { tokenExpires & 255 } );

Oh, and it may not do any good, but take a look at the Crypto class in the documentation. It has methods for creating md5, sha1, and other types of hashes for signing and so on.

All Answers

sfdcfoxsfdcfox

I don't believe you can, at least not directly through String.fromCharArray. Oh, and your example Apex Code forgot to mask by 0xFF (so I did this in my test code to verify the results); the resulting number from the bitmask is 225 in your example code. In PHP, chr(225) results in ß, while in Apex Code, String.fromCharArray(new Integer[] { 225 }) results in á.

 

The problem is you're asking for a UTF8 string to match an ANSI/ASCII string. At 128, UTF-8 deviates from ANSI. This was an intentional design feature of UTF8. Presumably, the characters that failed to display in the second table have no corresponding character at those code points; those characters were moved to other code points.

 

I wrote a quick output program for ASCII 128-255 in PHP:

 

   + 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
128 :Ç   ü   é   â   ä   à   å   ç   ê   ë   è   ï   î   ì   Ä   Å
144 :É   æ   Æ   ô   ö   ò   û   ù   ÿ   Ö   Ü   ¢   £   ¥   ₧   ƒ
160 :á   í   ó   ú   ñ   Ñ   ª   º   ¿   ⌐   ¬   ½   ¼   ¡   «   »
176 :░   ▒   ▓   │   ┤   ╡   ╢   ╖   ╕   ╣   ║   ╗   ╝   ╜   ╛   ┐
192 :└   ┴   ┬   ├   ─   ┼   ╞   ╟   ╚   ╔   ╩   ╦   ╠   ═   ╬   ╧
208 :╨   ╤   ╥   ╙   ╘   ╒   ╓   ╫   ╪   ┘   ┌   █   ▄   ▌   ▐   ▀
224 :α   ß   Γ   π   Σ   σ   µ   τ   Φ   Θ   Ω   δ   ∞   φ   ε   ∩
240 :≡   ±   ≥   ≤   ⌠   ⌡   ÷   ≈   °   ∙   ·   √   ⁿ   ²   ■    

And I wrote the same program for UTF8 in Apex Code:

 

    +0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
128 :€ � ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ � Ž � 144 :� ‘ ’ “ ” • – — ˜ ™ š › œ � ž Ÿ 160 :  ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ® ¯ 176 :° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ 192 :À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï 208 :Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß 224 :à á â ã ä å æ ç è é ê ë ì í î ï 240 :ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ

So, a direct translation fails; the same code points in ASCII are not all used in UTF-8. Unfortunately, String.fromCharArray requires a UTF8 code point array to work correctly, so a direct injection method is doomed to fail. You would instead need to use a conversion method.

 

Unfortunately, I don't know exactly what you're doing to get data from your PHP to Apex Code, so I can't really give you an exact method. After all, once the data becomes a string in Salesforce.com, it's already been converted to UTF8, which means it's too late to do anything about the problem. I suspect you'd have to retrieve the output from the script in Base-64 encoded form, then decode the Base64 string manually in Apex Code, and finally convert the string using an integer=>string array.

anthony_idanthony_id

This is very helpful and as complex as I worried it would be.  The PHP code creates a string and then MD5 hash (I have a Java example as well, but it's similar - create a byte array > loop through turn into a string > concatenate some strings > MD5 hash the string)

 

In java it's

byte expBytes[] = new byte[4];

expBytes[0] = (byte) (expires & 0xff);

...(fill in additional items in array)

 

The first step was getting that initial character string based on the input integer and it looks like I'll need some  sort of mapping to get to the end state.  I'm not trying to get data from the PHP to the Apex, just trying to do the same thing in Apex and get the same result.  I'm just having trouble with the Apex translation.

 

I have a question that may help me get a bit farther down the path, though:

 

How did you mask by 0xFF in Apex? I couldn't find syntax anywhere and nothing I tried worked.  That may be all I need and I can skip the chr() translation altogether and come at it from a slightly different angle.

 

To be honest, this is an area of coding I haven't had to do much and so I'm struggling a bit.

 

sfdcfoxsfdcfox

Masking in Apex Code is identical to Java, with a single caveat: Apex Code doesn't support octal, hexadecimal, or binary notations in source code. So, you can do this:

 

Integer tokenExpires = 1370359009;
String expByte1 = String.fromCharArray(new Integer[] { tokenExpires & 255 } );

Oh, and it may not do any good, but take a look at the Crypto class in the documentation. It has methods for creating md5, sha1, and other types of hashes for signing and so on.

This was selected as the best answer
anthony_idanthony_id

Aha! It was the notation that was hanging me up (at least for that part - octal notation) I may not be able to do the character translation with UTF-8 at this point, but I'm at least dealing with the right integer now, which gets me pretty far along.

 

The crypto classes work as expected and I'm hoping between that and the encodingUtil I'm able to get where I need to go.  If I end up getting a full solution, I'll post it.

 

Thanks!