Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 * This file is part of Psy Shell.
|
Chris@0
|
5 *
|
Chris@0
|
6 * (c) 2012-2017 Justin Hileman
|
Chris@0
|
7 *
|
Chris@0
|
8 * For the full copyright and license information, please view the LICENSE
|
Chris@0
|
9 * file that was distributed with this source code.
|
Chris@0
|
10 */
|
Chris@0
|
11
|
Chris@0
|
12 namespace Psy\ExecutionLoop;
|
Chris@0
|
13
|
Chris@0
|
14 use Psy\Configuration;
|
Chris@0
|
15 use Psy\Exception\BreakException;
|
Chris@0
|
16 use Psy\Exception\ErrorException;
|
Chris@0
|
17 use Psy\Exception\ThrowUpException;
|
Chris@0
|
18 use Psy\Exception\TypeErrorException;
|
Chris@0
|
19 use Psy\Shell;
|
Chris@0
|
20
|
Chris@0
|
21 /**
|
Chris@0
|
22 * The Psy Shell execution loop.
|
Chris@0
|
23 */
|
Chris@0
|
24 class Loop
|
Chris@0
|
25 {
|
Chris@0
|
26 const NOOP_INPUT = 'return null;';
|
Chris@0
|
27
|
Chris@0
|
28 /**
|
Chris@0
|
29 * Loop constructor.
|
Chris@0
|
30 *
|
Chris@0
|
31 * The non-forking loop doesn't have much use for Configuration, so we'll
|
Chris@0
|
32 * just ignore it.
|
Chris@0
|
33 *
|
Chris@0
|
34 * @param Configuration $config
|
Chris@0
|
35 */
|
Chris@0
|
36 public function __construct(Configuration $config)
|
Chris@0
|
37 {
|
Chris@0
|
38 // don't need this
|
Chris@0
|
39 }
|
Chris@0
|
40
|
Chris@0
|
41 /**
|
Chris@0
|
42 * Run the execution loop.
|
Chris@0
|
43 *
|
Chris@0
|
44 * @throws ThrowUpException if thrown by the `throw-up` command
|
Chris@0
|
45 *
|
Chris@0
|
46 * @param Shell $shell
|
Chris@0
|
47 */
|
Chris@0
|
48 public function run(Shell $shell)
|
Chris@0
|
49 {
|
Chris@0
|
50 $loop = function ($__psysh__) {
|
Chris@0
|
51 // Load user-defined includes
|
Chris@0
|
52 set_error_handler(array($__psysh__, 'handleError'));
|
Chris@0
|
53 try {
|
Chris@0
|
54 foreach ($__psysh__->getIncludes() as $__psysh_include__) {
|
Chris@0
|
55 include $__psysh_include__;
|
Chris@0
|
56 }
|
Chris@0
|
57 } catch (\Exception $_e) {
|
Chris@0
|
58 $__psysh__->writeException($_e);
|
Chris@0
|
59 }
|
Chris@0
|
60 restore_error_handler();
|
Chris@0
|
61 unset($__psysh_include__);
|
Chris@0
|
62
|
Chris@0
|
63 extract($__psysh__->getScopeVariables(false));
|
Chris@0
|
64
|
Chris@0
|
65 do {
|
Chris@0
|
66 $__psysh__->beforeLoop();
|
Chris@0
|
67 $__psysh__->setScopeVariables(get_defined_vars());
|
Chris@0
|
68
|
Chris@0
|
69 try {
|
Chris@0
|
70 // read a line, see if we should eval
|
Chris@0
|
71 $__psysh__->getInput();
|
Chris@0
|
72
|
Chris@0
|
73 // evaluate the current code buffer
|
Chris@0
|
74 ob_start(
|
Chris@0
|
75 array($__psysh__, 'writeStdout'),
|
Chris@0
|
76 version_compare(PHP_VERSION, '5.4', '>=') ? 1 : 2
|
Chris@0
|
77 );
|
Chris@0
|
78
|
Chris@0
|
79 // Let PsySH inject some magic variables back into the
|
Chris@0
|
80 // shell scope... things like $__class, and $__file set by
|
Chris@0
|
81 // reflection commands
|
Chris@0
|
82 extract($__psysh__->getSpecialScopeVariables(false));
|
Chris@0
|
83
|
Chris@0
|
84 // And unset any magic variables which are no longer needed
|
Chris@0
|
85 foreach ($__psysh__->getUnusedCommandScopeVariableNames() as $__psysh_var_name__) {
|
Chris@0
|
86 unset($$__psysh_var_name__, $__psysh_var_name__);
|
Chris@0
|
87 }
|
Chris@0
|
88
|
Chris@0
|
89 set_error_handler(array($__psysh__, 'handleError'));
|
Chris@0
|
90 $_ = eval($__psysh__->flushCode() ?: Loop::NOOP_INPUT);
|
Chris@0
|
91 restore_error_handler();
|
Chris@0
|
92
|
Chris@0
|
93 ob_end_flush();
|
Chris@0
|
94
|
Chris@0
|
95 $__psysh__->writeReturnValue($_);
|
Chris@0
|
96 } catch (BreakException $_e) {
|
Chris@0
|
97 restore_error_handler();
|
Chris@0
|
98 if (ob_get_level() > 0) {
|
Chris@0
|
99 ob_end_clean();
|
Chris@0
|
100 }
|
Chris@0
|
101 $__psysh__->writeException($_e);
|
Chris@0
|
102
|
Chris@0
|
103 return;
|
Chris@0
|
104 } catch (ThrowUpException $_e) {
|
Chris@0
|
105 restore_error_handler();
|
Chris@0
|
106 if (ob_get_level() > 0) {
|
Chris@0
|
107 ob_end_clean();
|
Chris@0
|
108 }
|
Chris@0
|
109 $__psysh__->writeException($_e);
|
Chris@0
|
110
|
Chris@0
|
111 throw $_e;
|
Chris@0
|
112 } catch (\TypeError $_e) {
|
Chris@0
|
113 restore_error_handler();
|
Chris@0
|
114 if (ob_get_level() > 0) {
|
Chris@0
|
115 ob_end_clean();
|
Chris@0
|
116 }
|
Chris@0
|
117 $__psysh__->writeException(TypeErrorException::fromTypeError($_e));
|
Chris@0
|
118 } catch (\Error $_e) {
|
Chris@0
|
119 restore_error_handler();
|
Chris@0
|
120 if (ob_get_level() > 0) {
|
Chris@0
|
121 ob_end_clean();
|
Chris@0
|
122 }
|
Chris@0
|
123 $__psysh__->writeException(ErrorException::fromError($_e));
|
Chris@0
|
124 } catch (\Exception $_e) {
|
Chris@0
|
125 restore_error_handler();
|
Chris@0
|
126 if (ob_get_level() > 0) {
|
Chris@0
|
127 ob_end_clean();
|
Chris@0
|
128 }
|
Chris@0
|
129 $__psysh__->writeException($_e);
|
Chris@0
|
130 }
|
Chris@0
|
131
|
Chris@0
|
132 $__psysh__->afterLoop();
|
Chris@0
|
133 } while (true);
|
Chris@0
|
134 };
|
Chris@0
|
135
|
Chris@0
|
136 // bind the closure to $this from the shell scope variables...
|
Chris@0
|
137 if (self::bindLoop()) {
|
Chris@0
|
138 $that = $shell->getBoundObject();
|
Chris@0
|
139 if (is_object($that)) {
|
Chris@0
|
140 $loop = $loop->bindTo($that, get_class($that));
|
Chris@0
|
141 } else {
|
Chris@0
|
142 $loop = $loop->bindTo(null, null);
|
Chris@0
|
143 }
|
Chris@0
|
144 }
|
Chris@0
|
145
|
Chris@0
|
146 $loop($shell);
|
Chris@0
|
147 }
|
Chris@0
|
148
|
Chris@0
|
149 /**
|
Chris@0
|
150 * A beforeLoop callback.
|
Chris@0
|
151 *
|
Chris@0
|
152 * This is executed at the start of each loop iteration. In the default
|
Chris@0
|
153 * (non-forking) loop implementation, this is a no-op.
|
Chris@0
|
154 */
|
Chris@0
|
155 public function beforeLoop()
|
Chris@0
|
156 {
|
Chris@0
|
157 // no-op
|
Chris@0
|
158 }
|
Chris@0
|
159
|
Chris@0
|
160 /**
|
Chris@0
|
161 * A afterLoop callback.
|
Chris@0
|
162 *
|
Chris@0
|
163 * This is executed at the end of each loop iteration. In the default
|
Chris@0
|
164 * (non-forking) loop implementation, this is a no-op.
|
Chris@0
|
165 */
|
Chris@0
|
166 public function afterLoop()
|
Chris@0
|
167 {
|
Chris@0
|
168 // no-op
|
Chris@0
|
169 }
|
Chris@0
|
170
|
Chris@0
|
171 /**
|
Chris@0
|
172 * Decide whether to bind the execution loop.
|
Chris@0
|
173 *
|
Chris@0
|
174 * @return bool
|
Chris@0
|
175 */
|
Chris@0
|
176 protected static function bindLoop()
|
Chris@0
|
177 {
|
Chris@0
|
178 // skip binding on HHVM <= 3.5.0
|
Chris@0
|
179 // see https://github.com/facebook/hhvm/issues/1203
|
Chris@0
|
180 if (defined('HHVM_VERSION')) {
|
Chris@0
|
181 return version_compare(HHVM_VERSION, '3.5.0', '>=');
|
Chris@0
|
182 }
|
Chris@0
|
183
|
Chris@0
|
184 return version_compare(PHP_VERSION, '5.4', '>=');
|
Chris@0
|
185 }
|
Chris@0
|
186 }
|