Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Way to purge or reload modified code without exiting? #416

Open
jchook opened this issue Sep 5, 2017 · 11 comments
Open

Way to purge or reload modified code without exiting? #416

jchook opened this issue Sep 5, 2017 · 11 comments

Comments

@jchook
Copy link

jchook commented Sep 5, 2017

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.

@bobthecow
Copy link
Owner

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 :(

@jchook
Copy link
Author

jchook commented Oct 3, 2017

Wow thanks for the tip on runkit.

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

@bobthecow
Copy link
Owner

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

@bobthecow
Copy link
Owner

bobthecow commented Oct 3, 2017

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 :)

@jchook
Copy link
Author

jchook commented Oct 6, 2017

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.

@bobthecow
Copy link
Owner

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

@SimonEast
Copy link

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.

@jchook
Copy link
Author

jchook commented Dec 19, 2018

I agree with @SimonEast.

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

@chuck-wood
Copy link

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

@bobthecow
Copy link
Owner

bobthecow commented May 3, 2020

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

@maxime-aknin
Copy link
Contributor

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à

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants