bhttoan
bhttoan

Reputation: 2736

PHP regex match valid salary including symbol(s)

I am trying to validate an input to match a salary or a salary range - the issue is that the user can enter a number of possible values.

For example, below is valid:

2000
2,000
2000.00
2,000.00

2000 - 4000
2,000-4,000
2,000.00-4,000.00
$2,000.00-$4,000.00
£2,000.00-£4,000.00

Initially we set out to allow everything except for letters but that would allow something like 2!000 which would not be valid

At the moment we have gotten:

if (preg_match('/^\d|\d,\d+(\.(\d{2}))?$/', $i)) {
   return true;
}

That does work for the first 4 examples but it allows things like 2a000 which is not valid - is there a way to list everything the string cannot contain (letters and symbols for example except for currency, period and comma) but still then do a pattern match so we can validate numbers for example?

Upvotes: 3

Views: 586

Answers (3)

George Pavelka
George Pavelka

Reputation: 2329

This one is quite strict so that all your entries from second group pass and none of the first group.

^[$£]?\d,?000(\.00)?([ -]+[$£]?\d,?000(\.00)?)?$

You can do something much more benevolent too. Such as replace the [$£] by \D to allow arbitrary currencies etc.

Check it out

Upvotes: -1

Shaunak Sontakke
Shaunak Sontakke

Reputation: 1270

If you get a hyphen -, it indicates that you have range. To keep it simple I would explode() on hyphen and validate each part in a loop with $numberFormatter = new NumberFormatter( 'en_US', NumberFormatter::CURRENCY ); var_dump( $numberFormatter->formatCurrency(1234.56789, "USD" ) );

If the formatter is able to return a string, it’s an indication that input is valid.

This would work even if salary is submitted instead of range.

Upvotes: 0

The fourth bird
The fourth bird

Reputation: 163632

You might capture $ or £ in a group and then for consistency use a backreference to match the same symbol in the second part (which is optional).

To shorten the pattern a bit, you could use a subroutine (?2) to repeat matching the digits in capture group 2.

^([$£]?)((?:\d{1,3}(?:,\d{3})*|\d+)\b(?:\.\d{2})?)(?:\h*-\h*\1(?2))?$

In parts

  • ^ Start of string
  • ( Capture group 1
    • [$£]? Optionally match $ or £
  • ) Close group
  • ( Capture group 2
    • (?:\d{1,3}(?:,\d{3})* Match 1-3 digits and repeat 0+ times matching , and 3 digits
    • | Or
    • \d+ Match 1+ digits
    • \b Word boundary
    • (?:\.\d{2})? Optionally match a dot and 2 digits
  • ) Close group
  • (?: Non capturing group
    • \h*-\h*\1 Match - between 0+ horizontal whitespace chars, followed by a backreference to what is captured in group 1 (optional $ or £)
    • (?2) Repeat the pattern in group 2 to match the digits
  • )? Close group and make it optional
  • $ End of string

Regex demo

Upvotes: 4

Related Questions