<?php
/*
* This file is part of the job-bundle package.
*
* (c) Hannes Schulz <hannes.schulz@aboutcoders.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Abc\Bundle\JobBundle\Job;

use Abc\Bundle\JobBundle\Job\Context\ContextInterface;
use Abc\Bundle\JobBundle\Job\ProcessControl\Factory;
use Abc\ProcessControl\ControllerAwareInterface;
use Psr\Log\LoggerAwareInterface;

/**
 * Invokes a job.
 *
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class Invoker
{
    /**
     * @var JobTypeRegistry
     */
    private $registry;

    /**
     * @var ManagerInterface
     */
    private $manager;

    /**
     * @var Factory
     */
    private $controllerFactory;

    /**
     * @param JobTypeRegistry $registry
     */
    function __construct(JobTypeRegistry $registry)
    {
        $this->registry = $registry;
    }

    /**
     * Register the manager that is passed to jobs implementing the ManagerAwareInterface
     *
     * @param ManagerInterface $manager
     * @return void
     * @see ManagerAwareInterface
     */
    public function setManager(ManagerInterface $manager)
    {
        $this->manager = $manager;
    }

    /**
     * Register the process controller that is passed to jobs implementing the ControllerAwareInterface
     *
     * @param Factory $controllerFactory
     */
    public function setControllerFactory(Factory $controllerFactory)
    {
        $this->controllerFactory = $controllerFactory;
    }

    /**
     * Invokes the job.
     *
     * @param JobInterface     $job
     * @param ContextInterface $context
     * @return mixed
     * @throws JobTypeNotFoundException
     */
    public function invoke(JobInterface $job, ContextInterface $context)
    {
        $jobType       = $this->registry->get($job->getType());
        $callableArray = $jobType->getCallable();
        $parameters    = static::resolveParameters($jobType, $context, $job->getParameters());

        if (is_array($callableArray) && $callable = $callableArray[0]) {
            if ($callable instanceof JobAwareInterface) {
                $callable->setJob($job);
            }

            if ($callable instanceof ManagerAwareInterface) {
                $callable->setManager($this->manager);
            }

            if ($callable instanceof ControllerAwareInterface) {
                $callable->setController($this->controllerFactory->create($job));
            }

            if ($callable instanceof LoggerAwareInterface && $context->has('abc.logger')) {
                $callable->setLogger($context->get('abc.logger'));
            }
        }

        return call_user_func_array($callableArray, $parameters);
    }

    /**
     * @param JobTypeInterface $jobType
     * @param ContextInterface $context
     * @param array|null       $parameters
     * @return array
     */
    public static function resolveParameters(JobTypeInterface $jobType, ContextInterface $context, $parameters = null)
    {
        $result     = array();
        $parameters = $parameters == null ? array() : $parameters;
        foreach ($jobType->getParameterTypes() as $parameterType) {
            if (0 === strpos($parameterType, '@')) {
                $key      = substr($parameterType, 1);
                $result[] = $context->has($key) ? $context->get($key) : null;
            } else {
                $result[] = array_shift($parameters);
            }
        }

        return $result;
    }
}


<?php
/*
* This file is part of the job-bundle package.
*
* (c) Hannes Schulz <hannes.schulz@aboutcoders.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Abc\Bundle\JobBundle\Job\ProcessControl;

use Abc\Bundle\JobBundle\Job\JobInterface;
use Abc\Bundle\JobBundle\Model\JobManagerInterface;
use Abc\ProcessControl\ChainController;
use Abc\ProcessControl\ControllerInterface;

/**
 * The default factory.
 *
 * This factory creates process controllers of type Controller
 *
 * @see    Controller
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class Factory implements FactoryInterface
{
    /**
     * @var JobManagerInterface
     */
    protected $manager;

    /**
     * @var int
     */
    protected $interval;

    /**
     * @var array ControllerInterface[]
     */
    protected $additionalController = [];

    /**
     * @param JobManagerInterface $manager
     * @param int                 $interval The minimum number of seconds that must have been passed between two refresh operations
     */
    public function __construct(JobManagerInterface $manager, $interval)
    {
        $this->manager  = $manager;
        $this->interval = $interval;
    }

    /**
     * Adds an additional controller.
     *
     * If additional controllers are registered an instance of ChainController is created
     * containing the all the additional controller plus the one which is created by this
     * factory by default.
     *
     * @param ControllerInterface $controller
     * @return void
     */
    public function addController(ControllerInterface $controller)
    {
        $this->additionalController[] = $controller;
    }

    /**
     * {@inheritdoc}
     */
    public function create(JobInterface $job)
    {
        $controller = new StatusController($job, $this->manager, $this->interval);
        if (count($this->additionalController) > 0) {
            $controller = new ChainController(array_merge($this->additionalController, [$controller]));
        }

        return new JobController($controller, $this->manager, $job);
    }
}


<?php
/*
* This file is part of the job-bundle package.
*
* (c) Hannes Schulz <hannes.schulz@aboutcoders.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Abc\Bundle\JobBundle\Job\Mailer;

use Abc\Bundle\JobBundle\Job\JobAwareInterface;
use Abc\Bundle\JobBundle\Annotation\ParamType;
use Abc\Bundle\JobBundle\Job\JobInterface;
use Psr\Log\LoggerInterface;

/**
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class Mailer implements JobAwareInterface
{
    /**
     * @var JobInterface
     */
    private $job;

    /**
     * @var \Swift_Mailer
     */
    private $mailer;

    /**
     * @param \Swift_Mailer $mailer
     */
    public function __construct(\Swift_Mailer $mailer)
    {
        $this->mailer = $mailer;
    }

    /**
     * {@inheritdoc}
     */
    public function setJob(JobInterface $job)
    {
        $this->job = $job;
    }

    /**
     * Sends a mail.
     *
     * @ParamType("message", type="Abc\Bundle\JobBundle\Job\Mailer\Message")
     * @ParamType("logger", type="@abc.logger")
     *
     * @param Message         $message
     * @param LoggerInterface $logger
     * @throws \Exception Rethrows exceptions thrown by mailer
     */
    public function send(Message $message, LoggerInterface $logger)
    {
        $logger->debug('Send mail {message}', array('message' => $message));

        $mail = $this->mailer->createMessage()
            ->setSubject($message->getSubject())
            ->setFrom($message->getFrom())
            ->setTo($message->getTo());

        $mail->addPart($message->getMessage(), 'text/plain');

        try {
            $this->mailer->send($mail);

            $this->mailer->getTransport()->stop();
        } catch (\Exception $e) {
            $this->mailer->getTransport()->stop();

            throw $e;
        }
    }
}


<?php
/*
* This file is part of the job-bundle package.
*
* (c) Hannes Schulz <hannes.schulz@aboutcoders.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Abc\Bundle\JobBundle\Adapter\Bernard;

use Abc\Bundle\JobBundle\Job\JobTypeRegistry;
use Abc\Bundle\JobBundle\Job\ManagerInterface;
use Abc\Bundle\JobBundle\Job\Queue\Message;
use Abc\Bundle\JobBundle\Job\Queue\ProducerInterface;
use Bernard\Message\PlainMessage;
use Bernard\Producer;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;

/**
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class ProducerAdapter implements ProducerInterface
{
    /**
     * @var Producer
     */
    private $producer;

    /**
     * @var JobTypeRegistry
     */
    private $registry;

    /**
     * @var ManagerInterface
     */
    private $manager;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @param Producer         $producer
     * @param JobTypeRegistry  $registry
     * @param LoggerInterface  $logger
     */
    public function __construct(Producer $producer, JobTypeRegistry $registry, LoggerInterface $logger = null)
    {
        $this->producer = $producer;
        $this->registry = $registry;
        $this->logger   = $logger == null ? new NullLogger() : $logger;
    }

    /**
     * {@inheritdoc}
     */
    public function setManager(ManagerInterface $manager)
    {
        $this->manager = $manager;
    }

    /**
     * {@inheritdoc}
     */
    public function produce(Message $message)
    {
        $producerMessage = new PlainMessage('ConsumeJob', [
            'type' => $message->getType(),
            'ticket' => $message->getTicket()
        ]);

        $this->logger->debug('Publish message to bernard queue backend', ['message' => $message]);

        $this->producer->produce($producerMessage, $this->registry->get($message->getType())->getQueue());
    }

    /**
     * Dispatches messages to the job manager.
     *
     * This method is registered as the message handler for messages with name "ConsumeJob".
     *
     * @param PlainMessage $message
     * @throws \Abc\Bundle\JobBundle\Job\Exception\TicketNotFoundException
     */
    public function consumeJob(PlainMessage $message){

        $ticket = $message->ticket;
        $type = $message->type;

        $this->logger->debug('Consume message from bernard backend', [
            'message' => $message
        ]);

        $this->manager->onMessage(new Message($type, $ticket));
    }
}


<?php
/*
* This file is part of the job-bundle package.
*
* (c) Hannes Schulz <hannes.schulz@aboutcoders.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Abc\Bundle\JobBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class RegisterConstraintProvidersPass implements CompilerPassInterface
{
    /**
     * @var string
     */
    private $validator;

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

    /**
     * @param string $validator Service name of the definition registry in processed container
     * @param string $tag       The tag name used for jobs
     */
    public function __construct($validator = 'abc.job.validator.parameters', $tag = 'abc.job.constraint_provider')
    {
        $this->validator = $validator;
        $this->tag       = $tag;
    }

    /**
     * {@inheritdoc}
     */
    public function process(ContainerBuilder $container)
    {
        if (!$container->hasDefinition($this->validator) && !$container->hasAlias($this->validator)) {
            return;
        }

        $validator = $container->findDefinition($this->validator);
        foreach ($container->findTaggedServiceIds($this->tag) as $id => $tags) {
            $definition = $container->getDefinition($id);

            foreach ($tags as $tag) {
                $validator->addMethodCall('register', array($definition));
            }
        }
    }
}


<?php
/*
* This file is part of the job-bundle package.
*
* (c) Hannes Schulz <hannes.schulz@aboutcoders.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Abc\Bundle\JobBundle\Logger\Manager;

use Abc\Bundle\JobBundle\Job\JobInterface;
use Abc\Bundle\JobBundle\Job\LogManagerInterface;
use Symfony\Component\Filesystem\Filesystem;

/**
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class FileLogManager implements LogManagerInterface
{
    /**
     * @var string
     */
    protected $directory;

    /**
     * @param string $directory Path to a directory where log files for jobs are stored
     * @throws \InvalidArgumentException If $directory does not specify a path to a writable directory
     * @throws \InvalidArgumentException If $processors contains elements that are not a callable
     */
    public function __construct($directory)
    {
        if (!is_string($directory) || !is_dir($directory) || !is_writable($directory)) {
            throw new \InvalidArgumentException('$directory must be a string specifying the path to a writable directory');
        }

        $this->directory = $directory;
    }

    /**
     * {@inheritdoc}
     */
    public function findByJob(JobInterface $job)
    {
        $path = $this->buildPath($job->getTicket());
        if(!file_exists($path)) {
            return [];
        }

        $content = file_get_contents($path);
        if(strlen($content) == 0) {
            return [];
        }

        $records = [];
        $lines = explode("/n", $content);
        foreach ($lines as $line) {
            $record = json_decode($line, true);
            if(false === $record) {
                throw new \RuntimeException('Failed to deserialize logs from file ' . $path);
            }
            if(null !== $record) {
                $records[] = $record;
            }
        }

        return $records;
    }

    /**Tests/
     * {@inheritdoc}
     */
    public function deleteByJob(JobInterface $job)
    {
        $path = $this->buildPath($job->getTicket());

        $filesystem = new Filesystem();
        if ($filesystem->exists($path)) {
            $filesystem->remove($path);
        }
    }

    /**
     * @param string $filename
     * @return string Path to the file
     */
    private function buildPath($filename)
    {
        return $this->directory . DIRECTORY_SEPARATOR . $filename . '.json';
    }
}


<?php
/*
* This file is part of the job-bundle package.
*
* (c) Hannes Schulz <hannes.schulz@aboutcoders.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Abc\Bundle\JobBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

/**
 * Registers the service abc.job.producer as listener for all messages dispatched from the sonata backend.
 *
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class RegisterSonataListenersPass implements CompilerPassInterface
{
    /**
     * @var string
     */
    private $queueEngineService;

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

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

    /**
     * @param string $dispatcherService Service name of the sonata event dispatcher in processed container
     * @param string $queueEngineService
     * @param string $jobTag
     */
    public function __construct($dispatcherService = 'sonata.notification.dispatcher', $queueEngineService = 'abc.job.producer', $jobTag = 'abc.job')
    {
        $this->dispatcherService  = $dispatcherService;
        $this->queueEngineService = $queueEngineService;
        $this->jobTag             = $jobTag;
    }

    /**
     * {@inheritdoc}
     */
    public function process(ContainerBuilder $container)
    {
        if(!($container->hasDefinition($this->queueEngineService) || !$container->hasAlias($this->queueEngineService))
            && !($container->hasDefinition($this->dispatcherService) || $container->hasAlias($this->dispatcherService))
        )
        {
            return;
        }

        $dispatcher = $container->getDefinition($this->dispatcherService);

        foreach($container->findTaggedServiceIds($this->jobTag) as $id => $tags)
        {
            foreach($tags as $tag)
            {
                // workaround
                $dispatcher->addMethodCall(
                    'addListener',
                    array(
                        $tag['type'],
                        array(new ServiceClosureArgument(new Reference($this->queueEngineService)), 'process')
                    )
                );
            }
        }
    }
}


<?php
/*
* This file is part of the job-bundle package.
*
* (c) Hannes Schulz <hannes.schulz@aboutcoders.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Abc\Bundle\JobBundle\Model;

use JMS\Serializer\Annotation\Type;

/**
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class Log implements LogInterface
{
    /**
     * @Type("string")
     * @var string
     */
    protected $channel;

    /**
     * @Type("integer")
     * @var integer
     */
    protected $level;

    /**
     * @Type("string")
     * @var string
     */
    protected $levelName;

    /**
     * @Type("string")
     * @var string
     */
    protected $message;

    /**
     * @Type("datetime")
     * @var \DateTime
     */
    protected $datetime;

    /**
     * @Type("array")
     * @var array
     */
    protected $context;

    /**
     * @Type("array")
     * @var array
     */
    protected $extra;

    public function __construct()
    {
        $this->context = [];
        $this->extra = [];
    }

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

    /**
     * {@inheritdoc}
     */
    public function setChannel($channel)
    {
        $this->channel = $channel;
    }

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

    /**
     * {@inheritdoc}
     */
    public function setLevel($level)
    {
        $this->level = $level;
    }

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

    /**
     * {@inheritdoc}
     */
    public function setLevelName($levelName)
    {
        $this->levelName = $levelName;
    }

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

    /**
     * {@inheritdoc}
     */
    public function setMessage($message)
    {
        $this->message = $message;
    }

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

    /**
     * {@inheritdoc}
     */
    public function setDatetime($datetime)
    {
        $this->datetime = $datetime;
    }

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

    /**
     * {@inheritdoc}
     */
    public function setContext($context)
    {
        $this->context = $context;
    }

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

    /**
     * {@inheritdoc}
     */
    public function setExtra($extra)
    {
        $this->extra = $extra;
    }

    /**
     * {@inheritdoc}
     */
    public function toRecord()
    {
        return [
            'channel' => $this->getChannel(),
            'level' => $this->getLevel(),
            'level_name' => $this->getLevelName(),
            'message' => $this->getMessage(),
            'datetime' => $this->getDatetime(),
            'context' => $this->getContext(),
            'extra' => $this->getExtra(),
        ];
    }
}


<?php

declare(strict_types=1);

namespace Chubbyphp\DoctrineDbServiceProvider\ServiceProvider;

use Chubbyphp\DoctrineDbServiceProvider\Driver\ClassMapDriver;
use Chubbyphp\DoctrineDbServiceProvider\Registry\DoctrineOrmManagerRegistry;
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain;
use Doctrine\Common\Persistence\Mapping\Driver\PHPDriver;
use Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver;
use Doctrine\ORM\Cache\CacheConfiguration;
use Doctrine\ORM\Cache\DefaultCacheFactory;
use Doctrine\ORM\Cache\RegionsConfiguration;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\Mapping\DefaultEntityListenerResolver;
use Doctrine\ORM\Mapping\DefaultNamingStrategy;
use Doctrine\ORM\Mapping\DefaultQuoteStrategy;
use Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver;
use Doctrine\ORM\Mapping\Driver\SimplifiedYamlDriver;
use Doctrine\ORM\Mapping\Driver\XmlDriver;
use Doctrine\ORM\Mapping\Driver\YamlDriver;
use Doctrine\ORM\Repository\DefaultRepositoryFactory;
use Pimple\Container;
use Pimple\ServiceProviderInterface;

/**
 * This provider is heavily inspired by
 * https://github.com/dflydev/dflydev-doctrine-orm-service-provider/blob/master/src/Dflydev/Provider/DoctrineOrm/DoctrineOrmServiceProvider.php.
 */
final class DoctrineOrmServiceProvider implements ServiceProviderInterface
{
    /**
     * Register ORM service.
     *
     * @param Container $container
     */
    public function register(Container $container)
    {
        $container['doctrine.orm.em'] = $this->getOrmEmDefinition($container);
        $container['doctrine.orm.em.config'] = $this->getOrmEmConfigDefinition($container);
        $container['doctrine.orm.em.default_options'] = $this->getOrmEmDefaultOptions();
        $container['doctrine.orm.ems'] = $this->getOrmEmsDefinition($container);
        $container['doctrine.orm.ems.config'] = $this->getOrmEmsConfigServiceProvider($container);
        $container['doctrine.orm.ems.options.initializer'] = $this->getOrmEmsOptionsInitializerDefinition($container);
        $container['doctrine.orm.entity.listener_resolver.default'] = $this->getOrmEntityListenerResolverDefinition($container);
        $container['doctrine.orm.manager_registry'] = $this->getOrmManagerRegistryDefintion($container);
        $container['doctrine.orm.mapping_driver.factory.annotation'] = $this->getOrmMappingDriverFactoryAnnotation($container);
        $container['doctrine.orm.mapping_driver.factory.class_map'] = $this->getOrmMappingDriverFactoryClassMap($container);
        $container['doctrine.orm.mapping_driver.factory.php'] = $this->getOrmMappingDriverFactoryPhp($container);
        $container['doctrine.orm.mapping_driver.factory.simple_xml'] = $this->getOrmMappingDriverFactorySimpleXml($container);
        $container['doctrine.orm.mapping_driver.factory.simple_yaml'] = $this->getOrmMappingDriverFactorySimpleYaml($container);
        $container['doctrine.orm.mapping_driver.factory.static_php'] = $this->getOrmMappingDriverFactoryStaticPhp($container);
        $container['doctrine.orm.mapping_driver.factory.xml'] = $this->getOrmMappingDriverFactoryXml($container);
        $container['doctrine.orm.mapping_driver.factory.yaml'] = $this->getOrmMappingDriverFactoryYaml($container);
        $container['doctrine.orm.mapping_driver_chain'] = $this->getOrmMappingDriverChainDefinition($container);
        $container['doctrine.orm.repository.factory.default'] = $this->getOrmRepositoryFactoryDefinition($container);
        $container['doctrine.orm.strategy.naming.default'] = $this->getOrmNamingStrategyDefinition($container);
        $container['doctrine.orm.strategy.quote.default'] = $this->getOrmQuoteStrategyDefinition($container);
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmEmDefinition(Container $container): callable
    {
        return function () use ($container) {
            $ems = $container['doctrine.orm.ems'];

            return $ems[$container['doctrine.orm.ems.default']];
        };
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmEmConfigDefinition(Container $container): callable
    {
        return function () use ($container) {
            $configs = $container['doctrine.orm.ems.config'];

            return $configs[$container['doctrine.orm.ems.default']];
        };
    }

    /**
     * @return array
     */
    private function getOrmEmDefaultOptions(): array
    {
        return [
            'cache.hydration' => ['type' => 'array'],
            'cache.metadata' => ['type' => 'array'],
            'cache.query' => ['type' => 'array'],
            'class_metadata.factory.name' => ClassMetadataFactory::class,
            'connection' => 'default',
            'custom.functions.datetime' => [],
            'custom.functions.numeric' => [],
            'custom.functions.string' => [],
            'custom.hydration_modes' => [],
            'entity.listener_resolver' => 'default',
            'mappings' => [],
            'proxies.auto_generate' => true,
            'proxies.dir' => sys_get_temp_dir().'/doctrine/orm/proxies',
            'proxies.namespace' => 'DoctrineProxy',
            'query_hints' => [],
            'repository.default.class' => EntityRepository::class,
            'repository.factory' => 'default',
            'second_level_cache' => ['type' => 'array'],
            'second_level_cache.enabled' => false,
            'strategy.naming' => 'default',
            'strategy.quote' => 'default',
        ];
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmEmsDefinition(Container $container): callable
    {
        return function () use ($container) {
            $container['doctrine.orm.ems.options.initializer']();

            $ems = new Container();
            foreach ($container['doctrine.orm.ems.options'] as $name => $options) {
                if ($container['doctrine.orm.ems.default'] === $name) {
                    $config = $container['doctrine.orm.em.config'];
                } else {
                    $config = $container['doctrine.orm.ems.config'][$name];
                }

                $ems[$name] = function () use ($container, $options, $config) {
                    return EntityManager::create(
                        $container['doctrine.dbal.dbs'][$options['connection']],
                        $config,
                        $container['doctrine.dbal.dbs.event_manager'][$options['connection']]
                    );
                };
            }

            return $ems;
        };
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmEmsConfigServiceProvider(Container $container): callable
    {
        return function () use ($container) {
            $container['doctrine.orm.ems.options.initializer']();

            $configs = new Container();
            foreach ($container['doctrine.orm.ems.options'] as $name => $options) {
                $connectionName = $options['connection'];

                $config = new Configuration();

                $config->setSQLLogger($container['doctrine.dbal.dbs.config'][$connectionName]->getSQLLogger());

                $config->setQueryCacheImpl($this->getCache($container, $options['cache.query']));
                $config->setHydrationCacheImpl($this->getCache($container, $options['cache.hydration']));
                $config->setMetadataCacheImpl($this->getCache($container, $options['cache.metadata']));

                $config->setResultCacheImpl(
                    $container['doctrine.dbal.dbs.config'][$connectionName]->getResultCacheImpl()
                );

                $config->setClassMetadataFactoryName($options['class_metadata.factory.name']);

                $config->setCustomDatetimeFunctions($options['custom.functions.datetime']);
                $config->setCustomHydrationModes($options['custom.hydration_modes']);
                $config->setCustomNumericFunctions($options['custom.functions.numeric']);
                $config->setCustomStringFunctions($options['custom.functions.string']);

                $config->setEntityListenerResolver(
                    $container[
                        sprintf('doctrine.orm.entity.listener_resolver.%s', $options['entity.listener_resolver'])
                    ]
                );

                $config->setMetadataDriverImpl(
                    $container['doctrine.orm.mapping_driver_chain']($config, $options['mappings'])
                );

                $config->setAutoGenerateProxyClasses($options['proxies.auto_generate']);
                $config->setProxyDir($options['proxies.dir']);
                $config->setProxyNamespace($options['proxies.namespace']);

                $config->setDefaultQueryHints($options['query_hints']);

                $config->setRepositoryFactory(
                    $container[sprintf('doctrine.orm.repository.factory.%s', $options['repository.factory'])]
                );
                $config->setDefaultRepositoryClassName($options['repository.default.class']);

                $this->assignSecondLevelCache($container, $config, $options);

                $config->setNamingStrategy(
                    $container[sprintf('doctrine.orm.strategy.naming.%s', $options['strategy.naming'])]
                );
                $config->setQuoteStrategy(
                    $container[sprintf('doctrine.orm.strategy.quote.%s', $options['strategy.quote'])]
                );

                $configs[$name] = $config;
            }

            return $configs;
        };
    }

    /**
     * @param Container    $container
     * @param string|array $cacheDefinition
     *
     * @return Cache
     */
    private function getCache(Container $container, $cacheDefinition): Cache
    {
        $cacheType = $cacheDefinition['type'];
        $cacheOptions = $cacheDefinition['options'] ?? [];

        $cacheFactory = $container[sprintf('doctrine.dbal.db.cache_factory.%s', $cacheType)];

        return $cacheFactory($cacheOptions);
    }

    /**
     * @param Container     $container
     * @param Configuration $config
     * @param array         $options
     */
    private function assignSecondLevelCache(Container $container, Configuration $config, array $options)
    {
        if (!$options['second_level_cache.enabled']) {
            $config->setSecondLevelCacheEnabled(false);

            return;
        }

        $regionsCacheConfiguration = new RegionsConfiguration();
        $factory = new DefaultCacheFactory(
            $regionsCacheConfiguration,
            $this->getCache($container, $options['second_level_cache'])
        );

        $cacheConfiguration = new CacheConfiguration();
        $cacheConfiguration->setCacheFactory($factory);
        $cacheConfiguration->setRegionsConfiguration($regionsCacheConfiguration);

        $config->setSecondLevelCacheEnabled(true);
        $config->setSecondLevelCacheConfiguration($cacheConfiguration);
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmEmsOptionsInitializerDefinition(Container $container): callable
    {
        return $container->protect(function () use ($container) {
            static $initialized = false;

            if ($initialized) {
                return;
            }

            $initialized = true;

            if (!isset($container['doctrine.orm.ems.options'])) {
                $container['doctrine.orm.ems.options'] = [
                    'default' => isset($container['doctrine.orm.em.options']) ?
                        $container['doctrine.orm.em.options'] : [],
                ];
            }

            $tmp = $container['doctrine.orm.ems.options'];
            foreach ($tmp as $name => &$options) {
                $options = array_replace($container['doctrine.orm.em.default_options'], $options);

                if (!isset($container['doctrine.orm.ems.default'])) {
                    $container['doctrine.orm.ems.default'] = $name;
                }
            }

            $container['doctrine.orm.ems.options'] = $tmp;
        });
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmEntityListenerResolverDefinition(Container $container): callable
    {
        return function () use ($container) {
            return new DefaultEntityListenerResolver();
        };
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmManagerRegistryDefintion(Container $container): callable
    {
        return function ($container) {
            return new DoctrineOrmManagerRegistry($container);
        };
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmMappingDriverFactoryAnnotation(Container $container): callable
    {
        return $container->protect(function (array $mapping, Configuration $config) {
            return $config->newDefaultAnnotationDriver((array) $mapping['path'], false);
        });
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmMappingDriverFactoryClassMap(Container $container): callable
    {
        return $container->protect(function (array $mapping, Configuration $config) {
            return new ClassMapDriver($mapping['map']);
        });
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmMappingDriverFactoryPhp(Container $container): callable
    {
        return $container->protect(function (array $mapping, Configuration $config) {
            return new PHPDriver($mapping['path']);
        });
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmMappingDriverFactorySimpleYaml(Container $container): callable
    {
        return $container->protect(function (array $mapping, Configuration $config) {
            return new SimplifiedYamlDriver(
                [$mapping['path'] => $mapping['namespace']],
                $mapping['extension'] ?? SimplifiedYamlDriver::DEFAULT_FILE_EXTENSION
            );
        });
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmMappingDriverFactorySimpleXml(Container $container): callable
    {
        return $container->protect(function (array $mapping, Configuration $config) {
            return new SimplifiedXmlDriver(
                [$mapping['path'] => $mapping['namespace']],
                $mapping['extension'] ?? SimplifiedXmlDriver::DEFAULT_FILE_EXTENSION
            );
        });
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmMappingDriverFactoryStaticPhp(Container $container): callable
    {
        return $container->protect(function (array $mapping, Configuration $config) {
            return new StaticPHPDriver($mapping['path']);
        });
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmMappingDriverFactoryYaml(Container $container): callable
    {
        return $container->protect(function (array $mapping, Configuration $config) {
            return new YamlDriver($mapping['path'], $mapping['extension'] ?? YamlDriver::DEFAULT_FILE_EXTENSION);
        });
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmMappingDriverFactoryXml(Container $container): callable
    {
        return $container->protect(function (array $mapping, Configuration $config) {
            return new XmlDriver($mapping['path'], $mapping['extension'] ?? XmlDriver::DEFAULT_FILE_EXTENSION);
        });
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmMappingDriverChainDefinition(Container $container): callable
    {
        return $container->protect(function (Configuration $config, array $mappings) use ($container) {
            $chain = new MappingDriverChain();
            foreach ($mappings as $mapping) {
                if (isset($mapping['alias'])) {
                    $config->addEntityNamespace($mapping['alias'], $mapping['namespace']);
                }

                $factoryKey = sprintf('doctrine.orm.mapping_driver.factory.%s', $mapping['type']);

                $chain->addDriver($container[$factoryKey]($mapping, $config), $mapping['namespace']);
            }

            return $chain;
        });
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmRepositoryFactoryDefinition(Container $container): callable
    {
        return function () use ($container) {
            return new DefaultRepositoryFactory();
        };
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmNamingStrategyDefinition(Container $container): callable
    {
        return function () use ($container) {
            return new DefaultNamingStrategy();
        };
    }

    /**
     * @param Container $container
     *
     * @return callable
     */
    private function getOrmQuoteStrategyDefinition(Container $container): callable
    {
        return function () use ($container) {
            return new DefaultQuoteStrategy();
        };
    }
}


<?php
/*
* This file is part of the job-bundle package.
*
* (c) Hannes Schulz <hannes.schulz@aboutcoders.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Abc\Bundle\JobBundle\Job\Metadata;

use \Metadata\ClassMetadata as BaseClassMetadata;

/**
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class ClassMetadata extends BaseClassMetadata
{
    /**
     * @var array
     */
    protected $parameterNames = array();

    /**
     * @var array
     */
    protected $parameterTypes = array();

    /**
     * @var array
     */
    protected $parameterOptions = array();

    /**
     * @var array
     */
    protected $returnTypes = array();

    /**
     * @var array
     */
    protected $returnOptions = array();

    /**
     * Returns whether a method has ben added.
     *
     * @param string $name
     * @return bool Whether a method has ben added
     */
    public function hasMethod($name)
    {
        return isset($this->parameterNames[$name]);
    }

    /**
     * @param string $method The method name
     * @param array  $names  The name of the parameters as defined in the method signature
     */
    public function addMethod($method, array $names)
    {
        $this->parameterNames[$method]   = $names;
        $this->parameterTypes[$method]   = array();
        $this->parameterOptions[$method] = array();

        foreach ($names as $name) {
            $this->parameterTypes[$method][$name]   = null;
            $this->parameterOptions[$method][$name] = array();
        }
    }

    /**
     * Sets the type of a parameter.
     *
     * @param string $method The name of the method
     * @param string $name   The name of the parameter
     * @param string $type   The type of the parameter
     * @throws \InvalidArgumentException If the method is not defined
     * @throws \InvalidArgumentException If the method is not defined
     */
    public function setParameterType($method, $name, $type)
    {
        if (!isset($this->parameterTypes[$method])) {
            throw new \InvalidArgumentException(sprintf('A method with name "%s" is not defined', $name, $method));
        }

        if (!array_key_exists($name, $this->parameterTypes[$method])) {
            throw new \InvalidArgumentException(sprintf('A parameter with name "%s" for method "%s" is not defined', $name, $method));
        }

        $this->parameterTypes[$method][$name] = $type;
    }

    /**
     * @param string $method The name of the method
     * @return array The types of the method parameters
     */
    public function getParameterTypes($method)
    {
        if (!isset($this->parameterTypes[$method])) {
            return array();
        }

        $types = array();
        foreach ($this->parameterTypes[$method] as $name => $type) {
            $types[] = $type;
        }

        return $types;
    }

    /**
     * @param string $method  The name of the method
     * @param string $name    The name of the parameter
     * @param array  $options An array of options
     * @throws \InvalidArgumentException If the method is not defined
     * @throws \InvalidArgumentException If the method is not defined
     */
    public function setParameterOptions($method, $name, array $options)
    {
        if (!isset($this->parameterTypes[$method])) {
            throw new \InvalidArgumentException(sprintf('A method with name "%s" is not defined', $method));
        }

        if (!array_key_exists($name, $this->parameterTypes[$method])) {
            throw new \InvalidArgumentException(sprintf('A parameter with name "%s" for method "%s" is not defined', $name, $method));
        }
        $this->parameterOptions[$method][$name] = $options;
    }

    /**
     * @param string $method The name of the method
     * @return array The types of the method parameters
     */
    public function getParameterOptions($method)
    {
        if (!isset($this->parameterOptions[$method])) {
            return array();
        }

        $result = array();
        foreach ($this->parameterOptions[$method] as $name => $options) {
            $result[] = $options;
        }

        return $result;
    }

    /**
     * @param string $method The name of the method
     * @param string $type   The return type
     * @return void
     */
    public function setReturnType($method, $type)
    {
        $this->returnTypes[$method] = $type;
    }

    /**
     * @param string $method The name of the method
     * @return string The return type
     */
    public function getReturnType($method)
    {
        return isset($this->returnTypes[$method]) ? $this->returnTypes[$method] : null;
    }

    /**
     * @param string $method The name of the method
     * @param  array $options
     * @return void
     */
    public function setReturnOptions($method, array $options)
    {
        $this->returnOptions[$method] = $options;
    }

    /**
     * @param string $method The name of the method
     * @return string The return type
     */
    public function getReturnOptions($method)
    {
        return isset($this->returnOptions[$method]) ? $this->returnOptions[$method] : array();
    }
}


<?php

declare(strict_types=1);

namespace Chubbyphp\DoctrineDbServiceProvider\Registry;

use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\Persistence\ObjectRepository;
use Doctrine\Common\Persistence\Proxy;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\ORMException;
use Pimple\Container;

final class DoctrineOrmManagerRegistry implements ManagerRegistry
{
    /**
     * @var Container
     */
    private $container;

    /**
     * @var Container|Connection[]
     */
    private $connections;

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

    /**
     * @var Container|EntityManager[]
     */
    private $originalManagers;

    /**
     * @var EntityManager[]
     */
    private $resetedManagers = [];

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

    /**
     * @param Container $container
     */
    public function __construct(Container $container)
    {
        $this->container = $container;
    }

    /**
     * @return string
     */
    public function getDefaultConnectionName(): string
    {
        $this->loadConnections();

        return $this->defaultConnectionName;
    }

    /**
     * @param string|null $name
     *
     * @return Connection
     *
     * @throws \InvalidArgumentException
     */
    public function getConnection($name = null): Connection
    {
        $this->loadConnections();

        $name = $name ?? $this->getDefaultConnectionName();

        if (!isset($this->connections[$name])) {
            throw new \InvalidArgumentException(sprintf('Missing connection with name "%s".', $name));
        }

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

    /**
     * @return Connection[]
     */
    public function getConnections(): array
    {
        $this->loadConnections();

        $connections = array();
        foreach ($this->connections->keys() as $name) {
            $connections[$name] = $this->connections[$name];
        }

        return $connections;
    }

    /**
     * @return string[]
     */
    public function getConnectionNames(): array
    {
        $this->loadConnections();

        return $this->connections->keys();
    }

    /**
     * @return string
     */
    public function getDefaultManagerName(): string
    {
        $this->loadManagers();

        return $this->defaultManagerName;
    }

    /**
     * @param string|null $name
     *
     * @return EntityManager|ObjectManager
     */
    public function getManager($name = null): ObjectManager
    {
        $this->loadManagers();

        $name = $name ?? $this->getDefaultManagerName();

        if (!isset($this->originalManagers[$name])) {
            throw new \InvalidArgumentException(sprintf('Missing manager with name "%s".', $name));
        }

        if (isset($this->resetedManagers[$name])) {
            return $this->resetedManagers[$name];
        }

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

    /**
     * @return EntityManager[]|ObjectManager[]
     */
    public function getManagers(): array
    {
        $this->loadManagers();

        $managers = array();
        foreach ($this->originalManagers->keys() as $name) {
            if (isset($this->resetedManagers[$name])) {
                $manager = $this->resetedManagers[$name];
            } else {
                $manager = $this->originalManagers[$name];
            }

            $managers[$name] = $manager;
        }

        return $managers;
    }

    /**
     * @return array
     */
    public function getManagerNames(): array
    {
        $this->loadManagers();

        return $this->originalManagers->keys();
    }

    /**
     * @param string|null $name
     *
     * @return EntityManager|ObjectManager
     */
    public function resetManager($name = null)
    {
        $this->loadManagers();

        $name = $name ?? $this->getDefaultManagerName();

        if (!isset($this->originalManagers[$name])) {
            throw new \InvalidArgumentException(sprintf('Missing manager with name "%s".', $name));
        }

        $originalManager = $this->originalManagers[$name];

        $this->resetedManagers[$name] = EntityManager::create(
            $originalManager->getConnection(),
            $originalManager->getConfiguration(),
            $originalManager->getEventManager()
        );

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

    /**
     * @param string $alias
     *
     * @return string
     *
     * @throws ORMException
     */
    public function getAliasNamespace($alias): string
    {
        foreach ($this->getManagerNames() as $name) {
            try {
                return $this->getManager($name)->getConfiguration()->getEntityNamespace($alias);
            } catch (ORMException $e) {
                // throw the exception only if no manager can solve it
            }
        }
        throw ORMException::unknownEntityNamespace($alias);
    }

    /**
     * @param string $persistentObject
     * @param null   $persistentManagerName
     *
     * @return EntityRepository|ObjectRepository
     */
    public function getRepository($persistentObject, $persistentManagerName = null): ObjectRepository
    {
        return $this->getManager($persistentManagerName)->getRepository($persistentObject);
    }

    /**
     * @param string $class
     *
     * @return EntityManager|ObjectManager|null
     */
    public function getManagerForClass($class)
    {
        $reflectionClass = new \ReflectionClass($class);
        if ($reflectionClass->implementsInterface(Proxy::class)) {
            $class = $reflectionClass->getParentClass()->name;
        }

        foreach ($this->getManagerNames() as $name) {
            $manager = $this->getManager($name);
            if (!$manager->getMetadataFactory()->isTransient($class)) {
                return $manager;
            }
        }
    }

    private function loadConnections()
    {
        if (null === $this->connections) {
            $this->connections = $this->container['doctrine.dbal.dbs'];
            $this->defaultConnectionName = $this->container['doctrine.dbal.dbs.default'];
        }
    }

    private function loadManagers()
    {
        if (null === $this->originalManagers) {
            $this->originalManagers = $this->container['doctrine.orm.ems'];
            $this->defaultManagerName = $this->container['doctrine.orm.ems.default'];
        }
    }
}


<?php

declare(strict_types=1);

namespace Chubbyphp\DoctrineDbServiceProvider\Logger;

use Psr\Log\LoggerInterface;
use Doctrine\DBAL\Logging\SQLLogger;

/**
 * @see https://github.com/symfony/doctrine-bridge/blob/master/Logger/DbalLogger.php
 */
final class DoctrineDbalLogger implements SQLLogger
{
    const MAX_STRING_LENGTH = 32;
    const BINARY_DATA_VALUE = '(binary value)';

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @param LoggerInterface $logger
     */
    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

    /**
     * {@inheritdoc}
     */
    public function startQuery($sql, array $params = null, array $types = null)
    {
        if (is_array($params)) {
            foreach ($params as $index => $param) {
                if (!is_string($params[$index])) {
                    continue;
                }

                // non utf-8 strings break json encoding
                if (!preg_match('//u', $params[$index])) {
                    $params[$index] = self::BINARY_DATA_VALUE;
                    continue;
                }

                if (self::MAX_STRING_LENGTH < mb_strlen($params[$index], 'UTF-8')) {
                    $params[$index] = mb_substr($params[$index], 0, self::MAX_STRING_LENGTH - 6, 'UTF-8').' [...]';
                    continue;
                }
            }
        }

        if (null !== $this->logger) {
            $this->log($sql, null === $params ? array() : $params);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function stopQuery()
    {
    }

    /**
     * Logs a message.
     *
     * @param string $message A message to log
     * @param array  $params  The context
     */
    private function log($message, array $params)
    {
        $this->logger->debug($message, $params);
    }
}


<?php
/*
* This file is part of the job-bundle package.
*
* (c) Hannes Schulz <hannes.schulz@aboutcoders.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Abc\Bundle\JobBundle\Job;

use Monolog\Logger;

/**
 * Definition of a job.
 *
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class JobType implements JobTypeInterface
{
    /**
     * @var string
     */
    private $serviceId;

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

    /**
     * @var callable
     */
    private $callable;

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

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

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

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

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

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

    /**
     * @var int
     */
    private $logLevel;

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

    /**
     * @param string            $serviceId The name of the service within the container
     * @param string            $name      The job type
     * @param callback|callable $callable  The callable associated with the type
     * @param int|null          $logLevel  The Monolog\Logger log level
     */
    function __construct($serviceId, $name, $callable, $logLevel = null)
    {
        if (!is_string($name) || strlen((string)$name) == 0) {
            throw new \InvalidArgumentException('$type must be a string');
        }
        if (!is_callable($callable)) {
            throw new \InvalidArgumentException(sprintf('The callable defined for type "%s" is not callable', $name));
        }
        if ($logLevel !== null && !is_int($logLevel)) {
            throw new \InvalidArgumentException('$logLevel must be an integer value');
        }
        if ($logLevel !== null && !in_array($logLevel, Logger::getLevels())) {
            throw new \InvalidArgumentException('$logLevel must be valid Monolog\Logger log level');
        }

        $this->serviceId = $serviceId;
        $this->name      = $name;
        $this->callable  = $callable;
        $this->logLevel  = $logLevel;
        $this->class     = !is_array($this->callable) ? null : get_class($this->callable[0]);
        $this->method    = !is_array($this->callable) ? null : $this->callable[1];
    }

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

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

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

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

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

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

    /**
     * {@inheritdoc}
     */
    public function setLogLevel($logLevel)
    {
        $this->logLevel = $logLevel;
    }

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

    /**
     * {@inheritdoc}
     */
    public function setParameterTypes(array $types)
    {
        $this->parameterTypes = $types;
    }

    /**
     * {@inheritdoc}
     */
    public function getParameterType($index)
    {
        if (!isset($this->parameterTypes[$index])) {
            throw new \InvalidArgumentException('No type defined for parameter #' . $index);
        }

        return $this->parameterTypes[$index];
    }

    /**
     * {@inheritdoc}
     */
    public function getParameterTypeOptions($index = null)
    {
        if (is_int($index) && $index >= 0) {
            return isset($this->parameterTypeOptions[$index]) ? $this->parameterTypeOptions[$index] : array();
        }

        return $this->parameterTypeOptions;
    }

    /**
     * {@inheritdoc}
     */
    public function setParameterTypeOptions(array $options)
    {
        $this->parameterTypeOptions = $options;
    }

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

    /**
     * {@inheritdoc}
     */
    public function setReturnType($type = null)
    {
        $this->returnType = $type;
    }

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

    /**
     * {@inheritdoc}
     */
    public function setReturnTypeOptions(array $options)
    {
        $this->returnTypeOptions = $options;
    }

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

    /**
     * {@inheritdoc}
     */
    public function setQueue($queue)
    {
        $this->queue = $queue;
    }

    /**
     * {@inheritdoc}
     */
    public function getIndicesOfSerializableParameters()
    {
        $indices = [];
        for ($i = 0; $i < count($this->parameterTypes); $i++) {
            if (0 !== strpos($this->parameterTypes[$i], '@')) {
                $indices[] = $i;
            }
        }

        return $indices;
    }
}


<?php
/*
* This file is part of the job-bundle package.
*
* (c) Hannes Schulz <hannes.schulz@aboutcoders.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Abc\Bundle\JobBundle\Adapter\Bernard;

use Abc\Bundle\JobBundle\Job\Queue\ConsumerInterface;
use Bernard\Consumer;
use Bernard\QueueFactory;

/**
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class ConsumerAdapter implements ConsumerInterface
{
    /**
     * @var Consumer
     */
    private $consumer;

    /**
     * @var QueueFactory;
     */
    private $queueFactory;

    /**
     * @param Consumer $consumer
     * @param QueueFactory       $queueFactory
     */
    public function __construct(Consumer $consumer, QueueFactory $queueFactory)
    {
        $this->consumer     = $consumer;
        $this->queueFactory = $queueFactory;
    }

    /**
     * {@inheritdoc}
     */
    public function consume($queue, array $options = [])
    {
        $queue = $this->queueFactory->create($queue);

        $this->consumer->consume($queue, $options);
    }
}


<?php
/*
* This file is part of the job-bundle package.
*
* (c) Hannes Schulz <hannes.schulz@aboutcoders.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Abc\Bundle\JobBundle\Entity;

use Abc\Bundle\JobBundle\Doctrine\LogManager as BaseLogManager;
use Abc\Bundle\JobBundle\Model\LogInterface;
use Doctrine\Common\Proxy\Exception\InvalidArgumentException;
use Doctrine\ORM\EntityManager;

/**
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class LogManager extends BaseLogManager
{
    /** @var EntityManager */
    protected $em;

    /**
     * @param EntityManager       $em
     * @param string              $class
     */
    public function __construct(EntityManager $em, $class)
    {
        parent::__construct($em, $class);

        $this->em = $em;
    }

    /**
     * {@inheritdoc}
     */
    public function save(LogInterface $log, $andFlush = true)
    {
        if(!$log instanceof $this->class)
        {
            throw new InvalidArgumentException('1st argument must be an instanceof '.$this->getClass());
        }

        $extra = $log->getExtra();
        if(is_array($extra) && isset($extra['job_ticket']))
        {
            /** @var \Abc\Bundle\JobBundle\Entity\Log $log */
            $log->setJobTicket($extra['job_ticket']);

            unset($extra['job_ticket']);

            $log->setExtra($extra);
        }

        parent::save($log, $andFlush);
    }
}


<?php

declare(strict_types=1);

namespace Chubbyphp\DoctrineDbServiceProvider\Command;

use Doctrine\Common\Persistence\ConnectionRegistry;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DriverManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputOption;

/**
 * @see https://github.com/doctrine/DoctrineBundle/blob/master/Command/CreateDatabaseDoctrineCommand.php
 */
final class CreateDatabaseDoctrineCommand extends Command
{
    /**
     * @var ConnectionRegistry
     */
    private $connectionRegistry;

    /**
     * @param ConnectionRegistry $connectionRegistry
     */
    public function __construct(ConnectionRegistry $connectionRegistry)
    {
        parent::__construct();

        $this->connectionRegistry = $connectionRegistry;
    }

    protected function configure()
    {
        $this
            ->setName('dbal:database:create')
            ->setDescription('Creates the configured database')
            ->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'The connection to use for this command')
            ->addOption(
                'if-not-exists',
                null,
                InputOption::VALUE_NONE,
                'Don\'t trigger an error, when the database already exists'
            )
            ->setHelp(<<<EOT
The <info>%command.name%</info> command creates the default connections database:

    <info>php %command.full_name%</info>

You can also optionally specify the name of a connection to create the database for:

    <info>php %command.full_name% --connection=default</info>
EOT
            );
    }

    /**
     * @param InputInterface  $input
     * @param OutputInterface $output
     *
     * @return int
     */
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $connectionName = $this->getConnectionName($input);

        $connection = $this->connectionRegistry->getConnection($connectionName);

        $params = $this->getParams($connection);

        $dbName = $this->getDbName($params, $connectionName);

        $isPath = isset($params['path']);

        $ifNotExists = $input->getOption('if-not-exists');

        // Need to get rid of _every_ occurrence of dbname from connection configuration
        unset($params['dbname'], $params['path'], $params['url']);

        $tmpConnection = DriverManager::getConnection($params);
        $shouldNotCreateDatabase = $ifNotExists && in_array($dbName, $tmpConnection->getSchemaManager()->listDatabases());

        // Only quote if we don't have a path
        if (!$isPath) {
            $dbName = $tmpConnection->getDatabasePlatform()->quoteSingleIdentifier($dbName);
        }

        return $this->createDatabase($output, $connectionName, $tmpConnection, $dbName, $shouldNotCreateDatabase);
    }

    /**
     * @param InputInterface $input
     *
     * @return string
     */
    private function getConnectionName(InputInterface $input): string
    {
        $connectionName = $input->getOption('connection');

        if (null !== $connectionName) {
            return $connectionName;
        }

        return $this->connectionRegistry->getDefaultConnectionName();
    }

    /**
     * @param Connection $connection
     *
     * @return array
     */
    private function getParams(Connection $connection): array
    {
        $params = $connection->getParams();
        if (isset($params['master'])) {
            $params = $params['master'];
        }

        return $params;
    }

    /**
     * @param array  $params
     * @param string $connectionName
     *
     * @return string
     */
    private function getDbName(array $params, string $connectionName): string
    {
        if (isset($params['path'])) {
            return $params['path'];
        }

        if (isset($params['dbname'])) {
            return $params['dbname'];
        }

        throw new \InvalidArgumentException('Connection does not contain a \'path\' or \'dbname\' parameter.');
    }

    /**
     * @param OutputInterface $output
     * @param string          $connectionName
     * @param Connection      $tmpConnection
     * @param string          $dbName
     * @param bool            $shouldNotCreateDatabase
     *
     * @return int
     */
    private function createDatabase(
        OutputInterface $output,
        string $connectionName,
        Connection $tmpConnection,
        string $dbName,
        bool $shouldNotCreateDatabase
    ): int {
        try {
            if ($shouldNotCreateDatabase) {
                $output->writeln(
                    sprintf(
                        '<info>Database <comment>%s</comment> for connection named <comment>%s</comment>'
                            .' already exists. Skipped.</info>',
                        $dbName,
                        $connectionName
                    )
                );
            } else {
                $tmpConnection->getSchemaManager()->createDatabase($dbName);
                $output->writeln(
                    sprintf(
                        '<info>Created database <comment>%s</comment>'
                             .' for connection named <comment>%s</comment>.</info>',
                        $dbName,
                        $connectionName
                    )
                );
            }

            return 0;
        } catch (\Exception $e) {
            $output->writeln(sprintf('<error>Could not create database <comment>%s</comment>.</error>', $dbName));
            $output->writeln(sprintf('<error>%s</error>', $e->getMessage()));

            return 1;
        }
    }
}


<?php
/*
* This file is part of the job-bundle package.
*
* (c) Hannes Schulz <hannes.schulz@aboutcoders.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Abc\Bundle\JobBundle\Validator\Constraints;

use Abc\Bundle\JobBundle\Job\JobInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Abc\Bundle\JobBundle\Validator\Constraints as AssertJob;

/**
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class JobValidator extends ConstraintValidator
{
    /**
     * {@inheritdoc}
     */
    public function validate($value, Constraint $constraint)
    {
        if (null === $value) {
            return;
        }

        if(!$value instanceof JobInterface) {
            throw new \InvalidArgumentException('The value must be an instance of '.JobInterface::class);
        }

        if(null == $value->getType()) {
            return;
        }

        $this->context->getValidator()
            ->inContext($this->context)
            ->atPath('parameters')
            ->validate($value->getParameters(), new AssertJob\Parameters(['type' => $value->getType()]));

        return;
    }
}


<?php
/*
* This file is part of the job-bundle package.
*
* (c) Hannes Schulz <hannes.schulz@aboutcoders.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Abc\Bundle\JobBundle\Job;

use Abc\Bundle\JobBundle\Event\ExecutionEvent;
use Abc\Bundle\JobBundle\Event\TerminationEvent;
use Abc\Bundle\JobBundle\Job\Context\Context;
use Abc\Bundle\JobBundle\Job\Exception\TicketNotFoundException;
use Abc\Bundle\JobBundle\Job\Exception\ValidationFailedException;
use Abc\Bundle\JobBundle\Job\Queue\Message;
use Abc\Bundle\JobBundle\Job\Queue\ProducerInterface;
use Abc\Bundle\JobBundle\Logger\LoggerFactoryInterface as LoggerFactoryInterface;
use Abc\Bundle\JobBundle\Event\JobEvents;
use Abc\Bundle\JobBundle\Model\JobManagerInterface;
use Abc\Bundle\ResourceLockBundle\Exception\LockException;
use Abc\Bundle\ResourceLockBundle\Model\LockInterface;
use Abc\Bundle\SchedulerBundle\Model\ScheduleInterface as BaseScheduleInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\Validator\Validator\ValidatorInterface;

/**
 * The job manager
 *
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 * @author Wojciech Ciolko <wojciech.ciolko@aboutcoders.com>
 */
class Manager implements ManagerInterface
{
    const JOB_LOCK_PREFIX = 'job-';

    /**
     * @var JobTypeRegistry
     */
    protected $registry;

    /**
     * @var ProducerInterface
     */
    protected $producer;

    /**
     * @var JobManagerInterface
     */
    protected $jobManager;

    /**
     * @var Invoker
     */
    protected $invoker;

    /**
     * @var LoggerFactoryInterface
     */
    protected $loggerFactory;

    /**
     * @var LogManagerInterface
     */
    protected $logManager;

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

    /**
     * @var JobHelper
     */
    protected $helper;

    /**
     * @var LockInterface
     */
    protected $locker;

    /**
     * @var ValidatorInterface
     */
    protected $validator;

    /**
     * @var LoggerInterface
     */
    protected $logger;

    /**
     * @var Stopwatch
     */
    protected $stopwatch;

    /**
     * @var array
     */
    private $jobLogger = array();

    /**
     * @param JobTypeRegistry          $registry
     * @param JobManagerInterface      $jobManager
     * @param Invoker                  $invoker
     * @param LoggerFactoryInterface   $loggerFactory
     * @param LogManagerInterface      $logManager
     * @param EventDispatcherInterface $eventDispatcher
     * @param JobHelper                $helper
     * @param LockInterface            $locker
     * @param ValidatorInterface|null  $validator
     * @param LoggerInterface|null     $logger
     */
    public function __construct(
        JobTypeRegistry $registry,
        JobManagerInterface $jobManager,
        Invoker $invoker,
        LoggerFactoryInterface $loggerFactory,
        LogManagerInterface $logManager,
        EventDispatcherInterface $eventDispatcher,
        JobHelper $helper,
        LockInterface $locker,
        ValidatorInterface $validator = null,
        LoggerInterface $logger = null)
    {
        $this->registry      = $registry;
        $this->jobManager    = $jobManager;
        $this->invoker       = $invoker;
        $this->loggerFactory = $loggerFactory;
        $this->logManager    = $logManager;
        $this->dispatcher    = $eventDispatcher;
        $this->helper        = $helper;
        $this->locker        = $locker;
        $this->validator     = $validator;
        $this->logger        = $logger == null ? new NullLogger() : $logger;
        $this->stopwatch     = new Stopwatch();
    }

    /**
     * @param ProducerInterface $producer
     * @return void
     */
    public function setProducer(ProducerInterface $producer)
    {
        $this->producer = $producer;
    }

    /**
     * {@inheritdoc}
     */
    public function addJob($type, array $parameters = null, BaseScheduleInterface $schedule = null)
    {
        return $this->add($this->jobManager->create($type, $parameters, $schedule));
    }

    /**
     * {@inheritdoc}
     */
    public function add(JobInterface $job)
    {
        if (null != $this->validator) {
            $this->logger->debug('Validate job');
            $errors = $this->validator->validate($job);
            if (count($errors) > 0) {
                $this->logger->debug('Validation failed with errors', ['errors' => $errors]);
                throw new ValidationFailedException($errors);
            }
        }

        if (!$this->jobManager->isManagerOf($job)) {
            $job = $this->helper->copyJob($job, $this->jobManager->create());
        }

        $job->setStatus(Status::REQUESTED());
        $job->setProcessingTime(0);

        $this->jobManager->save($job);

        $this->logger->info(sprintf('Added job %s of type "%s"', $job->getTicket(), $job->getType()), [
            'parameters' => $job->getParameters(),
            'schedules'  => $job->getSchedules()
        ]);

        if (!$job->hasSchedules()) {
            $this->publishJob($job);
        }

        return $job;
    }

    /**
     * {@inheritdoc}
     */
    public function cancel($ticket, $force = false)
    {
        $job = $this->findJob($ticket);

        if (Status::isTerminated($job->getStatus())) {
            return false;
        }

        $isProcessing = $job->getStatus() == Status::PROCESSING();
        $status       = $force ? Status::CANCELLED() : ($isProcessing ? Status::CANCELLING() : Status::CANCELLED());

        $this->helper->updateJob($job, $status);
        $this->jobManager->save($job);

        if ($force) {
            $this->locker->release($job->getTicket());
        }

        if (!$isProcessing || $force) {
            $this->dispatcher->dispatch(JobEvents::JOB_TERMINATED, new TerminationEvent($job));
            $message = $force ? 'Forced cancellation of job ' : 'Cancelled job ';
            $this->logger->info($message . $job->getTicket());
        } else {
            $this->logger->info('Request cancellation of job ' . $job->getTicket());
        }

        return $job;
    }

    /**
     * {@inheritdoc}
     */
    public function get($ticket)
    {
        $this->logger->debug('Get job ' . $ticket);

        return $this->findJob($ticket);
    }

    /**
     * {@inheritdoc}
     */
    public function getLogs($ticket)
    {
        $this->logger->debug('Get logs of job ' . $ticket);

        return $this->logManager->findByJob($this->findJob($ticket));
    }

    /**
     * {@inheritdoc}
     */
    public function onMessage(Message $message)
    {
        $this->stopwatch->start('processMessage');

        $job = $this->findJob($message->getTicket());

        if ($job->getStatus() == Status::PROCESSING() || $job->getStatus() == Status::CANCELLED() || $job->getStatus() == Status::ERROR()) {

            $this->logger->notice(sprintf('Skipped execution of job %s (status: %s)', $job->getTicket(), $job->getStatus()));

            return;
        }
        try {
            $this->locker->lock($this->getLockName($job));
        } catch (LockException $e) {
            $this->logger->warning('Failed to get lock for job ' . $job->getTicket());

            return;
        }

        $event = new ExecutionEvent($job, new Context());

        $this->dispatchExecutionEvent(JobEvents::JOB_PRE_EXECUTE, $event);

        $job->setStatus(Status::PROCESSING());
        $job->setProcessingTime(0);
        $this->jobManager->save($job);

        $response       = null;
        $this->stopwatch->start('processJob');

        try {
            $this->logger->debug(sprintf('Execute job %s of type "%s"', $job->getTicket(), $job->getType()), [
                'parameters' => $job->getParameters()
            ]);

            // invoke the job
            $response = $this->invoker->invoke($job, $event->getContext());

            if ($job->getStatus() != Status::CANCELLED()) {
                $status = $job->hasSchedules() ? Status::SLEEPING() : Status::PROCESSED();
            } else {
                $status = Status::CANCELLED();
            }

            $this->dispatchExecutionEvent(JobEvents::JOB_POST_EXECUTE, $event);
        }
        catch (\Throwable $e)
        {
            $this->logger->warning(sprintf('Failed to execute job %s (Error: $s)', $job->getTicket(), $e->getMessage()), [
                'job'       => $job,
                'exception' => $e
            ]);

            $this->getJobLogger($job)->error($e->getMessage(), ['exception' => $e]);

            $response = new ExceptionResponse($e);
            $status   = Status::ERROR();
        }
        catch (\Exception $e)
        {
            $this->logger->warning(sprintf('Failed to execute job %s (Error: $s)', $job->getTicket(), $e->getMessage()), [
                'job'       => $job,
                'exception' => $e
            ]);

            $this->getJobLogger($job)->error($e->getMessage(), ['exception' => $e]);

            $response = new ExceptionResponse($e);
            $status   = Status::ERROR();
        }

        $this->releaseLock($job);

        $this->helper->updateJob($job, $status, $this->stopwatch->stop('processJob')->getDuration(), $response);
        $this->jobManager->save($job);

        if (Status::isTerminated($job->getStatus())) {
            $this->dispatcher->dispatch(JobEvents::JOB_TERMINATED, new TerminationEvent($job));
        }
    }

    /**
     * {@inheritdoc}
     */
    public function restart($ticket)
    {
        $this->logger->debug('Restart job ' . $ticket);

        $job = $this->findJob($ticket);

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

    /**
     * {@inheritdoc}
     */
    public function update(JobInterface $job)
    {
        $existingJob = $this->jobManager->findByTicket($job->getTicket());
        if (null == $existingJob) {
            return $this->add($job);
        }

        $this->logger->debug('Update job ' . $job->getTicket(), ['job' => $job]);

        $job = $this->helper->copyJob($job, $existingJob);

        if (null != $this->validator) {
            $errors = $this->validator->validate($job);
            if (count($errors) > 0) {
                throw new ValidationFailedException($errors);
            }
        }

        $this->jobManager->save($job);

        $this->getJobLogger($job)->debug('Updated job', ['job' => $job]);

        return $job;
    }

    /**
     * @param JobInterface $job
     * @throws \Exception
     */
    protected function publishJob(JobInterface $job)
    {
        $message = new Message($job->getType(), $job->getTicket());

        try {
            $this->producer->produce($message);
        } catch (\Exception $e) {
            $this->logger->critical(sprintf('Failed to publish message for job %s (Error: %s)', $job->getTicket(), $e->getMessage()), [
                'job'       => $job,
                'exception' => $e
            ]);

            throw $e;
        }
    }

    /**
     * @param string $ticket
     * @return \Abc\Bundle\JobBundle\Model\JobInterface
     * @throws TicketNotFoundException If a job with the given ticket was not found
     */
    protected function findJob($ticket)
    {
        if ($job = $this->jobManager->findByTicket($ticket)) {
            return $job;
        }

        $this->logger->error('Job with ticket {ticket} not found', ['ticket' => $ticket]);

        throw new TicketNotFoundException($ticket);
    }

    /**
     * @param string         $eventName
     * @param ExecutionEvent $event
     * @return void
     */
    private function dispatchExecutionEvent($eventName, ExecutionEvent $event)
    {
        try {
            $this->logger->debug(sprintf('Dispatch event %s for job %s', $eventName, $event->getJob()->getTicket()));

            $this->dispatcher->dispatch($eventName, $event);
        } catch (\Exception $e) {
            $this->logger->critical(sprintf('An event listener for event %s threw an exception (Error: %s)', $eventName, $e->getMessage()), ['exception' => $e]);
        }
    }

    /**
     * Get lock name for job object
     *
     * @param JobInterface $job
     * @return string
     */
    private function getLockName(JobInterface $job)
    {
        return self::JOB_LOCK_PREFIX . $job->getTicket();
    }

    /**
     * @param JobInterface $job
     * @return void
     */
    private function releaseLock($job)
    {
        $this->locker->release($this->getLockName($job));

        $this->logger->debug('Released lock for job ' . $job->getTicket());
    }

    /**
     * @param JobInterface $job
     * @return LoggerInterface
     */
    private function getJobLogger(JobInterface $job)
    {
        if (!isset($this->jobLogger[0]) || $job !== $this->jobLogger[0]) {
            $this->jobLogger[0] = $job;
            $this->jobLogger[1] = $this->loggerFactory->create($job);
        }

        return $this->jobLogger[1];
    }
}


<?php
/*
* This file is part of the job-bundle package.
*
* (c) Hannes Schulz <hannes.schulz@aboutcoders.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Abc\Bundle\JobBundle\Logger\Handler;

use Abc\Bundle\JobBundle\Job\JobInterface;
use Abc\Bundle\JobBundle\Model\LogManagerInterface;

/**
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class OrmHandlerFactory extends BaseHandlerFactory
{
    /**
     * @var LogManagerInterface
     */
    protected $manager;

    /**
     * @param LogManagerInterface $manager
     */
    public function __construct(LogManagerInterface $manager)
    {
        $this->manager = $manager;
    }

    /**
     * {@inheritdoc}
     */
    public function createHandler(JobInterface $job, $level, $bubble)
    {
        $handler = new JobAwareOrmHandler($this->manager, $level, $bubble);
        $handler->setJob($job);

        return $this->initHandler($handler);
    }
}


<?php

namespace Gdbots\Pbj\Type;

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

final class TimestampType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function guard($value, Field $field)
    {
        Assertion::integer($value, null, $field->getName());
        Assertion::true(
            DateUtils::isValidTimestamp($value),
            sprintf('Field [%s] value [%d] is not a valid unix timestamp.', $field->getName(), $value),
            $field->getName()
        );
    }

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

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

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

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


<?php

namespace Gdbots\Pbj\Type;

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

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

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

        return null;
    }

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

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

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

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

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

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


<?php

namespace Gdbots\Pbj\WellKnown;

use Gdbots\Pbj\Exception\InvalidArgumentException;

/**
 * Value object for microtime with methods to convert to and from integers.
 * Note that this is a unix timestamp __WITH__ microseconds but stored
 * as an integer NOT a float.
 *
 * 10 digits (unix timestamp) concatenated with 6 microsecond digits.
 *
 * @link http://php.net/manual/en/function.microtime.php
 */
final class Microtime implements \JsonSerializable
{
    /**
     * The microtime is stored as a 16 digit integer.
     *
     * @var int
     */
    private $int;

    /** @var int */
    private $sec;

    /** @var int */
    private $usec;

    /**
     * Private constructor to ensure static methods are used.
     */
    private function __construct()
    {
    }

    /**
     * Create a new object using the current microtime.
     *
     * @return self
     */
    public static function create()
    {
        return self::fromTimeOfDay(gettimeofday());
    }

    /**
     * Create a new object from a float value, typically one that is returned
     * from the microtime(true) call.
     *
     * @link http://php.net/manual/en/function.microtime.php
     *
     * @param float $float e.g. 1422060753.9581
     *
     * @return self
     */
    public static function fromFloat($float)
    {
        $str = substr(str_pad(str_replace('.', '', $float), 16, '0'), 0, 16);
        $m = new self();
        $m->int = (int)$str;
        $m->sec = (int)substr($str, 0, 10);
        $m->usec = (int)substr($str, -6);
        return $m;
    }

    /**
     * Create a new object from the result of a gettimeofday call that
     * is NOT returned as a float.
     *
     * @link http://php.net/manual/en/function.gettimeofday.php
     *
     * @param array $tod
     *
     * @return self
     */
    public static function fromTimeOfDay(array $tod)
    {
        $str = $tod['sec'] . str_pad($tod['usec'], 6, '0', STR_PAD_LEFT);
        $m = new self();
        $m->int = (int)$str;
        $m->sec = (int)substr($str, 0, 10);
        $m->usec = (int)substr($str, -6);
        return $m;
    }

    /**
     * Create a new object from the integer (or string) version of the microtime.
     *
     * Total digits would be unix timestamp (10) + (3-6) microtime digits.
     * Lack of precision on digits will be automatically padded with zeroes.
     *
     * @param string|int $stringOrInteger
     *
     * @return self
     * @throws InvalidArgumentException
     */
    public static function fromString($stringOrInteger)
    {
        $int = (int)$stringOrInteger;
        $len = strlen($int);
        if ($len < 13 || $len > 16) {
            throw new InvalidArgumentException(
                sprintf(
                    'Input [%d] must be between 13 and 16 digits, [%d] given.',
                    $int,
                    $len
                )
            );
        }

        if ($len < 16) {
            $int = (int)str_pad($int, 16, '0');
        }

        $m = new self();
        $m->int = $int;
        $m->sec = (int)substr($int, 0, 10);
        $m->usec = (int)substr($int, -6);
        return $m;
    }

    /**
     * Creates a new microtime from a \DateTime object using
     * it's timestamp and microseconds.
     *
     * @param \DateTimeInterface $date
     *
     * @return self
     */
    public static function fromDateTime(\DateTimeInterface $date)
    {
        $str = $date->format('U') . str_pad($date->format('u'), 6, '0');
        $m = new self();
        $m->int = (int)$str;
        $m->sec = (int)substr($str, 0, 10);
        $m->usec = (int)substr($str, -6);
        return $m;
    }

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

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

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

    /**
     * @return int
     */
    public function getSeconds()
    {
        return $this->sec;
    }

    /**
     * @return int
     */
    public function getMicroSeconds()
    {
        return $this->usec;
    }

    /**
     * todo: shall we return \DateTimeImmutable here too?
     *
     * @return \DateTimeInterface
     */
    public function toDateTime()
    {
        return \DateTime::createFromFormat('U.u', $this->sec . '.' . str_pad($this->usec, 6, '0', STR_PAD_LEFT));
    }

    /**
     * @return float
     */
    public function toFloat()
    {
        return (float)($this->sec . '.' . str_pad($this->usec, 6, '0', STR_PAD_LEFT));
    }
}


<?php

namespace Gdbots\Pbj\Type;

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

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

        $classNames = $field->getAnyOfClassNames();
        if (empty($classNames)) {
            // means it can be "any message"
            return;
        }

        foreach ($classNames as $className) {
            if ($value instanceof $className) {
                return;
            }
        }

        Assertion::true(
            false,
            sprintf(
                'Field [%s] must be an instance of at least one of: %s.',
                $field->getName(),
                implode(',', $classNames)
            ),
            $field->getName()
        );
    }

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

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

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

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

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

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


<?php

namespace KassaCom\SDK\Actions;

use KassaCom\SDK\Exception\Response\ResponseParseException;
use KassaCom\SDK\Exception\Response\UnknownResponseTypeException;
use KassaCom\SDK\Exception\Response\UnsupportedResponseTypeException;
use KassaCom\SDK\Model\Response\AbstractResponse;

class ResponseCreator
{
    /**
     * @param string $response
     * @param array  $data
     *
     * @return AbstractResponse
     *
     * @throws UnknownResponseTypeException
     * @throws UnsupportedResponseTypeException
     * @throws ResponseParseException
     */
    public static function create($response, array $data)
    {
        if (!class_exists($response)) {
            throw new UnknownResponseTypeException(sprintf('Unknown response type: %s', $response));
        }

        $response = new $response();

        if (!$response instanceof AbstractResponse) {
            throw new UnsupportedResponseTypeException(sprintf('Unsupported response type: %s', get_class($response)));
        }

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

        return $response;
    }
}


<?php

namespace Gdbots\Pbj\Type;

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

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

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

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

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

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

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