<?php

namespace Dywee\OrderBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Dywee\CoreBundle\Model\UserInterface;

/**
 * Offer
 *
 * @ORM\Table(name="offers")
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks()
 *
 */
class Offer
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var float
     *
     * @ORM\Column(name="priceVatExcl", type="float")
     */
    private $priceVatExcl = 0;

    /**
     * @var float
     *
     * @ORM\Column(name="deliveryCost", type="float")
     */
    private $deliveryCost = 0;

    /**
     * @var float
     *
     * @ORM\Column(name="vatRate", type="float")
     */
    private $vatRate = 0;

    /**
     * @var float
     *
     * @ORM\Column(name="vatPrice", type="float")
     */
    private $vatPrice = 0;

    /**
     * @var float
     *
     * @ORM\Column(name="discountRate", type="float")
     */
    private $discountRate = 0;

    /**
     * @var float
     *
     * @ORM\Column(name="discountValue", type="float")
     */
    private $discountValue = 0;

    /**
     * @var float
     *
     * @ORM\Column(name="priceVatIncl", type="float")
     */
    private $priceVatIncl = 0;

    /**
     * @var float
     *
     * @ORM\Column(name="totalPrice", type="float")
     */
    private $totalPrice = 0;

    /**
     * @var string
     *
     * @ORM\Column(name="description", type="text", nullable=true)
     */
    private $description = '';

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="createdAt", type="datetime")
     */
    private $createdAt;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="validatedAt", type="datetime", nullable=true)
     */
    private $validatedAt;

    /**
     * @var string
     *
     * @ORM\Column(name="reference", type="string", length=255)
     */
    private $reference = '';

    /**
     * @var integer
     *
     * @ORM\Column(name="state", type="smallint")
     */
    private $state = 1;

    /**
     * @ORM\ManyToOne(targetEntity="Dywee\AddressBundle\Entity\Address", cascade={"persist"})
     */
    private $address;

    /**
     * @ORM\OneToMany(targetEntity="Dywee\OrderBundle\Entity\OfferElement", mappedBy="offer", cascade={"persist", "remove"})
     */
    private $offerElements;

    /**
     * @ORM\Column(name="isPriceTTC", type="boolean")
     */
    private $isPriceTTC = true;

    /*
    /**
     * @var string
     *
     * @ORM\ManyToOne(targetEntity="Dywee\ShipmentBundle\Entity\Deliver")
     * @ORM\JoinColumn(nullable=true)
     *
    private $deliver;
    */

    /**
     * @var string
     *
     * @ORM\Column(name="deliveryMethod", type="string", length=255, nullable=true)
     */
    private $deliveryMethod;

    /**
     * @var string
     *
     * @ORM\Column(name="deliveryInfo", type="string", length=255, nullable=true)
     */
    private $deliveryInfo;

    /**
     * @ORM\ManyToOne(targetEntity="Dywee\CoreBundle\Model\UserInterface")
     */
    private $createdBy;

    /**
     * @var integer
     *
     * @ORM\Column(name="duration", type="smallint")
     */
    private $duration = 0;

    /**
     * @ORM\Column(name="sellType", type="smallint", nullable=true)
     */
    private $sellType;

    /**
     * Get id
     *
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set priceVatExcl
     *
     * @param float $priceVatExcl
     * @return Offer
     */
    public function setPriceVatExcl($priceVatExcl)
    {
        $this->priceVatExcl = $priceVatExcl;

        return $this;
    }

    /**
     * Get priceVatExcl
     *
     * @return float
     */
    public function getPriceVatExcl()
    {
        return $this->priceVatExcl;
    }

    /**
     * Set deliveryCost
     *
     * @param float $deliveryCost
     * @return Offer
     */
    public function setDeliveryCost($deliveryCost)
    {
        $this->deliveryCost = $deliveryCost;

        return $this;
    }

    /**
     * Get deliveryCost
     *
     * @return float
     */
    public function getDeliveryCost()
    {
        return $this->deliveryCost;
    }

    /**
     * Set vatRate
     *
     * @param float $vatRate
     * @return Offer
     */
    public function setVatRate($vatRate)
    {
        $this->vatRate = $vatRate;

        return $this;
    }

    /**
     * Get vatRate
     *
     * @return float
     */
    public function getVatRate()
    {
        return $this->vatRate;
    }

    /**
     * Set vatPrice
     *
     * @param float $vatPrice
     * @return Offer
     */
    public function setVatPrice($vatPrice)
    {
        $this->vatPrice = $vatPrice;

        return $this;
    }

    /**
     * Get vatPrice
     *
     * @return float
     */
    public function getVatPrice()
    {
        return $this->vatPrice;
    }

    /**
     * Set discountRate
     *
     * @param float $discountRate
     * @return Offer
     */
    public function setDiscountRate($discountRate)
    {
        $this->discountRate = $discountRate;

        return $this;
    }

    /**
     * Get discountRate
     *
     * @return float
     */
    public function getDiscountRate()
    {
        return $this->discountRate;
    }

    /**
     * Set discountValue
     *
     * @param float $discountValue
     * @return Offer
     */
    public function setDiscountValue($discountValue)
    {
        $this->discountValue = $discountValue;

        return $this;
    }

    /**
     * Get discountValue
     *
     * @return float
     */
    public function getDiscountValue()
    {
        return $this->discountValue;
    }

    /**
     * Set priceVatIncl
     *
     * @param float $priceVatIncl
     * @return Offer
     */
    public function setPriceVatIncl($priceVatIncl)
    {
        $this->priceVatIncl = $priceVatIncl;

        return $this;
    }

    /**
     * Get priceVatIncl
     *
     * @return float
     */
    public function getPriceVatIncl()
    {
        return $this->priceVatIncl;
    }

    /**
     * Set totalPrice
     *
     * @param float $totalPrice
     * @return Offer
     */
    public function setTotalPrice($totalPrice)
    {
        $this->totalPrice = $totalPrice;

        return $this;
    }

    /**
     * Get totalPrice
     *
     * @return float
     */
    public function getTotalPrice()
    {
        return $this->totalPrice;
    }

    /**
     * Set description
     *
     * @param string $description
     * @return Offer
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Get description
     *
     * @return string
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Set createdAt
     *
     * @param \DateTime $createdAt
     * @return Offer
     */
    public function setCreatedAt($createdAt)
    {
        $this->createdAt = $createdAt;

        return $this;
    }

    /**
     * Get createdAt
     *
     * @return \DateTime
     */
    public function getCreatedAt()
    {
        return $this->createdAt;
    }

    /**
     * Set validatedAt
     *
     * @param \DateTime $validatedAt
     * @return Offer
     */
    public function setValidatedAt($validatedAt)
    {
        $this->validatedAt = $validatedAt;

        return $this;
    }

    /**
     * Get validatedAt
     *
     * @return \DateTime
     */
    public function getValidatedAt()
    {
        return $this->validatedAt;
    }

    /**
     * Set reference
     *
     * @param string $reference
     * @return Offer
     */
    public function setReference($reference)
    {
        $this->reference = $reference;

        return $this;
    }

    /**
     * Get reference
     *
     * @return string
     */
    public function getReference()
    {
        return $this->reference;
    }

    /**
     * Set state
     *
     * @param integer $state
     * @return Offer
     */
    public function setState($state)
    {
        $this->state = $state;

        return $this;
    }

    /**
     * Get state
     *
     * @return integer
     */
    public function getState()
    {
        return $this->state;
    }

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->offerElements = new \Doctrine\Common\Collections\ArrayCollection();
        $this->createdAt = new \DateTime();
        $this->state = 1;
    }

    /**
     * Set address
     *
     * @param \Dywee\AddressBundle\Entity\Address $address
     * @return Offer
     */
    public function setAddress(\Dywee\AddressBundle\Entity\Address $address = null)
    {
        $this->address = $address;

        return $this;
    }

    /**
     * Get address
     *
     * @return \Dywee\AddressBundle\Entity\Address
     */
    public function getAddress()
    {
        return $this->address;
    }

    /**
     * Add offerElements
     *
     * @param \Dywee\OrderBundle\Entity\OfferElement $offerElements
     * @return Offer
     */
    public function addOfferElement(\Dywee\OrderBundle\Entity\OfferElement $offerElements)
    {
        $this->offerElements[] = $offerElements;
        $offerElements->setOffer($this);
        return $this;
    }

    /**
     * Remove offerElements
     *
     * @param \Dywee\OrderBundle\Entity\OfferElement $offerElements
     */
    public function removeOfferElement(\Dywee\OrderBundle\Entity\OfferElement $offerElements)
    {
        $this->offerElements->removeElement($offerElements);
    }

    /**
     * Get offerElements
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getOfferElements()
    {
        return $this->offerElements;
    }

    /**
     * @ORM\PrePersist
     * @ORM\PreUpdate
     */
    public function forcePriceCalculation()
    {
        $isTTC = true;
        $price = 0;
        $this->setVatRate($this->getAddress()->getCountry()->getVatRate());
        $this->setPriceVatIncl(0);
        foreach($this->getOfferElements() as $offerElement)
            $price += $offerElement->getTotalPrice();

        if($isTTC)
        {
            $this->setPriceVatIncl($price);
            $this->setPriceVatExcl($price/(1 + $this->getVatRate()/100));
            $this->setVatPrice($this->getPriceVatIncl()-$this->getPriceVatExcl());
        }
        else{
            /*$this->setPriceVatExcl($price);
            $this->setVatPrice($this->getPriceVatExcl()*$this->getVatRate()/100);
            $this->setPriceVatIncl($this->getPriceVatExcl()+$this->getVatPrice());*/
        }

        if($this->getDiscountRate() > 0 || $this->getDiscountValue() > 0)
        {
            if($this->getDiscountValue() == 0)
                $this->setDiscountValue($this->getDiscountRate()*$this->getPriceVatExcl()/100);
            else if($this->getDiscountRate() == 0)
                $this->setDiscountRate(100*$this->getDiscountValue()/$this->getPriceVatExcl());
        }

        $this->setTotalPrice($this->getPriceVatIncl() + $this->getDeliveryCost() - $this->getDiscountValue());

        return $this;
    }

    /**
     * Set deliveryMethod
     *
     * @param string $deliveryMethod
     * @return Offer
     */
    public function setDeliveryMethod($deliveryMethod)
    {
        $this->deliveryMethod = $deliveryMethod;

        return $this;
    }

    /**
     * Get deliveryMethod
     *
     * @return string
     */
    public function getDeliveryMethod()
    {
        return $this->deliveryMethod;
    }

    /**
     * Set deliveryInfo
     *
     * @param string $deliveryInfo
     * @return Offer
     */
    public function setDeliveryInfo($deliveryInfo)
    {
        $this->deliveryInfo = $deliveryInfo;

        return $this;
    }

    /**
     * Get deliveryInfo
     *
     * @return string
     */
    public function getDeliveryInfo()
    {
        return $this->deliveryInfo;
    }

    /**
     * Set deliver
     *
     * @param \Dywee\OrderBundle\Entity\Deliver $deliver
     *
     * @return Offer
     */
    public function setDeliver(\Dywee\OrderBundle\Entity\Deliver $deliver = null)
    {
        $this->deliver = $deliver;

        return $this;
    }

    /**
     * Get deliver
     *
     * @return \Dywee\OrderBundle\Entity\Deliver
     */
    public function getDeliver()
    {
        return $this->deliver;
    }

    public function containsElementReduction()
    {
        foreach($this->offerElements as $offerElement)
            if($offerElement->getDiscountValue() > 0)return true;
        return false;
    }

    /**
     * Set createdBy
     *
     * @param UserInterface $createdBy
     * @return Offer
     */
    public function setCreatedBy(UserInterface $createdBy = null)
    {
        $this->createdBy = $createdBy;

        return $this;
    }

    /**
     * Get createdBy
     *
     * @return UserInterface
     */
    public function getCreatedBy()
    {
        return $this->createdBy;
    }

    /**
     * Set duration
     *
     * @param integer $duration
     * @return Offer
     */
    public function setDuration($duration)
    {
        $this->duration = $duration;

        return $this;
    }

    /**
     * Get duration
     *
     * @return integer
     */
    public function getDuration()
    {
        return $this->duration;
    }

    /**
     * Set isPriceTTC
     *
     * @param boolean $isPriceTTC
     *
     * @return Offer
     */
    public function setIsPriceTTC($isPriceTTC)
    {
        $this->isPriceTTC = $isPriceTTC;

        return $this;
    }

    /**
     * Get isPriceTTC
     *
     * @return boolean
     */
    public function getIsPriceTTC()
    {
        return $this->isPriceTTC;
    }

    /**
     * Set sellType
     *
     * @param integer $sellType
     *
     * @return Offer
     */
    public function setSellType($sellType)
    {
        $this->sellType = $sellType;

        return $this;
    }

    /**
     * Get sellType
     *
     * @return integer
     */
    public function getSellType()
    {
        return $this->sellType;
    }
}


<?php

declare(strict_types=1);

namespace RevisionTen\Forms\Handler;

use RevisionTen\Forms\Command\FormEditItemCommand;
use RevisionTen\Forms\Event\FormEditItemEvent;
use RevisionTen\Forms\Model\Form;
use RevisionTen\CQRS\Interfaces\AggregateInterface;
use RevisionTen\CQRS\Interfaces\CommandInterface;
use RevisionTen\CQRS\Interfaces\EventInterface;
use RevisionTen\CQRS\Interfaces\HandlerInterface;
use RevisionTen\CQRS\Message\Message;

final class FormEditItemHandler extends FormBaseHandler implements HandlerInterface
{
    /**
     * {@inheritdoc}
     *
     * @var Form $aggregate
     */
    public function execute(CommandInterface $command, AggregateInterface $aggregate): AggregateInterface
    {
        $payload = $command->getPayload();

        if (isset($payload['data']['name'])) {
            // Clean the name to only contrain lowercase letters.
            $payload['data']['name'] = strtolower(preg_replace('/[^A-Za-z0-9]/', '', $payload['data']['name']));
        }

        // Add to items.
        $data = $payload['data'];
        $uuid = $payload['uuid'];

        // A function that updates the items data by merging it with the new data.
        $updateDataFunction = function (&$item, &$collection) use ($data) {
            $item['data'] = array_merge($item['data'], $data);
        };
        self::onItem($aggregate, $uuid, $updateDataFunction);

        return $aggregate;
    }

    /**
     * {@inheritdoc}
     */
    public static function getCommandClass(): string
    {
        return FormEditItemCommand::class;
    }

    /**
     * {@inheritdoc}
     */
    public function createEvent(CommandInterface $command): EventInterface
    {
        return new FormEditItemEvent($command);
    }

    /**
     * {@inheritdoc}
     *
     * @var Form $aggregate
     */
    public function validateCommand(CommandInterface $command, AggregateInterface $aggregate): bool
    {
        $payload = $command->getPayload();
        // The uuid to edit.
        $uuid = $payload['uuid'] ?? null;
        $item = \is_string($uuid) ? self::getItem($aggregate, $uuid) : false;

        if (null === $uuid) {
            $this->messageBus->dispatch(new Message(
                'No uuid to edit is set',
                CODE_BAD_REQUEST,
                $command->getUuid(),
                $command->getAggregateUuid()
            ));

            return false;
        }

        if (!isset($payload['data'])) {
            $this->messageBus->dispatch(new Message(
                'No data set',
                CODE_BAD_REQUEST,
                $command->getUuid(),
                $command->getAggregateUuid()
            ));

            return false;
        }

        if (!$item) {
            $this->messageBus->dispatch(new Message(
                'Item with this uuid was not found '.$uuid,
                CODE_CONFLICT,
                $command->getUuid(),
                $command->getAggregateUuid()
            ));

            return false;
        }

        return true;
    }
}


<?php

namespace Gobline\Filter\Validator;

/**
 * @author Mathieu Decaffmeyer <mdecaffmeyer@gmail.com>
 */
class Callback extends AbstractValidator
{
    private $value;
    private $callback;

    /**
     * @param callable $callback
     */
    public function __construct(callable $callback)
    {
        parent::__construct();

        $this->callback = $callback;
    }

    /**
     * {@inheritdoc}
     */
    public function isValid($value)
    {
        $this->resetMessages();

        $this->value = $value;

        if (!call_user_func($this->callback, $value)) {
            $this->addMessage();

            return false;
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    protected function getMessageTemplates()
    {
        return ['The input is not valid'];
    }

    /**
     * {@inheritdoc}
     */
    protected function getMessageVariables()
    {
        return [
            'value' => $this->value,
        ];
    }
}


<?php declare(strict_types=1);

namespace korchasa\matched;

trait AssertMatchedTrait
{
    /**
     * Reports an error identified by $message if $actual JSON string is not matched $pattern.
     *
     * If both the presence and the value are important, then place the value in the pattern
     * If only the presence of a field is important, then place the "***" in the pattern
     * If both the presence and the value are NOT important, then do not place anything in pattern
     *
     * @example assertJsonMatched(
     *          '{
     *              "foo": "***", // check only presence
     *              "bar": 42 // check presence and value
     *          }',
     *          '{
     *              "foo": {
     *                  "baz": 1
     *              },
     *              "bar": 42,
     *              "baz": "foo" // do not check at all
     *          }'
     * )
     *
     * @param string $pattern
     * @param string $actual
     * @param string $message
     */
    private static function assertJsonMatched(string $pattern, string $actual, string $message = ''): void
    {
        static::assertThat($actual, new JsonConstraint($pattern), $message);
    }

    /**
     * Reports an error identified by $message if $actual array is not matched $pattern.
     *
     * If both the presence and the value are important, then place the value in the pattern
     * If only the presence of a field is important, then place the "***" in the pattern
     * If both the presence and the value are NOT important, then do not place anything in pattern
     *
     * @example assertArrayMatched(
     *          [
     *              "foo" => "***", // check only presence
     *              "bar" => 42 // check presence and value
     *          ],
     *          [
     *              "foo" => [
     *                  "baz" => 1
     *              ],
     *              "bar" => 42,
     *              "baz" => "foo" // do not check at all
     *          ]
     * )
     *
     * @param iterable $pattern
     * @param iterable $actual
     * @param string $message
     */
    private static function assertArrayMatched(iterable $pattern, iterable $actual, string $message = ''): void
    {
        static::assertThat((array)$actual, new ArrayConstraint((array)$pattern), $message);
    }

    /**
     * Reports an error identified by $message if $actual JSON string is not matched $pattern.
     *
     * If both the presence and the value are important, then place the value in the pattern
     * If only the presence of a field is important, then place the "***" in the pattern
     * If both the presence and the value are NOT important, then do not place anything in pattern
     *
     * @example assertStringMatched('cu***mber', 'cucumber')
     *
     * @param string $pattern
     * @param string $actual
     * @param string $message
     */
    private static function assertStringMatched(string $pattern, string $actual, string $message = ''): void
    {
        static::assertThat($actual, new StringConstraint($pattern), $message);
    }
}


<?php

/*
 * Gobline Framework
 *
 * (c) Mathieu Decaffmeyer <mdecaffmeyer@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Gobline\Filter;

use Gobline\Filter\Validator\ValidatorInterface;
use Gobline\Filter\Sanitizer\SanitizerInterface;

/**
 * @author Mathieu Decaffmeyer <mdecaffmeyer@gmail.com>
 */
class FilterClassMap
{
    private $validators;
    private $sanitizers;

    public function __construct()
    {
        $this->validators = [
            'alpha' => '\\Gobline\\Filter\\Validator\\Alpha',
            'alphanum' => '\\Gobline\\Filter\\Validator\\Alphanum',
            'between' => '\\Gobline\\Filter\\Validator\\Between',
            'boolean' => '\\Gobline\\Filter\\Validator\\Boolean',
            'callback' => '\\Gobline\\Filter\\Validator\\Callback',
            'email' => '\\Gobline\\Filter\\Validator\\Email',
            'float' => '\\Gobline\\Filter\\Validator\\Float',
            'int' => '\\Gobline\\Filter\\Validator\\Int',
            'length' => '\\Gobline\\Filter\\Validator\\Length',
            'max' => '\\Gobline\\Filter\\Validator\\Max',
            'min' => '\\Gobline\\Filter\\Validator\\Min',
            'notags' => '\\Gobline\\Filter\\Validator\\NoTags',
            'regex' => '\\Gobline\\Filter\\Validator\\Regex',
            'required' => '\\Gobline\\Filter\\Validator\\Required',
            'uuid' => '\\Gobline\\Filter\\Validator\\Uuid',
            'value' => '\\Gobline\\Filter\\Validator\\Value',
        ];

        $this->sanitizers = [
            'cast' => '\\Gobline\\Filter\\Sanitizer\\Cast',
            'lower' => '\\Gobline\\Filter\\Sanitizer\\Lower',
            'ltrim' => '\\Gobline\\Filter\\Sanitizer\\LTrim',
            'rtrim' => '\\Gobline\\Filter\\Sanitizer\\RTrim',
            'striptags' => '\\Gobline\\Filter\\Sanitizer\\StripTags',
            'trim' => '\\Gobline\\Filter\\Sanitizer\\Trim',
            'upper' => '\\Gobline\\Filter\\Sanitizer\\Upper',
        ];
    }

    /**
     * @param string $name
     * @param string $validator
     *
     * @throws \InvalidArgumentException
     */
    public function addValidator($name, $validator)
    {
        $name = (string) $name;
        if ($name === '') {
            throw new \InvalidArgumentException('$name cannot be empty');
        }

        $validator = (string) $validator;
        if ($validator === '') {
            throw new \InvalidArgumentException('$validator cannot be empty');
        }

        $this->validators[$name] = $validator;
    }

    /**
     * @param string $name
     *
     * @throws \InvalidArgumentException
     *
     * @return ValidatorInterface
     */
    public function getValidator($name)
    {
        if (!$this->hasValidator($name)) {
            throw new \InvalidArgumentException('validator "'.$name.'" not found');
        }

        return $this->validators[$name];
    }

    /**
     * @param string $name
     *
     * @throws \InvalidArgumentException
     *
     * @return bool
     */
    public function hasValidator($name)
    {
        if ((string) $name === '') {
            throw new \InvalidArgumentException('$name cannot be empty');
        }

        return isset($this->validators[$name]);
    }

    /**
     * @param string $name
     * @param string $validator
     *
     * @throws \InvalidArgumentException
     */
    public function addSanitizer($name, $sanitizer)
    {
        $name = (string) $name;
        if ($name === '') {
            throw new \InvalidArgumentException('$name cannot be empty');
        }

        $sanitizer = (string) $sanitizer;
        if ($sanitizer === '') {
            throw new \InvalidArgumentException('$sanitizer cannot be empty');
        }

        $this->sanitizers[$name] = $sanitizer;
    }

    /**
     * @param string $name
     *
     * @throws \InvalidArgumentException
     *
     * @return SanitizerInterface
     */
    public function getSanitizer($name)
    {
        if (!$this->hasSanitizer($name)) {
            throw new \InvalidArgumentException('sanitizer "'.$name.'" not found');
        }

        return $this->sanitizers[$name];
    }

    /**
     * @param string $name
     *
     * @throws \InvalidArgumentException
     *
     * @return bool
     */
    public function hasSanitizer($name)
    {
        if ((string) $name === '') {
            throw new \InvalidArgumentException('$name cannot be empty');
        }

        return isset($this->sanitizers[$name]);
    }
}


<?php

namespace Gobline\Filter\Validator;

/**
 * @author Mathieu Decaffmeyer <mdecaffmeyer@gmail.com>
 */
class Max extends AbstractValidator
{
    private $value;
    private $max;

    /**
     * @param int|float $max
     */
    public function __construct($max)
    {
        parent::__construct();

        $this->max = (float) $max;
    }

    /**
     * {@inheritdoc}
     */
    public function isValid($value)
    {
        $this->resetMessages();

        $this->value = $value;

        if (!is_scalar($value) || is_bool($value)) {
            throw new \InvalidArgumentException('Unexpected type: '.gettype($value));
        }
        if (!is_numeric($value)) {
            throw new \InvalidArgumentException('Unexpected value: '.$value);
        }

        if ($value > $this->max) {
            $this->addMessage();

            return false;
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    protected function getMessageTemplates()
    {
        return ['The input is not less than %max%'];
    }

    /**
     * {@inheritdoc}
     */
    protected function getMessageVariables()
    {
        return [
            'value' => $this->value,
            'max' => $this->max,
        ];
    }
}


<?php

namespace Gobline\Filter\Validator;

/**
 * @author Mathieu Decaffmeyer <mdecaffmeyer@gmail.com>
 */
class Between extends AbstractValidator
{
    private $value;
    private $min;
    private $max;
    private $isStringComparison = false;

    /**
     * @param int|float|string $min
     * @param int|float|string $max
     */
    public function __construct($min, $max)
    {
        parent::__construct();

        $this->min = $min;
        $this->max = $max;

        if (
            is_string($min) && !is_numeric($min) &&
            is_string($max) && !is_numeric($max)
        ) {
            $this->isStringComparison = true;
        } else {
            $this->min = (float) $this->min;
            $this->max = (float) $this->max;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function isValid($value)
    {
        $this->resetMessages();

        $this->value = $value;

        if ($this->isStringComparison) {
            if (strcmp($this->value, $this->min) < 0) {
                $this->addMessage();

                return false;
            }
            if (strcmp($this->value, $this->max) > 0) {
                $this->addMessage();

                return false;
            }

            return true;
        }

        if (!is_scalar($value) || is_bool($value)) {
            throw new \InvalidArgumentException('Unexpected type: '.gettype($value));
        }
        if (!is_numeric($value)) {
            throw new \InvalidArgumentException('Unexpected value: '.$value);
        }

        if ($value < $this->min || $value > $this->max) {
            $this->addMessage();

            return false;
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    protected function getMessageTemplates()
    {
        if ($this->isStringComparison) {
            return ['The input must be between "%min%" and "%max%"'];
        } else {
            return ['The input must be between %min% and %max%'];
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function getMessageVariables()
    {
        return [
            'value' => $this->value,
            'min' => $this->min,
            'max' => $this->max,
        ];
    }
}


<?php

namespace Gobline\Filter\Validator;

/**
 * @author Mathieu Decaffmeyer <mdecaffmeyer@gmail.com>
 */
class NoTags extends AbstractValidator
{
    private $value;
    private $allowedTags;

    /**
     * @param string $allowedTags
     */
    public function __construct($allowedTags = '')
    {
        parent::__construct();

        $this->allowedTags = (string) $allowedTags;
    }

    /**
     * {@inheritdoc}
     */
    public function isValid($value)
    {
        $this->resetMessages();

        $this->value = $value;

        if (!is_string($value)) {
            return true;
        }

        if (strlen($value) !== strlen(strip_tags($value, $this->allowedTags))) {
            $this->addMessage();

            return false;
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    protected function getMessageTemplates()
    {
        return ['The input is not valid'];
    }

    /**
     * {@inheritdoc}
     */
    protected function getMessageVariables()
    {
        return ['value' => $this->value];
    }
}


<?php

declare(strict_types=1);

namespace RevisionTen\Forms\Controller;

use RevisionTen\CQRS\Services\AggregateFactory;
use RevisionTen\CQRS\Services\CommandBus;
use RevisionTen\CQRS\Services\MessageBus;
use RevisionTen\Forms\Command\FormAddItemCommand;
use RevisionTen\Forms\Command\FormCloneCommand;
use RevisionTen\Forms\Command\FormCreateCommand;
use RevisionTen\Forms\Command\FormDeleteCommand;
use RevisionTen\Forms\Command\FormEditCommand;
use RevisionTen\Forms\Command\FormEditItemCommand;
use RevisionTen\Forms\Command\FormRemoveItemCommand;
use RevisionTen\Forms\Command\FormShiftItemCommand;
use RevisionTen\Forms\Form\FormType;
use RevisionTen\Forms\Form\ItemType;
use RevisionTen\Forms\Handler\FormBaseHandler;
use RevisionTen\Forms\Interfaces\ItemInterface;
use RevisionTen\Forms\Model\Form;
use RevisionTen\Forms\Model\FormRead;
use RevisionTen\Forms\Services\FormService;
use Doctrine\ORM\EntityManagerInterface;
use Ramsey\Uuid\Uuid;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Translation\TranslatorInterface;

/**
 * Class FormController.
 *
 * @Route("/admin/forms")
 */
class FormController extends AbstractController
{
    /** @var MessageBus */
    private $messageBus;

    /** @var CommandBus */
    private $commandBus;

    /** @var AggregateFactory */
    private $aggregateFactory;

    /** @var EntityManagerInterface */
    private $entityManager;

    /** @var FormService */
    private $formService;

    /** @var TranslatorInterface */
    private $translator;

    public function __construct(
        MessageBus $messageBus,
        CommandBus $commandBus,
        AggregateFactory $aggregateFactory,
        EntityManagerInterface $entityManager,
        FormService $formService,
        TranslatorInterface $translator
    )
    {
        $this->messageBus = $messageBus;
        $this->commandBus = $commandBus;
        $this->aggregateFactory = $aggregateFactory;
        $this->entityManager = $entityManager;
        $this->formService = $formService;
        $this->translator = $translator;
    }

    /**
     * TODO: Put this in a cqrs helper bundle.
     * Returns the difference between base array and change array.
     * Works with multidimensional arrays.
     *
     * @param array $base
     * @param array $change
     *
     * @return array
     */
    private function diff(array $base, array $change): array
    {
        $diff = [];

        foreach ($change as $property => $value) {
            $equal = true;

            if (!array_key_exists($property, $base)) {
                // Property is new.
                $equal = false;
            } else {
                $originalValue = $base[$property];

                if (\is_array($value) && \is_array($originalValue)) {
                    // Check if values arrays are identical.
                    if (0 !== strcmp(json_encode($value), json_encode($originalValue))) {
                        // Arrays are not equal.
                        $equal = false;
                    }
                } elseif ($originalValue !== $value) {
                    $equal = false;
                }
            }

            if (!$equal) {
                $diff[$property] = $value;
            }
        }

        return $diff;
    }

    /**
     * Returns info from the messageBus.
     *
     * @param string|NULL $formUuid
     *
     * @return JsonResponse|RedirectResponse|Response
     */
    public function errorResponse(string $formUuid = null)
    {
        $messages = $this->messageBus->getMessagesJson();

        if ($formUuid) {
            foreach ($messages as $message) {
                $this->addFlash(
                    'warning',
                    $message['message']
                );
            }

            return $this->redirectToForm($formUuid);
        }

        return new JsonResponse($messages);
    }

    /**
     * Redirects to the edit page of a Form Aggregate by its uuid.
     *
     * @param string $formUuid
     *
     * @return Response
     */
    private function redirectToForm(string $formUuid): Response
    {
        /** @var FormRead|null $formRead */
        $formRead = $this->entityManager->getRepository(FormRead::class)->findOneBy([
            'uuid' => $formUuid,
        ]);

        if ($formRead) {
            return $this->redirectToRoute('forms_edit_aggregate', [
                'id' => $formRead->getId(),
            ]);
        }

        return $this->redirect('/admin');
    }

    /**
     * Displays the Form Aggregate create form.
     *
     * @Route("/create-form", name="forms_create_form")
     *
     * @param Request $request
     *
     * @return JsonResponse|RedirectResponse|Response
     * @throws \Exception
     */
    public function createFormAggregate(Request $request)
    {
        /** @var \Symfony\Component\Security\Core\User\UserInterface $user */
        $user = $this->getUser();

        $form = $this->createForm(FormType::class);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $data = $form->getData();
            $aggregateUuid = Uuid::uuid1()->toString();

            // Execute Command.
            $success = false;
            $this->commandBus->dispatch(new FormCreateCommand($user->getId(), Uuid::uuid1()->toString(), $aggregateUuid, 0, $data, function ($commandBus, $event) use (&$success) {
                // Callback.
                $success = true;
            }));

            if ($success) {
                $this->addFlash(
                    'success',
                    'Form created'
                );

                return $this->redirectToForm($aggregateUuid);
            }

            return $this->errorResponse($aggregateUuid);
        }

        return $this->render('@forms/Form/form.html.twig', [
            'title' => 'Add Form',
            'form' => $form->createView(),
        ]);
    }

    /**
     * @Route("/delete-aggregate", name="forms_delete_aggregate")
     *
     * @param Request $request
     *
     * @return Response
     * @throws \Exception
     */
    public function deleteAggregateAction(Request $request): Response
    {
        /** @var \Symfony\Component\Security\Core\User\UserInterface $user */
        $user = $this->getUser();
        /** @var int $id FormRead Id. */
        $id = $request->get('id');
        /** @var FormRead $formRead */
        $formRead = $this->entityManager->getRepository(FormRead::class)->find($id);

        if (null === $formRead) {
            return $this->redirect('/admin');
        }

        $formUuid = $formRead->getUuid();
        /** @var Form $formAggregate */
        $formAggregate = $this->aggregateFactory->build($formUuid, Form::class, null, $user->getId());

        // Execute Command.
        $success = false;
        $this->commandBus->dispatch(new FormDeleteCommand($user->getId(), Uuid::uuid1()->toString(), $formAggregate->getUuid(), $formAggregate->getStreamVersion(), [], function ($commandBus, $event) use (&$success) {
            // Callback.
            $success = true;
        }));

        return $success ? $this->redirect('/admin/?entity=FormRead&action=list') : $this->errorResponse($formAggregate->getUuid());
    }

    /**
     * @Route("/edit-aggregate", name="forms_edit_aggregate")
     *
     * @param Request $request
     * @param ContainerInterface $container
     *
     * @return Response
     * @throws \Exception
     */
    public function editAggregateAction(Request $request, ContainerInterface $container): Response
    {
        $config = $container->getParameter('forms');

        /** @var \Symfony\Component\Security\Core\User\UserInterface $user */
        $user = $this->getUser();
        /** @var int $id FormRead Id. */
        $id = $request->get('id');
        /** @var FormRead $formRead */
        $formRead = $this->entityManager->getRepository(FormRead::class)->find($id);

        if (null === $formRead) {
            return $this->redirect('/admin');
        }

        $formUuid = $formRead->getUuid();
        /** @var Form $formAggregate */
        $formAggregate = $this->aggregateFactory->build($formUuid, Form::class, null, $user->getId());
        // Convert Aggregate to data array for form and remove properties we don't want changed.
        $aggregateData = json_decode(json_encode($formAggregate), true);

        // Get item variables.
        $itemVariables = [];
        if (isset($aggregateData['items']) && \is_array($aggregateData['items']) && \count($aggregateData['items']) > 0) {
            foreach ($aggregateData['items'] as $item) {
                /** @var ItemInterface $itemType */
                $itemType = $this->formService->getItemClass($item['itemName']);
                $itemVariables[] = $itemType::getVariables($item['data']);
            }
        }

        // Create Edit Form.
        unset($aggregateData['uuid'], $aggregateData['items']);
        $form = $this->createForm(FormType::class, $aggregateData);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $data = $form->getData();
            // Remove data that hasn't changed.
            $data = $this->diff($aggregateData, $data);

            // Execute Command.
            $success = false;
            $this->commandBus->dispatch(new FormEditCommand($user->getId(), Uuid::uuid1()->toString(), $formAggregate->getUuid(), $formAggregate->getStreamVersion(), $data, function ($commandBus, $event) use (&$success) {
                // Callback.
                $success = true;
            }));

            if ($success) {
                $this->addFlash(
                    'success',
                    'Form edited'
                );

                return $this->redirectToForm($formUuid);
            }

            return $this->errorResponse($formAggregate->getUuid());
        }

        if (class_exists('\Symfony\Component\HttpKernel\Kernel') && \defined('\Symfony\Component\HttpKernel\Kernel::MAJOR_VERSION')) {
            $symfony4 = 4 === Kernel::MAJOR_VERSION;
        } else {
            $symfony4 = true;
        }

        return $this->render('@forms/Admin/edit-aggregate.html.twig', [
            'form' => $form->createView(),
            'formRead' => $formRead,
            'formAggregate' => $formAggregate,
            'user' => $user,
            'config' => $config,
            'itemVariables' => $itemVariables,
            'symfony4' => $symfony4,
        ]);
    }

    /**
     * @param string     $itemName
     * @param array|null $data
     * @param array|null $items
     *
     * @return FormInterface
     */
    private function getItemForm(string $itemName, array $data = null, array $items = null): FormInterface
    {
        $data = $data ?? ['data' => []];
        $formClass = $this->formService->getItemClass($itemName);

        return $this->createForm(ItemType::class, $data, [
            'formClass' => $formClass,
            'items' => $items,
        ]);
    }

    /**
     * @Route("/add-item/{formUuid}/{onVersion}/{itemName}/{parent}", name="forms_add_item")
     *
     * @param Request     $request
     * @param string      $formUuid
     * @param int         $onVersion
     * @param string|null $parent
     * @param string      $itemName
     *
     * @throws \Exception
     * @return Response
     */
    public function formAddItem(Request $request, string $formUuid, int $onVersion, string $parent = null, string $itemName): Response
    {
        /** @var \Symfony\Component\Security\Core\User\UserInterface $user */
        $user = $this->getUser();

        /** @var Form $aggregate */
        $aggregate = $this->aggregateFactory->build($formUuid, Form::class, $onVersion, $user->getId());

        $form = $this->getItemForm($itemName, null, $aggregate->items);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $data = $form->getData()['data'];

            // Execute Command.
            $success = false;
            $this->commandBus->dispatch(new FormAddItemCommand($user->getId(), Uuid::uuid1()->toString(), $formUuid, $onVersion, [
                'itemName' => $itemName,
                'data' => $data,
                'parent' => $parent,
            ], function ($commandBus, $event) use (&$success) {
                // Callback.
                $success = true;
            }));

            if ($success) {
                $this->addFlash(
                    'success',
                    'Field added'
                );

                return $this->redirectToForm($formUuid);
            }

            return $this->errorResponse($formUuid);
        }

        $itemNameTranslated = $this->translator->trans($itemName);
        $title = $this->translator->trans('Add %itemName% field', ['%itemName%' => $itemNameTranslated]);

        return $this->render('@forms/Form/form.html.twig', [
            'title' => $title,
            'form' => $form->createView(),
        ]);
    }

    /**
     * @Route("/edit-item/{formUuid}/{onVersion}/{itemUuid}", name="forms_edit_item")
     *
     * @param Request $request
     * @param string  $formUuid
     * @param int     $onVersion
     * @param string  $itemUuid
     *
     * @throws \Exception
     * @return Response
     */
    public function formEditItem(Request $request, string $formUuid, int $onVersion, string $itemUuid): Response
    {
        /** @var \Symfony\Component\Security\Core\User\UserInterface $user */
        $user = $this->getUser();

        /** @var Form $aggregate */
        $aggregate = $this->aggregateFactory->build($formUuid, Form::class, $onVersion, $user->getId());

        if (empty($aggregate->items)) {
            // Aggregate does not exist, or is empty.
            return $this->redirectToForm($formUuid);
        }

        // Get the item from the aggregate.
        $item = FormBaseHandler::getItem($aggregate, $itemUuid);

        // Get the form title.
        $title = $this->translator->trans('Edit %itemName% field', [
            '%itemName%' => $this->translator->trans($item['itemName'] ?? 'Form'),
        ]);

        if ($item && isset($item['data'], $item['itemName'])) {
            $form = $this->getItemForm($item['itemName'], $item, $aggregate->items);
            $form->handleRequest($request);

            if ($form->isSubmitted()) {
                // Get differences in data and check if data has changed.
                $data = $form->getData()['data'];
                // Remove data that hasn't changed.
                $data = $this->diff($item['data'], $data);
                if (empty($data)) {
                    $form->addError(new FormError($this->translator->trans('Data has not changed.')));
                }

                if ($form->isValid()) {
                    // Execute Command.
                    $success = false;
                    $this->commandBus->dispatch(new FormEditItemCommand($user->getId(), Uuid::uuid1()->toString(), $formUuid, $onVersion, [
                        'uuid' => $itemUuid,
                        'data' => $data,
                    ], function ($commandBus, $event) use (&$success) {
                        // Callback.
                        $success = true;
                    }));

                    if ($success) {
                        $this->addFlash(
                            'success',
                            $this->translator->trans('Field edited')
                        );

                        return $this->redirectToForm($formUuid);
                    }

                    return $this->errorResponse($formUuid);
                }
            }
        } else {
            return $this->errorResponse($formUuid);
        }

        return $this->render('@forms/Form/form.html.twig', [
            'title' => $title,
            'form' => $form->createView(),
        ]);
    }

    /**
     * Removes an item from a Form Aggregate.
     *
     * @Route("/remove-item/{formUuid}/{onVersion}/{itemUuid}", name="forms_remove_item")
     *
     * @param string $formUuid
     * @param int    $onVersion
     * @param string $itemUuid
     *
     * @throws \Exception
     * @return JsonResponse|Response
     */
    public function formRemoveItem(string $formUuid, int $onVersion, string $itemUuid)
    {
        /** @var \Symfony\Component\Security\Core\User\UserInterface $user */
        $user = $this->getUser();

        $success = false;
        $this->commandBus->dispatch(new FormRemoveItemCommand($user->getId(), Uuid::uuid1()->toString(), $formUuid, $onVersion, [
            'uuid' => $itemUuid,
        ], function ($commandBus, $event) use (&$success) {
            // Callback.
            $success = true;
        }));

        if ($success) {
            $this->addFlash(
                'success',
                $this->translator->trans('Field deleted')
            );

            return $this->redirectToForm($formUuid);
        }

        return $this->errorResponse($formUuid);
    }

    /**
     * Shift a item up or down on a Form Aggregate.
     *
     * @Route("/shift-item/{formUuid}/{onVersion}/{itemUuid}/{direction}", name="forms_shift_item")
     *
     * @param string $formUuid
     * @param int    $onVersion
     * @param string $itemUuid
     * @param string $direction
     *
     * @throws \Exception
     * @return JsonResponse|RedirectResponse|Response
     */
    public function formShiftItem(string $formUuid, int $onVersion, string $itemUuid, string $direction)
    {
        /** @var \Symfony\Component\Security\Core\User\UserInterface $user */
        $user = $this->getUser();

        $success = false;
        $this->commandBus->dispatch(new FormShiftItemCommand($user->getId(), Uuid::uuid1()->toString(), $formUuid, $onVersion, [
            'uuid' => $itemUuid,
            'direction' => $direction,
        ], function ($commandBus, $event) use (&$success) {
            // Callback.
            $success = true;
        }));

        if ($success) {
            $this->addFlash(
                'success',
                $this->translator->trans('Field shifted')
            );

            return $this->redirectToForm($formUuid);
        }

        return $this->errorResponse($formUuid);
    }

    /**
     * @param RequestStack           $requestStack
     * @param string                 $formUuid
     * @param string|null            $template
     * @param array                  $defaultData
     *
     * @return Response
     */
    public function renderFormAction(RequestStack $requestStack, string $formUuid, string $template = null, array $defaultData): Response
    {
        $request = $requestStack->getMasterRequest();
        $handledRequest = $this->formService->handleRequest($request, $formUuid, $defaultData);

        // Get the forms template.
        $baseTemplate = $handledRequest['template'] ?? '@forms/Frontend/form.html.twig';
        $template = $template ?: $baseTemplate;

        foreach ($handledRequest['messages'] as $message) {
            $this->addFlash(
                $message['type'],
                $message['message']
            );
        }

        return $this->render($template, [
            'form' => $handledRequest['formView'],
            'request' => $request,
            'ignore_validation' => $handledRequest['ignore_validation'],
        ]);
    }

    /**
     * @Route("/clone-aggregate", name="forms_clone_aggregate")
     *
     * @param Request $request
     *
     * @throws \Exception
     * @return Response
     */
    public function cloneAggregateAction(Request $request): Response
    {
        /** @var \Symfony\Component\Security\Core\User\UserInterface $user */
        $user = $this->getUser();
        /** @var int $id FormRead Id. */
        $id = $request->get('id');
        /** @var FormRead $formRead */
        $formRead = $this->entityManager->getRepository(FormRead::class)->find($id);

        if (null === $formRead) {
            return $this->redirect('/admin');
        }

        $originalUuid = $formRead->getUuid();
        $originalVersion = $formRead->getVersion();

        $data = [
            'originalUuid' => $originalUuid,
            'originalVersion' => $originalVersion,
        ];
        $aggregateUuid = Uuid::uuid1()->toString();

        // Execute Command.
        $success = false;
        $this->commandBus->dispatch(new FormCloneCommand($user->getId(), Uuid::uuid1()->toString(), $aggregateUuid, 0, $data, function ($commandBus, $event) use (&$success) {
            // Callback.
            $success = true;
        }));

        if ($success) {
            $this->addFlash(
                'success',
                $this->translator->trans('Form duplicated')
            );

            return $this->redirectToForm($aggregateUuid);
        }

        return $this->errorResponse($aggregateUuid);
    }

    /**
     * @Route("/submissions", name="forms_submissions")
     *
     * @param SerializerInterface $serializer
     * @param Request             $request
     *
     * @return Response
     */
    public function submissions(SerializerInterface $serializer, Request $request): Response
    {
        /** @var int $id FormRead Id. */
        $id = (int) $request->get('id');

        $submissions = $this->formService->getFormSubmissions($id);

        $submissionsCsv = $serializer->encode($submissions, 'csv');

        $response = new Response($submissionsCsv);

        $response->headers->set('Content-Type', 'text/csv');
        $response->headers->set('Content-Disposition', 'attachment; filename="submissions.csv"');

        return $response;
    }
}


<?php
/**
 * MIT License <https://opensource.org/licenses/mit>
 *
 * Copyright (c) 2015 Kerem Güneş
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
declare(strict_types=1);

namespace froq\util\traits;

/**
 * Property trait.
 * @package froq\util\traits
 * @object  froq\util\traits\PropertyTrait
 * @author  Kerem Güneş <k-gun@mail.com>
 * @since   3.0
 */
trait PropertyTrait
{
    /**
     * Strict.
     * @var bool
     */
    private static $___strict;

    /**
     * Properties.
     * @var array
     */
    private static $___properties = [];

    /**
     * Properties type.
     * @var array
     */
    private static $___propertiesType = [
        'int'   => 'integer',
        'float' => 'double',
        'bool'  => 'boolean'
    ];

    /**
     * Strict.
     * @param  bool $strict
     * @return void
     */
    public static function ___strict(bool $strict): void
    {
        self::$___strict = $strict;
    }

    /**
     * Check property entry.
     * @param  string $name
     * @return void
     * @throws froq\util\traits\PropertyTraitException
     */
    private function ___checkPropertyEntry(string $name): void
    {
        if (!isset(self::$___properties[$name])) {
            try {
                // get type from docs, and strict status from self $___strict or class file
                $ref = new \ReflectionProperty($this, $name);
                $refDoc = $ref->getDocComment() ?: '';
                $refFile = $ref->getDeclaringClass()->getFileName();

                $type = null;
                $strict = self::$___strict !== null ? self::$___strict : null;
                $nullable = $this->{$name} === null;

                // get type
                if (strpos($refDoc, '@var')) {
                    $type = preg_replace('~.+@(?:var)\s+(\w+).+~is', '\1', $refDoc);
                    if (isset(self::$___propertiesType[$type])) {
                        $type = self::$___propertiesType[$type];
                    }
                }

                // get strict
                if ($strict === null) {
                    foreach (new \SplFileObject($refFile) as $line) {
                        $line = trim($line);
                        // skip comments
                        if (strpbrk($line, '/*#') !== false) {
                            continue;
                        }
                        // expects right declare() notation, sorry..
                        if (strpos($line, 'declare(strict_types=1)') !== false) {
                            $strict = true;
                            break;
                        }
                    }
                }

                // add entry
                self::$___properties[$name] = ['type' => $type, 'strict' => (bool) $strict,
                    'nullable' => $nullable];
            } catch (\ReflectionException $e) {
                throw new PropertyTraitException($e->getMessage(), $e->getCode());
            }
        }
    }
}


<?php

namespace Gobline\Filter\Validator;

/**
 * @author Mathieu Decaffmeyer <mdecaffmeyer@gmail.com>
 */
class Scalar extends AbstractValidator
{
    private $value;

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * {@inheritdoc}
     */
    public function isValid($value)
    {
        $this->resetMessages();

        $this->value = $value;

        if (!is_scalar($value)) {
            $this->addMessage();

            return false;
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    protected function getMessageTemplates()
    {
        return ['The input is not valid'];
    }

    /**
     * {@inheritdoc}
     */
    protected function getMessageVariables()
    {
        return ['value' => $this->value];
    }
}


<?php

namespace Gobline\Filter\Sanitizer;

/**
 * @author Mathieu Decaffmeyer <mdecaffmeyer@gmail.com>
 */
class Trim implements SanitizerInterface
{
    private $chars;

    /**
     * @param string $chars
     */
    public function __construct($chars = ' ')
    {
        $this->chars = (string) $chars;
    }

    /**
     * {@inheritdoc}
     */
    public function sanitize($value)
    {
        if (!is_scalar($value)) {
            throw new \InvalidArgumentException('Unexpected type: '.gettype($value));
        }

        return trim($value, $this->chars);
    }
}


<?php

/*
 * Gobline Framework
 *
 * (c) Mathieu Decaffmeyer <mdecaffmeyer@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Gobline\Filter;

use Gobline\Filter\Validator\ValidatorInterface;
use Gobline\Filter\Sanitizer\SanitizerInterface;
use Gobline\Translator\Translator;

/**
 * Allows to filter a variable through multiple sanitizers and validators at once.
 *
 * @author Mathieu Decaffmeyer <mdecaffmeyer@gmail.com>
 */
class FilterFunnel
{
    private $chain = [];
    private $filterClassMap;
    private $messages = [];
    private $optional = false;

    public function __construct(FilterClassMap $filterClassMap = null)
    {
        $this->filterClassMap = $filterClassMap ?: new FilterClassMap();
    }

    /**
     * @param ValidatorInterface|string $validator
     * @param mixed                     $params
     *
     * @throws \InvalidArgumentException
     *
     * @return FilterFunnel
     */
    public function addValidator($validator, ...$params)
    {
        if (!$validator instanceof ValidatorInterface) {
            $validator = $this->filterClassMap->getValidator($validator);
            $validator = new $validator(...$params);
        }
        $this->chain[] = $validator;

        return $this;
    }

    /**
     * @param SanitizerInterface|string $sanitizer
     * @param mixed                     $params
     *
     * @throws \InvalidArgumentException
     *
     * @return FilterFunnel
     */
    public function addSanitizer($sanitizer, ...$params)
    {
        if (!$sanitizer instanceof SanitizerInterface) {
            $sanitizer = $this->filterClassMap->getSanitizer($sanitizer);
            $sanitizer = new $sanitizer(...$params);
        }
        $this->chain[] = $sanitizer;

        return $this;
    }

    public function add($filter, ...$params)
    {
        if ($this->filterClassMap->hasValidator($filter)) {
            return $this->addValidator($filter, ...$params);
        } elseif ($this->filterClassMap->hasSanitizer($filter)) {
            return $this->addSanitizer($filter, ...$params);
        }

        throw new \RuntimeException('filter "'.$filter.'" not found');
    }

    public function __call($name, array $params)
    {
        return $this->add($name, ...$params);
    }

    public function __get($name)
    {
        return $this->add($name);
    }

    public function setOptional()
    {
        $this->optional = true;

        return $this;
    }

    /**
     * @param mixed $value
     *
     * @throws \RuntimeException
     *
     * @return mixed
     */
    public function filter($value, $filters = null)
    {
        $this->messages = [];

        if ($this->optional && ($value === null || (is_string($value) && trim($value) === ''))) {
            return null;
        }

        foreach ($this->chain as $filter) {
            if ($filter instanceof ValidatorInterface) {
                if ($filter->isValid($value)) {
                    continue;
                }
                $this->messages = $filter->getMessages();

                return null;
            } elseif ($filter instanceof SanitizerInterface) {
                $value = $filter->sanitize($value);
            }
        }

        if ($filters) {
            $filters = explode('|', (string) $filters);

            if ($filters && current($filters) === 'optional') {
                array_shift($filters);
                if ($value === null || (is_string($value) && trim($value) === '')) {
                    return null;
                }
            }

            foreach ($filters as $filter) {
                $filter = trim($filter);
                $name = $filter;
                $params = [];

                if (strpos($filter,'(') !== false) {
                    $name = trim(substr($filter, 0, strpos($filter,'(')));
                    $params = substr($filter, strpos($filter,'(') + 1, -1);
                    $params = explode(',', $params);
                }

                if ($this->filterClassMap->hasValidator($name)) {
                    $validator = $this->filterClassMap->getValidator($name);
                    $validator = new $validator(...$params);
                    if ($validator->isValid($value)) {
                        continue;
                    }
                    $this->messages = $validator->getMessages();

                    return null;
                } elseif ($this->filterClassMap->hasSanitizer($name)) {
                    $sanitizer = $this->filterClassMap->getSanitizer($name);
                    $sanitizer = new $sanitizer(...$params);
                    $value = $sanitizer->sanitize($value);
                } else {
                    throw new \RuntimeException('filter "'.$name.'" not found');
                }
            }
        }

        return $value;
    }

    /**
     * @param string $message
     * @param mixed  $key
     *
     * @throws \RuntimeException
     *
     * @return FilterFunnel
     */
    public function setMessageTemplate($message, $key = null)
    {
        $validator = end($this->chain);
        if (!$validator instanceof ValidatorInterface) {
            throw new \RuntimeException('setMessageTemplate() must be preceeded by addValidator()');
        }
        $validator->setMessageTemplate($message, $key);

        return $this;
    }

    /**
     * @return bool
     */
    public function hasMessages()
    {
        return (bool) $this->messages;
    }

    /**
     * @return string[]
     */
    public function getMessages()
    {
        return $this->messages;
    }

    /**
     * @return string
     */
    public function getMessage()
    {
        if (!$this->messages) {
            return null;
        }

        return current($this->messages);
    }

    /**
     * @return FilterClassMap
     */
    public function getFilterClassMap()
    {
        return $this->filterClassMap;
    }

    public function setDefaultTranslator(Translator $translator)
    {
        AbstractValidator::setDefaultTranslator($translator);
    }
}


<?php

declare(strict_types=1);

namespace RevisionTen\Forms\Handler;

use RevisionTen\Forms\Command\FormDeleteCommand;
use RevisionTen\Forms\Event\FormDeleteEvent;
use RevisionTen\Forms\Model\Form;
use RevisionTen\CQRS\Interfaces\AggregateInterface;
use RevisionTen\CQRS\Interfaces\CommandInterface;
use RevisionTen\CQRS\Interfaces\EventInterface;
use RevisionTen\CQRS\Interfaces\HandlerInterface;

final class FormDeleteHandler extends FormBaseHandler implements HandlerInterface
{
    /**
     * {@inheritdoc}
     *
     * @var Form $aggregate
     */
    public function execute(CommandInterface $command, AggregateInterface $aggregate): AggregateInterface
    {
        // Change Aggregate state.
        $aggregate->deleted = true;

        return $aggregate;
    }

    /**
     * {@inheritdoc}
     */
    public static function getCommandClass(): string
    {
        return FormDeleteCommand::class;
    }

    /**
     * {@inheritdoc}
     */
    public function createEvent(CommandInterface $command): EventInterface
    {
        return new FormDeleteEvent($command);
    }

    /**
     * {@inheritdoc}
     */
    public function validateCommand(CommandInterface $command, AggregateInterface $aggregate): bool
    {
        return true;
    }
}


<?php
/**
 * Created by PhpStorm.
 * User: Olivier
 * Date: 9/05/17
 * Time: 08:46
 */

namespace Dywee\OrderBundle\Service;

use Dywee\OrderBundle\Entity\BaseOrderInterface;
use Dywee\OrderBundle\Entity\OrderElement;
use Dywee\ProductBundle\Model\VirtualProductInterface;

class OrderVirtualizationManager
{
    /**
     * @param BaseOrderInterface $order
     *
     * @return bool
     */
    public function containsVirtualProduct(BaseOrderInterface $order)
    {
        return $this->calculVirtualization($order, false);
    }

    /**
     * @param BaseOrderInterface $order
     *
     * @return bool
     */
    public function isFullyVirtual(BaseOrderInterface $order)
    {
        return $this->calculVirtualization($order);
    }

    /**
     * @param BaseOrderInterface $order
     * @param bool               $exclusive
     *
     * @return bool
     */
    public function calculVirtualization(BaseOrderInterface $order, $exclusive = true)
    {
        $isVirtual = count($order->getOrderElements()) > 0;
        $containsVirtual = false;

        /** @var OrderElement $element */
        foreach ($order->getOrderElements() as $element) {
            if ($element->getProduct() instanceof VirtualProductInterface) {
                $containsVirtual = true;
            } else {
                $isVirtual = false;
            }
        }

        return $exclusive ? $isVirtual : $containsVirtual;
    }
}


<?php

declare(strict_types=1);

namespace RevisionTen\Forms\Form\Items;

use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\NotBlank;

class HiddenItem extends Item
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        parent::buildForm($builder, $options);

        $builder->add('isSubject', CheckboxType::class, [
            'label' => 'Is Subject',
            'required' => false,
        ]);

        $builder->add('isReceiver', CheckboxType::class, [
            'label' => 'Is Receiver',
            'required' => false,
        ]);

        $builder->remove('hideLabel');
        $builder->remove('popover');
    }

    /**
     * {@inheritdoc}
     */
    public static function getItem(FormBuilderInterface $builder, array $item)
    {
        $attributes = [];

        if (isset($item['twig_variable']) && $item['twig_variable']) {
            $attributes['twig_variable'] = $item['twig_variable'];
        }

        $options = [
            'label' => false,
            'required' => $item['required'] ?? false,
            'attr' => $attributes,
            'constraints' => [],
        ];

        if (isset($item['required']) && $item['required']) {
            $options['constraints'][] = new NotBlank();
        }

        if (isset($item['read_only']) && $item['read_only']) {
            // Do not add the element add all. Default data remains unchanged.
        } else {
            $builder->add($item['name'], HiddenType::class, $options);
        }
    }
}


<?php

namespace Railken\Amethyst\Database\Seeds;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Config;
use Railken\Amethyst\Managers\TaxonomyManager;

class TaxonomySeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run()
    {
        $taxonomyManager = new TaxonomyManager();

        foreach (Config::get('amethyst.taxonomy.data.taxonomy.seeds') as $seed) {
            $taxonomyManager->createOrFail($seed);
        }

        return 1;
    }
}


<?php

namespace Dywee\OrderBundle\Controller;

use Dywee\OrderBundle\Entity\Deliver;
use Dywee\OrderBundle\Form\DeliverType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class DeliverController extends Controller
{
    /**
     * @return Response
     *
     * @Route(name="deliver_table", path="admin/deliver")
     */
    public function tableAction()
    {
        $dr = $this->getDoctrine()->getRepository(Deliver::class);
        $deliverList = $dr->findAll();

        return $this->render('DyweeOrderBundle:Deliver:table.html.twig', array('delivers' => $deliverList));
    }

    /**
     * @Route(name="deliver_view", path="admin/deliver/{id}")
     */
    public function viewAction(Deliver $deliver)
    {
        return $this->render('DyweeOrderBundle:Deliver:view.html.twig', array('deliver' => $deliver));
    }

    /**
     * @Route(name="deliver_add", path="admin/deliver/create")
     */
    public function addAction(Request $request)
    {

    }

    /**
     * @Route(name="deliver_update", path="admin/deliver/{id}/update")
     */
    public function updateAction(Deliver $deliver, Request $request)
    {
        $form = $this->createForm(DeliverType::class, $deliver);

        if($form->handleRequest($request)->isValid())
        {
            $em = $this->getDoctrine()->getManager();
            $em->persist($deliver);
            $em->flush();

            return $this->redirectToRoute('deliver_table');
        }

        return $this->render('DyweeOrderBundle:Deliver:edit.html.twig', array('form' => $form->createView()));
    }

    /**
     * @Route(name="deliver_delete", path="admin/deliver/{id}/delete")
     */
    public function deleteAction()
    {
        return new Response('delete');
    }
}


<?php

namespace ether\splash;

use Craft;
use craft\base\FieldInterface;
use craft\base\FlysystemVolume;
use craft\base\Plugin;
use craft\events\RegisterUrlRulesEvent;
use craft\helpers\UrlHelper;
use craft\models\FieldLayout;
use craft\web\UrlManager;
use ether\splash\models\Settings;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
use yii\base\Event;

class Splash extends Plugin {

	/**
	 * @var Splash
	 */
	public static $i;

	public $controllerNamespace = 'ether\\splash\\controllers';
	public $hasCpSection  = true;
	public $hasCpSettings = true;

	public $changelogUrl = 'https://raw.githubusercontent.com/ethercreative/splash/v3/CHANGELOG.md';
	public $downloadUrl = 'https://github.com/ethercreative/splash/archive/v3.zip';

	/**
	 * Initialize
	 */
	public function init ()
	{
		parent::init();

		self::$i = self::getInstance();

		Event::on(
			UrlManager::class,
			UrlManager::EVENT_REGISTER_CP_URL_RULES,
			[$this, 'onRegisterCPUrlRules']
		);
	}

	/**
	 * @return Settings
	 */
	protected function createSettingsModel () : Settings
	{
		return new Settings();
	}

	/**
	 * @return string
	 * @throws LoaderError
	 * @throws RuntimeError
	 * @throws SyntaxError
	 */
	protected function settingsHtml () : string
	{
		// Get and pre-validate the settings
		$settings = $this->getSettings();
		$settings->validate();

		$validFields = ["PlainText", "RichText", "Color"];

		$volumes = [];
		/** @var FlysystemVolume $volume */
		foreach (\Craft::$app->volumes->getAllVolumes() as $volume)
		{
			$s = ["label" => $volume->name, "value" => $volume->id];

			$fields = [];

			if ($volume->fieldLayoutId !== null)
			{
				/** @var FieldLayout $layout */
				$layout =
					\Craft::$app->fields->getLayoutById($volume->fieldLayoutId);

				/** @var FieldInterface $field */
				foreach ($layout->getFields() as $field)
				{
					$fieldTypeClass = explode('\\', get_class($field));
					$fieldTypeClass = end($fieldTypeClass);
					if (in_array($fieldTypeClass, $validFields))
					{
						$fields[] = [
							"label" => $field->name,
							"value" => $field->handle,
							"type"  => $fieldTypeClass,
						];
					}
				}
			}

			$s["fields"] = $fields;

			$volumes[] = $s;
		}

		return \Craft::$app->view->renderTemplate('splash/settings', [
			'settings' => $this->getSettings(),
			'volumes' => $volumes,
		]);
	}

	public function afterInstall ()
	{
		parent::afterInstall();

		if (Craft::$app->getRequest()->getIsConsoleRequest())
			return;

		Craft::$app->getResponse()->redirect(
			UrlHelper::cpUrl('settings/plugins/splash')
		)->send();
	}

	// Events
	// =========================================================================

	public function onRegisterCPUrlRules (RegisterUrlRulesEvent $event)
	{
		$event->rules['splash'] = 'splash/splash/index';
		$event->rules['POST splash/un'] = 'splash/splash/un';
		$event->rules['POST splash/dl'] = 'splash/splash/dl';
	}

}


<?php
/**
 * This file is part of Pico. It's copyrighted by the contributors recorded
 * in the version control history of the file, available from the following
 * original location:
 *
 * <https://github.com/picocms/composer-installer/blob/master/src/Installer/PluginInstaller.php>
 *
 * SPDX-License-Identifier: MIT
 * License-Filename: LICENSE
 */

namespace Pico\Composer\Installer;

/**
 * Pico plugin and theme installer
 *
 * The Pico plugin and theme installer is responsible for installing plugins
 * and themes for Pico using Composer. Pico is a stupidly simple, blazing fast,
 * flat file CMS.
 *
 * See <http://picocms.org/> for more info.
 *
 * @author  Daniel Rudolf
 * @link    http://picocms.org
 * @license http://opensource.org/licenses/MIT The MIT License
 * @version 1.0
 */
class PluginInstaller extends \Composer\Installer\LibraryInstaller
{
    /**
     * Package name of this composer installer
     *
     * @var string
     */
    const PACKAGE_NAME = 'picocms/composer-installer';

    /**
     * Package type of Pico plugins
     *
     * @var string
     */
    const PACKAGE_TYPE_PLUGIN = 'pico-plugin';

    /**
     * Package type of Pico themes
     *
     * @var string
     */
    const PACKAGE_TYPE_THEME = 'pico-theme';

    /**
     * Composer root package
     *
     * @var \Composer\Package\PackageInterface|null
     */
    protected $rootPackage;

    /**
     * Default package installation locations
     *
     * @var string[]
     */
    protected $installDirs = array(
        self::PACKAGE_TYPE_PLUGIN => 'plugins',
        self::PACKAGE_TYPE_THEME => 'themes'
    );

    /**
     * A flag to check usage of the postAutoloadDump event
     *
     * @var bool|null
     */
    protected static $useAutoloadDump;

    /**
     * Initializes Pico plugin and theme installer
     *
     * This method tries to automatically register the `post-autoload-dump`
     * script ({@see static::postAutoloadDump()}), if it wasn't explicitly set
     * already. If this isn't possible, the autoload dump event consequently
     * can't be used ({@see static::checkAutoloadDump()}).
     *
     * @param \Composer\IO\IOInterface            $io
     * @param \Composer\Composer                  $composer
     * @param string                              $type
     * @param \Composer\Util\Filesystem           $filesystem
     * @param \Composer\Installer\BinaryInstaller $binaryInstaller
     */
    public function __construct(
        \Composer\IO\IOInterface $io,
        \Composer\Composer $composer,
        $type = 'library',
        \Composer\Util\Filesystem $filesystem = null,
        \Composer\Installer\BinaryInstaller $binaryInstaller = null
    ) {
        parent::__construct($io, $composer, $type, $filesystem, $binaryInstaller);
        $this->rootPackage = static::getRootPackage($this->composer);

        // try to register the `post-autoload-dump` script
        $scripts = $this->rootPackage->getScripts();
        $callback = get_called_class() . '::postAutoloadDump';
        if (isset($scripts['post-autoload-dump']) && in_array($callback, $scripts['post-autoload-dump'])) {
            // the user explicitly added the `post-autoload-dump` script,
            // force the autoload dump event to be used
            static::$useAutoloadDump = true;
        } else {
            if (is_callable(array($this->rootPackage, 'setScripts'))) {
                $scripts['post-autoload-dump'][] = $callback;
                $this->rootPackage->setScripts($scripts);
            }

            // check whether the autoload dump event is used
            static::checkAutoloadDump($this->composer);
        }
    }

    /**
     * Checks whether the autoload dump event is used
     *
     * Using the autoload dump event is synonymous with creating the
     * `pico-plugin.php` in Composer's vendor dir. Plugins are nevertheless
     * installed to Pico's `plugins/` dir ({@see static::getInstallPath()}).
     *
     * The autoload dump event is used when the root package is a project and
     * explicitly requires this composer installer.
     *
     * @param  \Composer\Composer $composer
     * @return bool
     */
    public static function checkAutoloadDump(\Composer\Composer $composer)
    {
        if (static::$useAutoloadDump === null) {
            static::$useAutoloadDump = false;

            $rootPackage = static::getRootPackage($composer);
            if (!$rootPackage || ($rootPackage->getType() !== 'project')) {
                return false;
            }

            $rootPackageRequires = $rootPackage->getRequires();
            if (!isset($rootPackageRequires[static::PACKAGE_NAME])) {
                return false;
            }

            $scripts = $rootPackage->getScripts();
            $callback = get_called_class() . '::postAutoloadDump';
            if (!isset($scripts['post-autoload-dump']) || !in_array($callback, $scripts['post-autoload-dump'])) {
                return false;
            }

            static::$useAutoloadDump = true;
        }

        return static::$useAutoloadDump;
    }

    /**
     * Called whenever Composer (re)generates the autoloader
     *
     * Recreates the `pico-plugin.php` in Composer's vendor dir, containing
     * a mapping of Composer package to Pico plugin class names.
     *
     * @param \Composer\Script\Event $event
     * @return void
     */
    public static function postAutoloadDump(\Composer\Script\Event $event)
    {
        $io = $event->getIO();
        $composer = $event->getComposer();

        $vendorDir = $composer->getConfig()->get('vendor-dir');
        $pluginConfig = static::getPluginConfig($vendorDir);

        if (!static::checkAutoloadDump($composer)) {
            if (file_exists($pluginConfig) || is_link($pluginConfig)) {
                $io->write('<info>Deleting Pico plugins file</info>');

                $filesystem = new \Composer\Util\Filesystem();
                $filesystem->unlink($pluginConfig);
            }

            return;
        }

        if (!file_exists($pluginConfig) && !is_link($pluginConfig)) {
            $io->write('<info>Creating Pico plugins file</info>');
        } else {
            $io->write('<info>Updating Pico plugins file</info>');
        }

        $rootPackage = static::getRootPackage($composer);
        $packages = $composer->getRepositoryManager()->getLocalRepository()->getPackages();

        $plugins = $pluginClassNames = array();
        foreach ($packages as $package) {
            if ($package->getType() !== static::PACKAGE_TYPE_PLUGIN) {
                continue;
            }

            $packageName = $package->getName();
            $plugins[$packageName] = static::getInstallName($package, $rootPackage);
            $pluginClassNames[$packageName] = static::getPluginClassNames($package, $rootPackage);
        }

        static::writePluginConfig($pluginConfig, $plugins, $pluginClassNames);
    }

    /**
     * Returns all Pico plugins that should be loaded using pico-plugin.php
     *
     * Returns a associative array of plugin class names, indexed by package
     * name.
     *
     * @param  \Composer\Composer $composer
     * @param  \Composer\Package\PackageInterface[] $packages
     * @return array
     */
    public static function getPluginConfigPlugins(\Composer\Composer $composer, array $packages)
    {

        return $plugins;
    }

    /**
     * Determines the plugin class names of a package
     *
     * Plugin class names are either specified explicitly in either the root
     * package's or the plugin package's `composer.json`, or are derived
     * implicitly from the plugin's installer name. The installer name is, for
     * its part, either specified explicitly, or derived implicitly from the
     * plugin package's name ({@see static::getInstallName()}).
     *
     * 1. Using the "pico-plugin" extra in the root package's `composer.json`:
     *    ```yaml
     *    {
     *        "extra": {
     *            "pico-plugin": {
     *                "<package name>": [ "<class name>", "<class name>", ... ]
     *            }
     *        }
     *    }
     *    ```
     *
     *    Besides matching the exact package name, you can also use the
     *    `vendor:` or `name:` prefixes. See {@see static::mapRootExtra()}.
     *
     * 2. Using the "pico-plugin" extra in the package's `composer.json`:
     *    ```yaml
     *    {
     *        "extra": {
     *            "pico-plugin": [ "<class name>", "<class name>", ... ]
     *        }
     *    }
     *    ```
     *
     * 3. Using the installer name. See {@see static::getInstallName()}.
     *
     * @param  \Composer\Package\PackageInterface      $package
     * @param  \Composer\Package\PackageInterface|null $rootPackage
     * @return string[]
     */
    public static function getPluginClassNames(
        \Composer\Package\PackageInterface $package,
        \Composer\Package\PackageInterface $rootPackage = null
    ) {
        $packageType = $package->getType();
        $packagePrettyName = $package->getPrettyName();
        $packageName = $package->getName();

        $classNames = array();

        // 1. root package
        $rootPackageExtra = $rootPackage ? $rootPackage->getExtra() : null;
        if (!empty($rootPackageExtra[$packageType])) {
            $classNames = (array) $this->mapRootExtra($rootPackageExtra[$packageType], $packagePrettyName);
        }

        // 2. package
        if (!$classNames) {
            $packageExtra = $package->getExtra();
            if (!empty($packageExtra[$packageType])) {
                $classNames = (array) $packageExtra[$packageType];
            }
        }

        // 3. guess by installer name
        if (!$classNames) {
            $installName = static::getInstallName($package, $rootPackage);
            $classNames = array($installName);
        }

        return $classNames;
    }

    /**
     * Returns the install name of a package
     *
     * The install name of packages are either explicitly specified in either
     * the root package's or the plugin package's `composer.json` using the
     * "installer-name" extra, or implicitly derived from the plugin package's
     * name.
     *
     * Install names are determined the same way as plugin class names. See
     * {@see static::getPluginClassNames()} for details.
     *
     * @param  \Composer\Package\PackageInterface      $package
     * @param  \Composer\Package\PackageInterface|null $rootPackage
     * @return string
     */
    public static function getInstallName(
        \Composer\Package\PackageInterface $package,
        \Composer\Package\PackageInterface $rootPackage = null
    ) {
        $packagePrettyName = $package->getPrettyName();
        $packageName = $package->getName();
        $installName = null;

        $rootPackageExtra = $rootPackage ? $rootPackage->getExtra() : null;
        if (!empty($rootPackageExtra['installer-name'])) {
            $installName = $this->mapRootExtra($rootPackageExtra['installer-name'], $packagePrettyName);
        }

        if (!$installName) {
            $packageExtra = $package->getExtra();
            if (!empty($packageExtra['installer-name'])) {
                $installName = $packageExtra['installer-name'];
            }
        }

        return $installName ?: static::guessInstallName($packageName);
    }

    /**
     * Guesses the install name of a package
     *
     * The install name of a Pico plugin or theme is guessed by converting the
     * package name to StudlyCase and removing "-plugin" or "-theme" suffixes,
     * if present.
     *
     * @param  string $packageName
     * @return string
     */
    protected static function guessInstallName($packageName)
    {
        $name = $packageName;
        if (strpos($packageName, '/') !== false) {
            list(, $name) = explode('/', $packageName);
        }

        $name = preg_replace('/[\.\-_]+(?>plugin|theme)$/u', '', $name);
        $name = preg_replace_callback(
            '/(?>^[\.\-_]*|[\.\-_]+)(.)/u',
            function ($matches) {
                return strtoupper($matches[1]);
            },
            $name
        );

        return $name;
    }

    /**
     * Maps the root package's extra data to a package
     *
     * Besides matching the exact package name, you can also use the `vendor:`
     * or `name:` prefixes to match all packages of a specific vendor resp.
     * all packages with a specific name, no matter the vendor.
     *
     * @param  mixed[] $packageExtra
     * @param  string  $packagePrettyName
     * @return mixed
     */
    protected static function mapRootExtra(array $packageExtra, $packagePrettyName)
    {
        if (isset($packageExtra[$packagePrettyName])) {
            return $packageExtra[$packagePrettyName];
        }

        if (strpos($packagePrettyName, '/') !== false) {
            list($vendor, $name) = explode('/', $packagePrettyName);
        } else {
            $vendor = '';
            $name = $packagePrettyName;
        }

        foreach ($packageExtra as $key => $value) {
            if ((substr($key, 0, 5) === 'name:') && (substr($key, 5) === $name)) {
                return $value;
            } elseif ((substr($key, 0, 7) === 'vendor:') && (substr($key, 7) === $vendor)) {
                return $value;
            }
        }

        return null;
    }

    /**
     * Returns the path to the pico-plugin.php in Composer's vendor dir
     *
     * @param  string $vendorDir
     * @return string
     */
    protected static function getPluginConfig($vendorDir)
    {
        return $vendorDir . '/' . static::PACKAGE_TYPE_PLUGIN . '.php';
    }

    /**
     * Rewrites the pico-plugin.php in Composer's vendor dir
     *
     * @param  string $pluginConfig
     * @param  array  $plugins
     * @param  array  $pluginClassNames
     * @return void
     */
    public static function writePluginConfig($pluginConfig, array $plugins, array $pluginClassNames)
    {
        $data = array();
        foreach ($plugins as $pluginName => $installerName) {
            // see https://github.com/composer/composer/blob/1.0.0/src/Composer/Command/InitCommand.php#L206-L210
            if (!preg_match('{^[a-z0-9_.-]+/[a-z0-9_.-]+$}', $pluginName)) {
                throw new \InvalidArgumentException(
                    "The package name '" . $pluginName . "' is invalid, it must be lowercase and have a vendor name, "
                    . "a forward slash, and a package name, matching: [a-z0-9_.-]+/[a-z0-9_.-]+"
                );
            }
            $data[] = sprintf("    '%s' => array(", $pluginName);

            if (!preg_match('{^[a-zA-Z0-9_.-]+$}', $installerName)) {
                throw new \InvalidArgumentException(
                    "The installer name '" . $installerName . "' is invalid, "
                    . "it must be alphanumeric, matching: [a-zA-Z0-9_.-]+"
                );
            }
            $data[] = sprintf("        'installerName' => '%s',", $installerName);

            if (isset($pluginClassNames[$pluginName])) {
                $data[] = sprintf("        'classNames' => array(");

                foreach ($pluginClassNames[$pluginName] as $className) {
                    // see https://secure.php.net/manual/en/language.oop5.basic.php
                    if (!preg_match('{^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$}', $className)) {
                        throw new \InvalidArgumentException(
                            "The plugin class name '" . $className . "' is no valid PHP class name"
                        );
                    }
                    $data[] = sprintf("            '%s',", $className);
                }

                $data[] = "        ),";
            }

            $data[] = "    ),";
        }

        $contents = <<<'PHP'
<?php

// %s @generated by %s

return array(
%s
);

PHP;

        $contents = sprintf(
            $contents,
            basename($pluginConfig),
            static::PACKAGE_NAME,
            implode("\n", $data)
        );

        file_put_contents($pluginConfig, $contents);
    }

    /**
     * Returns the root package of a composer instance
     *
     * @param  \Composer\Composer $composer
     * @return \Composer\Package\PackageInterface
     */
    protected static function getRootPackage(\Composer\Composer $composer)
    {
        $rootPackage = $composer->getPackage();
        if ($rootPackage) {
            while ($rootPackage instanceof \Composer\Package\AliasPackage) {
                $rootPackage = $rootPackage->getAliasOf();
            }
        }

        return $rootPackage;
    }

    /**
     * Decides if the installer supports installing the given package type
     *
     * @param  string $packageType
     * @return bool
     */
    public function supports($packageType)
    {
        return (
            ($packageType === static::PACKAGE_TYPE_PLUGIN)
            || ($packageType === static::PACKAGE_TYPE_THEME)
        );
    }

    /**
     * Returns the installation path of a package
     *
     * Plugins are installed to the `plugins/`, themes to the `themes/` dir
     * by default respectively. You can overwrite these target dirs using the
     * "pico-plugin-dir" resp. "pico-theme-dir" extra in the root package's
     * `composer.json`.
     *
     * @param  \Composer\Package\PackageInterface $package
     * @return string
     */
    public function getInstallPath(\Composer\Package\PackageInterface $package)
    {
        $packageType = $package->getType();

        $installDir = $this->initializeInstallDir($packageType);
        $installName = static::getInstallName($package, $this->rootPackage);
        return $installDir . '/' . $installName;
    }

    /**
     * Returns and initializes the installation directory of the given type
     *
     * @param  string $packageType
     * @return string
     */
    protected function initializeInstallDir($packageType)
    {
        $installDir = '';

        $rootPackageExtra = $this->rootPackage ? $this->rootPackage->getExtra() : null;
        if (!empty($rootPackageExtra[$packageType . '-dir'])) {
            $installDir = rtrim($rootPackageExtra[$packageType . '-dir'], '/\\');
        }

        if (!$installDir) {
            if (empty($this->installDirs[$packageType])) {
                throw new \InvalidArgumentException(
                    "The package type '" . $packageType . "' is not supported"
                );
            }

            $installDir = $this->installDirs[$packageType];
        }

        if (!$this->filesystem->isAbsolutePath($installDir)) {
            $installDir = dirname($this->vendorDir) . '/' . $installDir;
        }

        $this->filesystem->ensureDirectoryExists($installDir);
        return realpath($installDir);
    }
}


<?php

/**
 * Contao Bootstrap
 *
 * @package    contao-bootstrap
 * @subpackage Core
 * @author     David Molineus <david.molineus@netzmacht.de>
 * @copyright  2017 netzmacht David Molineus. All rights reserved.
 * @license    LGPL-3.0 https://github.com/contao-bootstrap/core
 * @filesource
 */

declare(strict_types=1);

namespace ContaoBootstrap\Core\Listener\Dca;

use Contao\Input;
use Contao\LayoutModel;
use ContaoBootstrap\Core\Environment;
use ContaoCommunityAlliance\MetaPalettes\MetaPalettes;

/**
 * Class Layout is used in tl_layout.
 *
 * @package ContaoBootstrap\Core\DataContainer
 */
final class LayoutDcaListener
{
    /**
     * Bootstrap environment..
     *
     * @var Environment
     */
    private $environment;

    /**
     * Settings constructor.
     *
     * @param Environment $environment Environment.
     */
    public function __construct(Environment $environment)
    {
        $this->environment = $environment;
    }

    /**
     * Modify palette if bootstrap is used.
     *
     * Hook palettes_hook (MetaPalettes) is called.
     *
     * @return void
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     */
    public function generatePalette(): void
    {
        // @codingStandardsIgnoreStart
        // TODO: How to handle editAll actions?
        // @codingStandardsIgnoreEnd

        if (Input::get('table') != 'tl_layout' || Input::get('act') != 'edit') {
            return;
        }

        $layout = LayoutModel::findByPk(Input::get('id'));

        // dynamically render palette so that extensions can plug into default palette
        if ($layout->layoutType == 'bootstrap') {
            $metaPalettes                             = & $GLOBALS['TL_DCA']['tl_layout']['metapalettes'];
            $metaPalettes['__base__']                 = $this->getMetaPaletteOfPalette('tl_layout');
            $metaPalettes['default extends __base__'] = $this->environment
                ->getConfig()
                ->get('layout.metapalette', array());

            // unset default palette. otherwise metapalettes will not render this palette
            unset($GLOBALS['TL_DCA']['tl_layout']['palettes']['default']);

            $subSelectPalettes = $this->environment->getConfig()->get('layout.metasubselectpalettes', array());

            foreach ($subSelectPalettes as $field => $meta) {
                foreach ($meta as $value => $definition) {
                    unset($GLOBALS['TL_DCA']['tl_layout']['subpalettes'][$field . '_' . $value]);
                    $GLOBALS['TL_DCA']['tl_layout']['metasubselectpalettes'][$field][$value] = $definition;
                }
            }
        } else {
            MetaPalettes::appendFields('tl_layout', 'title', array('layoutType'));
        }
    }

    /**
     * Creates an meta palette of a palettes.
     *
     * @param string $table Database table name.
     * @param string $name  Palette name.
     *
     * @return array
     *
     * @SuppressWarnings(PHPMD.Superglobals)
     */
    protected function getMetaPaletteOfPalette(string $table, string $name = 'default'): array
    {
        $palette     = $GLOBALS['TL_DCA'][$table]['palettes'][$name];
        $metaPalette = array();
        $legends     = explode(';', $palette);

        foreach ($legends as $legend) {
            $fields = explode(',', $legend);

            preg_match('/\{(.*)_legend(:hide)?\}/', $fields[0], $matches);

            if (isset($matches[2])) {
                $fields[0] = $matches[2];
            } else {
                array_shift($fields);
            }

            $metaPalette[$matches[1]] = $fields;
        }

        return $metaPalette;
    }
}


<?php

/**
 * Contao Bootstrap
 *
 * @package    contao-bootstrap
 * @subpackage Core
 * @author     David Molineus <david.molineus@netzmacht.de>
 * @copyright  2017 netzmacht David Molineus. All rights reserved.
 * @license    LGPL-3.0 https://github.com/contao-bootstrap/core
 * @filesource
 */

declare(strict_types=1);

namespace ContaoBootstrap\Core\Exception;

use ContaoBootstrap\Core\Environment\Context;
use Throwable;

/**
 * Class LeavingContextFailed.
 *
 * @package ContaoBootstrap\Core\Exception
 */
final class LeavingContextFailed extends Exception
{
    /**
     * Create exception for the current context.
     *
     * @param Context        $context  Current context.
     * @param int            $code     Error code.
     * @param Throwable|null $previous Previous exception.
     *
     * @return static
     */
    public static function inContext(Context $context, $code = 0, Throwable $previous = null): self
    {
        return new static(
            sprintf('Leaving context "%s" failed. Context stack is empty', $context->__toString()),
            $code,
            $previous
        );
    }

    /**
     * Create exception for no given context.
     *
     * @param int            $code     Error code.
     * @param Throwable|null $previous Previous exception.
     *
     * @return static
     */
    public static function noContext($code = 0, Throwable $previous = null): self
    {
        return new static(
            'Leaving context failed. Environment has no context.',
            $code,
            $previous
        );
    }
}