index of
/
home
/
thefkyzp
/
public_html
/
wp-content
/
plugins
/
forminator
/
library
/
calculator
/
File: /home/thefkyzp/public_html/wp-content/plugins/forminator/library/calculator/class-calculator.php
<?php /** * The Forminator_Calculator class. * * @package Forminator */ if ( ! defined( 'ABSPATH' ) ) { die(); } /********** * Attempt to rewrite * https://github.com/chriskonnertz/string-calc * License (26-dec-2018) : MIT * * What changes : * - PHP 5.2 Compat * - Reduce/Remove extensibility, since it will managed by ours in the future * - Remove unnecessary abstract classes to reduce size * - wpcs compat * - use WP hook as extensibility alternatives */ require_once __DIR__ . '/class-exception.php'; // tokenizer. require_once __DIR__ . '/parser/class-token.php'; require_once __DIR__ . '/parser/class-tokenizer.php'; // load symbols. require_once __DIR__ . '/symbol/class-loader.php'; // load nodes. require_once __DIR__ . '/parser/node/abstract-class.php'; require_once __DIR__ . '/parser/node/class-container.php'; require_once __DIR__ . '/parser/node/class-function.php'; require_once __DIR__ . '/parser/node/class-symbol.php'; // parser. require_once __DIR__ . '/parser/class-parser.php'; /** * Class Forminator_Calculator * * @since 1.7 */ class Forminator_Calculator { /** * Term * * @var string */ protected $term; /** * Tokenizer * * @var Forminator_Calculator_Parser_Tokenizer */ protected $tokenizer; /** * Symbol loader * * @var Forminator_Calculator_Symbol_Loader */ protected $symbol_loader; /** * Throwable * * @var bool */ protected $is_throwable = false; /** * Parser * * @var Forminator_Calculator_Parser */ protected $parser; /** * Forminator_Calculator constructor * * @param mixed $term Term. */ public function __construct( $term ) { $this->term = $term; $this->setup_tokenizer(); $this->symbol_loader = new Forminator_Calculator_Symbol_Loader(); $this->setup_parser(); $this->prepare_term(); } /** * Setup tokenizer * * @uses apply_filters() */ public function setup_tokenizer() { $term = $this->term; $tokenizer = new Forminator_Calculator_Parser_Tokenizer( $term ); /** * Filter tokenizer to be used on calculator * * @since 1.7 * * @param Forminator_Calculator_Parser_Tokenizer $tokenizer Tokenizer. * @param string $term Term. * * @return Forminator_Calculator_Parser_Tokenizer */ $tokenizer = apply_filters( 'forminator_calculator_tokenizer', $tokenizer, $term ); $this->tokenizer = $tokenizer; } /** * Setup Parser * * @uses apply_filters() */ public function setup_parser() { $term = $this->term; $symbol_loader = $this->symbol_loader; $parser = new Forminator_Calculator_Parser( $symbol_loader ); /** * Filter parser to be used on calculator * * @since 1.7 * * @param Forminator_Calculator_Parser $parser * @param Forminator_Calculator_Symbol_Loader $symbol_loader * @param string $term * * @return Forminator_Calculator_Parser */ $parser = apply_filters( 'forminator_calculator_tokenizer', $parser, $symbol_loader, $term ); $this->parser = $parser; } /** * Prepare term * * @return void */ public function prepare_term() { $term = $this->term; /** * Filter term that will be parsed by calculator * * @since 1.7 * * @param string $term * * @return string */ $term = apply_filters( 'forminator_calculator_prepare_term', $term ); $this->term = $term; } /** * Parse * * @return Forminator_Calculator_Parser_Node_Container|boolean * @throws Forminator_Calculator_Exception When there is an Calculator error. */ public function parse() { try { // reset. $this->tokenizer->input = $this->term; $this->tokenizer->reset(); $tokens = $this->tokenizer->tokenize(); if ( count( $tokens ) === 0 ) { throw new Forminator_Calculator_Exception( 'Error: Empty token of calculator term.' ); } $root_node = $this->parser->parse( $tokens ); if ( $root_node->is_empty() ) { throw new Forminator_Calculator_Exception( 'Error: Empty nodes of calculator tokens.' ); } return $root_node; } catch ( Forminator_Calculator_Exception $e ) { // suppress. forminator_maybe_log( __METHOD__, $e->getMessage(), $e->getTrace() ); if ( $this->is_throwable ) { throw $e; } } return false; } /** * Calculate * * @return float|int * @throws Forminator_Calculator_Exception When there is an Calculator error. */ public function calculate() { $result = 0; $root_node = $this->parse(); if ( false === $root_node ) { return $result; } try { $result = $this->calculate_node( $root_node ); } catch ( Forminator_Calculator_Exception $e ) { // suppress. forminator_maybe_log( __METHOD__, $e->getMessage(), $e->getTrace() ); if ( $this->is_throwable ) { throw $e; } } return $result; } /** * Calculates the numeric value / result of a node of * any known and calculable type. (For example symbol * nodes with a symbol of type separator are not * calculable.) * * @param Forminator_Calculator_Parser_Node_Abstract $node Forminator_Calculator_Parser_Node_Abstract. * * @return float|int * @throws Forminator_Calculator_Exception When there is an Calculator error. */ protected function calculate_node( $node ) { if ( $node instanceof Forminator_Calculator_Parser_Node_Symbol ) { /** * Forminator_Calculator_Parser_Node_Symbol * * @var Forminator_Calculator_Parser_Node_Symbol $node */ return $this->calculate_symbol_node( $node ); } elseif ( $node instanceof Forminator_Calculator_Parser_Node_Function ) { /** * Forminator_Calculator_Parser_Node_Function * * @var Forminator_Calculator_Parser_Node_Function $node */ return $this->calculate_function_node( $node ); } elseif ( $node instanceof Forminator_Calculator_Parser_Node_Container ) { /** * Forminator_Calculator_Parser_Node_Container * * @var Forminator_Calculator_Parser_Node_Container $node */ return $this->calculate_container_node( $node ); } else { throw new Forminator_Calculator_Exception( 'Error: Cannot calculate node of unknown type "' . esc_html( get_class( $node ) ) . '"' );// @codeCoverageIgnore. } } /** * This method actually calculates the results of every sub-terms * in the syntax tree (which consists of nodes). * It can call itself recursively. * Attention: $node must not be of type FunctionNode! * * @param Forminator_Calculator_Parser_Node_Container $container_node Forminator_Calculator_Parser_Node_Container. * * @return float|int * @throws Forminator_Calculator_Exception When there is an Calculator error. */ protected function calculate_container_node( $container_node ) { if ( $container_node instanceof Forminator_Calculator_Parser_Node_Function ) { throw new Forminator_Calculator_Exception( 'Error: Expected container node but got a function node' ); // @codeCoverageIgnore. } $nodes = $container_node->get_child_nodes(); $ordered_operator_nodes = $this->detect_calculation_order( $nodes ); // Actually calculate the term. Iterates over the ordered operators and. // calculates them, then replaces the parts of the operation by the result. foreach ( $ordered_operator_nodes as $index => $operator_node ) { reset( $nodes ); while ( key( $nodes ) !== $index ) { $left_operand = current( $nodes ); $left_operand_index = key( $nodes ); next( $nodes ); // back to operator cursor. } $right_operand = next( $nodes ); $right_operand_index = key( $nodes ); $right_number = is_numeric( $right_operand ) ? $right_operand : $this->calculate_node( $right_operand ); /** * Forminator_Calculator_Symbol_Operator_Abstract * * @var Forminator_Calculator_Symbol_Operator_Abstract $symbol */ $symbol = $operator_node->get_symbol(); if ( $operator_node->is_unary_operator() ) { $result = $symbol->operate( null, $right_number ); // Replace the participating symbols of the operation by the result. unset( $nodes[ $right_operand_index ] ); $nodes[ $index ] = $result; } elseif ( isset( $left_operand_index ) && isset( $left_operand ) ) { $left_number = is_numeric( $left_operand ) ? $left_operand : $this->calculate_node( $left_operand ); $result = $symbol->operate( $left_number, $right_number ); // Replace the participating symbols of the operation by the result. unset( $nodes[ $left_operand_index ] ); unset( $nodes[ $right_operand_index ] ); $nodes[ $index ] = $result; } } if ( count( $nodes ) === 0 ) { throw new Forminator_Calculator_Exception( 'Error: Missing calculable subterm. Are there empty brackets?' ); } if ( count( $nodes ) > 1 ) { throw new Forminator_Calculator_Exception( 'Error: Missing operators between parts of the term.' ); } // The only remaining element of the $nodes array contains the overall result. $result = end( $nodes ); // If the $nodes array did not contain any operator (but only one node) than. // the result of this node has to be calculated now. if ( ! is_numeric( $result ) ) { return $this->calculate_node( $result ); } return $result; } /** * Returns the numeric value of a function node. * * @param Forminator_Calculator_Parser_Node_Function $function_node Forminator_Calculator_Parser_Node_Function. * * @return int|float */ protected function calculate_function_node( $function_node ) { $nodes = $function_node->get_child_nodes(); $arguments = array(); // ex : func(1+2,3,4) : 1+2 need to be calculated first. $argument_child_nodes = array(); foreach ( $nodes as $node ) { if ( $node instanceof Forminator_Calculator_Parser_Node_Symbol ) { /** * Forminator_Calculator_Parser_Node_Symbol * * @var Forminator_Calculator_Parser_Node_Symbol $node */ if ( $node->get_symbol() instanceof Forminator_Calculator_Symbol_Separator ) { $container_node = new Forminator_Calculator_Parser_Node_Container( $argument_child_nodes ); $arguments[] = $this->calculate_container_node( $container_node ); $argument_child_nodes = array(); } else { $argument_child_nodes[] = $node; } } else { $argument_child_nodes[] = $node; } } if ( count( $argument_child_nodes ) > 0 ) { $container_node = new Forminator_Calculator_Parser_Node_Container( $argument_child_nodes ); $arguments[] = $this->calculate_container_node( $container_node ); } /** * Forminator_Calculator_Symbol_Function_Abstract * * @var Forminator_Calculator_Symbol_Function_Abstract $symbol */ $symbol = $function_node->get_symbol_node()->get_symbol(); $result = $symbol->execute( $arguments ); return $result; } /** * Returns the numeric value of a symbol node. * Attention: $node->symbol must not be of type AbstractOperator! * * @param Forminator_Calculator_Parser_Node_Symbol $symbol_node Node symbol. * * @return int|float * @throws Forminator_Calculator_Exception When there is an Calculator error. */ protected function calculate_symbol_node( $symbol_node ) { $symbol = $symbol_node->get_symbol(); if ( $symbol instanceof Forminator_Calculator_Symbol_Number ) { $number = $symbol_node->get_token()->value; // Convert string to int or float (depending on the type of the number). // Attention: The fractional part of a PHP float can only have a limited length. // If the number has a longer fractional part, it will be cut. $number = 0 + $number; } elseif ( $symbol instanceof Forminator_Calculator_Symbol_Constant_Abstract ) { /** * Forminator_Calculator_Symbol_Constant_Abstract * * @var Forminator_Calculator_Symbol_Constant_Abstract $symbol */ $number = $symbol->get_value(); } else { throw new Forminator_Calculator_Exception( 'Error: Found symbol of unexpected type "' . esc_html( get_class( $symbol ) ) . '", expected number or constant' ); } return $number; } /** * Detect the calculation order of a given array of nodes. * Does only care for the precedence of operators. * Does not care for child nodes of container nodes. * Returns a new array with ordered symbol nodes. * * @param Forminator_Calculator_Parser_Node_Abstract[] $nodes Forminator_Calculator_Parser_Node_Abstract. * * @return Forminator_Calculator_Parser_Node_Symbol[] */ protected function detect_calculation_order( $nodes ) { $operator_nodes = array(); // Store all symbol nodes that have a symbol of type abstract operator in an array. foreach ( $nodes as $index => $node ) { if ( $node instanceof Forminator_Calculator_Parser_Node_Symbol ) { if ( $node->get_symbol() instanceof Forminator_Calculator_Symbol_Operator_Abstract ) { $operator_nodes[ $index ] = $node; } } } // Using Quick-sort algorithm to sort the operators according to their precedence. Keeps the indices. uasort( $operator_nodes, array( $this, 'sort_operator_precedence' ) ); return $operator_nodes; } /** * * Returning 1 means $nodeTwo before $nodeOne, returning -1 means $nodeOne before $nodeTwo. * * @param Forminator_Calculator_Parser_Node_Symbol $node_one Forminator_Calculator_Parser_Node_Symbol. * @param Forminator_Calculator_Parser_Node_Symbol $node_two Forminator_Calculator_Parser_Node_Symbol. * * @return int */ private function sort_operator_precedence( $node_one, $node_two ) { // First-level precedence of node one. /** * Forminator_Calculator_Symbol_Operator_Abstract * * @var Forminator_Calculator_Symbol_Operator_Abstract $symbol_one */ $symbol_one = $node_one->get_symbol(); $precedence_one = 2; if ( $node_one->is_unary_operator() ) { $precedence_one = 3; } // First-level precedence of node two. /** * Forminator_Calculator_Symbol_Operator_Abstract * * @var Forminator_Calculator_Symbol_Operator_Abstract $symbol_two */ $symbol_two = $node_two->get_symbol(); $precedence_two = 2; if ( $node_two->is_unary_operator() ) { $precedence_two = 3; } // If the first-level precedence is the same, compare the second-level precedence. if ( $precedence_one === $precedence_two ) { $precedence_one = $symbol_one->get_precedence(); $precedence_two = $symbol_two->get_precedence(); } // If the second-level precedence is the same, we have to ensure that the sorting algorithm does. // insert the node / token that is left in the term before the node / token that is right. // Therefore we cannot return 0 but compare the positions and return 1 / -1. if ( $precedence_one === $precedence_two ) { return ( $node_one->get_token()->position < $node_two->get_token()->position ) ? - 1 : 1; } return ( $precedence_one < $precedence_two ) ? 1 : - 1; } /** * Set is throwable * * @param true $is_throwable Is throwable?. */ public function set_is_throwable( $is_throwable ) { $this->is_throwable = $is_throwable; } }