Created
December 5, 2013 01:46
-
-
Save tPl0ch/7798862 to your computer and use it in GitHub Desktop.
Symfony2 ContainerAwareCommand to transfer file sessions to a memcached interface.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Application\Bundle\DefaultBundle\Command; | |
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; | |
use Symfony\Component\Console\Input\InputArgument; | |
use Symfony\Component\Console\Input\InputInterface; | |
use Symfony\Component\Console\Input\InputOption; | |
use Symfony\Component\Console\Output\OutputInterface; | |
use Symfony\Component\Filesystem\Filesystem; | |
use Symfony\Component\Finder\Finder; | |
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler; | |
use Symfony\Component\Stopwatch\Stopwatch; | |
/** | |
* Class MoveFileSessionsToMemcachedCommand | |
*/ | |
class MoveFileSessionsToMemcachedCommand extends ContainerAwareCommand | |
{ | |
/** | |
* @var \Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler | |
*/ | |
protected $memcachedService; | |
/** | |
* @var \Symfony\Component\Finder\Finder | |
*/ | |
protected $finder; | |
/** | |
* @var string | |
*/ | |
protected $sessionDirectory; | |
/** | |
* @var string | |
*/ | |
protected $prefix; | |
/** | |
* @var \Symfony\Component\Stopwatch\Stopwatch | |
*/ | |
protected $stopwatch; | |
/** | |
* {@inheritDoc} | |
*/ | |
public function configure() | |
{ | |
$this->setName('sessions:transfer:memcached'); | |
$this->addArgument( | |
'directory', | |
InputArgument::REQUIRED, | |
'The session directory to read the file sessions from.' | |
); | |
$this->addOption( | |
'handler', | |
'hid', | |
InputOption::VALUE_REQUIRED, | |
'The session handler service id for the memcached session handler.', | |
'session.handler.memcached' | |
); | |
$this->addOption( | |
'prefix', | |
'pr', | |
InputOption::VALUE_REQUIRED, | |
'The prefix of the current file sessions.', | |
'sess_' | |
); | |
$this->addOption( | |
'only-modified-after', | |
'after', | |
InputOption::VALUE_REQUIRED, | |
'The `strtotime` date format after which the sessions should be transferred. Compares `modified` time!' | |
); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
protected function execute(InputInterface $input, OutputInterface $output) | |
{ | |
$successful = 0; | |
$step = 1; | |
/** @var \Symfony\Component\Console\Helper\ProgressHelper $progress */ | |
$progress = $this->getHelperSet()->get('progress'); | |
$invalidFiles = array(); | |
$errorFiles = array(); | |
if ($this->isVerbose($output) || $this->isVeryVerbose($output)) { | |
$output->writeln('<comment>Starting processing!</comment>'); | |
$output->writeln(''); | |
} | |
$this->readFiles($input); | |
$progress->start($output, $this->finder->count()); | |
/** @var \Symfony\Component\Finder\SplFileInfo $fileInfo */ | |
foreach ($this->finder as $fileInfo) { | |
$fileName = $fileInfo->getFilename(); | |
$realPath = $fileInfo->getRealPath(); | |
// if prefix doesn't match, skip file | |
if (!$this->hasPrefix($fileName)) { | |
$invalidFiles[] = $realPath; | |
$step++; | |
continue; | |
} | |
// Make this check again to make concurrency with garbage collected session files less probable | |
if (!file_exists($realPath) || !$fileInfo->isReadable()) { | |
$invalidFiles[] = $realPath; | |
$step++; | |
continue; | |
} | |
$writeSuccessful = $this->memcachedService->write( | |
str_replace($this->prefix, '', $fileName), | |
$fileInfo->getContents() | |
); | |
if (!$writeSuccessful) { | |
$errorFiles[] = $realPath; | |
$step++; | |
continue; | |
} | |
$progress->advance($step); | |
$step = 1; | |
$successful++; | |
} | |
$progress->finish(); | |
$stopEvent = $this->stopwatch->stop('sessionMove'); | |
if ($this->isVerbose($output) || $this->isVeryVerbose($output)) { | |
$output->writeln(''); | |
$output->writeln(sprintf("Files processed: %d", $successful)); | |
$output->writeln(sprintf("Files with errors: %d", count($errorFiles))); | |
$output->writeln(sprintf("Invalid files: %d", count($invalidFiles))); | |
$output->writeln(sprintf("Memory usage: %d kiB", $stopEvent->getMemory() / 1024)); | |
$output->writeln(sprintf("Running time: %d ms", $stopEvent->getDuration())); | |
$output->writeln('<info>Command complete!</info>'); | |
} | |
} | |
/** | |
* @param InputInterface $input | |
* | |
* @return \Symfony\Component\Finder\SplFileInfo[] | |
*/ | |
protected function readFiles(InputInterface $input) | |
{ | |
$this->finder | |
->in($this->sessionDirectory) | |
->files(); | |
$after = $input->getOption('only-modified-after'); | |
if (null !== $after) { | |
$this->finder->date(sprintf("after %s", $after)); | |
} | |
return $this->finder; | |
} | |
/** | |
* @param InputInterface $input | |
* @param OutputInterface $output | |
* | |
* @throws \InvalidArgumentException | |
*/ | |
protected function initialize(InputInterface $input, OutputInterface $output) | |
{ | |
$input->validate(); | |
if ($this->isVerbose($output) || $this->isVeryVerbose($output)) { | |
$output->writeln('<info>Initializing...</info>'); | |
} | |
$this->stopwatch = new Stopwatch(); | |
$this->stopwatch->start('sessionMove'); | |
$this->initializeFinder($input, $output); | |
$this->initializeMemcached($input, $output); | |
$this->prefix = $input->getOption('prefix'); | |
if ($this->isVeryVerbose($output) || $this->isVeryVerbose($output)) { | |
$output->writeln('<info>Initialization complete!</info>'); | |
$output->writeln(''); | |
} | |
} | |
/** | |
* @param InputInterface $input | |
* @param OutputInterface $output | |
* | |
* @throws \InvalidArgumentException | |
*/ | |
protected function initializeFinder(InputInterface $input, OutputInterface $output) | |
{ | |
$fileSystem = new Filesystem(); | |
$this->sessionDirectory = $input->getArgument('directory'); | |
if (!$fileSystem->exists($this->sessionDirectory)) { | |
throw new \InvalidArgumentException( | |
sprintf( | |
"The session directory '%s' does not exist or is not readable", | |
$this->sessionDirectory | |
) | |
); | |
} | |
unset($fileSystem); | |
$this->finder = new Finder(); | |
if ($this->isVeryVerbose($output)) { | |
$output->writeln('Initialized session directory finder!'); | |
} | |
} | |
/** | |
* @param InputInterface $input | |
* @param OutputInterface $output | |
* | |
* @throws \InvalidArgumentException | |
*/ | |
protected function initializeMemcached(InputInterface $input, OutputInterface $output) | |
{ | |
$memcachedHandlerId = $input->getOption('handler'); | |
if (!$this->getContainer()->has($memcachedHandlerId)) { | |
throw new \InvalidArgumentException( | |
sprintf("The handler service '%s' is not registered with the container", $this->sessionDirectory) | |
); | |
} | |
$this->memcachedService = $this->getContainer()->get($memcachedHandlerId); | |
if (!$this->memcachedService instanceof MemcachedSessionHandler) { | |
throw new \InvalidArgumentException( | |
sprintf( | |
"The handler service with id '%s' is not an instance of %s", | |
$memcachedHandlerId, | |
'Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler' | |
) | |
); | |
} | |
if ($this->isVeryVerbose($output)) { | |
$output->writeln('Initialized Memcached handler!'); | |
} | |
} | |
/** | |
* @param OutputInterface $output | |
* | |
* @return bool | |
*/ | |
protected function isVerbose(OutputInterface $output) | |
{ | |
return $output->getVerbosity() === OutputInterface::VERBOSITY_VERBOSE; | |
} | |
/** | |
* @param OutputInterface $output | |
* | |
* @return bool | |
*/ | |
protected function isVeryVerbose(OutputInterface $output) | |
{ | |
return $output->getVerbosity() === OutputInterface::VERBOSITY_VERY_VERBOSE; | |
} | |
/** | |
* @param string $fileName | |
* | |
* @return bool | |
*/ | |
protected function hasPrefix($fileName) | |
{ | |
return substr($fileName, 0, strlen($this->prefix)) === $this->prefix; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment