Allow formatted html placeholder, without check_plain, in format_string. My suggestion is ~:

bootstrap.inc

function format_string($string, array $args = array()) {
  // Transform arguments before inserting them.
  foreach ($args as $key => $value) {
    switch ($key[0]) {
      case '@':
        // Escaped only.
        $args[$key] = check_plain($value);
        break;

      case '~':
        // Html.
        $args[$key] = html_entity_decode($value);
        break;

      case '%':
      default:
        // Escaped and placeholder.
        $args[$key] = drupal_placeholder($value);
        break;

      case '!':
        // Pass-through.
    }
  }
  return strtr($string, $args);
}

When inserting watchdog messages, sometimes it is useful to print formatted html variables. In my case, when debugging an error and in some special cases, I like to print stack trace:

/**
 * Pone un mensaje en el watchdog.
 * ATENCIÓN: usar de la siguiente forma:
 * <pre>
 * if (variable_get('my_module_debug', false)) {
 *   debugMsg(
 * }</pre>
 *
 * o
 * <pre>
 * if (variable_get('my_module_debug_exhaustivo', false)) {
 *   debugMsg(
 * }</pre>
 *
 * @param string $type Clasificación del mensaje
 * @param string $message Texto del mensaje con parámetros
 * @param array $variables Parámetros del texto
 * @param string $file normalmente __FILE__
 * @param string $function normalmente __FUNCTION__
 * @param string $line normalmente __LINE__
 * @param boolean $printTrace Indica si volcar la pila
 * @param int $severity por defecto WATHCDOB_DEBUG. Disponibles:
 *   - WATCHDOG_EMERGENCY: Emergency, system is unusable.
 *   - WATCHDOG_ALERT: Alert, action must be taken immediately.
 *   - WATCHDOG_CRITICAL: Critical conditions.
 *   - WATCHDOG_ERROR: Error conditions.
 *   - WATCHDOG_WARNING: Warning conditions.
 *   - WATCHDOG_NOTICE: (default) Normal but significant conditions.
 *   - WATCHDOG_INFO: Informational messages.
 *   - WATCHDOG_DEBUG: Debug-level messages.
 */
function debugMsg($type, $message, $variables, $file = null, $function = null, $line = null, $printTrace = false, $severity = WATCHDOG_DEBUG) {
  if ($file != null && $function != null && $line != null) {
    $message .= "<p>File: @file<br/>Function: @function()<br/>Line: @@@line</p>";

    if (!is_array($variables) || empty($variables)) {
      $variables = array();
    }
    $variables = array_merge($variables, array('@file' => $file, '@function' => $function, '@line' => $line, ));
  }

  if ($printTrace) {
    $message .= "<p>##Stack Trace:<br/>";
    $message .= "###########################################<br/>";
    $message .= '@trace';
    $message .= "<br/>###########################################<br/>";
    $message .= "###End Stack Trace</p>";

    $trace = implode('<br/>-------------------------------------------<br/>', get_debug_print_backtrace());
    if (!is_array($variables) || empty($variables)) {
      $variables = array();
    }
    $variables = array_merge($variables, array('@trace' => check_plain($trace), )); // print_r(get_debug_print_backtrace(), TRUE), ));
  }

  watchdog($type, $message, $variables, $severity);
}

function debugMsgErr($type, $message, $variables, $e, $file = null, $function = null, $line = null, $printTrace = true) {
  $message .= "<p>Error: @error</p>";
  $newvars = array_merge($variables, array('@error' => $e->getMessage(), ));
  debugMsg($type, $message, $newvars, $file, $function, $line, $printTrace, WATCHDOG_ERROR);
}

function get_debug_print_backtrace($traces_to_ignore = 1){
  $traces = debug_backtrace();
  $ret = array();

  foreach($traces as $i => $call){
    if ($i < $traces_to_ignore ) {
      continue;
    }

    $object = '';
    if (isset($call['class'])) {
      $object = $call['class'] . $call['type'];
      if (is_array($call['args'])) {
        foreach ($call['args'] as &$arg) {
          get_arg($arg);
        }
      }
    }

    if (is_array($call['args'])) {
      foreach ($call['args'] as &$arg) {
        get_arg($arg);
      }
    }

    $line = '#[' . str_pad($i - $traces_to_ignore, 3, '] ') . $object . $call['function'] . '<br/>';

    if (isset($call['file'])) {
      $line .= 'Fichero: ' . $call['file'] . '<br/>';
    }
    if (isset($call['file'])) {
      $line .= 'Linea: ' . $call['line'] . '<br/>';
    }

    $line .= 'Argumentos:';
    $line .= '<pre>' . print_r($call['args'], TRUE) . '</pre>';

    //$line = '<pre>' . print_r($call, TRUE) . '</pre>';
    $ret[] = $line;
  }

  return $ret;
}

function get_arg(&$arg) {
  if (is_object($arg)) {
    $arr = (array)$arg;
    $args = array();
    foreach($arr as $key => $value) {
      if (strpos($key, chr(0)) !== false) {
        $key = '';    // Private variable found
      }
      $args[] =  '<br/>    ['.$key.'] => '.get_arg($value);
    }

    $arg = get_class($arg) . ' Object ('.implode(',', $args).')';
  }
}

Comments

droplet’s picture

It will exposed a big security holes to everyone.I think you can implement your own hook_watchdog to do it.

Niklas Fiekas’s picture

Version: 7.x-dev » 8.x-dev

There's already !foo for passing through anything. So if somehow the data you do really want to be interpreted as HTML is already encoded, nothing prevents you from using html_entity_decode() together with that.

Features would have to go into 8.x first, so moving there, but tempted to won't fix.

miqmago’s picture

For what I have been looking at, hook_watchdog is being called when saving the message to database.
What I would like is to format the message when showing it to the screen, so it means when reading from database.
So at this point, I don't see how could I do as you say.

I'm really interested into knowing how could I implement this feature using hook_watchdog, so I would really appreciate if you could provide some example or any idea on how to do it.

miqmago’s picture

Status: Active » Closed (won't fix)

Ok Niklas, thanks, It works with !fixed and passing formatted text (without check_plain)