You are here

protected static function SmartyPants::educateQuotes in Typogrify 8

EducatedQuotes.

Example input: "Isn't this fun?" Example output: [0]Isn’t this fun?[1];

Parameters

string $_: Input text.

string[] $quotes: An array of four quote characters: open/close, single/double.

Return value

string The string, with "educated" curly quote HTML entities.

2 calls to SmartyPants::educateQuotes()
SmartyPants::process in src/SmartyPants.php
SmartyPants.
SmartyPants::smartQuotes in src/SmartyPants.php
SmartQuotes.

File

src/SmartyPants.php, line 948

Class

SmartyPants
SmartyPants - Smart punctuation for web sites.

Namespace

Drupal\typogrify

Code

protected static function educateQuotes($_, array $quotes) {

  // Make our own "punctuation" character class, because the POSIX-style
  // [:PUNCT:] is only available in Perl 5.6 or later:
  $punct_class = "[!\"#\\\$\\%'()*+,-.\\/:;<=>?\\@\\[\\\\\\]\\^_`{|}~]";

  // Special case if the very first character is a quote
  // followed by punctuation at a non-word-break.
  // Close the quotes by brute force:
  $_ = preg_replace([
    "/^'(?={$punct_class}\\B)/",
    "/^\"(?={$punct_class}\\B)/",
  ], [
    $quotes[3],
    $quotes[1],
  ], $_);

  // Special case for double sets of quotes, e.g.:
  // <p>They said, "'Quoted' words in a larger quote."</p>.
  $spacer = '&#8201;';
  $_ = preg_replace([
    "/\"'(?=\\p{L})/u",
    "/'\"(?=\\p{L})/u",
    "/(\\p{L})\"'/u",
    "/(\\p{L})'\"/u",
  ], [
    $quotes[0] . $spacer . $quotes[2],
    $quotes[2] . $spacer . $quotes[0],
    '\\1' . $quotes[1] . $spacer . $quotes[3],
    '\\1' . $quotes[3] . $spacer . $quotes[1],
  ], $_);

  // Special case for decade abbreviations (the '80s):
  $_ = preg_replace("/'(?=\\d{2}s)/", '&#8217;', $_);

  // Special case for apostroph.
  $_ = preg_replace("/(\\p{L}|\\p{N})(')(?=\\p{L}|\$)/u", '\\1&#8217;', $_);
  $close_class = '[^\\ \\t\\r\\n\\[\\{\\(\\-]';
  $dec_dashes = '&\\#8211;|&\\#8212;';

  // Get most opening single quotes:
  $_ = preg_replace("{\n    (\n      \\s         |   # a whitespace char, or\n      &nbsp;      |   # a non-breaking space entity, or\n      --          |   # dashes, or\n      &[mn]dash;  |   # named dash entities\n      {$dec_dashes} |   # or decimal entities\n      &\\#x201[34];   # or hex\n    )\n      '                 # the quote\n      (?=\\p{L})           # followed by a word character\n  }x", '\\1' . $quotes[2], $_);

  // Single closing quotes:
  $_ = preg_replace("{\n    ({$close_class})?\n    '\n      (?(1)|            # If \$1 captured, then do nothing;\n        (?=\\s | s\\b)  # otherwise, positive lookahead for a whitespace\n      )                 # char or an 's' at a word ending position. This\n                        # is a special case to handle something like:\n                        # \"<i>Custer</i>'s Last Stand.\"\n      }xi", '\\1' . $quotes[3], $_);

  // Any remaining single quotes should be opening ones:
  $_ = str_replace("'", $quotes[2], $_);

  // Get most opening double quotes:
  $_ = preg_replace("{\n      (\n        \\s         |   # a whitespace char, or\n        &nbsp;      |   # a non-breaking space entity, or\n        --          |   # dashes, or\n        &[mn]dash;  |   # named dash entities\n        {$dec_dashes} |   # or decimal entities\n        &\\#x201[34];   # or hex\n      )\n      \"                # the quote\n      (?=\\p{L})           # followed by a word character\n      }x", '\\1' . $quotes[0], $_);

  // Double closing quotes:
  $_ = preg_replace("{\n      ({$close_class})?\n      \"\n      (?(1)|(?=\\s))   # If \$1 captured, then do nothing;\n                       # if not, then make sure the next char is whitespace.\n      }x", '\\1' . $quotes[1], $_);

  // Any remaining quotes should be opening ones.
  $_ = str_replace('"', $quotes[0], $_);
  return $_;
}