<?php

namespace KassaCom\SDK;

use GuzzleHttp\Psr7;
use KassaCom\SDK\Actions\ObjectRecursiveValidator;
use KassaCom\SDK\Actions\RequestCreator;
use KassaCom\SDK\Actions\ResponseCreator;
use KassaCom\SDK\Exception\ClientIncorrectAuthTypeException;
use KassaCom\SDK\Exception\JsonParseException;
use KassaCom\SDK\Exception\Request\RequestParseException;
use KassaCom\SDK\Exception\Response\ResponseParseException;
use KassaCom\SDK\Exception\ServerResponse\BadRequestException;
use KassaCom\SDK\Exception\ServerResponse\ForbiddenException;
use KassaCom\SDK\Exception\ServerResponse\InternalServerException;
use KassaCom\SDK\Exception\ServerResponse\NotFoundException;
use KassaCom\SDK\Exception\ServerResponse\RequestTimeoutException;
use KassaCom\SDK\Exception\ServerResponse\ResponseException;
use KassaCom\SDK\Exception\ServerResponse\TooManyRequestsException;
use KassaCom\SDK\Exception\ServerResponse\UnauthorizedException;
use KassaCom\SDK\Exception\TransportException;
use KassaCom\SDK\Model\Request\AbstractRequest;
use KassaCom\SDK\Model\Request\AbstractRequestTransport;
use KassaCom\SDK\Model\Request\Payment\CancelPaymentRequest;
use KassaCom\SDK\Model\Request\Payment\CancelPaymentSerializer;
use KassaCom\SDK\Model\Request\Payment\CancelPaymentTransport;
use KassaCom\SDK\Model\Request\Payment\CapturePaymentRequest;
use KassaCom\SDK\Model\Request\Payment\CapturePaymentSerializer;
use KassaCom\SDK\Model\Request\Payment\CapturePaymentTransport;
use KassaCom\SDK\Model\Request\Payment\CreatePaymentRequest;
use KassaCom\SDK\Model\Request\Payment\CreatePaymentSerializer;
use KassaCom\SDK\Model\Request\Payment\CreatePaymentTransport;
use KassaCom\SDK\Model\Request\Payment\GetPaymentRequest;
use KassaCom\SDK\Model\Request\Payment\GetPaymentSerializer;
use KassaCom\SDK\Model\Request\Payment\GetPaymentTransport;
use KassaCom\SDK\Model\Request\Payment\ProcessPaymentRequest;
use KassaCom\SDK\Model\Request\Payment\ProcessPaymentSerializer;
use KassaCom\SDK\Model\Request\Payment\ProcessPaymentTransport;
use KassaCom\SDK\Model\Request\Payout\CreatePayoutRequest;
use KassaCom\SDK\Model\Request\Payout\CreatePayoutSerializer;
use KassaCom\SDK\Model\Request\Payout\CreatePayoutTransport;
use KassaCom\SDK\Model\Request\Payout\GetPayoutRequest;
use KassaCom\SDK\Model\Request\Payout\GetPayoutRequestById;
use KassaCom\SDK\Model\Request\Payout\GetPayoutSerializer;
use KassaCom\SDK\Model\Request\Payout\GetPayoutTransport;
use KassaCom\SDK\Model\Request\Refund\CreateRefundRequest;
use KassaCom\SDK\Model\Request\Refund\CreateRefundSerializer;
use KassaCom\SDK\Model\Request\Refund\CreateRefundTransport;
use KassaCom\SDK\Model\Request\Refund\GetRefundRequest;
use KassaCom\SDK\Model\Request\Refund\GetRefundSerializer;
use KassaCom\SDK\Model\Request\Refund\GetRefundTransport;
use KassaCom\SDK\Model\Request\Reports\PaymentsReportRequest;
use KassaCom\SDK\Model\Request\Reports\PaymentsReportSerializer;
use KassaCom\SDK\Model\Request\Reports\PaymentsReportTransport;
use KassaCom\SDK\Model\Request\Reports\PayoutsReportRequest;
use KassaCom\SDK\Model\Request\Reports\PayoutsReportSerializer;
use KassaCom\SDK\Model\Request\Reports\PayoutsReportTransport;
use KassaCom\SDK\Model\Request\Subscription\GetSubscriptionRequest;
use KassaCom\SDK\Model\Request\Subscription\GetSubscriptionSerializer;
use KassaCom\SDK\Model\Request\Subscription\GetSubscriptionTransport;
use KassaCom\SDK\Model\Request\Wallet\WalletRequest;
use KassaCom\SDK\Model\Request\Wallet\WalletSerializer;
use KassaCom\SDK\Model\Response\AbstractResponse;
use KassaCom\SDK\Model\Response\Payment\CancelPaymentResponse;
use KassaCom\SDK\Model\Response\Payment\CapturePaymentResponse;
use KassaCom\SDK\Model\Response\Payment\CreatePaymentResponse;
use KassaCom\SDK\Model\Response\Payment\GetPaymentResponse;
use KassaCom\SDK\Model\Response\Payment\ProcessPaymentResponse;
use KassaCom\SDK\Model\Response\Payout\CreatePayoutResponse;
use KassaCom\SDK\Model\Response\Payout\GetPayoutResponse;
use KassaCom\SDK\Model\Response\Refund\CreateRefundResponse;
use KassaCom\SDK\Model\Response\Refund\GetRefundResponse;
use KassaCom\SDK\Model\Response\Subscription\GetSubscriptionResponse;
use KassaCom\SDK\Model\Response\Wallet\WalletResponse;
use KassaCom\SDK\Transport\AbstractApiTransport;
use KassaCom\SDK\Transport\Authorization\BasicAuthorization;
use KassaCom\SDK\Transport\Authorization\TokenAuthorization;
use KassaCom\SDK\Transport\CurlApiTransport;

class Client
{
    const VERSION = '1.0.0-dev';

    /** @var AbstractApiTransport */
    private $apiTransport;

    public function __construct(AbstractApiTransport $apiTransport = null)
    {
        $this->apiTransport = $apiTransport;
        if (!$this->apiTransport) {
            $this->apiTransport = new CurlApiTransport();
        }
    }

    /**
     * @param string $login
     * @param string $secret
     * @param string $type
     *
     * @throws ClientIncorrectAuthTypeException
     */
    public function setAuth($login, $secret, $type = TokenAuthorization::class)
    {
        switch ($type) {
            case TokenAuthorization::class:
                $auth = new TokenAuthorization($login, $secret);
                break;
            case BasicAuthorization::class:
                $auth = new BasicAuthorization($login, $secret);
                break;
            default:
                throw new ClientIncorrectAuthTypeException('Unknown authorization type');
        }

        $this->apiTransport->setAuth($auth);
    }

    /**
     * @param CreatePaymentRequest|AbstractRequest|array $payment
     *
     * @return CreatePaymentResponse|AbstractResponse
     *
     * @throws TransportException
     * @throws JsonParseException
     * @throws ResponseException
     * @throws ResponseParseException
     * @throws RequestParseException
     */
    public function createPayment($payment)
    {
        if (is_array($payment)) {
            $payment = RequestCreator::create(CreatePaymentRequest::class, $payment);
        }

        ObjectRecursiveValidator::validate($payment);
        $paymentSerializer = new CreatePaymentSerializer($payment);
        $paymentTransport = new CreatePaymentTransport($paymentSerializer);

        return $this->execute($paymentTransport, CreatePaymentResponse::class);
    }

    /**
     * @param ProcessPaymentRequest|AbstractRequest|array $payment
     *
     * @return ProcessPaymentResponse|AbstractResponse
     *
     * @throws TransportException
     * @throws JsonParseException
     * @throws ResponseException
     * @throws ResponseParseException
     * @throws RequestParseException
     */
    public function processPayment($payment)
    {
        if (is_array($payment)) {
            $payment = RequestCreator::create(ProcessPaymentRequest::class, $payment);
        }

        ObjectRecursiveValidator::validate($payment);
        $paymentSerializer = new ProcessPaymentSerializer($payment);
        $paymentTransport = new ProcessPaymentTransport($paymentSerializer);

        return $this->execute($paymentTransport, ProcessPaymentResponse::class);
    }

    /**
     * @param string|GetPaymentRequest $paymentToken
     *
     * @return GetPaymentResponse|AbstractResponse
     *
     * @throws TransportException
     * @throws JsonParseException
     * @throws ResponseException
     * @throws ResponseParseException
     */
    public function getPayment($paymentToken)
    {
        if (!($paymentToken instanceof GetPaymentRequest)) {
            $paymentToken = new GetPaymentRequest($paymentToken);
        }

        ObjectRecursiveValidator::validate($paymentToken);
        $paymentSerializer = new GetPaymentSerializer($paymentToken);
        $paymentTransport = new GetPaymentTransport($paymentSerializer);

        return $this->execute($paymentTransport, GetPaymentResponse::class);
    }

    /**
     * @param string|CapturePaymentRequest $paymentToken
     *
     * @return CapturePaymentResponse|AbstractResponse
     *
     * @throws ResponseException
     * @throws TransportException
     * @throws ResponseParseException
     * @throws JsonParseException
     */
    public function capturePayment($paymentToken)
    {
        if (!$paymentToken instanceof CapturePaymentRequest) {
            $paymentToken = new CapturePaymentRequest($paymentToken);
        }

        ObjectRecursiveValidator::validate($paymentToken);
        $paymentSerializer = new CapturePaymentSerializer($paymentToken);
        $paymentTransport = new CapturePaymentTransport($paymentSerializer);

        return $this->execute($paymentTransport, CapturePaymentResponse::class);
    }

    /**
     * @param string|CancelPaymentRequest $paymentToken
     * @param string|null                 $reason
     *
     * @return CancelPaymentResponse|AbstractResponse
     *
     * @throws ResponseException
     * @throws TransportException
     * @throws ResponseParseException
     * @throws JsonParseException
     */
    public function cancelPayment($paymentToken, $reason = null)
    {
        if (!$paymentToken instanceof CancelPaymentRequest) {
            $paymentToken = new CancelPaymentRequest($paymentToken);
        }

        if ($reason !== null) {
            $paymentToken->setReason($reason);
        }

        ObjectRecursiveValidator::validate($paymentToken);
        $paymentSerializer = new CancelPaymentSerializer($paymentToken);
        $paymentTransport = new CancelPaymentTransport($paymentSerializer);

        return $this->execute($paymentTransport, CancelPaymentResponse::class);
    }

    /**
     * @param array|CreatePayoutRequest|AbstractRequest $payout
     *
     * @return CreatePayoutResponse|AbstractResponse
     *
     * @throws TransportException
     * @throws JsonParseException
     * @throws ResponseException
     * @throws ResponseParseException
     * @throws RequestParseException
     */
    public function createPayout($payout)
    {
        if (is_array($payout)) {
            $payout = RequestCreator::create(CreatePayoutRequest::class, $payout);
        }

        ObjectRecursiveValidator::validate($payout);
        $payoutSerializer = new CreatePayoutSerializer($payout);
        $payoutTransport = new CreatePayoutTransport($payoutSerializer);

        return $this->execute($payoutTransport, CreatePayoutResponse::class);
    }

    /**
     * @param int|array|GetPayoutRequest|GetPayoutRequestById|AbstractRequest $payoutParam
     *
     * @return GetPayoutResponse|AbstractResponse
     *
     * @throws TransportException
     * @throws JsonParseException
     * @throws ResponseException
     * @throws ResponseParseException
     * @throws RequestParseException
     */
    public function getPayout($payoutParam)
    {
        if (is_int($payoutParam)) {
            $payout = new GetPayoutRequestById($payoutParam);
        } else if (is_array($payoutParam)) {
            $payout = RequestCreator::create(GetPayoutRequest::class, $payoutParam);
        } else {
            $payout = $payoutParam;
        }

        ObjectRecursiveValidator::validate($payout);
        $payoutSerializer = new GetPayoutSerializer($payout);
        $payoutTransport = new GetPayoutTransport($payoutSerializer);

        return $this->execute($payoutTransport, GetPayoutResponse::class);
    }

    /**
     * @param array|GetSubscriptionRequest $subscriptionRequest
     *
     * @return AbstractResponse|GetSubscriptionResponse
     * @throws ResponseException
     * @throws TransportException
     */
    public function getSubscription($subscriptionRequest)
    {
        if (is_array($subscriptionRequest)) {
            $subscriptionRequest = RequestCreator::create(GetSubscriptionRequest::class, $subscriptionRequest);
        } else if (!($subscriptionRequest instanceof GetSubscriptionRequest)) {
            $subscriptionRequest = new GetSubscriptionRequest($subscriptionRequest);
        }

        ObjectRecursiveValidator::validate($subscriptionRequest);
        $serializer = new GetSubscriptionSerializer($subscriptionRequest);
        $transport = new GetSubscriptionTransport($serializer);

        return $this->execute($transport, GetSubscriptionResponse::class);
    }

    /**
     * @param array|CreateRefundRequest $request
     *
     * @return AbstractResponse|CreateRefundResponse
     * @throws ResponseException
     * @throws TransportException
     */
    public function createRefund($request)
    {
        if (!($request instanceof CreateRefundRequest)) {
            $request = RequestCreator::create(CreateRefundRequest::class, $request);
        }

        ObjectRecursiveValidator::validate($request);
        $serializer = new CreateRefundSerializer($request);
        $transport = new CreateRefundTransport($serializer);

        return $this->execute($transport, CreateRefundResponse::class);
    }

    /**
     * @param string|array|GetRefundRequest $request
     *
     * @return AbstractResponse|GetRefundResponse
     * @throws ResponseException
     * @throws TransportException
     */
    public function getRefund($request)
    {
        if (is_array($request)) {
            $request = RequestCreator::create(GetSubscriptionRequest::class, $request);
        } else if (!($request instanceof GetRefundRequest)) {
            $request = new GetRefundRequest($request);
        }

        ObjectRecursiveValidator::validate($request);
        $serializer = new GetRefundSerializer($request);
        $transport = new GetRefundTransport($serializer);

        return $this->execute($transport, GetRefundResponse::class);
    }

    /**
     * @param array|PaymentsReportRequest $paymentsReport
     *
     * @return $this|Psr7\MessageTrait
     *
     * @throws TransportException
     */
    public function getPaymentsReport($paymentsReport)
    {
        if (!($paymentsReport instanceof PaymentsReportRequest)) {
            $paymentsReport = RequestCreator::create(PaymentsReportRequest::class, $paymentsReport);
        }

        ObjectRecursiveValidator::validate($paymentsReport);
        $paymentsReportSerializer = new PaymentsReportSerializer($paymentsReport);
        $paymentsReportTransport = new PaymentsReportTransport($paymentsReportSerializer);

        $filename = [];
        $filename[] = 'payments_report';
        $filename[] = $paymentsReport->getDatetimeFrom()->format('Y-m-d-H-i-s');
        $filename[] = '_';
        $filename[] = $paymentsReport->getDatetimeTo()->format('Y-m-d-H-i-s');
        $filename[] = '.csv';

        return $this->download($paymentsReportTransport, join($filename));
    }

    /**
     * @param array|PayoutsReportRequest $payoutsReport
     *
     * @return Psr7\MessageTrait
     * @throws TransportException
     */
    public function getPayoutsReport($payoutsReport)
    {
        if (!($payoutsReport instanceof PayoutsReportRequest)) {
            $payoutsReport = RequestCreator::create(PayoutsReportRequest::class, $payoutsReport);
        }

        ObjectRecursiveValidator::validate($payoutsReport);
        $payoutsReportSerializer = new PayoutsReportSerializer($payoutsReport);
        $payoutsReportTransport = new PayoutsReportTransport($payoutsReportSerializer);

        $filename = [];
        $filename[] = '$payouts_report';
        $filename[] = $payoutsReport->getDatetimeFrom()->format('Y-m-d-H-i-s');
        $filename[] = '_';
        $filename[] = $payoutsReport->getDatetimeTo()->format('Y-m-d-H-i-s');
        $filename[] = '.csv';

        return $this->download($payoutsReportTransport, join($filename));
    }

    /**
     * @param string|WalletRequest $walletRequest
     *
     * @return AbstractResponse
     *
     * @throws ResponseException
     * @throws TransportException
     */
    public function getWalletInfo($walletRequest)
    {
        if (!$walletRequest instanceof WalletRequest) {
            $walletRequest = new WalletRequest($walletRequest);
        }

        ObjectRecursiveValidator::validate($walletRequest);
        $walletSerializer = new WalletSerializer($walletRequest);

        return $this->execute($walletRequest->getTransport($walletSerializer), WalletResponse::class);
    }

    /**
     * @param AbstractRequestTransport $requestTransport
     * @param string                   $responseType
     *
     * @return AbstractResponse
     *
     * @throws ResponseException
     * @throws TransportException
     * @throws ResponseParseException
     * @throws JsonParseException
     */
    private function execute(AbstractRequestTransport $requestTransport, $responseType)
    {
        $response = $this->apiTransport->send(
            $requestTransport->getPath(),
            $requestTransport->getMethod(),
            $requestTransport->getQueryParams(),
            json_encode($requestTransport->getBody()),
            $requestTransport->getHeaders()
        );

        if ($response->getStatusCode() != 200) {
            $this->processError($response); // throw ResponseException
        }

        $responseData = json_decode($response->getBody(), true);

        if (!$responseData) {
            throw new JsonParseException('Decode response error', json_last_error());
        }

        return ResponseCreator::create($responseType, $responseData);
    }

    /**
     * @param AbstractRequestTransport $requestTransport
     * @param string                   $filename
     *
     * @return Psr7\MessageTrait
     *
     * @throws TransportException
     */
    private function download(AbstractRequestTransport $requestTransport, $filename)
    {
        $response = $this->apiTransport->send(
            $requestTransport->getPath(),
            $requestTransport->getMethod(),
            $requestTransport->getQueryParams(),
            $requestTransport->getBody(),
            $requestTransport->getHeaders()
        );

        if ($response->getStatusCode() === 200) {
            return (new Psr7\Response())
                ->withHeader('Content-Type', 'text/csv; charset=utf-8')
                ->withHeader('Content-Disposition', 'attachment; filename="' . $filename . '"')
                ->withBody($response->getBody());
        }

        return $response;
    }

    /**
     * @param Psr7\Response $response
     *
     * @throws ResponseException
     */
    private function processError(Psr7\Response $response)
    {
        $content = $response->getBody()->getContents();

        switch ($response->getStatusCode()) {
            case BadRequestException::HTTP_CODE:
                throw new BadRequestException($response->getHeaders(), $content);
            case UnauthorizedException::HTTP_CODE:
                throw new UnauthorizedException($response->getHeaders(), $content);
            case ForbiddenException::HTTP_CODE:
                throw new ForbiddenException($response->getHeaders(), $content);
            case NotFoundException::HTTP_CODE:
                throw new NotFoundException($response->getHeaders(), $content);
            case RequestTimeoutException::HTTP_CODE:
                throw new RequestTimeoutException($response->getHeaders(), $content);
            case TooManyRequestsException::HTTP_CODE:
                throw new TooManyRequestsException($response->getHeaders(), $content);
            case InternalServerException::HTTP_CODE:
                throw new InternalServerException($response->getHeaders(), $content);
            default:
                throw new ResponseException(
                    'An unknown API error occurred',
                    $response->getStatusCode(),
                    $response->getHeaders(),
                    $content
                );
        }
    }
}


<?php

namespace KassaCom\SDK\Model\Traits;

use KassaCom\SDK\Model\Types\AbstractCustomType;
use KassaCom\SDK\Model\Interfaces\RestorableInterface;
use KassaCom\SDK\Utils\AccessorUtil;

trait RecursiveRestoreTrait
{
    /**
     * @param array $data
     * @param array $fields
     *
     * @return void
     * @throws \UnexpectedValueException
     */
    public function restore(array $data, array $fields)
    {
        foreach ($fields as $field => $type) {
            if (!isset($data[$field])) {
                continue;
            }

            $property = AccessorUtil::property($field);
            $value = $data[$field];

            $value = $this->getPropertyValueForRestore($type, $value, $field);

            $this->{$property} = $value;
        }
    }

    /**
     * @param mixed  $type
     * @param mixed  $value
     * @param string $field
     *
     * @return mixed
     * @throws \UnexpectedValueException
     */
    private function getPropertyValueForRestore($type, $value, $field)
    {
        if ($type instanceof AbstractCustomType) {
            $type = $type->getBaseType();
        }

        switch ($type) {
            case RestorableInterface::TYPE_BOOLEAN:
                $value = (bool)$value;
                break;
            case RestorableInterface::TYPE_INTEGER:
                if (!is_numeric($value)) {
                    throw new \UnexpectedValueException('Value is not int');
                }

                $value = (int)$value;
                break;
            case RestorableInterface::TYPE_FLOAT:
                if (!is_numeric($value)) {
                    throw new \UnexpectedValueException('Value is not float');
                }

                $value = (float)$value;
                break;
            case RestorableInterface::TYPE_STRING:
                if (!is_string($value) && !is_numeric($value)) {
                    throw new \UnexpectedValueException('Value is not string');
                }

                $value = (string)$value;
                break;
            case RestorableInterface::TYPE_ARRAY:
                if (!is_array($value)) {
                    throw new \UnexpectedValueException('Value is not array');
                }
                break;
            case RestorableInterface::TYPE_DATE:
                if (empty($value) || $value instanceof \DateTime) {
                    break;
                }

                try {
                    $value = new \DateTime($value);
                } catch (\Exception $e) {
                    throw new \UnexpectedValueException('Date incorrect ' . $field, 0, $e);
                }
                break;
            case is_array($type):
                if (empty($type) || count($type) > 1) {
                    throw new \UnexpectedValueException('Incorrect array type. Please provide one element in array.');
                }

                $type = reset($type);
                $arrayValues = [];

                foreach ((array)$value as $data) {
                    $arrayValues[] = $this->getPropertyValueForRestore($type, $data, $field);
                }

                $value = $arrayValues;
                break;
            default:
                if (is_array($value) && is_subclass_of($type, RestorableInterface::class)) {
                    /** @var RestorableInterface $subObject */
                    $subObject = new $type();
                    $subObject->restore($value, array_merge($subObject->getRequiredFields(), $subObject->getOptionalFields()));
                    $value = $subObject;
                    break;
                }

                throw new \UnexpectedValueException('Unknown type for restore object');
        }

        return $value;
    }
}


<?php

namespace KassaCom\SDK\Model\Request\Item;

use KassaCom\SDK\Model\Traits\RecursiveRestoreTrait;

class ReceiptRequestItem extends AbstractRequestItem
{
    use RecursiveRestoreTrait;

    /**
     * @var array|ItemsReceiptRequestItem[]
     */
    private $items;

    /**
     * @var string
     */
    private $email;

    /**
     * @var string
     */
    private $phone;

    /**
     * @var string
     */
    private $place;

    /**
     * ReceiptRequestItem constructor.
     */
    public function __construct()
    {
        $this->items = [];
    }

    /**
     * @return array|ItemsReceiptRequestItem[]
     */
    public function getItems()
    {
        return $this->items;
    }

    /**
     * @param array|ItemsReceiptRequestItem[] $items
     *
     * @return $this
     */
    public function setItems($items)
    {
        $this->items = $items;

        return $this;
    }

    /**
     * @param ItemsReceiptRequestItem $item
     *
     * @return $this
     */
    public function addItem(ItemsReceiptRequestItem $item)
    {
        if (!in_array($item, $this->items)) {
            $this->items[] = $item;
        }

        return $this;
    }

    /**
     * @return string
     */
    public function getEmail()
    {
        return $this->email;
    }

    /**
     * @param string $email
     *
     * @return $this
     */
    public function setEmail($email)
    {
        $this->email = $email;

        return $this;
    }

    /**
     * @return string
     */
    public function getPhone()
    {
        return $this->phone;
    }

    /**
     * @param string $phone
     *
     * @return $this
     */
    public function setPhone($phone)
    {
        $this->phone = $phone;

        return $this;
    }

    /**
     * @return string
     */
    public function getPlace()
    {
        return $this->place;
    }

    /**
     * @param string $place
     *
     * @return $this
     */
    public function setPlace($place)
    {
        $this->place = $place;

        return $this;
    }

    /**
     * @inheritDoc
     */
    public function getRequiredFields()
    {
        return [
            'items' => [ItemsReceiptRequestItem::class],
            'place' => self::TYPE_STRING,
        ];
    }

    /**
     * @inheritDoc
     */
    public function getOptionalFields()
    {
        return [
            'email' => self::TYPE_STRING,
            'phone' => self::TYPE_STRING,
        ];
    }

    /**
     * @inheritDoc
     */
    public function getThoughOneField()
    {
        return [
            ['email', 'phone'],
        ];
    }
}


<?php

namespace Gdbots\Pbj\Serializer;

use Gdbots\Pbj\Assertion;
use Gdbots\Pbj\Message;

class PhpSerializer implements Serializer
{
    /**
     * {@inheritdoc}
     */
    public function serialize(Message $message, array $options = [])
    {
        return serialize($message);
    }

    /**
     * {@inheritdoc}
     *
     * @return Message
     */
    public function deserialize($data, array $options = [])
    {
        /** @var Message $message */
        $message = unserialize($data);
        Assertion::isInstanceOf($message, Message::class);
        return $message;
    }
}


<?php

/**
 * This file is part of MetaModels/attribute_translatedselect.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/attribute_translatedselect
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Stefan Heimes <stefan_heimes@hotmail.com>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @author     David Molineus <david.molineus@netzmacht.de>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/attribute_translatedselect/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\AttributeTranslatedSelectBundle\EventListener;

use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent;
use MetaModels\AttributeSelectBundle\EventListener\BackendEventsListener;

/**
 * Handle events for tl_metamodel_attribute.
 */
class PropertyOptionsListener extends BackendEventsListener
{
    /**
     * Retrieve all database table names.
     *
     * @param GetPropertyOptionsEvent $event The event.
     *
     * @return void
     */
    public function getTableNames(GetPropertyOptionsEvent $event)
    {
        if (($event->getEnvironment()->getDataDefinition()->getName() !== 'tl_metamodel_attribute')
            || ($event->getPropertyName() !== 'select_srctable')) {
            return;
        }

        $event->setOptions($this->getTableAndMetaModelsList());
    }

    /**
     * Retrieve all column names for the current selected table.
     *
     * @param GetPropertyOptionsEvent $event The event.
     *
     * @return void
     */
    public function getColumnNames(GetPropertyOptionsEvent $event)
    {
        if (($event->getEnvironment()->getDataDefinition()->getName() !== 'tl_metamodel_attribute')
            || ($event->getPropertyName() !== 'select_langcolumn')
        ) {
            return;
        }

        $result = $this->getColumnNamesFrom($event->getModel()->getProperty('select_table'));

        if (!empty($result)) {
            \asort($result);
            $event->setOptions($result);
        }
    }

    /**
     * Retrieve all column names of type int for the current selected table.
     *
     * @param GetPropertyOptionsEvent $event The event.
     *
     * @return void
     */
    public function getSourceColumnNames(GetPropertyOptionsEvent $event)
    {
        if (($event->getEnvironment()->getDataDefinition()->getName() !== 'tl_metamodel_attribute')
            || ($event->getPropertyName() !== 'select_srcsorting')
        ) {
            return;
        }

        $model         = $event->getModel();
        $table         = $model->getProperty('tag_srctable');
        $schemaManager = $this->connection->getSchemaManager();

        if (!$table || !$schemaManager->tablesExist([$table])) {
            return;
        }

        $result = [];

        foreach ($schemaManager->listTableColumns($table) as $column) {
            $result[$column->getName()] = $column->getName();
        }

        $event->setOptions($result);
    }

    /**
     * Retrieve all columns from a database table.
     *
     * @param string     $tableName  The database table name.
     *
     * @param array|null $typeFilter Optional of types to filter for.
     *
     * @return string[]
     */
    protected function getColumnNamesFromMetaModel($tableName, $typeFilter = null)
    {
        if (!$this->connection->getSchemaManager()->tablesExist([$tableName])) {
            return array();
        }

        $result    = [];
        $fieldList = $this->connection->getSchemaManager()->listTableColumns($tableName);

        foreach ($fieldList as $column) {
            if (($typeFilter === null) || in_array($column->getType()->getName(), $typeFilter)) {
                $result[$column->getName()] = $column->getName();
            }
        }

        if (!empty($result)) {
            asort($result);
            return $result;
        }

        return $result;
    }
}


<?php

namespace Gdbots\Pbj\Type;

use Gdbots\Pbj\Assertion;
use Gdbots\Pbj\Codec;
use Gdbots\Pbj\Field;

// todo: review precision/scale handling.  this seems putrid
final class DecimalType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function guard($value, Field $field)
    {
        Assertion::float($value, null, $field->getName());
    }

    /**
     * {@inheritdoc}
     */
    public function encode($value, Field $field, Codec $codec = null)
    {
        return (float) bcadd((float) $value, '0', $field->getScale());
    }

    /**
     * {@inheritdoc}
     */
    public function decode($value, Field $field, Codec $codec = null)
    {
        return (float) bcadd((float) $value, '0', $field->getScale());
    }

    /**
     * {@inheritdoc}
     */
    public function getDefault()
    {
        return 0.0;
    }

    /**
     * {@inheritdoc}
     */
    public function isNumeric()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function getMin()
    {
        return -1;
    }

    /**
     * {@inheritdoc}
     */
    public function getMax()
    {
        return INF;
    }
}


<?php

namespace Gdbots\Pbj\Marshaler\DynamoDb;

use Gdbots\Pbj\Assertion;
use Gdbots\Pbj\Codec;
use Gdbots\Pbj\Enum\FieldRule;
use Gdbots\Pbj\Enum\TypeName;
use Gdbots\Pbj\Exception\EncodeValueFailed;
use Gdbots\Pbj\Exception\GdbotsPbjException;
use Gdbots\Pbj\Exception\InvalidResolvedSchema;
use Gdbots\Pbj\Field;
use Gdbots\Pbj\Message;
use Gdbots\Pbj\MessageRef;
use Gdbots\Pbj\MessageResolver;
use Gdbots\Pbj\Schema;
use Gdbots\Pbj\SchemaCurie;
use Gdbots\Pbj\SchemaId;
use Gdbots\Pbj\WellKnown\DynamicField;
use Gdbots\Pbj\WellKnown\GeoPoint;

/**
 * Creates an array in the DynamoDb expected attribute value format.
 * @link http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html
 * @link http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_AttributeValue.html
 * @link http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DataModel.html#DataModel.DataTypes
 *
 * @link http://blogs.aws.amazon.com/php/post/Tx3QE1CEXG8QG1Z/DynamoDB-JSON-and-Array-Marshaling-for-PHP
 */
final class ItemMarshaler implements Codec
{
    const TYPE_S = 'S';
    const TYPE_N = 'N';
    const TYPE_B = 'B';
    const TYPE_SS = 'SS';
    const TYPE_NS = 'NS';
    const TYPE_BS = 'BS';
    const TYPE_STRING = 'S';
    const TYPE_NUMBER = 'N';
    const TYPE_BINARY = 'B';
    const TYPE_STRING_SET = 'SS';
    const TYPE_NUMBER_SET = 'NS';
    const TYPE_BINARY_SET = 'BS';

    /**
     * @param Message $message
     *
     * @return array
     *
     * @throws \Exception
     * @throws GdbotsPbjException
     */
    public function marshal(Message $message)
    {
        $schema = $message::schema();
        $message->validate();

        $payload = [];

        foreach ($schema->getFields() as $field) {
            $fieldName = $field->getName();

            if (!$message->has($fieldName)) {
                /*
                if ($message->hasClearedField($fieldName)) {
                    $payload[$fieldName] = ['NULL' => true];
                }
                */
                continue;
            }

            $value = $message->get($fieldName);

            switch ($field->getRule()->getValue()) {
                case FieldRule::A_SINGLE_VALUE:
                    $payload[$fieldName] = $this->encodeValue($value, $field);
                    break;

                case FieldRule::A_SET:
                    $payload[$fieldName] = $this->encodeASetValue($value, $field);
                    break;

                case FieldRule::A_LIST:
                    $list = [];
                    foreach ($value as $v) {
                        $list[] = $this->encodeValue($v, $field);
                    }
                    $payload[$fieldName] = ['L' => $list];
                    break;

                case FieldRule::A_MAP:
                    $map = [];
                    foreach ($value as $k => $v) {
                        $map[$k] = $this->encodeValue($v, $field);
                    }
                    $payload[$fieldName] = ['M' => $map];
                    break;

                default:
                    break;
            }
        }

        return $payload;
    }

    /**
     * Pass the Item of a result.  $result['Item']
     *
     * @param array $data
     *
     * @return Message
     *
     * @throws \Exception
     * @throws GdbotsPbjException
     */
    public function unmarshal(array $data)
    {
        return $this->doUnmarshal(['M' => $data]);
    }

    /**
     * @param Message $message
     * @param Field   $field
     *
     * @return mixed
     */
    public function encodeMessage(Message $message, Field $field)
    {
        return ['M' => $this->marshal($message)];
    }

    /**
     * @param mixed $value
     * @param Field $field
     *
     * @return Message
     */
    public function decodeMessage($value, Field $field)
    {
        return $this->unmarshal($value);
    }

    /**
     * @param MessageRef $messageRef
     * @param Field      $field
     *
     * @return mixed
     */
    public function encodeMessageRef(MessageRef $messageRef, Field $field)
    {
        return [
            'M' => [
                'curie' => [self::TYPE_STRING => $messageRef->getCurie()->toString()],
                'id'    => [self::TYPE_STRING => $messageRef->getId()],
                'tag'   => $messageRef->hasTag() ? [self::TYPE_STRING => $messageRef->getTag()] : ['NULL' => true],
            ],
        ];
    }

    /**
     * @param mixed $value
     * @param Field $field
     *
     * @return MessageRef
     */
    public function decodeMessageRef($value, Field $field)
    {
        return new MessageRef(
            SchemaCurie::fromString($value['curie']['S']),
            $value['id']['S'],
            isset($value['tag']['NULL']) ? null : $value['tag']['S']
        );
    }

    /**
     * @param GeoPoint $geoPoint
     * @param Field    $field
     *
     * @return mixed
     */
    public function encodeGeoPoint(GeoPoint $geoPoint, Field $field)
    {
        return [
            'M' => [
                'type'        => [self::TYPE_STRING => 'Point'],
                'coordinates' => [
                    'L' => [
                        [self::TYPE_NUMBER => (string)$geoPoint->getLongitude()],
                        [self::TYPE_NUMBER => (string)$geoPoint->getLatitude()],
                    ],
                ],
            ],
        ];
    }

    /**
     * @param mixed $value
     * @param Field $field
     *
     * @return GeoPoint
     */
    public function decodeGeoPoint($value, Field $field)
    {
        return new GeoPoint($value['coordinates']['L'][1]['N'], $value['coordinates']['L'][0]['N']);
    }

    /**
     * @param DynamicField $dynamicField
     * @param Field        $field
     *
     * @return mixed
     */
    public function encodeDynamicField(DynamicField $dynamicField, Field $field)
    {
        return [
            'M' => [
                'name'                   => [self::TYPE_STRING => $dynamicField->getName()],
                $dynamicField->getKind() => $this->encodeValue($dynamicField->getValue(), $dynamicField->getField()),
            ],
        ];
    }

    /**
     * @param mixed $value
     * @param Field $field
     *
     * @return DynamicField
     */
    public function decodeDynamicField($value, Field $field)
    {
        $data = ['name' => $value['name']['S']];
        unset($value['name']);

        $kind = key($value);
        $data[$kind] = current($value[$kind]);

        return DynamicField::fromArray($data);
    }

    /**
     * @param array $data
     *
     * @return Message
     *
     * @throws \Exception
     * @throws GdbotsPbjException
     */
    private function doUnmarshal(array $data)
    {
        Assertion::keyIsset(
            $data['M'],
            Schema::PBJ_FIELD_NAME,
            sprintf(
                '[%s::%s] Array provided must contain the [%s] key.',
                get_called_class(),
                __FUNCTION__,
                Schema::PBJ_FIELD_NAME
            )
        );

        $schemaId = SchemaId::fromString((string)$data['M'][Schema::PBJ_FIELD_NAME]['S']);
        $className = MessageResolver::resolveId($schemaId);

        /** @var Message $message */
        $message = new $className();
        Assertion::isInstanceOf($message, Message::class);

        if ($message::schema()->getCurieMajor() !== $schemaId->getCurieMajor()) {
            throw new InvalidResolvedSchema($message::schema(), $schemaId, $className);
        }

        $schema = $message::schema();

        foreach ($data['M'] as $fieldName => $dynamoValue) {
            if (!$schema->hasField($fieldName)) {
                continue;
            }

            $dynamoType = key($dynamoValue);
            $value = current($dynamoValue);

            if ('NULL' === $dynamoType) {
                $message->clear($fieldName);
                continue;
            }

            $field = $schema->getField($fieldName);
            $type = $field->getType();

            switch ($field->getRule()->getValue()) {
                case FieldRule::A_SINGLE_VALUE:
                    $message->set($fieldName, $type->decode($value, $field, $this));
                    break;

                case FieldRule::A_SET:
                case FieldRule::A_LIST:
                    $values = [];
                    if ('L' === $dynamoType) {
                        foreach ($value as $v) {
                            $values[] = $type->decode(isset($v['M']) ? $v['M'] : current($v), $field, $this);
                        }
                    } else {
                        foreach ($value as $v) {
                            $values[] = $type->decode($v, $field, $this);
                        }
                    }

                    if ($field->isASet()) {
                        $message->addToSet($fieldName, $values);
                    } else {
                        $message->addToList($fieldName, $values);
                    }
                    break;

                case FieldRule::A_MAP:
                    foreach ($value as $k => $v) {
                        $message->addToMap($fieldName, $k, $type->decode(current($v), $field, $this));
                    }
                    break;

                default:
                    break;
            }
        }

        return $message->set(Schema::PBJ_FIELD_NAME, $schema->getId()->toString())->populateDefaults();
    }

    /**
     * @param mixed $value
     * @param Field $field
     *
     * @return mixed
     *
     * @throws EncodeValueFailed
     */
    private function encodeValue($value, Field $field)
    {
        $type = $field->getType();

        if ($type->encodesToScalar()) {
            if ($type->isString()) {
                $value = $type->encode($value, $field, $this);
                if (empty($value)) {
                    return ['NULL' => true];
                } else {
                    return [self::TYPE_STRING => $value];
                }
            } elseif ($type->isNumeric()) {
                return [self::TYPE_NUMBER => (string)$type->encode($value, $field, $this)];
            } elseif ($type->isBoolean()) {
                return ['BOOL' => $type->encode($value, $field, $this)];
            } elseif ($type->isBinary()) {
                $value = $type->encode($value, $field, $this);
                if (empty($value)) {
                    return ['NULL' => true];
                } else {
                    return [self::TYPE_BINARY => $value];
                }
            }

            throw new EncodeValueFailed($value, $field, get_called_class() . ' has no handling for this value.');
        }

        return $type->encode($value, $field, $this);
    }

    /**
     * @param array $value
     * @param Field $field
     *
     * @return mixed
     *
     * @throws EncodeValueFailed
     */
    private function encodeASetValue(array $value, Field $field)
    {
        $type = $field->getType();

        /*
         * A MessageRefType is the only object/map value that can be
         * used in a set.  In this case of DynamoDb, we can store it as
         * a list of maps.
         */
        if ($type->getTypeName() === TypeName::MESSAGE_REF()) {
            $list = [];
            foreach ($value as $v) {
                $list[] = $type->encode($v, $field, $this);
            }
            return ['L' => $list];
        }

        if ($type->isString()) {
            $dynamoType = self::TYPE_STRING_SET;
        } elseif ($type->isNumeric()) {
            $dynamoType = self::TYPE_NUMBER_SET;
        } elseif ($type->isBinary()) {
            $dynamoType = self::TYPE_BINARY_SET;
        } else {
            throw new EncodeValueFailed(
                $value,
                $field,
                sprintf('%s::%s has no handling for this value.', get_called_class(), __FUNCTION__)
            );
        }

        $result = [];
        foreach ($value as $v) {
            if ($type->encodesToScalar()) {
                $result[] = (string)$type->encode($v, $field, $this);
            } else {
                $result[] = (string)$v;
            }
        }

        return [$dynamoType => $result];
    }
}


<?php

namespace Gdbots\Pbj;

use Gdbots\Common\FromArray;
use Gdbots\Common\ToArray;
use Gdbots\Pbj\Exception\FrozenMessageIsImmutable;
use Gdbots\Pbj\Exception\LogicException;
use Gdbots\Pbj\Exception\SchemaNotDefined;
use Gdbots\Pbj\Serializer\PhpArraySerializer;
use Gdbots\Pbj\Serializer\YamlSerializer;
use Gdbots\Pbj\Exception\RequiredFieldNotSet;

abstract class AbstractMessage implements Message, FromArray, ToArray, \JsonSerializable
{
    /**
     * An array of schemas per message type.
     * ['Fully\Qualified\ClassName' => [ array of Schema objects ]
     * @var array
     */
    private static $schemas = [];

    /** @var PhpArraySerializer */
    private static $serializer;

    /** @var YamlSerializer */
    private static $yamlSerializer;

    /** @var array */
    private $data = [];

    /**
     * An array of fields that have been cleared or set to null that
     * must be included when serialized so it's clear that the
     * value has been unset.
     *
     * @var array
     */
    private $clearedFields = [];

    /**
     * @see Message::freeze
     * @var bool
     */
    private $isFrozen = false;

    /**
     * @see Message::isReplay
     * @var bool
     */
    private $isReplay;

    /**
     * Nothing fancy on new messages... we let the serializers or application code get fancy.
     */
    final public function __construct() {}

    /**
     * {@inheritdoc}
     * @return Schema
     */
    final public static function schema()
    {
        $type = get_called_class();
        if (!isset(self::$schemas[$type])) {
            $schema = static::defineSchema();

            if (!$schema instanceof Schema) {
                throw new SchemaNotDefined(
                    sprintf('Message [%s] must return a Schema from the defineSchema method.', $type)
                );
            }

            if ($schema->getClassName() !== $type) {
                throw new SchemaNotDefined(
                    sprintf(
                        'Schema [%s] returned from defineSchema must be for class [%s], not [%s]',
                        $schema->getId()->toString(),
                        $type,
                        $schema->getClassName()
                    )
                );
            }
            self::$schemas[$type] = $schema;
        }
        return self::$schemas[$type];
    }

    /**
     * @return Schema
     * @throws SchemaNotDefined
     */
    protected static function defineSchema()
    {
        throw new SchemaNotDefined(
            sprintf('Message [%s] must return a Schema from the defineSchema method.', get_called_class())
        );
    }

    /**
     * {@inheritdoc}
     * @return static
     */
    final public static function create()
    {
        /** @var Message $message */
        $message = new static();
        return $message->populateDefaults();
    }

    /**
     * {@inheritdoc}
     * @return static
     */
    final public static function fromArray(array $data = [])
    {
        if (null === self::$serializer) {
            self::$serializer = new PhpArraySerializer();
        }

        if (!isset($data[Schema::PBJ_FIELD_NAME])) {
            $data[Schema::PBJ_FIELD_NAME] = static::schema()->getId()->toString();
        }

        $message = self::$serializer->deserialize($data);
        return $message;
    }

    /**
     * {@inheritdoc}
     */
    final public function toArray()
    {
        if (null === self::$serializer) {
            self::$serializer = new PhpArraySerializer();
        }
        return self::$serializer->serialize($this);
    }

    /**
     * {@inheritdoc}
     */
    final public function toYaml(array $options = [])
    {
        try {
            if (null === self::$yamlSerializer) {
                self::$yamlSerializer = new YamlSerializer();
            }
            return self::$yamlSerializer->serialize($this, $options);
        } catch (\Exception $e) {
            return sprintf(
                'Failed to render [%s] as a yaml string with error: %s',
                self::schema()->toString(),
                $e->getMessage()
            );
        }
    }

    /**
     * {@inheritdoc}
     */
    final public function __toString()
    {
        return $this->toYaml();
    }

    /**
     * @return array
     */
    final public function jsonSerialize()
    {
        return $this->toArray();
    }

    /**
     * @return static
     */
    final public function __clone()
    {
        $this->data = unserialize(serialize($this->data));
        $this->unFreeze();
    }

    /**
     * {@inheritdoc}
     * todo: review performance
     */
    final public function generateEtag(array $ignoredFields = [])
    {
        if (null === self::$serializer) {
            self::$serializer = new PhpArraySerializer();
        }
        $array = self::$serializer->serialize($this, ['includeAllFields' => true]);

        if (empty($ignoredFields)) {
            return md5(json_encode($array));
        }

        foreach ($ignoredFields as $field) {
            unset($array[$field]);
        }

        return md5(json_encode($array));
    }

    /**
     * todo: recursively validate nested messages?
     * {@inheritdoc}
     * @return static
     */
    final public function validate()
    {
        foreach (static::schema()->getRequiredFields() as $field) {
            if (!$this->has($field->getName())) {
                throw new RequiredFieldNotSet($this, $field);
            }
        }

        return $this;
    }

    /**
     * {@inheritdoc}
     * @return static
     */
    final public function freeze()
    {
        if ($this->isFrozen()) {
            return $this;
        }

        $this->validate();
        $this->isFrozen = true;

        foreach (static::schema()->getFields() as $field) {
            if ($field->getType()->isMessage()) {
                /** @var self $value */
                $value = $this->get($field->getName());
                if (empty($value)) {
                    continue;
                }

                if ($value instanceof Message) {
                    $value->freeze();
                    continue;
                }

                /** @var self $v */
                foreach ($value as $v) {
                    $v->freeze();
                }
            }
        }

        return $this;
    }

    /**
     * Recursively unfreezes this object and any of its children.
     * Used internally during the clone process.
     */
    private function unFreeze()
    {
        $this->isFrozen = false;
        $this->isReplay = null;

        foreach (static::schema()->getFields() as $field) {
            if ($field->getType()->isMessage()) {
                /** @var self $value */
                $value = $this->get($field->getName());
                if (empty($value)) {
                    continue;
                }

                if ($value instanceof Message) {
                    $value->unFreeze();
                    continue;
                }

                /** @var self $v */
                foreach ($value as $v) {
                    $v->unFreeze();
                }
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    final public function isFrozen()
    {
        return $this->isFrozen;
    }

    /**
     * Ensures a frozen message can't be modified.
     * @throws FrozenMessageIsImmutable
     */
    private function guardFrozenMessage()
    {
        if ($this->isFrozen) {
            throw new FrozenMessageIsImmutable($this);
        }
    }

    /**
     * {@inheritdoc}
     * This could probably use some work.  :)  low level serialization string match.
     */
    public function equals(Message $other)
    {
        return json_encode($this) === json_encode($other);
    }

    /**
     * {@inheritdoc}
     */
    final public function isReplay($replay = null)
    {
        if (null === $replay) {
            if (null === $this->isReplay) {
                $this->isReplay = false;
            }
            return $this->isReplay;
        }

        if (null === $this->isReplay) {
            $this->isReplay = (bool) $replay;
            if ($this->isReplay) {
                $this->freeze();
            }
            return $this->isReplay;
        }

        throw new LogicException('You can only set the replay mode on one time.');
    }

    /**
     * {@inheritdoc}
     * @return static
     */
    final public function populateDefaults($fieldName = null)
    {
        $this->guardFrozenMessage();

        if (!empty($fieldName)) {
            $this->populateDefault(static::schema()->getField($fieldName));
            return $this;
        }

        foreach (static::schema()->getFields() as $field) {
            $this->populateDefault($field);
        }

        return $this;
    }

    /**
     * Populates the default on a single field if it's not already set
     * and the default generated is not a null value or empty array.
     *
     * @param Field $field
     * @return bool Returns true if a non null/empty default was applied or already present.
     */
    private function populateDefault(Field $field)
    {
        if ($this->has($field->getName())) {
            return true;
        }

        $default = $field->getDefault($this);
        if (null === $default) {
            return false;
        }

        if ($field->isASingleValue()) {
            $this->data[$field->getName()] = $default;
            unset($this->clearedFields[$field->getName()]);
            return true;
        }

        if (empty($default)) {
            return false;
        }

        /*
         * sets have a special handling to deal with unique values
         */
        if ($field->isASet()) {
            $this->addToSet($field->getName(), $default);
            return true;
        }

        $this->data[$field->getName()] = $default;
        unset($this->clearedFields[$field->getName()]);
        return true;
    }

    /**
     * {@inheritdoc}
     */
    final public function has($fieldName)
    {
        if (!isset($this->data[$fieldName])) {
            return false;
        }

        if (is_array($this->data[$fieldName])) {
            return !empty($this->data[$fieldName]);
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    final public function get($fieldName, $default = null)
    {
        if (!$this->has($fieldName)) {
            return $default;
        }

        $field = static::schema()->getField($fieldName);
        if ($field->isASet()) {
            return array_values($this->data[$fieldName]);
        }

        return $this->data[$fieldName];
    }

    /**
     * {@inheritdoc}
     * @return static
     */
    final public function clear($fieldName)
    {
        $this->guardFrozenMessage();
        $field = static::schema()->getField($fieldName);
        unset($this->data[$fieldName]);
        $this->clearedFields[$fieldName] = true;
        $this->populateDefault($field);
        return $this;
    }

    /**
     * {@inheritdoc}
     */
    final public function hasClearedField($fieldName)
    {
        return isset($this->clearedFields[$fieldName]);
    }

    /**
     * {@inheritdoc}
     */
    final public function getClearedFields()
    {
        return array_keys($this->clearedFields);
    }

    /**
     * {@inheritdoc}
     * @return static
     */
    final public function setSingleValue($fieldName, $value)
    {
        return $this->set($fieldName, $value);
    }

    /**
     * {@inheritdoc}
     * @return static
     */
    final public function set($fieldName, $value)
    {
        $this->guardFrozenMessage();
        $field = static::schema()->getField($fieldName);
        Assertion::true($field->isASingleValue(), sprintf('Field [%s] must be a single value.', $fieldName), $fieldName);

        if (null === $value) {
            return $this->clear($fieldName);
        }

        $field->guardValue($value);
        $this->data[$fieldName] = $value;
        unset($this->clearedFields[$fieldName]);
        return $this;
    }

    /**
     * {@inheritdoc}
     */
    final public function isInSet($fieldName, $value)
    {
        if (empty($this->data[$fieldName]) || !is_array($this->data[$fieldName])) {
            return false;
        }

        if (is_scalar($value) || (is_object($value) && method_exists($value, '__toString'))) {
            $key = trim((string) $value);
        } else {
            return false;
        }

        if (0 === strlen($key)) {
            return false;
        }

        return isset($this->data[$fieldName][strtolower($key)]);
    }

    /**
     * {@inheritdoc}
     * @return static
     */
    final public function addToSet($fieldName, array $values)
    {
        $this->guardFrozenMessage();
        $field = static::schema()->getField($fieldName);
        Assertion::true($field->isASet(), sprintf('Field [%s] must be a set.', $fieldName), $fieldName);

        foreach ($values as $value) {
            if (0 === strlen($value)) {
                continue;
            }
            $field->guardValue($value);
            $key = strtolower(trim((string) $value));
            $this->data[$fieldName][$key] = $value;
        }

        if (!empty($this->data[$fieldName])) {
            unset($this->clearedFields[$fieldName]);
        }

        return $this;
    }

    /**
     * {@inheritdoc}
     * @return static
     */
    final public function removeFromSet($fieldName, array $values)
    {
        $this->guardFrozenMessage();
        $field = static::schema()->getField($fieldName);
        Assertion::true($field->isASet(), sprintf('Field [%s] must be a set.', $fieldName), $fieldName);

        foreach ($values as $value) {
            if (0 === strlen($value)) {
                continue;
            }
            $key = strtolower(trim((string) $value));
            unset($this->data[$fieldName][$key]);
        }

        if (empty($this->data[$fieldName])) {
            $this->clearedFields[$fieldName] = true;
        }

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    final public function isInList($fieldName, $value)
    {
        if (empty($this->data[$fieldName]) || !is_array($this->data[$fieldName])) {
            return false;
        }

        return in_array($value, $this->data[$fieldName]);
    }

    /**
     * {@inheritdoc}
     */
    final public function getFromListAt($fieldName, $index, $default = null)
    {
        $index = (int) $index;
        if (empty($this->data[$fieldName])
            || !is_array($this->data[$fieldName])
            || !isset($this->data[$fieldName][$index])
        ) {
            return $default;
        }
        return $this->data[$fieldName][$index];
    }

    /**
     * {@inheritdoc}
     * @return static
     */
    final public function addToList($fieldName, array $values)
    {
        $this->guardFrozenMessage();
        $field = static::schema()->getField($fieldName);
        Assertion::true($field->isAList(), sprintf('Field [%s] must be a list.', $fieldName), $fieldName);

        foreach ($values as $value) {
            $field->guardValue($value);
            $this->data[$fieldName][] = $value;
        }

        unset($this->clearedFields[$fieldName]);
        return $this;
    }

    /**
     * {@inheritdoc}
     * @return static
     */
    final public function removeFromListAt($fieldName, $index)
    {
        $this->guardFrozenMessage();
        $field = static::schema()->getField($fieldName);
        Assertion::true($field->isAList(), sprintf('Field [%s] must be a list.', $fieldName), $fieldName);
        $index = (int) $index;

        if (empty($this->data[$fieldName])) {
            return $this;
        }

        array_splice($this->data[$fieldName], $index, 1);
        if (empty($this->data[$fieldName])) {
            $this->clearedFields[$fieldName] = true;
            return $this;
        }

        // reset the numerical indexes
        // todo: review, does this need to be optimized?
        $this->data[$fieldName] = array_values($this->data[$fieldName]);
        return $this;
    }

    /**
     * {@inheritdoc}
     */
    final public function isInMap($fieldName, $key)
    {
        if (empty($this->data[$fieldName]) || !is_array($this->data[$fieldName]) || !is_string($key)) {
            return false;
        }
        return isset($this->data[$fieldName][$key]);
    }

    /**
     * {@inheritdoc}
     */
    final public function getFromMap($fieldName, $key, $default = null)
    {
        if (!$this->isInMap($fieldName, $key)) {
            return $default;
        }
        return $this->data[$fieldName][$key];
    }

    /**
     * {@inheritdoc}
     * @return static
     */
    final public function addToMap($fieldName, $key, $value)
    {
        $this->guardFrozenMessage();
        $field = static::schema()->getField($fieldName);
        Assertion::true($field->isAMap(), sprintf('Field [%s] must be a map.', $fieldName), $fieldName);

        if (null === $value) {
            return $this->removeFromMap($fieldName, $key);
        }

        $field->guardValue($value);
        $this->data[$fieldName][$key] = $value;
        unset($this->clearedFields[$fieldName]);

        return $this;
    }

    /**
     * {@inheritdoc}
     * @return static
     */
    final public function removeFromMap($fieldName, $key)
    {
        $this->guardFrozenMessage();
        $field = static::schema()->getField($fieldName);
        Assertion::true($field->isAMap(), sprintf('Field [%s] must be a map.', $fieldName), $fieldName);

        unset($this->data[$fieldName][$key]);

        if (empty($this->data[$fieldName])) {
            $this->clearedFields[$fieldName] = true;
        }

        return $this;
    }
}


<?php

namespace KassaCom\SDK;

use KassaCom\SDK\Actions\RequestCreator;
use KassaCom\SDK\Exception\Notification\EmptyApiKeyException;
use KassaCom\SDK\Exception\Notification\IncorrectBodyRequestException;
use KassaCom\SDK\Exception\Notification\NotificationParseException;
use KassaCom\SDK\Exception\Notification\NotificationSecurityException;
use KassaCom\SDK\Model\Request\NotificationRequest;

class Notification
{
    const RESPONSE_SUCCESS = 'ok';

    const RESPONSE_ERROR = 'error';

    private $skipIpCheck = false;

    private $allowedIps = [
        '95.216.144.80',
        '95.216.143.141',
        '52.213.148.150',
        '34.252.2.182',
    ];

    private $apiKey;

    /**
     * @param string $apiKey
     */
    public function setApiKey($apiKey)
    {
        $this->apiKey = $apiKey;
    }

    /**
     * @param bool $autoResponse
     *
     * @return NotificationRequest
     *
     * @throws EmptyApiKeyException
     * @throws NotificationSecurityException
     * @throws NotificationParseException
     * @throws IncorrectBodyRequestException
     * @throws \Exception
     */
    public function process($autoResponse = true)
    {
        try {
            $request = $this->getRequest();
            $autoResponse && $this->successResponse();
        } catch (\Exception $e) {
            $autoResponse && $this->errorResponse($e->getMessage());

            throw $e;
        }

        return $request;
    }

    public function successResponse()
    {
        $this->response(self::RESPONSE_SUCCESS);
    }

    public function errorResponse($message)
    {
        $this->response(self::RESPONSE_ERROR, $message);
    }

    public function setSkipIpCheck($skip = true)
    {
        $this->skipIpCheck = $skip;
    }

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

    /**
     * @return NotificationRequest
     *
     * @throws NotificationSecurityException
     * @throws NotificationParseException
     * @throws IncorrectBodyRequestException
     * @throws EmptyApiKeyException
     */
    protected function getRequest()
    {
        if (empty($this->apiKey)) {
            throw new EmptyApiKeyException('Please provide the api key');
        }

        $this->checkRequest();

        return $this->getRequestFromBody();
    }

    protected function response($status, $message = null)
    {
        $response = [
            'status' => $status,
        ];

        if ($message) {
            $response['message'] = $message;
        }

        header('Content-Type: application/json');
        echo json_encode($response);
    }

    /**
     * @return NotificationRequest
     *
     * @throws NotificationParseException
     * @throws IncorrectBodyRequestException
     */
    protected function getRequestFromBody()
    {
        $body = $this->getBody();

        if (!is_string($body)) {
            throw new IncorrectBodyRequestException('The request body contains an invalid json');
        }

        $body = json_decode($body, true);

        if ($body === null) {
            throw new IncorrectBodyRequestException('The request body contains an invalid json');
        }

        try {
            /** @var NotificationRequest $request */
            $request = RequestCreator::create(NotificationRequest::class, $body);
        } catch (\Exception $e) {
            throw new NotificationParseException('An error occurred while parsing the request');
        }

        return $request;
    }

    /**
     * @throws NotificationSecurityException
     */
    protected function checkRequest()
    {
        if (empty($_SERVER['HTTP_X_KASSA_SIGNATURE'])) {
            throw new NotificationSecurityException('Empty signature');
        }

        $signature = strtolower($_SERVER['HTTP_X_KASSA_SIGNATURE']);
        $expectedSignature = strtolower(hash('sha256', $this->getBody() . $this->apiKey));

        if ($signature !== $expectedSignature) {
            throw new NotificationSecurityException('Incorrect signature');
        }

        if (strtolower($_SERVER['REQUEST_METHOD']) !== 'post') {
            throw new NotificationSecurityException('Only post requests are expected');
        }

        if (!$this->isIpAllowed()) {
            throw new NotificationSecurityException('Remote ip is not allowed');
        }
    }

    /**
     * @return bool|string
     */
    protected function getBody()
    {
        return file_get_contents('php://input');
    }

    /**
     * @return bool
     */
    protected function isIpAllowed()
    {
        return $this->skipIpCheck || in_array($_SERVER['REMOTE_ADDR'], $this->allowedIps);
    }
}


<?php

/**
 * This file is part of MetaModels/attribute_translatedselect.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/attribute_translatedselect
 * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
 * @author     Stefan Heimes <stefan_heimes@hotmail.com>
 * @author     Sven Baumann <baumann.sv@gmail.com>
 * @author     David Molineus <david.molineus@netzmacht.de>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/attribute_translatedselect/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\AttributeTranslatedSelectBundle\Attribute;

use Doctrine\DBAL\Connection;
use MetaModels\Attribute\IAttributeTypeFactory;
use MetaModels\Helper\TableManipulator;

/**
 * Attribute type factory for select attributes.
 */
class AttributeTypeFactory implements IAttributeTypeFactory
{
    /**
     * Database connection.
     *
     * @var Connection
     */
    protected $connection;

    /**
     * Table manipulator.
     *
     * @var TableManipulator
     */
    protected $tableManipulator;

    /**
     * Construct.
     *
     * @param Connection       $connection       Database connection.
     * @param TableManipulator $tableManipulator Table manipulator.
     */
    public function __construct(Connection $connection, TableManipulator $tableManipulator)
    {
        $this->connection       = $connection;
        $this->tableManipulator = $tableManipulator;
    }

    /**
     * {@inheritdoc}
     */
    public function getTypeName()
    {
        return 'translatedselect';
    }

    /**
     * {@inheritdoc}
     */
    public function getTypeIcon()
    {
        return 'bundles/metamodelsattributetranslatedselect/select.png';
    }

    /**
     * {@inheritdoc}
     */
    public function createInstance($information, $metaModel)
    {
        return new TranslatedSelect($metaModel, $information, $this->connection, $this->tableManipulator);
    }

    /**
     * {@inheritdoc}
     */
    public function isTranslatedType()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function isSimpleType()
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function isComplexType()
    {
        return true;
    }
}


<?php

namespace Gdbots\Pbj\Type;

use Gdbots\Pbj\Assertion;
use Gdbots\Pbj\Codec;
use Gdbots\Pbj\Exception\DecodeValueFailed;
use Gdbots\Pbj\Field;
use Gdbots\Pbj\WellKnown\Identifier;

final class IdentifierType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function guard($value, Field $field)
    {
        /** @var Identifier $value */
        Assertion::isInstanceOf($value, Identifier::class, null, $field->getName());
        Assertion::isInstanceOf($value, $field->getClassName(), null, $field->getName());
        $v = $value->toString();
        //Assertion::string($v, null, $field->getName());

        // intentionally using strlen to get byte length, not mb_strlen
        $length = strlen($v);
        $maxBytes = $this->getMaxBytes();
        $okay = $length > 0 && $length <= $maxBytes;
        Assertion::true(
            $okay,
            sprintf(
                'Field [%s] must be between [1] and [%d] bytes, [%d] bytes given.',
                $field->getName(),
                $maxBytes,
                $length
            ),
            $field->getName()
        );
    }

    /**
     * {@inheritdoc}
     */
    public function encode($value, Field $field, Codec $codec = null)
    {
        if ($value instanceof Identifier) {
            return (string) $value->toString();
        }

        return null;
    }

    /**
     * {@inheritdoc}
     */
    public function decode($value, Field $field, Codec $codec = null)
    {
        if (empty($value)) {
            return null;
        }

        /** @var Identifier $className */
        $className = $field->getClassName();

        try {
            return $className::fromString((string) $value);
        } catch (\Exception $e) {
            throw new DecodeValueFailed($value, $field, $e->getMessage());
        }
    }

    /**
     * {@inheritdoc}
     */
    public function isScalar()
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function isString()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function getMaxBytes()
    {
        return 255;
    }
}


<?php

namespace Gdbots\Pbj\Type;

use Gdbots\Common\Util\NumberUtils;
use Gdbots\Pbj\Assertion;
use Gdbots\Pbj\Codec;
use Gdbots\Pbj\Field;

abstract class AbstractIntType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function guard($value, Field $field)
    {
        Assertion::integer($value, null, $field->getName());
        $intMin = $this->getMin();
        $intMax = $this->getMax();
        $min = NumberUtils::bound($field->getMin(), $intMin, $intMax);
        $max = NumberUtils::bound($field->getMax(), $intMin, $intMax);
        Assertion::range($value, $min, $max, null, $field->getName());
    }

    /**
     * {@inheritdoc}
     */
    public function encode($value, Field $field, Codec $codec = null)
    {
        return (int) $value;
    }

    /**
     * {@inheritdoc}
     */
    public function decode($value, Field $field, Codec $codec = null)
    {
        return (int) $value;
    }

    /**
     * {@inheritdoc}
     */
    public function getDefault()
    {
        return 0;
    }

    /**
     * {@inheritdoc}
     */
    public function isNumeric()
    {
        return true;
    }
}


<?php

namespace Gdbots\Pbj\Type;

use Gdbots\Pbj\Assertion;
use Gdbots\Pbj\Codec;
use Gdbots\Pbj\Field;
use Gdbots\Pbj\WellKnown\Microtime;

final class MicrotimeType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function guard($value, Field $field)
    {
        /** @var Microtime $value */
        Assertion::isInstanceOf($value, Microtime::class, null, $field->getName());
    }

    /**
     * {@inheritdoc}
     */
    public function encode($value, Field $field, Codec $codec = null)
    {
        if ($value instanceof Microtime) {
            return $value->toString();
        }

        return null;
    }

    /**
     * {@inheritdoc}
     */
    public function decode($value, Field $field, Codec $codec = null)
    {
        if (empty($value)) {
            return null;
        }

        if ($value instanceof Microtime) {
            return $value;
        }

        return Microtime::fromString((string) $value);
    }

    /**
     * {@inheritdoc}
     */
    public function isScalar()
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function getDefault()
    {
        return Microtime::create();
    }

    /**
     * {@inheritdoc}
     */
    public function isNumeric()
    {
        return true;
    }
}


<?php

namespace KassaCom\SDK\Model\Types;

use KassaCom\SDK\Exception\Validation\BadGetterException;
use KassaCom\SDK\Exception\Validation\InvalidPropertyException;
use KassaCom\SDK\Exception\UnsupportableTypeException;
use KassaCom\SDK\Utils\AccessorUtil;

abstract class AbstractCustomType
{
    /**
     * @var object
     */
    protected $object;

    /**
     * AbstractCustomType constructor.
     *
     * @param object $object
     *
     * @throws UnsupportableTypeException
     */
    public function __construct($object)
    {
        $this->object = $object;

        if (!$this->isAccept()) {
            throw new UnsupportableTypeException('Object does not support validation ');
        }
    }

    /**
     * @param string $field
     *
     * @return bool
     *
     * @throws BadGetterException
     * @throws InvalidPropertyException
     */
    abstract public function validate($field);

    /**
     * @return bool
     */
    abstract public function isAccept();

    /**
     * @return mixed
     */
    abstract public function getBaseType();

    /**
     * @param string $field
     *
     * @return mixed
     * @throws BadGetterException
     */
    protected function getValue($field)
    {
        $getter = $method = AccessorUtil::getter($field);

        if (!method_exists($this->object, $getter)) {
            $isser = $method = AccessorUtil::isser($field);

            if (!method_exists($this->object, $isser)) {
                throw new BadGetterException('Getter %s or %s for object does not exists', $getter, $isser);
            }
        }

        return $this->object->{$method}();
    }
}


<?php

namespace Gdbots\Pbj\Type;

use Gdbots\Pbj\Assertion;
use Gdbots\Pbj\Codec;
use Gdbots\Pbj\Field;

/**
 * @link https://en.wikipedia.org/wiki/Three-valued_logic
 * 0 = unknown
 * 1 = true
 * 2 = false
 */
final class TrinaryType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function guard($value, Field $field)
    {
        Assertion::choice($value, [0, 1, 2], null, $field->getName());
    }

    /**
     * {@inheritdoc}
     */
    public function encode($value, Field $field, Codec $codec = null)
    {
        return (int) $value;
    }

    /**
     * {@inheritdoc}
     */
    public function decode($value, Field $field, Codec $codec = null)
    {
        return (int) $value;
    }

    /**
     * {@inheritdoc}
     */
    public function getDefault()
    {
        return 0;
    }

    /**
     * {@inheritdoc}
     */
    public function isNumeric()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function getMin()
    {
        return 0;
    }

    /**
     * {@inheritdoc}
     */
    public function getMax()
    {
        return 2;
    }

    /**
     * {@inheritdoc}
     */
    public function allowedInSet()
    {
        return false;
    }
}


<?php

namespace Gdbots\Pbj\Type;

use Gdbots\Common\Util\DateUtils;
use Gdbots\Pbj\Assertion;
use Gdbots\Pbj\Codec;
use Gdbots\Pbj\Exception\DecodeValueFailed;
use Gdbots\Pbj\Field;

final class DateTimeType extends AbstractType
{
    /** @var \DateTimeZone */
    private $utc;

    /**
     * {@inheritdoc}
     */
    public function guard($value, Field $field)
    {
        /** @var \DateTimeInterface $value */
        Assertion::isInstanceOf($value, \DateTimeInterface::class, null, $field->getName());
    }

    /**
     * {@inheritdoc}
     */
    public function encode($value, Field $field, Codec $codec = null)
    {
        if ($value instanceof \DateTimeInterface) {
            return $this->convertToUtc($value)->format(DateUtils::ISO8601_ZULU);
        }

        return null;
    }

    /**
     * {@inheritdoc}
     */
    public function decode($value, Field $field, Codec $codec = null)
    {
        if (empty($value)) {
            return null;
        }

        if ($value instanceof \DateTimeInterface) {
            return $this->convertToUtc($value);
        }

        $date = \DateTimeImmutable::createFromFormat(DateUtils::ISO8601_ZULU, str_replace('+00:00', 'Z', $value));
        if ($date instanceof \DateTimeInterface) {
            return $this->convertToUtc($date);
        }

        throw new DecodeValueFailed(
            $value,
            $field,
            sprintf(
                'Format must be [%s].  Errors: [%s]',
                DateUtils::ISO8601_ZULU,
                // this is mutant
                print_r(\DateTimeImmutable::getLastErrors() ?: \DateTime::getLastErrors(), true)
            )
        );
    }

    /**
     * {@inheritdoc}
     */
    public function isScalar()
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function isString()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function allowedInSet()
    {
        return false;
    }

    /**
     * @param \DateTimeInterface $date
     *
     * @return \DateTimeInterface
     */
    private function convertToUtc(\DateTimeInterface $date)
    {
        if ($date->getOffset() !== 0) {
            if (null === $this->utc) {
                $this->utc = new \DateTimeZone('UTC');
            }

            if ($date instanceof \DateTimeImmutable) {
                $date = \DateTime::createFromFormat(
                    DateUtils::ISO8601_ZULU,
                    $date->format(DateUtils::ISO8601_ZULU)
                );
            }

            $date->setTimezone($this->utc);
            $date = \DateTimeImmutable::createFromMutable($date);
        }

        return $date;
    }
}


<?php

namespace Gdbots\Pbj\Type;

use Gdbots\Pbj\Assertion;
use Gdbots\Pbj\Codec;
use Gdbots\Pbj\Field;

final class BooleanType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function guard($value, Field $field)
    {
        Assertion::boolean($value, null, $field->getName());
    }

    /**
     * {@inheritdoc}
     */
    public function encode($value, Field $field, Codec $codec = null)
    {
        return (bool) $value;
    }

    /**
     * {@inheritdoc}
     */
    public function decode($value, Field $field, Codec $codec = null)
    {
        return filter_var($value, FILTER_VALIDATE_BOOLEAN);
    }

    /**
     * {@inheritdoc}
     */
    public function getDefault()
    {
        return false;
    }

    /**
     * @see Type::isBoolean
     */
    public function isBoolean()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function allowedInSet()
    {
        return false;
    }
}


<?php

namespace Gdbots\Pbj\Type;

use Gdbots\Common\Util\DateUtils;
use Gdbots\Common\Util\HashtagUtils;
use Gdbots\Common\Util\NumberUtils;
use Gdbots\Pbj\Assertion;
use Gdbots\Pbj\Codec;
use Gdbots\Pbj\Enum\Format;
use Gdbots\Pbj\Field;

abstract class AbstractStringType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function guard($value, Field $field)
    {
        Assertion::string($value, null, $field->getName());

        // intentionally using strlen to get byte length, not mb_strlen
        $length = strlen($value);
        $minLength = $field->getMinLength();
        $maxLength = NumberUtils::bound($field->getMaxLength(), $minLength, $this->getMaxBytes());
        $okay = $length >= $minLength && $length <= $maxLength;

        Assertion::true(
            $okay,
            sprintf(
                'Field [%s] must be between [%d] and [%d] bytes, [%d] bytes given.',
                $field->getName(),
                $minLength,
                $maxLength,
                $length
            ),
            $field->getName()
        );

        if ($pattern = $field->getPattern()) {
            Assertion::regex($value, $pattern, null, $field->getName());
        }

        switch ($field->getFormat()->getValue()) {
            case Format::UNKNOWN:
                break;

            case Format::DATE:
                Assertion::regex($value, '/^\d{4}-\d{2}-\d{2}$/', null, $field->getName());
                break;

            case Format::DATE_TIME:
                Assertion::true(
                    DateUtils::isValidISO8601Date($value),
                    sprintf(
                        'Field [%s] must be a valid ISO8601 date-time.  Format must match one of [%s], [%s] or [%s].',
                        $field->getName(),
                        DateUtils::ISO8601_ZULU,
                        DateUtils::ISO8601,
                        \DateTime::ISO8601
                    ),
                    $field->getName()
                );
                break;

            case Format::SLUG:
                Assertion::regex($value, '/^([\w\/-]|[\w-][\w\/-]*[\w-])$/', null, $field->getName());
                break;

            case Format::EMAIL:
                Assertion::email($value, null, $field->getName());
                break;

            case Format::HASHTAG:
                Assertion::true(
                    HashtagUtils::isValid($value),
                    sprintf('Field [%s] must be a valid hashtag.  @see HashtagUtils::isValid', $field->getName()),
                    $field->getName()
                );
                break;

            case Format::IPV4:
            case Format::IPV6:
                /*
                 * todo: need separate assertion for ipv4 and ipv6
                 */
                Assertion::url(
                    'http://' . $value,
                    sprintf(
                        'Field [%s] must be a valid [%s].',
                        $field->getName(),
                        $field->getFormat()->getValue()
                    ),
                    $field->getName()
                );
                break;

            case Format::HOSTNAME:
            case Format::URI:
            case Format::URL:
                /*
                 * fixme: need better handling for HOSTNAME, URI and URL... assertion library just has one "url" handling
                 * but we really need separate ones for each of these formats.  right now we're just prefixing
                 * the value with a http so it looks like a url.  this won't work for thinks like mailto:
                 * urn:, etc.
                 */
                if (false === strpos($value, 'http')) {
                    $value = 'http://' . $value;
                }

                Assertion::url(
                    $value,
                    sprintf(
                        'Field [%s] must be a valid [%s].',
                        $field->getName(),
                        $field->getFormat()->getValue()
                    ),
                    $field->getName()
                );
                break;

            case Format::UUID:
                Assertion::uuid($value, null, $field->getName());
                break;

            default:
                break;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function encode($value, Field $field, Codec $codec = null)
    {
        $value = trim($value);
        if ($value === '') {
            return null;
        }

        return $value;
    }

    /**
     * {@inheritdoc}
     */
    public function decode($value, Field $field, Codec $codec = null)
    {
        $value = trim((string)$value);
        if ($value === '') {
            return null;
        }

        return $value;
    }

    /**
     * {@inheritdoc}
     */
    public function isString()
    {
        return true;
    }
}


<?php

namespace KassaCom\SDK\Actions;

use KassaCom\SDK\Exception\Request\RequestParseException;
use KassaCom\SDK\Exception\Request\UnknownRequestTypeException;
use KassaCom\SDK\Exception\Request\UnsupportedRequestTypeException;
use KassaCom\SDK\Model\Request\AbstractRequest;

class RequestCreator
{
    /**
     * @param string $request
     * @param array  $data
     *
     * @return AbstractRequest
     *
     * @throws UnknownRequestTypeException
     * @throws UnsupportedRequestTypeException
     * @throws RequestParseException
     */
    public static function create($request, array $data)
    {
        if (!class_exists($request)) {
            throw new UnknownRequestTypeException(sprintf('Unknown request type: %s', $request));
        }

        $request = new $request();

        if (!$request instanceof AbstractRequest) {
            throw new UnsupportedRequestTypeException(sprintf('Unsupported request type: %s', get_class($request)));
        }

        try {
            $request->restore($data, array_merge($request->getRequiredFields(), $request->getOptionalFields()));
        } catch (\Exception $e) {
            throw new RequestParseException($e->getMessage(), $e->getCode());
        }

        return $request;
    }
}


<?php

/**
 * This file is part of MetaModels/attribute_translatedselect.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/attribute_translatedselect
 * @author     David Molineus <david.molineus@netzmacht.de>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/attribute_translatedselect/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\AttributeTranslatedSelectBundle\ContaoManager;

use Contao\ManagerPlugin\Bundle\BundlePluginInterface;
use Contao\ManagerPlugin\Bundle\Config\BundleConfig;
use Contao\ManagerPlugin\Bundle\Parser\ParserInterface;
use MetaModels\AttributeTranslatedSelectBundle\MetaModelsAttributeTranslatedSelectBundle;
use MetaModels\AttributeSelectBundle\MetaModelsAttributeSelectBundle;
use MetaModels\CoreBundle\MetaModelsCoreBundle;

/**
 * Contao Manager plugin.
 */
class Plugin implements BundlePluginInterface
{
    /**
     * {@inheritdoc}
     */
    public function getBundles(ParserInterface $parser)
    {
        return [
            BundleConfig::create(MetaModelsAttributeTranslatedSelectBundle::class)
                ->setLoadAfter(
                    [
                        MetaModelsCoreBundle::class,
                        MetaModelsAttributeSelectBundle::class,
                    ]
                )
                ->setReplace(['metamodelsattribute_translatedselect'])
        ];
    }
}


<?php

namespace Gdbots\Pbj\Type;

use Gdbots\Pbj\Assertion;
use Gdbots\Pbj\Codec;
use Gdbots\Pbj\Field;
use Gdbots\Pbj\WellKnown\UuidIdentifier;

final class UuidType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function guard($value, Field $field)
    {
        Assertion::isInstanceOf($value, UuidIdentifier::class, null, $field->getName());
        if ($field->hasClassName()) {
            Assertion::isInstanceOf($value, $field->getClassName(), null, $field->getName());
        }
    }

    /**
     * {@inheritdoc}
     */
    public function encode($value, Field $field, Codec $codec = null)
    {
        if ($value instanceof UuidIdentifier) {
            return $value->toString();
        }

        return null;
    }

    /**
     * {@inheritdoc}
     */
    public function decode($value, Field $field, Codec $codec = null)
    {
        if (empty($value)) {
            return null;
        }

        /** @var UuidIdentifier $className */
        $className = $field->getClassName() ?: UuidIdentifier::class;
        if ($value instanceof $className) {
            return $value;
        }

        return $className::fromString((string) $value);
    }

    /**
     * {@inheritdoc}
     */
    public function isScalar()
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function getDefault()
    {
        return UuidIdentifier::generate();
    }

    /**
     * {@inheritdoc}
     */
    public function isString()
    {
        return true;
    }
}


<?php

namespace Gdbots\Pbj\Type;

use Gdbots\Pbj\Assertion;
use Gdbots\Pbj\Codec;
use Gdbots\Pbj\Field;
use Gdbots\Pbj\WellKnown\GeoPoint;

final class GeoPointType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function guard($value, Field $field)
    {
        /** @var GeoPoint $value */
        Assertion::isInstanceOf($value, GeoPoint::class, null, $field->getName());
    }

    /**
     * {@inheritdoc}
     */
    public function encode($value, Field $field, Codec $codec = null)
    {
        return $codec->encodeGeoPoint($value, $field);
    }

    /**
     * {@inheritdoc}
     */
    public function decode($value, Field $field, Codec $codec = null)
    {
        return $codec->decodeGeoPoint($value, $field);
    }

    /**
     * {@inheritdoc}
     */
    public function isScalar()
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function encodesToScalar()
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function allowedInSet()
    {
        return false;
    }
}


<?php

namespace Gdbots\Pbj\Type;

use Gdbots\Pbj\Assertion;
use Gdbots\Pbj\Codec;
use Gdbots\Pbj\Field;
use Gdbots\Pbj\MessageRef;

final class MessageRefType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function guard($value, Field $field)
    {
        /** @var MessageRef $value */
        Assertion::isInstanceOf($value, MessageRef::class, null, $field->getName());
    }

    /**
     * {@inheritdoc}
     */
    public function encode($value, Field $field, Codec $codec = null)
    {
        return $codec->encodeMessageRef($value, $field);
    }

    /**
     * {@inheritdoc}
     */
    public function decode($value, Field $field, Codec $codec = null)
    {
        return $codec->decodeMessageRef($value, $field);
    }

    /**
     * {@inheritdoc}
     */
    public function isScalar()
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function encodesToScalar()
    {
        return false;
    }
}


<?php

namespace Nerdify\SmsGateway;

use GuzzleHttp\Client as HttpClient;

class SmsGatewayClient
{
    /**
     * The ID of device to send the message from.
     *
     * @var int
     */
    protected $device;

    /**
     * Username for SMS Gateway.
     *
     * @var string
     */
    protected $email;

    /**
     * The HTTP Client instance.
     *
     * @var \GuzzleHttp\Client
     */
    protected $http;

    /**
     * Password for SMS Gateway.
     *
     * @var string
     */
    protected $password;

    /**
     * Create a new client instance.
     *
     * @param HttpClient $http
     * @param string     $email
     * @param string     $password
     * @param int        $device
     */
    public function __construct(HttpClient $http, $email, $password, $device)
    {
        $this->device = $device;
        $this->email = $email;
        $this->http = $http;
        $this->password = $password;
    }

    /**
     * @param array $params
     *
     * @return array
     */
    public function send(array $params)
    {
        $base = [
            'device' => $this->device,
            'email' => $this->email,
            'password' => $this->password,
        ];

        $params = array_merge($base, $params);

        $response = $this->http->post('https://smsgateway.me/api/v3/messages/send', [
            'form_params' => $params,
        ]);

        return json_decode($response->getBody(), true);
    }
}


<?php

namespace Nerdify\SmsGateway;

use GuzzleHttp\Client as HttpClient;
use Illuminate\Support\ServiceProvider;

class SmsGatewayServiceProvider extends ServiceProvider
{
    /**
     * Boot the application services.
     *
     * @return void
     */
    public function boot()
    {
        $this->app->when(SmsGatewayChannel::class)
            ->needs(SmsGatewayClient::class)
            ->give(function () {
                $config = $this->app['config']['services.smsgateway'];

                return new SmsGatewayClient(
                    new HttpClient,
                    $config['email'],
                    $config['password'],
                    $config['device']
                );
            });
    }
}


<?php

namespace KassaCom\SDK\Transport;

use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7;
use GuzzleHttp\RequestOptions;
use KassaCom\SDK\Exception\TransportException;

class GuzzleApiTransport extends AbstractApiTransport
{
    private $guzzleClient;

    public function __construct(GuzzleClient $guzzleClient)
    {
        parent::__construct();
        $this->guzzleClient = $guzzleClient;
    }

    /**
     * @param Psr7\Request $request
     *
     * @return Psr7\Response|\Psr\Http\Message\ResponseInterface
     *
     * @throws \Exception
     * @throws GuzzleException
     * @throws TransportException
     */
    protected function sendRequest(Psr7\Request $request)
    {
        try {
            return $this->guzzleClient->send($request, [
                RequestOptions::HTTP_ERRORS => false,
            ]);
        } catch (\Exception $e) {
            if ($e instanceof GuzzleException) {
                throw new TransportException($e->getMessage(), $e->getCode());
            }

            throw $e;
        }
    }
}