Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,85 @@
<?php
/**
* This file is part of the MathExecutor package
*
* (c) Alexander Kiryukhin
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code
*/
namespace NXP\Classes;
use NXP\Exception\IncorrectExpressionException;
use NXP\Exception\UnknownFunctionException;
use NXP\Exception\UnknownOperatorException;
use NXP\Exception\UnknownVariableException;
/**
* @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
*/
class Calculator
{
/**
* @todo PHP8: Use constructor property promotion -> public function __construct(private array $functions, private array $operators)
*
* @param array<string, CustomFunction> $functions
* @param array<Operator> $operators
*/
public function __construct(private array $functions, private array $operators)
{
}
/**
* Calculate array of tokens in reverse polish notation
*
* @param Token[] $tokens
* @param array<string, float|string> $variables
*
* @throws UnknownVariableException
* @throws IncorrectExpressionException
* @return int|float|string|null
*/
public function calculate(array $tokens, array $variables, ?callable $onVarNotFound = null)
{
/** @var Token[] $stack */
$stack = [];
foreach ($tokens as $token) {
if (Token::Literal === $token->type || Token::String === $token->type) {
$stack[] = $token;
} elseif (Token::Variable === $token->type) {
$variable = $token->value;
$value = null;
if (\array_key_exists($variable, $variables)) {
$value = $variables[$variable];
} elseif ($onVarNotFound) {
$value = \call_user_func($onVarNotFound, $variable);
} else {
throw new UnknownVariableException($variable);
}
$stack[] = new Token(Token::Literal, $value, $variable);
} elseif (Token::Function === $token->type) {
if (! \array_key_exists($token->value, $this->functions)) {
throw new UnknownFunctionException($token->value);
}
$stack[] = $this->functions[$token->value]->execute($stack, $token->paramCount);
} elseif (Token::Operator === $token->type) {
if (! \array_key_exists($token->value, $this->operators)) {
throw new UnknownOperatorException($token->value);
}
$stack[] = $this->operators[$token->value]->execute($stack);
}
}
$result = \array_pop($stack);
if (null === $result || ! empty($stack)) {
throw new IncorrectExpressionException('Stack must be empty');
}
return $result->value;
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace NXP\Classes;
use NXP\Exception\IncorrectNumberOfFunctionParametersException;
use ReflectionException;
use ReflectionFunction;
class CustomFunction
{
/**
* @var callable $function
*/
public $function;
private bool $isVariadic;
private int $totalParamCount;
private int $requiredParamCount;
/**
* CustomFunction constructor.
*
* @throws ReflectionException
*/
public function __construct(public string $name, callable $function)
{
$this->function = $function;
$reflection = (new ReflectionFunction($function));
$this->isVariadic = $reflection->isVariadic();
$this->totalParamCount = $reflection->getNumberOfParameters();
$this->requiredParamCount = $reflection->getNumberOfRequiredParameters();
}
/**
* @param array<Token> $stack
*
* @throws IncorrectNumberOfFunctionParametersException
*/
public function execute(array &$stack, int $paramCountInStack) : Token
{
if ($paramCountInStack < $this->requiredParamCount) {
throw new IncorrectNumberOfFunctionParametersException($this->name);
}
if ($paramCountInStack > $this->totalParamCount && ! $this->isVariadic) {
throw new IncorrectNumberOfFunctionParametersException($this->name);
}
$args = [];
if ($paramCountInStack > 0) {
for ($i = 0; $i < $paramCountInStack; $i++) {
\array_unshift($args, \array_pop($stack)->value);
}
}
$result = \call_user_func_array($this->function, $args);
return new Token(Token::Literal, $result);
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace NXP\Classes;
use NXP\Exception\IncorrectExpressionException;
use ReflectionFunction;
class Operator
{
/**
* @var callable(\SplStack)
*/
public $function;
public int $places = 0;
/**
* Operator constructor.
*/
public function __construct(public string $operator, public bool $isRightAssoc, public int $priority, callable $function)
{
$this->function = $function;
$reflection = new ReflectionFunction($function);
$this->places = $reflection->getNumberOfParameters();
}
/**
* @param array<Token> $stack
*
* @throws IncorrectExpressionException
*/
public function execute(array &$stack) : Token
{
if (\count($stack) < $this->places) {
throw new IncorrectExpressionException();
}
$args = [];
for ($i = 0; $i < $this->places; $i++) {
\array_unshift($args, \array_pop($stack)->value);
}
$result = \call_user_func_array($this->function, $args);
return new Token(Token::Literal, $result);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace NXP\Classes;
class Token
{
public const Literal = 'literal';
public const Variable = 'variable';
public const Operator = 'operator';
public const LeftParenthesis = 'LP';
public const RightParenthesis = 'RP';
public const Function = 'function';
public const ParamSeparator = 'separator';
public const String = 'string';
public const Space = 'space';
public ?int $paramCount = null;//to store function parameter count in stack
/**
* Token constructor.
*
*/
public function __construct(public string $type, public mixed $value, public ?string $name = null)
{
}
}

View File

@@ -0,0 +1,411 @@
<?php
/**
* This file is part of the MathExecutor package
*
* (c) Alexander Kiryukhin
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code
*/
namespace NXP\Classes;
use NXP\Exception\IncorrectBracketsException;
use NXP\Exception\UnknownOperatorException;
use RuntimeException;
use SplStack;
/**
* @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
*/
class Tokenizer
{
/** @var array<Token> */
public array $tokens = [];
private string $numberBuffer = '';
private string $stringBuffer = '';
private bool $allowNegative = true;
private bool $inSingleQuotedString = false;
private bool $inDoubleQuotedString = false;
/**
* Tokenizer constructor.
* @param Operator[] $operators
*/
public function __construct(private string $input, private array $operators)
{
}
public function tokenize() : self
{
$isLastCharEscape = false;
foreach (\str_split($this->input) as $ch) {
switch (true) {
case $this->inSingleQuotedString:
if ('\\' === $ch) {
if ($isLastCharEscape) {
$this->stringBuffer .= '\\';
$isLastCharEscape = false;
} else {
$isLastCharEscape = true;
}
continue 2;
} elseif ("'" === $ch) {
if ($isLastCharEscape) {
$this->stringBuffer .= "'";
$isLastCharEscape = false;
} else {
$this->tokens[] = new Token(Token::String, $this->stringBuffer);
$this->inSingleQuotedString = false;
$this->stringBuffer = '';
}
continue 2;
}
if ($isLastCharEscape) {
$this->stringBuffer .= '\\';
$isLastCharEscape = false;
}
$this->stringBuffer .= $ch;
continue 2;
case $this->inDoubleQuotedString:
if ('\\' === $ch) {
if ($isLastCharEscape) {
$this->stringBuffer .= '\\';
$isLastCharEscape = false;
} else {
$isLastCharEscape = true;
}
continue 2;
} elseif ('"' === $ch) {
if ($isLastCharEscape) {
$this->stringBuffer .= '"';
$isLastCharEscape = false;
} else {
$this->tokens[] = new Token(Token::String, $this->stringBuffer);
$this->inDoubleQuotedString = false;
$this->stringBuffer = '';
}
continue 2;
}
if ($isLastCharEscape) {
$this->stringBuffer .= '\\';
$isLastCharEscape = false;
}
$this->stringBuffer .= $ch;
continue 2;
case '[' === $ch:
$this->tokens[] = new Token(Token::Function, 'array');
$this->allowNegative = true;
$this->tokens[] = new Token(Token::LeftParenthesis, '');
continue 2;
case ' ' == $ch || "\n" == $ch || "\r" == $ch || "\t" == $ch:
$this->emptyNumberBufferAsLiteral();
$this->emptyStrBufferAsVariable();
$this->tokens[] = new Token(Token::Space, '');
continue 2;
case $this->isNumber($ch):
if ('' != $this->stringBuffer) {
$this->stringBuffer .= $ch;
continue 2;
}
$this->numberBuffer .= $ch;
$this->allowNegative = false;
break;
/** @noinspection PhpMissingBreakStatementInspection */
case 'e' === \strtolower($ch):
if (\strlen($this->numberBuffer) && \str_contains($this->numberBuffer, '.')) {
$this->numberBuffer .= 'e';
$this->allowNegative = false;
break;
}
// no break
// Intentionally fall through
case $this->isAlpha($ch):
if (\strlen($this->numberBuffer)) {
$this->emptyNumberBufferAsLiteral();
$this->tokens[] = new Token(Token::Operator, '*');
}
$this->allowNegative = false;
$this->stringBuffer .= $ch;
break;
case '"' == $ch:
$this->inDoubleQuotedString = true;
continue 2;
case "'" == $ch:
$this->inSingleQuotedString = true;
continue 2;
case $this->isDot($ch):
$this->numberBuffer .= $ch;
$this->allowNegative = false;
break;
case $this->isLP($ch):
if ('' != $this->stringBuffer) {
$this->tokens[] = new Token(Token::Function, $this->stringBuffer);
$this->stringBuffer = '';
} elseif (\strlen($this->numberBuffer)) {
$this->emptyNumberBufferAsLiteral();
$this->tokens[] = new Token(Token::Operator, '*');
}
$this->allowNegative = true;
$this->tokens[] = new Token(Token::LeftParenthesis, '');
break;
case $this->isRP($ch) || ']' === $ch :
$this->emptyNumberBufferAsLiteral();
$this->emptyStrBufferAsVariable();
$this->allowNegative = false;
$this->tokens[] = new Token(Token::RightParenthesis, '');
break;
case $this->isComma($ch):
$this->emptyNumberBufferAsLiteral();
$this->emptyStrBufferAsVariable();
$this->allowNegative = true;
$this->tokens[] = new Token(Token::ParamSeparator, '');
break;
default:
// special case for unary operations
if ('-' == $ch || '+' == $ch) {
if ($this->allowNegative) {
$this->allowNegative = false;
$this->tokens[] = new Token(Token::Operator, '-' == $ch ? 'uNeg' : 'uPos');
continue 2;
}
// could be in exponent, in which case negative should be added to the numberBuffer
if ($this->numberBuffer && 'e' == $this->numberBuffer[\strlen($this->numberBuffer) - 1]) {
$this->numberBuffer .= $ch;
continue 2;
}
}
$this->emptyNumberBufferAsLiteral();
$this->emptyStrBufferAsVariable();
if ('$' != $ch) {
if (\count($this->tokens) > 0) {
if (Token::Operator === $this->tokens[\count($this->tokens) - 1]->type) {
$this->tokens[\count($this->tokens) - 1]->value .= $ch;
} else {
$this->tokens[] = new Token(Token::Operator, $ch);
}
} else {
$this->tokens[] = new Token(Token::Operator, $ch);
}
}
$this->allowNegative = true;
}
}
$this->emptyNumberBufferAsLiteral();
$this->emptyStrBufferAsVariable();
return $this;
}
/**
* @throws IncorrectBracketsException
* @throws UnknownOperatorException
* @return Token[] Array of tokens in revers polish notation
*/
public function buildReversePolishNotation() : array
{
$tokens = [];
/** @var SplStack<Token> $stack */
$stack = new SplStack();
/**
* @var SplStack<int> $paramCounter
*/
$paramCounter = new SplStack();
foreach ($this->tokens as $token) {
switch ($token->type) {
case Token::Literal:
case Token::Variable:
case Token::String:
$tokens[] = $token;
if ($paramCounter->count() > 0 && 0 === $paramCounter->top()) {
$paramCounter->push($paramCounter->pop() + 1);
}
break;
case Token::Function:
if ($paramCounter->count() > 0 && 0 === $paramCounter->top()) {
$paramCounter->push($paramCounter->pop() + 1);
}
$stack->push($token);
$paramCounter->push(0);
break;
case Token::LeftParenthesis:
$stack->push($token);
break;
case Token::ParamSeparator:
while (Token::LeftParenthesis !== $stack->top()->type) {
if (0 === $stack->count()) {
throw new IncorrectBracketsException();
}
$tokens[] = $stack->pop();
}
$paramCounter->push($paramCounter->pop() + 1);
break;
case Token::Operator:
if (! \array_key_exists($token->value, $this->operators)) {
throw new UnknownOperatorException($token->value);
}
$op1 = $this->operators[$token->value];
while ($stack->count() > 0 && Token::Operator === $stack->top()->type) {
if (! \array_key_exists($stack->top()->value, $this->operators)) {
throw new UnknownOperatorException($stack->top()->value);
}
$op2 = $this->operators[$stack->top()->value];
if ($op2->priority >= $op1->priority) {
$tokens[] = $stack->pop();
continue;
}
break;
}
$stack->push($token);
break;
case Token::RightParenthesis:
while (true) {
try {
$ctoken = $stack->pop();
if (Token::LeftParenthesis === $ctoken->type) {
break;
}
$tokens[] = $ctoken;
} catch (RuntimeException) {
throw new IncorrectBracketsException();
}
}
if ($stack->count() > 0 && Token::Function == $stack->top()->type) {
/**
* @var Token $f
*/
$f = $stack->pop();
$f->paramCount = $paramCounter->pop();
$tokens[] = $f;
}
break;
case Token::Space:
//do nothing
}
}
while (0 !== $stack->count()) {
if (Token::LeftParenthesis === $stack->top()->type || Token::RightParenthesis === $stack->top()->type) {
throw new IncorrectBracketsException();
}
if (Token::Space === $stack->top()->type) {
$stack->pop();
continue;
}
$tokens[] = $stack->pop();
}
return $tokens;
}
private function isNumber(string $ch) : bool
{
return $ch >= '0' && $ch <= '9';
}
private function isAlpha(string $ch) : bool
{
return $ch >= 'a' && $ch <= 'z' || $ch >= 'A' && $ch <= 'Z' || '_' == $ch;
}
private function emptyNumberBufferAsLiteral() : void
{
if (\strlen($this->numberBuffer)) {
$this->tokens[] = new Token(Token::Literal, $this->numberBuffer);
$this->numberBuffer = '';
}
}
private function isDot(string $ch) : bool
{
return '.' == $ch;
}
private function isLP(string $ch) : bool
{
return '(' == $ch;
}
private function isRP(string $ch) : bool
{
return ')' == $ch;
}
private function emptyStrBufferAsVariable() : void
{
if ('' != $this->stringBuffer) {
$this->tokens[] = new Token(Token::Variable, $this->stringBuffer);
$this->stringBuffer = '';
}
}
private function isComma(string $ch) : bool
{
return ',' == $ch;
}
}