3

I've been struggling with a mystery crash most of today which I have finally resolved but don't really understand my own fix.

The purpose of the code is to replace placeholders in a template.

Below is the final minimum PHP code I need to reproduce the problem.

$path = realpath("path_to_template.html");
$content = file_get_contents($path);
$loop_regex = '/{{\*(\w+)}}((?:.|\n)+?){{\/\w+}}/s';
$data = array(
    "Trainees"=> array(
        array(
             "display_name" => "Joe",
             "status" => "Pending",
             "invited_date" => "01 Sep 2018",
             "percentage" => "80%"
       )
    )
);

preg_match_all($loop_regex, $content, $output_array);

Template:

<table>
    <tbody>
    <tr>
        <th>Trainee</th>
        <th>Status</th>
        <th>Invited</th>
        <th>Score</th>
        <th>Action</th>
    </tr>
    {{*Trainees}}
    <tr>
        <td>{{display_name}}</td>
        <td>{{status}}</td>
        <td>{{invited_date}}</td>
        <td>{{percentage}}</td>
        <td>Some action button</td>
    </tr>
    {{/Trainees}}
    </tbody>
</table>

All was fine until I tried to add more content into the template. All of a sudden, ERR_CONNECTION_RESET whenever it hits the preg_match_all.

The breaking point seems to be related to the size of the content within the {{Trainees}} group only, when it reaches about 395 characters, it breaks.

I found via a Drupal blog that adding this to the Apache httpd.config fixes it.

<IfModule mpm_winnt_module>
   ThreadStackSize 8388608
</IfModule>

But I don't really understand why this code would exceed the stack size and therefore potentially I could easily still break it with a bit more content.

Any theories would be welcome.

Environment: WAMPServer 3.0.8, PHP 5.6.25, Apache 2.4.23

2 Answers2

1

The expression

{{\*(\w+)}}((?:.|\n)+?){{\/\w+}}

is very ineffective, better use

{{\*(\w+)}}(.+?){{/\w+}}

with other delimiters, e.g. ~ instead.


Your old expression needs 780 steps (see this demo on regex101.com) while the latter only needs 404 steps (see another demo here).
Jan
  • 42,290
  • 8
  • 54
  • 79
  • 1
    Fair play Jan! That regex works much better, I've added extra content too and it holds up. Also, I wasn't aware of the "steps" thing so you've taught me something else too. – Ross Coombes Jan 22 '18 at 16:59
  • @RossCoombes: As important as the matching is usually the amount of steps needed to achieve this goal. Anyway, glad to help. – Jan Jan 22 '18 at 17:19
0

Incidentially, I also found that switching to PHP 7.0.10 also prevented the problem but that would presumably mask the inefficiency in my original regex so Jan's answer would seem to be the correct one.