How to validate that a string only contain lowercase letters?

Note that the $ anchor also matches before the line break char sequence that is at the very end of the string (yes, even if m MULTILINE flag is not specified). See these tests, all returning a match:

if (preg_match("#^[a-z]*$#", "ok")) echo "OK!\n"; // => is OK!
if (preg_match("#^[a-z]*$#", "ok\n")) echo "OK!\n"; // => is OK!
if (preg_match("#(*ANY)^[a-z]*$#", "ok\r\n")) echo "OK!"; // => is also OK!

You may "fix" this $ behavior by using the D modifier. The PCRE-compliant PCRE_DOLLAR_ENDONLY modifier makes the $ anchor match at the very end of the string (excluding the position before the final newline in the string).

/^\d+$/D

is equal to

/^\d+\z/

and matches a whole string that consists of 1 or more digits and will not match "123\n", but will match "123".

Alternatively, to anchor the string match at the very start and end of the string you might also consider using \A and \z anchors.

preg_match('~\A[a-z]*\z~', "333k");
             ^^      ^^

The \A and \z are unambiguous start and end of string anchors in PHP regex (as their behavior does not depend on any regex modifiers, even if you specify the m MULTILINE modifier, they will keep on asserting string start/end positions). See more on PHP regex anchors in PHP documentation.

Beware of \Z though: \Z will match the whole string before the last single linebreak, whereas \z will only match at the very end of the string, that is, after all the characters there are in the string (see Whats the difference between \z and \Z in a regular expression and when and how do I use it?)


You use the start and end markers, ^ and $ respectively, to indicate beginning and end of the string in your regular expression pattern. That way you can make the expression match only the whole string, not any kind of substring. In your case it would then look like this:

preg_match("#^[a-z]*$#", "333k");

You can also, with one these markers, specify that the pattern must only match the beginning or the end of the string.


I super-love regex, but not for this task -- it is needless overhead because there is a single, direct, non-regex equivalent. https://www.php.net/manual/en/function.ctype-lower.php

Code: (Demo)

$tests = [
    'Test',
    'test',
    'test1',
    '1test',
    'test!'
];
foreach ($tests as $test) {
    echo "$test: " , (ctype_lower($test) ? 'true' : 'false') , "\n";
}

Output:

Test: false
test: true
test1: false
1test: false
test!: false

p.s. For the record, an empty string returns false. I'm not sure of your logical validation requirements for this test unit.