skip to Main Content

The following code:

<?php

function e($m, $n) {
    return eval(
        'return new class() {

            // '. str_repeat('a', $m). '
            public function test() {
                return "'. $n. '";
            }

        };'
    );
}

for ($i = 131; $i < 140; $i++) {
    printf("%d: %d, %d, %d, %d, %d, %dn",
        $i,
        e($i, 1)->test(),
        e($i, 2)->test(),
        e($i, 1)->test(),
        e($i, 2)->test(),
        e($i + 1, 1)->test(),
        e($i + 1, 2)->test()
    );
}

When run using php 7.4 or 8.* it prints

131: 1, 2, 1, 2, 1, 2
132: 1, 2, 1, 2, 1, 2
133: 1, 2, 1, 2, 1, 2
134: 1, 2, 1, 2, 1, 2
135: 1, 2, 1, 2, 1, 2
136: 1, 2, 1, 2, 1, 2
137: 1, 2, 1, 2, 1, 2
138: 1, 2, 1, 2, 1, 2
139: 1, 2, 1, 2, 1, 2

which is expected. When run using php 7.3 – 7.0 it prints

131: 1, 2, 2, 2, 1, 1
132: 1, 1, 1, 1, 1, 2
133: 1, 2, 1, 2, 1, 2
134: 1, 2, 1, 2, 1, 2
135: 1, 2, 1, 2, 1, 1
136: 1, 1, 1, 1, 1, 1
137: 1, 1, 1, 1, 1, 1
138: 1, 1, 1, 1, 1, 1
139: 1, 1, 1, 1, 1, 1

which is quite unexpected. I haven’t found a workaround and I really need one!

Initially it seemed to be about the number of characters in the beginning of the eval’d code that are the same, but when the code is more complex this is no longer the case.

Please, help!

2

Answers


  1. I am not sure what cause your problem, but usage of eval() must be avoided when it’s possible, and here it’s possible:

    <?php
    
    class MyTestClass {
        private $n;
        private $m;
    
        public function __construct($m, $n) {
            $this->m = str_repeat('a', $m);
            $this->n = $n;
        }
    
        public function test() {
            return $this->n;
        }
    }
    
    function evalClass($m, $n) {
        return new MyTestClass($m, $n);
    }
    
    for ($i = 131; $i < 140; $i++) {
        echo $i. ': '. evalClass($i, 1)->test(). ', '. evalClass($i, 2)->test(). ', '. evalClass($i, 1)->test(). ', '. evalClass($i, 2)->test(). ', '. evalClass($i + 1, 1)->test(). ', '. evalClass($i + 1, 2)->test(). "n";
    }
    

    Output:

    131: 1, 2, 1, 2, 1, 2
    132: 1, 2, 1, 2, 1, 2
    133: 1, 2, 1, 2, 1, 2
    134: 1, 2, 1, 2, 1, 2
    135: 1, 2, 1, 2, 1, 2
    136: 1, 2, 1, 2, 1, 2
    137: 1, 2, 1, 2, 1, 2
    138: 1, 2, 1, 2, 1, 2
    139: 1, 2, 1, 2, 1, 2
    
    Login or Signup to reply.
  2. While I have no concrete idea what is causing this bug, my gut says that it’s something to do with the object reference being returned from the eval’d code.

    That said, while I was fiddling around with test cases it started working for some reason with the following code:

    for ($i = 0; $i < 1024; $i++) {
        
        $result = [];
        $status = false;
        for( $k=0; $k<6; ++$k) {
            $r = e($i, $i)->test();
            $result[] = $r;
            if( $r != $i ) {
                $status = true;
            }
        }
        
        if( $status ) {
            printf("%3d: %3d, %3d, %3d, %3d, %3d, %3dn",
                $i,
                ...$result
            );
        }
    }
    

    If anything it’s probably the array assignment, or merely having another actual operation between invocations of eval() code, or having an opportunity to GC the orphan eval object result and avoid pollution.

    Questionable if it will actually work in your actual code, but it’s something. You should still also file that bug.

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