PageRenderTime 46ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/PHP/Repl.php

https://github.com/sethwalker/php_repl
PHP | 380 lines | 245 code | 35 blank | 100 comment | 24 complexity | 12a394c820ff7d1907c3e93b3d6f95c7 MD5 | raw file
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * A REPL for PHP
  5. *
  6. * PHP version 5
  7. *
  8. * @category PHP
  9. * @package Repl
  10. * @author Ian Eure <ieure@php.net>
  11. * @copyright 2009 Ian Eure.
  12. * @filesource
  13. */
  14. /**
  15. * PHP_Repl
  16. *
  17. * @package PHP_Repl
  18. * @author Ian Eure <ieure@php.net>
  19. * @version @package_version@
  20. */
  21. class PHP_Repl
  22. {
  23. /**
  24. * Where we're reading input from
  25. *
  26. * @var resource
  27. */
  28. private $input;
  29. /**
  30. * The options for this instance
  31. *
  32. * @var array
  33. */
  34. private $options = array();
  35. /**
  36. * The path to the configuration file
  37. *
  38. * @var string
  39. */
  40. private $rc_file;
  41. /**
  42. * Constructor
  43. *
  44. * @return void
  45. */
  46. public function __construct($options = array())
  47. {
  48. $this->input = fopen('php://stdin', 'r');
  49. $this->rc_file = getenv('PHPREPLRC') ? getenv('PHPREPLRC') :
  50. getenv('HOME') . '/.phpreplrc';
  51. $defaults = $this->defaultOptions();
  52. $this->options = array_merge($defaults, $options);
  53. if ($this->options['readline'] &&
  54. is_readable($this->options['readline_hist'])) {
  55. array_map('readline_add_history',
  56. file($this->options['readline_hist'],
  57. FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES));
  58. }
  59. if ($this->options['autorun']) {
  60. $this->run();
  61. }
  62. }
  63. /**
  64. * Get default options
  65. *
  66. * @return array Defaults
  67. */
  68. private function defaultOptions()
  69. {
  70. $defaults = array('prompt' => 'php> ',
  71. 'autorun' => false,
  72. 'readline' => true,
  73. 'readline_hist' => getenv('HOME') .
  74. '/.phprepl_history');
  75. if (!function_exists('readline') || getenv('TERM') == 'dumb') {
  76. $defaults['readline'] = false;
  77. }
  78. if (is_readable($this->rc_file)) {
  79. $rc_defaults = parse_ini_file($this->rc_file);
  80. if (isset($rc_defaults['autorun'])) {
  81. unset($rc_defaults['autorun']);
  82. }
  83. $defaults = array_merge($defaults, $rc_defaults);
  84. }
  85. return $defaults;
  86. }
  87. /**
  88. * Destructor
  89. *
  90. * @return void
  91. */
  92. public function __destruct()
  93. {
  94. fclose($this->input);
  95. if ($this->options['readline']) {
  96. readline_write_history($this->options['readline_hist']);
  97. }
  98. // Save config
  99. $fp = fopen($this->rc_file, 'w');
  100. if ($fp === false) {
  101. return;
  102. }
  103. foreach ($this->options as $k => $v) {
  104. fwrite($fp, "$k = \"$v\"\n");
  105. }
  106. fclose($fp);
  107. }
  108. /**
  109. * Run the main loop
  110. *
  111. * @return void
  112. */
  113. public function run()
  114. {
  115. ob_start();
  116. while (true) {
  117. ob_flush();
  118. ob_end_clean();
  119. try {
  120. if (((boolean) $__code__ = $this->read()) === false) {
  121. break;
  122. }
  123. ob_start(array($this, 'ob_cleanup'));
  124. ob_implicit_flush(true);
  125. error_reporting(E_ALL | E_STRICT);
  126. ini_set('html_errors', 'Off');
  127. ini_set('display_errors', 'On');
  128. $this->_print($_ = eval($this->cleanup($__code__)));
  129. } catch (Exception $e) {
  130. echo ($_ = $e) . "\n";
  131. }
  132. }
  133. }
  134. /**
  135. * Read input
  136. *
  137. * @param
  138. *
  139. * @return string Input
  140. */
  141. private function read()
  142. {
  143. $code = '';
  144. $done = false;
  145. $lines = 0;
  146. do {
  147. $prompt = $lines > 0 ? '> ' : $this->options['prompt'];
  148. if ($this->options['readline']) {
  149. $line = readline($prompt);
  150. } else {
  151. echo $prompt;
  152. $line = fgets($this->input);
  153. }
  154. // If the input was empty, return false; this breaks the loop.
  155. if ($line === false) {
  156. return false;
  157. }
  158. $line = trim($line);
  159. // If the last char is a backslash, remove it and
  160. // accumulate more lines.
  161. if (substr($line, -1) == '\\') {
  162. $line = substr($line, 0, strlen($line) - 1);
  163. } else {
  164. $done = true;
  165. }
  166. $code .= $line;
  167. $lines++;
  168. } while (!$done);
  169. // Add the whole block to the readline history.
  170. if ($this->options['readline']) {
  171. readline_add_history($code);
  172. }
  173. return $code;
  174. }
  175. /**
  176. * Clean up the read string
  177. *
  178. * @param string $input The input we read
  179. *
  180. * @return string Cleaned up code to eval
  181. */
  182. private function cleanup($input)
  183. {
  184. static $implicit = array('return', 'throw', 'class', 'function',
  185. 'interface', 'abstract', 'static', 'echo',
  186. 'include', 'include_once', 'require',
  187. 'require_once');
  188. static $sugar = array(',' => 'dissect',
  189. 'd' => 'doc',
  190. 'l' => 'dir',
  191. 'e' => 'cleanup');
  192. static $last;
  193. $input = trim($input);
  194. // Sugar
  195. if (substr($input, 0, 1) == ',' &&
  196. isset($sugar[$m = substr($input, 1, 1)])) {
  197. $input = preg_replace('/^,.\s*/', '', $input);
  198. if (empty($input)) {
  199. $input = $last;
  200. }
  201. if (substr($input, 0, 1) != '$') {
  202. $input = "'$input'";
  203. }
  204. return $this->cleanup("\$this->{$sugar[$m]}($input)");
  205. }
  206. // Add a trailing semicolon
  207. if (substr($input, -1) != ';') {
  208. $input .= ';';
  209. }
  210. // Make sure we get a value back from eval()
  211. $first = substr($input, 0, strpos($input, " "));
  212. if (!in_array($first, $implicit)) {
  213. $input = 'return ' . $input;
  214. }
  215. return $last = $input;
  216. }
  217. /**
  218. * Clean up output captured from eval()'d code
  219. *
  220. * @param string $output Output from the code
  221. *
  222. * @return string Cleaned up output
  223. */
  224. public function ob_cleanup($output)
  225. {
  226. if (strlen($output) > 0 && substr($output, -1) != "\n") {
  227. $output .= "\n";
  228. }
  229. return $output;
  230. }
  231. /**
  232. * Print the output of some code
  233. *
  234. * @param mixed $out The output
  235. *
  236. * @return void
  237. */
  238. private function _print($out)
  239. {
  240. $type = gettype($out);
  241. switch ($type) {
  242. case 'NULL':
  243. case 'double':
  244. case 'float':
  245. case 'integer':
  246. case 'boolean':
  247. var_dump($out);
  248. break;
  249. case 'string':
  250. case 'array':
  251. var_export($out);
  252. echo "\n";
  253. break;
  254. default:
  255. print_r($out);
  256. }
  257. }
  258. /**
  259. * Get reflection for something
  260. *
  261. * @param string $thing The thing to get reflection for
  262. *
  263. * @return mixed ReflectionFoo instance
  264. */
  265. protected function getReflection($thing)
  266. {
  267. switch (true) {
  268. case is_object($thing):
  269. return new ReflectionObject($thing);
  270. case class_exists($thing, false):
  271. return new ReflectionClass($thing);
  272. case function_exists($thing):
  273. return new ReflectionFunction($thing);
  274. case strstr($thing, '::'):
  275. list($class, $what) = explode('::', $thing);
  276. $rc = new ReflectionClass($class);
  277. switch (true) {
  278. case substr($what, -2) == '()':
  279. $what = substr($what, 0, strlen($what) - 2);
  280. case $rc->hasMethod($what):
  281. return $rc->getMethod($what);
  282. case substr($what, 0, 1) == '$':
  283. $what = substr($what, 1);
  284. case $rc->hasProperty($what):
  285. return $rc->getProperty($what);
  286. case $rc->hasConstant($what):
  287. return $rc->getConstant($what);
  288. }
  289. }
  290. }
  291. /**
  292. * Dissect something
  293. *
  294. * @param mixed $thing The thing to dissect
  295. *
  296. * @return void
  297. */
  298. protected function dissect($thing)
  299. {
  300. echo (string) $ref = $this->getReflection($thing);
  301. return "---";
  302. }
  303. /**
  304. * Get a list of methods and properties of a class
  305. *
  306. * @param mixed $thing The thing to dissect
  307. *
  308. * @return void
  309. */
  310. protected function dir($thing)
  311. {
  312. $rc = $this->getReflection($thing);
  313. foreach ($rc->getProperties() as $prop) {
  314. echo "\${$prop->getName()}\n";
  315. }
  316. foreach ($rc->getMethods() as $meth) {
  317. echo "\{$meth->getName()}()\n";
  318. }
  319. return "---";
  320. }
  321. /**
  322. * Get documentation for something
  323. *
  324. * @param mixed $thing The thing to dissect
  325. *
  326. * @return void
  327. */
  328. protected function doc($thing)
  329. {
  330. echo preg_replace('/^\s*\*/m', ' *',
  331. $this->getReflection($thing)->getDocComment()) . "\n";
  332. return "---";
  333. }
  334. }
  335. ?>