0

I am trying to add a mechanism for loading classes on demand and then basically unloaded them / being able to reload them. Essentially, each time the class is needed, it should be loaded freshly from disk as opposed to using what already exists in memory.

To do so, I am following the instructions here: Reloading a Class

However, I have noticed some odd behavior:

file2.php:

class OtherClass {
   public function doSomething() {
      echo "Hello, goodbye";
   }
}

file.php:

class MyClass {
   public function myFunction() {
       print_r(get_declared_classes());
        $pid = pcntl_fork();
        if ($pid === -1) {
            /* fork failed */
        } else if ($pid > 0) { /* parent */
            pcntl_wait($status);
            print_r(get_declared_classes());
        } else { /* the child */
            echo "Loading class";
            require("file2.php"); /* load the class */
            OtherClass::doSomething();
        }
   }
}

As would be required, the require executes only when the function is called, not when the file itself is loaded by PHP. So this means that prior to that line of code being executed when the function is called, which would be after the fork, that class doesn't exist.

As such, I would expect that when the second print_r executes, it is exactly the same output as the first time. This is because the child has loaded the class, not the parent.

However, what actually happens is that the class that was loaded by the child has somehow has become visible to the parent process. (The second print_r has the class that the child loaded and is now larger.)

Am I missing something here? When the program forked, this other class didn't exist. The child loaded it and used it, and then died; yet, that class is now visible to the parent.

The real problem here is that the next time the function executes, loading the class again using require fails because the class already exists. This would be expected if the class existed in the parent process at the time it was forked. This means that somehow, the class "leaked" from the child process to the parent process.

My understanding is that once the fork happens, stuff in the child shouldn't affect the parent, outside of sharing file descriptors like STDOUT, so why is this happening? How can I achieve the "expected result"? At the moment, this allows loading a class later during program execution, but it can only be done once, as opposed to multiple times. I don't see anything in the PHP docs for this function that would explain this behavior.

1
  • While not related to fixing the problem: require and include are expressions, just like echo; they're not function calls. Adding parentheses will work, but is not the right way to use these. Just like your echo "Loading class";, use require "file2.php";. Commented Jan 23, 2022 at 19:32

1 Answer 1

0

Indeed, I was missing something simple:

Adding posix_kill(getmypid(), SIGKILL); at the end of the child block fixes it, e.g.:

lass MyClass {
   public function myFunction() {
       print_r(get_declared_classes());
        $pid = pcntl_fork();
        if ($pid === -1) {
            /* fork failed */
        } else if ($pid > 0) { /* parent */
            pcntl_wait($status);
            print_r(get_declared_classes());
        } else { /* the child */
            echo "Loading class";
            require("file2.php"); /* load the class */
            OtherClass::doSomething();
            posix_kill(getmypid(), SIGKILL);
        }
   }
}

I guess without that, the child wasn't actually dying at the end, and somehow that caused the issue.

Not the answer you're looking for? Browse other questions tagged or ask your own question.