diff --git a/Module.php b/Module.php index ec526e9..99d447b 100644 --- a/Module.php +++ b/Module.php @@ -45,6 +45,13 @@ class Module extends \yii\base\Module */ public $yiiScript = '@app/yii'; + /** + * @var string path to the yii console app configuration file + * used to run the yii command in the same process as the web application's + * if so chosen. + */ + public $consoleConfig = '@app/config/console.php'; + /** * @var array the list of IPs that are allowed to access this module. * Each array element represents a single IP filter which can be either an IP address diff --git a/controllers/DefaultController.php b/controllers/DefaultController.php index 204e512..216cea3 100644 --- a/controllers/DefaultController.php +++ b/controllers/DefaultController.php @@ -50,11 +50,71 @@ public function actionRpc() switch ($options['method']) { case 'yii': - list ($status, $output) = $this->runConsole(implode(' ', $options['params'])); - return ['result' => $output]; + $exitCode = 1; + $output = null; + $params = explode(' ', $options['params']); + + if (!empty($params) and in_array($params[0], ['-wp', '--runInWebProcess'])) { + array_shift($params); // remove runInWebProcess option parameter + + // mimicking yii command behaviour + // just typing 'yii' in console defaults to 'yii help' + if (empty($params)) + $params[] = 'help'; + + list($exitCode, $output) = $this->runYiiConsoleCommandsInWebProcess($params); + } + else + list ($exitCode, $output) = $this->runConsole(implode(' ', $params)); + + return [ + 'exitCode' => $exitCode, + 'output' => $output + ]; } } + // popen might be disabled for security reasons in the php.ini. + // this is common on shared hosting. + // read http://www.cyberciti.biz/faq/linux-unix-apache-lighttpd-phpini-disable-functions/ + // refer to http://php.net/manual/en/ini.core.php#ini.disable-functions + private function runYiiConsoleCommandsInWebProcess(array $requestParams) + { + // remember current web app + // inspired by https://github.com/tebazil/yii2-console-runner/blob/master/src/ConsoleCommandRunner.php + $webApp = Yii::$app; + + try { + //use console request to resolve command and arguments + $request = new \yii\console\Request; + $request->setParams($requestParams); + list ($route, $params) = $request->resolve(); + + /* redefine STDOUT and STDERR streams + to write to the memory stream which is readable. + this might cause a PHP Notice because of global constant + redefinition if they're previously defined or this method + is called mor than once per HTTP request. */ + define('STDOUT', fopen('php://memory', 'w+')); + define('STDERR', STDOUT); + ob_start(); // aditionally buffer output, echo() and printf() write to this + // set app context + Yii::$app = new \yii\console\Application(require(Yii::getAlias($this->module->consoleConfig))); + $exitCode = Yii::$app->runAction($route, $params); + rewind(STDOUT); + $output = stream_get_contents(STDOUT); // whatever has been written to STDOUT and STDERR + $output .= ob_get_clean(); // and what has been echoed and printed + fclose(STDOUT); + } + catch (\Exception $ex) { + $output = $webApp->errorHandler->convertExceptionToString($ex); + $exitCode = 1; + } + + Yii::$app = $webApp; // revert to web app context + return [$exitCode, $output]; + } + /** * Runs console command * diff --git a/views/default/index.php b/views/default/index.php index 7a76de6..f7b7c8a 100644 --- a/views/default/index.php +++ b/views/default/index.php @@ -18,17 +18,21 @@ webshell.terminal( function(command, term) { if (command.indexOf('yii') === 0 || command.indexOf('yii') === 3) { - $.jrpc('{$endpoint}', 'yii', [command.replace(/^yii ?/, '')], function(json) { - term.echo(json.result); + $.jrpc('{$endpoint}', 'yii', command.replace(/^yii ?/, ''), function(json) { + term.echo(json.output); scrollDown(); }); } else if (command === 'help') { term.echo('Available commands are:'); term.echo(''); - term.echo("clear\tClear console"); - term.echo('help\tThis help text'); - term.echo('yii\tyii command'); - term.echo('quit\tQuit web shell'); + term.echo('help\t This help text'); + term.echo('clear\tClear console'); + term.echo('quit\t Quit web shell'); + term.echo('yii [-wp|--runInWebProcess] '); + term.echo('\tcommand \t\t\t\t A yii console command. Type "yii help" to list available commands.'); + term.echo('\t-wp|--runInWebProcess\tRun command in the web application process.'); + term.echo('\t\t\t\t\t\t\t Does not require creating a background process via "popen",'); + term.echo('\t\t\t\t\t\t\t a function which might be disabled in your PHP config.'); scrollDown(); } else if (command === 'quit') { var exitUrl = '{$quitUrl}';