Pastakhov has uploaded a new change for review. https://gerrit.wikimedia.org/r/56366
Change subject: Add php interpreter and runtime (version 0.0.1) ...................................................................... Add php interpreter and runtime (version 0.0.1) works only command 'echo' with strings and variables. Change-Id: I062ac9e6084e4afec15c82ae655d943ca217ed45 --- A Foxway.body.php A Foxway.i18n.magic.php A Foxway.i18n.php A Foxway.php A includes/Interpreter.php A includes/Runtime.php 6 files changed, 504 insertions(+), 0 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Foxway refs/changes/66/56366/1 diff --git a/Foxway.body.php b/Foxway.body.php new file mode 100644 index 0000000..0867415 --- /dev/null +++ b/Foxway.body.php @@ -0,0 +1,46 @@ +<?php +/** + * Main class of Foxway extension. + * + * @file Foxway.body.php + * @ingroup Foxway + * @author Pavel Astakhov <[email protected]> + * @licence GNU General Public Licence 2.0 or later + */ +class Foxway { + + /** + * Render function used in hook ParserFirstCallInit + * + * @param Parser $parser + * @return string + */ + public static function renderParserFunction(Parser &$parser) { + $params = func_get_args(); + array_shift( $params ); + + if( count($params) < 2 ) { + return '<span class="error">' . wfMessage( 'foxway-not-enough-parameters' )->escaped() . '</span>'; + } + + $action = strtolower( $params[0] ); + switch ($action) { + case 'set': + $matches = array(); + if( preg_match('/^\s*([^=]+)\s*=\s*(.+)\s*$/si', $params[1], &$matches) ) { + $propertyName = $matches[1]; + $propertyValue = $matches[2]; + return \Foxway\ORM::SetProperty($propertyName, $propertyValue); + break; + } + break; + default: + return '<span class="error">' . wfMessage( 'foxway-unknown-action', $action )->escaped() . '</span>'; + break; + } + } + + public static function render($input, array $args, Parser $parser, PPFrame $frame) { + return Foxway\Interpreter::run($input); + } +} \ No newline at end of file diff --git a/Foxway.i18n.magic.php b/Foxway.i18n.magic.php new file mode 100644 index 0000000..b29eb7b --- /dev/null +++ b/Foxway.i18n.magic.php @@ -0,0 +1,17 @@ +<?php +/** + * Internationalization file for the magic word of the Foxway extension. + * + * @file Foxway.i18n.magic.php + * @ingroup Foxway + * @author Pavel Astakhov <[email protected]> + */ + +$magicWords = array(); + +/** English + * @author pastakhov + */ +$magicWords['en'] = array( + 'foxway' => array( 0, 'foxway' ), +); diff --git a/Foxway.i18n.php b/Foxway.i18n.php new file mode 100644 index 0000000..d915ab0 --- /dev/null +++ b/Foxway.i18n.php @@ -0,0 +1,28 @@ +<?php +/** + * Internationalization file for the messages of the Foxway extension. + * + * @file Foxway.i18n.php + * @ingroup Foxway + * @author Pavel Astakhov <[email protected]> + */ + +$messages = array(); + +/** English + * @author pastakhov + */ +$messages['en'] = array( + 'foxway-desc' => 'Allows to store an object-oriented data and implements its own runtime for php code on wikipage', + 'foxway-php-syntax-error-unexpected' => 'PHP Parse error: syntax error, unexpected "$1" in Command line code on line $2' +); + +/** Message documentation (Message documentation) + * @author pastakhov + */ +$messages['qqq'] = array( + 'foxway-desc' => '{{desc|name=Foxway|url=http://www.mediawiki.org/wiki/Extension:Foxway}}', + 'foxway-php-syntax-error-unexpected' => 'Error message, parameters: +* $1 - user-specified string +* $2 - the line number where the error occurred', +); diff --git a/Foxway.php b/Foxway.php new file mode 100644 index 0000000..4131a57 --- /dev/null +++ b/Foxway.php @@ -0,0 +1,71 @@ +<?php +/** + * Foxway - An extension that allows to store an object-oriented data and implements its own runtime for php code on wikipage + * + * @link https://www.mediawiki.org/wiki/Extension:Foxway Documentation + * @file Foxway.php + * @defgroup Foxway + * @ingroup Extensions + * @author Pavel Astakhov <[email protected]> + * @licence GNU General Public Licence 2.0 or later + */ + +// Check to see if we are being called as an extension or directly +if ( !defined( 'MEDIAWIKI' ) ) { + die( 'This file is an extension to MediaWiki and thus not a valid entry point.' ); +} + +define( 'Foxway_VERSION' , '0.0.1' ); + +// Register this extension on Special:Version +$wgExtensionCredits['parserhook'][] = array( + 'path' => __FILE__, + 'name' => 'Foxway', + 'version' => Foxway_VERSION, + 'url' => 'https://www.mediawiki.org/wiki/Extension:Foxway', + 'author' => array( '[[mw:User:Pastakhov|Pavel Astakhov]]' ), + 'descriptionmsg' => 'foxway-desc' +); + +// Tell the whereabouts of files +$dir = __DIR__; + +// Allow translations for this extension +$wgExtensionMessagesFiles['Foxway'] = $dir . '/Foxway.i18n.php'; +$wgExtensionMessagesFiles['FoxwayMagic'] = $dir . '/Foxway.i18n.magic.php'; + +// Include the settings file. +//require_once $dir . '/Settings.php'; + +// Specify the function that will initialize the parser function. +/** + * @codeCoverageIgnore + */ +$wgHooks['ParserFirstCallInit'][] = function( Parser &$parser ) { + //$parser->setFunctionHook( 'foxway', 'Foxway::renderParserFunction' ); + $parser->setHook( 'foxway', 'Foxway::render' ); + return true; +}; + +//Preparing classes for autoloading +$wgAutoloadClasses['Foxway'] = $dir . '/Foxway.body.php'; + +$wgAutoloadClasses['Foxway\\Interpreter'] = $dir . '/includes/Interpreter.php'; +//$wgAutoloadClasses['Foxway\\ORM'] = $dir . '/includes/ORM.php'; +$wgAutoloadClasses['Foxway\\Runtime'] = $dir . '/includes/Runtime.php'; + +//$wgAutoloadClasses['Foxway\\BaseValue'] = $dir . '/includes/ORMValues/BaseValue.php'; +//$wgAutoloadClasses['Foxway\\ValueNumber'] = $dir . '/includes/ORMValues/ValueNumber.php'; + +/******** DB UPDATING ********/ + +# Schema changes +$wgHooks['LoadExtensionSchemaUpdates'][] = function( DatabaseUpdater $updater ) { + $dirsql = __DIR__ . '/sql'; + + //$updater->addExtensionTable( 'foxway_properties', "$dirsql/foxway_properties.sql" ); + //$updater->addExtensionTable( 'foxway_links', "$dirsql/foxway_links.sql" ); + + return true; +}; + diff --git a/includes/Interpreter.php b/includes/Interpreter.php new file mode 100644 index 0000000..322a215 --- /dev/null +++ b/includes/Interpreter.php @@ -0,0 +1,211 @@ +<?php +namespace Foxway; +/** + * Interpreter class of Foxway extension. + * + * @file Interpreter.php + * @ingroup Foxway + * @author Pavel Astakhov <[email protected]> + * @licence GNU General Public Licence 2.0 or later + */ +class Interpreter { + + private static $skipTokenIds = array( + T_WHITESPACE, + T_COMMENT, + T_DOC_COMMENT, + ); + + public static function run($source, $is_debug = true) { + $tokens = token_get_all("<?php $source ?>"); + $return = ""; + $debug = array(); + $expected = false; + $expectListParams = false; + $expectCurlyClose = false; + $line = 1; + $runtime = new Runtime(); + + foreach ($tokens as $token) { + if ( is_string($token) ) { + $id = $token; + if($is_debug) { + $debug[] = $token; + } + } else { + list($id, $text, $line) = $token; + } + + if( $expected && in_array($id, self::$skipTokenIds) === false && in_array($id, $expected) === false) { + $id_str = is_string($id) ? $id : token_name($id); + $return .= '<br><span class="error">' . wfMessage( 'foxway-php-syntax-error-unexpected', $id_str, $line )->escaped() . '</span>'; + break; + } + + switch ($id) { + case T_COMMENT: + case T_DOC_COMMENT: + if($is_debug) { + $debug[] = '<span style="color:#969696" title="'. token_name($id) . '">' . str_replace("\n", "<br />\n", htmlspecialchars($text) ) . '</span>'; + } + break; + case T_WHITESPACE: + if($is_debug) { + $debug[] = str_replace("\n", "<br />\n", $text); + } + break; + case '"': + if($is_debug) { + array_pop($debug); + $debug[] = '<span style="color:#CE7B00">"</span>'; + } + break; + case ';': + $return .= $runtime->getCommandResult($debug); + $expectListParams = false; + $expected = false; + break; + case '=': + $runtime->setVariableOperator('='); + $expected = array( + T_CONSTANT_ENCAPSED_STRING, + T_ENCAPSED_AND_WHITESPACE, + T_VARIABLE, + T_CURLY_OPEN, + '"', + ';', + ); + break; + case '.': + $expected = array( + T_CONSTANT_ENCAPSED_STRING, + T_ENCAPSED_AND_WHITESPACE, + T_VARIABLE, + T_CURLY_OPEN, + '"', + ';', + ); + $runtime->addOperator('.'); + break; + case ',': + $expected = array( + T_CONSTANT_ENCAPSED_STRING, + T_ENCAPSED_AND_WHITESPACE, + T_VARIABLE, + T_CURLY_OPEN, + '"', + // ';', + ); + break; + case '}': + if( $expectCurlyClose ) { + $expectCurlyClose = false; + $expected = array( + T_CONSTANT_ENCAPSED_STRING, + T_ENCAPSED_AND_WHITESPACE, + T_VARIABLE, + T_CURLY_OPEN, + '"', + ';', + ); + } else { + $return .= '<br><span class="error">' . wfMessage( 'foxway-php-syntax-error-unexpected', '}', $line )->escaped() . '</span>'; + break 2; + } + break; + case T_ECHO: + if($is_debug) { + $i = array_push($debug, $text)-1; + } else { + $i = false; + } + $runtime->addCommand('echo', $i); + //@todo: + /*$expected = array( + T_START_HEREDOC, + T_DNUMBER, + T_LNUMBER, + T_STRING_CAST, + T_INT_CAST, + T_FUNCTION, + );*/ + $expectListParams = true; + $expected = array( + T_CONSTANT_ENCAPSED_STRING, + T_ENCAPSED_AND_WHITESPACE, + T_VARIABLE, + T_CURLY_OPEN, + '"', + ';', + ); + break; + case T_CONSTANT_ENCAPSED_STRING: + if($is_debug) { + $debug[] = '<span style="color:#CE7B00" title="'. token_name($id) . '">' . htmlspecialchars($text) . '</span>'; + } + $runtime->addParam( substr($text, 1, -1) ); + $expected = array( + ';', + '.', + ); + if($expectListParams){ + $expected[] = ','; + } + break; + case T_ENCAPSED_AND_WHITESPACE: + if($is_debug) { + $debug[] = '<span style="color:#CE7B00" title="'. token_name($id) . '">' . htmlspecialchars($text) . '</span>'; + } + $runtime->addParam( $text ); + $runtime->addOperator('.'); + break; + case T_VARIABLE: + if( $expected && in_array(T_VARIABLE, $expected) ) { + $value = $runtime->getVariable( substr($text, 1) ); + $runtime->addParam($value); + if( $expectCurlyClose ) { + $expected = array( + '}', + ); + } + if($is_debug) { + if( is_null($value) ) { + $debug[] = '<span style="color:red" title="'.token_name($id).' = '.htmlspecialchars( var_export($value, true) ).'">' . $text . '</span>'; + } else { + $debug[] = '<span style="color:#6D3206" title="'.token_name($id).' = '.htmlspecialchars( var_export($value, true) ).'">' . $text . '</span>'; + } + } + } else { + if($is_debug) { + $i = array_push($debug, $text)-1; + } else { + $i = false; + } + $runtime->setVariable($text, $i); + $expected = array('='); + } + break; + case T_CURLY_OPEN: + if($is_debug) { + $debug[] = '{'; + } + $expectCurlyClose = true; + $expected = array( + T_VARIABLE, + ); + break; + default: + if($is_debug) { + $debug[] = '<span title="'. token_name($id) . '">' . htmlspecialchars($text) . '</span>'; + } + break; + } + } + + if( $is_debug ) { + $return = implode('', $debug) . '<HR>' . $return; + } + return $return; + } + +} diff --git a/includes/Runtime.php b/includes/Runtime.php new file mode 100644 index 0000000..d06671e --- /dev/null +++ b/includes/Runtime.php @@ -0,0 +1,131 @@ +<?php +namespace Foxway; +/** + * Runtime class of Foxway extension. + * + * @file Runtime.php + * @ingroup Foxway + * @author Pavel Astakhov <[email protected]> + * @licence GNU General Public Licence 2.0 or later + */ +class Runtime { + + private $lastCommand = false; + private $lastDebug = false; + private $lastParam = null; + private $listParams = array(); + private $lastOperator = false; + private $lastVariable = false; + private $listVariables = array(); + private $lastVariableOperator = false; + + private $stack = array(); + private static $variables = array(); + + private function pushStack() { + $this->stack[] = array($this->lastCommand, $this->lastDebug, $this->lastParam, $this->listParams, $this->lastOperator, $this->lastVariable, $this->listVariables, $this->lastVariableOperator); + } + + public function popStack() { + if( count($this->stack) == 0 ) { + $this->lastCommand = false; + $this->lastDebug = false; + $this->lastParam = null; + $this->listParams = array(); + $this->lastOperator = false; + $this->lastVariable = false; + $this->listVariables = array(); + $this->lastVariableOperator = false; + } else { + list($this->lastCommand, $this->lastDebug, $this->lastParam, $this->listParams, $this->lastOperator, $this->lastVariable, $this->listVariables, $this->lastVariableOperator) = array_pop($this->stack); + } + } + + public function addCommand( $name, $debug ) { + if( $this->lastCommand ) { + $this->pushStack(); + } + $this->lastCommand = $name; + $this->lastDebug = $debug; + } + + public function addParam( $param ) { + if( $this->lastOperator ) { + switch ( $this->lastOperator ) { + case '.': + $this->lastParam = $this->lastParam . $param; + $this->lastOperator = false; + break; + default: + // TODO + \MWDebug::log( 'Error! Unknown operator "' . htmlspecialchars($this->lastOperator) . '" in ' . __METHOD__ ); + break; + } + } else { + if( !is_null($this->lastParam) ) { + $this->listParams[] = $this->lastParam; + } + $this->lastParam = $param; + } + } + + public function addOperator( $operator ) { + $this->lastOperator = $operator; + } + + public function getCommandResult( &$debug ) { + $this->listParams[] = $this->lastParam; + $return = null; + + switch ($this->lastCommand) { + case 'echo': + $return = implode('', $this->listParams); + if( $this->lastDebug !== false ) { + $debug[$this->lastDebug] = '<span style="color:#0000E6" title="'. token_name(T_ECHO) . ' do ' . htmlspecialchars($return) . '">' . $debug[$this->lastDebug] . '</span>'; + } + break; + default: + $lastCommand = $this->lastCommand; + if( substr($lastCommand, 0, 1) == '$' ) { + switch ($this->lastVariableOperator) { + case '=': + if( $this->lastDebug !== false ) { + $debug[$this->lastDebug] = '<span style="color:#6D3206" title="'.token_name(T_VARIABLE).' set '.htmlspecialchars( var_export($this->lastParam, true) ).'">' . $lastCommand . '</span>'; + } + self::$variables[ substr($lastCommand, 1) ] = $this->lastParam; + break; + default: + // TODO exception + $return = 'Error! Unknown operator "' . htmlspecialchars($this->lastVariableOperator) . '" in ' . __METHOD__; + \MWDebug::log($return); + break; + } + + } else { + // TODO exception + $return = 'Error in ' . __METHOD__; + } + break; + } + $this->popStack(); + return $return; + } + + public function setVariable( $name, $debug ) { + $this->addCommand($name, $debug); + } + + public function setVariableOperator( $operator ) { + $this->lastVariableOperator = $operator; + } + + public function getVariable( $name ) { + if( isset(self::$variables[$name]) ) { + return self::$variables[$name]; + } else { + //TODO THROW + return null; + } + } + +} -- To view, visit https://gerrit.wikimedia.org/r/56366 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I062ac9e6084e4afec15c82ae655d943ca217ed45 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/Foxway Gerrit-Branch: master Gerrit-Owner: Pastakhov <[email protected]> _______________________________________________ MediaWiki-commits mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits
