Import aus Monorepo (frischer Start)

This commit is contained in:
Lucas Orth
2026-08-24 12:14:00 +02:00
commit 8dad67b4c1
3 changed files with 579 additions and 0 deletions

155
formel.php Normal file
View File

@@ -0,0 +1,155 @@
<?php
/**
* Kleiner, sicherer Formel-Interpreter (kein eval).
* Unterstützt: Zahlen, Variablen, + - * /, Klammern, unäres Minus,
* Vergleiche (>= <= > < == !=), Ternär (a ? b : c) und die Funktionen
* sqrt, pow, abs, min, max, round, floor, ceil.
*
* Damit rechnet das Plugin mit GENAU DER Formel, die in Directus hinterlegt
* ist – Änderungen an der Formel wirken automatisch mit.
*/
if (!defined('ABSPATH')) exit;
/** Zerlegt den Ausdruck in Tokens. */
function skrift_formel_tokens($ausdruck) {
$s = (string) $ausdruck;
$s = str_replace('Math.', '', $s); // Math.sqrt(...) → sqrt(...)
$s = preg_replace('/%(\w+)%/', '$1', $s); // %qty% → qty
$tokens = array();
$pos = 0; $len = strlen($s);
while ($pos < $len) {
$c = $s[$pos];
if (ctype_space($c)) { $pos++; continue; }
if (ctype_digit($c) || ($c === '.' && $pos + 1 < $len && ctype_digit($s[$pos + 1]))) {
$start = $pos;
while ($pos < $len && (ctype_digit($s[$pos]) || $s[$pos] === '.')) $pos++;
$tokens[] = array('num', (float) substr($s, $start, $pos - $start));
continue;
}
if (ctype_alpha($c) || $c === '_') {
$start = $pos;
while ($pos < $len && (ctype_alnum($s[$pos]) || $s[$pos] === '_')) $pos++;
$tokens[] = array('id', substr($s, $start, $pos - $start));
continue;
}
$zwei = substr($s, $pos, 2);
if (in_array($zwei, array('>=', '<=', '==', '!='), true)) { $tokens[] = array('op', $zwei); $pos += 2; continue; }
$tokens[] = array('op', $c); $pos++;
}
return $tokens;
}
function skrift_formel_ist($t, $i, $wert) {
return $i < count($t) && $t[$i][0] === 'op' && $t[$i][1] === $wert;
}
function skrift_formel_fn($name, $args) {
$a0 = isset($args[0]) ? (float) $args[0] : 0.0;
switch ($name) {
case 'sqrt': return $a0 < 0 ? 0.0 : sqrt($a0);
case 'pow': return pow($a0, isset($args[1]) ? (float) $args[1] : 0.0);
case 'abs': return abs($a0);
case 'min': return empty($args) ? 0.0 : min($args);
case 'max': return empty($args) ? 0.0 : max($args);
case 'round': return round($a0);
case 'floor': return floor($a0);
case 'ceil': return ceil($a0);
}
return 0.0;
}
function skrift_formel_primary(&$i, $t, $v) {
if ($i >= count($t)) return 0.0;
$tk = $t[$i];
if ($tk[0] === 'num') { $i++; return $tk[1]; }
if ($tk[0] === 'id') {
$name = $tk[1]; $i++;
if (skrift_formel_ist($t, $i, '(')) {
$i++; $args = array();
if (!skrift_formel_ist($t, $i, ')')) {
$args[] = skrift_formel_ternary($i, $t, $v);
while (skrift_formel_ist($t, $i, ',')) { $i++; $args[] = skrift_formel_ternary($i, $t, $v); }
}
if (skrift_formel_ist($t, $i, ')')) $i++;
return skrift_formel_fn($name, $args);
}
return isset($v[$name]) ? (float) $v[$name] : 0.0;
}
if ($tk[0] === 'op' && $tk[1] === '(') {
$i++; $r = skrift_formel_ternary($i, $t, $v);
if (skrift_formel_ist($t, $i, ')')) $i++;
return $r;
}
$i++; return 0.0;
}
function skrift_formel_unary(&$i, $t, $v) {
if (skrift_formel_ist($t, $i, '-')) { $i++; return -skrift_formel_unary($i, $t, $v); }
if (skrift_formel_ist($t, $i, '+')) { $i++; return skrift_formel_unary($i, $t, $v); }
return skrift_formel_primary($i, $t, $v);
}
function skrift_formel_mul(&$i, $t, $v) {
$l = skrift_formel_unary($i, $t, $v);
while (skrift_formel_ist($t, $i, '*') || skrift_formel_ist($t, $i, '/')) {
$op = $t[$i][1]; $i++;
$r = skrift_formel_unary($i, $t, $v);
$l = $op === '*' ? $l * $r : ($r == 0 ? 0.0 : $l / $r);
}
return $l;
}
function skrift_formel_add(&$i, $t, $v) {
$l = skrift_formel_mul($i, $t, $v);
while (skrift_formel_ist($t, $i, '+') || skrift_formel_ist($t, $i, '-')) {
$op = $t[$i][1]; $i++;
$r = skrift_formel_mul($i, $t, $v);
$l = $op === '+' ? $l + $r : $l - $r;
}
return $l;
}
function skrift_formel_compare(&$i, $t, $v) {
$l = skrift_formel_add($i, $t, $v);
while ($i < count($t) && $t[$i][0] === 'op'
&& in_array($t[$i][1], array('>=', '<=', '==', '!=', '>', '<'), true)) {
$op = $t[$i][1]; $i++;
$r = skrift_formel_add($i, $t, $v);
switch ($op) {
case '>=': $l = $l >= $r; break;
case '<=': $l = $l <= $r; break;
case '>': $l = $l > $r; break;
case '<': $l = $l < $r; break;
case '==': $l = $l == $r; break;
case '!=': $l = $l != $r; break;
}
}
return $l;
}
function skrift_formel_ternary(&$i, $t, $v) {
$c = skrift_formel_compare($i, $t, $v);
if (skrift_formel_ist($t, $i, '?')) {
$i++;
$a = skrift_formel_ternary($i, $t, $v);
if (skrift_formel_ist($t, $i, ':')) $i++;
$b = skrift_formel_ternary($i, $t, $v);
return $c ? $a : $b;
}
return $c;
}
/** Wertet den Ausdruck aus. Bei Problemen wird $fallback zurückgegeben. */
function skrift_formel_eval($ausdruck, $vars, $fallback = 1.0) {
if (!is_string($ausdruck) || trim($ausdruck) === '') return $fallback;
try {
$t = skrift_formel_tokens($ausdruck);
$i = 0;
$r = (float) skrift_formel_ternary($i, $t, $vars);
if (!is_finite($r)) return $fallback;
return $r;
} catch (Throwable $e) {
return $fallback;
}
}