An open API service indexing awesome lists of open source software.

https://github.com/philiagus/parser

PHP converter classes for asserting, parsing and converting of inputs.
https://github.com/philiagus/parser

composer-package extract parser php sanitize

Last synced: 5 months ago
JSON representation

PHP converter classes for asserting, parsing and converting of inputs.

Awesome Lists containing this project

README

          

# philiagus/parser

PHP classes to assert, convert and parse data.

# Is it tested?

Tested on the following PHP Version:

- PHP8.3

100% test covered. Test coverage generated on PHP8.3

## Why do I need it?

Making sure your inputs are what they should be is one of the core principles of secure coding.

Obviously there are more, but we must tackle them one step at a time.

The basic idea of the parsers is, that the developer defines a structure through code and later throws a set of data
against it. The parsers make sure, that the data is following the rules of defined in the structure.

## How does it work?

A more in depth documentation and tutorials on how to write own parsers can be found [here](doc/index.md).

A simple example to assert that a provided value lies within defined boundaries:

```php
parse(Subject::default($integer))
->getValue();

// or, also possible:
AssertInteger::range(0, 100)
->thenAssignTo($target)
->parse(Subject::default($integer))
->getValue();
```

The real fun begins, when you start stacking parsers into one another:

```php
assertSequentialKeys()
->giveEachValue(
OneOf::new()
->parser(
AssertInteger::minimum(0)
->thenAppendTo($integers),
AssertFloat::minimum(0.0)
->thenAppendTo($floats)
)
)
->parse(Subject::default($input));

// $integers will contain [1, 2, 4]
// $floats will contain [1.0, 4.20]

```

Let's say: You have an API which receives requests from a client in JSON format, and you need to find the relevant information.

```php
then(
AssertStdClass::new()
->givePropertyValue(
'name',
AssertStringMultibyte::UTF8()
->giveLength(
AssertInteger::new()
->assertMinimum(1)
->assertMaximum(64)
)
->thenAssignTo($name)
)
->givePropertyValue(
'birthday',
ConvertToDateTime::fromSourceFormat(
'!Y-m-d', new \DateTimeZone('UTC'),
'The provided birthday is not a valid date'
)
->setTimezone(new \DateTimeZone('UTC'))
->thenAssignTo($birthday)
)
);

$result = $parser->parse(Subject::default($sourceValue, 'Input', false));

if ($result->hasErrors()) {
foreach ($result->getErrors() as $error) {
echo $error->getPathAsString(), ': ', $error->getMessage(), PHP_EOL;
}
exit;
}

$today = new \DateTime();
$delta = $today->diff($birthday);
if ($today < $birthday) {
echo "$name will be born in ", $delta->y, " years", PHP_EOL;
} else {
echo "$name was born ", $delta->y, " years ago", PHP_EOL;
}
```

If you execute this code the result will be `Frank herbert was born 101 years ago` (at least on the date of this typing).

Would you change the input to `{"name":123,"birthday":"1920-10-0f"}`, the result would be:
```text
Input.name: Provided value is not of type string
Input.birthday: The provided birthday is not a valid date
```

## What if something is missing?

Fear not! All parsers implement `Philiagus\Parser\Contract\Parser`, so you can easily write your own to fit your specific need. Check out `Philiagus\Parser\Base\Parser` for a base class you can easily extend.

Some hints:
- If your parser validates a type (example: a parser the reads an XML, so non-strings are a no-go), the `Philiagus\Parser\Base\OverwritableTypeErrorMessage`-Trait might help
- The `Philiagus\Parser\Base\Parser` already implements basic things such as chaining using `->then($parser)` among other things, so you don't have to worry about that
- If you need more control over the behaviour of the parser or just don't like the `ResultBuilder`, you only need to implement the `Philiagus\Parser\Contract\Parser`-Interface to be interoperable with other parsers. Just be aware that you MUST
- create a `ParserBegin` subject when you enter the parser, wrapping the provided subject into another subject to ensure that the value chain is upheld
- respect the `throwOnError()` info of the subject: If an error occurs and `throwOnError()` is active, you have to throw the Error (most times using `$error->throw()`). Accordingly, if `throwOnError()` is not active you have to add the Errors to the Result object.