wiki.techinc.nl/maintenance/shell.php
Antoine Musso 7671b2b71e Drop in replacement of eval.php based on psysh
eval.php is meant to eval() commands in MediaWiki global scope. We had
at least a couple attempts to move it to a regular Maintenance script.

As noted on the revert commit b475e930 r54839, using a Maintenance
script drop us in the callee function scope instead of the global scope
which is a hard requirement.

http://psysh.org/ is a Read-Eval-Print-Loop with color highlight, modern
code and more.

Add maintenance/shell.php script as MediaWikiShell class.

Passing command from stdin is supported.
Execution is forked for graceful handling of PHP fatal errors.
Colors!!
Support the undocumented '-d' arguments from eval.php:
  0 set $wgDebugLogFile to stdout. Eval.php used /dev/stdout, make it
    php://stdout in the new script.
  1 additionally set DBO_DEBUG on all the database load balancer servers

PHP globals have to be whitelisted which is not supported out of the box
by Psy. Upon request, the author of PsySh, Justin Hileman, kindly
provided a pass to inject globals (with the exceptions of super
globals). He agreed for code reuse:
    https://github.com/bobthecow/psysh/issues/353
The code was added to maintenance/CodeCleanerGlobalsPass.inc

Note that this is not a perfect simulation of global scope (but pretty
close): variables declared in the shell which did not exist before
won't be global unless the 'global' keword is used.

Example usage:

$ php maintenance/shell.php

>>> $wgFullyInitialised
=> true

>>> $hashar = User::newFromName( 'hashar' )
=> User {#274
     +mId: null,
     +mName: "Hashar",
     ...

>>> ls --long --all $h
<descriptive output of all object properties/methods>

Bug: T117661
Signed-off-by: Justin Hileman <justin@justinhileman.info>
Signed-off-by: Gergő Tisza <tgr.huwiki@gmail.com>
Change-Id: I3d6d42e138d3cc4a0aaafdd7f5f97cb17d8b8cb3
2017-02-13 08:34:38 +00:00

102 lines
3 KiB
PHP

<?php
/**
* Modern interactive shell within the MediaWiki engine.
*
* Merely wraps around http://psysh.org/ and drop an interactive PHP shell in
* the global scope.
*
* Copyright © 2017 Antoine Musso <hashar@free.fr>
* Copyright © 2017 Gergő Tisza <tgr.huwiki@gmail.com>
* Copyright © 2017 Justin Hileman <justin@justinhileman.info>
* Copyright © 2017 Wikimedia Foundation Inc.
* https://www.mediawiki.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup Maintenance
*
* @author Antoine Musso <hashar@free.fr>
* @author Justin Hileman <justin@justinhileman.info>
* @author Gergő Tisza <tgr.huwiki@gmail.com>
*/
require_once __DIR__ . '/Maintenance.php';
/**
* Interactive shell with completion and global scope.
*
*/
class MediaWikiShell extends Maintenance {
public function __construct() {
parent::__construct();
$this->addOption( 'd',
'For back compatibility with eval.php. ' .
'0 send debug to stdout. ' .
'With 1 additionally initialize database with debugging ',
false, true
);
}
public function execute() {
if ( !class_exists( \Psy\Shell::class ) ) {
$this->error( 'PsySH not found. Please run composer with the --dev option.', 1 );
}
$traverser = new \PhpParser\NodeTraverser();
$codeCleaner = new \Psy\CodeCleaner( null, null, $traverser );
// add this after initializing the code cleaner so all the default passes get added first
$traverser->addVisitor( new CodeCleanerGlobalsPass() );
$config = new \Psy\Configuration( [ 'codeCleaner' => $codeCleaner ] );
$config->setUpdateCheck( \Psy\VersionUpdater\Checker::NEVER );
$shell = new \Psy\Shell( $config );
if ( $this->hasOption( 'd' ) ) {
$this->setupLegacy();
}
$shell->run();
}
/**
* For back compatibility with eval.php
*/
protected function setupLegacy() {
global $wgDebugLogFile;
$d = intval( $this->getOption( 'd' ) );
if ( $d > 0 ) {
$wgDebugLogFile = 'php://stdout';
}
if ( $d > 1 ) {
# Set DBO_DEBUG (equivalent of $wgDebugDumpSql)
# XXX copy pasted from eval.php :(
$lb = wfGetLB();
$serverCount = $lb->getServerCount();
for ( $i = 0; $i < $serverCount; $i++ ) {
$server = $lb->getServerInfo( $i );
$server['flags'] |= DBO_DEBUG;
$lb->setServerInfo( $i, $server );
}
}
}
}
$maintClass = 'MediaWikiShell';
require_once RUN_MAINTENANCE_IF_MAIN;