The Read More (<!--more-->
) tag is processed by get_the_content()
in wp-includes/post-template.php
. It does not run any hookable filters on its content input; however, that comes from the $pages
global which can be modified at the time it is set up by hooking the the_post
action.
The following function will auto-insert a <!--more-->
tag at the first space after 280 characters (skipping HTML tags, and counting sequences of whitespace as a single character), if there isn’t one already present:
\add_action(
'the_post',
function($post, $query) {
global $pages, $shortcode_tags;
// Matches an HTML entity, block of whitespace, or single character:
static $rxChar = '&#?+\\w++;|\\s++|[^<]';
// Matches an HTML tag:
static $rxTag = '<[^>]*+>';
// Based on regex in from `get_shortcode_regex`
$tagnames = \array_keys($shortcode_tags);
$tagregexp = \join('|', \array_map('preg_quote', $tagnames));
$rxShortcode =
'\\[' // Opening bracket
. '\\[?+' // 1: Optional second opening bracket for escaping
. '(?&shortcode_tag)' // 2: Shortcode name
. '(?![\\w-])' // Not followed by word character or hyphen
. '[^\\]\\/]*+' // Not a closing bracket or forward slash
. '(?:'
. '\\/(?!\\])' // A forward slash not followed by a closing bracket
. '[^\\]\\/]*+' // Not a closing bracket or forward slash
. ')*+'
. '\\/?+' // 4: Self closing tag ...
. '\\]' // ... and closing bracket
. '\\]?+'; // 6: Optional second closing brocket for escaping
// Matches zero or more HTML tags or shortcode tags
$rxTags = "(?:$rxTag|$rxShortcode)*+";
$regex = "/(?(DEFINE)(?<shortcode_tag>$tagregexp)(?<char>$rxChar)"
. "(?<tags>$rxTags))"
// At least 280 characters, from the start, followed by a space
// (catastrophic failure with general repetition quantifier mitigated
// against by splitting into two multiplicative repetitions):
. '^(?:(?:(?&tags)(?&char)){10}+){28}+(?&tags)(?:(?&char)(?&tags))*?'
. '(?=\\s)/u';
foreach ($pages as $i => $page) {
if (\strpos($page, '<!--more') === false) {
$page = \preg_replace($regex, '$0<!--more-->', $page, 1);
if ($page !== null) {
$pages[$i] = $page;
} else {
trigger_error(
"Regex failure inserting 'Read More' tag into `$pages[$i]`."
);
}
}
}
},
10, 2
);
It should be fairly self-explanatory, though I’ll add a few notes on the regex:
- The
u
(UTF-8) modifier ensures that multibyte characters count only
as one.
- The
+
(possessive) quantifier massively optimizes the
performance and memory footprint (where the token can’t consume any
of the following token). However, it doesn’t seem to make much difference
mitigating against failure when applied to the general repetition quantifier,
so this has been split into repetitions of repetitions. And nonetheless, in
the event of failure the content will be left unchanged.
- The
?
(lazy) quantifier ensures the first
space after 280 characters is stopped at, not the last.
(I know this question was asked more than 4 years ago, but it hadn’t been answered; I found it looking for a solution for exactly the same, which I did not find elsewhere.)
Codex
for more details