1. YoureCom
    1. Über mich
  2. Lösungen
    1. Tipprunde
    2. Aufgaben & Support
    3. Mitglieder-Netzwerk
    4. Online-Shop
    5. Alle Lösungen & Preise
  3. Für Admins
    1. Plugins
    2. ByteMon
    3. ByteMon: Community verwalten
    4. ByteMon: Portalidentität
    5. Individuelle Entwicklung
    6. WSC 6.3 Alpha
    7. GitHub
  4. Neuigkeiten
    1. Kurzmeldungen
  5. Support
  6. Mein Konto
  7. Lösungen testen
  • Anmelden
  • Registrieren
  • Warenkorb 0
  • Suche
Alles
  • Alles
  • Artikel
  • Seiten
  • Aufgaben
  • Produkte
  • Pulse-Beiträge
  • Erweiterte Suche
  1. YoureCom – Software für Vereine, Teams & Communities
  2. Repository-Liste
  3. de.yourecom.wcf.api.mpdf

YoureCom Standard automatisch aktualisiert (verwaltete Dateien aus _dev/standard)

  • Marcel Beckers
  • +237 Hinzufügungen
  • 237 Gesamt
  • 3 Dateien
  • 21. Oktober 2026 um 22:11
  • tests/lint/check-architecture.php
    <pre>@@ -112,6 +112,22 @@
    $report($relative, 'endpoint-validate-action', 'RPC endpoint calls validateAction(), call the check directly (e.g. validateDelete()) or check in the endpoint/command');
    }
    + // ->query()/->prepare() (not the parameter-bound ->prepareStatement() wrapper) with a string built by
    + // concatenation: same risk as classic SQL injection, whether or not the value comes from user input
    + \preg_match_all('~\$(\w+)\s*(?:\.=|=\s*(?:\$\1\s*)?\.)~', $source, $concatMatches);
    + $concatenatedVars = \array_unique($concatMatches[1]);
    + \preg_match_all('~->(query|prepare)\s*\(\s*([^)]*)~', $source, $sqlCalls, \PREG_SET_ORDER);
    + foreach ($sqlCalls as $call) {
    + [, $method, $argument] = $call;
    + if (\str_contains($argument, '.')) {
    + $report($relative, 'sql-concat', "->{$method}() with inline string concatenation, use ->prepareStatement() with bound parameters");
    + continue;
    + }
    + if (\preg_match('~^\$(\w+)~', \trim($argument), $variable) && \in_array($variable[1], $concatenatedVars, true)) {
    + $report($relative, 'sql-concat', "->{$method}(\${$variable[1]}) where \${$variable[1]} is built by concatenation, use ->prepareStatement() with bound parameters");
    + }
    + }
    +
    // rows are reloaded through core/grid-views/rows with the grid view parameters of the client: a grid view that takes
    // such a parameter (an ID of a parent object) must check access to that object, not only that someone is logged in
    if (
  • tests/lint/check-comments.php
    <pre>@@ -0,0 +1,219 @@
    +<?php
    +
    +/**
    + * YoureCom Standard: comment rules (Marcel 21.10.2026, Sitzung). Managed by _dev/standard (apply.php), do not edit here.
    + * - templates (.tpl) and SCSS never have a comment ({* *}, <!-- -->, //, /* */) - not even one line.
    + * - PHP and TypeScript: a comment is only allowed directly above a class/interface/trait/enum, a function/method or a
    + * property, and only one short descriptive line - PHPStan/TSDoc annotation lines (@param, @return, @var, @template,
    + * @throws, @inheritDoc, ...) do not count toward that line and may be as many as needed. Anywhere else (inline in a
    + * method body, floating, a file header) is a violation.
    + * - Every function/method needs either such a comment or #[\Override] (valid for a parent class, an interface or a
    + * trait method alike, PHP checks this itself at compile time) - never neither.
    + * - _dev/standard itself is exempt (tooling documentation needs more room, Marcel 21.10.2026: "ist mir egal").
    + *
    + * Known findings that cannot be fixed yet are listed in tests/lint/config.php (`commentsKnown`: relative file => list
    + * of rules); entries that no longer match make the check fail, so the list can only shrink.
    + *
    + * Usage: php tests/lint/check-comments.php
    + */
    +
    +namespace yourecom\standard;
    +
    +require __DIR__ . '/lib.php';
    +
    +/** @var array<string, list<string>> $findings relative file => rules */
    +$findings = [];
    +/** @var array<string, string> $messages "file|rule" => message */
    +$messages = [];
    +
    +$report = static function (string $file, string $rule, string $message) use (&$findings, &$messages): void {
    + $findings[$file][] = $rule;
    + $messages["{$file}|{$rule}"] ??= "{$file}: [{$rule}] {$message}";
    +};
    +
    +/** Strips comment delimiters and asterisks, returns null if the descriptive text (without PHPStan/TSDoc tags) is one line. */
    +$tooLong = static function (string $text): ?string {
    + $lines = \preg_split('~\R~', $text) ?: [];
    + $lines = \array_map(static fn (string $line): string => \trim($line, " \t*/"), $lines);
    + $lines = \array_values(\array_filter($lines, static fn (string $line): bool => $line !== ''));
    + $descriptive = \array_values(\array_filter($lines, static fn (string $line): bool => !\preg_match('~^@[a-zA-Z]~', $line)));
    + return \count($descriptive) > 1 ? 'mehr als eine beschreibende Zeile' : null;
    +};
    +
    +// an attribute group "#[...]" can itself contain "]" inside strings/nested brackets; conservative bracket counting
    +$skipAttribute = static function (array $tokens, int $index, int $direction) use (&$skipAttribute): int {
    + $open = $direction > 0 ? '[' : ']';
    + $close = $direction > 0 ? ']' : '[';
    + $depth = 1;
    + $i = $index + $direction;
    + for ($count = \count($tokens); $i >= 0 && $i < $count; $i += $direction) {
    + $text = \is_array($tokens[$i]) ? $tokens[$i][1] : $tokens[$i];
    + $depth += \substr_count($text, $open) - \substr_count($text, $close);
    + if ($depth <= 0) {
    + return $i + $direction;
    + }
    + }
    + return $i;
    +};
    +
    +foreach (files(['files', 'files_wcf'], ['.php']) as $relative => $path) {
    + $tokens = \token_get_all((string)\file_get_contents($path));
    + $count = \count($tokens);
    + $declarationTokens = [\T_CLASS, \T_INTERFACE, \T_TRAIT, \T_ENUM, \T_FUNCTION, \T_PUBLIC, \T_PROTECTED, \T_PRIVATE, \T_VAR, \T_READONLY, \T_CASE, \T_CONST];
    +
    + for ($i = 0; $i < $count; $i++) {
    + $token = $tokens[$i];
    + if (!\is_array($token) || !\in_array($token[0], [\T_COMMENT, \T_DOC_COMMENT], true)) {
    + continue;
    + }
    + $line = $token[2];
    +
    + $j = $i + 1;
    + while ($j < $count) {
    + if (\is_array($tokens[$j]) && $tokens[$j][0] === \T_WHITESPACE) {
    + $j++;
    + continue;
    + }
    + if (\is_array($tokens[$j]) && $tokens[$j][0] === \T_ATTRIBUTE) {
    + $j = $skipAttribute($tokens, $j, 1);
    + continue;
    + }
    + break;
    + }
    + $next = $tokens[$j] ?? null;
    + $onDeclaration = \is_array($next) && \in_array($next[0], $declarationTokens, true);
    +
    + if (!$onDeclaration) {
    + $report($relative, 'stray-comment', "Zeile {$line}: Kommentar nicht direkt über Klasse/Funktion/Property, entfernen");
    + continue;
    + }
    + $problem = $tooLong($token[1]);
    + if ($problem !== null) {
    + $report($relative, 'comment-too-long', "Zeile {$line}: {$problem}, ein kurzer Satz reicht");
    + }
    + }
    +
    + for ($i = 0; $i < $count; $i++) {
    + $token = $tokens[$i];
    + if (!\is_array($token) || $token[0] !== \T_FUNCTION) {
    + continue;
    + }
    + $line = $token[2];
    + $hasOverride = false;
    + $hasComment = false;
    + $j = $i - 1;
    + while ($j >= 0) {
    + $previous = $tokens[$j];
    + if (\is_array($previous) && \in_array($previous[0], [\T_WHITESPACE, \T_PUBLIC, \T_PROTECTED, \T_PRIVATE, \T_STATIC, \T_ABSTRACT, \T_FINAL], true)) {
    + $j--;
    + continue;
    + }
    + if (\is_array($previous) && \in_array($previous[0], [\T_COMMENT, \T_DOC_COMMENT], true)) {
    + $hasComment = $tooLong($previous[1]) === null;
    + break;
    + }
    + $text = \is_array($previous) ? $previous[1] : $previous;
    + if ($text === ']') {
    + $start = $skipAttribute($tokens, $j, -1);
    + $group = '';
    + for ($k = $start; $k <= $j; $k++) {
    + $group .= \is_array($tokens[$k]) ? $tokens[$k][1] : $tokens[$k];
    + }
    + if (\preg_match('~\\\\?Override\b~', $group)) {
    + $hasOverride = true;
    + }
    + $j = $start - 1;
    + continue;
    + }
    + break;
    + }
    + if (!$hasOverride && !$hasComment) {
    + $report($relative, 'function-needs-comment-or-override', "Zeile {$line}: Funktion ohne Kommentar und ohne #[\\Override]");
    + }
    + }
    +}
    +
    +// TypeScript: no reliable tokenizer available, line-based heuristic (a comment line followed directly by a
    +// declaration-looking line counts as "on" it; anything else floating or inline is a violation)
    +foreach (files(['ts'], ['.ts']) as $relative => $path) {
    + if (\str_ends_with($relative, '.d.ts')) {
    + continue;
    + }
    + $lines = \preg_split('~\R~', (string)\file_get_contents($path)) ?: [];
    + $inBlock = false;
    + $blockStartLine = 0;
    + $blockLines = [];
    + foreach ($lines as $index => $rawLine) {
    + $lineNo = $index + 1;
    + $trimmed = \trim($rawLine);
    + if ($inBlock) {
    + $blockLines[] = $trimmed;
    + if (\str_contains($trimmed, '*/')) {
    + $inBlock = false;
    + $after = \trim($lines[$index + 1] ?? '');
    + $onDeclaration = (bool)\preg_match('~^(export\s+)?(default\s+)?(public\s+|private\s+|protected\s+|readonly\s+|static\s+|abstract\s+)*(class|interface|function|get\s|set\s|async\s|\w+[?!]?\s*[:(<])~', $after);
    + if (!$onDeclaration) {
    + $report($relative, 'stray-comment', "Zeile {$blockStartLine}: Kommentar nicht direkt über Klasse/Funktion/Property, entfernen");
    + } else {
    + $descriptive = \array_values(\array_filter(
    + \array_map(static fn (string $line): string => \trim($line, " \t*/"), $blockLines),
    + static fn (string $line): bool => $line !== '' && !\preg_match('~^@[a-zA-Z]~', $line),
    + ));
    + if (\count($descriptive) > 1) {
    + $report($relative, 'comment-too-long', "Zeile {$blockStartLine}: mehr als eine beschreibende Zeile, ein kurzer Satz reicht");
    + }
    + }
    + }
    + continue;
    + }
    + if (\str_starts_with($trimmed, '/*')) {
    + $inBlock = !\str_contains($trimmed, '*/');
    + $blockStartLine = $lineNo;
    + $blockLines = [$trimmed];
    + if (!$inBlock) {
    + $after = \trim($lines[$index + 1] ?? '');
    + $onDeclaration = (bool)\preg_match('~^(export\s+)?(default\s+)?(public\s+|private\s+|protected\s+|readonly\s+|static\s+|abstract\s+)*(class|interface|function|get\s|set\s|async\s|\w+[?!]?\s*[:(<])~', $after);
    + if (!$onDeclaration) {
    + $report($relative, 'stray-comment', "Zeile {$lineNo}: Kommentar nicht direkt über Klasse/Funktion/Property, entfernen");
    + }
    + }
    + continue;
    + }
    + if (\str_starts_with($trimmed, '//')) {
    + $report($relative, 'stray-comment', "Zeile {$lineNo}: `//`-Kommentar, in TS nie erlaubt (nur /** ... */ direkt über Klasse/Funktion/Property)");
    + }
    + }
    +}
    +
    +// templates and SCSS: never a comment, not even one line
    +foreach (files(templateDirectories(), ['.tpl']) as $relative => $path) {
    + $source = (string)\file_get_contents($path);
    + if (\preg_match('~\{\*.*?\*\}~s', $source) || \preg_match('~<!--(?!\[if).*?-->~s', $source)) {
    + $report($relative, 'template-comment', 'Template-Kommentar ({* *} oder <!-- -->), Templates haben nie Kommentare');
    + }
    +}
    +foreach (files(['files/style', 'files_wcf/style'], ['.scss']) as $relative => $path) {
    + $source = (string)\file_get_contents($path);
    + if (\preg_match('~//|/\*~', $source)) {
    + $report($relative, 'scss-comment', 'SCSS-Kommentar, SCSS hat nie Kommentare');
    + }
    +}
    +
    +$known = config()['commentsKnown'] ?? [];
    +$errors = [];
    +foreach ($findings as $file => $rules) {
    + foreach (\array_unique($rules) as $rule) {
    + if (!\in_array($rule, $known[$file] ?? [], true)) {
    + $errors[] = $messages["{$file}|{$rule}"];
    + }
    + }
    +}
    +foreach ($known as $file => $rules) {
    + foreach ($rules as $rule) {
    + if (!\in_array($rule, $findings[$file] ?? [], true)) {
    + $errors[] = "tests/lint/config.php: commentsKnown {$file} [{$rule}] no longer applies, remove it";
    + }
    + }
    +}
    +
    +finish($errors, \count($findings) === 0 ? 'keine Kommentar-Verstöße' : \count($known) . ' bekannt');
  • tests/lint/lib.php
    <pre>@@ -18,6 +18,7 @@
    * languageIgnore: list<string>,
    * xsdKnown: array<string, list<string>>,
    * architectureKnown: array<string, list<string>>,
    + * commentsKnown: array<string, list<string>>,
    * jsBuildDir: string|null,
    * jsPhrasesDynamic: bool,
    * }
    @@ -32,6 +33,7 @@ function config(): array
    'languageIgnore' => $config['languageIgnore'] ?? [],
    'xsdKnown' => $config['xsdKnown'] ?? [],
    'architectureKnown' => $config['architectureKnown'] ?? [],
    + 'commentsKnown' => $config['commentsKnown'] ?? [],
    'jsBuildDir' => $config['jsBuildDir'] ?? null,
    'jsPhrasesDynamic' => $config['jsPhrasesDynamic'] ?? false,
    'coreTables' => $config['coreTables'] ?? [],

Bereit, deine Community smarter zu machen?

Ob fertiges Produkt oder individuelle Softwarelösung – überzeuge dich selbst.

Jetzt kaufen Demo testen

Direkter Support vom Entwickler

Zuletzt erledigte Aufgaben

  1. Overlay beim Verschieben eines Themas Clan Verwaltung Clan Verwaltung: Foren

    Marcel Beckers Erledigt am 23. Oktober 2026 um 22:10
  2. Feature-Wunsch: Einführung von Tippgemeinschaften / Team-Wertung (Team-Tippen) Wettbewerbe Basissystem Wettbewerbe: Tippspiel

    villain Erledigt am 22. Oktober 2026 um 08:15
  3. Verschiebung zwischen Clanforen/WSB Clan Verwaltung Clan Verwaltung: Foren

    Marcel Beckers Erledigt am 22. Oktober 2026 um 00:12
  4. Massenbearbeitung von Clans Clan Verwaltung

    Marcel Beckers Erledigt am 22. Oktober 2026 um 00:10
  5. Synchronisation zwischen Clan und Benutzergruppe erweitern Clan Verwaltung

    Marcel Beckers Erledigt am 21. Oktober 2026 um 23:59
  6. Überarbeitung der eigenen Events Clan Verwaltung

    Marcel Beckers Erledigt am 21. Oktober 2026 um 23:40
Arbeitsplatz
YoureCom – Software für Vereine, Teams & Communities

YoureCom – Software für Vereine, Teams & Communities. Seit 2004.

Fertig eingerichtete Lösungen für Verein, Team und Community, dazu Erweiterungen für die WoltLab Suite. Aus Wuppertal, für Kunden weltweit.

Lösungen

  • Tipprunde
  • Aufgaben & Support
  • Mitglieder-Netzwerk
  • Online-Shop
  • Alle Lösungen & Preise

Für Administratoren

  • Plugins
  • Clan Verwaltung
  • Wettbewerbe
  • MultiHunter
  • ByteMon
  • Individuelle Entwicklung

YoureCom

  • Über mich
  • Shop
  • Kontakt
  • Neuigkeiten
  • GitHub

Cloud-Verträge

  • AGB Cloud
  • AVV Cloud
  • Rechtswidrige Inhalte melden
  • Datenwechsel

Rechtliches

  • Datenschutzerklärung
  • Impressum
  • AGB
  • Vertrag widerrufen
  • Abonnements kündigen
  • Widerrufsbelehrung
© 2026 YoureCom · Marcel Beckers