ExecutionClosure.php 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. <?php
  2. /*
  3. * This file is part of Psy Shell.
  4. *
  5. * (c) 2012-2023 Justin Hileman
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Psy;
  11. /**
  12. * The Psy Shell's execution scope.
  13. */
  14. class ExecutionClosure
  15. {
  16. const NOOP_INPUT = 'return null;';
  17. private $closure;
  18. /**
  19. * @param Shell $__psysh__
  20. */
  21. public function __construct(Shell $__psysh__)
  22. {
  23. $this->setClosure($__psysh__, function () use ($__psysh__) {
  24. try {
  25. // Restore execution scope variables
  26. \extract($__psysh__->getScopeVariables(false));
  27. // Buffer stdout; we'll need it later
  28. \ob_start([$__psysh__, 'writeStdout'], 1);
  29. // Convert all errors to exceptions
  30. \set_error_handler([$__psysh__, 'handleError']);
  31. // Evaluate the current code buffer
  32. $_ = eval($__psysh__->onExecute($__psysh__->flushCode() ?: self::NOOP_INPUT));
  33. } catch (\Throwable $_e) {
  34. // Clean up on our way out.
  35. if (\ob_get_level() > 0) {
  36. \ob_end_clean();
  37. }
  38. throw $_e;
  39. } finally {
  40. // Won't be needing this anymore
  41. \restore_error_handler();
  42. }
  43. // Flush stdout (write to shell output, plus save to magic variable)
  44. \ob_end_flush();
  45. // Save execution scope variables for next time
  46. $__psysh__->setScopeVariables(\get_defined_vars());
  47. return $_;
  48. });
  49. }
  50. /**
  51. * Set the closure instance.
  52. *
  53. * @param Shell $shell
  54. * @param \Closure $closure
  55. */
  56. protected function setClosure(Shell $shell, \Closure $closure)
  57. {
  58. $that = $shell->getBoundObject();
  59. if (\is_object($that)) {
  60. $this->closure = $closure->bindTo($that, \get_class($that));
  61. } else {
  62. $this->closure = $closure->bindTo(null, $shell->getBoundClass());
  63. }
  64. }
  65. /**
  66. * Go go gadget closure.
  67. *
  68. * @return mixed
  69. */
  70. public function execute()
  71. {
  72. $closure = $this->closure;
  73. return $closure();
  74. }
  75. }