psysh icon indicating copy to clipboard operation
psysh copied to clipboard

Way to purge or reload modified code without exiting?

Open jchook opened this issue 8 years ago • 11 comments

Hey, first.. huge fan of psysh. Thanks for making such a fantastic tool.

My request may already be possible, or may be _im_possible to achieve with PHP. Ultimately it would be really awesome if I could:

  1. Start psysh with a config file (to include my autoloader, etc.)
  2. Instantiate some classes and do some stuff
  3. Encounter an error
  4. Go into the code, fix the error, save the file
  5. Come back to psysh and re-execute the last command, but using the newly modified code.

Obviously there may need to be a "step 5a." in there where we "purge" the bytecode cache or something. Maybe it's not even possible to do this.

jchook avatar Sep 05 '17 23:09 jchook

Unfortunately this isn't really possible. If you're still using 5.x there is a PHP extension called "runkit" which allows this. And PsySH even has support for that! But runkit doesn't support PHP 7, and the closest available option doesn't really support what we'd need for code reloading :(

bobthecow avatar Sep 28 '17 04:09 bobthecow

Wow thanks for the tip on runkit.

After Googling, looks like there are some other PHP7 options: Soft-Mocks and UPOZ.

jchook avatar Oct 03 '17 01:10 jchook

Unfortunately neither of those allows re-importing files like runkit did.

bobthecow avatar Oct 03 '17 06:10 bobthecow

Ideally—and with the runkit code reloader this is possible—any existing objects you have would be redefined whenever you update their code.

Something like Soft Mocks works by importing a different version every time you import code, but even if we got it working correctly, it would never update existing objects to match the new code.

So if you create an object, then change its source code, then create an identical object, they won't have the same type. In my opinion, this is a worse place (read: less predictable and less understandable) to be in than having to reload your shell to pick up code changes.

We'd also have to deal with all the gymnastics of mapping "user typed App/User, but we imported as another name, so remap on the fly to the right name, but be sure to remap back whenever we're showing objects or names to the user… and be sure not to forget values like $foo::class". It's a not an easy proposition :)

While it doesn't come with a built-in ability to re-import files, UOPZ has at least some of the functionality necessary to do code reloading: If we wrote a fairly sophisticated code reloader, we could delete and redefine methods on existing classes, and change them from private to public or vice versa. We could change the inheritance or interfaces of an already defined class. We could replace functions. And we could redefine constants. All of this would work with objects already instantiated in the shell.

And this might be enough. I've definitely considered exploring it. If you want to take a shot, I'd welcome a pull request :)

bobthecow avatar Oct 03 '17 06:10 bobthecow

Man, thanks for the exquisite explanation of all this.

Ultimately I realize that my ideal solution is to write a test and have a watcher replay the test as I make changes to the source. But using psysh is so much easier and immediate for prototyping or trying out ideas; it has a kind of "hands-on" feel and is always at the ready without additional setup.

I wonder if it would be possible to use a simple record-reset-replay strategy.

  1. Psysh automatically starts recording REPL input, with commands to manage recording(s)
  2. After you make changes to the source and want to reload, enter reload or whatever. Maybe this could be bound to ⌘+R or something fancy.
  3. Psysh dumps it's entire PHP thread (or however this is accomplished) and starts a new one using the current config
  4. Psysh replays recorded input, printing each input and output again.

jchook avatar Oct 06 '17 00:10 jchook

Replaying input might work, but not for anything that's stateful. Database access, filesystem access, etc all break.

bobthecow avatar Oct 21 '17 20:10 bobthecow

What if there was a psysh command to just reload psysh with the same parameters it was originally started with?

For example, if I ran:

psysh bootstrap.php run_tests.php

And later typed:

reload

It would be great if it could exit and reload the same entry scripts. Admittedly, this is not really much different from just exiting back to command prompt, hitting up arrow and enter to replay the last command, but it would save a few keystrokes.

SimonEast avatar Sep 09 '18 22:09 SimonEast

I agree with @SimonEast.

This seems like something very easy to implement and would be much appreciated, especially now that we have history --replay.

jchook avatar Dec 19 '18 01:12 jchook

All I want is to not have to use App\Models\<etc> again! 😄

chuck-wood avatar Oct 03 '19 20:10 chuck-wood

I just added a section about this to the Troubleshooting wiki page.

bobthecow avatar May 03 '20 16:05 bobthecow

Hey there's an existing fork of runkit for php7. Unfortunatly the runkit_import function was removed in version 4.0.0a1, apparently because of bugs with php7.3+, so this will not work for latest versions of php, but it should be mostly ok for earlier versions (haven't fully tested though). To install:

  1. pecl install runkit7-3.1.0a1
  2. I created a PR to update RunkitReloader.php class (nb: might not work for class props, see pr modifications)

Voilà

maxime-aknin avatar Feb 10 '21 11:02 maxime-aknin