Filter Class

<?php

/**
 * @file
 * Contains \Drupal\my_module\Plugin\Filter\MyFilter.
 */

namespace Drupal\my_module\Plugin\Filter;

use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;


/**
 * Provides a filter that <your filter description here>
 *
 * @Filter(
 *   id = "my_module_my_filter_id",
 *   title = @Translation("My Filter Short Description."),
 *   description = @Translation("My Filter Long Description"),
 *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE,
 * )
 */

class MyFilter extends FilterBase {

  /**
   * {@inheritdoc}
   * Filter tips callback. Displays help with using this filter 
   */

  public function tips($long = FALSE) {
    if ($long) {
      return $this->t("My filter's full description of what it does and how to use it");
    } else {
      return $this->t('One line description of my filter');
    }
  }

  /**
   * {@inheritdoc}
   * Filter process callback.
   */

  public function process($text, $langcode) {

    /* Do something here with $text, which is the node content
     * you are transforming.  The following is a skeleton for doing
     * shortcode style search and replace */

    if (stristr(strtoupper($text), '[SHORTCODE') !== FALSE) {
      $text = preg_replace_callback("/\[SHORTCODE\|([^\]]+)\]/i",
 
        function ($results) {
          /* Do something here with $results, which contains everything
           * from inside the square brackets after SHORTCODE, and put
           * the results in $newText */
          return $newText;
        }

       }

      , $text);
    }

    return $text;
  }

}

Notes:

  • The @Filter section in the comments at the top of the file is very important and must be formatted and configured correctly.  Otherwise, your file won't be loaded.

  • The filename must match the classname (e.g. "MyFilter.php" for the example above) and be placed in "src/Plugin/Filter/" relative to your module's root directory.