Files
WP-LIT-MCP/src/Abilities/Errors.php
T
janniclas.loosenandClaude Opus 5.5 d831cf59ca fix: never publish a missed schedule, refuse trashed posts, fail on meta read errors
- PostGuard::editable() refuses trashed entries unless the caller opts in
  (content-get, revisions-list, post-trash); publish/schedule/unpublish/update/
  restore/duplicate/replace now refuse them.
- PostGuard::statusStable(): content-update, revision-restore and
  replace-with-copy refuse a future post whose date has passed (any save would
  publish it); the content-update rollback and PostCloner row writes re-check.
- PostCloner::ownMeta() treats a failed SELECT as an error; replace() reads the
  copy before its first write.
- replace-with-copy needs create_posts (backup clone) and reports backup_id in
  write_failed/partial_write.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 01:20:21 +02:00

164 lines
5.7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace LIT\MCP\Abilities;
use WP_Error;
/**
* Factory for the error objects abilities return.
*
* mcp-adapter 0.5 turns a WP_Error into a tool result with isError: true but
* forwards only the message text. The message therefore carries the whole
* structured error as JSON: {code, message, hint, fields?, usages?, …} (spec §8.2).
*/
final class Errors
{
/**
* @param array<string, mixed> $details Extra keys for the client, e.g. fields or usages.
*/
public static function make(string $code, string $message, string $hint, int $status, array $details = []): WP_Error
{
$payload = array_merge(['code' => $code, 'message' => $message, 'hint' => $hint], $details);
$json = wp_json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
return new WP_Error(
'lit_' . $code,
is_string($json) ? $json : $message,
array_merge(['status' => $status, 'hint' => $hint], $details)
);
}
public static function forbidden(
string $message,
string $hint = 'Mit den Rechten des verbundenen WordPress-Kontos nicht erlaubt; ggf. Administrator fragen.'
): WP_Error {
return self::make('forbidden', $message, $hint, 403);
}
/**
* @param array<string, string> $fields Input path => problem.
*/
public static function invalidInput(
string $message,
array $fields = [],
string $hint = 'Die unter fields genannten Eingaben korrigieren und erneut senden.'
): WP_Error {
return self::make('validation_failed', $message, $hint, 400, $fields === [] ? [] : ['fields' => $fields]);
}
public static function notFound(
string $message,
string $hint = 'ID prüfen, z. B. über lit-content-query oder lit-media-search suchen.'
): WP_Error {
return self::make('not_found', $message, $hint, 404);
}
public static function revMismatch(): WP_Error
{
return self::make(
'rev_mismatch',
'Der Inhalt wurde seit dem letzten Lesen geändert; nichts wurde geschrieben.',
'Neu lesen (z. B. lit-content-get) und die Änderung auf dem aktuellen Stand wiederholen.',
409
);
}
/**
* @param list<array<string, mixed>> $usages
*/
public static function inUse(string $message, array $usages): WP_Error
{
return self::make(
'in_use',
$message,
'Verwendungen prüfen; nur mit force: true trotzdem ausführen.',
409,
['usages' => $usages]
);
}
/**
* @param array<string, mixed> $details
*/
public static function confirmRequired(string $message, array $details = []): WP_Error
{
return self::make(
'confirm_required',
$message,
'Beim Nutzer nachfragen und dann mit confirm: true erneut aufrufen.',
409,
$details
);
}
public static function unsupported(string $message, string $hint): WP_Error
{
return self::make('unsupported', $message, $hint, 400);
}
/**
* A write failed and was rolled back completely.
*
* @param array<string, mixed> $details Extra error details (e.g. a backup that stays).
*/
public static function writeFailed(string $reason, array $details = []): WP_Error
{
return self::make(
'write_failed',
trim('Speichern fehlgeschlagen: ' . $reason) . ' Nichts wurde gespeichert.',
'Eingaben prüfen, ggf. korrigieren und erneut versuchen; tritt der Fehler wieder auf, den Administrator '
. 'informieren (Details im PHP-Error-Log der Website).',
500,
$details
);
}
/**
* A write failed and could not be rolled back completely – something may have been saved.
* $state and $hint replace the post-oriented defaults for writes that are not about a post
* (e.g. menus); $extra is merged into the error details.
*
* @param array<string, mixed> $extra
*/
public static function partialWrite(
string $reason,
?int $postId = null,
?int $revisionId = null,
?string $state = null,
?string $hint = null,
array $extra = []
): WP_Error {
$state ??= $postId === null
? 'Möglicherweise wurde trotzdem ein Entwurf angelegt.'
: sprintf('Eintrag %d ist möglicherweise teilweise geändert bzw. angelegt worden', $postId)
. ($revisionId === null ? '.' : sprintf(' (Stand vorher: Revision %d).', $revisionId));
$details = array_merge($extra, array_filter(
['post_id' => $postId, 'revision_id' => $revisionId],
static fn (?int $id): bool => $id !== null
));
return self::make(
'partial_write',
trim('Speichern fehlgeschlagen: ' . $reason) . ' Die Änderung konnte nicht vollständig zurückgenommen '
. 'werden. ' . $state,
$hint ?? 'Den Aufruf nicht einfach wiederholen. Zuerst den aktuellen Stand prüfen (lit-content-get, bei '
. 'einem neuen Entwurf lit-content-query) und ggf. über lit-revisions-list den vorherigen Stand '
. 'wiederherstellen; dem Nutzer Bescheid geben.',
500,
$details
);
}
public static function internal(): WP_Error
{
return self::make(
'internal_error',
'Interner Fehler bei der Ausführung. Details stehen im PHP-Error-Log der Website.',
'Später erneut versuchen oder den Administrator der Website informieren.',
500
);
}
}