coren has uploaded a new change for review. https://gerrit.wikimedia.org/r/282525
Change subject: Linting and slight tweak to parser ...................................................................... Linting and slight tweak to parser - add a tweak to the parser (keep the value tokens in a declaration separate in the parsed tree to ease matching at render time - add error checking to encoding/decoding the templatestyles property - pick some lint suggested by Brion and Bryan Change-Id: If60b91e119102c0f0f559fe7e5a4c421c94b7ff4 --- M CSSParser.php M CSSRenderer.php M TemplateStyles.hooks.php M composer.json M i18n/en.json M i18n/qqq.json 6 files changed, 72 insertions(+), 37 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/TemplateStyles refs/changes/25/282525/1 diff --git a/CSSParser.php b/CSSParser.php index a43d802..5e0792a 100644 --- a/CSSParser.php +++ b/CSSParser.php @@ -27,31 +27,53 @@ function __construct( $css ) { preg_match_all( '/( [ \n\t]+ + (?# Sequences of whitespace ) | \/\* (?: [^*]+ | \*[^\/] )* \*\/ [ \n\t]* + (?# Comments and any trailing whitespace ) | " (?: [^"\\\\\n]+ | \\\\\. )* ["\n] + (?# Double-quoted string literals (to newline when unclosed ) | \' (?: [^\'\\\\\n]+ | \\\\\. )* [\'\n] + (#? Single-quoted string literals (to newline when unclosed ) | [+-]? (?: [0-9]* \. )? [0-9]+ (?: [_a-z][_a-z0-9-]* | % )? - | url [ \n\t]* \( + (#? Numerical literals - including optional trailing units or percent sign ) | @? -? (?: [_a-z] | \\\\[0-9a-f]{1,6} [ \n\t]? ) - (?: [_a-z0-9-]+ | \\\\[0-9a-f]{1,6} [ \n\t]? | [^\0-\177] )* + (?: [_a-z0-9-]+ | \\\\[0-9a-f]{1,6} [ \n\t]? | [^\0-\177] )* (?: [ \n\t]* \( )? + (#? Identifiers - including leading `@` for at-rule blocks ) + (#? Trailing open captures are captured to match functional values ) | \# (?: [_a-z0-9-]+ | \\\\[0-9a-f]{1,6} [ \n\t]? | [^\0-\177] )* + (#? So-called hatch literals ) | u\+ [0-9a-f]{1,6} (?: - [0-9a-f]{1,6} )? + (#? Unicode range literals ) | u\+ [0-9a-f?]{1,6} - | <!-- - | --> - | .)/xis', $css, $match ); + (#? Unicode mask literals ) + | .) + (#? Any unmatched token is reduced to single characters ) + /xis', $css, $match ); $space = false; foreach ( $match[0] as $t ) { - if ( preg_match( '/^(?:[ \n\t]|\/\*|<!--|-->)/', $t ) ) { + if ( preg_match( '/^(?:[ \n\t]|\/\*)/', $t ) ) { + + // Fold any sequence of whitespace to a single space token + if ( !$space ) { $space = true; $this->tokens[] = ' '; continue; } + } else { + + // decode any hexadecimal escape character into their corresponding UTF-8 + // sequence - our output is UTF-8 so the escaping is unnecessary and this + // prevents trying to obfuscate ASCII in identifiers to prevent matches. + + $t = preg_replace_callback('/\\\\([0-9a-f]{1,6})[ \n\t]?/', function( $match ) { + return html_entity_decode( '&#'.$match[1].';', ENT_NOQUOTES, 'UTF-8' ); + }, $t); $space = false; $this->tokens[] = $t; + } } $this->index = 0; @@ -67,7 +89,7 @@ if ( $num > 0 ) { if ( $this->index+$num >= count( $this->tokens ) ) $num = count( $this->tokens ) - $this->index; - $text = implode( array_slice( $this->tokens, $this->index, $num ) ); + $text = array_slice( $this->tokens, $this->index, $num ); $this->index += $num; return $text; } @@ -99,7 +121,7 @@ */ private function parseDecl() { $this->consumeWS(); - $name = $this->consume(); + $name = $this->consume()[0]; $this->consumeWS(); if ( $this->peek( 0 )!=':' ) { $this->consumeTo( [';', '}', null] ); @@ -113,7 +135,7 @@ $this->consumeWS(); $value = $this->consumeTo( [';', '}', null] ); if ( $this->peek( 0 ) == ';' ) { - $value .= $this->consume(); + $this->consume(); $this->consumeWS(); } return [ $name => $value ]; @@ -164,7 +186,7 @@ $this->consumeWS(); $text = ''; } else - $text .= $this->consume(); + $text .= $this->consume()[0]; } $selectors[] = $text; if ( $this->peek( 0 ) == '{' ) { @@ -205,7 +227,7 @@ $this->consumeWS(); $text = ''; while ( !in_array( $this->peek( 0 ), ['{', ';', null] ) ) - $text .= $this->consume(); + $text .= $this->consume()[0]; if ( $this->peek( 0 ) == '{' ) { $this->consume(); $r = $this->rules( [ '}', null ] ); @@ -218,7 +240,7 @@ $at = $this->consume(); $text = ''; while ( !in_array( $this->peek( 0 ), ['{', ';', null] ) ) - $text .= $this->consume(); + $text .= $this->consume()[0]; if ( $this->peek( 0 ) == '{' ) { $this->consume(); $decl = $this->parseDecls(); diff --git a/CSSRenderer.php b/CSSRenderer.php index f0e95e4..ce54f94 100644 --- a/CSSRenderer.php +++ b/CSSRenderer.php @@ -59,7 +59,7 @@ foreach ( $rules as $rule ) { $css .= implode( ',', $rule['selectors'] ) . "{"; foreach ( $rule['decls'] as $key => $value ) { - $css .= "$key:$value"; + $css .= "$key:" . implode( '', $value ) . ';'; } $css .= "} "; } diff --git a/TemplateStyles.hooks.php b/TemplateStyles.hooks.php index 7a4524d..57b2b3d 100644 --- a/TemplateStyles.hooks.php +++ b/TemplateStyles.hooks.php @@ -11,8 +11,19 @@ * Register parser hooks */ public static function onParserFirstCallInit( &$parser ) { - $parser->setHook( 'templatestyles', array( 'TemplateStylesHooks', 'render' ) ); + $parser->setHook( 'templatestyles', 'TemplateStylesHooks::render' ); return true; + } + + private static function decodeFromBlob( $blob ) { + $tree = gzdecode( $blob ); + if ( $tree ) + $tree = unserialize( $tree ); + return $tree; + } + + private static function encodeToBlob( $tree ) { + return gzencode( serialize( $tree ) ); } public static function onOutputPageParserOutput( &$out, $parseroutput ) { @@ -24,9 +35,6 @@ $renderer = new CSSRenderer(); $pages = []; - - if ( $out->canUseWikiPage() ) - $pages[$out->getWikiPage()->getID()] = 'self'; foreach ( $namespaces as $ns ) if ( array_key_exists( $ns, $parseroutput->getTemplates() ) ) @@ -43,16 +51,18 @@ [ 'ORDER BY', 'pp_page' ] ); foreach ( $res as $row ) { - $css = unserialize( gzdecode( $row->pp_value ) ); - $renderer->add( $css ); + $css = decodeFromBlob( $row->pp_value ); + if ( $css ) + $renderer->add( $css ); } } $selfcss = $out->getProperty( 'templatestyles' ); if ( $selfcss ) { - $selfcss = unserialize( gzdecode( $selfcss ) ); - $renderer->add( $selfcss ); + $selfcss = decodeFromBlob( unserialize( gzdecode( $selfcss ) ); + if ( $selfcss ) + $renderer->add( $selfcss ); } $css = $renderer->render(); @@ -78,8 +88,11 @@ $css = new CSSParser( $input ); if ( $css ) - $parser->getOutput()->setProperty( 'templatestyles', gzencode( serialize( $css->rules() ) ) ); + $parser->getOutput()->setProperty( 'templatestyles', encodeToBlob( $css->rules() ) ); + // TODO: The UX would benefit from the CSS being run through the + // hook for syntax highlighting rather that simply being presented + // as a preformatted block. $html = Html::openElement( 'div', [ 'class' => 'mw-templatestyles-doc' ] ) . Html::rawElement( diff --git a/composer.json b/composer.json index d9fc29b..7b145a3 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "license": "LGPL-2.1+", "require-dev": { "jakub-onderka/php-parallel-lint": "0.9", - "mediawiki/mediawiki-codesniffer": "0.4.0" + "mediawiki/mediawiki-codesniffer": "0.5.0" }, "scripts": { "test": [ diff --git a/i18n/en.json b/i18n/en.json index 640383e..59eb4e0 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1,10 +1,10 @@ { - "@metadata": { - "authors": [ - "Marc A. Pelletier" - ] - }, - "templateStyles": "TemplateStyles", - "templateStyles-desc": "Implement per-template style sheets", + "@metadata": { + "authors": [ + "Marc A. Pelletier" + ] + }, + "templateStyles": "TemplateStyles", + "templateStyles-desc": "Implement per-template style sheets", "templatestyles-doc-header": "Template-specific style sheet:" } diff --git a/i18n/qqq.json b/i18n/qqq.json index 707c1dd..a0d33a4 100644 --- a/i18n/qqq.json +++ b/i18n/qqq.json @@ -1,10 +1,10 @@ { - "@metadata": { - "authors": [ - "Marc A. Pelletier" - ] - }, - "templateStyles": "The name of the extension", - "templateStyles-desc": "{{desc|name=TemplateStyles|url=https://www.mediawiki.org/wiki/Extension:TemplateStyles}}", + "@metadata": { + "authors": [ + "Marc A. Pelletier" + ] + }, + "templateStyles": "The name of the extension", + "templateStyles-desc": "{{desc|name=TemplateStyles|url=https://www.mediawiki.org/wiki/Extension:TemplateStyles}}", "templatestyles-doc-header": "Used as caption for the display of the style sheet of the current template." } -- To view, visit https://gerrit.wikimedia.org/r/282525 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: If60b91e119102c0f0f559fe7e5a4c421c94b7ff4 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/TemplateStyles Gerrit-Branch: master Gerrit-Owner: coren <m...@uberbox.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits