skip to Main Content

I have this code to process a config file:

<?php
$config = '[log]
log_writers[] = "file"
log_writers[] = "screen"

[General]
maintenance_mode = 0
enable_browser_archiving_triggering = 0
enable_sql_optimize_queries = 0
force_ssl = 1';

echo preg_match_all( '/^maintenance_mode[ t]*=[ t]*d$/m', $config );

The echo displays 0

https://onlinephp.io/c/51407

Updating the regex to:

echo preg_match_all( '/^maintenance_mode[ t]*=[ t]*ds$/m', $config );

results in the expected 1

WHY??


I even verified my sanity in regex101

https://regex101.com/r/CIxCkN/1


Local test environments:

RHEL 7
PHP 5.6.25
PCRE v8.32 2012-11-30

and

Windows Server 2022
PHP 8.2.7
PCRE v10.40 2022-04-14


Per comment request:

var_dump(base64_encode($config));

string(240) "W2xvZ10NCmxvZ193cml0ZXJzW10gPSAiZmlsZSINCmxvZ193cml0ZXJzW10gPSAic2NyZWVuIg0KDQpbR2VuZXJhbF0NCm1haW50ZW5hbmNlX21vZGUgPSAwDQplbmFibGVfYnJvd3Nlcl9hcmNoaXZpbmdfdHJpZ2dlcmluZyA9IDANCmVuYWJsZV9zcWxfb3B0aW1pemVfcXVlcmllcyA9IDANCmZvcmNlX3NzbCA9IDE="

var_dump(bin2hex($config));

string(358) "5b6c6f675d0d0a6c6f675f777269746572735b5d203d202266696c65220d0a6c6f675f777269746572735b5d203d202273637265656e220d0a0d0a5b47656e6572616c5d0d0a6d61696e74656e616e63655f6d6f6465203d20300d0a656e61626c655f62726f777365725f617263686976696e675f74726967676572696e67203d20300d0a656e61626c655f73716c5f6f7074696d697a655f71756572696573203d20300d0a666f7263655f73736c203d2031"

2

Answers


  1. One answer would be that your string (or script generally) has Windows line-endings.

    In multi-line mode, d$ will only match a digit followed by an immediate newline (as determined by PCRE’s compile-time setting), which might not work if there was a r hiding in there.

    Adding s at the end of your regex would match all line-ending characters, which explains why that helps in your affected test environments.

    For a fix (other than the s addition you’ve already found), PCRE lets you adjust which characters are matched as a newline using a modifier at the start of the string, e.g. (*ANYCRLF):

    // Force Windows line-ending
    <?php
    $test = "foornbar";
    
    var_dump(preg_match_all('/^foo$/m', $test));
    var_dump(preg_match_all('/(*ANYCRLF)^foo$/m', $test));
    

    int(0)
    int(1)

    See https://3v4l.org/vOUgM for a demo, and the Newline Conventions section of the PCRE docs for some detail.

    Or alternatively, just use the newline character(s) in your string that PCRE is expecting locally.


    And more generally, if you’re actually trying to parse the string/file in your question then a combination of array_key_exists and parse_ini_string/parse_ini_file will make everything a lot cleaner.

    Login or Signup to reply.
  2. your config file does indeed have windows-newlines rn , the first part of your bin2hex 5b6c6f675d0d0a translates to [log]rn which means @iainn’s hunch is correct 🙂

    Still though, i would have written that regex as

    ‘/^maintenance_modes*=s*(d)s*$/m’

    it’s just more robust that way, then doesn’t matter if you write it as

    maintenance_mode=5

    or

    maintenance_mode =5

    or

    maintenance_mode= 5

    or

    maintenance_mode = 5

    , and doesn’t matter if you use spaces or tabs, and doesn’t matter what your line endings are.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search