mirror of
https://e.coding.net/circlecloud/MinecraftAccount.git
synced 2025-11-25 21:36:08 +00:00
174
ThinkPHP/Library/Vendor/Boris/Boris.php
vendored
Normal file
174
ThinkPHP/Library/Vendor/Boris/Boris.php
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
/* vim: set shiftwidth=2 expandtab softtabstop=2: */
|
||||
|
||||
namespace Boris;
|
||||
|
||||
/**
|
||||
* Boris is a tiny REPL for PHP.
|
||||
*/
|
||||
class Boris {
|
||||
const VERSION = "1.0.8";
|
||||
|
||||
private $_prompt;
|
||||
private $_historyFile;
|
||||
private $_exports = array();
|
||||
private $_startHooks = array();
|
||||
private $_failureHooks = array();
|
||||
private $_inspector;
|
||||
|
||||
/**
|
||||
* Create a new REPL, which consists of an evaluation worker and a readline client.
|
||||
*
|
||||
* @param string $prompt, optional
|
||||
* @param string $historyFile, optional
|
||||
*/
|
||||
public function __construct($prompt = 'boris> ', $historyFile = null) {
|
||||
$this->setPrompt($prompt);
|
||||
$this->_historyFile = $historyFile
|
||||
? $historyFile
|
||||
: sprintf('%s/.boris_history', getenv('HOME'))
|
||||
;
|
||||
$this->_inspector = new ColoredInspector();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new hook to run in the context of the REPL when it starts.
|
||||
*
|
||||
* @param mixed $hook
|
||||
*
|
||||
* The hook is either a string of PHP code to eval(), or a Closure accepting
|
||||
* the EvalWorker object as its first argument and the array of defined
|
||||
* local variables in the second argument.
|
||||
*
|
||||
* If the hook is a callback and needs to set any local variables in the
|
||||
* REPL's scope, it should invoke $worker->setLocal($var_name, $value) to
|
||||
* do so.
|
||||
*
|
||||
* Hooks are guaranteed to run in the order they were added and the state
|
||||
* set by each hook is available to the next hook (either through global
|
||||
* resources, such as classes and interfaces, or through the 2nd parameter
|
||||
* of the callback, if any local variables were set.
|
||||
*
|
||||
* @example Contrived example where one hook sets the date and another
|
||||
* prints it in the REPL.
|
||||
*
|
||||
* $boris->onStart(function($worker, $vars){
|
||||
* $worker->setLocal('date', date('Y-m-d'));
|
||||
* });
|
||||
*
|
||||
* $boris->onStart('echo "The date is $date\n";');
|
||||
*/
|
||||
public function onStart($hook) {
|
||||
$this->_startHooks[] = $hook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new hook to run in the context of the REPL when a fatal error occurs.
|
||||
*
|
||||
* @param mixed $hook
|
||||
*
|
||||
* The hook is either a string of PHP code to eval(), or a Closure accepting
|
||||
* the EvalWorker object as its first argument and the array of defined
|
||||
* local variables in the second argument.
|
||||
*
|
||||
* If the hook is a callback and needs to set any local variables in the
|
||||
* REPL's scope, it should invoke $worker->setLocal($var_name, $value) to
|
||||
* do so.
|
||||
*
|
||||
* Hooks are guaranteed to run in the order they were added and the state
|
||||
* set by each hook is available to the next hook (either through global
|
||||
* resources, such as classes and interfaces, or through the 2nd parameter
|
||||
* of the callback, if any local variables were set.
|
||||
*
|
||||
* @example An example if your project requires some database connection cleanup:
|
||||
*
|
||||
* $boris->onFailure(function($worker, $vars){
|
||||
* DB::reset();
|
||||
* });
|
||||
*/
|
||||
public function onFailure($hook){
|
||||
$this->_failureHooks[] = $hook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a local variable, or many local variables.
|
||||
*
|
||||
* @example Setting a single variable
|
||||
* $boris->setLocal('user', $bob);
|
||||
*
|
||||
* @example Setting many variables at once
|
||||
* $boris->setLocal(array('user' => $bob, 'appContext' => $appContext));
|
||||
*
|
||||
* This method can safely be invoked repeatedly.
|
||||
*
|
||||
* @param array|string $local
|
||||
* @param mixed $value, optional
|
||||
*/
|
||||
public function setLocal($local, $value = null) {
|
||||
if (!is_array($local)) {
|
||||
$local = array($local => $value);
|
||||
}
|
||||
|
||||
$this->_exports = array_merge($this->_exports, $local);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Boris prompt text
|
||||
*
|
||||
* @param string $prompt
|
||||
*/
|
||||
public function setPrompt($prompt) {
|
||||
$this->_prompt = $prompt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an Inspector object for Boris to output return values with.
|
||||
*
|
||||
* @param object $inspector any object the responds to inspect($v)
|
||||
*/
|
||||
public function setInspector($inspector) {
|
||||
$this->_inspector = $inspector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the REPL (display the readline prompt).
|
||||
*
|
||||
* This method never returns.
|
||||
*/
|
||||
public function start() {
|
||||
declare(ticks = 1);
|
||||
pcntl_signal(SIGINT, SIG_IGN, true);
|
||||
|
||||
if (!$pipes = stream_socket_pair(
|
||||
STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP)) {
|
||||
throw new \RuntimeException('Failed to create socket pair');
|
||||
}
|
||||
|
||||
$pid = pcntl_fork();
|
||||
|
||||
if ($pid > 0) {
|
||||
if (function_exists('setproctitle')) {
|
||||
setproctitle('boris (master)');
|
||||
}
|
||||
|
||||
fclose($pipes[0]);
|
||||
$client = new ReadlineClient($pipes[1]);
|
||||
$client->start($this->_prompt, $this->_historyFile);
|
||||
} elseif ($pid < 0) {
|
||||
throw new \RuntimeException('Failed to fork child process');
|
||||
} else {
|
||||
if (function_exists('setproctitle')) {
|
||||
setproctitle('boris (worker)');
|
||||
}
|
||||
|
||||
fclose($pipes[1]);
|
||||
$worker = new EvalWorker($pipes[0]);
|
||||
$worker->setLocal($this->_exports);
|
||||
$worker->setStartHooks($this->_startHooks);
|
||||
$worker->setFailureHooks($this->_failureHooks);
|
||||
$worker->setInspector($this->_inspector);
|
||||
$worker->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
85
ThinkPHP/Library/Vendor/Boris/CLIOptionsHandler.php
vendored
Normal file
85
ThinkPHP/Library/Vendor/Boris/CLIOptionsHandler.php
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/* vim: set shiftwidth=2 expandtab softtabstop=2: */
|
||||
|
||||
namespace Boris;
|
||||
|
||||
/**
|
||||
* Processes available command line flags.
|
||||
*/
|
||||
class CLIOptionsHandler {
|
||||
/**
|
||||
* Accept the REPL object and perform any setup necessary from the CLI flags.
|
||||
*
|
||||
* @param Boris $boris
|
||||
*/
|
||||
public function handle($boris) {
|
||||
$args = getopt('hvr:', array('help', 'version', 'require:'));
|
||||
|
||||
foreach ($args as $option => $value) {
|
||||
switch ($option) {
|
||||
/*
|
||||
* Sets files to load at startup, may be used multiple times,
|
||||
* i.e: boris -r test.php,foo/bar.php -r ba/foo.php --require hey.php
|
||||
*/
|
||||
case 'r':
|
||||
case 'require':
|
||||
$this->_handleRequire($boris, $value);
|
||||
break;
|
||||
|
||||
/*
|
||||
* Show Usage info
|
||||
*/
|
||||
case 'h':
|
||||
case 'help':
|
||||
$this->_handleUsageInfo();
|
||||
break;
|
||||
|
||||
/*
|
||||
* Show version
|
||||
*/
|
||||
case 'v':
|
||||
case 'version':
|
||||
$this->_handleVersion();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- Private Methods
|
||||
|
||||
private function _handleRequire($boris, $paths) {
|
||||
$require = array_reduce(
|
||||
(array) $paths,
|
||||
function($acc, $v) { return array_merge($acc, explode(',', $v)); },
|
||||
array()
|
||||
);
|
||||
|
||||
$boris->onStart(function($worker, $scope) use($require) {
|
||||
foreach($require as $path) {
|
||||
require $path;
|
||||
}
|
||||
|
||||
$worker->setLocal(get_defined_vars());
|
||||
});
|
||||
}
|
||||
|
||||
private function _handleUsageInfo() {
|
||||
echo <<<USAGE
|
||||
Usage: boris [options]
|
||||
boris is a tiny REPL for PHP
|
||||
|
||||
Options:
|
||||
-h, --help show this help message and exit
|
||||
-r, --require a comma-separated list of files to require on startup
|
||||
-v, --version show Boris version
|
||||
|
||||
USAGE;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
private function _handleVersion() {
|
||||
printf("Boris %s\n", Boris::VERSION);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
273
ThinkPHP/Library/Vendor/Boris/ColoredInspector.php
vendored
Normal file
273
ThinkPHP/Library/Vendor/Boris/ColoredInspector.php
vendored
Normal file
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
|
||||
/* vim: set shiftwidth=2 expandtab softtabstop=2: */
|
||||
|
||||
/**
|
||||
* @author Rob Morris <rob@irongaze.com>
|
||||
* @author Chris Corbyn <chris@w3style.co.uk>
|
||||
*
|
||||
* Copyright © 2013-2014 Rob Morris.
|
||||
*/
|
||||
|
||||
namespace Boris;
|
||||
|
||||
/**
|
||||
* Identifies data types in data structures and syntax highlights them.
|
||||
*/
|
||||
class ColoredInspector implements Inspector {
|
||||
static $TERM_COLORS = array(
|
||||
'black' => "\033[0;30m",
|
||||
'white' => "\033[1;37m",
|
||||
'none' => "\033[1;30m",
|
||||
'dark_grey' => "\033[1;30m",
|
||||
'light_grey' => "\033[0;37m",
|
||||
'dark_red' => "\033[0;31m",
|
||||
'light_red' => "\033[1;31m",
|
||||
'dark_green' => "\033[0;32m",
|
||||
'light_green' => "\033[1;32m",
|
||||
'dark_yellow' => "\033[0;33m",
|
||||
'light_yellow' => "\033[1;33m",
|
||||
'dark_blue' => "\033[0;34m",
|
||||
'light_blue' => "\033[1;34m",
|
||||
'dark_purple' => "\033[0;35m",
|
||||
'light_purple' => "\033[1;35m",
|
||||
'dark_cyan' => "\033[0;36m",
|
||||
'light_cyan' => "\033[1;36m",
|
||||
);
|
||||
|
||||
private $_fallback;
|
||||
private $_colorMap = array();
|
||||
|
||||
/**
|
||||
* Initialize a new ColoredInspector, using $colorMap.
|
||||
*
|
||||
* The colors should be an associative array with the keys:
|
||||
*
|
||||
* - 'integer'
|
||||
* - 'float'
|
||||
* - 'keyword'
|
||||
* - 'string'
|
||||
* - 'boolean'
|
||||
* - 'default'
|
||||
*
|
||||
* And the values, one of the following colors:
|
||||
*
|
||||
* - 'none'
|
||||
* - 'black'
|
||||
* - 'white'
|
||||
* - 'dark_grey'
|
||||
* - 'light_grey'
|
||||
* - 'dark_red'
|
||||
* - 'light_red'
|
||||
* - 'dark_green'
|
||||
* - 'light_green'
|
||||
* - 'dark_yellow'
|
||||
* - 'light_yellow'
|
||||
* - 'dark_blue'
|
||||
* - 'light_blue'
|
||||
* - 'dark_purple'
|
||||
* - 'light_purple'
|
||||
* - 'dark_cyan'
|
||||
* - 'light_cyan'
|
||||
*
|
||||
* An empty $colorMap array effectively means 'none' for all types.
|
||||
*
|
||||
* @param array $colorMap
|
||||
*/
|
||||
public function __construct($colorMap = null) {
|
||||
$this->_fallback = new DumpInspector();
|
||||
|
||||
if (isset($colorMap)) {
|
||||
$this->_colorMap = $colorMap;
|
||||
} else {
|
||||
$this->_colorMap = $this->_defaultColorMap();
|
||||
}
|
||||
}
|
||||
|
||||
public function inspect($variable) {
|
||||
return preg_replace(
|
||||
'/^/m',
|
||||
$this->_colorize('comment', '// '),
|
||||
$this->_dump($variable)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an associative array of an object's properties.
|
||||
*
|
||||
* This method is public so that subclasses may override it.
|
||||
*
|
||||
* @param object $value
|
||||
* @return array
|
||||
* */
|
||||
public function objectVars($value) {
|
||||
return get_object_vars($value);
|
||||
}
|
||||
|
||||
// -- Private Methods
|
||||
|
||||
public function _dump($value) {
|
||||
$tests = array(
|
||||
'is_null' => '_dumpNull',
|
||||
'is_string' => '_dumpString',
|
||||
'is_bool' => '_dumpBoolean',
|
||||
'is_integer' => '_dumpInteger',
|
||||
'is_float' => '_dumpFloat',
|
||||
'is_array' => '_dumpArray',
|
||||
'is_object' => '_dumpObject'
|
||||
);
|
||||
|
||||
foreach ($tests as $predicate => $outputMethod) {
|
||||
if (call_user_func($predicate, $value))
|
||||
return call_user_func(array($this, $outputMethod), $value);
|
||||
}
|
||||
|
||||
return $this->_fallback->inspect($value);
|
||||
}
|
||||
|
||||
private function _dumpNull($value) {
|
||||
return $this->_colorize('keyword', 'NULL');
|
||||
}
|
||||
|
||||
private function _dumpString($value) {
|
||||
return $this->_colorize('string', var_export($value, true));
|
||||
}
|
||||
|
||||
private function _dumpBoolean($value) {
|
||||
return $this->_colorize('bool', var_export($value, true));
|
||||
}
|
||||
|
||||
private function _dumpInteger($value) {
|
||||
return $this->_colorize('integer', var_export($value, true));
|
||||
}
|
||||
|
||||
private function _dumpFloat($value) {
|
||||
return $this->_colorize('float', var_export($value, true));
|
||||
}
|
||||
|
||||
private function _dumpArray($value) {
|
||||
return $this->_dumpStructure('array', $value);
|
||||
}
|
||||
|
||||
private function _dumpObject($value) {
|
||||
return $this->_dumpStructure(
|
||||
sprintf('object(%s)', get_class($value)),
|
||||
$this->objectVars($value)
|
||||
);
|
||||
}
|
||||
|
||||
private function _dumpStructure($type, $value) {
|
||||
return $this->_astToString($this->_buildAst($type, $value));
|
||||
}
|
||||
|
||||
public function _buildAst($type, $value, $seen = array()) {
|
||||
// FIXME: Improve this AST so it doesn't require access to dump() or colorize()
|
||||
if ($this->_isSeen($value, $seen)) {
|
||||
return $this->_colorize('default', '*** RECURSION ***');
|
||||
} else {
|
||||
$nextSeen = array_merge($seen, array($value));
|
||||
}
|
||||
|
||||
if (is_object($value)) {
|
||||
$vars = $this->objectVars($value);
|
||||
} else {
|
||||
$vars = $value;
|
||||
}
|
||||
|
||||
$self = $this;
|
||||
|
||||
return array(
|
||||
'name' => $this->_colorize('keyword', $type),
|
||||
'children' => empty($vars) ? array() : array_combine(
|
||||
array_map(array($this, '_dump'), array_keys($vars)),
|
||||
array_map(
|
||||
function($v) use($self, $nextSeen) {
|
||||
if (is_object($v)) {
|
||||
return $self->_buildAst(
|
||||
sprintf('object(%s)', get_class($v)),
|
||||
$v,
|
||||
$nextSeen
|
||||
);
|
||||
} elseif (is_array($v)) {
|
||||
return $self->_buildAst('array', $v, $nextSeen);
|
||||
} else {
|
||||
return $self->_dump($v);
|
||||
}
|
||||
},
|
||||
array_values($vars)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function _astToString($node, $indent = 0) {
|
||||
$children = $node['children'];
|
||||
$self = $this;
|
||||
|
||||
return implode(
|
||||
"\n",
|
||||
array(
|
||||
sprintf('%s(', $node['name']),
|
||||
implode(
|
||||
",\n",
|
||||
array_map(
|
||||
function($k) use($self, $children, $indent) {
|
||||
if (is_array($children[$k])) {
|
||||
return sprintf(
|
||||
'%s%s => %s',
|
||||
str_repeat(' ', ($indent + 1) * 2),
|
||||
$k,
|
||||
$self->_astToString($children[$k], $indent + 1)
|
||||
);
|
||||
} else {
|
||||
return sprintf(
|
||||
'%s%s => %s',
|
||||
str_repeat(' ', ($indent + 1) * 2),
|
||||
$k,
|
||||
$children[$k]
|
||||
);
|
||||
}
|
||||
},
|
||||
array_keys($children)
|
||||
)
|
||||
),
|
||||
sprintf('%s)', str_repeat(' ', $indent * 2))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private function _defaultColorMap() {
|
||||
return array(
|
||||
'integer' => 'light_green',
|
||||
'float' => 'light_yellow',
|
||||
'string' => 'light_red',
|
||||
'bool' => 'light_purple',
|
||||
'keyword' => 'light_cyan',
|
||||
'comment' => 'dark_grey',
|
||||
'default' => 'none'
|
||||
);
|
||||
}
|
||||
|
||||
private function _colorize($type, $value) {
|
||||
if (!empty($this->_colorMap[$type])) {
|
||||
$colorName = $this->_colorMap[$type];
|
||||
} else {
|
||||
$colorName = $this->_colorMap['default'];
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
"%s%s\033[0m",
|
||||
static::$TERM_COLORS[$colorName],
|
||||
$value
|
||||
);
|
||||
}
|
||||
|
||||
private function _isSeen($value, $seen) {
|
||||
foreach ($seen as $v) {
|
||||
if ($v === $value)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
85
ThinkPHP/Library/Vendor/Boris/Config.php
vendored
Normal file
85
ThinkPHP/Library/Vendor/Boris/Config.php
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/* vim: set shiftwidth=2 expandtab softtabstop=2: */
|
||||
|
||||
namespace Boris;
|
||||
|
||||
/**
|
||||
* Config handles loading configuration files for boris
|
||||
*/
|
||||
class Config {
|
||||
private $_searchPaths;
|
||||
private $_cascade = false;
|
||||
private $_files = array();
|
||||
|
||||
/**
|
||||
* Create a new Config instance, optionally with an array
|
||||
* of paths to search for configuration files.
|
||||
*
|
||||
* Additionally, if the second, optional boolean argument is
|
||||
* true, all existing configuration files will be loaded, and
|
||||
* effectively merged.
|
||||
*
|
||||
* @param array $searchPaths
|
||||
* @param bool $cascade
|
||||
*/
|
||||
public function __construct($searchPaths = null, $cascade = false) {
|
||||
if (is_null($searchPaths)) {
|
||||
$searchPaths = array();
|
||||
|
||||
if ($userHome = getenv('HOME')) {
|
||||
$searchPaths[] = "{$userHome}/.borisrc";
|
||||
}
|
||||
|
||||
$searchPaths[] = getcwd() . '/.borisrc';
|
||||
}
|
||||
|
||||
$this->_cascade = $cascade;
|
||||
$this->_searchPaths = $searchPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for configuration files in the available
|
||||
* search paths, and applies them to the provided
|
||||
* boris instance.
|
||||
*
|
||||
* Returns true if any configuration files were found.
|
||||
*
|
||||
* @param Boris\Boris $boris
|
||||
* @return bool
|
||||
*/
|
||||
public function apply(Boris $boris) {
|
||||
$applied = false;
|
||||
|
||||
foreach($this->_searchPaths as $path) {
|
||||
if (is_readable($path)) {
|
||||
$this->_loadInIsolation($path, $boris);
|
||||
|
||||
$applied = true;
|
||||
$this->_files[] = $path;
|
||||
|
||||
if (!$this->_cascade) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $applied;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of files that were loaded
|
||||
* for this Config
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function loadedFiles() {
|
||||
return $this->_files;
|
||||
}
|
||||
|
||||
// -- Private Methods
|
||||
|
||||
private function _loadInIsolation($path, $boris) {
|
||||
require $path;
|
||||
}
|
||||
}
|
||||
16
ThinkPHP/Library/Vendor/Boris/DumpInspector.php
vendored
Normal file
16
ThinkPHP/Library/Vendor/Boris/DumpInspector.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/* vim: set shiftwidth=2 expandtab softtabstop=2: */
|
||||
|
||||
namespace Boris;
|
||||
|
||||
/**
|
||||
* Passes values through var_dump() to inspect them.
|
||||
*/
|
||||
class DumpInspector implements Inspector {
|
||||
public function inspect($variable) {
|
||||
ob_start();
|
||||
var_dump($variable);
|
||||
return sprintf(" → %s", trim(ob_get_clean()));
|
||||
}
|
||||
}
|
||||
247
ThinkPHP/Library/Vendor/Boris/EvalWorker.php
vendored
Normal file
247
ThinkPHP/Library/Vendor/Boris/EvalWorker.php
vendored
Normal file
@@ -0,0 +1,247 @@
|
||||
<?php
|
||||
|
||||
/* vim: set shiftwidth=2 expandtab softtabstop=2: */
|
||||
|
||||
namespace Boris;
|
||||
|
||||
/**
|
||||
* EvalWorker is responsible for evaluating PHP expressions in forked processes.
|
||||
*/
|
||||
class EvalWorker {
|
||||
const ABNORMAL_EXIT = 255;
|
||||
const DONE = "\0";
|
||||
const EXITED = "\1";
|
||||
const FAILED = "\2";
|
||||
const READY = "\3";
|
||||
|
||||
private $_socket;
|
||||
private $_exports = array();
|
||||
private $_startHooks = array();
|
||||
private $_failureHooks = array();
|
||||
private $_ppid;
|
||||
private $_pid;
|
||||
private $_cancelled;
|
||||
private $_inspector;
|
||||
private $_exceptionHandler;
|
||||
|
||||
/**
|
||||
* Create a new worker using the given socket for communication.
|
||||
*
|
||||
* @param resource $socket
|
||||
*/
|
||||
public function __construct($socket) {
|
||||
$this->_socket = $socket;
|
||||
$this->_inspector = new DumpInspector();
|
||||
stream_set_blocking($socket, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set local variables to be placed in the workers's scope.
|
||||
*
|
||||
* @param array|string $local
|
||||
* @param mixed $value, if $local is a string
|
||||
*/
|
||||
public function setLocal($local, $value = null) {
|
||||
if (!is_array($local)) {
|
||||
$local = array($local => $value);
|
||||
}
|
||||
|
||||
$this->_exports = array_merge($this->_exports, $local);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set hooks to run inside the worker before it starts looping.
|
||||
*
|
||||
* @param array $hooks
|
||||
*/
|
||||
public function setStartHooks($hooks) {
|
||||
$this->_startHooks = $hooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set hooks to run inside the worker after a fatal error is caught.
|
||||
*
|
||||
* @param array $hooks
|
||||
*/
|
||||
public function setFailureHooks($hooks) {
|
||||
$this->_failureHooks = $hooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an Inspector object for Boris to output return values with.
|
||||
*
|
||||
* @param object $inspector any object the responds to inspect($v)
|
||||
*/
|
||||
public function setInspector($inspector) {
|
||||
$this->_inspector = $inspector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the worker.
|
||||
*
|
||||
* This method never returns.
|
||||
*/
|
||||
public function start() {
|
||||
$__scope = $this->_runHooks($this->_startHooks);
|
||||
extract($__scope);
|
||||
|
||||
$this->_write($this->_socket, self::READY);
|
||||
|
||||
/* Note the naming of the local variables due to shared scope with the user here */
|
||||
for (;;) {
|
||||
declare(ticks = 1);
|
||||
// don't exit on ctrl-c
|
||||
pcntl_signal(SIGINT, SIG_IGN, true);
|
||||
|
||||
$this->_cancelled = false;
|
||||
|
||||
$__input = $this->_transform($this->_read($this->_socket));
|
||||
|
||||
if ($__input === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$__response = self::DONE;
|
||||
|
||||
$this->_ppid = posix_getpid();
|
||||
$this->_pid = pcntl_fork();
|
||||
|
||||
if ($this->_pid < 0) {
|
||||
throw new \RuntimeException('Failed to fork child labourer');
|
||||
} elseif ($this->_pid > 0) {
|
||||
// kill the child on ctrl-c
|
||||
pcntl_signal(SIGINT, array($this, 'cancelOperation'), true);
|
||||
pcntl_waitpid($this->_pid, $__status);
|
||||
|
||||
if (!$this->_cancelled && $__status != (self::ABNORMAL_EXIT << 8)) {
|
||||
$__response = self::EXITED;
|
||||
} else {
|
||||
$this->_runHooks($this->_failureHooks);
|
||||
$__response = self::FAILED;
|
||||
}
|
||||
} else {
|
||||
// user exception handlers normally cause a clean exit, so Boris will exit too
|
||||
if (!$this->_exceptionHandler =
|
||||
set_exception_handler(array($this, 'delegateExceptionHandler'))) {
|
||||
restore_exception_handler();
|
||||
}
|
||||
|
||||
// undo ctrl-c signal handling ready for user code execution
|
||||
pcntl_signal(SIGINT, SIG_DFL, true);
|
||||
$__pid = posix_getpid();
|
||||
|
||||
$__result = eval($__input);
|
||||
|
||||
if (posix_getpid() != $__pid) {
|
||||
// whatever the user entered caused a forked child
|
||||
// (totally valid, but we don't want that child to loop and wait for input)
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (preg_match('/\s*return\b/i', $__input)) {
|
||||
fwrite(STDOUT, sprintf("%s\n", $this->_inspector->inspect($__result)));
|
||||
}
|
||||
$this->_expungeOldWorker();
|
||||
}
|
||||
|
||||
$this->_write($this->_socket, $__response);
|
||||
|
||||
if ($__response == self::EXITED) {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* While a child process is running, terminate it immediately.
|
||||
*/
|
||||
public function cancelOperation() {
|
||||
printf("Cancelling...\n");
|
||||
$this->_cancelled = true;
|
||||
posix_kill($this->_pid, SIGKILL);
|
||||
pcntl_signal_dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* If any user-defined exception handler is present, call it, but be sure to exit correctly.
|
||||
*/
|
||||
public function delegateExceptionHandler($ex) {
|
||||
call_user_func($this->_exceptionHandler, $ex);
|
||||
exit(self::ABNORMAL_EXIT);
|
||||
}
|
||||
|
||||
// -- Private Methods
|
||||
|
||||
private function _runHooks($hooks) {
|
||||
extract($this->_exports);
|
||||
|
||||
foreach ($hooks as $__hook) {
|
||||
if (is_string($__hook)) {
|
||||
eval($__hook);
|
||||
} elseif (is_callable($__hook)) {
|
||||
call_user_func($__hook, $this, get_defined_vars());
|
||||
} else {
|
||||
throw new \RuntimeException(
|
||||
sprintf(
|
||||
'Hooks must be closures or strings of PHP code. Got [%s].',
|
||||
gettype($__hook)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// hooks may set locals
|
||||
extract($this->_exports);
|
||||
}
|
||||
|
||||
return get_defined_vars();
|
||||
}
|
||||
|
||||
private function _expungeOldWorker() {
|
||||
posix_kill($this->_ppid, SIGTERM);
|
||||
pcntl_signal_dispatch();
|
||||
}
|
||||
|
||||
private function _write($socket, $data) {
|
||||
if (!fwrite($socket, $data)) {
|
||||
throw new \RuntimeException('Socket error: failed to write data');
|
||||
}
|
||||
}
|
||||
|
||||
private function _read($socket)
|
||||
{
|
||||
$read = array($socket);
|
||||
$except = array($socket);
|
||||
|
||||
if ($this->_select($read, $except) > 0) {
|
||||
if ($read) {
|
||||
return stream_get_contents($read[0]);
|
||||
} else if ($except) {
|
||||
throw new \UnexpectedValueException("Socket error: closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function _select(&$read, &$except) {
|
||||
$write = null;
|
||||
set_error_handler(function(){return true;}, E_WARNING);
|
||||
$result = stream_select($read, $write, $except, 10);
|
||||
restore_error_handler();
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function _transform($input) {
|
||||
if ($input === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$transforms = array(
|
||||
'exit' => 'exit(0)'
|
||||
);
|
||||
|
||||
foreach ($transforms as $from => $to) {
|
||||
$input = preg_replace('/^\s*' . preg_quote($from, '/') . '\s*;?\s*$/', $to . ';', $input);
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
}
|
||||
14
ThinkPHP/Library/Vendor/Boris/ExportInspector.php
vendored
Normal file
14
ThinkPHP/Library/Vendor/Boris/ExportInspector.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
/* vim: set shiftwidth=2 expandtab softtabstop=2: */
|
||||
|
||||
namespace Boris;
|
||||
|
||||
/**
|
||||
* Passes values through var_export() to inspect them.
|
||||
*/
|
||||
class ExportInspector implements Inspector {
|
||||
public function inspect($variable) {
|
||||
return sprintf(" → %s", var_export($variable, true));
|
||||
}
|
||||
}
|
||||
19
ThinkPHP/Library/Vendor/Boris/Inspector.php
vendored
Normal file
19
ThinkPHP/Library/Vendor/Boris/Inspector.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/* vim: set shiftwidth=2 expandtab softtabstop=2: */
|
||||
|
||||
namespace Boris;
|
||||
|
||||
/**
|
||||
* Something that is capable of returning a useful representation of a variable.
|
||||
*/
|
||||
interface Inspector {
|
||||
/**
|
||||
* Return a debug-friendly string representation of $variable.
|
||||
*
|
||||
* @param mixed $variable
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function inspect($variable);
|
||||
}
|
||||
109
ThinkPHP/Library/Vendor/Boris/ReadlineClient.php
vendored
Normal file
109
ThinkPHP/Library/Vendor/Boris/ReadlineClient.php
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
/* vim: set shiftwidth=2 expandtab softtabstop=2: */
|
||||
|
||||
namespace Boris;
|
||||
|
||||
/**
|
||||
* The Readline client is what the user spends their time entering text into.
|
||||
*
|
||||
* Input is collected and sent to {@link \Boris\EvalWorker} for processing.
|
||||
*/
|
||||
class ReadlineClient {
|
||||
private $_socket;
|
||||
private $_prompt;
|
||||
private $_historyFile;
|
||||
private $_clear = false;
|
||||
|
||||
/**
|
||||
* Create a new ReadlineClient using $socket for communication.
|
||||
*
|
||||
* @param resource $socket
|
||||
*/
|
||||
public function __construct($socket) {
|
||||
$this->_socket = $socket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the client with an prompt and readline history path.
|
||||
*
|
||||
* This method never returns.
|
||||
*
|
||||
* @param string $prompt
|
||||
* @param string $historyFile
|
||||
*/
|
||||
public function start($prompt, $historyFile) {
|
||||
readline_read_history($historyFile);
|
||||
|
||||
declare(ticks = 1);
|
||||
pcntl_signal(SIGCHLD, SIG_IGN);
|
||||
pcntl_signal(SIGINT, array($this, 'clear'), true);
|
||||
|
||||
// wait for the worker to finish executing hooks
|
||||
if (fread($this->_socket, 1) != EvalWorker::READY) {
|
||||
throw new \RuntimeException('EvalWorker failed to start');
|
||||
}
|
||||
|
||||
$parser = new ShallowParser();
|
||||
$buf = '';
|
||||
$lineno = 1;
|
||||
|
||||
for (;;) {
|
||||
$this->_clear = false;
|
||||
$line = readline(
|
||||
sprintf(
|
||||
'[%d] %s',
|
||||
$lineno,
|
||||
($buf == ''
|
||||
? $prompt
|
||||
: str_pad('*> ', strlen($prompt), ' ', STR_PAD_LEFT))
|
||||
)
|
||||
);
|
||||
|
||||
if ($this->_clear) {
|
||||
$buf = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (false === $line) {
|
||||
$buf = 'exit(0);'; // ctrl-d acts like exit
|
||||
}
|
||||
|
||||
if (strlen($line) > 0) {
|
||||
readline_add_history($line);
|
||||
}
|
||||
|
||||
$buf .= sprintf("%s\n", $line);
|
||||
|
||||
if ($statements = $parser->statements($buf)) {
|
||||
++$lineno;
|
||||
|
||||
$buf = '';
|
||||
foreach ($statements as $stmt) {
|
||||
if (false === $written = fwrite($this->_socket, $stmt)) {
|
||||
throw new \RuntimeException('Socket error: failed to write data');
|
||||
}
|
||||
|
||||
if ($written > 0) {
|
||||
$status = fread($this->_socket, 1);
|
||||
if ($status == EvalWorker::EXITED) {
|
||||
readline_write_history($historyFile);
|
||||
echo "\n";
|
||||
exit(0);
|
||||
} elseif ($status == EvalWorker::FAILED) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the input buffer.
|
||||
*/
|
||||
public function clear() {
|
||||
// FIXME: I'd love to have this send \r to readline so it puts the user on a blank line
|
||||
$this->_clear = true;
|
||||
}
|
||||
}
|
||||
233
ThinkPHP/Library/Vendor/Boris/ShallowParser.php
vendored
Normal file
233
ThinkPHP/Library/Vendor/Boris/ShallowParser.php
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
|
||||
/* vim: set shiftwidth=2 expandtab softtabstop=2: */
|
||||
|
||||
namespace Boris;
|
||||
|
||||
/**
|
||||
* The ShallowParser takes whatever is currently buffered and chunks it into individual statements.
|
||||
*/
|
||||
class ShallowParser {
|
||||
private $_pairs = array(
|
||||
'(' => ')',
|
||||
'{' => '}',
|
||||
'[' => ']',
|
||||
'"' => '"',
|
||||
"'" => "'",
|
||||
'//' => "\n",
|
||||
'#' => "\n",
|
||||
'/*' => '*/',
|
||||
'<<<' => '_heredoc_special_case_'
|
||||
);
|
||||
|
||||
private $_initials;
|
||||
|
||||
public function __construct() {
|
||||
$this->_initials = '/^(' . implode('|', array_map(array($this, 'quote'), array_keys($this->_pairs))) . ')/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Break the $buffer into chunks, with one for each highest-level construct possible.
|
||||
*
|
||||
* If the buffer is incomplete, returns an empty array.
|
||||
*
|
||||
* @param string $buffer
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function statements($buffer) {
|
||||
$result = $this->_createResult($buffer);
|
||||
|
||||
while (strlen($result->buffer) > 0) {
|
||||
$this->_resetResult($result);
|
||||
|
||||
if ($result->state == '<<<') {
|
||||
if (!$this->_initializeHeredoc($result)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$rules = array('_scanEscapedChar', '_scanRegion', '_scanStateEntrant', '_scanWsp', '_scanChar');
|
||||
|
||||
foreach ($rules as $method) {
|
||||
if ($this->$method($result)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($result->stop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($result->statements) && trim($result->stmt) === '' && strlen($result->buffer) == 0) {
|
||||
$this->_combineStatements($result);
|
||||
$this->_prepareForDebug($result);
|
||||
return $result->statements;
|
||||
}
|
||||
}
|
||||
|
||||
public function quote($token) {
|
||||
return preg_quote($token, '/');
|
||||
}
|
||||
|
||||
// -- Private Methods
|
||||
|
||||
private function _createResult($buffer) {
|
||||
$result = new \stdClass();
|
||||
$result->buffer = $buffer;
|
||||
$result->stmt = '';
|
||||
$result->state = null;
|
||||
$result->states = array();
|
||||
$result->statements = array();
|
||||
$result->stop = false;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function _resetResult($result) {
|
||||
$result->stop = false;
|
||||
$result->state = end($result->states);
|
||||
$result->terminator = $result->state
|
||||
? '/^(.*?' . preg_quote($this->_pairs[$result->state], '/') . ')/s'
|
||||
: null
|
||||
;
|
||||
}
|
||||
|
||||
private function _combineStatements($result) {
|
||||
$combined = array();
|
||||
|
||||
foreach ($result->statements as $scope) {
|
||||
if (trim($scope) == ';' || substr(trim($scope), -1) != ';') {
|
||||
$combined[] = ((string) array_pop($combined)) . $scope;
|
||||
} else {
|
||||
$combined[] = $scope;
|
||||
}
|
||||
}
|
||||
|
||||
$result->statements = $combined;
|
||||
}
|
||||
|
||||
private function _prepareForDebug($result) {
|
||||
$result->statements []= $this->_prepareDebugStmt(array_pop($result->statements));
|
||||
}
|
||||
|
||||
private function _initializeHeredoc($result) {
|
||||
if (preg_match('/^([\'"]?)([a-z_][a-z0-9_]*)\\1/i', $result->buffer, $match)) {
|
||||
$docId = $match[2];
|
||||
$result->stmt .= $match[0];
|
||||
$result->buffer = substr($result->buffer, strlen($match[0]));
|
||||
|
||||
$result->terminator = '/^(.*?\n' . $docId . ');?\n/s';
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function _scanWsp($result) {
|
||||
if (preg_match('/^\s+/', $result->buffer, $match)) {
|
||||
if (!empty($result->statements) && $result->stmt === '') {
|
||||
$result->statements[] = array_pop($result->statements) . $match[0];
|
||||
} else {
|
||||
$result->stmt .= $match[0];
|
||||
}
|
||||
$result->buffer = substr($result->buffer, strlen($match[0]));
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function _scanEscapedChar($result) {
|
||||
if (($result->state == '"' || $result->state == "'")
|
||||
&& preg_match('/^[^' . $result->state . ']*?\\\\./s', $result->buffer, $match)) {
|
||||
|
||||
$result->stmt .= $match[0];
|
||||
$result->buffer = substr($result->buffer, strlen($match[0]));
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function _scanRegion($result) {
|
||||
if (in_array($result->state, array('"', "'", '<<<', '//', '#', '/*'))) {
|
||||
if (preg_match($result->terminator, $result->buffer, $match)) {
|
||||
$result->stmt .= $match[1];
|
||||
$result->buffer = substr($result->buffer, strlen($match[1]));
|
||||
array_pop($result->states);
|
||||
} else {
|
||||
$result->stop = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function _scanStateEntrant($result) {
|
||||
if (preg_match($this->_initials, $result->buffer, $match)) {
|
||||
$result->stmt .= $match[0];
|
||||
$result->buffer = substr($result->buffer, strlen($match[0]));
|
||||
$result->states[] = $match[0];
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function _scanChar($result) {
|
||||
$chr = substr($result->buffer, 0, 1);
|
||||
$result->stmt .= $chr;
|
||||
$result->buffer = substr($result->buffer, 1);
|
||||
if ($result->state && $chr == $this->_pairs[$result->state]) {
|
||||
array_pop($result->states);
|
||||
}
|
||||
|
||||
if (empty($result->states) && ($chr == ';' || $chr == '}')) {
|
||||
if (!$this->_isLambda($result->stmt) || $chr == ';') {
|
||||
$result->statements[] = $result->stmt;
|
||||
$result->stmt = '';
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function _isLambda($input) {
|
||||
return preg_match(
|
||||
'/^([^=]*?=\s*)?function\s*\([^\)]*\)\s*(use\s*\([^\)]*\)\s*)?\s*\{.*\}\s*;?$/is',
|
||||
trim($input)
|
||||
);
|
||||
}
|
||||
|
||||
private function _isReturnable($input) {
|
||||
$input = trim($input);
|
||||
if (substr($input, -1) == ';' && substr($input, 0, 1) != '{') {
|
||||
return $this->_isLambda($input) || !preg_match(
|
||||
'/^(' .
|
||||
'echo|print|exit|die|goto|global|include|include_once|require|require_once|list|' .
|
||||
'return|do|for|foreach|while|if|function|namespace|class|interface|abstract|switch|' .
|
||||
'declare|throw|try|unset' .
|
||||
')\b/i',
|
||||
$input
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function _prepareDebugStmt($input) {
|
||||
if ($this->_isReturnable($input) && !preg_match('/^\s*return/i', $input)) {
|
||||
$input = sprintf('return %s', $input);
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user