Commit 445ffcb4 authored by Felix Imobersteg's avatar Felix Imobersteg

init

parents
<?php
/*
* Copyright (c) 2019, whatwedo GmbH
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace whatwedo\CronBundle\Command;
use whatwedo\CronBundle\Manager\ExecutionManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class CheckCommand
*
* @package whatwedo\CronBundle\Command
*/
class CheckCommand extends Command
{
/**
* @var ExecutionManager
*/
protected $executionManager;
/**
* CheckCommand constructor.
*
* @param ExecutionManager $executionManager
*/
public function __construct(ExecutionManager $executionManager)
{
parent::__construct();
$this->executionManager = $executionManager;
}
/**
* Configures the current command.
*/
protected function configure(): void
{
parent::configure();
$this->setName('whatwedo:cron:check')
->setDescription('Check (once) and run cron jobs');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int|void|null
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->executionManager->check();
}
}
<?php
/*
* Copyright (c) 2019, whatwedo GmbH
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace whatwedo\CronBundle\Command;
use whatwedo\CronBundle\CronJob\CronJobInterface;
use whatwedo\CronBundle\Entity\Execution;
use whatwedo\CronBundle\Exception\MaxRuntimeReachedException;
use whatwedo\CronBundle\Manager\CronJobManager;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process;
/**
* Class ExecuteCommand
*
* @package whatwedo\CronBundle\Command
*/
class ExecuteCommand extends Command
{
/**
* @var CronJobManager
*/
protected $cronJobManager;
/**
* @var EntityManagerInterface
*/
protected $em;
/**
* @var string
*/
protected $projectDir;
/**
* @var string
*/
protected $environment;
/**
* ExecuteCommand constructor.
*
* @param CronJobManager $cronJobManager
* @param EntityManagerInterface $em
* @param string $projectDir
* @param string $environment
*/
public function __construct(CronJobManager $cronJobManager, EntityManagerInterface $em, string $projectDir, string $environment)
{
parent::__construct();
$this->em = $em;
$this->projectDir = $projectDir;
$this->cronJobManager = $cronJobManager;
$this->environment = $environment;
}
/**
* @param Execution $execution
* @param CronJobInterface $cronJob
* @param Process $process
*/
public function checkMaxRuntime(Execution $execution, CronJobInterface $cronJob, Process $process): void
{
if (!$cronJob->getMaxRuntime()) {
return;
}
$now = new \DateTime();
$diff = $now->getTimestamp() - $execution->getStartedAt()->getTimestamp();
if ($diff > $cronJob->getMaxRuntime()) {
$execution
->setState(Execution::STATE_TERMINATED)
->setPid(null)
->setStdout($process->getOutput())
->setStderr($process->getErrorOutput());
$this->em->flush($execution);
throw new MaxRuntimeReachedException($execution);
}
}
/**
* Configures the current command.
*/
protected function configure(): void
{
parent::configure();
$this->setName('whatwedo:cron:execute')
->setDescription('Execute cron job (internal use only)')
->addArgument('cron_job', InputArgument::REQUIRED, 'Class of cron job to execute')
->setHidden(true);
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int|void|null
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
// Get job definition
$cronJob = $this->cronJobManager->getCronJob($input->getArgument('cron_job'));
// Build command to execute
$command = array_merge(['bin/console', $cronJob->getCommand(), '--env='.$this->environment], $cronJob->getArguments());
// Create execution
$execution = new Execution();
$execution->setClass(get_class($cronJob))
->setCommand($command);
$this->em->persist($execution);
$this->em->flush($execution);
// Run command
$process = new Process($command, $this->projectDir);
$process->start();
$execution->setPid($process->getPid());
$this->em->flush($execution);
// Update command output every 5 seconds
while ($process->isRunning()) {
$this->checkMaxRuntime($execution, $cronJob, $process);
$output->writeln(sprintf('Process %s is running...', $process->getCommandLine()));
sleep(5);
$execution->setStdout($process->getOutput())
->setStderr($process->getErrorOutput());
$this->em->flush($execution);
}
// Finish execution
$output->writeln('Execution finished with exit code '.$process->getExitCode());
$execution
->setState(Execution::STATE_FINISHED)
->setFinishedAt(new \DateTime())
->setPid(null)
->setStdout($process->getOutput())
->setStderr($process->getErrorOutput())
->setExitCode($process->getExitCode());
$this->em->flush($execution);
}
}
<?php
/*
* Copyright (c) 2019, whatwedo GmbH
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace whatwedo\CronBundle\Command;
use whatwedo\CronBundle\CronJob\CronJobInterface;
use whatwedo\CronBundle\Entity\Execution;
use whatwedo\CronBundle\Manager\CronJobManager;
use whatwedo\CronBundle\Manager\ExecutionManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class InfoCommand
*
* @package whatwedo\CronBundle\Command
*/
class InfoCommand extends Command
{
/**
* @var CronJobManager
*/
protected $cronJobManager;
/**
* @var ExecutionManager
*/
protected $executionManager;
/**
* InfoCommand constructor.
*
* @param CronJobManager $cronJobManager
* @param string $projectDir
*/
public function __construct(CronJobManager $cronJobManager, ExecutionManager $executionManager)
{
parent::__construct();
$this->cronJobManager = $cronJobManager;
$this->executionManager = $executionManager;
}
/**
* Configures the current command.
*/
protected function configure(): void
{
parent::configure();
$this->setName('whatwedo:cron:info')
->setDescription('Print information about the given cron job')
->addArgument('cron_job', InputArgument::REQUIRED, 'Class of cron job to execute');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int|void|null
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
// Get job definition
$cronJob = $this->cronJobManager->getCronJob($input->getArgument('cron_job'));
// Build table
$table = new Table($output);
$table->setHeaders(['Name', 'Value']);
$table->addRows([
['Cron job', get_class($cronJob)],
['Description', $cronJob->getDescription()],
['Expression', $cronJob->getExpression()],
['Command', $cronJob->getCommand()],
['Arguments', implode(' ', $cronJob->getArguments())],
['Last execution', $this->getLastExecutionDateString($cronJob)],
['Next execution', $this->getNextExecutionDateString($cronJob)],
['Max runtime', $cronJob->getMaxRuntime().' seconds'],
['Lock status', $this->getLockStatus($cronJob)],
]);
// Render table
$table->render();
}
/**
* @param CronJobInterface $cronJob
*
* @return string|null
*/
protected function getLastExecutionDateString(CronJobInterface $cronJob): ?string
{
$lastExecutionDate = $this->executionManager->getLastExecutionDate($cronJob);
if (!$lastExecutionDate) {
return null;
}
return $this->getFormattedDate($lastExecutionDate);
}
/**
* @param CronJobInterface $cronJob
*
* @return string|null
*/
protected function getNextExecutionDateString(CronJobInterface $cronJob): ?string
{
$nextExecutionDate = $this->executionManager->getNextExecutionDate($cronJob);
if (!$nextExecutionDate) {
return null;
}
$now = new \DateTime();
if ($nextExecutionDate < $now) {
return 'Now';
}
return $this->getFormattedDate($nextExecutionDate);
}
/**
* @param \DateTime $date
*
* @return string|null
*/
protected function getFormattedDate(\DateTime $date): ?string
{
if (!$date) {
return null;
}
return $date->format('Y-m-d H:i:s');
}
/**
* @param CronJobInterface $cronJob
*
* @return string
*/
protected function getLockStatus(CronJobInterface $cronJob): string
{
if ($cronJob->isParallelAllowed()) {
return 'Parallel execution allowed';
}
$lastExecution = $this->executionManager->getLastExecution($cronJob);
if ($lastExecution && $lastExecution->getState() == Execution::STATE_RUNNING) {
return 'Locked';
}
return 'Not locked';
}
}
<?php
/*
* Copyright (c) 2019, whatwedo GmbH
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace whatwedo\CronBundle\Command;
use whatwedo\CronBundle\Manager\CronJobManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class ListCommand
*
* @package whatwedo\CronBundle\Command
*/
class ListCommand extends Command
{
/**
* @var CronJobManager
*/
protected $cronJobManager;
/**
* ListCommand constructor.
*
* @param CronJobManager $cronJobManager
*/
public function __construct(CronJobManager $cronJobManager)
{
parent::__construct();
$this->cronJobManager = $cronJobManager;
}
/**
* Configures the current command.
*/
protected function configure(): void
{
parent::configure();
$this->setName('whatwedo:cron:list')
->setDescription('List all cron jobs');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int|void|null
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$table = new Table($output);
$table->setHeaders(['Cron job', 'Description']);
foreach ($this->cronJobManager->getCronJobs() as $cronJob) {
$table->addRow([get_class($cronJob), $cronJob->getDescription()]);
}
$table->render();
}
}
<?php
/*
* Copyright (c) 2019, whatwedo GmbH
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace whatwedo\CronBundle\Command;
use whatwedo\CronBundle\Manager\ExecutionManager;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class SchedulerCommand
*
* @package whatwedo\CronBundle\Command
*/
class SchedulerCommand extends Command
{
/**
* @var LoggerInterface
*/
protected $logger;
/**
* @var ExecutionManager
*/
protected $executionManager;
/**
* SchedulerCommand constructor.
*
* @param LoggerInterface $logger
* @param ExecutionManager $executionManager
*/
public function __construct(LoggerInterface $logger, ExecutionManager $executionManager)
{
parent::__construct();
$this->logger = $logger;
$this->executionManager = $executionManager;
}
/**
* Configures the current command.
*/
protected function configure(): void
{
parent::configure();
$this->setName('whatwedo:cron:scheduler')
->setDescription('Run scheduler process');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int|void|null
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
// Set the max amount of memory to prevent memory leak (initial memory usage * 2)
$maxMemory = memory_get_usage() * 2;
$this->logger->debug(sprintf('Setting max amount of scheduler memory to %d', $maxMemory));
ini_set('memory_limit', $maxMemory);
// Check cron jobs every 15s
while (true) {
$this->executionManager->check();
sleep(15);
}
}
}
<?php
/*
* Copyright (c) 2019, whatwedo GmbH
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace whatwedo\CronBundle\CronJob;
use whatwedo\CronBundle\Manager\CronJobManager;
/**
* Class AbstractCronJob
*
* @package whatwedo\CronBundle\CronJob
*/
abstract class AbstractCronJob implements CronJobInterface
{
/**
* @var CronJobManager
*/
protected $cronJobManager;
/**
* AbstractCronJob constructor.
*
* @param CronJobManager $cronJobManager
*/
public function __construct(CronJobManager $cronJobManager)
{
$this->cronJobManager = $cronJobManager;
}
/**
* Returns command description
*
* @return string
*/
public function getDescription(): ?string
{
$command = $this->cronJobManager->getCommandByCronJob($this);
return $command->getDescription();
}
/**
* Returns command arguments
*
* @return string[]
*/
public function getArguments(): array
{
return [];
}
/**
* Returns max runtime in seconds
*
* @return int
*/
public function getMaxRuntime(): ?int
{
return null;
}
/**
* @return bool
*/
public function isParallelAllowed(): bool
{
return true;
}
}
<?php
/*
* Copyright (c) 2019, whatwedo GmbH
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace whatwedo\CronBundle\CronJob;
/**
* Interface CronJobInterface
*
* @package whatwedo\CronBundle\CronJob
*/
interface CronJobInterface
{
/**
* Returns command description
*
* @return string
*/
public function getDescription(): ?string;
/**
* Returns command name
*
* @return string
*/
public function getCommand(): string;
/**