content = $content; $this->elements = $this->find_elements($content); } public static function min_chars($kind) { $default = ($kind === 'image') ? self::MIN_CHARS_IMAGE : self::MIN_CHARS_HEADING; return (int) apply_filters('adghost_proximity_min_chars', $default, $kind); } public function reason_for($offset) { foreach ($this->elements as $el) { $min = self::min_chars($el['kind']); if ($min <= 0) { continue; } if ($el['end'] <= $offset) { $gap = $this->text_length($el['end'], $offset); if ($gap < $min) { return sprintf( 'Only %d characters below %s (needs %d)', $gap, $el['label'], $min ); } } } return null; } public function filter(array $points, ?array &$rejected = null) { $collect = is_array($rejected); $out = []; foreach ($points as $p) { $reason = $this->reason_for($p['offset'] ?? 0); if ($reason !== null) { if ($collect) { $rejected[$p['id']] = $reason; } continue; } $out[] = $p; } return $out; } private function text_length($from, $to) { if ($to <= $from) { return 0; } $slice = substr($this->content, $from, $to - $from); $slice = preg_replace('//s', '', $slice); return strlen(trim(preg_replace('/\s+/', ' ', wp_strip_all_tags($slice)))); } private function find_elements($content) { $out = []; if (preg_match_all('/<(h[1-6])(\s[^>]*)?>.*?<\/\1>/is', $content, $m, PREG_OFFSET_CAPTURE)) { foreach ($m[0] as $i => $match) { $out[] = [ 'start' => $match[1], 'end' => $match[1] + strlen($match[0]), 'kind' => 'heading', 'label' => 'a <' . strtolower($m[1][$i][0]) . '> heading', ]; } } if (preg_match_all('/]*>.*?<\/figure>|]*>/is', $content, $m, PREG_OFFSET_CAPTURE)) { foreach ($m[0] as $match) { $out[] = [ 'start' => $match[1], 'end' => $match[1] + strlen($match[0]), 'kind' => 'image', 'label' => 'an image', ]; } } return $out; } }