Giter VIP home page Giter VIP logo

fpp's Introduction

FPP

Functional PHP Preprocessor - Immutable data type generator

This library can generate immutable data types based on fpp definitions. The syntax is inspired by Haskell.

Build Status Coverage Status

Sponsor

If you want to support my work, become a patron at patreon.com/prolic.

Credits

Marcello Duarte created the ParserCombinators project in 2017. The rewrite of this library is heavily inspired by it and reuses some of its base functions.

Docs

See the docs here

Install

composer require prolic/fpp dev-master

Usage

Disable xdebug or increase xdebug.max_nesting_level in your php.ini file.

php vendor/bin/fpp.php <source dir or file>

Generate configuration

php vendor/bin/fpp.php --gen-config

You can then modify the config file to adjust to your needs.

Changes from 0.1.0 Release

This library has been rewritten from scratch. If you want to use the old version, pin your composer requirement to 0.1.0.

The master branch is not compatible at all.

fpp's People

Contributors

adirelle avatar ambersariya avatar cwoskoski avatar darrylhein avatar dependabot-preview[bot] avatar dependabot[bot] avatar gquemener avatar jeromegamez avatar lctrs avatar lunetics avatar notfloran avatar paales avatar prolic avatar sevavietl avatar sgomez avatar simensen avatar simonprins avatar unixslayer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fpp's Issues

[Feature] allow lowercased values

The example

namespace MyEnum;

data BrowserType = application | bot | browser | pim deriving (Enum);

is actually generating an error.

Parse Error: Syntax error, definiton name application must be upper case at line 4 on file

Please allow lowercased values.

Changed generation of non-mandatory command fields

My .fpp file contains the following:

    data SubmitFormStoreScope = SubmitFormStoreScope {
        \ReachDigital\Subscription\Model\ProductPlan\PlanId $planId,
        \ReachDigital\Subscription\Model\ProductPlan\StoreId $storeId,
        ?\ReachDigital\Subscription\Model\ProductPlan\Title $title,
        ?\ReachDigital\Subscription\Model\ProductPlan\Description $description
    } deriving (Command);

This generates:

namespace ReachDigital\Subscription\Model\ProductPlan\Command;

final class SubmitFormStoreScope extends \Prooph\Common\Messaging\Command
{
...
    public function title(): ?\ReachDigital\Subscription\Model\ProductPlan\Title
    {
        return \ReachDigital\Subscription\Model\ProductPlan\Title::fromString($this->payload['title']);
    }
...

This can throw the following exception:

Argument 1 passed to ReachDigital\Subscription\Model\ProductPlan\Title::fromString() must be of the type string, null given, called in .../src/Model/ProductPlan/Command/SubmitFormStoreScope.php on line 28

I just recently updated the repo to generate the files, before I was on 637ffc3 and there this same .fpp content generated:

namespace ReachDigital\Subscription\Model\ProductPlan\Command;

final class SubmitFormStoreScope extends \Prooph\Common\Messaging\Command
{
...
     public function title(): ?\ReachDigital\Subscription\Model\ProductPlan\Title
     {
        return isset($this->payload['title']) ? \ReachDigital\Subscription\Model\ProductPlan\Title::fromString($this->payload['title']) : null;
     }
...

This worked fine! What changed?

ID field stripped from payload

I ran into the following error:

 PHPUnit\Framework\Exception: Notice: Undefined index: planId in /data/web/magento2/vendor/reach-digital/magento2-subscription/src/Model/Domain.php:670.

This is the corresponding code:

namespace ReachDigital\Subscription\Model\ProductPlan {
    final class SubmitFormGlobalScope extends \Prooph\EventSourcing\AggregateChanged
    {
...
        public function planId(): PlanId
        {
            if (! isset($this->planId)) {
                $this->planId = PlanId::fromString($this->payload['planId']);
            }

            return $this->planId;
        }
...

This has to be because planId is stripped from the payload here:

namespace ReachDigital\Subscription\Model\ProductPlan {
    final class SubmitFormGlobalScope extends \Prooph\EventSourcing\AggregateChanged
    {
...
        public static function with(PlanId $planId, ProductId $productId, Enabled $enabled, EnabledOnFrontend $enabledOnFrontend, Title $title, Description $description, Schedule $schedule, Interval $interval, InitialPrice $initialPrice, InstallmentPrice $installmentPrice): SubmitFormGlobalScope
        {
            return new self($planId->toString(), [
                'productId' => $productId->toScalar(),
                'enabled' => $enabled->toScalar(),
                'enabledOnFrontend' => $enabledOnFrontend->toScalar(),
                'title' => $title->toString(),
                'description' => $description->toString(),
                'schedule' => $schedule->toArray(),
                'interval' => $interval->toArray(),
                'initialPrice' => $initialPrice->toScalar(),
                'installmentPrice' => $installmentPrice->toScalar(),
            ]);
        }
...

Any idea on how to solve this?

Incompatibility between the Equals and the Enum derivings

When defining the following fpp:

namespace Foo;
data MyData = MyData { MyEnum $myEnum } deriving (Equals);
data MyEnum = Value1 | Value2 | Value3 derivings (Enum);

The following files will be generated:

<?php

// this file is auto-generated by prolic/fpp
// don't edit this file manually

declare(strict_types=1);

namespace App;

final class MyData
{
    private $myEnum;

    public function __construct(MyEnum $myEnum)
    {
        $this->myEnum = $myEnum;
    }

    public function myEnum(): MyEnum
    {
        return $this->myEnum;
    }

    public function withMyEnum(MyEnum $myEnum): MyData
    {
        return new self($myEnum);
    }

    public function equals(MyData $myData): bool
    {
        if (\get_class($this) !== \get_class($myData)) {
            return false;
        }

        return $this->myEnum->toString() === $myData->myEnum->toString();
    }
}
<?php

// this file is auto-generated by prolic/fpp
// don't edit this file manually

declare(strict_types=1);

namespace App;

final class MyEnum
{
    public const OPTIONS = [
        'Value1' => 0,
        'Value2' => 1,
        'Value3' => 2,
    ];

    public const Value1 = 0;
    public const Value2 = 1;
    public const Value3 = 2;

    private $name;
    private $value;

    private function __construct(string $name)
    {
        $this->name = $name;
        $this->value = self::OPTIONS[$name];
    }

    public static function value1(): self
    {
        return new self('Value1');
    }

    public static function value2(): self
    {
        return new self('Value2');
    }

    public static function value3(): self
    {
        return new self('Value3');
    }

    public static function fromName(string $value): self
    {
        if (! isset(self::OPTIONS[$value])) {
            throw new \InvalidArgumentException('Unknown enum name given');
        }

        return self::{$value}();
    }

    public static function fromValue($value): self
    {
        foreach (self::OPTIONS as $name => $v) {
            if ($v === $value) {
                return self::{$name}();
            }
        }

        throw new \InvalidArgumentException('Unknown enum value given');
    }

    public function equals(MyEnum $other): bool
    {
        return \get_class($this) === \get_class($other) && $this->name === $other->name;
    }

    public function name(): string
    {
        return $this->name;
    }

    public function value()
    {
        return $this->value;
    }

    public function __toString(): string
    {
        return $this->name;
    }
}

As we can see in the method MyData::equals, there's a call on the method toString of MyEnum which does not exist.
The enum class has a __toString method though.

So I guess it would make sense to support Equals deriving along with Enum by adding a toString method to the enums, or to change the equals method so that the enum is casted into string instead of calling toString.

As a side note: I don't understand how it's possible to access the private property myEnum as if it was public in $myData->myEnum->toString(). If could enlight me, that'd be great, please :)

EDIT: This is a php behaviour, haven't found the doc/reason yet though => https://3v4l.org/adkHX

Cannot call abstract method Prooph\Common\Messaging\DomainMessage::setPayload()

It seems the setPayload tries to call an abstract parent::setPayload, should probably be removed.

namespace ReachDigital\Subscription\Model\ProductPlan {
    final class SubmitFormGlobalScope extends \Prooph\Common\Messaging\Command
    {
        use \Prooph\Common\Messaging\PayloadTrait;

        protected $messageName = 'ReachDigital\Subscription\Model\ProductPlan\SubmitFormGlobalScope';

        public function planId(): PlanId
        {
            return PlanId::fromString($this->payload['planId']);
        }

        public function productId(): ProductId
        {
            return ProductId::fromScalar($this->payload['productId']);
        }

        public function enabled(): Enabled
        {
            return Enabled::fromScalar($this->payload['enabled']);
        }

        public function enabledOnFrontend(): EnabledOnFrontend
        {
            return EnabledOnFrontend::fromScalar($this->payload['enabledOnFrontend']);
        }

        public function title(): Title
        {
            return Title::fromString($this->payload['title']);
        }

        public function description(): Description
        {
            return Description::fromString($this->payload['description']);
        }

        public function schedule(): Schedule
        {
            return Schedule::fromArray($this->payload['schedule']);
        }

        public function interval(): Interval
        {
            return Interval::fromArray($this->payload['interval']);
        }

        public function initialPrice(): InitialPrice
        {
            return InitialPrice::fromScalar($this->payload['initialPrice']);
        }

        public function installmentPrice(): InstallmentPrice
        {
            return InstallmentPrice::fromScalar($this->payload['installmentPrice']);
        }

        public static function with(PlanId $planId, ProductId $productId, Enabled $enabled, EnabledOnFrontend $enabledOnFrontend, Title $title, Description $description, Schedule $schedule, Interval $interval, InitialPrice $initialPrice, InstallmentPrice $installmentPrice): SubmitFormGlobalScope
        {
            return new self([
                'planId' => $planId->toString(),
                'productId' => $productId->toScalar(),
                'enabled' => $enabled->toScalar(),
                'enabledOnFrontend' => $enabledOnFrontend->toScalar(),
                'title' => $title->toString(),
                'description' => $description->toString(),
                'schedule' => $schedule->toArray(),
                'interval' => $interval->toArray(),
                'initialPrice' => $initialPrice->toScalar(),
                'installmentPrice' => $installmentPrice->toScalar(),
            ]);
        }

        protected function setPayload(array $payload): void
        {
            if (! isset($payload['planId']) || ! is_string($payload['planId'])) {
                throw new \InvalidArgumentException("Key 'planId' is missing in payload or is not a string");
            }

            if (! isset($payload['productId']) || ! is_int($payload['productId'])) {
                throw new \InvalidArgumentException("Key 'productId' is missing in payload or is not a int");
            }

            if (! isset($payload['enabled']) || ! is_bool($payload['enabled'])) {
                throw new \InvalidArgumentException("Key 'enabled' is missing in payload or is not a bool");
            }

            if (! isset($payload['enabledOnFrontend']) || ! is_bool($payload['enabledOnFrontend'])) {
                throw new \InvalidArgumentException("Key 'enabledOnFrontend' is missing in payload or is not a bool");
            }

            if (! isset($payload['title']) || ! is_string($payload['title'])) {
                throw new \InvalidArgumentException("Key 'title' is missing in payload or is not a string");
            }

            if (! isset($payload['description']) || ! is_string($payload['description'])) {
                throw new \InvalidArgumentException("Key 'description' is missing in payload or is not a string");
            }

            if (! isset($payload['schedule']) || ! is_array($payload['schedule'])) {
                throw new \InvalidArgumentException("Key 'schedule' is missing in payload or is not an array");
            }

            if (! isset($payload['interval']) || ! is_array($payload['interval'])) {
                throw new \InvalidArgumentException("Key 'interval' is missing in payload or is not an array");
            }

            if (! isset($payload['initialPrice']) || ! is_float($payload['initialPrice'])) {
                throw new \InvalidArgumentException("Key 'initialPrice' is missing in payload or is not a float");
            }

            if (! isset($payload['installmentPrice']) || ! is_float($payload['installmentPrice'])) {
                throw new \InvalidArgumentException("Key 'installmentPrice' is missing in payload or is not a float");
            }

            parent::setPayload($payload);
        }
    }
}

Define named constructors for non-derived entities

While trying to use this great library, I find out that it is really problematic to use custom entities in the payload, because there is no mechanism to deduce the proper named constructor.

For example, I use marc-mabe/php-enum for enums. It has byValue($value), that can be used for constructing from inside AggregateChanged or Command. I think it would be usefull to have some kind of config where I can define the list of named constructors like following:

return [
    'Mabe\Enum' => 'byValue'
]

Then in the buildArgumentConstructor helper function we can use this mapping to build a construction. The code can look like the following:

foreach ($mapping as $type => $method) {
    if (is_($argument->type(), $type, true)) {
        return "{$calledClass}::{$method}(\${$argument->name()})";
    }
}

If you like this approach I can provide a PR.

Thank you in advance.

Can't create an event with a single field

When trying to add an event with a single field, like this:

namespace ReachDigital\Subscription\Model\ProductPlan\Event {
...
    data ProductPlanWasDiscontinued = ProductPlanWasDiscontinued {
        \ReachDigital\Subscription\Model\ProductPlan\PlanId $planId,
    } deriving (AggregateChanged);
...
}

The following error appears:

Fatal error: Uncaught TypeError: Return value of Fpp\Builder\buildPayloadValidation() must be of the type string, boolean returned in /src/vendor/prolic/fpp/src/builder/buildPayloadValidation.php:203
Stack trace:
#0 /src/vendor/prolic/fpp/src/replace.php(28): Fpp\Builder\buildPayloadValidation(Object(Fpp\Definition), Object(Fpp\Constructor), Object(Fpp\DefinitionCollection), '{{payload_valid...')
#1 /src/vendor/prolic/fpp/src/dump.php(47): Fpp\replace('namespace Reach...', Object(Fpp\Definition), Object(Fpp\Constructor), Object(Fpp\DefinitionCollection))
#2 /src/vendor/prolic/fpp/bin/fpp.php(44): Fpp\dump(Object(Fpp\DefinitionCollection), Object(Closure), '\\Fpp\\loadTempla...', '\\Fpp\\replace')
#3 {main}
  thrown in /src/vendor/prolic/fpp/src/builder/buildPayloadValidation.php on line 203

Process finished with exit code 255

deriving(Uuid) Should have its \Ramsey\Uuid\UuidInterface as constructor instead of Uuid

namespace ReachDigital\Subscription\Model\ProductPlan {
    data PlanId = PlanId deriving(Uuid);
}

__construct(\Ramsey\Uuid\UuidInterface $planId) should be generated, but it returns:

namespace ReachDigital\Subscription\Model\ProductPlan {
    final class PlanId
    {
        private $uuid;

        public static function generate(): PlanId
        {
            return new self(\Ramsey\Uuid\Uuid::uuid4());
        }

        public static function fromString(string $planId): PlanId
        {
            return new self(\Ramsey\Uuid\Uuid::fromString($planId));
        }

        private function __construct(Uuid $planId)
        {
            $this->uuid = $planId;
        }

        public function toString(): string
        {
            return $this->uuid->toString();
        }

        public function __toString(): string
        {
            return $this->uuid->toString();
        }

        public function equals(PlanId $other): bool
        {
            return $this->uuid->equals($other->uuid);
        }
    }
}

Optional arguments when providing full namespaced paths

namespace ReachDigital\Subscription\Model\ProductPlan\Command {
    data SubmitFormWebsiteScope = SubmitFormWebsiteScope {
        \ReachDigital\Subscription\Model\ProductPlan\PlanId $planId,
        \ReachDigital\Subscription\Model\ProductPlan\WebsiteId $websiteId,
        ?\ReachDigital\Subscription\Model\ProductPlan\Enabled $enabled,
        ?\ReachDigital\Subscription\Model\ProductPlan\EnabledOnFrontend $enabledOnFrontend,
        ?\ReachDigital\Subscription\Model\ProductPlan\InitialPrice $initialPrice,
        ?\ReachDigital\Subscription\Model\ProductPlan\InstallmentPrice $installmentPrice,
    } deriving (Command);
}

Will thow an error:
Parse Error: Syntax error, unexpected '\', expecting 'T_STRING' at line 59 on file

Also it seems there is an off-by-one error in the error reporting, it actually should be line 58 in my case.

Using php keywords breaks fpp

E.g. when you want to use php keywords like "new" or "class" breaks fpp:

e.g.

data Bar = Old | New deriving (Enum);

results in:

Parse Error: Syntax error, unexpected 'New', expecting 'T_STRING' at line 5 on file 'foo.fpp'

Wrongly generated Commands with enum types?

Hi @prolic,

I've just changed my declaration to the new ENUM style:

    data CancellationReason = BadService | UnknownReason deriving(Enum);

But this seems to be causing issues when using in a command.

My .fpp (shortened):

namespace ReachDigital\Subscription\Model\Subscription {
    data SubscriptionId = SubscriptionId deriving(Uuid);
    ...
    data CancellationReason = BadService | UnknownReason deriving(Enum);
}
namespace ReachDigital\Subscription\Model\Subscription\Command {
    ...
    data CancelSubscription = CancelSubscription {
        \ReachDigital\Subscription\Model\Subscription\SubscriptionId $subscriptionId,
        \ReachDigital\Subscription\Model\Subscription\CancellationReason $reason
    } deriving(Command);
    ...
}

This results in the following file for ReachDigital\Subscription\Model\Subscription\Command\CancelSubscription:

<?php

// this file is auto-generated by prolic/fpp
// don't edit this file manually

declare(strict_types=1);

namespace ReachDigital\Subscription\Model\Subscription\Command;

final class CancelSubscription extends \Prooph\Common\Messaging\Command
{
    use \Prooph\Common\Messaging\PayloadTrait;

    protected $messageName = 'ReachDigital\Subscription\Model\Subscription\Command\CancelSubscription';

    public function subscriptionId(): \ReachDigital\Subscription\Model\Subscription\SubscriptionId
    {
        return \ReachDigital\Subscription\Model\Subscription\SubscriptionId::fromString($this->payload['subscriptionId']);
    }

    public function reason(): \ReachDigital\Subscription\Model\Subscription\CancellationReason
    {
        return \ReachDigital\Subscription\Model\Subscription\CancellationReason::fromString($this->payload['reason']);
    }

    public static function with(\ReachDigital\Subscription\Model\Subscription\SubscriptionId $subscriptionId, \ReachDigital\Subscription\Model\Subscription\CancellationReason $reason): CancelSubscription
    {
        return new self([
            'subscriptionId' => $subscriptionId->toString(),
            'reason' => $reason->toString(),
        ]);
    }

    protected function setPayload(array $payload): void
    {
        if (! isset($payload['subscriptionId']) || ! is_string($payload['subscriptionId'])) {
            throw new \InvalidArgumentException("Key 'subscriptionId' is missing in payload or is not a string");
        }

        if (! isset($payload['reason']) || ! is_string($payload['reason'])) {
            throw new \InvalidArgumentException("Key 'reason' is missing in payload or is not a string");
        }

        $this->payload = $payload;
    }
}

Which causes issues, because CancellationReason::fromString() and CancellationReason::toString() are being used, while these are not in the that class anymore since the updated enum logic.

Am I doing something wrong?

Thanks!

FPP File Watcher config for docker

Program: docker
Argument: run --rm -i -v $ProjectFileDir$:/app -u 1000 -w /app prooph/php:7.1-cli php vendor/prolic/fpp/bin/fpp.php $FileDirRelativeToProjectRoot$/$FileName$

Note: docker run argument -u 1000 might be adjusted according to UID of the user who should own the generated files.

use/import support

Hi,
first of all, many thanks for this great library. Expect some PR's in near future from me. ;)

I have this in a types.fpp

namespace App\Model\State {

    data BuildingId = BuildingId deriving (Uuid);

    data User = String deriving (ToString, FromString);

    data Building = Building {BuildingId $buildingId, ?User $user} deriving (ToArray, FromArray);
}

namespace App\Model\Message {

    data AddBuilding = AddBuilding { \App\Model\State\BuildingId $buildingId, \App\Model\State\User $user } deriving (Command: "Building.AddBuilding");

}

FPP does an awesome job already. However, it would be nice to import definitions from other namespaces like it is possible in plain PHP or JavaScript (which might be easier to implement and would allow support for multiple FPP files)

PHP use statement:

namespace App\Model\State {

    data BuildingId = BuildingId deriving (Uuid);

    data User = String deriving (ToString, FromString);

    data Building = Building {BuildingId $buildingId, ?User $user} deriving (ToArray, FromArray);
}

namespace App\Model\Message {
    
    use \App\Model\State\BuildingId;
    use \App\Model\State\User;
    //Maybe it is too hard to support something like this: use \App\Model\State;
    //State\BuildingId $building, State\User $user
    //but direct imports would be a nice start

    data AddBuilding = AddBuilding { BuildingId $buildingId, User $user } deriving (Command: "Building.AddBuilding");

}

JS import

JS import statement would have the advantage to specify the fpp file that contains the definition rather than the assigned PHP namespace.

File <project_root>/fpp/model.fpp

namespace App\Model\State;

data BuildingId = BuildingId deriving (Uuid);

data User = String deriving (ToString, FromString);

data Building = Building {BuildingId $buildingId, ?User $user} deriving (ToArray, FromArray);

File <project_root>/fpp/messages.fpp

namespace App\Model\Message;

import {BuildingId, User} from '../model';

data AddBuilding = AddBuilding { BuildingId $buildingId, User $user } deriving (Command: "Building.AddBuilding");

Note: Import path is relative to current file (or maybe absolute to project root?). File extension .fpp is not needed. FPP would need to detect conflicting namespaces.

Impossible to use existing marker on marker

Scenario

Given the following "domain.fpp":

namespace App;
marker Person : \App\User;

and the following existing marker:

<?php

declare (strict_types = 1);

namespace App;

interface User
{
}

When I run the fpp command
Then the "src/Person.php" file should contain:

<?php

// this file is auto-generated by prolic/fpp
// don't edit this file manually

declare(strict_types=1);

namespace App;

interface Person extends \App\User
{
}

Result

This scenario fails with the following error:

Exception: Marker App\Person cannot extend unknown marker App\User

Fix

Switch the autoloader flag to true on this interface_exists function call.

error in readme

The example enum in the readme

namespace MyEnum;

enum Color = Red | Blue | Green | Yellow deriving (Enum);

is generating an error.

Parse Error: Syntax error, unexpected 'enum', expecting 'data' at line 4 on file

Impossible to use existing marker on data

Scenario

Given the following "domain.fpp":

namespace App;
data Person : \App\User = Person;

and the following existing marker:

<?php

declare (strict_types = 1);

namespace App;

interface User
{
}

When I run the fpp command
Then the "src/Person.php" file should contain:

<?php

// this file is auto-generated by prolic/fpp
// don't edit this file manually

declare(strict_types=1);

namespace App;

final class Person implements \App\User
{
}

Result

This scenario fails with the following error:

Exception: Cannot mark data App\Person with unknown marker App\User

Fix

Switch the autoloader flag to true on this interface_exists function call.

Enum VALUE const should have access modified (low prio)

Generated class:

namespace ReachDigital\Subscription\Model\ProductPlan\IntervalUnit;

final class Month extends \ReachDigital\Subscription\Model\ProductPlan\IntervalUnit
{
    const VALUE = 'Month';
}

should probably be

namespace ReachDigital\Subscription\Model\ProductPlan\IntervalUnit;

final class Month extends \ReachDigital\Subscription\Model\ProductPlan\IntervalUnit
{
    public const VALUE = 'Month';
}

"An argument cannot be a list and nullable at the same time"

In addition to #42;

Why isn't this possible?

    data SubmitFormWebsiteScope = SubmitFormWebsiteScope {
        \ReachDigital\Subscription\Model\ProductPlan\PlanId $planId,
        \ReachDigital\Subscription\Model\ProductPlan\WebsiteId $websiteId,
        ?\ReachDigital\Subscription\Model\ProductPlan\Enabled $enabled,
        ?\ReachDigital\Subscription\Model\ProductPlan\EnabledOnFrontend $enabledOnFrontend,
        ?\ReachDigital\Subscription\Model\ProductPlan\TermPrice[] $termPrices,
    } deriving (Command);

When making TermPrice[] mandatory (removing the question mark), all works fine, but I want to be able to make $termPrices null.

Deriving 'AggregateChanged' extends 'DomainEvent'

For example:

namespace ReachDigital\Subscription\Model\ProductPlan {
        data FormWasSubmittedInGlobalScope = FormWasSubmittedInGlobalScope {
                ...
        } deriving (AggregateChanged);
}

Generates the following:

namespace ReachDigital\Subscription\Model\ProductPlan {
    final class FormWasSubmittedInGlobalScope extends \Prooph\Common\Messaging\DomainEvent
    {
    ...
    }
}

Invalid deriving

Hi @prolic,

The following code worked before:

namespace ReachDigital\Subscription\Model\Subscription {
    ...
    data Status = Status\AwaitingFulfillment | Status\Active | Status\Cancelled deriving(Enum);
    ...
}

But I just tried to edit our fpp file for some other things, and now the following error shows up:

Fatal error: Uncaught Fpp\InvalidDeriving: Invalid deriving on ReachDigital\Subscription\Model\Subscription\Status, deriving Enum expects constructor without any namespace in /Users/maikelkoek/Sites/vanmoof/src/vendor/prolic/fpp/src/InvalidDeriving.php:58

Any ideas?

My full stack trace:

php /Users/maikelkoek/Sites/vanmoof/src/vendor/prolic/fpp/bin/fpp.php /Users/maikelkoek/Sites/vanmoof/src/vendor/reach-digital/magento2-subscriptions/src/Model/Domain.fpp /Users/maikelkoek/Sites/vanmoof/src/vendor/reach-digital/magento2-subscriptions/src/Model/Domain.php

Fatal error: Uncaught Fpp\InvalidDeriving: Invalid deriving on ReachDigital\Subscription\Model\Subscription\Status, deriving Enum expects constructor without any namespace in /Users/maikelkoek/Sites/vanmoof/src/vendor/prolic/fpp/src/InvalidDeriving.php:58
Stack trace:
#0 /Users/maikelkoek/Sites/vanmoof/src/vendor/prolic/fpp/src/Deriving/Enum.php(75): Fpp\InvalidDeriving::noConstructorNamespacesAllowed(Object(Fpp\Definition), 'Enum')
#1 /Users/maikelkoek/Sites/vanmoof/src/vendor/prolic/fpp/src/Definition.php(103): Fpp\Deriving\Enum->checkDefinition(Object(Fpp\Definition))
#2 /Users/maikelkoek/Sites/vanmoof/src/vendor/prolic/fpp/src/parse.php(562): Fpp\Definition->__construct('ReachDigital\\Su...', 'Status', Array, Array, Array, NULL)
#3 /Users/maikelkoek/Sites/vanmoof/src/vendor/prolic/fpp/bin/fpp.php(36): Fpp\parse('/Users/maikelko...', Array)
#4 {main}
  thrown in /Users/maikelkoek/Sites/vanmoof/src/vendor/prolic/fpp/src/InvalidDeriving.php on line 58

Process finished with exit code 255

Add a marker keyword

A marker interface is defined simply by the marker keyword, followed by an interface name, this way we can reuse this marker interface later. Source

namespace My\Component;
marker Exception;

generates

namespace My\Component;

interface Exception
{
}

[Feature] generate files according to PSR

The example

namespace MyEnum;

data BrowserType = Application | Bot | Browser | Pim deriving (Enum);

is generating the output

declare(strict_types=1);

namespace MyEnum {
    final class Application extends BrowserType
    {
        const VALUE = 'Application';
    }
}

namespace MyEnum {
    final class Bot extends BrowserType
    {
        const VALUE = 'Bot';
    }
}

...

Could you change the output like this? (I would prefer Version 3)

Version 1 (one namespace for all classes)

declare(strict_types=1);

namespace MyEnum {
    final class Application extends BrowserType
    {
        const VALUE = 'Application';
    }

    final class Bot extends BrowserType
    {
        const VALUE = 'Bot';
    }
}

Version 2 (one namespace for all classes without brackets)

declare(strict_types=1);

namespace MyEnum;

final class Application extends BrowserType
{
    const VALUE = 'Application';
}

final class Bot extends BrowserType
{
    const VALUE = 'Bot';
}

Version 3: each class into a seperate file according to PSR1/2

Cannot build argument constructor when deriving Command

Hi, I believe this could be a bug but also could be a mistake of my behalf.

namespace App\Domain\Model;

data Isbn = Isbn { string $isbn } deriving (ToString) where
    Isbn:
        | strlen($isbn) !== 10 | strlen($isbn) !== 13 => 'Invalid Isbn length'
    ;

data Title = Title {string $title} deriving (ToString);
data Book = Book { Isbn $isbn, Title $title };

namespace App\Application;

data BorrowBook = BorrowBook { \App\Domain\Model\Book $book } deriving (Command);

Throws the following error Exception: Cannot build argument constructor for App\Domain\Model\Book%

PHP Storm integration path

The filewatcher command path can be shortened to:
$ProjectFileDir$/vendor/prolic/fpp/bin/fpp.php $FilePath$

Strict type for decimal number?

We tried to use float for our (currency) decimal numbers, but this fails when passing no decimal numbers:

is_float(10)

But an int should be allowed as a float type.

https://wiki.php.net/rfc/scalar_type_hints_v5#int-_float_exception_makes_strict_mode_flawed

Maybe we can check is_float(x) OR is_int(x)?

The corresponding code with the issue in setPayload():

if (! isset($payload['ourDecimalField']) || ! is_float($payload['ourDecimalField'])) {
    throw new \InvalidArgumentException("Key 'ourDecimalField' is missing in payload or is not a float");
}

fromString doesn't actually instantiate the field type

namespace A {
    data Uuid = Uuid deriving(Uuid);
}

namespace B {
    data Author = Author { \A\Uuid $author } deriving(FromString, ToString, Equals); 
}

I expect something like this:

public static function fromString(string $author): Author
{
    return new self(\A\Uuid::fromString($author));
}

It actually generates:

public static function fromString(string $author): Author
{
    return new Author($author);
}

Array of objects in command

Hi @prolic,

We'd like to use an array of objects as data in a command, but it seems that's not possible now, or we're doing something wrong.

We've got this object created:

    data TermPrice = TermPrice {
        int $termNrStart,
        ?int $termCount,
        float $price,
        ?float $specialPrice
    } deriving (FromArray, ToArray);

And we try to use an array of TermPrice's in our command:

...
namespace ReachDigital\Subscription\Model\ProductPlan\Command {
    data SubmitFormGlobalScope = SubmitFormGlobalScope {
        ...
        \ReachDigital\Subscription\Model\ProductPlan\TermPrice[] $termPrices
    } deriving (Command);
...

But this results in the following in our SubmitFormGlobalScope class:

...
    public function termPrices(): array
    {
        return \ReachDigital\Subscription\Model\ProductPlan\TermPrice::fromArray($this->payload['termPrices']);
    }

    public static function with(\ReachDigital\Subscription\Model\ProductPlan\PlanId $planId, \ReachDigital\Subscription\Model\ProductPlan\ProductId $productId, \ReachDigital\Subscription\Model\ProductPlan\Enabled $enabled, \ReachDigital\Subscription\Model\ProductPlan\EnabledOnFrontend $enabledOnFrontend, \ReachDigital\Subscription\Model\ProductPlan\Title $title, \ReachDigital\Subscription\Model\ProductPlan\Description $description, \ReachDigital\Subscription\Model\ProductPlan\Schedule $schedule, \ReachDigital\Subscription\Model\ProductPlan\Interval $interval, array $termPrices): SubmitFormGlobalScope
    {
        return new self([
            ...
            'termPrices' => $termPrices->toArray(),
        ]);
    }
...

The result of termPrices() is not an array, this method should probably loop through $this->payload['termPrices'], and call fromArray on all of those items?

The same goes for the value for termPrices in with() (the toArray() should be called in a loop to call toArray() for all termPrices).

Thanks!

ToArray can not be implemented for single array argument

I am trying to do the following:

data Projects = Projects { ProjectKey[] $projects } deriving (ToArray, ToString);

This isn't allowed, because it requires two arugments. ToString, FromString builds but that tries to do a toString() on an array but that doesn't work.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.