Unexpected behavior of the "Money" field in PostgreSQL - php

I started developing a web sistem using Linux Ubuntu and at some point I had to do the following with the data type "money":
explode(" ", "R$ 3,000.00"); // [0] => "R$" and [1] => "3,000.00"
However when I installed the software in Windows I realized that the data is saved without space, that is, "R$3,000.00". Soon, the code snippet fails to function properly.
Note: 1 could "fix" this using:
preg_replace("/[R$]+/", "$0 $1", "R$3,000.00"); // "R$ 3,000.00"
But certainly not a better way.
Note 2: The version of PostgreSQL used is 9.5
Would anyone have any suggestions for resolving this?
Thank you very much.

The issue you are having is that the lc_monetary locale does not have the same value on both computers. This is what you have an "Unexpected behavior" on two different operating systems.
You can change the lc_monetary locale with:
set lc_monetary to 'SOME_LOCALE';
Then test it with:
test=# SELECT 34.888::money;
money
--------
$34.89
(1 row)
Read more at https://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-LC-MONETARY
If your application is using different OS, it is wise to set the locale correctly at the beginning of the connection or in the configuration.
On Mac/Linux you can see available locales with locale -a. I an not sure for Windows.
If you don't generally use the currency symbol you should definitively consider to store the number as a decimal instead.

Related

[Docker][PHP] NumberFormatter::formatCurrency incompatible between 7.4.29 and 7.4.28

(Same as https://github.com/docker-library/php/issues/1301 )
The results of running the following script were different between php:7.4.29-fpm-alpine and php:7.4.28-fpm-alpine.
<?php
$fmt = new \NumberFormatter('JA_JP', \NumberFormatter::CURRENCY);
$formatString = $fmt->formatCurrency(0, 'JPY');
var_dump($formatString);
Result
php:7.4.28-fpm-alpine (I expects)
string(4) "¥0"
php:7.4.29-fpm-alpine
string(3) "¥0"
(My) problem
Is there any way to get the results in php:7.4.29-fpm-alpine to be what they were in php:7.4.28-fpm-alpine ? (Is there a workaround?)
Way to reproduce
see https://github.com/sogaoh/reproduce-incompatibility-of-format-currency (README.md)
Remarks
( unconfirmed ) I'm guessing that there is a similar problem between
8.0.19 and 8.0.18
8.1.6 and 8.1.5
The difference between the two is that the .28 version uses \uffe5 'Fullwidth Yen Sign, and the .29 version uses \u00a5 'Yen Sign.
As noted in the github issue comment, it's likely a change in the underlying ICU library between builds of the image, as opposed to an issue in the image, Alpine, or PHP themselves. As an interim fix you could use a modified image that installs a pinned version of ICU, but YMMV.
Alternatively you could shim in a simple replacement like:
str_replace("\xc2\xa5", "\xef\xbf\xa5", $formatter_output);
Though the issue itself is fairly cosmetic, and IMO the original use of a fullwidth glyph for the currency symbol was the more questionable option.
My solution:
Install package: icu-data-full additionally.
refs
https://github.com/sogaoh/reproduce-incompatibility-of-format-currency/pull/2/files
https://github.com/docker-library/php/issues/1301#issuecomment-1139142261

ldapmodify: invalid format (line 5) entry: " ... " on LDIF (passed from PHP)

I'm attempting to write a bit of code that will allow users to change their expired Active Directory passwords via a PHP web interface. Due to limitations with PHP's ldap library's*, it seems the only way to do this is by generating an ldif and then passing this directly to ldapmodify.
The code I've come up with (minus the vars) is:
ldapmodify -H {$ad_server} -D '{$dn}' -w {$old} <<!
dn: {$dn}
changetype: modify
delete: unicodePwd
unicodePwd:: {$oldPassword}
-
add: unicodePwd
unicodePwd:: {$newPassword}
-
!
The code appears to work fine when I paste the generated code straight in to my console, but so far I've had no luck running it from PHP.
I originally tried passing the code to exec, only to get exitcode 247(which doesn't appear to be a real thing)
I then attempted to use proc_open instead, which provided the current error
ldapmodify: invalid format (line 5) entry: " ... "
So far as I can see the only thing on line 5 is a "-". So I'm a bit stuck as to what could be wrong.
P.S. I also read this post LDIF file error?? Invalid Format? which reported a similar problem, although assuming the encoding of the "-" character is the issue, I'm not sure what I can really do with it from with PHP (mb_string_encoding the whole string into utf-8 doesn't appear to have any effect)
This is also running on a solaris machine which may also be a factor.
*PHP is unable to perform two actions within a single command, somthing that is required in order to do a user password change in AD. (so far as I'm aware)
Edit: No sure why this is getting downvotes, but I'd be happy to be told I'm an idiot if I'm doing something patently stupid without noticing (so long as you point out what that is, as I've been stuck on this for a while now)
Thanks to some help from the #ldap channel on freenode it turns out I am indeed an idiot (especially considering that I've been poking and prodding this for most of the day).
It seems ldapmodify does not like it when an LDIF contains a windows new line characters after the "-" Switching line endings from windows to unix in sublime has fixed the problem for me*.

wide strings in php?

Originally, I had the problem that, although I had the same path by optical inspection, file_exists() returned true for one and false for the other. After spending hours narrowing down my problem, I wound up with the following code... (paths redacted)
$myCorePath = $modx->getOption('my.core_path', null, $modx->getOption('core_path').'components/my/');
$pkg1 = $myCorePath.'model/';
$pkg2 = MODX_CORE_PATH . 'components/my/model/';
$pkg3 = '/path/to/modx/core/components/my/model/';
var_dump($pkg1, $pkg2, $pkg3);
...and its output:
string '/path/to/modx/core/components/my/model/' (length=37)
string '/path/to/modx/core/components/my/model/' (length=78)
string '/path/to/modx/core/components/my/model/' (length=78)
So two versions, interestingly including simply writing the string down, apparently use wide characters (these worked, file_exists()-wise), while sadly my preferred variant uses narrow characters. I tried to research this but the only thing I wound up with told me that php has no such thing as wide strings. I also verified with a hex editor that all string constants really only take one byte per character in the php file.
phpinfo() tells me I have PHP Version 5.4.9, and I run on a 64 bit linux machine, fwiw. The manual was edited a week ago; is its info not accurate, or what's going on here?
I think it is caused by multibyte coding.

GBP £ symbol in ASCII php file being converted to £ on live server (transferring with git)

I have a piece of PHP code, which was written in notepad++ on a Windows 7 machine
The Encoding in notepad++ is set to "Encode to ANSI" (ASCII)
I am them doing this in my code:
utf8_encode("£")
so I am sure to get the utf friendly version of the £ symbol.
All works perfectly fine on the local server.
But when I push it up to my live server I'm getting all sorts of issues with utf8 encoding errors in php.
Is something in the git push/pull process corrupting this, or is it perhaps a locale setting on the live server?
Both local and live servers run ubuntu 12.04
Thanks
Update 1
The actual error I'm getting is
invalid byte sequence for encoding "UTF8": 0xa3'
(This is a Postgres SQL error)
Other difference in local and live is live is over https and local is just http (both apache)
Update 2
Running:
file -bi script.php
on both local and live produces:
text/x-php; charset=iso-8859-1
So it seems as if the encoding of the file is intact?
Update 3
Looking at the local Postgres installation it has the following settings:
ENCODING = 'UTF8'
LC_COLLATE = 'en_GB.UTF-8'
LC_CTYPE = 'en_GB.UTF-8'
Whereas live has:
ENCODING = 'UTF8'
LC_COLLATE = 'en_US.UTF-8'
LC_CTYPE = 'en_US.UTF-8'
I'm going to see if I can swap the collate types to match local and see if that helps
Update 4
I'm doing this, which is the ultimately resulting in the failing piece of code on live (not local)
setlocale(LC_MONETARY, 'en_GB');
$equivFinal = utf8_encode("£") . money_format('%.2n', $equivFinal);
Update 5
I'm getting closer to the issue.
On local the string is produced as
£1.00
On live the string is produced as
£�1.00
So for some reason the live server is adding more crap in when doing the UTF8 conversion
Update 6
Ok so I've pinned it down to this:
setlocale(LC_MONETARY, 'en_GB');
Logger::getInstance(__NAMESPACE__)->info("TEST 01= " .money_format('%.2n', 1.00));
On local it outputs
TEST 01= 1.00
As expected
on live it output
TEST 01= �1.00
With the random characters added to the start, which is what is causing my utf8 issue as it's croaking on that.
Any idea why money_format would do that on one server and not another?
finally nailed it
it's money_format
if you dont specifiy a locale or specify it incorrectly then it just does its own thing
so i was doing
setlocale(LC_MONETARY, 'en_GB');
and on local that meant money_format just ignored the £ from the start of the output
but on live it meant that money_format put the unicode WTF character.
doing it properly for ubuntu of
setlocale(LC_MONETARY, 'en_GB.UTF-8');
means money_format comes out with £ at the front and therefore i dont need my utf8 rubbish
Update 1
Better still, don't bother with setlocale and I'm just going to do this:
utf8_encode("£") . money_format('%!.2n', $equivFinal);
Which basically formats the money and excludes the symbol prefix
and then better still just use number_format and do
utf8_encode("£") . number_format($equivFinal, 2);
I've learnt something new :)
The issue is that you can't save raw GBP symbol inside ASCII file.
Never use weird characters in your source code because no matter how much they "should" work you always run into problems like this. (You can come up with your own definition of "weird" but mine is anything you can't type in on a us-english keyboard without resorting to alt-codes.)
To get arround this restriction concatinate in the results of the chr() function. (use the following code snipit to find out the parameter you need to pass chr is 163 in this case.)
<?php echo(ord('£')); ?>
so in your case the line would read:
$equivFinal = chr(163) . money_format('%.2n', $equivFinal);

Which PHP version is required for str_split?

I relogin to my server in dreamhost and test some scripts.And I found I couldn't use str_split. Message of Undefined function was given.I checked the version in the server and its PHP Version is 5.2.12.And I just wonder which version is required?Thanks.
Testcode:
<?php
$arr = str_split("lsdjflsdjflsdjflsdjfl");
print_r($arr);
?>
Message:
Fatal error: Call to undefined function: str_split() in /test.php on line 3
Edit #Justin Johnson
I checked the server's system directory,and I found there are two versions of PHP in Dreamhost.In user's webroot,file will be parsed by PHP5 and that's why I got php 5.2.12 by putting a phpinfo.php in the webroot.And if php files are ran in command line directly using php test.php,another php version which is 4.x worked.That's the reason I got an error.When I use
/usr/local/php5/bin/php test.php
Everything is fine.
Rather than use str_split, it's usually much easier to iterate through the characters of the string directly:
$s="abc";
$i=0;
while(isset($s[$i])) {
echo $s[$i++]." ";
}
see?
First off: The PHP documentation will always say what version is required for every function on that function's documentation page directly under the function name.
It is possible that an .htaccess file is somewhere in your path and is causing a previous version (<5) of PHP to be used. To double (or triple) check to make sure that you are running in the proper PHP version, place this code above the line where you call str_split
echo "version:", phpversion(),
"<br/>\nstr_split exists? ",
function_exists("str_split") ? "true" : "false";
However, as shown by Col. Shrapnel, it is not necessary to convert a string to an array of individual characters in order to iterate over the characters of that string. Strings can also be iterated over using traditional iteration methods, thus making the call to str_split unnecessary and wasteful (unless you need to segment the string into fixed length chunks, e.g.: str_split($s, 3))
foreach ( str_split($s) as $c ) {
// do something with character $c
}
can be replaced by
$s = "lsdjflsdjflsdjflsdjfl";
for ( $i=0; isset($s[$i]); ++$i ) {
// do something with character $s[$i]." ";
}
which is equally, if not more clear.
According to dreamhost wiki, you need to switch to php5 manually from control panel, if you created your domain before 2008 sept.
http://wiki.dreamhost.com/Installing_PHP5#Using_DreamHost.27s_PHP_5
PHP 5 was added to all plans by
DreamHost as of June 2005. As of
September 2008, support for PHP4 was
discontinued, so you can no longer
switch back to PHP 4 from PHP 5 from
the panel.
If you haven't switched to PHP 5 yet,
you can do this in the Control Panel.
But, again, you will not be able to
switch back to PHP 4 after switching
to PHP 5.
Here's how to switch from PHP 4 to PHP
5:
Log into the DreamHost Control Panel.
Click Domains, then Manage Domains.
Click the wrench icon next to the domain you want to activate PHP 5
on (under the Web Hosting column).
Select PHP 5.x.x from the dropdown menu.
Click Change fully hosted settings now! at the bottom of the
section.
Repeat steps 3-5 for each additional domain you want to
activate.
you could also check your php version with
<?php
phpinfo();
?>
The version required is PHP 5 or later. So theoretically your program should work.
If you can't get str_split to work, just use a string as an array:
$stuff = "abcdefghijkl";
echo $stuff[3];
will produce
d
This method is fastest, anyway. I don't know if it suits your needs, but if it does, I hope it helps!
Could be anything in your code. How do we know its not a 10 line script or 2000 line script?
You can use preg_split() to split an array into single characters, but it will return an extra empty string at the begining and the end.
$a = preg_split("//","abcdefg");
echo json_encode($a);
prints:
["","a","b","c","d","e","f","g",""]

Categories