{"id":16749646,"url":"https://github.com/chriskonnertz/string-calc","last_synced_at":"2025-04-06T04:13:48.220Z","repository":{"id":19350155,"uuid":"86851914","full_name":"chriskonnertz/string-calc","owner":"chriskonnertz","description":"PHP calculator library for mathematical terms (expressions) passed as strings","archived":false,"fork":false,"pushed_at":"2022-05-13T17:36:38.000Z","size":314,"stargazers_count":100,"open_issues_count":6,"forks_count":19,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-03-30T03:07:24.445Z","etag":null,"topics":["calc","calculate","calculator","math","mathematical","mathematics","parser","php","php-calculator","string","term","tokenizer"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chriskonnertz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-31T19:08:18.000Z","updated_at":"2025-02-27T12:51:07.000Z","dependencies_parsed_at":"2022-08-08T19:00:05.660Z","dependency_job_id":null,"html_url":"https://github.com/chriskonnertz/string-calc","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chriskonnertz%2Fstring-calc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chriskonnertz%2Fstring-calc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chriskonnertz%2Fstring-calc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chriskonnertz%2Fstring-calc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chriskonnertz","download_url":"https://codeload.github.com/chriskonnertz/string-calc/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247430883,"owners_count":20937874,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["calc","calculate","calculator","math","mathematical","mathematics","parser","php","php-calculator","string","term","tokenizer"],"created_at":"2024-10-13T02:25:30.094Z","updated_at":"2025-04-06T04:13:48.089Z","avatar_url":"https://github.com/chriskonnertz.png","language":"PHP","readme":"# StringCalc 2\n\n[![Build Status](https://img.shields.io/github/workflow/status/chriskonnertz/string-calc/PHP%20Composer?style=flat-square)](https://github.com/chriskonnertz/string-calc/actions)\n[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://raw.githubusercontent.com/chriskonnertz/string-calc/master/LICENSE)\n[![Source](https://img.shields.io/badge/source-chriskonnertz%2Fstring--calc-blue.svg?style=flat-square)](https://github.com/chriskonnertz/string-calc)\n[![Version](https://img.shields.io/packagist/v/chriskonnertz/string-calc.svg?style=flat-square)](https://packagist.org/packages/chriskonnertz/string-calc)\n[![Version](https://img.shields.io/packagist/dt/chriskonnertz/string-calc?style=flat-square)](https://packagist.org/packages/chriskonnertz/string-calc)\n\nStringCalc is a zero-dependency PHP calculator library for mathematical terms (expressions) passed as strings. \n\n## Installation\n\nThrough Composer:\n\n```\ncomposer require chriskonnertz/string-calc\n```\n\n\u003e This library requires PHP 8.0 or higher. For PHP 5.6 support, use StringCalc 1.\n\n## Usage example\n\nHere is a minimalistic example of PHP code that calculates a term. It assumes that there is an autoloader.\n\n```php\n$stringCalc = new ChrisKonnertz\\StringCalc\\StringCalc();\n\n$term = '1+2';\n\n$result = $stringCalc-\u003ecalculate($term); // $result will contain 3\n```\n\n\u003e There is an interactive PHP demo script included. It is located at `dev/demo.php`.\n\n## Motivation\n\nImagine you are building a web application that allows users to enter mathematical terms in a text field.\n How would you calculate the results of such terms? PHP's [eval](http://php.net/manual/de/function.eval.php) function\n is not the answer. The official documentation recommends not to use this function: \n _The eval() language construct is very dangerous because it allows execution of arbitrary PHP code. \n Its use thus is discouraged._ StringCalc has its own parser implementation and therefore is a better and \n save alternative. StringCalc follows modern coding principles, is extensible and well documented. \n If you have any suggestions how to improve this library, feel free to discuss them in the [issue](https://github.com/chriskonnertz/string-calc/issues) section.\n\n## The term\n\nExample, we need an example! Here we go: `2*(pi-abs(-0.4))`\n\nThis is a mathematical term following syntactical and grammatical rules that StringCalc understands. \nSyntax and grammar of these terms are very similar to what you would write in PHP code. \nTo be more precise, there is an intersecting set of syntactical and grammatical rules. \nThere are some exceptions, but usually you will be able to write terms for StringCalc \nby pretending that you are writing PHP code. \n\n### Term examples\n\nHere are some unspectacular examples:\n\n```\n1+2*3-4\n1 + 2 * 3 - 4\npi * 2\nPI * 2\nabs(1) + min(1,2) * max(1,2,3)\nmin(1+2, abs(-1))\n1 + ((2 - 3) * (5 - 7))\n2 * (-3)\n```\n\nHere is a list that shows examples with more exotic syntax:\n\n```\n1         // A term can consist of just a number\n(1+((2))) // Usage of obsolete brackets is allowed\n00001     // Prefixing a number with obsolete zero digits is possible\n.1        // Ommiting a zero digit before a period charcter is okay\n```\n\nTo see a list of all available types of mathematical symbols (parts of a term) follow this link:\n[Symbols/Concrete classes](src/ChrisKonnertz/StringCalc/Symbols/Concrete)\n\n## List of symbols\n\nOperators:\n````\n+\n-\n*\n/\n````\n\nFunctions:\n````\nabs\naCos\naCosH\naSin\naSinH\naTan\naTanH\naTanTwo\nceil\ncos\ncosH\ndegToRad\nen\nexp\nexpMOne\nfloor\nfMod\nintVal\nhypot\nlog\nlogOneP\nlogTen\nmax\nmin\npow\nradToDeg\nround\nsin\nsinH\nsqrt\ntan\ntanH\n````\n\nConstants:\n````\ne\neuler\nlnPi\nlnTen\nlnTwo\nlogTenE\nlogTwoE\nonePi\npi\npiFour\npiTwo\nsqrtOneTwo\nsqrtPi\nsqrtThree\nsqrtTwo\ntwoPi\ntwoSqrtPi\n````\n\nOthers:\n````\n(\n)\n,\n.\n````\n\n## The StringCalc class\n\nThe `StringCalc` is the  is the API frontend of the StringCalc library. \nThis section describes the public methods of this class.\n\n### Constructor\n\nThe constructor has one optional parameter named `$container` that implements the `Container\\ContainerInterface`. \nThis is the service container used by StringCalc. \nIf no argument is passed, the constructor will create a new container object with the type `Container\\Container` on its own.\nThe container interface ensures that the container implements the \n[PSR-11 standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-11-container.md). Therefore, you can\nreplace the default container with every other container that implements the PSR-11 standard, but you have to wrap it \nin a wrapper class that makes it compatible to the `Container\\Container` class. We recommend avoiding the extra afford \nand to extend the `Container\\Container` class instead.\n\n### calculate\n\nThe `calculate()` method is the most important method of this class. \nIt expects one parameter with the type string called `$term`.\nIt returns a number with the type float or int. We strongly recommend wrapping any calls of this method in a `try-catch` block \nand to write a `catch` statement that catches all exceptions with the type `Exceptions\\StringCalcException`.\n \n```php\ntry {\n    $result = $stringCalc-\u003ecalculate('min()');\n} catch (Exceptions\\StringCalcException $exception) {\n    ... // Handle exception\n} catch (\\Exception $exception) {\n    ... // Handle exception. \n}\n```\n\nIn the example an exception with class `StringCalcException` will be thrown, \nbecause the `min` method has to be called with one parameter at least. Exceptions with the type `StringCalcException` \nare usually thrown when grammar or syntax of a given term are invalid. They have two additional properties: `position` \nis an integer telling you where the problem occurred in the term, and `subject` is a string that might contain \nadditional data, **especially raw user input that you should never print to a website without encoding it via `htmlentities()`!**\n \n### tokenize\n\nThe `tokenize($term)` method tokenizes the passed term. It returns an array with the tokens. \nTokens are the parts of a term or to be more precise the mathematical symbols of a term. A token is an object\nthat has the `Tokenizer\\Token` class as its parent. It implements the `__toString()` method,\nso you can do something like this:\n\n```php\n$term = '1+(2+max(-3,3))';\n\n$tokens = $stringCalc-\u003etokenize($term);\n\nforeach ($tokens as $token) {\n    echo ' '.$token.' ';\n}\n```\n\nThis will print the tokens of the term aka a string representation the whole term. A token consists of three properties: \nThe value, the type and the position. The value is returned by the  `__toString()` method. The type is a constant \nthat represents one of these types: characters, words or numbers. \nThe position is the index of the value string in the term string. Tokens do not have a semantic meaning.\n\n### parse\n\nThe `parse(array $tokens)` method parses an array of tokens. It returns an array of nodes. Internally it uses the\nparser aka `Parser\\Parser` to parse the tokens. It transforms the tokens to nodes of the syntax tree. These nodes have\na semantic meaning, for example they are numbers or operators \n(take a look at the [Types of symbols](#types-of-symbols) section for a full list of symbol types). \nAlso they have a hierarchy, also known as the \"tree\" in the \"syntax tree\". \nBrackets in a term create a node in the syntax tree. \n\nUsage example:\n\n```php\n$term = '1+(2+max(-3,3))';\n\n$tokens = $stringCalc-\u003etokenize($term);\n\n$rootNode = $stringCalc-\u003eparse($tokens);\n\n$rootNode-\u003etraverse(function($node, $level)\n{\n    echo str_repeat('__', $level).' ['.get_class($node).']\u003cbr\u003e';\n});\n```\n\nThis example code will visualize the syntax tree. It uses the `traverse(Closure $callback)` method to go through all\nnodes of the tree. The level of the node is visualized by indentation and the name of the class of the node \nobject is printed to display the type of the node. A node implements the abstract `Parser\\Nodes\\AbstractNode` class.\nThere are three types of nodes: Container nodes (representing what is inside brackets), function nodes (representing\na mathematical function and its arguments) and symbol nodes that represent mathematical symbols of certain types\n(numbers, operators, ...). These classes live in the `Parser\\Nodes` namespace.\n \n### addSymbol\n\nCall the `addSymbol()` method if you want to add custom symbols to the list of symbols. It has two parameters. \nThe first one named `$symbol` is the symbol object. \nTherefore the object has to extend the abstract class `Symbol\\AbstractSymbol`.\nThe second parameter named `$replaceSymbol` is optional and allows you to replace a symbol in the symbol list. \nIf you want to use this parameter you have to pass the full name of the class that you want to replace.\n\nExample:\n```php\nclass ExampleClassOne extends AbstractConstant\n{\n    protected $identifiers = ['exampleConst'];\n\n    protected $value = 123;\n}\n\n// The AbstractSymbol class has this dependency:\n$stringHelper = $container-\u003eget('stringcalc_stringhelper'); \n$symbol = new ExampleClassOne($stringHelper);\n$replaceSymbol = ExampleClassTwo::class;\n\n$stringCalc-\u003eaddSymbol($symbol, $replaceSymbol);\n```\n\nThe `addSymbol()` method is just a shortcut, you can call this method on the symbol container object as well. \nThis object also has a `remove` method that removes a symbol from the container.\n\nIf you want to add a new symbol, it cannot directly extend from the `Symbol\\AbstractSymbol` class but has to extend one\nof the abstract symbol type classes that extend the `Symbol\\AbstractSymbol` class. The reason for this constraint is\nthat these classes have a semantic meaning that is not implemented in the classes themselves but in other classes \nsuch as the tokenizer and the parser. Take a look at the [Types of symbols](#Types-of-symbols) section to familiarize \nwith the symbol type classes.\n\n### getSymbolContainer\n\nThe `getSymbolContainer()` method is a getter method that returns the symbol container. \nThe symbol container implements the `Symbols\\SymbolContainerInterface` and contains instances of all registered\nsymbols. It has several methods such as `add()`, `remove()`, `size()` and `getAll()`.\n\n### getContainer\n\nThe `getContainer()` method is a getter method that returns the service container. \nTake a look at the notes about the constructor for more details. There is no setter method for the container, \nyou can only set it via the constructor.\n\n## Types of symbols\n\nA term consists of symbols that have a specific type. This section lists all available symbol types.\n\n### Numbers\n\nNumbers in a term always consist of digits and may include exactly one period. Good usage examples:\n\n```\n0\n00\n123\n4.56\n.7\n```\n\nBad usage examples:\n\n```\n0.1.2         // Two periods\n2.2e3         // \"e\" will work in PHP code but not in a term\n7E-10         // \"E\" will work in PHP code but not in a term\n0xf4c3b00c    // Hexadecimal notation is not allowed\n0b10100111001 // Binary notation is not allowed\n-1            // This is not a number but the \"-\" operator plus the number \"1\"\n```\n\nJust for your information: From the tokenizer's point of view, numbers in a term are always positive. \nThis means that the tokenizer will split the term `-1` in two parts: `-` and `1`. \n\n\u003e 💡 Notice: The fractional part of a PHP float can only have a limited length. If a number in a term has a longer \nfractional part, the fractional part will be cut somewhere.\n\n#### Number implementation\n\nThere is only one concrete number class: `Symbols\\Concrete\\Number`. \nIt extends the abstract class `Symbols\\AbstractNumber`. It does not implement any behaviour. \nIt is basically a placeholder for concrete numbers in the term.\n\n### Brackets\n\nThere are two types of brackets in a term: Opening and closing brackets. There is no other typification. For example \nthere can be classes that implement support for parentheses `()` and square brackets `[]` \nbut they will be treated equally. Therefore, this is a valid term even though it might not be valid \nfrom a mathematical point of view: `[1+)`\n\nFor every opening bracket there must be a closing bracket and vice versa. Good usage examples:\n                                                                           \n```\n(1+1)\n(1)\n((1+2)*(3+4))\n```\n\nBad usage examples:\n\n```\n()      // Empty brackets\n(1+1    // Missing closing bracket\n1+1)    // Missing opening bracket\n)1+1(   // Missing opening bracket for the closing bracket, missing closing bracket for the open bracket\n```\n\n#### Bracket implementation\n\nThe `Symbols\\AbstractBracket` class is the base class for all brackets. It is extended by the abstract classes\n`Symbols\\AbstractOpeningBracket` and `Symbols\\AbstractClosingBracket`. These are extended by concrete classes: \n`Symbols\\Concrete\\OpeningBracket` and `Symbols\\Concrete\\ClosingBracket`. These classes do not implement behaviour.\n\n### Constants\n\nConstants in a term typically represent mathematical constants, for example pi.\n \nUsage examples:\n\n```\npi\nPI\n1+pi*2\n```\n\n#### Constant implementation\n\nThe `Symbols\\AbstractConstant` class is the base class for all constants. \nThere are several concrete constants that extend this class.\n\nConstants classes have a property called `value` that stores the value of the constant. It is possible to overwrite this\nvalue in a concrete constant class or to overwrite the getter method `getValue()`.\n\n### Operators\n\nOperators in a term can be unary or binary or even both. However, if they are unary, they have to follow\n the prefix notation (example: `-1`). \n \nUnary operator example: `-1`\nBinary operator example: `2-1`\n\n#### Operator implementation\n\nThe `Symbols\\AbstractOperator` class is the base class for all operators. \nThere are several concrete operators that extend this class.\n\nPlease be aware that operators are closely related to functions. Functions are at least as powerful as operators are.\nIf an operator does not seem suitable for a purpose, a function might be an appropriate alternative.\n\nOperator classes implement the `operate($leftNumber, $rightNumber)` method. Its parameters represent the operands.\nIt might be confusing that even if the operator is a unary operator its `operate` method needs to offer\nboth parameters. The `$rightNumber` argument will be the operand of the unary operation while the left will \nbe 0.\n\n### Functions\n\nFunctions in a term represent mathematical functions. Typically, the textual representation of a function consists of \ntwo or more letters, for example: `min`\n\nGood usage examples of using functions:\n                                                                           \n```\nabs(-1)\nABS(-1)\nabs(1+abs(2))\nmin(1,2)\nmin(1,2,3)\n```\n\nBad usage examples:\n\n```\nabs-1 // Missing brackets\nmin(1,) // Missing argument\n```\n\n#### Function implementation\n\nThe `Symbols\\AbstractFunction` class is the base class for all functions. \nThere are several concrete functions that extend this class.\n\nPlease be aware that operators are closely related to functions. Functions are at least as powerful as operators are.\nIf an operator does not seem suitable for a purpose, a function should be an appropriate alternative.\n\nFunction classes implement the `execute(array $arguments)` method. The arguments are passed as an array to this method. \nThe size of the arguments array can be 0-n. The implementation of this method is responsible to validate the number of \narguments. If the number of arguments is improper, throw an `Exceptions\\NumberOfArgumentsException`. Example:\n\n```php\nif (sizeof($arguments) \u003c 1) {\n    throw new NumberOfArgumentsException('Error: Expected at least one argument, none given.');\n}\n```\n\nThe items of the `$arguments` array will always have the type int or float. They will never be null.\n\n### Separators\n\nA separator is used to separate the arguments of a (mathematical) function. \nOut-of-the-box there is one separator symbol with one identifier: `,`\n \nGood usage examples:\n \n```\nmax(1,2)\nmax(1,2,3) \n```\n\nBad usage examples:\n \n```\n3+1,2     // Usage out of scope / missusage as decimal mark\nmax(1,,3) // Missing calculable expression between separators\n```\n\n#### Separator implementation\n\nThe `Symbols\\AbstractSeparator` class is the base class for all separators. \nThere is only one concrete class that extends this class: `Symbols\\Concrete\\Separator`\n\n## Grammar\n\nThis section deals with the grammar for terms that StringCalc can process.\n\n### Grammar vs Implementation\n\nIt is important to notice that the implementations of the \nparser (`Parser\\Parser` class) and the calculator (`Calculator\\Calculator` class) \ndo not mimic the production rules defined below exactly.\nSo don't be irritated if you compare the actual implementation with the\ngrammar rules.\n\n### Grammar Definition\n\n````\nS := expression\n\nexpression := number | constant | function\nexpression := openingBracket expression closingBracket\nexpression := [unaryOperator] simpleExpression (operator [unaryOperator] simpleExpression)*\n\nsimpleExpression := number | constant | function\nsimpleExpression := openingBracket expression closingBracket\nsimpleExpression := simpleExpression (operator [unaryOperator] simpleExpression)*\n\nfunction := functionBody openingBracket closingBracket\nfunction := functionBody openingBracket expression (argumentSeparator expression)* closingBracket\n````\n\nRemember that numbers are always positive! The term \"-1\" consists of a unary operator followed by a number.\n\n## Tests\n\nRun `composer install` from the StringCalc directory, then run the tests:\n```\n./vendor/phpunit/phpunit/phpunit\n```\n\n## General notes\n\n* Internally this library uses PHP's mathematical constants, operators and functions to calculate the term. \nTherefore - as a rule of thumb - please transfer your knowledge about mathematics in PHP to the mathematics \nin StringCalc. This is also true about PHP's problems with floating point precision. \nFor example `(0.1 + 0.7) * 10` is not 8 nor 8.0 but 7.9999999999999991118… in PHP in general and in StringCalc as well.\n\n* This library does not offer support for any other numeral system than the decimal numeral system. \nIt is not intended to provide such support. Therefore, if you need support of other numeral systems \n(such as the binary numeral system) this might not be the library of your choice.\n \n* Namespaces in this documentation are relative. For example the namespace `Exceptions\\StringCalcException` refers to\n`\\ChrisKonnertz\\StringCalc\\Exceptions\\StringCalcException`.\n\n* [Releases](https://github.com/chriskonnertz/string-calc/releases) of this library are 100% free of _known_ bugs. \nThere are some TODO notes in the code but these refer to possible improvements, not to bugs. \n\n* General advice: The code of this library is well documented. Therefore, do not hesitate to take a closer \nlook at the [implementation](src/ChrisKonnertz/StringCalc).\n\n* The code of this library is formatted according to the code style defined by the \n[PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) standard.\n\n* Status of this repository: _Maintained_. Create an [issue](https://github.com/chriskonnertz/string-calc/issues), \nand you will get a response.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchriskonnertz%2Fstring-calc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchriskonnertz%2Fstring-calc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchriskonnertz%2Fstring-calc/lists"}