<?php
/**
* Phinx
*
* (The MIT license)
* Copyright (c) 2015 Rob Morgan
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated * documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* @package Phinx
* @subpackage Phinx\Console
*/
namespace Phinx\Console\Command;
use Phinx\Config\Config;
use Phinx\Config\ConfigInterface;
use Phinx\Db\Adapter\AdapterInterface;
use Phinx\Migration\Manager;
use Phinx\Util\Util;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Abstract command, contains bootstrapping info
*
* @author Rob Morgan <[email protected]>
*/
abstract class AbstractCommand extends Command
{
/**
* The location of the default migration template.
*/
const DEFAULT_MIGRATION_TEMPLATE = '/../../Migration/Migration.template.php.dist';
/**
* The location of the default seed template.
*/
const DEFAULT_SEED_TEMPLATE = '/../../Seed/Seed.template.php.dist';
/**
* @var \Phinx\Config\ConfigInterface
*/
protected $config;
/**
* @var \Phinx\Db\Adapter\AdapterInterface
*/
protected $adapter;
/**
* @var \Phinx\Migration\Manager
*/
protected $manager;
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->addOption('--configuration', '-c', InputOption::VALUE_REQUIRED, 'The configuration file to load');
$this->addOption('--parser', '-p', InputOption::VALUE_REQUIRED, 'Parser used to read the config file. Defaults to YAML');
}
/**
* Bootstrap Phinx.
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
* @return void
*/
public function bootstrap(InputInterface $input, OutputInterface $output)
{
if (!$this->getConfig()) {
$this->loadConfig($input, $output);
}
$this->loadManager($input, $output);
// report the paths
$paths = $this->getConfig()->getMigrationPaths();
$output->writeln('<info>using migration paths</info> ');
foreach (Util::globAll($paths) as $path) {
$output->writeln('<info> - ' . realpath($path) . '</info>');
}
try {
$paths = $this->getConfig()->getSeedPaths();
$output->writeln('<info>using seed paths</info> ');
foreach (Util::globAll($paths) as $path) {
$output->writeln('<info> - ' . realpath($path) . '</info>');
}
} catch (\UnexpectedValueException $e) {
// do nothing as seeds are optional
}
}
/**
* Sets the config.
*
* @param \Phinx\Config\ConfigInterface $config
* @return \Phinx\Console\Command\AbstractCommand
*/
public function setConfig(ConfigInterface $config)
{
$this->config = $config;
return $this;
}
/**
* Gets the config.
*
* @return \Phinx\Config\ConfigInterface
*/
public function getConfig()
{
return $this->config;
}
/**
* Sets the database adapter.
*
* @param \Phinx\Db\Adapter\AdapterInterface $adapter
* @return \Phinx\Console\Command\AbstractCommand
*/
public function setAdapter(AdapterInterface $adapter)
{
$this->adapter = $adapter;
return $this;
}
/**
* Gets the database adapter.
*
* @return \Phinx\Db\Adapter\AdapterInterface
*/
public function getAdapter()
{
return $this->adapter;
}
/**
* Sets the migration manager.
*
* @param \Phinx\Migration\Manager $manager
* @return \Phinx\Console\Command\AbstractCommand
*/
public function setManager(Manager $manager)
{
$this->manager = $manager;
return $this;
}
/**
* Gets the migration manager.
*
* @return \Phinx\Migration\Manager|null
*/
public function getManager()
{
return $this->manager;
}
/**
* Returns config file path
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @return string
*/
protected function locateConfigFile(InputInterface $input)
{
$configFile = $input->getOption('configuration');
$useDefault = false;
if ($configFile === null || $configFile === false) {
$useDefault = true;
}
$cwd = getcwd();
// locate the phinx config file (default: phinx.yml)
// TODO - In future walk the tree in reverse (max 10 levels)
$locator = new FileLocator([
$cwd . DIRECTORY_SEPARATOR
]);
if (!$useDefault) {
// Locate() throws an exception if the file does not exist
return $locator->locate($configFile, $cwd, $first = true);
}
$possibleConfigFiles = ['phinx.php', 'phinx.json', 'phinx.yml'];
foreach ($possibleConfigFiles as $configFile) {
try {
return $locator->locate($configFile, $cwd, $first = true);
} catch (\InvalidArgumentException $exception) {
$lastException = $exception;
}
}
throw $lastException;
}
/**
* Parse the config file and load it into the config object
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
* @throws \InvalidArgumentException
* @return void
*/
protected function loadConfig(InputInterface $input, OutputInterface $output)
{
$configFilePath = $this->locateConfigFile($input);
$output->writeln('<info>using config file</info> .' . str_replace(getcwd(), '', realpath($configFilePath)));
$parser = $input->getOption('parser');
// If no parser is specified try to determine the correct one from the file extension. Defaults to YAML
if ($parser === null) {
$extension = pathinfo($configFilePath, PATHINFO_EXTENSION);
switch (strtolower($extension)) {
case 'json':
$parser = 'json';
break;
case 'php':
$parser = 'php';
break;
case 'yml':
default:
$parser = 'yaml';
}
}
switch (strtolower($parser)) {
case 'json':
$config = Config::fromJson($configFilePath);
break;
case 'php':
$config = Config::fromPhp($configFilePath);
break;
case 'yaml':
$config = Config::fromYaml($configFilePath);
break;
default:
throw new \InvalidArgumentException(sprintf('\'%s\' is not a valid parser.', $parser));
}
$output->writeln('<info>using config parser</info> ' . $parser);
$this->setConfig($config);
}
/**
* Load the migrations manager and inject the config
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
*/
protected function loadManager(InputInterface $input, OutputInterface $output)
{
if ($this->getManager() === null) {
$manager = new Manager($this->getConfig(), $input, $output);
$this->setManager($manager);
} else {
$manager = $this->getManager();
$manager->setInput($input);
$manager->setOutput($output);
}
}
/**
* Verify that the migration directory exists and is writable.
*
* @param string $path
* @throws \InvalidArgumentException
* @return void
*/
protected function verifyMigrationDirectory($path)
{
if (!is_dir($path)) {
throw new \InvalidArgumentException(sprintf(
'Migration directory "%s" does not exist',
$path
));
}
if (!is_writable($path)) {
throw new \InvalidArgumentException(sprintf(
'Migration directory "%s" is not writable',
$path
));
}
}
/**
* Verify that the seed directory exists and is writable.
*
* @param string $path
* @throws \InvalidArgumentException
* @return void
*/
protected function verifySeedDirectory($path)
{
if (!is_dir($path)) {
throw new \InvalidArgumentException(sprintf(
'Seed directory "%s" does not exist',
$path
));
}
if (!is_writable($path)) {
throw new \InvalidArgumentException(sprintf(
'Seed directory "%s" is not writable',
$path
));
}
}
/**
* Returns the migration template filename.
*
* @return string
*/
protected function getMigrationTemplateFilename()
{
return __DIR__ . self::DEFAULT_MIGRATION_TEMPLATE;
}
/**
* Returns the seed template filename.
*
* @return string
*/
protected function getSeedTemplateFilename()
{
return __DIR__ . self::DEFAULT_SEED_TEMPLATE;
}
}