You are here

class CodeFilter in Code Filter 8

Text filter for highlighting PHP source code.

Plugin annotation


@Filter(
  id = "codefilter",
  module = "codefilter",
  title = @Translation("Code filter"),
  type = Drupal\filter\Plugin\FilterInterface::TYPE_MARKUP_LANGUAGE,
  settings = {
    "nowrap_expand" = 0
  }
)

Hierarchy

Expanded class hierarchy of CodeFilter

1 file declares its use of CodeFilter
CodeFilterTest.php in src/Tests/CodeFilterTest.php

File

src/Plugin/Filter/CodeFilter.php, line 23

Namespace

Drupal\codefilter\Plugin\Filter
View source
class CodeFilter extends FilterBase {

  /**
   * {@inheritdoc}
   */
  public function settingsForm(array $form, FormStateInterface $form_state) {
    $form['nowrap_expand'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t('Expand code boxes on hover.'),
      '#description' => $this
        ->t('By default, code boxes inherit text wrapping from the active theme. With this setting, code boxes will not wrap, but will expand to full width on hover (with Javascript).'),
      '#default_value' => $this->settings['nowrap_expand'],
    ];
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function tips($long = FALSE) {
    if ($long) {
      return $this
        ->t('To post pieces of code, surround them with <code>...</code> tags. For PHP code, you can use <?php ... ?>, which will also colour it based on syntax.');
    }
    else {
      return $this
        ->t('You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.');
    }
  }

  /**
   * {@inheritdoc}
   */
  public function process($text, $langcode) {

    // Escape code tags to prevent other filters from acting on them.
    $text = preg_replace_callback('@<code>(.+?)</code>@s', [
      get_class($this),
      'codeTagCallback',
    ], $text);
    $text = preg_replace_callback('@[\\[<](\\?php)(.+?)(\\?)[\\]>]@s', [
      get_class($this),
      'phpTagCallback',
    ], $text);

    // Replace code.
    $text = preg_replace_callback('@\\[codefilter_code\\](.+?)\\[/codefilter_code\\]@s', [
      get_class($this),
      'processCodeCallback',
    ], $text);
    $text = preg_replace_callback('@\\[codefilter_php\\](.+?)\\[/codefilter_php\\]@s', [
      get_class($this),
      'processPHPCallback',
    ], $text);

    // A hack, so we can conditionally nowrap based on filter settings.
    // @todo Refactor how replacements are done so we can do this more cleanly.
    if ($this->settings['nowrap_expand']) {
      $text = str_replace('class="codeblock"', 'class="codeblock nowrap-expand"', $text);
    }
    return new FilterProcessResult($text);
  }

  /**
   * Callback to escape content of <code> tags.
   *
   * @param array $matches
   *   An array of matches passed by preg_replace_callback().
   *
   * @return string
   *   A formatted string.
   */
  public static function codeTagCallback(array $matches) {
    return self::escape($matches[1], 'code');
  }

  /**
   * Callback to escape content of <?php ?>, [?php ?], <% %>, and [% %] tags.
   *
   * @param array $matches
   *   An array of matches passed by preg_replace_callback().
   *
   * @return string
   *   A formatted string.
   */
  public static function phpTagCallback(array $matches) {
    return self::escape($matches[2], 'php');
  }

  /**
   * Callback to replace content of the <code> elements.
   *
   * @param array $matches
   *   An array of matches passed by preg_replace_callback().
   *
   * @return string
   *   A formatted string.
   */
  public static function processCodeCallback(array $matches) {
    return self::processCode($matches[1]);
  }

  /**
   * Callback to replace content of the <?php ?> elements.
   *
   * @param array $matches
   *   An array of matches passed by preg_replace_callback().
   *
   * @return string
   *   A formatted string.
   */
  public static function processPHPCallback(array $matches) {
    return self::processPHP($matches[1]);
  }

  /**
   * Escape code blocks.
   *
   * @param string $text
   *   The string to escape.
   * @param string $type
   *   The type of code block, either 'code' or 'php'.
   *
   * @return string
   *   The escaped string.
   */
  public static function escape($text, $type = 'code') {

    // Note, pay attention to odd preg_replace-with-/e behaviour on slashes.
    $text = Html::escape(str_replace('\\"', '"', $text));

    // Protect newlines from line break converter.
    $text = str_replace([
      "\r",
      "\n",
    ], [
      '',
      '&#10;',
    ], $text);

    // Add codefilter escape tags.
    $text = "[codefilter_{$type}]{$text}[/codefilter_{$type}]";
    return $text;
  }

  /**
   * Processes chunks of escaped code into HTML.
   */
  public static function processCode($text) {

    // Undo linebreak escaping.
    $text = str_replace('&#10;', "\n", $text);

    // Inline or block level piece?
    $multiline = strpos($text, "\n") !== FALSE;

    // Note, pay attention to odd preg_replace-with-/e behaviour on slashes.
    $text = preg_replace("/^\n/", '', preg_replace('@</?(br|p)\\s*/?>@', '', str_replace('\\"', '"', $text)));

    // Trim leading and trailing linebreaks.
    $text = trim($text, "\n");

    // Escape newlines.
    $text = nl2br($text);

    // PHP code in regular code.
    $text = preg_replace_callback('/&lt;\\?php.+?\\?&gt;/s', [
      get_class(),
      'processPHPInline',
    ], $text);
    $text = '<code>' . self::fixSpaces(str_replace(' ', '&nbsp;', $text)) . '</code>';
    $text = $multiline ? '<div class="codeblock">' . $text . '</div>' : $text;

    // Remove newlines to avoid clashing with the linebreak filter.
    return str_replace("\n", '', $text);
  }

  /**
   * Helper function for processCode().
   */
  public static function processPHPInline($matches) {

    // Undo nl2br.
    $text = str_replace('<br />', '', $matches[0]);

    // Decode entities (the highlighter re-entifies) and highlight text.
    $text = highlight_string(Html::decodeEntities($text), 1);

    // Remove PHPs own added code tags.
    $text = str_replace([
      '<code>',
      '</code>',
      "\n",
    ], [
      '',
      '',
      '',
    ], $text);
    return $text;
  }

  /**
   * Processes chunks of escaped PHP code into HTML.
   */
  public static function processPHP($text) {

    // Note, pay attention to odd preg_replace-with-/e behaviour on slashes.
    // Undo possible linebreak filter conversion.
    $text = preg_replace('@</?(br|p)\\s*/?>@', '', str_replace('\\"', '"', $text));

    // Undo the escaping in the prepare step.
    $text = Html::decodeEntities($text);

    // Trim leading and trailing linebreaks.
    $text = trim($text, "\r\n");

    // Highlight as PHP.
    $text = '<div class="codeblock">' . highlight_string("<?php\n{$text}\n?>", 1) . '</div>';

    // Remove newlines to avoid clashing with the linebreak filter.
    $text = str_replace("\n", '', $text);
    return self::fixSpaces($text);
  }

  /**
   * Replace html space elements with literal space characters.
   *
   * @param string $text
   *   A string to fix spaces for.
   *
   * @return string
   *   A formatted string.
   */
  public static function fixSpaces($text) {
    $text = preg_replace('@&nbsp;(?!&nbsp;)@', ' ', $text);

    // A single space before text is ignored by browsers. If a single space
    // follows a break tag, replace it with a non-breaking space.
    $text = preg_replace('@<br /> ([^ ])@', '<br />&nbsp;$1', $text);
    return $text;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
CodeFilter::codeTagCallback public static function Callback to escape content of <code> tags.
CodeFilter::escape public static function Escape code blocks.
CodeFilter::fixSpaces public static function Replace html space elements with literal space characters.
CodeFilter::phpTagCallback public static function Callback to escape content of <?php ?>, [?php ?], <% %>, and [% %] tags.
CodeFilter::process public function Performs the filter processing. Overrides FilterInterface::process
CodeFilter::processCode public static function Processes chunks of escaped code into HTML.
CodeFilter::processCodeCallback public static function Callback to replace content of the <code> elements.
CodeFilter::processPHP public static function Processes chunks of escaped PHP code into HTML.
CodeFilter::processPHPCallback public static function Callback to replace content of the <?php ?> elements.
CodeFilter::processPHPInline public static function Helper function for processCode().
CodeFilter::settingsForm public function Generates a filter's settings form. Overrides FilterBase::settingsForm
CodeFilter::tips public function Generates a filter's tip. Overrides FilterBase::tips
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
FilterBase::$provider public property The name of the provider that owns this filter.
FilterBase::$settings public property An associative array containing the configured settings of this filter.
FilterBase::$status public property A Boolean indicating whether this filter is enabled.
FilterBase::$weight public property The weight of this filter compared to others in a filter collection.
FilterBase::calculateDependencies public function Calculates dependencies for the configured plugin. Overrides DependentPluginInterface::calculateDependencies 1
FilterBase::defaultConfiguration public function Gets default configuration for this plugin. Overrides ConfigurableInterface::defaultConfiguration
FilterBase::getConfiguration public function Gets this plugin's configuration. Overrides ConfigurableInterface::getConfiguration
FilterBase::getDescription public function Returns the administrative description for this filter plugin. Overrides FilterInterface::getDescription
FilterBase::getHTMLRestrictions public function Returns HTML allowed by this filter's configuration. Overrides FilterInterface::getHTMLRestrictions 4
FilterBase::getLabel public function Returns the administrative label for this filter plugin. Overrides FilterInterface::getLabel
FilterBase::getType public function Returns the processing type of this filter plugin. Overrides FilterInterface::getType
FilterBase::prepare public function Prepares the text for processing. Overrides FilterInterface::prepare
FilterBase::setConfiguration public function Sets the configuration for this plugin instance. Overrides ConfigurableInterface::setConfiguration 1
FilterBase::__construct public function Constructs a \Drupal\Component\Plugin\PluginBase object. Overrides PluginBase::__construct 4
FilterInterface::TYPE_HTML_RESTRICTOR constant HTML tag and attribute restricting filters to prevent XSS attacks.
FilterInterface::TYPE_MARKUP_LANGUAGE constant Non-HTML markup language filters that generate HTML.
FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE constant Irreversible transformation filters.
FilterInterface::TYPE_TRANSFORM_REVERSIBLE constant Reversible transformation filters.
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 3
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.