diff --git a/README.md b/README.md index 5ce615b1..597f58d7 100644 --- a/README.md +++ b/README.md @@ -30,13 +30,13 @@ $ sudo cp phpcrsh.phar /usr/bin/local/phpcrsh To connect to a doctrine-dbal PHPCR repository: - $ phpcr --db-name=foobar --db-username=user --db-password=foobar + $ phpcr --transport=doctrine-dbal --db-name=foobar --db-username=user --db-password=foobar Full definition: ````bash Usage: - phpcr_shell [-h|--help] [-v|--verbose] [-V|--version] [--ansi] [--no-ansi] [-t|--transport="..."] [-pu|--phpcr-username="..."] [-pp|--phpcr-password[="..."]] [-pw|--phpcr-workspace[="..."]] [-du|--db-username="..."] [-dn|--db-name="..."] [-dp|--db-password[="..."]] [-dh|--db-host="..."] [-dd|--db-driver="..."] [-dP|--db-path="..."] [--no-interaction] [--unsupported] [-url|--repo-url="..."] [--command="..."] + phpcrsh [-h|--help] [-v|--verbose] [-V|--version] [--ansi] [--no-ansi] [-t|--transport="..."] [-pu|--phpcr-username="..."] [-pp|--phpcr-password[="..."]] [-pw|--phpcr-workspace[="..."]] [-du|--db-username="..."] [-dn|--db-name="..."] [-dp|--db-password[="..."]] [-dh|--db-host="..."] [-dd|--db-driver="..."] [-dP|--db-path="..."] [--no-interaction] [--unsupported] [-url|--repo-url="..."] [--command="..."] Options: --help (-h) Display this help message. @@ -60,7 +60,32 @@ Options: --command Run the given command ```` -Todo: +## Using profiles + +Profiles enable you to save and reuse connection settings. Profiles can be +created or used by using the `--profile` option. + +To create or update a profile, use it in conjunction with `--transport`, i.e.: + +````bash +$ phpcrsh --profile=mydb --transport=doctrine-dbal --db-user=foobar --db-name=mydb +Create new profile "mydb"? +```` + +To use the profile: + +````bash +$ phpcrsh --profile=mydb +```` + +Or use the short syntax: + +````bash +$ phpcrsh --pmydb +```` + + +## Todo - Better querying support - Better autocompletion diff --git a/composer.json b/composer.json index bd0ebeda..86b30f88 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,8 @@ "jackalope/jackalope-jackrabbit": "dev-master", "jackalope/jackalope": "dev-master", "phpcr/phpcr": "dev-master", - "phpcr/phpcr-utils": "dev-master" + "phpcr/phpcr-utils": "dev-master", + "symfony/finder": "~2.0" }, "require-dev": { "mockery/mockery": "0.9", diff --git a/spec/PHPCR/Shell/Config/ProfileLoaderSpec.php b/spec/PHPCR/Shell/Config/ProfileLoaderSpec.php new file mode 100644 index 00000000..ff69f7b1 --- /dev/null +++ b/spec/PHPCR/Shell/Config/ProfileLoaderSpec.php @@ -0,0 +1,82 @@ +getConfigDir()->willReturn(__DIR__); + $this->beConstructedWith($configHelper, $filesystem); + } + + function it_is_initializable() + { + $this->shouldHaveType('PHPCR\Shell\Config\ProfileLoader'); + } + + function it_should_list_profile_names() + { + $this->getProfileNames()->shouldReturn(array( + 'one', 'two' + )); + } + + function it_should_load_data_into_a_given_profile( + Profile $profile, + Filesystem $filesystem + ) + { + $profile->getName()->willReturn('one'); + $profile->set('transport', array( + 'name' => 'foobar', + 'bar_foo' => 'barfoo', + 'foo_bar' => 'foobar', + ))->shouldBeCalled(); + $profile->set('phpcr', array( + 'username' => 'username', + 'password' => 'password', + 'workspace' => 'default', + ))->shouldBeCalled(); + + $this->loadProfile($profile); + } + + function it_should_save_a_given_profile( + Profile $profile, + Filesystem $filesystem + ) + { + $profile->getName()->willReturn('newprofile'); + $profile->toArray()->willReturn(array( + 'transport' => array( + 'name' => 'test_transport', + 'option1' => 'value1', + ), + 'phpcr' => array( + 'username' => 'daniel', + 'password' => 'leech', + ), + )); + $filesystem->dumpFile(Argument::type('string'), <<shouldBeCalled(); + $this->saveProfile($profile); + } +} diff --git a/spec/PHPCR/Shell/Config/ProfileSpec.php b/spec/PHPCR/Shell/Config/ProfileSpec.php new file mode 100644 index 00000000..db87badf --- /dev/null +++ b/spec/PHPCR/Shell/Config/ProfileSpec.php @@ -0,0 +1,41 @@ +beConstructedWith( + 'foobar' + ); + } + + function it_is_initializable() + { + $this->shouldHaveType('PHPCR\Shell\Config\Profile'); + } + + function it_has_a_method_to_set_config( + ) + { + $this->set('transport', array()); + } + + function it_has_a_method_to_get_config() + { + $this->set('transport', array( + 'foo' => 'bar' + )); + + $this->get('transport')->shouldReturn(array( + 'foo' => 'bar' + )); + + $this->get('transport', 'foo')->shouldReturn('bar'); + } +} diff --git a/spec/PHPCR/Shell/Config/profiles/one.yml b/spec/PHPCR/Shell/Config/profiles/one.yml new file mode 100644 index 00000000..5bdd4eaa --- /dev/null +++ b/spec/PHPCR/Shell/Config/profiles/one.yml @@ -0,0 +1,8 @@ +transport: + name: foobar + bar_foo: barfoo + foo_bar: foobar +phpcr: + username: username + password: password + workspace: default diff --git a/spec/PHPCR/Shell/Config/profiles/two.yml b/spec/PHPCR/Shell/Config/profiles/two.yml new file mode 100644 index 00000000..6b31619c --- /dev/null +++ b/spec/PHPCR/Shell/Config/profiles/two.yml @@ -0,0 +1,3 @@ +transport: daadoo +dar_boo: darboo +boo_dar: boodar diff --git a/spec/PHPCR/Shell/Console/Helper/PhpcrHelperSpec.php b/spec/PHPCR/Shell/Console/Helper/PhpcrHelperSpec.php index af846754..334d4c2e 100644 --- a/spec/PHPCR/Shell/Console/Helper/PhpcrHelperSpec.php +++ b/spec/PHPCR/Shell/Console/Helper/PhpcrHelperSpec.php @@ -4,14 +4,16 @@ use PhpSpec\ObjectBehavior; use Prophecy\Argument; -use Symfony\Component\Console\Input\InputInterface; +use PHPCR\Shell\Config\Profile; +use PHPCR\Shell\Transport\TransportRegistryInterface; class PhpcrHelperSpec extends ObjectBehavior { function let( - InputInterface $input + Profile $profile, + TransportRegistryInterface $transportRegistry ) { - $this->beConstructedWith($input); + $this->beConstructedWith($transportRegistry, $profile); } function it_is_initializable() diff --git a/spec/PHPCR/Shell/Event/ProfileInitEventSpec.php b/spec/PHPCR/Shell/Event/ProfileInitEventSpec.php new file mode 100644 index 00000000..9b07c8d7 --- /dev/null +++ b/spec/PHPCR/Shell/Event/ProfileInitEventSpec.php @@ -0,0 +1,39 @@ +shouldHaveType('PHPCR\Shell\Event\ProfileInitEvent'); + } + + function let( + Profile $profile, + InputInterface $input, + OutputInterface $output + ) + { + $this->beConstructedWith( + $profile, $input, $output + ); + } + + function it_should_have_getters( + Profile $profile, + InputInterface $input, + OutputInterface $output + ) + { + $this->getProfile()->shouldReturn($profile); + $this->getInput()->shouldReturn($input); + $this->getOutput()->shouldReturn($output); + } +} diff --git a/spec/PHPCR/Shell/Subscriber/AliasSubscriberSpec.php b/spec/PHPCR/Shell/Subscriber/AliasSubscriberSpec.php index 1525a007..e725bc19 100644 --- a/spec/PHPCR/Shell/Subscriber/AliasSubscriberSpec.php +++ b/spec/PHPCR/Shell/Subscriber/AliasSubscriberSpec.php @@ -7,6 +7,7 @@ use PHPCR\Shell\Console\Helper\ConfigHelper; use PHPCR\Shell\Console\Input\StringInput; use PHPCR\Shell\Event\CommandPreRunEvent; +use Symfony\Component\Console\Helper\HelperSet; class AliasSubscriberSpec extends ObjectBehavior { @@ -16,10 +17,13 @@ function it_is_initializable() } function let( + HelperSet $helperSet, ConfigHelper $config ) { + $helperSet->get('config')->willReturn($config); + $this->beConstructedWith( - $config + $helperSet ); $config->getConfig('alias')->willReturn(array( diff --git a/spec/PHPCR/Shell/Transport/Transport/DoctrineDbalSpec.php b/spec/PHPCR/Shell/Transport/Transport/DoctrineDbalSpec.php new file mode 100644 index 00000000..fcbfef32 --- /dev/null +++ b/spec/PHPCR/Shell/Transport/Transport/DoctrineDbalSpec.php @@ -0,0 +1,14 @@ +shouldHaveType('PHPCR\Shell\Transport\Transport\DoctrineDbal'); + } +} diff --git a/spec/PHPCR/Shell/Transport/Transport/JackrabbitSpec.php b/spec/PHPCR/Shell/Transport/Transport/JackrabbitSpec.php new file mode 100644 index 00000000..6e803124 --- /dev/null +++ b/spec/PHPCR/Shell/Transport/Transport/JackrabbitSpec.php @@ -0,0 +1,14 @@ +shouldHaveType('PHPCR\Shell\Transport\Transport\Jackrabbit'); + } +} diff --git a/spec/PHPCR/Shell/Transport/TransportRegistrySpec.php b/spec/PHPCR/Shell/Transport/TransportRegistrySpec.php new file mode 100644 index 00000000..0c425416 --- /dev/null +++ b/spec/PHPCR/Shell/Transport/TransportRegistrySpec.php @@ -0,0 +1,48 @@ +shouldHaveType('PHPCR\Shell\Transport\TransportRegistry'); + } + + function it_can_register_transports( + TransportInterface $transport + ) + { + $transport->getName()->willReturn('foobar'); + $this->register($transport); + } + + function it_can_return_the_names_of_the_transports( + TransportInterface $transport1, + TransportInterface $transport2 + ) + { + $transport1->getName()->willReturn('transport1'); + $transport2->getName()->willReturn('transport2'); + $this->register($transport1); + $this->register($transport2); + + $this->getTransportNames()->shouldReturn(array( + 'transport1', 'transport2' + )); + } + + function it_can_return_a_named_transport_object( + TransportInterface $transport + ) + { + $transport->getName()->willReturn('test'); + $this->register($transport); + + $this->getTransport('test')->shouldReturn($transport); + } +} diff --git a/src/PHPCR/Shell/Config/Exception/FileExistsException.php b/src/PHPCR/Shell/Config/Exception/FileExistsException.php new file mode 100644 index 00000000..b4368e53 --- /dev/null +++ b/src/PHPCR/Shell/Config/Exception/FileExistsException.php @@ -0,0 +1,7 @@ + array(), + 'phpcr' => array(), + ); + + protected $name; + + public function __construct($name = null) + { + $this->name = $name; + } + + /** + * Return the array data for this profile + * + * @return array + */ + public function toArray() + { + return $this->profile; + } + + protected function validateDomain($domain) + { + if (!array_key_exists($domain, $this->profile)) { + throw new \InvalidArgumentException(sprintf( + 'Unknown profile domain "%s", can only use one of: %s', + $domain, implode(', ', array_keys($this->profile)) + )); + } + } + + /** + * Set a domain configuration + * + * @param string $domain + * @param array $value + * + * @throws \InvalidArgumentException + */ + public function set($domain, $key, $value = null) + { + $this->validateDomain($domain); + if (null !== $value) { + $this->profile[$domain][$key] = $value; + } else { + $this->profile[$domain] = $key; + } + } + + /** + * Get a domain configuration + * + * @param string $domain + * + * @return array + */ + public function get($domain, $key = null) + { + $this->validateDomain($domain); + + if (null === $key) { + return $this->profile[$domain]; + } + + if (!isset($this->profile[$domain][$key])) { + throw new \InvalidArgumentException(sprintf( + 'Unknown key "%s" for profile domain "%s"', + $key, $domain + )); + } + + return $this->profile[$domain][$key]; + } + + /** + * Return the name of this profile + * + * @return string $name + */ + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } +} diff --git a/src/PHPCR/Shell/Config/ProfileLoader.php b/src/PHPCR/Shell/Config/ProfileLoader.php new file mode 100644 index 00000000..68104372 --- /dev/null +++ b/src/PHPCR/Shell/Config/ProfileLoader.php @@ -0,0 +1,95 @@ +config = $config; + $this->filesystem = $filesystem ? : new Filesystem; + } + + protected function getProfileDir() + { + $dir = sprintf('%s/%s', $this->config->getConfigDir(), self::DIR_PROFILE);; + + return $dir; + } + + public function getProfilePath($name) + { + $dir = sprintf('%s/%s/%s.yml', $this->config->getConfigDir(), self::DIR_PROFILE, $name);; + + return $dir; + } + + public function getProfileNames() + { + $dir = $this->getProfileDir(); + + if (false === $this->filesystem->exists($dir)) { + return array(); + } + + $files = Finder::create()->files()->name('*.yml')->in($dir); + + $profiles = array(); + foreach ($files as $file) { + $profiles[] = substr($file->getBasename(), 0, -4); + } + + sort($profiles); + + return $profiles; + } + + public function loadProfile(Profile $profile) + { + $path = $this->getProfilePath($profile->getName()); + + if (!file_exists($path)) { + throw new \InvalidArgumentException(sprintf('Profile "%s" does not exist, expected to find it in "%s"', + $profile->getName(), $path + )); + } + + $contents = file_get_contents($path); + $data = Yaml::parse($contents); + + if (isset($data['transport'])) { + $profile->set('transport', $data['transport']); + } + + if (isset($data['phpcr'])) { + $profile->set('phpcr', $data['phpcr']); + } + } + + public function saveProfile(Profile $profile, $overwrite = false) + { + $profileDir = $this->getProfileDir(); + $path = $this->getProfilePath($profile->getName()); + + if (false === $overwrite && file_exists($path)) { + throw new FileExistsException(sprintf( + 'Profile already exists at "%s"', $path + )); + } + + $yaml = Yaml::dump($profile->toArray()); + + $this->filesystem->dumpFile($path, $yaml, 0600); + } +} diff --git a/src/PHPCR/Shell/Console/Application/ShellApplication.php b/src/PHPCR/Shell/Console/Application/ShellApplication.php index 2831d33f..02ae0c8c 100644 --- a/src/PHPCR/Shell/Console/Application/ShellApplication.php +++ b/src/PHPCR/Shell/Console/Application/ShellApplication.php @@ -27,6 +27,9 @@ use PHPCR\Shell\Event\PhpcrShellEvents; use PHPCR\Shell\Subscriber; use PHPCR\Shell\Console\Command\Phpcr\PhpcrShellCommand; +use PHPCR\Shell\Config\Profile; +use PHPCR\Shell\Transport\TransportRegistry; +use PHPCR\Shell\Config\ProfileLoader; /** * Main application for PHPCRSH @@ -43,9 +46,9 @@ class ShellApplication extends Application protected $initialized; /** - * @var \Symfony\Component\Console\Input\InputInterface + * @var boolean */ - protected $sessionInput; + protected $showUnsupported = false; /** * Constructor - name and version inherited from SessionApplication @@ -55,18 +58,40 @@ class ShellApplication extends Application public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') { parent::__construct($name, $version); + $this->profile = new Profile(); $this->dispatcher = new EventDispatcher(); + $this->transportRegistry = new TransportRegistry(); + $this->registerTransports(); + $this->registerHelpers(); + $this->registerEventListeners(); } /** - * The SessionInput is the input used to intialize the shell. - * It contains the connection parameters. + * Initialize the supported transports. * - * @param \Symfony\Component\Console\Input\InputInterface $input + * @access private */ - public function setSessionInput(InputInterface $input) + protected function registerTransports() { - $this->sessionInput = $input; + $transports = array( + new \PHPCR\Shell\Transport\Transport\DoctrineDbal($this->profile), + new \PHPCR\Shell\Transport\Transport\Jackrabbit($this->profile), + ); + + foreach ($transports as $transport) { + $this->transportRegistry->register($transport); + } + } + + /** + * If true, show all commands, even if they are unsupported by the + * transport. + * + * @param boolean $boolean + */ + public function setShowUnsupported($boolean) + { + $this->showUnsupported = $boolean; } /** @@ -82,15 +107,7 @@ public function init() return; } - if (null === $this->sessionInput) { - throw new \RuntimeException( - 'sessionInput has not been set.' - ); - } - - $this->registerHelpers(); $this->registerCommands(); - $this->registerEventListeners(); $event = new ApplicationInitEvent($this); $this->dispatcher->dispatch(PhpcrShellEvents::APPLICATION_INIT, $event); @@ -103,7 +120,7 @@ public function init() */ private function registerHelpers() { - $phpcrHelper = new PhpcrHelper($this->sessionInput); + $phpcrHelper = new PhpcrHelper($this->transportRegistry, $this->profile); $helpers = array( new ConfigHelper(), @@ -207,9 +224,21 @@ private function registerCommands() private function registerEventListeners() { + $this->dispatcher->addSubscriber(new Subscriber\ProfileFromSessionInputSubscriber()); + $this->dispatcher->addSubscriber(new Subscriber\ProfileWriterSubscriber( + new ProfileLoader( + $this->getHelperSet()->get('config') + ) + )); + $this->dispatcher->addSubscriber(new Subscriber\ProfileLoaderSubscriber( + new ProfileLoader( + $this->getHelperSet()->get('config') + ) + )); + $this->dispatcher->addSubscriber(new Subscriber\ConfigInitSubscriber()); $this->dispatcher->addSubscriber(new Subscriber\ExceptionSubscriber()); - $this->dispatcher->addSubscriber(new Subscriber\AliasSubscriber($this->getHelperSet()->get('config'))); + $this->dispatcher->addSubscriber(new Subscriber\AliasSubscriber($this->getHelperSet())); } /** @@ -286,13 +315,17 @@ public function renderException($exception, $output) public function add(Command $command) { if ($command instanceof PhpcrShellCommand) { - $showUnsupported = $this->sessionInput->getOption('unsupported'); - - if ($showUnsupported || $command->isSupported($this->getHelperSet()->get('repository'))) { + if ($this->showUnsupported || $command->isSupported($this->getHelperSet()->get('repository'))) { parent::add($command); } } else { parent::add($command); } } + + public function dispatchProfileInitEvent(InputInterface $sessionInput, OutputInterface $output) + { + $event = new Event\ProfileInitEvent($this->profile, $sessionInput, $output); + $this->dispatcher->dispatch(PhpcrShellEvents::PROFILE_INIT, $event); + } } diff --git a/src/PHPCR/Shell/Console/Command/ShellCommand.php b/src/PHPCR/Shell/Console/Command/ShellCommand.php index 02b33e4a..83ebb7dd 100644 --- a/src/PHPCR/Shell/Console/Command/ShellCommand.php +++ b/src/PHPCR/Shell/Console/Command/ShellCommand.php @@ -47,7 +47,8 @@ public function configure() new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version.'), new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output.'), new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output.'), - new InputOption('--transport', '-t', InputOption::VALUE_REQUIRED, 'Transport to use.', 'doctrine-dbal'), + + new InputOption('--transport', '-t', InputOption::VALUE_REQUIRED, 'Transport to use.'), new InputOption('--phpcr-username', '-pu', InputOption::VALUE_REQUIRED, 'PHPCR Username.', 'admin'), new InputOption('--phpcr-password', '-pp', InputOption::VALUE_OPTIONAL, 'PHPCR Password.', 'admin'), new InputOption('--phpcr-workspace','-pw', InputOption::VALUE_OPTIONAL, 'PHPCR Workspace.', 'default'), @@ -58,10 +59,10 @@ public function configure() new InputOption('--db-driver', '-dd', InputOption::VALUE_REQUIRED, 'Database Transport.', 'pdo_mysql'), new InputOption('--db-path', '-dP', InputOption::VALUE_REQUIRED, 'Database Path.'), new InputOption('--no-interaction', null, InputOption::VALUE_NONE, 'Turn off interaction (for testing purposes)'), + new InputOption('--repo-url', '-url', InputOption::VALUE_REQUIRED, 'URL of repository (e.g. for jackrabbit).', 'http://localhost:8080/server/'), + + new InputOption('--profile', '-p', InputOption::VALUE_OPTIONAL, 'Speicfy a profile name, use wit --transport to update or create'), new InputOption('--unsupported', null, InputOption::VALUE_NONE, 'Show all commands, including commands not supported by the repository'), - new InputOption('--repo-url', '-url', InputOption::VALUE_REQUIRED, 'URL of repository (e.g. for jackrabbit).', - 'http://localhost:8080/server/' - ), new InputOption('--command', null, InputOption::VALUE_REQUIRED, 'Run the given command'), )); } @@ -71,8 +72,12 @@ public function configure() */ public function execute(InputInterface $input, OutputInterface $output) { + $showUnspported = $input->getOption('unsupported'); + $application = $this->application; - $application->setSessionInput($input); + $application->setShowUnsupported($showUnspported); + $application->dispatchProfileInitEvent($input, $output); + $noInteraction = $input->getOption('no-interaction'); if ($command = $input->getOption('command')) { diff --git a/src/PHPCR/Shell/Console/Helper/PhpcrHelper.php b/src/PHPCR/Shell/Console/Helper/PhpcrHelper.php index 24137610..6e6160bb 100644 --- a/src/PHPCR/Shell/Console/Helper/PhpcrHelper.php +++ b/src/PHPCR/Shell/Console/Helper/PhpcrHelper.php @@ -2,10 +2,12 @@ namespace PHPCR\Shell\Console\Helper; -use PHPCR\Shell\PhpcrSession; use Symfony\Component\Console\Helper\Helper; -use Symfony\Component\Console\Input\InputInterface; + +use PHPCR\Shell\Config\Profile; +use PHPCR\Shell\PhpcrSession; use PHPCR\SimpleCredentials; +use PHPCR\Shell\Transport\TransportRegistryInterface; /** * Helper for managing PHPCR sessions @@ -14,13 +16,6 @@ */ class PhpcrHelper extends Helper { - /** - * Initial input which was used to initialize the shell. - * - * @var \Symfony\Component\Console\Input\InputInterface - */ - protected $sessionInput; - /** * Active PHPCR session * @@ -29,11 +24,11 @@ class PhpcrHelper extends Helper protected $session; /** - * Available transports + * The transport registry * - * @var \Jackalope\Transport\TransportInterface[] + * @var TransportRegistryInterface */ - protected $transports = array(); + protected $transportRegistry; /** * Lazy initialize PHPCR session @@ -43,11 +38,13 @@ class PhpcrHelper extends Helper protected $initialized = false; /** - * @param InputInterface $sessionInput + * @param TransportRegistryInterface $transportRegistry + * @param Profile $profile */ - public function __construct(InputInterface $sessionInput) + public function __construct(TransportRegistryInterface $transportRegistry, Profile $profile) { - $this->sessionInput = $sessionInput; + $this->transportRegistry = $transportRegistry; + $this->profile = $profile; } /** @@ -61,7 +58,6 @@ public function getName() private function init() { if (false === $this->initialized) { - $this->initializeTransports(); $this->initSession(); $this->initialized = true; } @@ -74,14 +70,15 @@ private function init() */ private function initSession() { - $transport = $this->getTransport(); - $repository = $transport->getRepository(); + $transport = $this->transportRegistry->getTransport($this->profile->get('transport', 'name')); + $repository = $transport->getRepository($this->profile->get('transport')); + $credentials = new SimpleCredentials( - $this->sessionInput->getOption('phpcr-username'), - $this->sessionInput->getOption('phpcr-password') + $this->profile->get('phpcr', 'username'), + $this->profile->get('phpcr', 'password') ); - $session = $repository->login($credentials, $this->sessionInput->getOption('phpcr-workspace')); + $session = $repository->login($credentials, $this->profile->get('phpcr', 'workspace')); // if you are wondering wtf here -- we wrap the PhpcrSession if (!$this->session) { @@ -91,44 +88,6 @@ private function initSession() } } - /** - * Return the transport as defined in the sessionInput - * - * @access private - */ - private function getTransport() - { - $transportName = $this->sessionInput->getOption('transport'); - - if (!isset($this->transports[$transportName])) { - throw new \InvalidArgumentException(sprintf( - 'Unknown transport "%s", I have "%s"', - $transportName, implode(', ', array_keys($this->transports)) - )); - } - - $transport = $this->transports[$transportName]; - - return $transport; - } - - /** - * Initialize the supported transports. - * - * @access private - */ - private function initializeTransports() - { - $transports = array( - new \PHPCR\Shell\Transport\DoctrineDbal($this->sessionInput), - new \PHPCR\Shell\Transport\Jackrabbit($this->sessionInput), - ); - - foreach ($transports as $transport) { - $this->transports[$transport->getName()] = $transport;; - } - } - /** * Change the current workspace * @@ -138,8 +97,8 @@ public function changeWorkspace($workspaceName) { $this->init(); $this->session->logout(); - $this->sessionInput->setOption('phpcr-workspace', $workspaceName); - $this->initSession($this->sessionInput); + $this->profile->set('phpcr', 'workspace', $workspaceName); + $this->initSession($this->profile); } /** @@ -155,11 +114,11 @@ public function relogin($username, $password, $workspaceName = null) $this->session->logout(); } - $this->sessionInput->setOption('phpcr-username', $username); - $this->sessionInput->setOption('phpcr-password', $password); + $this->profile->set('phpcr', 'username', $username); + $this->profile->set('phpcr', 'password', $password); if ($workspaceName) { - $this->sessionInput->setOption('phpcr-workspace', $workspaceName); + $this->profile->set('phpcr', 'workspace', $workspaceName); } $this->init(); diff --git a/src/PHPCR/Shell/Console/TransportInterface.php b/src/PHPCR/Shell/Console/TransportInterface.php deleted file mode 100644 index 6a545982..00000000 --- a/src/PHPCR/Shell/Console/TransportInterface.php +++ /dev/null @@ -1,10 +0,0 @@ - + */ class PhpcrShellEvents { + /** + * Fired when an exception is thrown + */ const COMMAND_EXCEPTION = 'command.exception'; + + /** + * Fired before a command is executed + */ const COMMAND_PRE_RUN = 'command.pre_run'; - const APPLICATION_INIT= 'application.init'; + + /** + * Fired when the application is initialized + */ + const APPLICATION_INIT = 'application.init'; + + /** + * Fired when the profile needs to be populated + */ + const PROFILE_INIT = 'profile.init'; } diff --git a/src/PHPCR/Shell/Event/ProfileInitEvent.php b/src/PHPCR/Shell/Event/ProfileInitEvent.php new file mode 100644 index 00000000..7ab509a4 --- /dev/null +++ b/src/PHPCR/Shell/Event/ProfileInitEvent.php @@ -0,0 +1,45 @@ + + */ +class ProfileInitEvent extends Event +{ + protected $profile; + protected $input; + protected $output; + + public function __construct(Profile $profile, InputInterface $input, OutputInterface $output) + { + $this->profile = $profile; + $this->input = $input; + $this->output = $output; + } + + public function getInput() + { + return $this->input; + } + + public function getOutput() + { + return $this->output; + } + + public function getProfile() + { + return $this->profile; + } +} diff --git a/src/PHPCR/Shell/Subscriber/AliasSubscriber.php b/src/PHPCR/Shell/Subscriber/AliasSubscriber.php index 8bd556c8..d3dfd58c 100644 --- a/src/PHPCR/Shell/Subscriber/AliasSubscriber.php +++ b/src/PHPCR/Shell/Subscriber/AliasSubscriber.php @@ -7,6 +7,7 @@ use PHPCR\Shell\Event\PhpcrShellEvents; use PHPCR\Shell\Event\CommandPreRunEvent; use PHPCR\Shell\Console\Input\StringInput; +use Symfony\Component\Console\Helper\HelperSet; /** * Check to see if the input references a command alias and @@ -16,11 +17,21 @@ */ class AliasSubscriber implements EventSubscriberInterface { - protected $config; + /** + * Lazy load helper + * + * @var HelperSet + */ + protected $helperSet; + + public function __construct(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } - public function __construct(ConfigHelper $config) + protected function getConfig() { - $this->config = $config; + return $this->helperSet->get('config'); } public static function getSubscribedEvents() @@ -42,7 +53,7 @@ public function handleAlias(CommandPreRunEvent $event) $commandName = $input->getFirstArgument(); - $aliasConfig = $this->config->getConfig('alias'); + $aliasConfig = $this->getConfig()->getConfig('alias'); if (!isset($aliasConfig[$commandName])) { return; diff --git a/src/PHPCR/Shell/Subscriber/ProfileFromSessionInputSubscriber.php b/src/PHPCR/Shell/Subscriber/ProfileFromSessionInputSubscriber.php new file mode 100644 index 00000000..1f1dc331 --- /dev/null +++ b/src/PHPCR/Shell/Subscriber/ProfileFromSessionInputSubscriber.php @@ -0,0 +1,48 @@ + 'handleProfileInit', + ); + } + + public function handleProfileInit(ProfileInitEvent $e) + { + $profile = $e->getProfile(); + $input = $e->getInput(); + + $transportOptions = array( + 'transport' => 'name', + 'db-username' => 'db_username', + 'db-name' => 'db_name', + 'db-password' => 'db_password', + 'db-host' => 'db_host', + 'db-path' => 'db_path', + 'db-driver' => 'db_driver', + 'repo-url' => 'repo_url', + ); + + $phpcrOptions = array( + 'phpcr-username' => 'username', + 'phpcr-password' => 'password', + 'phpcr-workspace' => 'workspace', + ); + + foreach ($transportOptions as $optionName => $configName) { + $profile->set('transport', $configName, (string) $input->getOption($optionName)); + } + + foreach ($phpcrOptions as $optionName => $configName) { + $profile->set('phpcr', $configName, $input->getOption($optionName)); + } + } +} diff --git a/src/PHPCR/Shell/Subscriber/ProfileLoaderSubscriber.php b/src/PHPCR/Shell/Subscriber/ProfileLoaderSubscriber.php new file mode 100644 index 00000000..081cd67b --- /dev/null +++ b/src/PHPCR/Shell/Subscriber/ProfileLoaderSubscriber.php @@ -0,0 +1,104 @@ + 'handleProfileInit', + ); + } + + public function __construct(ProfileLoader $profileLoader) + { + $this->profileLoader = $profileLoader; + $this->dialogHelper = new DialogHelper; + } + + public function handleProfileInit(ProfileInitEvent $e) + { + $profile = $e->getProfile(); + $input = $e->getInput(); + $output = $e->getOutput(); + $transport = $input->getOption('transport'); + $profileName = $input->getOption('profile'); + + if ($profileName && null === $transport) { + $profile->setName($profileName); + $this->profileLoader->loadProfile($profile); + $this->showProfile($output, $profile); + } + + if (null === $profileName && null === $transport) { + $profileNames = $this->profileLoader->getProfileNames(); + + if (count($profileNames) === 0) { + $output->writeln('No transport specified, and no profiles available.'); + $output->writeln(<<--profile option: + } + $ phpcrsh --profile=mywebsite --transport=doctrine-dbal --db-name=mywebsite + +Profiles can then be used later on: + + $ phpcrsh --profile=mywebsite +EOT + ); + + exit(1); + } + + $output->writeln('No connection parameters, given. Select an existing profile:'); + $output->writeln(''); + + foreach ($profileNames as $i => $profileName) { + $output->writeln(sprintf(' (%d) %s', $i, $profileName)); + } + + $output->writeln(''); + + $selectedName = null; + while (null === $selectedName) { + $number = $this->dialogHelper->ask($output, 'Enter profile number: '); + + if (!isset($profileNames[$number])) { + $output->writeln('Invalid selection!'); + continue; + } + + $selectedName = $profileNames[$number]; + } + + $profile->setName($selectedName); + $this->profileLoader->loadProfile($profile); + $this->showProfile($output, $profile); + } + } + + protected function showProfile(OutputInterface $output, Profile $profile) + { + $output->writeln(sprintf('Using profile "%s"', $profile->getName())); + } +} diff --git a/src/PHPCR/Shell/Subscriber/ProfileWriterSubscriber.php b/src/PHPCR/Shell/Subscriber/ProfileWriterSubscriber.php new file mode 100644 index 00000000..0c3af5e8 --- /dev/null +++ b/src/PHPCR/Shell/Subscriber/ProfileWriterSubscriber.php @@ -0,0 +1,56 @@ + 'handleProfileInit', + ); + } + + public function __construct(ProfileLoader $profileLoader) + { + $this->profileLoader = $profileLoader; + $this->dialogHelper = new DialogHelper; + } + + public function handleProfileInit(ProfileInitEvent $e) + { + $profile = $e->getProfile(); + $input = $e->getInput(); + $output = $e->getOutput(); + $transport = $input->getOption('transport'); + $profileName = $input->getOption('profile'); + + // if both transport and profile specified, then user wants + // to create or update an existing profile + if ($profileName && $transport) { + $profile->setName($profileName); + $overwrite = false; + + if (file_exists($this->profileLoader->getProfilePath($profileName))) { + $res = $this->dialogHelper->askConfirmation($output, sprintf('Update existing profile "%s"?', $profileName)); + $overwrite = true; + } else { + $res = $this->dialogHelper->askConfirmation($output, sprintf('Create new profile "%s"?', $profileName)); + } + + if ($res) { + $this->profileLoader->saveProfile($profile, $overwrite); + } + } + } +} + diff --git a/src/PHPCR/Shell/Transport/DoctrineDbal.php b/src/PHPCR/Shell/Transport/DoctrineDbal.php deleted file mode 100644 index c0e218cc..00000000 --- a/src/PHPCR/Shell/Transport/DoctrineDbal.php +++ /dev/null @@ -1,42 +0,0 @@ -input = $input; - } - - public function getName() - { - return 'doctrine-dbal'; - } - - public function getRepository() - { - $connection = DriverManager::getConnection($ops = array( - 'user' => $this->input->getOption('db-username'), - 'password' => $this->input->getOption('db-password'), - 'host' => $this->input->getOption('db-host'), - 'driver' => $this->input->getOption('db-driver'), - 'dbname' => $this->input->getOption('db-name'), - 'path' => $this->input->getOption('db-path'), - )); - - $factory = new RepositoryFactoryDoctrineDBAL(); - $repository = $factory->getRepository(array( - 'jackalope.doctrine_dbal_connection' => $connection - )); - - return $repository; - } -} diff --git a/src/PHPCR/Shell/Transport/Transport/DoctrineDbal.php b/src/PHPCR/Shell/Transport/Transport/DoctrineDbal.php new file mode 100644 index 00000000..a8821fa6 --- /dev/null +++ b/src/PHPCR/Shell/Transport/Transport/DoctrineDbal.php @@ -0,0 +1,35 @@ + $config['db_username'], + 'password' => $config['db_password'], + 'host' => $config['db_host'], + 'driver' => $config['db_driver'], + 'dbname' => $config['db_name'], + 'path' => $config['db_path'], + )); + + $factory = new RepositoryFactoryDoctrineDBAL(); + $repository = $factory->getRepository(array( + 'jackalope.doctrine_dbal_connection' => $connection + )); + + return $repository; + } +} diff --git a/src/PHPCR/Shell/Transport/Jackrabbit.php b/src/PHPCR/Shell/Transport/Transport/Jackrabbit.php similarity index 58% rename from src/PHPCR/Shell/Transport/Jackrabbit.php rename to src/PHPCR/Shell/Transport/Transport/Jackrabbit.php index 4f330a97..72797f32 100644 --- a/src/PHPCR/Shell/Transport/Jackrabbit.php +++ b/src/PHPCR/Shell/Transport/Transport/Jackrabbit.php @@ -1,27 +1,22 @@ input = $input; - } - public function getName() { return 'jackrabbit'; } - public function getRepository() + public function getRepository(array $config) { $params = array( - 'jackalope.jackrabbit_uri' => $this->input->getOption('repo-url'), + 'jackalope.jackrabbit_uri' => $config['repo_url'], ); $factory = new RepositoryFactoryJackrabbit(); $repository = $factory->getRepository($params); diff --git a/src/PHPCR/Shell/Transport/TransportInterface.php b/src/PHPCR/Shell/Transport/TransportInterface.php new file mode 100644 index 00000000..c847601b --- /dev/null +++ b/src/PHPCR/Shell/Transport/TransportInterface.php @@ -0,0 +1,15 @@ + + */ +interface TransportInterface +{ + public function getName(); + + public function getRepository(array $config); +} diff --git a/src/PHPCR/Shell/Transport/TransportRegistry.php b/src/PHPCR/Shell/Transport/TransportRegistry.php new file mode 100644 index 00000000..7e26a27e --- /dev/null +++ b/src/PHPCR/Shell/Transport/TransportRegistry.php @@ -0,0 +1,26 @@ +transports[$transport->getname()] = $transport; + } + + public function getTransportNames() + { + return array_keys($this->transports); + } + + public function getTransport($name) + { + return $this->transports[$name]; + } +} diff --git a/src/PHPCR/Shell/Transport/TransportRegistryInterface.php b/src/PHPCR/Shell/Transport/TransportRegistryInterface.php new file mode 100644 index 00000000..f83dc9fc --- /dev/null +++ b/src/PHPCR/Shell/Transport/TransportRegistryInterface.php @@ -0,0 +1,31 @@ + + */ +interface TransportRegistryInterface +{ + /** + * Return all of the registered transport names + * + * @return array + */ + public function getTransportNames(); + + /** + * Return the transport with the given name + * + * @param string $name + * + * @return PHPCR\Shell\Transport\Transport\TransportInterface + */ + public function getTransport($name); +} diff --git a/tests/PHPCR/Shell/Application/ShellApplicationTest.php b/tests/PHPCR/Shell/Application/ShellApplicationTest.php deleted file mode 100644 index 1395f1ef..00000000 --- a/tests/PHPCR/Shell/Application/ShellApplicationTest.php +++ /dev/null @@ -1,49 +0,0 @@ -transport = $this->getMock( - 'PHPCR\Shell\Console\TransportInterface' - ); - - $this->sessionInput = $this->getMock( - 'Symfony\Component\Console\Input\InputInterface' - ); - $this->sessionInput->expects($this->any()) - ->method('getOption') - ->will($this->returnCallback(function ($name) { - $options = array( - 'transport' => 'test', - 'phpcr-username' => 'test-username', - 'phpcr-password' => 'test-password', - 'phpcr-workspace' => 'test-workspace', - ); - - return $options[$name]; - })); - - $this->session = $this->getMock( - 'PHPCR\SessionInterface' - ); - - $this->repository = $this->getMock( - 'PHPCR\RepositoryInterface' - ); - - $this->application = new ShellApplication('phpcr','v0.test', $this->sessionInput, array($this->transport)); - $this->application->setSessionInput($this->sessionInput); - $this->application->setAutoExit(false); - } - - public function testShellApplication() - { - $this->application->run(); - } -}