diff --git a/core/lib/Drupal/Component/Utility/SafeMarkup.php b/core/lib/Drupal/Component/Utility/SafeMarkup.php
index c1c86f5..fbc448c 100644
--- a/core/lib/Drupal/Component/Utility/SafeMarkup.php
+++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php
@@ -30,10 +30,24 @@
  * @see sanitization
  * @see theme_render
  */
-class SafeMarkup {
+class SafeMarkup implements SafeStringInterface {
   use PlaceholderTrait;
 
   /**
+   * The safe string.
+   *
+   * @var string
+   */
+  protected $string;
+
+  /**
+   * The arguments to replace placeholders with.
+   *
+   * @var array
+   */
+  protected $arguments = [];
+
+  /**
    * The list of safe strings.
    *
    * Strings in this list are marked as secure for the entire page render, not
@@ -216,9 +230,9 @@ public static function checkPlain($text) {
    *       self::checkPlain() as part of that.
    *     - Some other special reason for suppressing sanitization.
    *
-   * @return string
-   *   The formatted string, which is marked as safe unless sanitization of an
-   *   unsafe argument was suppressed (see above).
+   * @return string|\Drupal\Component\Utility\SafeStringInterface
+   *   The formatted string, which is a SafeMarkup object unless sanitization of
+   *   an unsafe argument was suppressed (see above).
    *
    * @ingroup sanitization
    *
@@ -228,13 +242,48 @@ public static function checkPlain($text) {
    * @see \Drupal\Core\Url::fromUri()
    */
   public static function format($string, array $args) {
+    $string = (string) $string;
+    if ($string === '') {
+      return '';
+    }
+    // @todo temporary hack whilst !placeholder exists.
     $safe = TRUE;
-    $output = static::placeholderFormat($string, $args, $safe);
-    if ($safe) {
-      static::$safeStrings[$output]['html'] = TRUE;
+    foreach ($args as $key => $value) {
+      if ($key[0] == '!' && !static::isSafe($value)) {
+        $safe = FALSE;
+      }
     }
-    return $output;
+    $safe_string = new static();
+    $safe_string->string = $string;
+    $safe_string->arguments = $args;
+    return $safe ? $safe_string : (string) $safe_string;
+  }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function __toString() {
+    return static::placeholderFormat($this->string, $this->arguments);
+  }
+
+  /**
+   * Returns the string length.
+   *
+   * @return int
+   *   The length of the string.
+   */
+  public function count() {
+    return Unicode::strlen($this->string);
+  }
+
+  /**
+   * Returns a representation of the object for use in JSON serialization.
+   *
+   * @return string
+   *   The safe string content.
+   */
+  public function jsonSerialize() {
+    return $this->__toString();
   }
 
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageBase.php b/core/lib/Drupal/Core/Entity/EntityStorageBase.php
index 31bf8ed..989924c 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageBase.php
@@ -493,8 +493,12 @@ protected function doPostSave(EntityInterface $entity, $update) {
    */
   protected function buildPropertyQuery(QueryInterface $entity_query, array $values) {
     foreach ($values as $name => $value) {
-      // Cast scalars to array so we can consistently use an IN condition.
-      $entity_query->condition($name, (array) $value, 'IN');
+      // Make scalars and objects to array so we can consistently use an IN
+      // condition.
+      if (!is_array($value)) {
+        $value = [$value];
+      }
+      $entity_query->condition($name, $value, 'IN');
     }
   }
 
diff --git a/core/lib/Drupal/Core/Template/Attribute.php b/core/lib/Drupal/Core/Template/Attribute.php
index 3629cda..3f948d0 100644
--- a/core/lib/Drupal/Core/Template/Attribute.php
+++ b/core/lib/Drupal/Core/Template/Attribute.php
@@ -112,10 +112,13 @@ protected function createAttributeValue($name, $value) {
     // An array value or 'class' attribute name are forced to always be an
     // AttributeArray value for consistency.
     if (is_array($value) || $name == 'class') {
-      // Cast the value to an array if the value was passed in as a string.
+      // Cast the value to an array if the value was passed in is not.
+      if (!is_array($value)) {
+        $value = [$value];
+      }
       // @todo Decide to fix all the broken instances of class as a string
       // in core or cast them.
-      $value = new AttributeArray($name, (array) $value);
+      $value = new AttributeArray($name, $value);
     }
     elseif (is_bool($value)) {
       $value = new AttributeBoolean($name, $value);
diff --git a/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php b/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php
index 641ac89..afbffcc 100644
--- a/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php
+++ b/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php
@@ -135,7 +135,7 @@ public function challengeException(Request $request, \Exception $previous) {
     $challenge = SafeMarkup::format('Basic realm="@realm"', array(
       '@realm' => !empty($site_name) ? $site_name : 'Access restricted',
     ));
-    return new UnauthorizedHttpException($challenge, 'No authentication credentials provided.', $previous);
+    return new UnauthorizedHttpException((string) $challenge, 'No authentication credentials provided.', $previous);
   }
 
 }
