PageRenderTime 42ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/d11wtq/boris/README.md

https://gitlab.com/xolotsoft/pumasruiz
Markdown | 306 lines | 216 code | 90 blank | 0 comment | 0 complexity | a84e3cf0e7adcedfa926622cc8f0bd5f MD5 | raw file
  1. # Boris: A tiny little, but robust REPL for PHP
  2. ![Demo](http://dl.dropbox.com/u/508607/BorisDemo-v3.gif "Quick Demo")
  3. Python has one. Ruby has one. Clojure has one. Now PHP has one too. Boris is
  4. PHP's missing REPL (read-eval-print loop), allowing developers to experiment
  5. with PHP code in the terminal in an interactive manner. If you make a mistake,
  6. it doesn't matter, Boris will report the error and stand to attention for
  7. further input.
  8. Everything you enter into Boris is evaluated and the result inspected so you
  9. can understand what is happening. State is maintained between inputs, allowing
  10. you to gradually build up a solution to a problem.
  11. ## Why?
  12. I'm in the process of transitioning away from PHP to Ruby. I have come to find
  13. PHP's lack of a real REPL to be frustrating and was not able to find an existing
  14. implementation that was complete. Boris weighs in at a few hundred lines of
  15. fairly straightforward code.
  16. ## Installation
  17. ### 1. As a pre-built phar file
  18. Boris is available for download as a Phar archive:
  19. - https://github.com/d11wtq/boris/releases/download/v1.0.8/boris.phar
  20. Simply download it and run it.
  21. curl -O https://github.com/d11wtq/boris/releases/download/v1.0.8/boris.phar
  22. chmod +x boris.phar
  23. ./boris.phar
  24. ### 2. Via packagist
  25. For use with composer.
  26. - https://packagist.org/packages/d11wtq/boris
  27. ### 3. Directly from this repo
  28. This is great if you want to stay really up-to-date. I don't commit unstable
  29. code to master, ever.
  30. git clone git://github.com/d11wtq/boris.git
  31. cd boris
  32. ./bin/boris
  33. ### 4. Build your own phar
  34. You can also build a PHAR file using [Box](http://box-project.org/):
  35. box build
  36. This will create a `boris.phar` file. Feel free to move it into your bin
  37. directory:
  38. chmod +x boris.phar
  39. mv boris.phar /usr/local/bin/boris
  40. ### Pro Tip
  41. Add boris to your $PATH for easy access.
  42. ## Usage
  43. When Boris starts, you will be at the `boris>` prompt. PHP code you enter at
  44. this prompt is evaluated. If an expression spans multiple lines, Boris will
  45. collect the input and then evaluate the expression when it is complete. Press
  46. CTRL-C to clear a multi-line input buffer if you make a mistake. The output
  47. is dumped with `var_dump()` by default.
  48. boris> $x = 1;
  49. int(1)
  50. boris> $y = 2;
  51. int(2)
  52. boris> "x + y = " . ($x + $y);
  53. string(9) "x + y = 3"
  54. boris> exit;
  55. You can also use CTRL-D to exit the REPL.
  56. ### Cancelling long-running operations
  57. Long-running operations, such as infinite loops, may be cancelled at any time
  58. without quitting the REPL, by using CTRL-C while the operation is running.
  59. boris> for ($i = 0; ; ++$i) {
  60. *> if ($i % 2 == 0) printf("Tick\n");
  61. *> else printf("Tock\n");
  62. *> sleep(1);
  63. *> }
  64. Tick
  65. Tock
  66. Tick
  67. Tock
  68. Tick
  69. Tock
  70. Tick
  71. ^CCancelling...
  72. boris>
  73. ### Using Boris with your application loaded
  74. You can also use Boris as part of a larger project (e.g. with your application
  75. environment loaded).
  76. require_once 'lib/autoload.php';
  77. $boris = new \Boris\Boris('myapp> ');
  78. $boris->start();
  79. The constructor parameter is optional and changes the prompt.
  80. If you want to pass local variables straight into Boris (e.g. parts of your
  81. application), you can do that too (thanks to [@dhotston](https://github.com/dhotston)):
  82. $boris = new \Boris\Boris('myapp> ');
  83. $boris->setLocal(array('appContext' => $appContext));
  84. $boris->start();
  85. In the above example, $appContext will be present inside the REPL.
  86. ### Using start hooks
  87. It is possible to add callbacks to Boris that are executed inside the REPL
  88. before it begins looping. Any number of hooks can be added, and they will be
  89. executed in order. Any variables set or exported by the hook will become
  90. visible from inside the REPL and consequently, to subsequent hooks that are
  91. run.
  92. There are two ways to specify hooks: as arbitrary strings of PHP code to
  93. evaluate, or as callbacks given as closures. Both approaches allow you access
  94. to the scope, though you need to do slightly more work with the callback
  95. approach.
  96. // this will simply be evaluated in the REPL scope
  97. // the variables $foo and $bar will be visible inside the REPL
  98. $boris->onStart('$foo = 42; $bar = 2; echo "Hello Boris!\n";');
  99. // this will be passed the REPL and it's scope as arguments
  100. // any changes to the scope can be expressed by invoking the usual
  101. // methods on the REPL
  102. $boris->onStart(function($worker, $scope){
  103. extract($scope); // we could also just access specific vars by key
  104. echo '$foo * $bar = ' . ($foo * $bar) . "\n";
  105. $worker->setLocal('name', 'Chris');
  106. });
  107. Above we added two hooks. The first just gets passed to `eval()` and leaves
  108. `$foo` and `$bar` in scope. The second uses the callback style and reads its
  109. variables from the `$scope` parameter, then sets variables into the REPL
  110. with `setLocal()`.
  111. ### User configuration files
  112. If you have, things you always want to do when Boris starts, such as load
  113. useful utility functions, change the prompt or set local variable, you
  114. may create a ~/.borisrc file, which will be loaded whenever Boris starts up.
  115. The contents of this file are just arbitrary PHP code. You are *not* inside
  116. the REPL itself in this file, but you have access to `$boris`, which is the
  117. REPL object. Here's an example ~/.borisrc that sets the prompt.
  118. <?php
  119. /* File: ~/.borisrc */
  120. $boris->setPrompt('prompty> ');
  121. Boris will also look under your current working directory for this file. If
  122. it finds one on both locations, they will both be loaded by default (not that
  123. this is customizable at the code level). If you need to execute code in the
  124. REPL itself, use hooks as documented above.
  125. Thanks to [@filp](https://github.com/filp) for this feature!
  126. ### Customizing the output
  127. After each expression you enter, Boris passes it through an Inspector to get a
  128. representation that is useful for debugging. The default is does some nice
  129. highlighting of the data types in the value, to make it easier to read at a
  130. glance, but you can change this behaviour.
  131. Any object that has an `inspect($variable)` method may be used for this purpose.
  132. $boris->setInspector(new BlinkInspector());
  133. Boris comes with three alternatives out of the box:
  134. * \Boris\ColoredInspector, which does data-type highlighting and is the default
  135. * \Boris\DumpInspector, which uses a simple, but effective var_dump()
  136. * \Boris\ExportInspector, which uses var_export()
  137. Note that you can change this from inside the REPL too:
  138. boris> $this->setInspector(new \Boris\ExportInspector());
  139. -> NULL
  140. boris> "Test";
  141. -> 'Test'
  142. To further customize object output within `\Boris\ColoredInspector`, you may
  143. subclass and override the `objectVars($value)` method:
  144. class MyInspector extends \Boris\ColoredInspector {
  145. public function objectVars($value) {
  146. if ($value instanceof User) {
  147. return array('user_id' => $value->getUserId());
  148. }
  149. return parent::objectVars($value);
  150. }
  151. }
  152. This overrides the default behavior of simply calling [`get_object_vars()`][get_object_vars]
  153. on the object, allowing you to display properties that may be otherwise obfuscated
  154. behind magic methods or property visibility.
  155. [get_object_vars]: http://php.net/get_object_vars
  156. ## Boris doesn't display exceptions or errors when running in my app?
  157. Boris honours your environment. If your application has error handlers
  158. installed, they will mask the error. Likewise, if an exception handler is
  159. installed, you won't see a backtrace (unless your exception handler displays it).
  160. Since Boris is much more useful when you can see errors in the console, the best
  161. thing to do is to disable any exception/error handlers when your application
  162. is running inside of Boris.
  163. ## What about PHP's interactive mode?
  164. PHP's interactive mode does not print the result of evaluating expressions and
  165. more importantly, it exits if you type something that produces a fatal error,
  166. such as invoking a function/method that does not exist, or an uncaught
  167. exception. Boris is designed to be robust, like other REPLs, so you can
  168. experiment with things that you know may error, without losing everything.
  169. ## Architecture Overview
  170. This section of the README only applies to those curious enough to read the
  171. code. Boris is quite different to other PHP REPLs, because it deals with fatal
  172. errors (not Exceptions, fatal errors) in a special way.
  173. Boris will only work on POSIX systems (Linux and Mac OS). This is primarily
  174. because it depends on the ability to fork, but also because it plays with signals
  175. a lot too.
  176. Boris is made up of two parts:
  177. 1. A REPL worker process, which receives expressions to evaluate and print
  178. 2. A readline client, which simply takes your input, sends it to the worker
  179. and then loops
  180. If all errors in PHP were exceptions, building a REPL would be simple. This is
  181. not the case, however. Some PHP errors are truly fatal and cannot be caught.
  182. In order to prevent such fatal errors from killing the REPL, the worker looks
  183. something like this:
  184. for(;;) {
  185. $input = accept_some_input();
  186. if (fork_child()) {
  187. wait_for_child();
  188. } else { // inside child
  189. var_dump(eval($input));
  190. kill_parent();
  191. }
  192. }
  193. The child is forked with all current variables and resources. It evaluates the
  194. input then kills the parent, then the loop continues inside the child, waiting
  195. for the next input.
  196. While the child is evaluating the input, the parent waits. The parent is
  197. expecting the worstthat the child will die abnormallyat which point the parent
  198. continues waiting for input and does not terminate. The state remains unchanged.
  199. After each expression is evaluated, the worker reports back to the main process
  200. with a status code of 0 (keep running) or 1 (terminate).
  201. The main process (readline) of Boris is much more straightforward. It takes
  202. your input, performs a (very) shallow parse on it, in order to decide if it
  203. needs to wait for further input, or evaluate the input (one statement at a time)
  204. it has received. If the worker reports back with a status code of 1, the process
  205. terminates, otherwise the next iteration of the loop is entered.
  206. ## Will it work with...?
  207. Boris depends on the following PHP features:
  208. - PHP >= 5.3
  209. - The Readline functions
  210. - The PCNTL functions
  211. - The POSIX functions
  212. There's no chance it can work on Windows, due to the dependency on POSIX
  213. features (the code is almost entirely dependant on POSIX).
  214. ## Copyright & Licensing
  215. Boris is written and maintained by Chris Corbyn (@d11wtq). You can use the
  216. code as you see fit. See the LICENSE file for details.