-
-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
short arrow functions implement typeName-parser + enum class generator extract builder function for enum classes add manyArray & manyArray1 parsers, refactor namespace type object update kahlan parse enum constructors implement enum parser namespace parsing, hack into spaces() function parse ns with sub ns add fpp.php entry point make use of configuration namespace parsing is configurable, gen default config file
- Loading branch information
Showing
148 changed files
with
1,535 additions
and
15,308 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,194 +1,12 @@ | ||
# FPP | ||
|
||
## Functional PHP Preprocessor - Immutable data type generator | ||
## Functional PHP Preprocessor - Immutable data type generator | ||
|
||
### What it this? | ||
|
||
This library can generate immutable data types based on fpp definitions. The syntax is inspired by Haskell. | ||
|
||
### YouTube Video Tutorial | ||
## Credits | ||
|
||
[](https://youtu.be/MYh1_sydQ5U) | ||
|
||
### Docs | ||
|
||
[See the docs here](https://github.com/prolic/fpp/tree/master/docs/Home.md) | ||
|
||
### Sponsor | ||
|
||
If you want to support my work, become a patron at [patreon.com/prolic](https://www.patreon.com/notifications). | ||
|
||
### Install | ||
|
||
```console | ||
composer require prolic/fpp | ||
``` | ||
|
||
### So what is it really? | ||
|
||
Create a file and put this in it: | ||
|
||
```fpp | ||
namespace Model\Foo; | ||
data Person = Person { string $name, ?int $age }; | ||
``` | ||
|
||
This will generate the following php code: | ||
|
||
```php | ||
namespace Model\Foo; | ||
final class Person | ||
{ | ||
private $name; | ||
private $age; | ||
|
||
public function __construct(string $name, ?int $age) | ||
{ | ||
$this->name = $name; | ||
$this->age = $age; | ||
} | ||
|
||
public function name(): string | ||
{ | ||
return $this->name; | ||
} | ||
|
||
public function age(): ?int | ||
{ | ||
return $this->age; | ||
} | ||
|
||
public function withName(string $name): Person | ||
{ | ||
return new self($name, $this->age); | ||
} | ||
|
||
public function withAge(?int $age): Person | ||
{ | ||
return new self($this->name, $age); | ||
} | ||
} | ||
``` | ||
|
||
### Enums? | ||
|
||
No problem | ||
|
||
```fpp | ||
namespace MyEnum; | ||
data Color = Red | Blue | Green | Yellow deriving (Enum); | ||
``` | ||
|
||
```php | ||
$blue = Color::blue(); | ||
var_dump($blue->equals(Color::blue())); // true | ||
var_dump($blue->equals(Color::red())); // false | ||
|
||
function (MyEnum\Color $color): string | ||
{ | ||
return $color->value(); | ||
} | ||
``` | ||
|
||
Enums with value mappings | ||
|
||
```fpp | ||
namespace MyEnum; | ||
data Color = Red | Blue deriving (Enum) with (Red:'someThing', Blue: 13); | ||
``` | ||
|
||
```php | ||
var_dump(Color::red()->value()); // 'someThing' | ||
var_dump(Color::blue()->value()); // 13 | ||
``` | ||
|
||
### Derivings | ||
|
||
Derivings are kind of PHP's extends keyword, the following rules apply: | ||
|
||
- It's possible to derive multiple times | ||
- Some derivings are not compatible to each other (ie, Command and ToArray cannot be mixed) | ||
|
||
There are 14 deriving types for now: | ||
|
||
- `AggregateChanged` | ||
- `Command` | ||
- `DomainEvent` | ||
- `Enum(useName)` (default) / `Enum(useValue)` | ||
- `Equals` | ||
- `FromArray` | ||
- `FromScalar` | ||
- `FromString` | ||
- `Query` | ||
- `MicroAggregateChanged` (not extending from prooph/eventsourcing, f.e. for prooph/micro) | ||
- `ToArray` | ||
- `ToScalar` | ||
- `ToString` | ||
- `Uuid` | ||
|
||
Deriving Equals + ToArray | ||
|
||
```fpp | ||
namespace Model\Foo; | ||
data Person = Person { string $name, ?int $age } deriving (ToArray, Equals); | ||
``` | ||
|
||
Now you can do this: | ||
|
||
```php | ||
$p = new Model\Foo\Person(['name' => 'sasa', 'age' => 36]); | ||
var_dump($p->toArray()); // ['name' => 'sasa', 'age' => 36] | ||
$p->equals($p) // true | ||
``` | ||
|
||
### Conditions | ||
|
||
You can add additional conditions to your code: | ||
|
||
``` | ||
namespace Foo; | ||
data FirstName = String deriving (FromString, ToString); | ||
data LastName = String deriving (FromString, ToString); | ||
data Age = Int deriving (FromScalar, ToScalar); | ||
data Person = Person { FirstName $firstName, LastName $lastName, Age $age } | Boss { FirstName $firstName, LastName $lastName } where | ||
Person: | ||
| strlen($lastName->toString()) === 0 => 'last name too short' | ||
| $age->toScalar() < 18 => "you\'re too young, sorry" | ||
Boss: | ||
| strlen($lastName->toString()) < 5 => 'last name too short' | ||
_: | ||
| strlen($firstName->toString()) === 0 => 'first name too short'; | ||
``` | ||
|
||
### Usage | ||
|
||
```php | ||
php bin/fpp.php <source dir or file> | ||
# or | ||
php vendor/prolic/fpp/bin/fpp.php <source dir or file> | ||
``` | ||
|
||
It will try to find your composer autoload and fetch psr-4 and psr-0 prefixes from it. | ||
You'll get an exception, if you want to dump a class, where you have no composer autoload definition. | ||
|
||
### Features | ||
|
||
- [x] Create immutable data types with ease | ||
- [x] Strict types always | ||
- [x] Generate prooph commands | ||
- [x] Generate prooph events | ||
- [x] Generate prooph queries | ||
- [x] Generate prooph aggregate changed events | ||
- [x] Ability to switch dumper implementation for custom output | ||
- [x] Allow composite data objects | ||
- [x] Allow composite prooph objects | ||
- [x] Constructor validation | ||
- [x] Allow creating of custom constructors | ||
- [x] Dump files according to psr-4 and psr-0 autoloading rules | ||
- [x] Array notation for objects and scalar types | ||
- [x] Enum value mappings | ||
- [x] Support for conditions | ||
[Marcello Duarte](https://github.com/MarcelloDuarte/) created the [ParserCombinators](https://github.com/MarcelloDuarte/ParserCombinators/) project in 2017. | ||
The rewrite of this library is heavily inspired by it and reuses some of its base functions. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
|
||
/** | ||
* This file is part of prolic/fpp. | ||
* (c) 2018-2019 Sascha-Oliver Prolic <[email protected]> | ||
* (c) 2018-2020 Sascha-Oliver Prolic <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
|
@@ -12,41 +12,109 @@ | |
|
||
namespace Fpp; | ||
|
||
use Nette\PhpGenerator\PsrPrinter; | ||
use function Pair; | ||
|
||
if (! isset($argv[1])) { | ||
echo 'Missing input directory or file argument'; | ||
exit(1); | ||
} | ||
|
||
$path = $argv[1]; | ||
|
||
$autoloader = require __DIR__ . '/../src/bootstrap.php'; | ||
$pwd = \realpath(\getcwd()); | ||
$vendorName = 'vendor'; | ||
|
||
$prefixesPsr4 = $autoloader->getPrefixesPsr4(); | ||
$prefixesPsr0 = $autoloader->getPrefixes(); | ||
if (\file_exists($composerPath = "{$pwd}/composer.json")) { | ||
$composerJson = \json_decode(\file_get_contents($composerPath), true); | ||
$vendorName = isset($composerJson['config']['vendor-dir']) ? $composerJson['config']['vendor-dir'] : $vendorName; | ||
} | ||
|
||
$locatePsrPath = function (Definition $definition, ?Constructor $constructor) use ($prefixesPsr4, $prefixesPsr0): string { | ||
return locatePsrPath($prefixesPsr4, $prefixesPsr0, $definition, $constructor); | ||
}; | ||
if (! \file_exists("{$pwd}/{$vendorName}/autoload.php")) { | ||
echo "\033[1;31mYou need to set up the project dependencies using the following commands: \033[0m" . PHP_EOL; | ||
echo 'curl -s http://getcomposer.org/installer | php' . PHP_EOL; | ||
echo 'php composer.phar install' . PHP_EOL; | ||
exit(1); | ||
} | ||
|
||
$derivingMap = defaultDerivingMap(); | ||
require "{$pwd}/{$vendorName}/autoload.php"; | ||
|
||
$collection = new DefinitionCollection(); | ||
$config = [ | ||
'use_strict_types' => true, | ||
'one_class_per_file' => true, // @todo this configuration isn't working right now | ||
'printer' => PsrPrinter::class, | ||
'namespace_parser' => Pair(namespaceName, 'buildNamespace'), | ||
'types' => [ | ||
Type\Enum\Enum::class => Pair(enum, buildEnum), | ||
], | ||
]; | ||
|
||
try { | ||
foreach (scan($path) as $file) { | ||
$collection = $collection->merge(parse($file, $derivingMap)); | ||
} | ||
} catch (ParseError $e) { | ||
echo 'Parse Error: ' . $e->getMessage(); | ||
exit(1); | ||
if ($path === '--gen-config') { | ||
$file = <<<CODE | ||
<?php | ||
declare(strict_types=1); | ||
namespace Fpp; | ||
use Nette\PhpGenerator\PsrPrinter; | ||
use function Pair; | ||
return [ | ||
'use_strict_types' => true, | ||
'one_class_per_file' => true, // @todo this configuration isn't working right now | ||
'printer' => PsrPrinter::class, | ||
'namespace_parser' => Pair(namespaceName, 'buildNamespace'), | ||
'types' => [ | ||
Type\Enum\Enum::class => Pair(enum, buildEnum), | ||
], | ||
]; | ||
CODE; | ||
|
||
\file_put_contents("{$pwd}/fpp-config.php", $file); | ||
|
||
echo "Default configuration written to {$pwd}/fpp-config.php\n"; | ||
exit(0); | ||
} | ||
|
||
if (\file_exists("{$pwd}/fpp-config.php")) { | ||
$config = \array_merge_recursive($config, require "{$pwd}/fpp-config.php"); | ||
} | ||
|
||
try { | ||
dump($collection, $locatePsrPath, loadTemplate, replace); | ||
} catch (\Exception $e) { | ||
echo 'Exception: ' . $e->getMessage(); | ||
if (empty($config['types'])) { | ||
echo "\033[1;31mNo parser found, check your fpp-config.php file\033[0m" . PHP_EOL; | ||
exit(1); | ||
} | ||
|
||
echo "Successfully generated and written to disk\n"; | ||
// bootstrapping done - @todo: make this bottom part more FP stylish | ||
$parser = zero(); | ||
|
||
foreach ($config['types'] as $type => $pair) { | ||
$parser = $parser->or(($pair->_1)()); | ||
} | ||
|
||
$namespaceParser = $config['namespace_parser']->_1; | ||
|
||
$toDump = \Nil(); | ||
|
||
scan($path)->map( | ||
fn ($f) => Pair(manyList($namespaceParser($parser))->run(\file_get_contents($f)), $f) | ||
)->map(function ($p) use ($config, &$toDump) { | ||
$parsed = $p->_1->head(); | ||
$filename = $p->_2; | ||
|
||
if (\strlen($parsed->_2) !== 0) { | ||
echo "\033[1;31mSyntax error at file $filename at:\033[0m" . PHP_EOL . PHP_EOL; | ||
echo \substr($parsed->_2, 0, 40) . PHP_EOL; | ||
exit(1); | ||
} | ||
|
||
$toDump = $toDump->combine($parsed->_1); | ||
}); | ||
|
||
$toDump->map(function ($ns) use ($config) { | ||
\var_dump(dump($ns, $config)); | ||
}); | ||
|
||
exit(0); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
namespace Foo\Bar { | ||
enum Color = Red | Green | Blue | ||
enum Human = Man | Woman | ||
} | ||
|
||
namespace Foo { | ||
enum Ok = Right | Wrong | ||
} |
Oops, something went wrong.