fix: GlobalUsages finds every reference Divi renders, fail closed (F1, ruling P23)
Candidates come from one ID/status/type query: content with globalModule, global_module or a JSON-escaped letter. Their content is read in chunks of 50 and parsed by UsageMatcher: Divi 5 openers are json-decoded (escaped keys and values, leading zeros, any depth), and Divi 4 global_module attributes are read in any quoting. An undecodable escaped opener counts as a possible reference. Beyond 5000 candidates the rest count unread. The guide names the formats and the wp_block parity. Also fixes the grammar of the blocked-hidden count. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Divi\Library;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Whether post content shows library entry $id (ruling P23, re-review F1), fail closed:
|
||||
* - Divi 5: the textual "globalModule":"<digits>" Divi's render takes (leading zeros = intval), and
|
||||
* every block opener whose JSON – in any spelling, escaped keys or values included – decodes to a
|
||||
* globalModule with that ID at any depth;
|
||||
* - Divi 4: a shortcode attribute global_module=<digits> in any quoting (intval);
|
||||
* - an opener with JSON escapes (or a globalModule) that cannot be decoded, or content the regex
|
||||
* engine cannot scan, counts as a possible reference.
|
||||
* Pure PHP.
|
||||
*/
|
||||
final class UsageMatcher
|
||||
{
|
||||
/** A block opener and its attribute JSON (the Parser's delimiter grammar). */
|
||||
private const OPENER = '/<!--\s+wp:(?:[a-z][a-z0-9_-]*\/)?[a-z][a-z0-9_-]*\s+'
|
||||
. '({(?:(?:[^}]+|}+(?=})|(?!}\s+\/?-->).)*+)?})\s+\/?-->/s';
|
||||
|
||||
/** Divi 4: global_module=, any quote characters (straight, typographic, entities) before the digits. */
|
||||
private const SHORTCODE = '/\bglobal_module\s*=\s*[^\d\s\]]{0,12}(\d+)/i';
|
||||
|
||||
public static function shows(string $content, int $id): bool
|
||||
{
|
||||
$textual = preg_match('/"globalModule":"0*' . $id . '"/', $content);
|
||||
if ($textual !== 0) {
|
||||
return true; // A match, or a regex failure (fail closed).
|
||||
}
|
||||
|
||||
if (stripos($content, 'global_module') !== false) {
|
||||
$found = preg_match_all(self::SHORTCODE, $content, $matches);
|
||||
if ($found === false) {
|
||||
return true;
|
||||
}
|
||||
foreach ($matches[1] as $value) {
|
||||
if ((int) $value === $id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$escape = chr(92) . 'u';
|
||||
if (! str_contains($content, 'globalModule') && ! str_contains($content, $escape)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$found = preg_match_all(self::OPENER, $content, $matches);
|
||||
if ($found === false) {
|
||||
return true;
|
||||
}
|
||||
foreach ($matches[1] as $json) {
|
||||
if (! str_contains($json, $escape) && ! str_contains($json, 'globalModule')) {
|
||||
continue;
|
||||
}
|
||||
$attrs = json_decode($json, true);
|
||||
if (! is_array($attrs) || self::names($attrs, $id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @param array<array-key, mixed> $attrs Whether a globalModule key at any depth names $id. */
|
||||
private static function names(array $attrs, int $id): bool
|
||||
{
|
||||
foreach ($attrs as $key => $value) {
|
||||
if (
|
||||
$key === 'globalModule'
|
||||
&& (is_int($value) || (is_string($value) && preg_match('/^\d+$/D', $value) === 1))
|
||||
&& (int) $value === $id
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (is_array($value) && self::names($value, $id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user