// matches are tested first
$this->sortByWeight();
+ $matches = $this->internalParse( $path );
+ if ( is_null( $matches ) ) {
+ // Try with the normalized path (T100782)
+ $path = wfRemoveDotSegments( $path );
+ $path = preg_replace( '#/+#', '/', $path );
+ $matches = $this->internalParse( $path );
+ }
+
+ // We know the difference between null (no matches) and
+ // array() (a match with no data) but our WebRequest caller
+ // expects array() even when we have no matches so return
+ // a array() when we have null
+ return is_null( $matches ) ? [] : $matches;
+ }
+
+ /**
+ * Match a path against each defined pattern
+ *
+ * @param string $path
+ * @return array|null
+ */
+ protected function internalParse( $path ) {
$matches = null;
foreach ( $this->patterns as $pattern ) {
break;
}
}
-
- // We know the difference between null (no matches) and
- // array() (a match with no data) but our WebRequest caller
- // expects array() even when we have no matches so return
- // a array() when we have null
- return is_null( $matches ) ? [] : $matches;
+ return $matches;
}
/**
* @param string $path
- * @param string $pattern
+ * @param object $pattern
* @return array|null
*/
protected static function extractTitle( $path, $pattern ) {
$value = $paramData['value'];
} elseif ( isset( $paramData['pattern'] ) ) {
// For patterns we have to make value replacements on the string
- $value = $paramData['pattern'];
- $replacer = new PathRouterPatternReplacer;
- $replacer->params = $m;
- if ( isset( $pattern->key ) ) {
- $replacer->key = $pattern->key;
- }
- $value = $replacer->replace( $value );
+ $value = self::expandParamValue( $m, $pattern->key ?? null,
+ $paramData['pattern'] );
if ( $value === false ) {
// Pattern required data that wasn't available, abort
return null;
return $matches;
}
-}
+ /**
+ * Replace $key etc. in param values with the matched strings from the path.
+ *
+ * @param array $pathMatches The match results from the path
+ * @param string|null $key The key of the matching pattern
+ * @param string $value The param value to be expanded
+ * @return string|false
+ */
+ protected static function expandParamValue( $pathMatches, $key, $value ) {
+ $error = false;
-class PathRouterPatternReplacer {
+ $replacer = function ( $m ) use ( $pathMatches, $key, &$error ) {
+ if ( $m[1] == "key" ) {
+ if ( is_null( $key ) ) {
+ $error = true;
- public $key, $params, $error;
+ return '';
+ }
- /**
- * Replace keys inside path router patterns with text.
- * We do this inside of a replacement callback because after replacement we can't tell the
- * difference between a $1 that was not replaced and a $1 that was part of
- * the content a $1 was replaced with.
- * @param string $value
- * @return string
- */
- public function replace( $value ) {
- $this->error = false;
- $value = preg_replace_callback( '/\$(\d+|key)/u', [ $this, 'callback' ], $value );
- if ( $this->error ) {
- return false;
- }
- return $value;
- }
+ return $key;
+ } else {
+ $d = $m[1];
+ if ( !isset( $pathMatches["par$d"] ) ) {
+ $error = true;
- /**
- * @param array $m
- * @return string
- */
- protected function callback( $m ) {
- if ( $m[1] == "key" ) {
- if ( is_null( $this->key ) ) {
- $this->error = true;
- return '';
- }
- return $this->key;
- } else {
- $d = $m[1];
- if ( !isset( $this->params["par$d"] ) ) {
- $this->error = true;
- return '';
+ return '';
+ }
+
+ return rawurldecode( $pathMatches["par$d"] );
}
- return rawurldecode( $this->params["par$d"] );
+ };
+
+ $value = preg_replace_callback( '/\$(\d+|key)/u', $replacer, $value );
+ if ( $error ) {
+ return false;
}
- }
+ return $value;
+ }
}