diff --git a/core/lib/Drupal/Component/Render/FormattableMarkup.php b/core/lib/Drupal/Component/Render/FormattableMarkup.php index cde8e86bdda380c9c545b2fd19ceab1d8e29176d..ec42e068a7934fa14ec4cb9d9d0ce3ca3966e645 100644 --- a/core/lib/Drupal/Component/Render/FormattableMarkup.php +++ b/core/lib/Drupal/Component/Render/FormattableMarkup.php @@ -223,7 +223,6 @@ protected static function placeholderFormat($string, array $args) { break; case '%': - default: // Similarly to @, escape non-safe values. Also, add wrapping markup // in order to render as a placeholder. Not for use within attributes, // per the warning above about @@ -231,6 +230,16 @@ protected static function placeholderFormat($string, array $args) { // due to the wrapping markup. $args[$key] = '' . static::placeholderEscape($value) . ''; break; + + default: + // We do not trigger an error for placeholder that start with an + // alphabetic character. + if (!ctype_alpha($key[0])) { + // We trigger an error as we may want to introduce new placeholders + // in the future without breaking backward compatibility. + trigger_error('Invalid placeholder: ' . $key, E_USER_ERROR); + } + break; } } diff --git a/core/lib/Drupal/Core/Controller/TitleResolver.php b/core/lib/Drupal/Core/Controller/TitleResolver.php index 4fe90a6a7efc1cc6a57197207b6e82440b03bf2d..9adff8c2f470956eab415032c36c34e09c10cf9e 100644 --- a/core/lib/Drupal/Core/Controller/TitleResolver.php +++ b/core/lib/Drupal/Core/Controller/TitleResolver.php @@ -60,7 +60,6 @@ public function getTitle(Request $request, Route $route) { if (($raw_parameters = $request->attributes->get('_raw_variables'))) { foreach ($raw_parameters->all() as $key => $value) { $args['@' . $key] = $value; - $args['!' . $key] = $value; $args['%' . $key] = $value; } } diff --git a/core/modules/aggregator/src/Tests/AggregatorTestBase.php b/core/modules/aggregator/src/Tests/AggregatorTestBase.php index e6d5124df0da135c2d9847f5964f026cdf726379..d5b09e5256a5bd9bafe441cf79151f96df297376 100644 --- a/core/modules/aggregator/src/Tests/AggregatorTestBase.php +++ b/core/modules/aggregator/src/Tests/AggregatorTestBase.php @@ -166,7 +166,7 @@ public function getDefaultFeedItemCount() { public function updateFeedItems(FeedInterface $feed, $expected_count = NULL) { // First, let's ensure we can get to the rss xml. $this->drupalGet($feed->getUrl()); - $this->assertResponse(200, format_string('!url is reachable.', array('!url' => $feed->getUrl()))); + $this->assertResponse(200, format_string(':url is reachable.', array(':url' => $feed->getUrl()))); // Attempt to access the update link directly without an access token. $this->drupalGet('admin/config/services/aggregator/update/' . $feed->id()); diff --git a/core/modules/dblog/src/Tests/Views/ViewsIntegrationTest.php b/core/modules/dblog/src/Tests/Views/ViewsIntegrationTest.php index ae088e0040889b921ce620cdc49b34c209785f6b..50659ae283ae53e65c3ea34f6afc36e696787dd2 100644 --- a/core/modules/dblog/src/Tests/Views/ViewsIntegrationTest.php +++ b/core/modules/dblog/src/Tests/Views/ViewsIntegrationTest.php @@ -71,13 +71,13 @@ public function testIntegration() { ); // Setup a watchdog entry with two tokens. $entries[] = array( - 'message' => '@token1 !token2', + 'message' => '@token1 @token2', // Setup a link with a tag which is filtered by // \Drupal\Component\Utility\Xss::filterAdmin() in order to make sure // that strings which are not marked as safe get filtered. 'variables' => array( '@token1' => $this->randomMachineName(), - '!token2' => $this->randomMachineName(), + '@token2' => $this->randomMachineName(), 'link' => 'Link', ), ); diff --git a/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php index 0a2e0ea180c8d04af4e26abb99b2e6d5778a5293..7b9cfaaba49152f887a452d98a8df760330857e2 100644 --- a/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php +++ b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php @@ -22,6 +22,20 @@ */ class SafeMarkupTest extends UnitTestCase { + /** + * The error message of the last error in the error handler. + * + * @var string + */ + protected $lastErrorMessage; + + /** + * The error number of the last error in the error handler. + * + * @var int + */ + protected $lastErrorNumber; + /** * {@inheritdoc} */ @@ -159,6 +173,40 @@ function providerFormat() { return $tests; } + /** + * Custom error handler that saves the last error. + * + * We need this custom error handler because we cannot rely on the error to + * exception conversion as __toString is never allowed to leak any kind of + * exception. + * + * @param int $error_number + * The error number. + * @param string $error_message + * The error message. + */ + public function errorHandler($error_number, $error_message) { + $this->lastErrorNumber = $error_number; + $this->lastErrorMessage = $error_message; + } + + /** + * String formatting with SafeMarkup::format() and an unsupported placeholder. + * + * When you call SafeMarkup::format() with an unsupported placeholder, an + * InvalidArgumentException should be thrown. + */ + public function testUnexpectedFormat() { + + // We set a custom error handler because of https://github.com/sebastianbergmann/phpunit/issues/487 + set_error_handler([$this, 'errorHandler']); + // We want this to trigger an error. + $error = SafeMarkup::format('Broken placeholder: ~placeholder', ['~placeholder' => 'broken'])->__toString(); + restore_error_handler(); + + $this->assertEquals(E_USER_ERROR, $this->lastErrorNumber); + $this->assertEquals('Invalid placeholder: ~placeholder', $this->lastErrorMessage); + } }