Parsica - PHP Parser Combinators - The easiest way to build robust parsers.

Overview

Parsica

Tests

The easiest way to build robust parsers in PHP.

composer require parsica-php/parsica

Documentation & API: parsica-php.github.io

output(); // Hello ">

$parser = between(char('{'), char('}'), atLeastOne(alphaChar()));
$result = $parser->tryString("{Hello}");
echo $result->output(); // Hello

Twitter Follow

Development

After running composer install, run these to validate if everything is in working order:

composer run phpunit
composer run psalm
composer run uptodocs

# or all of them:

composer run test
Comments
  • parse efficiently ordered file paths to build a tree

    parse efficiently ordered file paths to build a tree

    Reported on Twitter https://twitter.com/Wasquen/status/1276906414833758208?s=20

    Awesome! Good job! I'm working on a small library using Parsica I'd like to open source.

    I'm facing some performance issues.

    How would you parse efficiently ordered file paths to build a tree?

    [
      "/a/b/c/file1",
      "/a/b/c/file2",
      "/a/b/c/file3",
      "/a/b/file4"
    ]
    

    I'd like to avoid to parse "/a/b/c/" many times.

    It's hard to do anything dodgy because I don't have access to remainder/input.

    opened by mathiasverraes 15
  • AST validation [Question]

    AST validation [Question]

    Hi there. Thanks for great project!

    I have a quesion. I need to validate AST which I build inside map(...) functions. But I don't know how to keep track of current position whie generating AST nodes to print meaningful messages. Is it possible to pass current position to map() function so it can be saved into AST node?

    opened by zim32 7
  • prevent use ob mb_* functions on non-multibyte strings - 2,8% perf gain

    prevent use ob mb_* functions on non-multibyte strings - 2,8% perf gain

    this PR does 2 things

    • use mb_* functions only, when the string beeing streamed contains multibyte chars. that way we can use way faster non mb-string functions on non-multibyte strings
    • use full-qualified call to string and multibyte-string functions, which allows the php parser to apply compile time optimizations when strings are known (e.g. string literals)

    grafik

    opened by staabm 3
  • use static callback functions which improves performance by ~3%

    use static callback functions which improves performance by ~3%

    inspired by your blackfire session on twitch yesterday, I took a stab at improving performance on parsica.

    grafik

    https://blackfire.io/profiles/compare/554e2a9c-1726-43a1-b626-95aa2a230949/graph

    using static function instead of function reduces overhead of php in callables, because the runtime does not need to introduce a $this object for each function.

    opened by staabm 3
  • How to parse annotations

    How to parse annotations

    Hi! ✋ This library looks great and I would like to take a closer look.

    Let's say I would like to practice on annotations.

    /**
    * @foo Simple line
    * @bar Advanced line with !@#$%^&*()
    * @baz Multi line
    *            with extra line
    */
    

    Could you please point me how to start with it? :-)

    Thank you, Felix

    opened by f3l1x 3
  • Hot to fail expression parser if no operator in input

    Hot to fail expression parser if no operator in input

    I have simple math expression defined like this

    $arithmeticExpression->recurse(expression(
        $parens($arithmeticExpression)->or($token($term)),
        [
            leftAssoc(
                binaryOperator($token(string("-")), fn($l, $r) => new MinusOperator($l, $r))
            ),
            leftAssoc(
                binaryOperator($token(string("+")), fn($l, $r) => new PlusOperator($l, $r))
            ),
        ]
    ));
    

    But this parser succeeds when only term provided. F.e. it parses inputs like 123 + 321 and just 123

    Hot to prevent it succeed parsing without any operator?

    opened by zim32 2
  • Succeed: simplify appendSuccess

    Succeed: simplify appendSuccess

    remove calls to gettype() which is the most expensive operation within appendSuccess

    grafik

    before

    staabm@staabm_laptop MINGW64 /c/xampp7.3/htdocs/redaxo/redaxo/src/addons/parsica (simplify-type)
    $ /c/xampp7.4.2/php/php /c/dev/composer/vendor/bin/phpbench run benchmarks --report=aggregate  --retry-threshold=5
    PHPBench @git_tag@ running benchmarks...
    with configuration file: C:\xampp7.3\htdocs\redaxo\redaxo\src\addons\parsica/phpbench.json
    with PHP version 7.4.1, xdebug ❌, opcache ❌
    
    \JSONBench
    
        bench_json_encode.......................I2 ✔ Mo13.241μs (±1.899%)
        bench_Parsica_JSON......................I2 ✔ Mo21,338.523μs (±1.071%)
        bench_basemax_jpophp....................I2 ✔ Mo1,375.314μs (±0.654%)
    
    ⅀T: 67,812.200μs μSD/r 78.783μs μRSD/r: 1.208%
    
    Subjects: 3, Assertions: 0, Failures: 0, Errors: 0
    suite: 134628a77f5b70ee8034abd04114ab3247c4f4cd, date: 2021-03-14, stime: 21:27:10
    +-----------+----------------------+-----+------+-----+------------+--------------+--------------+--------------+--------------+-----------+--------+-----------+
    | benchmark | subject              | set | revs | its | mem_peak   | best         | mean         | mode         | worst        | stdev     | rstdev | diff      |
    +-----------+----------------------+-----+------+-----+------------+--------------+--------------+--------------+--------------+-----------+--------+-----------+
    | JSONBench | bench_json_encode    | 0   | 5    | 3   | 1,666,776b | 12.800μs     | 13.133μs     | 13.241μs     | 13.400μs     | 0.249μs   | 1.90%  | 1.00x     |
    | JSONBench | bench_Parsica_JSON   | 0   | 5    | 3   | 2,182,400b | 20,891.400μs | 21,210.467μs | 21,338.523μs | 21,401.400μs | 227.066μs | 1.07%  | 1,615.01x |
    | JSONBench | bench_basemax_jpophp | 0   | 5    | 3   | 1,867,520b | 1,373.200μs  | 1,380.467μs  | 1,375.314μs  | 1,393.200μs  | 9.034μs   | 0.65%  | 105.11x   |
    +-----------+----------------------+-----+------+-----+------------+--------------+--------------+--------------+--------------+-----------+--------+-----------+
    

    after

    staabm@staabm_laptop MINGW64 /c/xampp7.3/htdocs/redaxo/redaxo/src/addons/parsica (simplify-type)
    $ /c/xampp7.4.2/php/php /c/dev/composer/vendor/bin/phpbench run benchmarks --report=aggregate  --retry-threshold=5
    PHPBench @git_tag@ running benchmarks...
    with configuration file: C:\xampp7.3\htdocs\redaxo\redaxo\src\addons\parsica/phpbench.json
    with PHP version 7.4.1, xdebug ❌, opcache ❌
    
    \JSONBench
    
        bench_json_encode.......................I2 ✔ Mo13.436μs (±2.08%)
        bench_Parsica_JSON......................I2 ✔ Mo20,541.307μs (±2.514%)
        bench_basemax_jpophp....................R3 I2 ✔ Mo1,366.153μs (±1.837%)
    
    ⅀T: 66,626.400μs μSD/r 182.998μs μRSD/r: 2.143%
    
    Subjects: 3, Assertions: 0, Failures: 0, Errors: 0
    suite: 134628ae7cb50c4018c4881c0c020c1f4335617d, date: 2021-03-14, stime: 21:26:40
    +-----------+----------------------+-----+------+-----+------------+--------------+--------------+--------------+--------------+-----------+--------+-----------+
    | benchmark | subject              | set | revs | its | mem_peak   | best         | mean         | mode         | worst        | stdev     | rstdev | diff      |
    +-----------+----------------------+-----+------+-----+------------+--------------+--------------+--------------+--------------+-----------+--------+-----------+
    | JSONBench | bench_json_encode    | 0   | 5    | 3   | 1,666,792b | 13.400μs     | 13.600μs     | 13.436μs     | 14.000μs     | 0.283μs   | 2.08%  | 1.00x     |
    | JSONBench | bench_Parsica_JSON   | 0   | 5    | 3   | 2,180,704b | 20,411.200μs | 20,840.067μs | 20,541.307μs | 21,577.600μs | 523.818μs | 2.51%  | 1,532.36x |
    | JSONBench | bench_basemax_jpophp | 0   | 5    | 3   | 1,867,520b | 1,321.800μs  | 1,355.133μs  | 1,366.153μs  | 1,381.600μs  | 24.891μs  | 1.84%  | 99.64x    |
    +-----------+----------------------+-----+------+-----+------------+--------------+--------------+--------------+--------------+-----------+--------+-----------+
    
    opened by staabm 2
  • ⚡️ Use `strlen()` instead of `mb_strlen()` to find EOF

    ⚡️ Use `strlen()` instead of `mb_strlen()` to find EOF

    To find EOF, we just want to know if the length of the remaining string is 0. We don't need to use mb_strlen() for that, because in an empty string, there shouldn't be any multibyte characters anyway.

    See also #32

    Co-authored-by: Markus Staab [email protected]

    opened by turanct 2
  • JSON string parsing speed improvements

    JSON string parsing speed improvements

    While testing some things in the JSON parser benchmarks when writing this, i found these:

    ⚡️ Write character parsers instead of string parsers

    Since we're looking for a backslash, followed by a character from a list of special escaped characters, we can make this a simpler parse than looking for the explicit combinations as strings. This makes us backtrack less.

    This results in a x3 speed improvement for parsing strings.

    ⚡️ Using takeWhile(f) is faster than zeroOrMore(satisfy(f)) combo

    Because it doesn't have to run the parser completely, every time. It can just rely on the stream implementation. This results in a ± x1.25 speed improvement according to some benchmarks I did.

    ⚡️ Accept any character in JSON Strings instead of the special ones first

    Instead of looking for special characters first, and backtracking if we don't find them, look for the accepted ones first, and parse the special ones only if that fails.

    This results in a x1.35 speed improvement in the benchmarks I did.

    opened by ToonGagor 2
  • bench: use more iterations/revisions for the native json_encode baseline

    bench: use more iterations/revisions for the native json_encode baseline

    The function call is very very fast and very little differences will have a large effect on rel. comparision to parsica. Try to make it more stable.

    Motivation: in https://github.com/parsica-php/parsica/pull/45#issue-602130996 the benchmark went from 12.533μs (before-mean) to 13.333μs (after-mean) which is a diff of ~ 10% without other modifications

    opened by staabm 1
  • ⚡️ Make many 400% faster by using while instead of recurse

    ⚡️ Make many 400% faster by using while instead of recurse

    • Added a benchmark which encapsulates the old implementation and compares to the new implementation
    • Created the new implementation based on a while loop

    This is 4 times faster than the old many in some basic tests...

    Subjects: 5, Assertions: 0, Failures: 0, Errors: 0
    suite: 134628ad035def57f08210ae05b1eb88c2d8ce07, date: 2021-03-14, stime: 22:26:52
    +-----------+----------------------+-----+------+-----+------------+-----------+-----------+-----------+-----------+----------+--------+-------+
    | benchmark | subject              | set | revs | its | mem_peak   | best      | mean      | mode      | worst     | stdev    | rstdev | diff  |
    +-----------+----------------------+-----+------+-----+------------+-----------+-----------+-----------+-----------+----------+--------+-------+
    | ManyBench | bench_takeWhile      | 0   | 10   | 10  | 1,825,752b | 97.400μs  | 109.530μs | 106.858μs | 133.200μs | 10.392μs | 9.49%  | 1.00x |
    | ManyBench | bench_manySatisfy    | 0   | 10   | 10  | 1,862,264b | 208.700μs | 244.190μs | 225.526μs | 352.000μs | 41.893μs | 17.16% | 2.23x |
    | ManyBench | bench_manyChar       | 0   | 10   | 10  | 1,863,008b | 232.100μs | 244.510μs | 237.852μs | 262.400μs | 9.926μs  | 4.06%  | 2.23x |
    | ManyBench | bench_oldManySatisfy | 0   | 10   | 10  | 2,959,440b | 712.800μs | 765.140μs | 740.255μs | 910.400μs | 56.543μs | 7.39%  | 6.99x |
    | ManyBench | bench_oldManyChar    | 0   | 10   | 10  | 2,960,192b | 733.100μs | 781.460μs | 757.570μs | 878.500μs | 48.064μs | 6.15%  | 7.13x |
    +-----------+----------------------+-----+------+-----+------------+-----------+-----------+-----------+-----------+----------+--------+-------+
    
    opened by turanct 1
  • Library maintenance

    Library maintenance

    I am sorry to read about the passing of the maintainer of this library.

    I just recently learned about this library and think it's definitely keeping alive in one form or another. mathiasverraes (intentionally not yet pinging you) you mentioned there is no maintainer, what kind of maintenance are you looking for exactly? Are you able / willing to review PRs, or just able to hand over appropriate permissions to a new maintainer or new maintainer(s)?

    From what I can see the library is well tested and merging in at least some of the PRs should be pretty safe. While I don't have a lot of time to maintain the library I am interesting in helping out, the first things I would do:

    • Update CI to enforce code style (ecs)
    • Update CI to enforce commit messages
    • Update CI to automate releases based on commit messages
    • Set up git hooks (captainhook) to automate the above for contributors

    These steps should make it relatively painless to accept small iterative improvements.

    If people want to (help) in maintaining this library please respond here, if mathias doesn't respond in a few days I'll consider pinging him and hopefully he can guide us forward a bit.

    opened by SamMousa 0
  • Beginner question - extending expression parser

    Beginner question - extending expression parser

    Curious, I am trying to extend your expression example, by allowing variable names with "." separators:

            $identifier = atLeastOne(alphaChar());
            $identifierDot = many($identifier->optional()->append(char('.')));
    

    This isn't working, so I'm clearly not understanding how this library works. What am I missing? I'm stuck trying to think of the problem in terms of a regex solution.

    How would I parse / match a variable with arbitrary number of '.' separators? And have that returned to the AST as a complete string, not further parsed.

    Any help much appreciated :)

    opened by alex-barylski 0
  • Performance: ~250% faster JSON Parser

    Performance: ~250% faster JSON Parser

    related: #17 inspired by this regex:

    /^
      "[^"\\]*              # double quoted strings with possibly escaped double quotes
        (?:
          \\.               # escaped character (quote)
          [^"\\]*           # unrolled loop following Jeffrey E.F. Friedl
        )*
      "
    /x
    

    i implemented an unrolled loop in combination with takeWhile. (the last use of the deprecated zeroOrMore was also removed) this leads to a big performance boost from ca ~250%(without xdebug) - ~330%(with xdebug) - both without opcache.

    before: bench_json_encode: 4.133μs bench_Parsica_JSON: 4.109ms bench_basemax_jpophp: 432.128μs

    after: bench_json_encode: 3.744μs bench_Parsica_JSON: 1.662ms bench_basemax_jpophp: 401.154μs

    opened by mhsdesign 0
  • Learning Parsica

    Learning Parsica

    In response to @mathiasverraes ' tweet here:

    I think the documentation is very useful and developer-friendly already. I thought of a couple of things that might be improved:

    • For most developers I think the closest thing to parsing that they already know and understand is regular expressions. At times, as a user of this library I felt like "if I could just use a regex, I'd be done already". Still, I wanted to do the right thing, and I kept looking for similar concepts and found them, like atLeastOne, zeroOrMore, between. In terms of documentation, I think it would be really helpful if there would be a page that shows some simple regexes and then shows and explains the alternative using parsers.
    • I think it will be interesting to have a chapter that shows alternatives for different parsers that achieve the same thing, or how some some parsers are shortcuts of others.
    • The parts of the documentation that say "TODO" should be removed because they aren't useful for the reader, only for the writer/maintainer.
    • The JSON parser is a great source for learning material, and I think it may deserve a full explanation in the documentation.
    • It's very confusing that some parsers are marked as @deprecated; they are not in fact deprecated, it just seems they are not tested. As a user of the library it makes you feel somewhat bad about using these parsers, even though there's no alternative.

    Great work on the type annotations by the way, those are really helpful, and I think it's amazing that this is now possible in PHP today.

    Thanks a lot for this awesome library! It was a nice thing to dive in and get to know.

    opened by matthiasnoback 3
  • Feature idea: collect a map intead of a list

    Feature idea: collect a map intead of a list

    Since I use collect() to capture values that are to be fed into an AST node, it would be very helpful to use a map instead of a list. Similarly to how you can capture named groups with regular expressions. Here's an example where I parse a Markdown heading:

    return collect(
                keepFirst(atLeastOne(char('#')), skipSpace1()),
                atLeastOne(satisfy(fn (string $char) => ! in_array($char, ["\n"], true))),
                self::newLineOrEof()
            )->map(fn (array $output) => new Heading(strlen($output[1]), $output[2], $output[0]));
    

    Suggested improvement (last line)

    return collect(
                keepFirst(atLeastOne(char('#')), skipSpace1()),
                atLeastOne(satisfy(fn (string $char) => ! in_array($char, ["\n"], true))),
                self::newLineOrEof()
            )->map(fn (array $output) => new Heading(strlen($output['level']), $output['title']));
    

    One way could be to use label() for each collected parser and use the label as the array key passed to map():

    return collect(
                keepFirst(atLeastOne(char('#')), skipSpace1())->label('level'),
                atLeastOne(satisfy(fn (string $char) => ! in_array($char, ["\n"], true)))->label('title'),
                self::newLineOrEof()
            )->map(fn (array $output) => new Heading(strlen($output['level']), $output['title']));
    

    The advantage being that label() is already there and used for a similar purpose. However, this might lead to values being overwritten if you collect two parsers with the same name. Another option is to provide the map upfront:

    return collect([
                'level' => keepFirst(atLeastOne(char('#')), skipSpace1()),
                'title' => atLeastOne(satisfy(fn (string $char) => ! in_array($char, ["\n"], true))),
                'eol' => self::newLineOrEof()
            ])->map(fn (array $output) => new Heading(strlen($output['level']), $output['title']));
    

    It might be useful to have separate collectList() and collectMap() functions by the way.

    opened by matthiasnoback 0
Releases(0.8.1)
  • 0.8.1(Apr 10, 2021)

    • PERFORMANCE: use static keyword on all closures
    • PSALM: update some annotations to work with newer psalm versions
    • PERFORMANCE: simplify appendSuccess in the Succeed class
    • PERFORMANCE: reduce indirection with the guardEndOfStream() in StringStream

    These resulted in a small speedup of the JSON Parser example.

    Before changes:

    +-----------+----------------------+-----+------+-----+------------+-------------+---------+
    | benchmark | subject              | set | revs | its | mem_peak   | mode        | rstdev  |
    +-----------+----------------------+-----+------+-----+------------+-------------+---------+
    | JSONBench | bench_json_encode    | 0   | 5    | 3   | 1,710,104b | 7.617μs     | ±46.68% |
    | JSONBench | bench_Parsica_JSON   | 0   | 5    | 3   | 2,168,552b | 6,539.741μs | ±5.28%  |
    | JSONBench | bench_basemax_jpophp | 0   | 5    | 3   | 1,878,760b | 633.373μs   | ±24.11% |
    +-----------+----------------------+-----+------+-----+------------+-------------+---------+
    

    After changes:

    +-----------+----------------------+-----+------+-----+------------+-------------+----------+
    | benchmark | subject              | set | revs | its | mem_peak   | mode        | rstdev   |
    +-----------+----------------------+-----+------+-----+------------+-------------+----------+
    | JSONBench | bench_json_encode    | 0   | 5    | 3   | 1,710,200b | 5.978μs     | ±29.48%  |
    | JSONBench | bench_Parsica_JSON   | 0   | 5    | 3   | 2,160,184b | 6,256.940μs | ±8.37%   |
    | JSONBench | bench_basemax_jpophp | 0   | 5    | 3   | 1,878,856b | 633.060μs   | ±3.00%   |
    +-----------+----------------------+-----+------+-----+------------+-------------+----------+
    
    Source code(tar.gz)
    Source code(zip)
  • 0.8.0(Mar 20, 2021)

    • PERFORMANCE: Use strlen() instead of mb_strlen() to find EOF
    • DEPENDENCY: Add phpbench as a dev dependency
    • PERFORMANCE: Succeed: use property instead of internal getter call on the hot path
    • PERFORMANCE: Succeed: use lookup private properties instead of calling getters
    • PERFORMANCE: Make many() 400% faster by using while instead of recurse
    • PERFORMANCE: Check for empty string comparing to empty string instead of strlen 

    These resulted in a x2 speedup of the JSON Parser example.

    Before changes

    Subjects: 3, Assertions: 0, Failures: 0, Errors: 0
    suite: 1346290d9eebcc1aade3cbbff1b5f7017d112890, date: 2021-03-20, stime: 10:39:04
    +-----------+----------------------+-----+------+-----+------------+--------------+--------------+--------------+--------------+-----------+--------+-----------+
    | benchmark | subject              | set | revs | its | mem_peak   | best         | mean         | mode         | worst        | stdev     | rstdev | diff      |
    +-----------+----------------------+-----+------+-----+------------+--------------+--------------+--------------+--------------+-----------+--------+-----------+
    | JSONBench | bench_json_encode    | 0   | 5    | 3   | 1,709,704b | 5.200μs      | 5.467μs      | 5.576μs      | 5.600μs      | 0.189μs   | 3.45%  | 1.00x     |
    | JSONBench | bench_Parsica_JSON   | 0   | 5    | 3   | 2,195,024b | 10,658.600μs | 11,040.933μs | 10,757.806μs | 11,737.200μs | 493.126μs | 4.47%  | 2,019.68x |
    | JSONBench | bench_basemax_jpophp | 0   | 5    | 3   | 1,878,400b | 520.600μs    | 637.733μs    | 571.178μs    | 811.000μs    | 125.023μs | 19.60% | 116.66x   |
    +-----------+----------------------+-----+------+-----+------------+--------------+--------------+--------------+--------------+-----------+--------+-----------+
    

    After changes:

    Subjects: 3, Assertions: 0, Failures: 0, Errors: 0
    suite: 1346290eaf4ce8a0eb08bdddb54b69d2020be280, date: 2021-03-20, stime: 10:38:11
    +-----------+----------------------+-----+------+-----+------------+-------------+-------------+-------------+-------------+-----------+--------+-----------+
    | benchmark | subject              | set | revs | its | mem_peak   | best        | mean        | mode        | worst       | stdev     | rstdev | diff      |
    +-----------+----------------------+-----+------+-----+------------+-------------+-------------+-------------+-------------+-----------+--------+-----------+
    | JSONBench | bench_json_encode    | 0   | 5    | 3   | 1,710,104b | 5.200μs     | 5.333μs     | 5.388μs     | 5.400μs     | 0.094μs   | 1.77%  | 1.00x     |
    | JSONBench | bench_Parsica_JSON   | 0   | 5    | 3   | 2,168,552b | 5,383.000μs | 5,505.267μs | 5,431.564μs | 5,693.200μs | 134.883μs | 2.45%  | 1,032.24x |
    | JSONBench | bench_basemax_jpophp | 0   | 5    | 3   | 1,878,760b | 541.800μs   | 725.133μs   | 794.251μs   | 853.400μs   | 133.036μs | 18.35% | 135.96x   |
    +-----------+----------------------+-----+------+-----+------------+-------------+-------------+-------------+-------------+-----------+--------+-----------+
    
    Source code(tar.gz)
    Source code(zip)
  • 0.7.0(Feb 20, 2021)

    As part of the move to the parsica-php organisation, we decided to move the namespace to Parsica. Sorry if this would break your integration with Parsica! We did a minor version update because we don't want to go to a v1.x yet, but want to let you know that there has been a backwards compatibility break.

    Fix your composer.json file and make sure you use the correct namespace in your code!

    Source code(tar.gz)
    Source code(zip)
  • 0.6.1(Dec 1, 2020)

  • 0.6.0(Nov 30, 2020)

    • FEATURE: Expression parser. Using a simple interface, you can define parsers for recursive expression languages with different associativity and operators precedence.

    • FEATURE: Lots of fine grained Psalm type annotations. You should now be able to use Psalm in your own parsers, and get useful warnings if you do something wrong.

    • FEATURE: Parser#thenEof(). Make sure the parser reached the end of the input by appending ->thenEof()

    • IMPROVEMENT: Many error messages are now more clear

    • PERFORMANCE: 10x improvement by generating the error messages lazily. Still a long way before Parsica is performant enough for large scale production usage, but this gives us confidence there's still many low hanging fruits.

    • DOCS: New tutorial about writing expression parsers

    • DOCS: New tutorial about dealing with whitespace

    • DOCS: Added lots of missing API docs

    • DOCS: Improved the tutorial on types

    • DOCS: New examples in tests/Examples for expressions and an quick demo of an Excel formula parser

    • BREAKING: The Fail class no longer does double duty as an exception. Instead, use ParseResult#throw(), which throws a ParserHasFailed exception.

    • BREAKING: Parser#recurse() doesn't return the parser anymore, and instead returns void.

    • BREAKING: Parser#construct(ClassName) is removed in favour of map(fn($v) => new ClassName))

    ... and lots of minor improvements

    Source code(tar.gz)
    Source code(zip)
  • 0.5.2(Sep 14, 2020)

  • 0.5.1(Sep 14, 2020)

    FEATURE lookahead() combinator DOCS: A large chunk of API documentation was not being rendered FIX: PHPUnit max nesting level was insufficient IMPROVEMENT: reimplemented applicative functors without monads FIX: #10 fail when using applicative functors when the output of the first parser is not a callable

    • various minor cleanup
    Source code(tar.gz)
    Source code(zip)
  • 0.5.0(Aug 25, 2020)

    FEATURE: sepBy2 combinator FEATURE: apply function FEATURE: integer parser FEATURE: float parser FEATURE: Parser#and() is an alias for append() FEATURE: Parser#then() is an alias for sequence() DOCS: Misc improvements and additions BREAKING: removed some helper functions from the JSON parser
    BREAKING nicer API for ParserAssertions

    Source code(tar.gz)
    Source code(zip)
  • 0.4.0(Jul 12, 2020)

    • FEATURE: Parser errors are now a lot more useful. They show the line and column position where the parser expected something else. Labels are also vastly improved.

    • BREAKING / FEATURE: Inputs must now be of type Stream. Right now it only comes with a StringStream implementation, but in the future this will allow us to handle different types of input, such as an actual stream.

    • FEATURE: The new emit() combinator can be used for side effects and for emitting parser events

    • FEATURE: We implemented a fully compliant JSON parser

    • FEATURE: zeroOrMore() combinator

    • FEATURE: map() is now also a function

    • BREAKING: Parser tracks label so that combinators can combine them into smarter error messages

    • BREAKING: Removed ParseResult::alternative

    • BREAKING: When appending with null, the null gets ignored

    • BREAKING: optional and success now output null instead of empty string

    • BREAKING: renamed success() and failure() to succeed() and fail()

    • BREAKING: skipWhile outputs null instead of empty string

    • BREAKING: skipWhile1 outputs null instead of empty string

    ...and lots of improvements and additions to the documentation, test coverage, code organisation, Psalm types, ...

    Source code(tar.gz)
    Source code(zip)
  • 0.2(Jun 19, 2020)

The Easiest way to start using PHP CS Fixer and PHP_CodeSniffer with 0-knowledge

The Easiest Way to Use Any Coding Standard Features Blazing fast Parallel run Use PHP_CodeSniffer || PHP-CS-Fixer - anything you like 2nd run under fe

null 1.1k Jan 6, 2023
The easiest way to match data structures like JSON/PlainText/XML against readable patterns. Sandbox:

PHP Matcher Library created for testing all kinds of JSON/XML/TXT/Scalar values against patterns. API: PHPMatcher::match($value = '{"foo": "bar"}', $p

Coduo 774 Dec 31, 2022
A robust and flexible way to add double-opt-in (DOI) to any form in Mautic

Mautic double-opt-in (DOI) plugin Adds a robust and flexible way to add a double-opt-in process (DOI) to any form in Mautic. What is the plugin for? I

Content Optimizer GmbH 11 Dec 29, 2022
Laravel Blog Package. Easiest way to add a blog to your Laravel website. A package which adds wordpress functionality to your website and is compatible with laravel 8.

Laravel Blog Have you worked with Wordpress? Developers call this package wordpress-like laravel blog. Give our package a Star to support us ⭐ ?? Inst

Binshops 279 Dec 28, 2022
The easiest way to get started with event sourcing in Laravel

Event sourcing for Artisans ?? This package aims to be the entry point to get started with event sourcing in Laravel. It can help you with setting up

Spatie 591 Jan 4, 2023
DBML parser for PHP8. It's a PHP parser for DBML syntax.

DBML parser written on PHP8 DBML (database markup language) is a simple, readable DSL language designed to define database structures. This page outli

Pavel Buchnev 32 Dec 29, 2022
MOP is a php query handling and manipulation library providing easy and reliable way to manipulate query and get result in a fastest way

Mysql Optimizer mysql optimizer also known as MOP is a php query handling and manipulation library providing easy and reliable way to manipulate query

null 2 Nov 20, 2021
A PHP parser written in PHP

PHP Parser This is a PHP 5.2 to PHP 8.1 parser written in PHP. Its purpose is to simplify static code analysis and manipulation. Documentation for ver

Nikita Popov 15.9k Jan 8, 2023
PHP Simple M3U Parser, it clean the playlist and remove duplicate

SimpleM3UParser PHP Simple M3U Playlist Parser, it clean, remove duplicate, and group the playlist. Usage see example.php <?php require_once "M3UPars

erwin solihin 3 May 30, 2022
Referer-parser PHP library

referer-parser PHP library This is the PHP implementation of referer-parser, the library for extracting search marketing data from referer (sic) URLs.

null 4 Sep 3, 2022
Golang 1.18 parser written in PHP 8.1

GoParser Golang (1.18) parser written in PHP 8.1 Installation To install this package, run: composer require tuqqu/go-parser Example $parser = new \G

Arthur Kurbidaev 37 Nov 2, 2022
Lightning Fast, Minimalist PHP User Agent String Parser.

Lightning Fast, Minimalist PHP User Agent String Parser.

Jesse Donat 523 Dec 21, 2022
A PHP package for MRZ (Machine Readable Zones) code parser for Passport, Visa & Travel Document (TD1 & TD2).

MRZ (Machine Readable Zones) Parser for PHP A PHP package for MRZ (Machine Readable Zones) code parser for Passport, Visa & Travel Document (TD1 & TD2

Md. Rakibul Islam 25 Aug 24, 2022
Annotations Docblock Parser

Doctrine Annotations Docblock Annotations Parser library (extracted from Doctrine Common). Documentation See the doctrine-project website. Contributin

Doctrine 6.6k Dec 29, 2022
A proof-of-concept parser for the SMART Health Cards format.

SMART Health Cards parser A proof-of-concept parser for the SMART Health Cards format. This is not intended for production use. I just hacked this tog

Mikkel Paulson 55 Jul 31, 2022
uaDetect – A multi-language port of Browserscope's user agent parser

uaDetect is a lightweight for detecting mobile devices. It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.

Fadjrir Herlambang 1 Jan 7, 2022
JsonCollectionParser - Event-based parser for large JSON collections (consumes small amount of memory)

Event-based parser for large JSON collections (consumes small amount of memory). Built on top of JSON Streaming Parser This packa

Max Grigorian 113 Dec 6, 2022
MySQL parser for free-id/core package

MySQL parser Installation You can install the package via composer: composer require free-id/mysql Usage use FreeId\Mysql\Parser; $parser = new Parse

Free ID 0 Jul 27, 2022
This is a Native PHP MVC. If you will build your own PHP project in MVC with router, you can clone this ready to use MVC pattern repo.

Welcome to PHP-Native-MVC-Pattern ?? If you will build your own PHP project in MVC with router, you can clone this ready to use MVC pattern repo. Work

null 2 Jun 6, 2022