<?php

/*
 * This file is part of the phpdish/phpdish
 *
 * (c) Slince <taosikai@yeah.net>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace PHPDish\Bundle\UserBundle\Service;

use Carbon\Carbon;
use Doctrine\Common\Collections\Criteria;
use FOS\UserBundle\Doctrine\UserManager as BaseUserManager;
use PHPDish\Bundle\CoreBundle\Service\PaginatorTrait;
use PHPDish\Bundle\ForumBundle\Model\ReplyInterface;
use PHPDish\Bundle\ForumBundle\Model\TopicInterface;
use PHPDish\Bundle\PostBundle\Model\CategoryInterface;
use PHPDish\Bundle\PostBundle\Model\CommentInterface;
use PHPDish\Bundle\PostBundle\Model\PostInterface;
use PHPDish\Bundle\UserBundle\Event\Events;
use PHPDish\Bundle\UserBundle\Event\UserFollowedEvent;
use PHPDish\Bundle\UserBundle\Model\UserInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Translation\TranslatorInterface;

class UserManager extends BaseUserManager implements UserManagerInterface
{
    use PaginatorTrait;

    /**
     * @var EventDispatcherInterface
     */
    protected $eventDispatcher;

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

    /**
     * @param EventDispatcherInterface $eventDispatcher
     */
    public function setEventDispatcher($eventDispatcher)
    {
        $this->eventDispatcher = $eventDispatcher;
    }

    /**
     * @param TranslatorInterface $translator
     */
    public function setTranslator(TranslatorInterface $translator)
    {
        $this->translator = $translator;
    }

    /**
     * {@inheritdoc}
     */
    public function createUser()
    {
        $user = parent::createUser();
        $user->setCreatedAt(Carbon::now());

        return $user;
    }

    /**
     * {@inheritdoc}
     */
    public function saveUser(UserInterface $user)
    {
        $user->setUpdatedAt(Carbon::now());
        $this->objectManager->persist($user);
        $this->objectManager->flush();

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function findUserByName($username)
    {
        return $this->findUserByUsername($username);
    }

    /**
     * {@inheritdoc}
     */
    public function findUserById($id)
    {
        return $this->findUserBy(['id' => $id]);
    }

    /**
     * {@inheritdoc}
     */
    public function getLatestUsersQuery($limit)
    {
        return $this->getRepository()->createQueryBuilder('u')
            ->orderBy('u.createdAt', 'desc')
            ->setMaxResults($limit)
            ->getQuery();
    }

    /**
     * {@inheritdoc}
     */
    public function findLatestUsers($limit, $caching = false)
    {
        $query = $this->getLatestUsersQuery($limit);
        if ($caching) {
            $query->useResultCache(true);
        }
        return $query->getResult();
    }

    /**
     * {@inheritdoc}
     */
    public function findUsersByNames($userNames)
    {
        $qb = $this->getRepository()->createQueryBuilder('u');

        return $qb->where($qb->expr()->in('u.username', $userNames))->getQuery()->getResult();
    }

    /**
     * {@inheritdoc}
     */
    public function findUserFollowing(UserInterface $user, $page, $limit = null)
    {
        $query = $this->getRepository()->createQueryBuilder('u')
            ->innerJoin('u.followers', 'f')
            ->where('f.id = :userId')->setParameter('userId', $user->getId())
            ->getQuery();

        return $this->createPaginator($query, $page, $limit);
    }

    /**
     * {@inheritdoc}
     */
    public function findUserFollowers(UserInterface $user, $page, $limit = null)
    {
        $query = $this->getRepository()->createQueryBuilder('u')
            ->innerJoin('u.following', 'f')
            ->where('f.id = :userId')->setParameter('userId', $user->getId())
            ->getQuery();

        return $this->createPaginator($query, $page, $limit);
    }

    /**
     * {@inheritdoc}
     */
    public function findCategoryFollowers(CategoryInterface $category, $page, $limit = null)
    {
        $query = $this->getRepository()->createQueryBuilder('u')
            ->innerJoin('u.followingCategories', 'f')
            ->where('f.id = :categoryId')->setParameter('categoryId', $category->getId())
            ->getQuery();

        return $this->createPaginator($query, $page, $limit);
    }

    /**
     * {@inheritdoc}
     */
    public function findTopicVoters(TopicInterface $topic, $page, $limit = null)
    {
        $query = $this->getRepository()->createQueryBuilder('u')
            ->innerJoin('u.votedTopics', 'vt')
            ->where('vt.id = :topicId')->setParameter('topicId', $topic->getId())
            ->getQuery();

        return $this->createPaginator($query, $page, $limit);
    }

    /**
     * {@inheritdoc}
     */
    public function findTopicReplyVoters(ReplyInterface $reply, $page, $limit = null)
    {
        $query = $this->getRepository()->createQueryBuilder('u')
            ->innerJoin('u.votedReplies', 'vr')
            ->where('vr.id = :replyId')->setParameter('replyId', $reply->getId())
            ->getQuery();

        return $this->createPaginator($query, $page, $limit);
    }

    /**
     * {@inheritdoc}
     */
    public function findPostVoters(PostInterface $post, $page, $limit = null)
    {
        $query = $this->getRepository()->createQueryBuilder('u')
            ->innerJoin('u.votedPosts', 'vp')
            ->where('vp.id = :postId')->setParameter('postId', $post->getId())
            ->getQuery();

        return $this->createPaginator($query, $page, $limit);
    }

    /**
     * {@inheritdoc}
     */
    public function findCommentVoters(CommentInterface $comment, $page, $limit = null)
    {
        $query = $this->getRepository()->createQueryBuilder('u')
            ->innerJoin('u.votedComments', 'vc')
            ->where('vc.id = :commentId')->setParameter('commentId', $comment->getId())
            ->getQuery();

        return $this->createPaginator($query, $page, $limit);
    }

    /**
     * {@inheritdoc}
     */
    public function followUser(UserInterface $user, UserInterface $follower)
    {
        if ($user->getId() == $follower->getId()) {
            throw new \LogicException($this->translator->trans('follow.cannot_follow_yourself'));
        }
        $user->addFollower($follower);
        $user->setFollowerCount($user->getFollowerCount() + 1);
        $follower->setFollowingCount($follower->getFollowingCount() + 1);

        $this->objectManager->persist($user);
        $this->objectManager->persist($follower);
        $this->objectManager->flush();

        //触发事件
        $this->eventDispatcher->dispatch(Events::USER_FOLLOWED, new UserFollowedEvent($user, $follower));

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function unFollowUser(UserInterface $user, UserInterface $follower)
    {
        $user->removeFollower($follower);
        $user->setFollowerCount($user->getFollowerCount() - 1);
        $follower->setFollowingCount($follower->getFollowingCount() - 1);
        $this->objectManager->persist($user);
        $this->objectManager->persist($follower);
        $this->objectManager->flush();

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function checkEmailExist($email)
    {
        return (bool) $this->findUserByEmail($email);
    }

    /**
     * {@inheritdoc}
     */
    public function checkUsernameExist($username)
    {
        return (bool) $this->findUserByUsername($username);
    }

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

    /**
     * {@inheritdoc}
     */
    public function findUsersByCriteria(Criteria $criteria)
    {
        return $this->getRepository()->createQueryBuilder('u')
            ->addCriteria($criteria)
            ->getQuery()
            ->getResult();
    }

    /**
     * {@inheritdoc}
     */
    public function findUsersPager(Criteria $criteria, $page, $limit = null)
    {
        $query = $this->getRepository()->createQueryBuilder('u')
            ->addCriteria($criteria)
            ->getQuery();
        return $this->createPaginator($query, $page, $limit);
    }
}


<?php

/*
 * This file is part of the phpdish/phpdish
 *
 * (c) Slince <taosikai@yeah.net>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace PHPDish\Bundle\ResourceBundle\Security;

use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\User\UserInterface;

abstract class EntityVoter extends Voter
{
    const ACTION_VIEW = 'view';

    const ACTION_EDIT = 'edit';

    /**
     * @var AccessDecisionManagerInterface
     */
    protected $accessDecisionManager;

    /**
     * @var array
     */
    protected $actions;

    /**
     * @var false
     */
    protected $hasAuthenticatedUser;

    public function __construct(AccessDecisionManagerInterface $accessDecisionManager, $actions = [])
    {
        $this->accessDecisionManager = $accessDecisionManager;
        $this->actions = array_merge($actions, [static::ACTION_EDIT, static::ACTION_VIEW]);
    }

    /**
     * 获取资源实体类.
     *
     * @return string
     */
    abstract protected function getResourceClass();

    /**
     * {@inheritdoc}
     */
    protected function supports($attribute, $subject)
    {
        $entityClass = $this->getResourceClass();
        return in_array($attribute, $this->actions)
            && $subject instanceof $entityClass;
    }

    /**
     * {@inheritdoc}
     */
    protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
    {
        $user = $token->getUser();
        $this->hasAuthenticatedUser = $user instanceof UserInterface;
        //没有登录的用户不允许
        if (!$this->hasAuthenticatedUser) {
            return false;
        }
        if ($this->accessDecisionManager->decide($token, ['ROLE_ADMIN'])) {
            return true;
        }
        $result = false;
        switch ($attribute) {
            case static::ACTION_VIEW:
                $result = $this->canView($subject, $user);
                break;
            case static::ACTION_EDIT:
                $result = $this->canEdit($subject, $user);
                break;
        }
        return $result;
    }

    /**
     * 如果资源已经公开，或者资源属于该用户则公开查看.
     *
     * @param mixed         $entity
     * @param UserInterface $user
     *
     * @return bool
     */
    protected function canView($entity, UserInterface $user)
    {
        return $this->canEdit($entity, $user) || $entity->isEnabled();
    }

    /**
     * @param mixed  $entity
     * @param UserInterface $user
     *
     * @return bool
     */
    protected function canEdit($entity, UserInterface $user)
    {
        return method_exists($entity, 'isBelongsTo') && $entity->isBelongsTo($user);
    }
}


<?php

/*
 * This file is part of the phpdish/phpdish
 *
 * (c) Slince <taosikai@yeah.net>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace PHPDish\Bundle\ResourceBundle;

use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Form\Util\StringUtil;
use Symfony\Component\HttpKernel\Bundle\Bundle;

abstract class AbstractBundle extends Bundle
{
    /**
     * @var string
     */
    protected $modelNamespace;

    /**
     * {@inheritdoc}
     */
    public function build(ContainerBuilder $container)
    {
        $container->addCompilerPass(DoctrineOrmMappingsPass::createXmlMappingDriver(
            [$this->getConfigFilesPath() => $this->getModelNamespace()],
            [sprintf('%s.object_manager', $this->getBundlePrefix())]
        ));
    }

    /**
     * 获取实体命名空间
     * @return string
     */
    protected function getModelNamespace()
    {
        if ($this->modelNamespace) {
            return $this->modelNamespace;
        }
        return $this->modelNamespace = $this->getNamespace() . '\Model';
    }

    /**
     * 获取bundle前缀
     *
     * @return string
     */
    protected function getBundlePrefix(): string
    {
        return Container::underscore(substr(strrchr(get_class($this), '\\'), 1, -6));
    }

    /**
     * @return string
     */
    protected function getConfigFilesPath(): string
    {
        return sprintf('%s/Resources/config/%s',
            $this->getPath(),
            $this->getDoctrineMappingDirectory()
        );
    }

    /**
     * @return string
     */
    protected function getDoctrineMappingDirectory(): string
    {
        return  'doctrine-mapping';
    }
}


<?php

declare(strict_types=1);

namespace PHPFluent\ArrayStorage;

use Countable;
use PHPFluent\ArrayStorage\Filter\Filter;
use UnexpectedValueException;
use function count;

class Criteria implements Countable, Filter
{
    /**
     * @var string|null
     */
    protected $currentIndex = null;

    /**
     * @var Factory
     */
    protected $factory;

    /**
     * @var Filter[]
     */
    protected $filters = [];

    public function __construct(Factory $factory)
    {
        $this->factory = $factory;
    }

    /**
     * @return Filter[]
     */
    public function getFilters(): array
    {
        return $this->filters;
    }

    public function addFilter(string $index, Filter $filter): void
    {
        $this->filters[] = [$index, $filter];
    }

    public function count(): int
    {
        return count($this->filters);
    }

    public function __get(string $index): self
    {
        $this->currentIndex = $index;

        return $this;
    }

    /**
     * @param mixed[] $arguments
     */
    public function __call(string $methodName, array $arguments = []): self
    {
        if ($this->currentIndex === null) {
            throw new UnexpectedValueException('You first need to call a property for this filter');
        }

        $filter = $this->factory->filter($methodName, $arguments);

        $this->addFilter($this->currentIndex, $filter);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function isValid($record): bool
    {
        $record = ($record instanceof Record ? $record : new Record($record));
        foreach ($this->filters as $value) {
            [$index, $filter] = $value;
            $value = $record->__get($index);
            if ($filter->isValid($value)) {
                continue;
            }

            return false;
        }

        return true;
    }
}


<?php

declare(strict_types=1);

namespace PHPFluent\ArrayStorage\Converter;

use Traversable;

class Arr implements Converter
{
    /**
     * @var bool
     */
    private $recursive;

    public function __construct(bool $recursive = true)
    {
        $this->recursive = $recursive;
    }

    /**
     * {@inheritdoc}
     */
    public function convert(Traversable $traversable)
    {
        $result = [];
        foreach ($traversable as $key => $value) {
            if ($this->recursive
                && $value instanceof Traversable) {
                $value = $this->convert($value);
            }
            $result[$key] = $value;
        }

        return $result;
    }
}


<?php

declare(strict_types=1);

namespace PHPFluent\ArrayStorage\Filter;

use InvalidArgumentException;

class OneOf implements Filter
{
    /**
     * @var Filter[]
     */
    protected $filters = [];

    /**
     * @param Filter[] $filters
     */
    public function __construct(array $filters)
    {
        foreach ($filters as $filter) {
            if (!$filter instanceof Filter) {
                throw new InvalidArgumentException('Filter is not valid');
            }
            $this->filters[] = $filter;
        }
    }

    /**
     * @return Filter[]
     */
    public function getFilters(): array
    {
        return $this->filters;
    }

    /**
     * {@inheritdoc}
     */
    public function isValid($input): bool
    {
        foreach ($this->getFilters() as $filter) {
            if (!$filter->isValid($input)) {
                continue;
            }

            return true;
        }

        return false;
    }
}


<?php

declare(strict_types=1);

namespace PHPFluent\ArrayStorage\Converter;

use DOMDocument;
use DOMElement;
use ReflectionObject;
use stdClass;
use Traversable;
use function is_array;
use function is_int;
use function is_object;

class Xml implements Converter
{
    /**
     * @var DOMDocument
     */
    protected $document;

    public function __construct(?DOMDocument $document = null)
    {
        if ($document === null) {
            $document = new DOMDocument('1.0', 'UTF-8');
            $document->formatOutput = true;
        }

        $this->document = $document;
    }

    /**
     * @param mixed $value
     */
    protected function getNodeName($value): string
    {
        if (is_object($value)) {
            return (new ReflectionObject($value))->getShortName();
        }

        return 'value';
    }

    /**
     * @param Traversable|mixed[] $traversable
     */
    protected function parseNode($traversable, DOMElement $parentNode): void
    {
        foreach ($traversable as $key => $value) {
            $nodeName = is_int($key) ? $this->getNodeName($value) : $key;
            $childNode = $this->document->createElement($nodeName);
            $parentNode->appendChild($childNode);
            if ($value instanceof Traversable
                || $value instanceof stdClass
                || is_array($value)) {
                $this->parseNode($value, $childNode);
                continue;
            }
            $childNode->nodeValue = $value;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function convert(Traversable $traversable)
    {
        $name = $this->getNodeName($traversable);
        $childNode = $this->document->createElement($name);
        $this->document->appendChild($childNode);
        $this->parseNode($traversable, $childNode);

        return $this->document->saveXml();
    }
}


<?php

declare(strict_types=1);

namespace PHPFluent\ArrayStorage\Converter;

use Traversable;
use function json_encode;
use const JSON_PRETTY_PRINT;

class Json implements Converter
{
    /**
     * @var int
     */
    protected $jsonEncodeFlags;

    public function __construct(int $jsonEncodeFlags = JSON_PRETTY_PRINT)
    {
        $this->jsonEncodeFlags = $jsonEncodeFlags;
    }

    /**
     * {@inheritdoc}
     */
    public function convert(Traversable $traversable)
    {
        return json_encode((new Arr(true))->convert($traversable), $this->jsonEncodeFlags);
    }
}


<?php

declare(strict_types=1);

namespace PHPFluent\ArrayStorage\Filter;

use function class_exists;
use function settype;

class EqualTo implements Filter
{
    public const NORMAL = 'normal';
    public const IDENTICAL = 'identical';

    /**
     * @var mixed
     */
    protected $expected;

    /**
     * @var string
     */
    protected $precision;

    /**
     * @param mixed $expected
     */
    public function __construct($expected, string $precision = self::NORMAL)
    {
        $this->expected = $expected;
        $this->precision = $precision;
    }

    /**
     * @param mixed $input
     *
     * @return mixed
     */
    protected function cast($input)
    {
        if ($input instanceof $this->precision) {
            return $input;
        }

        if (class_exists($this->precision)) {
            return new $this->precision($input);
        }

        settype($input, $this->precision);

        return $input;
    }

    /**
     * {@inheritdoc}
     */
    public function isValid($actual): bool
    {
        if ($this->precision == self::NORMAL) {
            return $this->expected == $actual;
        }

        if ($this->precision == self::IDENTICAL) {
            return $this->expected === $actual;
        }

        return $this->cast($this->expected) == $this->cast($actual);
    }
}


<?php

declare(strict_types=1);

namespace EventSauce\EventSourcing;

use Generator;

/**
 * @see AggregateRoot
 */
trait AggregateRootBehaviour
{
    /**
     * @var AggregateRootId
     */
    private $aggregateRootId;

    /**
     * @var int
     */
    private $aggregateRootVersion = 0;

    /**
     * @var object[]
     */
    private $recordedEvents = [];

    public function __construct(AggregateRootId $aggregateRootId)
    {
        $this->aggregateRootId = $aggregateRootId;
    }

    public function aggregateRootId(): AggregateRootId
    {
        return $this->aggregateRootId;
    }

    public function aggregateRootVersion(): int
    {
        return $this->aggregateRootVersion;
    }

    protected function apply(object $event)
    {
        $parts = explode('\\', get_class($event));
        $this->{'apply' . end($parts)}($event);
        $this->aggregateRootVersion++;
    }

    protected function recordThat(object $event)
    {
        $this->apply($event);
        $this->recordedEvents[] = $event;
    }

    /**
     * @return object[]
     */
    public function releaseEvents(): array
    {
        $releasedEvents = $this->recordedEvents;
        $this->recordedEvents = [];

        return $releasedEvents;
    }

    /**
     * @param AggregateRootId $aggregateRootId
     * @param Generator       $events
     *
     * @return AggregateRoot
     * @see AggregateRoot::reconstituteFromEvents
     */
    public static function reconstituteFromEvents(AggregateRootId $aggregateRootId, Generator $events): AggregateRoot
    {
        /** @var AggregateRoot&AggregateRootBehaviour $aggregateRoot */
        $aggregateRoot = new static($aggregateRootId);

        /** @var object $event */
        foreach ($events as $event) {
            $aggregateRoot->apply($event);
        }

        return $aggregateRoot;
    }
}


<?php

declare(strict_types=1);

namespace EventSauce\EventSourcing\CodeGeneration;

use LogicException;
use const null;
use function array_filter;
use function sprintf;
use function ucfirst;
use function var_export;

class CodeDumper
{
    /**
     * @var DefinitionGroup
     */
    private $definitionGroup;

    public function dump(DefinitionGroup $definitionGroup, bool $withHelpers = true, bool $withSerialization = true): string
    {
        $this->definitionGroup = $definitionGroup;
        $eventsCode = $this->dumpEvents($definitionGroup->events(), $withHelpers, $withSerialization);
        $commandCode = $this->dumpCommands($definitionGroup->commands());
        $namespace = $definitionGroup->namespace();
        $allCode = implode("\n\n", array_filter([$eventsCode, $commandCode]));

        if ($withSerialization) {
            $namespace .= ";

use EventSauce\EventSourcing\Serialization\SerializableEvent";
        }

        return <<<EOF
<?php

namespace $namespace;

$allCode

EOF;
    }

    private function dumpEvents(array $events, bool $withHelpers, bool $withSerialization): string
    {
        $code = [];

        if (empty($events)) {
            return '';
        }

        foreach ($events as $event) {
            $name = $event->name();
            $fields = $this->dumpFields($event);
            $constructor = $this->dumpConstructor($event);
            $methods = $this->dumpMethods($event);
            $deserializer = $this->dumpSerializationMethods($event);
            $testHelpers = $withHelpers ? $this->dumpTestHelpers($event) : '';
            $implements = $withSerialization ? ' implements SerializableEvent' : '';

            $code[] = <<<EOF
final class $name$implements
{
$fields$constructor$methods$deserializer

$testHelpers}

EOF;
        }

        return rtrim(implode('', $code));
    }

    private function dumpFields(DefinitionWithFields $definition): string
    {
        $fields = $this->fieldsFromDefinition($definition);
        $code = [];
        $code[] = <<<EOF

EOF;

        foreach ($fields as $field) {
            $name = $field['name'];
            $type = $this->definitionGroup->resolveTypeAlias($field['type']);

            $code[] = <<<EOF
    /**
     * @var $type
     */
    private \$$name;

EOF;
        }

        return implode('', $code);
    }

    private function dumpConstructor(DefinitionWithFields $definition): string
    {
        $arguments = [];
        $assignments = [];
        $fields = $this->fieldsFromDefinition($definition);

        if (empty($fields)) {
            return '';
        }

        foreach ($fields as $field) {
            $resolvedType = $this->definitionGroup->resolveTypeAlias($field['type']);
            $arguments[] = sprintf('        %s $%s', $resolvedType, $field['name']);
            $assignments[] = sprintf('        $this->%s = $%s;', $field['name'], $field['name']);
        }

        $arguments = implode(",\n", $arguments);
        $assignments = implode("\n", $assignments);

        return <<<EOF
    public function __construct(
$arguments
    ) {
$assignments
    }

EOF;
    }

    private function dumpMethods(DefinitionWithFields $command): string
    {
        $methods = [];

        foreach ($this->fieldsFromDefinition($command) as $field) {
            $methods[] = <<<EOF
    public function {$field['name']}(): {$this->definitionGroup->resolveTypeAlias($field['type'])}
    {
        return \$this->{$field['name']};
    }

EOF;
        }

        return empty($methods) ? '' : rtrim(implode('', $methods)) . "\n";
    }

    private function dumpSerializationMethods(EventDefinition $event)
    {
        $name = $event->name();
        $arguments = [];
        $serializers = [];

        foreach ($this->fieldsFromDefinition($event) as $field) {
            $type = $this->definitionGroup->resolveTypeAlias($field['type']);
            $parameter = sprintf('$payload[\'%s\']', $field['name']);
            $template = $event->deserializerForField($field['name'])
                ?: $event->deserializerForType($field['type']);
            $arguments[] = trim(strtr($template, ['{type}' => $type, '{param}' => $parameter]));

            $property = sprintf('$this->%s', $field['name']);
            $template = $event->serializerForField($field['name'])
                ?: $event->serializerForType($field['type']);
            $template = sprintf("'%s' => %s", $field['name'], $template);
            $serializers[] = trim(strtr($template, ['{type}' => $type, '{param}' => $property]));
        }

        $arguments = preg_replace('/^.{2,}$/m', '            $0', implode(",\n", $arguments));

        if ( ! empty($arguments)) {
            $arguments = "\n$arguments";
        }

        $serializers = preg_replace('/^.{2,}$/m', '            $0', implode(",\n", $serializers));

        if ( ! empty($serializers)) {
            $serializers = "\n$serializers,\n        ";
        }

        return <<<EOF
    public static function fromPayload(array \$payload): SerializableEvent
    {
        return new $name($arguments);
    }

    public function toPayload(): array
    {
        return [$serializers];
    }
EOF;
    }

    private function dumpTestHelpers(EventDefinition $event): string
    {
        $constructor = [];
        $constructorArguments = '';
        $constructorValues = [];
        $helpers = [];

        foreach ($this->fieldsFromDefinition($event) as $field) {
            $resolvedType = $this->definitionGroup->resolveTypeAlias($field['type']);

            if (null === $field['example']) {
                $constructor[] = ucfirst($field['name']);

                if ('' !== $constructorArguments) {
                    $constructorArguments .= ', ';
                }

                $constructorArguments .= sprintf('%s $%s', $resolvedType, $field['name']);
                $constructorValues[] = sprintf('$%s', $field['name']);
            } else {
                $constructorValues[] = $this->dumpConstructorValue($field, $event);
                $method = sprintf('with%s', ucfirst($field['name']));
                $helpers[] = <<<EOF
    /**
     * @codeCoverageIgnore
     */
    public function $method({$resolvedType} \${$field['name']}): {$event->name()}
    {
        \$this->{$field['name']} = \${$field['name']};

        return \$this;
    }

EOF;
            }
        }

        $constructor = sprintf('with%s', implode('And', $constructor));
        $constructorValues = implode(",\n            ", $constructorValues);

        if ('' !== $constructorValues) {
            $constructorValues = "\n            $constructorValues\n        ";
        }

        $helpers[] = <<<EOF
    /**
     * @codeCoverageIgnore
     */
    public static function $constructor($constructorArguments): {$event->name()}
    {
        return new {$event->name()}($constructorValues);
    }

EOF;

        return rtrim(implode('', $helpers)) . "\n";
    }

    private function dumpConstructorValue(array $field, EventDefinition $event): string
    {
        $parameter = rtrim($field['example']);
        $resolvedType = $this->definitionGroup->resolveTypeAlias($field['type']);

        if (gettype($parameter) === $resolvedType) {
            $parameter = var_export($parameter, true);
        }

        $template = $event->deserializerForField($field['name'])
            ?: $event->deserializerForType($field['type']);

        return rtrim(strtr($template, ['{type}' => $resolvedType, '{param}' => $parameter]));
    }

    /**
     * @param CommandDefinition[] $commands
     *
     * @return string
     */
    private function dumpCommands(array $commands): string
    {
        $code = [];

        foreach ($commands as $command) {
            $code[] = <<<EOF
final class {$command->name()}
{
{$this->dumpFields($command)}{$this->dumpConstructor($command)}{$this->dumpMethods($command)}}

EOF;
        }

        return rtrim(implode('', $code));
    }

    /**
     * @param DefinitionWithFields $definition
     *
     * @return array
     */
    private function fieldsFromDefinition(DefinitionWithFields $definition): array
    {
        $fields = $this->fieldsFrom($definition->fieldsFrom());

        foreach ($definition->fields() as $field) {
            array_push($fields, $field);
        }

        return $fields;
    }

    private function fieldsFrom(string $fieldsFrom): array
    {
        if (empty($fieldsFrom)) {
            return [];
        }

        foreach ($this->definitionGroup->events() as $event) {
            if ($event->name() === $fieldsFrom) {
                return $event->fields();
            }
        }

        foreach ($this->definitionGroup->commands() as $command) {
            if ($command->name() === $fieldsFrom) {
                return $command->fields();
            }
        }

        throw new LogicException("Could not inherit fields from {$fieldsFrom}.");
    }
}


<?php

namespace Spatie\String\Integrations;

use Spatie\String\Str;

class Underscore
{
    protected $underscoreMethods =
        [
            //name, firstArgumentIsString, returnsAString
            'accord'        => [false, true],
            'random'        => [false, true],
            'quickRandom'   => [false, true],
            'randomStrings' => [false, true],
            'endsWith'      => [true, false],
            'isIp'          => [true, false],
            'isEmail'       => [true, false],
            'isUrl'         => [true, false],
            'startsWith'    => [true, false],
            'find'          => [true, false],
            'slice'         => [true, false],
            'sliceFrom'     => [true, true],
            'sliceTo'       => [true, true],
            'baseClass'     => [true, true],
            'prepend'       => [true, true],
            'append'        => [true, true],
            'limit'         => [true, true],
            'remove'        => [true, true],
            'replace'       => [true, true],
            'toggle'        => [true, true],
            'slugify'       => [true, true],
            'explode'       => [true, false],
            'lower'         => [true, true],
            'plural'        => [true, true],
            'singular'      => [true, true],
            'upper'         => [true, true],
            'title'         => [true, true],
            'words'         => [true, true],
            'toPascalCase'  => [true, true],
            'toSnakeCase'   => [true, true],
            'toCamelCase'   => [true, true],
        ];

    /**
     * @param \Spatie\String\Str $string
     * @param string             $method
     * @param array              $args
     *
     * @return mixed|\Spatie\String\Str
     */
    public function call($string, $method, $args)
    {
        if ($this->methodUsesStringAsFirstArgument($method)) {
            array_unshift($args, (string) $string);
        }

        $underscoreResult = call_user_func_array(['Underscore\Types\Strings', $method], $args);

        if ($this->methodReturnsAString($method)) {
            return new Str($underscoreResult);
        }

        return $underscoreResult;
    }

    /**
     * Determine if the given method is supported.
     *
     * @param $method
     *
     * @return bool
     */
    public function isSupportedMethod($method)
    {
        return array_key_exists($method, $this->underscoreMethods);
    }

    /**
     * Determine if the given method uses the string as it's first argument.
     *
     * @param $method
     *
     * @return bool
     */
    public function methodUsesStringAsFirstArgument($method)
    {
        return $this->isSupportedMethod($method) ? $this->underscoreMethods[$method][0] : false;
    }

    /**
     * Determine if the given method returns a string.
     *
     * @param $method
     *
     * @return bool
     */
    public function methodReturnsAString($method)
    {
        return $this->isSupportedMethod($method) ? $this->underscoreMethods[$method][1] : false;
    }
}


<?php

namespace Spatie\String;

use ArrayAccess;
use Spatie\String\Integrations\Underscore;
use Spatie\String\Exceptions\UnsetOffsetException;
use Spatie\String\Exceptions\UnknownFunctionException;
use Spatie\String\Exceptions\ErrorCreatingStringException;

/**
 * Magic methods provided by underscore are documented here.
 *
 * @see \Underscore\Methods\StringsMethods
 *
 * @method \Spatie\String\Str accord($count, $many, $one, $zero = null)
 * @method \Spatie\String\Str random($length = 16)
 * @method \Spatie\String\Str quickRandom($length = 16)
 * @method randomStrings($words, $length = 10)
 * @method bool endsWith($needles)
 * @method bool isIp()
 * @method bool isEmail()
 * @method bool isUrl()
 * @method bool startsWith()
 * @method bool find($needle, $caseSensitive = false, $absolute = false)
 * @method array slice($slice)
 * @method \Spatie\String\Str sliceFrom($slice)
 * @method \Spatie\String\Str sliceTo($slice)
 * @method \Spatie\String\Str baseClass()
 * @method \Spatie\String\Str prepend($with)
 * @method \Spatie\String\Str append($with)
 * @method \Spatie\String\Str limit($limit = 100, $end = '...')
 * @method \Spatie\String\Str remove($remove)
 * @method \Spatie\String\Str replace($replace, $with)
 * @method \Spatie\String\Str toggle($first, $second, $loose = false)
 * @method \Spatie\String\Str slugify($separator = '-')
 * @method array explode($with, $limit = null)
 * @method \Spatie\String\Str lower()
 * @method \Spatie\String\Str plural()
 * @method \Spatie\String\Str singular()
 * @method \Spatie\String\Str upper()
 * @method \Spatie\String\Str title()
 * @method \Spatie\String\Str words($words = 100, $end = '...')
 * @method \Spatie\String\Str toPascalCase()
 * @method \Spatie\String\Str toSnakeCase()
 * @method \Spatie\String\Str toCamelCase()
 */
class Str implements ArrayAccess
{
    /**
     * @var string
     */
    protected $string;

    /**
     * @param string $string
     */
    public function __construct($string = '')
    {
        if (is_array($string)) {
            throw new ErrorCreatingStringException('Can\'t create string from an array');
        }

        if (is_object($string) && ! method_exists($string, '__toString')) {
            throw new ErrorCreatingStringException(
                'Can\'t create string from an object that doesn\'t implement __toString'
            );
        }

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

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

    /**
     * Get the string between the given start and end.
     *
     * @param $start
     * @param $end
     *
     * @return \Spatie\String\Str
     */
    public function between($start, $end)
    {
        if ($start == '' && $end == '') {
            return $this;
        }

        if ($start != '' && strpos($this->string, $start) === false) {
            return new static();
        }

        if ($end != '' && strpos($this->string, $end) === false) {
            return new static();
        }

        if ($start == '') {
            return new static(substr($this->string, 0, strpos($this->string, $end)));
        }

        if ($end == '') {
            return new static(substr($this->string, strpos($this->string, $start) + strlen($start)));
        }

        $stringWithoutStart = explode($start, $this->string)[1];

        $middle = explode($end, $stringWithoutStart)[0];

        return new static($middle);
    }

    /**
     * Convert the string to uppercase.
     *
     * @return \Spatie\String\Str
     */
    public function toUpper()
    {
        return new static(strtoupper($this->string));
    }

    /**
     * Convert the string to lowercase.
     *
     * @return \Spatie\String\Str
     */
    public function toLower()
    {
        return new static(strtolower($this->string));
    }

    /**
     * Shortens a string in a pretty way. It will clean it by trimming
     * it, remove all double spaces and html. If the string is then still
     * longer than the specified $length it will be shortened. The end
     * of the string is always a full word concatinated with the
     * specified moreTextIndicator.
     *
     * @param int    $length
     * @param string $moreTextIndicator
     *
     * @return \Spatie\String\Str
     */
    public function tease($length = 200, $moreTextIndicator = '...')
    {
        $sanitizedString = $this->sanitizeForTease($this->string);

        if (strlen($sanitizedString) == 0) {
            return new static();
        }

        if (strlen($sanitizedString) <= $length) {
            return new static($sanitizedString);
        }

        $ww = wordwrap($sanitizedString, $length, "\n");
        $shortenedString = substr($ww, 0, strpos($ww, "\n")).$moreTextIndicator;

        return new static($shortenedString);
    }

    /**
     * Sanitize the string for teasing.
     *
     * @param $string
     *
     * @return string
     */
    private function sanitizeForTease($string)
    {
        $string = trim($string);

        //remove html
        $string = strip_tags($string);

        //replace multiple spaces
        $string = preg_replace("/\s+/", ' ', $string);

        return $string;
    }

    /**
     * Replace the first occurrence of a string.
     *
     * @param $search
     * @param $replace
     *
     * @return \Spatie\String\Str
     */
    public function replaceFirst($search, $replace)
    {
        if ($search == '') {
            return $this;
        }

        $position = strpos($this->string, $search);

        if ($position === false) {
            return $this;
        }

        $resultString = substr_replace($this->string, $replace, $position, strlen($search));

        return new static($resultString);
    }

    /**
     * Replace the last occurrence of a string.
     *
     * @param $search
     * @param $replace
     *
     * @return \Spatie\String\Str
     */
    public function replaceLast($search, $replace)
    {
        if ($search == '') {
            return $this;
        }

        $position = strrpos($this->string, $search);

        if ($position === false) {
            return $this;
        }

        $resultString = substr_replace($this->string, $replace, $position, strlen($search));

        return new static($resultString);
    }

    /**
     * Prefix a string.
     *
     * @param $string
     *
     * @return \Spatie\String\Str
     */
    public function prefix($string)
    {
        return new static($string.$this->string);
    }

    /**
     * Suffix a string.
     *
     * @param $string
     *
     * @return \Spatie\String\Str
     */
    public function suffix($string)
    {
        return new static($this->string.$string);
    }

    /**
     * Concatenate a string.
     *
     * @param $string
     *
     * @return \Spatie\String\Str
     */
    public function concat($string)
    {
        return $this->suffix($string);
    }

    /**
     * Get the possessive version of a string.
     *
     * @return \Spatie\String\Str
     */
    public function possessive()
    {
        if ($this->string == '') {
            return new static();
        }

        $noApostropheEdgeCases = ['it'];

        if (in_array($this->string, $noApostropheEdgeCases)) {
            return new static($this->string.'s');
        }

        return new static($this->string.'\''.($this->string[strlen($this->string) - 1] != 's' ? 's' : ''));
    }

    /**
     * Get a segment from a string based on a delimiter.
     * Returns an empty string when the offset doesn't exist.
     * Use a negative index to start counting from the last element.
     *
     * @param string $delimiter
     * @param int    $index
     *
     * @return \Spatie\String\Str
     */
    public function segment($delimiter, $index)
    {
        $segments = explode($delimiter, $this->string);

        if ($index < 0) {
            $segments = array_reverse($segments);
            $index = abs($index) - 1;
        }

        $segment = isset($segments[$index]) ? $segments[$index] : '';

        return new static($segment);
    }

    /**
     * Get the first segment from a string based on a delimiter.
     *
     * @param string $delimiter
     *
     * @return \Spatie\String\Str
     */
    public function firstSegment($delimiter)
    {
        return (new static($this->string))->segment($delimiter, 0);
    }

    /**
     * Get the last segment from a string based on a delimiter.
     *
     * @param string $delimiter
     *
     * @return \Spatie\String\Str
     */
    public function lastSegment($delimiter)
    {
        return (new static($this->string))->segment($delimiter, -1);
    }

    /**
     * Pop (remove) the last segment of a string based on a delimiter.
     *
     * @param string $delimiter
     *
     * @return \Spatie\String\Str
     */
    public function pop($delimiter)
    {
        return (new static($this->string))->replaceLast($delimiter.$this->lastSegment($delimiter), '');
    }

    /**
     * Strip whitespace (or other characters) from the beginning and end of a string.
     *
     * @param string $characterMask
     *
     * @return \Spatie\String\Str
     */
    public function trim($characterMask = " \t\n\r\0\x0B")
    {
        return new static(trim($this->string, $characterMask));
    }

    /**
     * Alias for find.
     *
     * @param array|string $needle
     * @param bool         $caseSensitive
     * @param bool         $absolute
     *
     * @return bool
     */
    public function contains($needle, $caseSensitive = false, $absolute = false)
    {
        return $this->find($needle, $caseSensitive, $absolute);
    }

    /**
     * Unknown methods calls will be handled by various integrations.
     *
     * @param $method
     * @param $args
     *
     * @throws UnknownFunctionException
     *
     * @return mixed|\Spatie\String\Str
     */
    public function __call($method, $args)
    {
        $underscore = new Underscore();

        if ($underscore->isSupportedMethod($method)) {
            return $underscore->call($this, $method, $args);
        }

        throw new UnknownFunctionException(sprintf('String function %s does not exist', $method));
    }

    /**
     * Whether a offset exists.
     *
     * @link http://php.net/manual/en/arrayaccess.offsetexists.php
     *
     * @param mixed $offset An offset to check for.
     *
     * @return bool true on success or false on failure.
     *              The return value will be casted to boolean if non-boolean was returned.
     */
    public function offsetExists($offset)
    {
        return strlen($this->string) >= ($offset + 1);
    }

    /**
     * Offset to retrieve.
     *
     * @link http://php.net/manual/en/arrayaccess.offsetget.php
     *
     * @param mixed $offset The offset to retrieve.
     *
     * @return mixed Can return all value types.
     */
    public function offsetGet($offset)
    {
        $character = substr($this->string, $offset, 1);

        return new static($character ?: '');
    }

    /**
     * Offset to set.
     *
     * @link http://php.net/manual/en/arrayaccess.offsetset.php
     *
     * @param mixed $offset The offset to assign the value to.
     * @param mixed $value  The value to set.
     */
    public function offsetSet($offset, $value)
    {
        $this->string[$offset] = $value;
    }

    /**
     * Offset to unset.
     *
     * @link http://php.net/manual/en/arrayaccess.offsetunset.php
     *
     * @param mixed $offset The offset to unset.
     *
     * @throws UnsetOffsetException
     */
    public function offsetUnset($offset)
    {
        throw new UnsetOffsetException();
    }
}


<?php namespace Softon\Indipay\Gateways;

use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\View;
use Softon\Indipay\Exceptions\IndipayParametersMissingException;

class ZapakPayGateway implements PaymentGatewayInterface {

    protected $parameters = array();
    protected $testMode = false;
    protected $secret = '';
    protected $merchantIdentifier = '';
    protected $checksum = '';
    protected $liveEndPoint = 'https://api.zaakpay.com/api/paymentTransact/V7';
    protected $testEndPoint = 'https://api.zaakpay.com/api/paymentTransact/V7';
    public $response = '';

    function __construct()
    {
        $this->secret = Config::get('indipay.zapakpay.secret');
        $this->merchantIdentifier = Config::get('indipay.zapakpay.merchantIdentifier');
        $this->testMode = Config::get('indipay.testMode');
        $this->parameters['merchantIdentifier'] = $this->merchantIdentifier;
        $this->parameters['returnUrl'] = url(Config::get('indipay.zapakpay.returnUrl'));

    }

    public function getEndPoint()
    {
        return $this->testMode?$this->testEndPoint:$this->liveEndPoint;
    }

    public function request($parameters)
    {
        $this->parameters = array_merge($this->parameters,$parameters);

        $this->checkParameters($this->parameters);

        $encodeString = $this->getAllParams($this->parameters);
        $this->checksum = $this->calculateChecksum($this->secret,$encodeString);

        return $this;

    }

    /**
     * @return mixed
     */
    public function send()
    {

        Log::info('Indipay Payment Request Initiated: ');
        return View::make('indipay::zapakpay')->with('params',$this->parameters)
                             ->with('checksum',$this->checksum)
                             ->with('endPoint',$this->getEndPoint());

    }

    /**
     * Check Response
     * @param $request
     * @return array
     */
    public function response($request)
    {
        $response = $request->all();
        $rcvd_checksum = $request->checksum;

        $rcvd_data = $this->getAllResponseParams($response);
        $checksum_check = $this->verifyChecksum($rcvd_checksum,$rcvd_data,$this->secret);

        if(!$checksum_check){
            return "Recieved Checksum Mismatch.";
        }

        $this->response = $response;

        return $this->response;
    }

    /**
     * @param $parameters
     * @throws IndipayParametersMissingException
     */
    public function checkParameters($parameters)
    {
        $validator = Validator::make($parameters, [
            'merchantIdentifier' => 'required',
            'buyerEmail' => 'required|email',
            'currency' => 'required',
            'orderId' => 'required',
            'amount' => 'required|numeric',
        ]);

        if ($validator->fails()) {
            throw new IndipayParametersMissingException;
        }

    }

    public function calculateChecksum($secret_key, $all)
    {
        $hash = hash_hmac('sha256', $all , $secret_key);
        $checksum = $hash;
        return $checksum;
    }

    public function getAllParams($params) {
        //ksort($_POST);
        $all = '';

        $checksumsequence= array("amount","bankid","buyerAddress",
                "buyerCity","buyerCountry","buyerEmail","buyerFirstName","buyerLastName","buyerPhoneNumber","buyerPincode",
                "buyerState","currency","debitorcredit","merchantIdentifier","merchantIpAddress","mode","orderId",
                "product1Description","product2Description","product3Description","product4Description",
                "productDescription","productInfo","purpose","returnUrl","shipToAddress","shipToCity","shipToCountry",
                "shipToFirstname","shipToLastname","shipToPhoneNumber","shipToPincode","shipToState","showMobile","txnDate",
                "txnType","zpPayOption");

        foreach($checksumsequence as $seqvalue) {
            if(array_key_exists($seqvalue, $params)) {
                if(!$params[$seqvalue]=="")
                {
                    if($seqvalue != 'checksum')
                    {
                            $all .= $seqvalue;
                            $all .="=";
                            if ($seqvalue == 'returnUrl')
                            {
                                $params[$seqvalue] = $this->sanitizedURL($params[$seqvalue]);
                                $all .= $this->sanitizedURL($params[$seqvalue]);
                        }
                            else
                            {
                                $params[$seqvalue] = $this->sanitizedParam($params[$seqvalue]);
                                $all .= $this->sanitizedParam($params[$seqvalue]);
                            }
                        $all .= "&";
                        }
                }

            }
        }

        return $all;
    }

    public function verifyChecksum($checksum, $all, $secret) {
        $cal_checksum = $this->calculateChecksum($secret, $all);
        $bool = 0;
        if($checksum == $cal_checksum)  {
            $bool = 1;
        }

        return $bool;
    }

    public function sanitizedParam($param) {
        $pattern[0] = "%,%";
        $pattern[1] = "%#%";
        $pattern[2] = "%\(%";
        $pattern[3] = "%\)%";
        $pattern[4] = "%\{%";
        $pattern[5] = "%\}%";
        $pattern[6] = "%<%";
        $pattern[7] = "%>%";
        $pattern[8] = "%`%";
        $pattern[9] = "%!%";
        $pattern[10] = "%\\$%";
        $pattern[11] = "%\%%";
        $pattern[12] = "%\^%";
        $pattern[13] = "%=%";
        $pattern[14] = "%\+%";
        $pattern[15] = "%\|%";
        $pattern[16] = "%\\\%";
        $pattern[17] = "%:%";
        $pattern[18] = "%'%";
        $pattern[19] = "%\"%";
        $pattern[20] = "%;%";
        $pattern[21] = "%~%";
        $pattern[22] = "%\[%";
        $pattern[23] = "%\]%";
        $pattern[24] = "%\*%";
        $pattern[25] = "%&%";
        $sanitizedParam = preg_replace($pattern, "", $param);
        return $sanitizedParam;
    }

    public function sanitizedURL($param) {
        $pattern[0] = "%,%";
        $pattern[1] = "%\(%";
        $pattern[2] = "%\)%";
        $pattern[3] = "%\{%";
        $pattern[4] = "%\}%";
        $pattern[5] = "%<%";
        $pattern[6] = "%>%";
        $pattern[7] = "%`%";
        $pattern[8] = "%!%";
        $pattern[9] = "%\\$%";
        $pattern[10] = "%\%%";
        $pattern[11] = "%\^%";
        $pattern[12] = "%\+%";
        $pattern[13] = "%\|%";
        $pattern[14] = "%\\\%";
        $pattern[15] = "%'%";
        $pattern[16] = "%\"%";
        $pattern[17] = "%;%";
        $pattern[18] = "%~%";
        $pattern[19] = "%\[%";
        $pattern[20] = "%\]%";
        $pattern[21] = "%\*%";
        $sanitizedParam = preg_replace($pattern, "", $param);
        return $sanitizedParam;
    }

    public function getAllResponseParams($response) {

        $all = '';
        $checksumsequence= array("amount","bank","bankid",
                "cardId","cardScheme","cardToken","cardhashid","doRedirect",
                "orderId","paymentMethod","paymentMode","responseCode",
                "responseDescription");
        foreach($checksumsequence as $seqvalue) {
            if(array_key_exists($seqvalue, $response)) {

                $all .= $seqvalue;
                $all .="=";
                if ($seqvalue == 'returnUrl') {
                    $all .= $response[$seqvalue];
                } else {
                    $all .= $response[$seqvalue];
                }
                $all .= "&";

            }
        }

        return $all;
    }

}


<?php namespace Softon\Indipay\Gateways;

use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\View;
use Softon\Indipay\Exceptions\IndipayParametersMissingException;

class InstaMojoGateway implements PaymentGatewayInterface {

    protected $parameters = array();
    protected $merchantData = '';
    protected $encRequest = '';
    protected $testMode = false;
    protected $api_key = '';
    protected $auth_token = '';
    protected $liveEndPoint = 'https://www.instamojo.com/api/1.1/';
    protected $testEndPoint = 'https://test.instamojo.com/api/1.1/';
    public $response = '';

    function __construct()
    {
        $this->testMode = Config::get('indipay.testMode');
        $this->api_key = Config::get('indipay.instamojo.api_key');
        $this->auth_token = Config::get('indipay.instamojo.auth_token');

        $this->parameters['redirect_url'] = url(Config::get('indipay.instamojo.redirectUrl'));
    }

    public function getEndPoint($param='')
    {
        $endpoint = $this->testMode?$this->testEndPoint:$this->liveEndPoint;
        return $endpoint.$param;
    }

    public function request($parameters)
    {
        $this->parameters = array_merge($this->parameters,$parameters);

        $this->checkParameters($this->parameters);

        $client = new \GuzzleHttp\Client();
        $response = $client->post($this->getEndPoint('payment-requests/'),
                                        [
                                            'headers'=> array(
                                                'X-Api-Key' => $this->api_key,
                                                'X-Auth-Token' => $this->auth_token,
                                            ),
                                            'form_params' => $this->parameters,
                                        ])->getBody()->getContents();
        $response = json_decode($response);

        if($response->success){
            $this->response = $response;
        }

        return $this;

    }

    /**
     * @return mixed
     */
    public function send()
    {

        Log::info('Indipay Payment Request Initiated: ');
        //dd($this->response->payment_request->longurl);
        return View::make('indipay::instamojo')->with('longurl',$this->response->payment_request->longurl);

    }

    /**
     * Check Response
     * @param $request
     * @return array
     */
    public function response($request)
    {
        $payment_request_id = Request::input('payment_request_id');
        $payment_id = Request::input('payment_id');

        $client = new \GuzzleHttp\Client();
        $response = $client->get($this->getEndPoint('payment-requests/'.$payment_request_id.'/'.$payment_id.'/'),
            [
                'headers'=> array(
                    'X-Api-Key' => $this->api_key,
                    'X-Auth-Token' => $this->auth_token,
                ),
            ])->getBody()->getContents();
        $response = json_decode($response);

        if($response->success){
            return $response;
        }

        return false;

    }

    /**
     * @param $parameters
     * @throws IndipayParametersMissingException
     */
    public function checkParameters($parameters)
    {
        $validator = Validator::make($parameters, [
            'purpose' => 'required|max:30',
            'amount' => 'required|numeric|between:9,200000',
            'buyer_name' => 'max:100',
            'email' => 'email|max:75',
            'phone' => 'digits:10',
        ]);

        if ($validator->fails()) {
            throw new IndipayParametersMissingException;
        }

    }

}


<?php namespace Softon\Indipay\Gateways;

use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\View;
use Softon\Indipay\Exceptions\IndipayParametersMissingException;

class PayUMoneyGateway implements PaymentGatewayInterface {

    protected $parameters = array();
    protected $testMode = false;
    protected $merchantKey = '';
    protected $salt = '';
    protected $hash = '';
    protected $liveEndPoint = 'https://secure.payu.in/_payment';
    protected $testEndPoint = 'https://test.payu.in/_payment';
    public $response = '';

    function __construct()
    {
        $this->merchantKey = Config::get('indipay.payumoney.merchantKey');
        $this->salt = Config::get('indipay.payumoney.salt');
        $this->testMode = Config::get('indipay.testMode');

        $this->parameters['key'] = $this->merchantKey;
        $this->parameters['txnid'] = $this->generateTransactionID();
        $this->parameters['surl'] = url(Config::get('indipay.payumoney.successUrl'));
        $this->parameters['furl'] = url(Config::get('indipay.payumoney.failureUrl'));
        $this->parameters['service_provider'] = 'payu_paisa';
    }

    public function getEndPoint()
    {
        return $this->testMode?$this->testEndPoint:$this->liveEndPoint;
    }

    public function request($parameters)
    {
        $this->parameters = array_merge($this->parameters,$parameters);

        $this->checkParameters($this->parameters);

        $this->encrypt();

        return $this;

    }

    /**
     * @return mixed
     */
    public function send()
    {

        Log::info('Indipay Payment Request Initiated: ');
        return View::make('indipay::payumoney')->with('hash',$this->hash)
                             ->with('parameters',$this->parameters)
                             ->with('endPoint',$this->getEndPoint());

    }

    /**
     * Check Response
     * @param $request
     * @return array
     */
    public function response($request)
    {
        $response = $request->all();

        $response_hash = $this->decrypt($response);

        if($response_hash!=$response['hash']){
            return 'Hash Mismatch Error';
        }

        return $response;
    }

    /**
     * @param $parameters
     * @throws IndipayParametersMissingException
     */
    public function checkParameters($parameters)
    {
        $validator = Validator::make($parameters, [
            'key' => 'required',
            'txnid' => 'required',
            'surl' => 'required|url',
            'furl' => 'required|url',
            'firstname' => 'required',
            'email' => 'required',
            'phone' => 'required',
            'productinfo' => 'required',
            'service_provider' => 'required',
            'amount' => 'required|numeric',
        ]);

        if ($validator->fails()) {
            throw new IndipayParametersMissingException;
        }

    }

    /**
     * PayUMoney Encrypt Function
     *
     */
    protected function encrypt()
    {
        $this->hash = '';
        $hashSequence = "key|txnid|amount|productinfo|firstname|email|udf1|udf2|udf3|udf4|udf5|udf6|udf7|udf8|udf9|udf10";
        $hashVarsSeq = explode('|', $hashSequence);
        $hash_string = '';

        foreach($hashVarsSeq as $hash_var) {
            $hash_string .= isset($this->parameters[$hash_var]) ? $this->parameters[$hash_var] : '';
            $hash_string .= '|';
        }

        $hash_string .= $this->salt;
        $this->hash = strtolower(hash('sha512', $hash_string));
    }

    /**
     * PayUMoney Decrypt Function
     *
     * @param $plainText
     * @param $key
     * @return string
     */
    protected function decrypt($response)
    {

        $hashSequence = "status||||||udf5|udf4|udf3|udf2|udf1|email|firstname|productinfo|amount|txnid|key";
        $hashVarsSeq = explode('|', $hashSequence);
        $hash_string = $this->salt."|";

        foreach($hashVarsSeq as $hash_var) {
            $hash_string .= isset($response[$hash_var]) ? $response[$hash_var] : '';
            $hash_string .= '|';
        }

        $hash_string = trim($hash_string,'|');

        return strtolower(hash('sha512', $hash_string));
    }

    public function generateTransactionID()
    {
        return substr(hash('sha256', mt_rand() . microtime()), 0, 20);
    }

}


<?php namespace Softon\Indipay;

use Illuminate\Support\Facades\Config;
use Illuminate\Support\ServiceProvider;

class IndipayServiceProvider extends ServiceProvider {

	/**
	 * Indicates if loading of the provider is deferred.
	 *
	 * @var bool
	 */
	protected $defer = false;

	/**
	 * Register the service provider.
	 *
	 * @return void
	 */
	public function register()
	{
        $gateway = Config::get('indipay.gateway');
        $this->app->bind('indipay', 'Softon\Indipay\Indipay');

        $this->app->bind('Softon\Indipay\Gateways\PaymentGatewayInterface','Softon\Indipay\Gateways\\'.$gateway.'Gateway');
	}

    public function boot(){
        $this->publishes([
            __DIR__.'/config/config.php' => base_path('config/indipay.php'),
            __DIR__.'/views/middleware.blade.php' => base_path('app/Http/Middleware/VerifyCsrfMiddleware.php'),
        ]);

		$this->loadViewsFrom(__DIR__.'/views', 'indipay');

    }

	/**
	 * Get the services provided by the provider.
	 *
	 * @return array
	 */
	public function provides()
	{
		return [

        ];
	}

}


<?php namespace Softon\Indipay\Gateways;

use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\View;
use Softon\Indipay\Exceptions\IndipayParametersMissingException;

class CCAvenueGateway implements PaymentGatewayInterface {

    protected $parameters = array();
    protected $merchantData = '';
    protected $encRequest = '';
    protected $testMode = false;
    protected $workingKey = '';
    protected $accessCode = '';
    protected $liveEndPoint = 'https://secure.ccavenue.com/transaction/transaction.do?command=initiateTransaction';
    protected $testEndPoint = 'https://test.ccavenue.com/transaction/transaction.do?command=initiateTransaction';
    public $response = '';

    function __construct()
    {
        $this->workingKey = Config::get('indipay.ccavenue.workingKey');
        $this->accessCode = Config::get('indipay.ccavenue.accessCode');
        $this->testMode = Config::get('indipay.testMode');
        $this->parameters['merchant_id'] = Config::get('indipay.ccavenue.merchantId');
        $this->parameters['currency'] = Config::get('indipay.ccavenue.currency');
        $this->parameters['redirect_url'] = url(Config::get('indipay.ccavenue.redirectUrl'));
        $this->parameters['cancel_url'] = url(Config::get('indipay.ccavenue.cancelUrl'));
        $this->parameters['language'] = Config::get('indipay.ccavenue.language');
    }

    public function getEndPoint()
    {
        return $this->testMode?$this->testEndPoint:$this->liveEndPoint;
    }

    public function request($parameters)
    {
        $this->parameters = array_merge($this->parameters,$parameters);

        $this->checkParameters($this->parameters);

        foreach($this->parameters as $key=>$value) {
            $this->merchantData .= $key.'='.$value.'&';
        }

        $this->encRequest = $this->encrypt($this->merchantData,$this->workingKey);

        return $this;

    }

    /**
     * @return mixed
     */
    public function send()
    {

        Log::info('Indipay Payment Request Initiated: ');
        return View::make('indipay::ccavenue')->with('encRequest',$this->encRequest)
                             ->with('accessCode',$this->accessCode)
                             ->with('endPoint',$this->getEndPoint());

    }

    /**
     * Check Response
     * @param $request
     * @return array
     */
    public function response($request)
    {
        $encResponse = $request->encResp;

        $rcvdString = $this->decrypt($encResponse,$this->workingKey);
        parse_str($rcvdString, $decResponse);

        return $decResponse;
    }

    /**
     * @param $parameters
     * @throws IndipayParametersMissingException
     */
    public function checkParameters($parameters)
    {
        $validator = Validator::make($parameters, [
            'merchant_id' => 'required',
            'currency' => 'required',
            'redirect_url' => 'required|url',
            'cancel_url' => 'required|url',
            'language' => 'required',
            'order_id' => 'required',
            'amount' => 'required|numeric',
        ]);

        if ($validator->fails()) {
            throw new IndipayParametersMissingException;
        }

    }

    /**
     * CCAvenue Encrypt Function
     *
     * @param $plainText
     * @param $key
     * @return string
     */
    protected function encrypt($plainText,$key)
    {
        $secretKey = $this->hextobin(md5($key));
        $initVector = pack("C*", 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f);
        $openMode = @mcrypt_module_open(MCRYPT_RIJNDAEL_128, '','cbc', '');
        $blockSize = @mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, 'cbc');
        $plainPad = $this->pkcs5_pad($plainText, $blockSize);
        if (@mcrypt_generic_init($openMode, $secretKey, $initVector) != -1)
        {
            $encryptedText = @mcrypt_generic($openMode, $plainPad);
            @mcrypt_generic_deinit($openMode);

        }
        return bin2hex($encryptedText);
    }

    /**
     * CCAvenue Decrypt Function
     *
     * @param $encryptedText
     * @param $key
     * @return string
     */
    protected function decrypt($encryptedText,$key)
    {
        $secretKey = $this->hextobin(md5($key));
        $initVector = pack("C*", 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f);
        $encryptedText=$this->hextobin($encryptedText);
        $openMode = @mcrypt_module_open(MCRYPT_RIJNDAEL_128, '','cbc', '');
        @mcrypt_generic_init($openMode, $secretKey, $initVector);
        $decryptedText = @mdecrypt_generic($openMode, $encryptedText);
        $decryptedText = rtrim($decryptedText, "\0");
        @mcrypt_generic_deinit($openMode);
        return $decryptedText;

    }

    /**
     * @param $plainText
     * @param $blockSize
     * @return string
     */
    protected function pkcs5_pad($plainText, $blockSize)
    {
        $pad = $blockSize - (strlen($plainText) % $blockSize);
        return $plainText . str_repeat(chr($pad), $pad);
    }

    /**
     * @param $hexString
     * @return string
     */
    protected function hextobin($hexString)
    {
        $length = strlen($hexString);
        $binString="";
        $count=0;
        while($count<$length)
        {
            $subString =substr($hexString,$count,2);
            $packedString = pack("H*",$subString);
            if ($count==0)
            {
                $binString=$packedString;
            }

            else
            {
                $binString.=$packedString;
            }

            $count+=2;
        }
        return $binString;
    }

}


<?php namespace Softon\Indipay\Gateways;

use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\View;
use Softon\Indipay\Exceptions\IndipayParametersMissingException;

class CitrusGateway implements PaymentGatewayInterface {

    protected $parameters = array();
    protected $testMode = false;
    protected $hash = '';
    protected $vanityUrl = '';
    protected $liveEndPoint = 'https://www.citruspay.com/';
    protected $testEndPoint = 'https://sandbox.citruspay.com/';
    public $response = '';

    function __construct()
    {
        $this->vanityUrl = Config::get('indipay.citrus.vanityUrl');
        $this->secretKey = Config::get('indipay.citrus.secretKey');
        $this->testMode = Config::get('indipay.testMode');

        $this->parameters['merchantTxnId'] = $this->generateTransactionID();
        $this->parameters['currency'] = 'INR';
        $this->parameters['returnUrl'] = url(Config::get('indipay.citrus.returnUrl'));
        $this->parameters['notifyUrl'] = url(Config::get('indipay.citrus.notifyUrl'));
    }

    public function getEndPoint()
    {
        return $this->testMode?$this->testEndPoint.$this->vanityUrl:$this->liveEndPoint.$this->vanityUrl;
    }

    public function request($parameters)
    {
        $this->parameters = array_merge($this->parameters,$parameters);

        $this->checkParameters($this->parameters);

        $this->encrypt();

        return $this;

    }

    /**
     * @return mixed
     */
    public function send()
    {
        Log::info('Indipay Payment Request Initiated: ');
        return View::make('indipay::citrus')->with('hash',$this->hash)
                             ->with('parameters',$this->parameters)
                             ->with('endPoint',$this->getEndPoint());

    }

    /**
     * Check Response
     * @param $request
     * @return array
     */
    public function response($request)
    {
        $response = $request->all();

        $response_hash = $this->decrypt($response);

        if($response_hash!=$response['signature']){
            return 'Hash Mismatch Error';
        }

        return $response;
    }

    /**
     * @param $parameters
     * @throws IndipayParametersMissingException
     */
    public function checkParameters($parameters)
    {
        $validator = Validator::make($parameters, [
            'merchantTxnId' => 'required',
            'currency' => 'required',
            'returnUrl' => 'required|url',
            'orderAmount' => 'required|numeric',
        ]);

        if ($validator->fails()) {
            throw new IndipayParametersMissingException;
        }

    }

    /**
     * Citrus Encrypt Function
     *
     */
    protected function encrypt()
    {

        $hash_string = $this->vanityUrl.$this->parameters['orderAmount'].$this->parameters['merchantTxnId'].$this->parameters['currency'];

        $this->hash = hash_hmac('sha1', $hash_string, $this->secretKey);

    }

    /**
     * Citrus Decrypt Function
     *
     * @param $response
     * @return string
     */
    protected function decrypt($response)
    {
        $hash_string = '';
        $hash_string .= $response['TxId'];
        $hash_string .= $response['TxStatus'];
        $hash_string .= $response['amount'];
        $hash_string .= $response['pgTxnNo'];
        $hash_string .= $response['issuerRefNo'];
        $hash_string .= $response['authIdCode'];
        $hash_string .= $response['firstName'];
        $hash_string .= $response['lastName'];
        $hash_string .= $response['pgRespCode'];
        $hash_string .= $response['addressZip'];

        return hash_hmac('sha1', $hash_string, $this->secretKey);

    }

    public function generateTransactionID()
    {
        return substr(hash('sha256', mt_rand() . microtime()), 0, 20);
    }

}


<?php namespace Softon\Indipay\Gateways;

use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\View;
use Softon\Indipay\Exceptions\IndipayParametersMissingException;

class EBSGateway implements PaymentGatewayInterface {

    protected $parameters = array();
    protected $testMode = false;
    protected $merchantKey = '';
    protected $salt = '';
    protected $hash = '';
    protected $endPoint = 'https://secure.ebs.in/pg/ma/payment/request';
    public $response = '';

    function __construct()
    {
        $this->secretKey = Config::get('indipay.ebs.secretKey');
        $this->testMode = Config::get('indipay.testMode');

        $this->parameters['channel'] = 0;       // Standard
        $this->parameters['account_id'] = Config::get('indipay.ebs.account_id');
        $this->parameters['reference_no'] = $this->generateTransactionID();
        $this->parameters['currency'] = 'INR';
        $this->parameters['mode'] = 'LIVE';
        if($this->testMode){
            $this->parameters['mode'] = 'TEST';
        }
        $this->parameters['return_url'] = url(Config::get('indipay.ebs.return_url'));

    }

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

    public function request($parameters)
    {
        $this->parameters = array_merge($this->parameters,$parameters);

        $this->checkParameters($this->parameters);

        $this->encrypt();

        return $this;

    }

    /**
     * @return mixed
     */
    public function send()
    {

        Log::info('Indipay Payment Request Initiated: ');
        return View::make('indipay::ebs')->with('hash',$this->hash)
                             ->with('parameters',$this->parameters)
                             ->with('endPoint',$this->getEndPoint());

    }

    /**
     * Check Response
     * @param $request
     * @return array
     */
    public function response($request)
    {
        $response = $request->all();

        return $response;
    }

    /**
     * @param $parameters
     * @throws IndipayParametersMissingException
     */
    public function checkParameters($parameters)
    {
        $validator = Validator::make($parameters, [
            'channel' => 'required',
            'account_id' => 'required',
            'reference_no' => 'required',
            'mode' => 'required',
            'currency' => 'required',
            'description' => 'required',
            'return_url' => 'required|url',
            'name' => 'required',
            'address' => 'required',
            'city' => 'required',
            'country' => 'required',
            'postal_code' => 'required',
            'phone' => 'required',
            'email' => 'required|email',
            'amount' => 'required|numeric',
        ]);

        if ($validator->fails()) {
            throw new IndipayParametersMissingException;
        }

    }

    /**
     * EBS Encrypt Function
     *
     */
    protected function encrypt()
    {
        $this->hash = '';
        $hash_string = $this->secretKey."|".urlencode($this->parameters['account_id'])."|".urlencode($this->parameters['amount'])."|".urlencode($this->parameters['reference_no'])."|".$this->parameters['return_url']."|".urlencode($this->parameters['mode']);
        $this->hash = md5($hash_string);
    }

    /**
     * EBS Decrypt Function
     *
     * @param $plainText
     * @param $key
     * @return string
     */
    protected function decrypt($response)
    {

        return $response;
    }

    public function generateTransactionID()
    {
        return substr(hash('sha256', mt_rand() . microtime()), 0, 20);
    }

}


<?php

namespace VerbalExpressions\PHPVerbalExpressions;

use VerbalExpressions\PHPVerbalExpressions\Entity\Expression;
use VerbalExpressions\PHPVerbalExpressions\Parser\MethodParser;

/**
 * VerbalExpressionsScenario,
 * based on Verbal Expressions v0.1 (https://github.com/jehna/VerbalExpressions) ported in PHP.
 *
 * Provides a business readable language support for expressions definition.
 *
 * @author Nicola Pietroluongo <nik.longstone@gmail.com>
 */
class VerbalExpressionsScenario extends VerbalExpressions
{

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

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

    /**
     * @var MethodParser
     */
    private $methodParser;

    /**
     * @param string                        $scenario
     * @param string                        $methodDelimiter
     * @param string                        $argumentDelimiter
     * @param null|MethodParserInterface    $methodParser
     *
     * @return VerbalExpressionsScenario
     */
    public function __construct($scenario, $methodDelimiter = ', ', $argumentDelimiter = '"', $methodParser = null)
    {
        if (null == $methodParser || !$methodParser instanceof MethodParserInterface) {
            $this->methodParser = new MethodParser();
        }
        $this->methodDelimiter = $methodDelimiter;
        $this->argumentDelimiter = $argumentDelimiter;
        $parsedScenario = $this->parseScenario($scenario);

        return $this->runScenario($parsedScenario);
    }

    /**
     * @param string $scenarioString
     *
     * @return array
     */
    private function parseScenario($scenarioString)
    {
        $parsedScenario = array();
        $scenarios = explode($this->methodDelimiter, $scenarioString);
        foreach ($scenarios as $scenario) {
            $arguments = null;
            $scenarioElement = explode($this->argumentDelimiter, $scenario);
            if (isset($scenarioElement[1])) {
                $arguments = $scenarioElement[1];
            }
            $parsedScenario[] = new Expression($this->methodParser->parse($scenarioElement[0]), $arguments);
        }

        return $parsedScenario;
    }

    /**
     * @param array $parsedScenario
     *
     * @return VerbalExpressions
     */
    private function runScenario($parsedScenario)
    {
        foreach ($parsedScenario as $expression) {
            call_user_func_array(array($this, $expression->getName()), array($expression->getArguments()));
        }

        return $this;
    }

    /**
     * @param null $range
     *
     * @return VerbalExpressions
     * @throws \InvalidArgumentException
     */
    public function rangeBridge($range = null)
    {
        $range = explode(",", $range);
        call_user_func_array(array($this, 'range'), $range);
    }

    /**
     * @param array $arguments
     */
    public function replaceBridge($arguments)
    {
        call_user_func(array($this, 'replace'), $arguments[0], $arguments[1]);
    }
}


<?php

namespace VerbalExpressions\PHPVerbalExpressions;

/**
 * Verbal Expressions v0.1 (https://github.com/jehna/VerbalExpressions) ported in PHP
 * @author Mihai Ionut Vilcu (ionutvmi@gmail.com)
 * 22.July.2013
 */

class VerbalExpressions
{
    public $prefixes     = '';
    public $source       = '';
    public $suffixes     = '';
    public $modifiers    = 'm'; // default to global multi line matching
    public $replaceLimit = 1;   // the limit of preg_replace when g modifier is not set
    protected $lastAdded = false; // holds the last added regex

    /**
     * Sanitize
     *
     * Sanitation function for adding anything safely to the expression
     *
     * @access public
     * @param  string $value the to be added
     * @return string escaped value
     */
    public static function sanitize($value)
    {
        return $value ? preg_quote($value, '/') : $value;
    }

    /**
     * Add
     *
     * Add stuff to the expression
     *
     * @access public
     * @param  string $value the stuff to be added
     * @return VerbalExpressions
     */
    public function add($value)
    {
        $this->source .= $this->lastAdded = $value;

        return $this;
    }

    /**
     * Start of Line
     *
     * Mark the expression to start at the beginning of the line.
     *
     * @access public
     * @param  boolean $enable Enables or disables the line starting. Default value: true
     * @return VerbalExpressions
     */
    public function startOfLine($enable = true)
    {
        $this->prefixes = $enable ? '^' : '';

        return $this;
    }

    /**
     * End of line
     *
     * Mark the expression to end at the last character of the line.
     *
     * @access public
     * @param  boolean $enable Enables or disables the line ending. Default value: true
     * @return VerbalExpressions
     */
    public function endOfLine($enable = true)
    {
        $this->suffixes = $enable ? '$' : '';

        return $this;
    }

    /**
     * Then
     *
     * Add a string to the expression
     *
     * @access public
     * @param  string $value The string to be looked for
     * @return VerbalExpressions
     */
    public function then($value)
    {
        return $this->add('(?:' . self::sanitize($value) . ')');
    }

    /**
     * Find
     *
     * Alias for then()
     *
     * @access public
     * @param  string $value The string to be looked for
     * @return VerbalExpressions
     */
    public function find($value)
    {
        return $this->then($value);
    }

    /**
     * Maybe
     *
     * Add a string to the expression that might appear once (or not).
     *
     * @access public
     * @param  string $value The string to be looked for
     * @return VerbalExpressions
     */
    public function maybe($value)
    {
        return $this->add('(?:' . self::sanitize($value) . ')?');
    }

    /**
     * Anything
     *
     * Accept any string
     *
     * @access public
     * @return VerbalExpressions
     */
    public function anything()
    {
        return $this->add('(?:.*)');
    }

    /**
     * AnythingBut
     *
     * Anything but this chars
     *
     * @access public
     * @param  string $value The unaccepted chars
     * @return VerbalExpressions
     */
    public function anythingBut($value)
    {
        return $this->add('(?:[^' . self::sanitize($value) . ']*)');
    }

    /**
     * Something
     *
     * Accept any non-empty string
     *
     * @access public
     * @return VerbalExpressions
     */
    public function something()
    {
        return $this->add('(?:.+)');
    }

    /**
     * Anything but
     *
     * Anything non-empty except for these chars
     *
     * @access public
     * @param  string $value The unaccepted chars
     * @return VerbalExpressions
     */
    public function somethingBut($value)
    {
        return $this->add('(?:[^' . self::sanitize($value) . ']+)');
    }

    /**
     * Preg Replace
     *
     * Shorthand for preg_replace()
     *
     * @access public
     * @param  string $source the string that will be affected(subject)
     * @param  string $value  the replacement
     * @return VerbalExpressions
     */
    public function replace($source, $value)
    {
        // php doesn't have g modifier so we remove it if it's there and we remove limit param
        if (strpos($this->modifiers, 'g') !== false) {
            $this->modifiers = str_replace('g', '', $this->modifiers);

            return preg_replace($this->getRegex(), $value, $source);
        }

        return preg_replace($this->getRegex(), $value, $source, $this->replaceLimit);
    }

    /**
     * Line break
     *
     * Match line break
     *
     * @access public
     * @return VerbalExpressions
     */
    public function lineBreak()
    {
        return $this->add('(?:\\n|(\\r\\n))');
    }

    /**
     * Line break
     *
     * Shorthand for lineBreak
     *
     * @access public
     * @return VerbalExpressions
     */
    public function br()
    {
        return $this->lineBreak();
    }

    /**
     * Tabs
     *
     * Match tabs.
     *
     * @access public
     * @return VerbalExpressions
     */
    public function tab()
    {
        return $this->add('\\t');
    }

    /**
     * Alpha Numeric
     *
     * Match any alpha numeric
     *
     * @access public
     * @return VerbalExpressions
     */
    public function word()
    {
        return $this->add('\\w+');
    }

    /**
     * Digit
     *
     * Match any digit
     *
     * @access public
     * @return VerbalExpressions
     */
    public function digit()
    {
        return $this->add('\\d');
    }

    /**
     * List Chars
     *
     * Any of the listed chars
     *
     * @access public
     * @param  string $value The chars looked for
     * @return VerbalExpressions
     */
    public function anyOf($value)
    {
        return $this->add('[' . $value . ']');
    }

    /**
     * Alias
     *
     * Shorthand for anyOf
     *
     * @access public
     * @param  string $value The chars looked for
     * @return VerbalExpressions
     */
    public function any($value)
    {
        return $this->anyOf($value);
    }

    /**
     * Add a range
     *
     * Adds a range to our expression ex: range(a,z) => a-z, range(a,z,0,9) => a-z0-9
     *
     * @access public
     * @return VerbalExpressions
     * @throws \InvalidArgumentException
     */
    public function range()
    {
        $args = func_get_args();
        $arg_num = count($args);

        if ($arg_num%2 != 0) {
            throw new \InvalidArgumentException('Number of args must be even', 1);
        }

        $value = '[';

        for ($i = 0; $i < $arg_num;) {
            $value .= self::sanitize($args[$i++]) . '-' . self::sanitize($args[$i++]);
        }

        $value .= ']';

        return $this->add($value);
    }

    /**
     * Add a modifier
     *
     * Adds a modifier
     *
     * @access public
     * @param  string $modifier
     * @return VerbalExpressions
     */
    public function addModifier($modifier)
    {
        if (strpos($this->modifiers, $modifier) === false) {
            $this->modifiers .= $modifier;
        }

        return $this;
    }

    /**
     * Remove Modifier
     *
     * Removes a modifier
     *
     * @access public
     * @param  string $modifier
     * @return VerbalExpressions
     */
    public function removeModifier($modifier)
    {
        $this->modifiers = str_replace($modifier, '', $modifier);

        return $this;
    }

    /**
     * Case Sensitivity
     *
     * Match case insensitive or sensitive based on $enable value
     *
     * @access public
     * @param  boolean $enable Enables or disables case sensitive. Default true
     * @return VerbalExpressions
     */
    public function withAnyCase($enable = true)
    {
        return $enable ? $this->addModifier('i') : $this->removeModifier('i');
    }

    /**
     * Stop At First
     *
     * Toggles g modifier
     *
     * @access public
     * @param  boolean $enable Enables or disables g modifier. Default true
     * @return VerbalExpressions
     */
    public function stopAtFirst($enable = true)
    {
        return $enable ? $this->addModifier('g') : $this->removeModifier('g');
    }

    /**
     * SearchOneLine
     *
     * Toggles m modifier
     *
     * @access public
     * @param  boolean $enable Enables or disables m modifier. Default true
     * @return VerbalExpressions
     */
    public function searchOneLine($enable = true)
    {
        return $enable ? $this->addModifier('m') : $this->removeModifier('m');
    }

    /**
     * Multiple
     *
     * Adds the multiple modifier at the end of your expression
     *
     * @access public
     * @param  string $value Your expression
     * @return VerbalExpressions
     */
    public function multiple($value)
    {
        $value = self::sanitize($value);

        $last = substr($value, -1);

        if ($last !== '+' && $last !== '*') {
            $value .= '+';
        }

        return $this->add($value);
    }

    /**
     * OR
     *
     * Wraps the current expression in an `or` with $value
     * Notice: OR is a reserved keyword in PHP, so this method is prefixed with "_"
     *
     * @access public
     * @param  string $value new expression
     * @return VerbalExpressions
     */
    public function _or($value)
    {
        if (strpos($this->prefixes, '(') === false) {
            $this->prefixes .= '(?:';
        }

        if (strpos($this->suffixes, ')') === false) {
            $this->suffixes .= ')';
        }

        $this->add(')|(?:');

        if ($value) {
            $this->add($value);
        }

        return $this;
    }

    /**
     * Object to string
     *
     * PHP Magic method to return a string representation of the object.
     *
     * @access public
     * @return string
     */
    public function __toString()
    {
        return $this->getRegex();
    }

    /**
     * Get the Expression
     *
     * Creates the final regex.
     *
     * @access public
     * @return string The final regex
     */
    public function getRegex()
    {
        return '/' . $this->prefixes . $this->source . $this->suffixes . '/' . $this->modifiers;
    }

    /**
     * Test
     *
     * Tests the match of a string to the current regex
     *
     * @access public
     * @param  string  $value The string to be tested
     * @return boolean true if it's a match
     */
    public function test($value)
    {
        // php doesn't have g modifier so we remove it if it's there and call preg_match_all()
        if (strpos($this->modifiers, 'g') !== false) {
            $this->modifiers = str_replace('g', '', $this->modifiers);
            $matches = array();//because it's not optional in <5.4
            return preg_match_all($this->getRegex(), $value, $matches);
        }

        return (bool) preg_match($this->getRegex(), $value);
    }

    /**
     * Clean
     *
     * Deletes the current regex for a fresh start
     *
     * @access public
     * @param  array $options
     * @return VerbalExpressions
     */
    public function clean(array $options = array())
    {
        $options            = array_merge(
            array(
                'prefixes'      => '',
                'source'        => '',
                'suffixes'      => '',
                'modifiers'     => 'gm',
                'replaceLimit'  => '1'
            ),
            $options
        );
        $this->prefixes     = $options['prefixes'];
        $this->source       = $options['source'];
        $this->suffixes     = $options['suffixes'];
        $this->modifiers    = $options['modifiers'];    // default to global multi line matching
        $this->replaceLimit = $options['replaceLimit']; // default to global multi line matching

        return $this;
    }

    /**
     * Limit
     *
     * Adds char limit to the last added expression.
     * If $max is less then $min the limit will be: At least $min chars {$min,}
     * If $max is 0 the limit will be: exactly $min chars {$min}
     * If $max bigger then $min the limit will be: at least $min but not more then $max {$min, $max}
     *
     * @access public
     * @param integer $min
     * @param integer $max
     * @return VerbalExpressions
     */
    public function limit($min, $max = 0)
    {
        if ($max == 0) {
            $value = '{' . $min . '}';
        } elseif ($max < $min) {
            $value = '{' . $min . ',}';
        } else {
            $value = '{' . $min . ',' . $max . '}';
        }

        // check if the expression has * or + for the last expression
        if (preg_match('/\*|\+/', $this->lastAdded)) {
            $l = 1;
            $this->source = strrev(str_replace(array('+', '*'), strrev($value), strrev($this->source), $l));

            return $this;
        }

        return $this->add($value);
    }
}


<?php

declare(strict_types = 1);

namespace iio\libmergepdf;

/**
 * Parse page numbers from string
 */
final class Pages implements PagesInterface
{
    /**
     * @var int[] Added integer page numbers
     */
    private $pages = [];

    /**
     * Parse page numbers from expression string
     *
     * Pages should be formatted as 1,3,6 or 12-16 or combined. Note that pages
     * are merged in the order that you provide them. If you put pages 12-14
     * before 1-5 then 12-14 will be placed first.
     */
    public function __construct(string $expressionString = '')
    {
        $expressions = explode(
            ',',
            str_replace(' ', '', $expressionString)
        );

        foreach ($expressions as $expr) {
            if (empty($expr)) {
                continue;
            }
            if (ctype_digit($expr)) {
                $this->addPage((int)$expr);
                continue;
            }
            if (preg_match("/^(\d+)-(\d+)/", $expr, $matches)) {
                $this->addRange((int)$matches[1], (int)$matches[2]);
                continue;
            }
            throw new Exception("Invalid page number(s) for expression '$expr'");
        }
    }

    /**
     * Add a single page
     */
    public function addPage(int $page): void
    {
        $this->pages[] = $page;
    }

    /**
     * Add a range of pages
     */
    public function addRange(int $start, int $end): void
    {
        $this->pages = array_merge($this->pages, range($start, $end));
    }

    public function getPageNumbers(): array
    {
        return $this->pages;
    }
}


<?php

declare(strict_types = 1);

namespace iio\libmergepdf;

use iio\libmergepdf\Driver\DriverInterface;
use iio\libmergepdf\Driver\DefaultDriver;
use iio\libmergepdf\Source\SourceInterface;
use iio\libmergepdf\Source\FileSource;
use iio\libmergepdf\Source\RawSource;

/**
 * Merge existing pdfs into one
 *
 * Note that your PDFs are merged in the order that you add them
 */
final class Merger
{
    /**
     * @var SourceInterface[] List of pdf sources to merge
     */
    private $sources = [];

    /**
     * @var DriverInterface
     */
    private $driver;

    public function __construct(DriverInterface $driver = null)
    {
        $this->driver = $driver ?: new DefaultDriver;
    }

    /**
     * Add raw PDF from string
     */
    public function addRaw(string $content, PagesInterface $pages = null): void
    {
        $this->sources[] = new RawSource($content, $pages);
    }

    /**
     * Add PDF from file
     */
    public function addFile(string $filename, PagesInterface $pages = null): void
    {
        $this->sources[] = new FileSource($filename, $pages);
    }

    /**
     * Add files using iterator
     *
     * Note that optional pages constraint is used for every added pdf
     */
    public function addIterator(iterable $iterator, PagesInterface $pages = null): void
    {
        foreach ($iterator as $filename) {
            $this->addFile($filename, $pages);
        }
    }

    /**
     * Merges loaded PDFs
     */
    public function merge(): string
    {
        return $this->driver->merge(...$this->sources);
    }

    /**
     * Reset internal state
     */
    public function reset(): void
    {
        $this->sources = [];
    }
}


<?php

namespace GeoJson\CoordinateReferenceSystem;

use GeoJson\Exception\UnserializationException;

/**
 * Linked coordinate reference system object.
 *
 * @see http://www.geojson.org/geojson-spec.html#linked-crs
 * @since 1.0
 */
class Linked extends CoordinateReferenceSystem
{
    protected $type = 'link';

    /**
     * Constructor.
     *
     * @param string $href
     * @param string $type
     */
    public function __construct($href, $type = null)
    {
        $this->properties = array('href' => (string) $href);

        if (isset($type)) {
            $this->properties['type'] = (string) $type;
        }
    }

    /**
     * Factory method for creating a Linked CRS object from properties.
     *
     * @see CoordinateReferenceSystem::jsonUnserialize()
     * @param array|object $properties
     * @return Linked
     * @throws UnserializationException
     */
    protected static function jsonUnserializeFromProperties($properties)
    {
        if ( ! is_array($properties) && ! is_object($properties)) {
            throw UnserializationException::invalidProperty('Linked CRS', 'properties', $properties, 'array or object');
        }

        $properties = new \ArrayObject($properties);

        if ( ! $properties->offsetExists('href')) {
            throw UnserializationException::missingProperty('Linked CRS', 'properties.href', 'string');
        }

        $href = (string) $properties['href'];
        $type = isset($properties['type']) ? (string) $properties['type'] : null;

        return new self($href, $type);
    }
}