Skip to content

Commit 4dd3f7d

Browse files
committed
First commit
0 parents  commit 4dd3f7d

17 files changed

+626
-0
lines changed

README.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Shell for PHPCR
2+
---------------
3+
4+
````bash
5+
$ phpcr --transport=doctrine-dbal --username=foo --password=bar

bin/phpcr

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
$vendorDir = __DIR__.'/../vendor';
5+
6+
$file = $vendorDir.'/autoload.php';
7+
8+
if (file_exists($file)) {
9+
$autoload = require_once $file;
10+
} else {
11+
echo 'Cannot find the vendor directory, have you executed composer install?';
12+
exit(1);
13+
}
14+
15+
$shell = new PHPCR\Shell\Console\Application\SessionApplication();
16+
$shell->run();

composer.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "phpcr/shell",
3+
"description": "Shell for PHPCR",
4+
"require": {
5+
"symfony/console": "2.1.*",
6+
"jackalope/jackalope-doctrine-dbal": "dev-master",
7+
"jackalope/jackalope": "dev-master",
8+
"phpcr/phpcr": "dev-master",
9+
"phpcr/phpcr-utils": "dev-master"
10+
},
11+
"license": "MIT",
12+
"authors": [
13+
{
14+
"name": "dantleech",
15+
"email": "daniel@dantleech.com"
16+
}
17+
],
18+
"autoload": {
19+
"psr-0": {"": "src"}
20+
}
21+
}

src/PHPCR/Shell/Console/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
vendor
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
namespace PHPCR\Shell\Console\Application;
4+
5+
use Symfony\Component\Console\Application as BaseApplication;
6+
use Symfony\Component\Console\Input\InputOption;
7+
use Symfony\Component\Console\Input\InputArgument;
8+
use Symfony\Component\Console\Input\InputDefinition;
9+
use PHPCR\Shell\Console\Command\QueryCommand;
10+
use Symfony\Component\Console\Input\InputInterface;
11+
use Symfony\Component\Console\Output\OutputInterface;
12+
use PHPCR\Shell\Console\Command\DoctrineDbalInitCommand;
13+
use PHPCR\Shell\Console\Helper\DoctrineDbalHelper;
14+
use PHPCR\Shell\Console\Helper\ShellHelper;
15+
use Symfony\Component\Console\Command\Command;
16+
use PHPCR\Shell\Console\Command\ShellCommand;
17+
use PHPCR\Shell\Console\Input\ArgvInput;
18+
19+
class SessionApplication extends BaseApplication
20+
{
21+
public function __construct()
22+
{
23+
parent::__construct('PHPCR', '1.0');
24+
25+
$command = new ShellCommand();
26+
$command->setApplication($this);
27+
$this->add($command);
28+
}
29+
30+
public function getDefaultInputDefinition()
31+
{
32+
return new InputDefinition(array());
33+
}
34+
35+
public function run()
36+
{
37+
parent::run();
38+
}
39+
40+
protected function getCommandName($input)
41+
{
42+
return 'phpcr_shell';
43+
}
44+
}
45+
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
<?php
2+
3+
namespace PHPCR\Shell\Console\Application;
4+
5+
use Symfony\Component\Console\Application;
6+
use Symfony\Component\Console\Output\ConsoleOutput;
7+
use Symfony\Component\Process\ProcessBuilder;
8+
use Symfony\Component\Process\PhpExecutableFinder;
9+
use Symfony\Component\Console\Shell as BaseShell;
10+
use PHPCR\Shell\Console\Input\StringInput;
11+
12+
class Shell
13+
{
14+
private $application;
15+
private $history;
16+
private $output;
17+
private $hasReadline;
18+
private $prompt;
19+
private $processIsolation;
20+
21+
/**
22+
* Constructor.
23+
*
24+
* If there is no readline support for the current PHP executable
25+
* a \RuntimeException exception is thrown.
26+
*
27+
* @param Application $application An application instance
28+
*/
29+
public function __construct(Application $application)
30+
{
31+
$this->hasReadline = function_exists('readline');
32+
$this->application = $application;
33+
$this->history = getenv('HOME').'/.history_'.$application->getName();
34+
$this->output = new ConsoleOutput();
35+
$this->prompt = $application->getName().' > ';
36+
$this->processIsolation = false;
37+
}
38+
39+
/**
40+
* Runs the shell.
41+
*/
42+
public function run()
43+
{
44+
$this->application->setAutoExit(false);
45+
$this->application->setCatchExceptions(true);
46+
47+
if ($this->hasReadline) {
48+
readline_read_history($this->history);
49+
readline_completion_function(array($this, 'autocompleter'));
50+
}
51+
52+
$this->output->writeln($this->getHeader());
53+
54+
while (true) {
55+
$command = $this->readline();
56+
57+
if (false === $command) {
58+
$this->output->writeln("\n");
59+
60+
break;
61+
}
62+
63+
if ($this->hasReadline) {
64+
readline_add_history($command);
65+
readline_write_history($this->history);
66+
}
67+
68+
if ($this->processIsolation) {
69+
$pb = new ProcessBuilder();
70+
71+
$process = $pb
72+
->add($php)
73+
->add($_SERVER['argv'][0])
74+
->add($command)
75+
->inheritEnvironmentVariables(true)
76+
->getProcess()
77+
;
78+
79+
$output = $this->output;
80+
$process->run(function($type, $data) use ($output) {
81+
$output->writeln($data);
82+
});
83+
84+
$ret = $process->getExitCode();
85+
} else {
86+
$ret = $this->application->run(new StringInput($command), $this->output);
87+
}
88+
89+
if (0 !== $ret) {
90+
$this->output->writeln(sprintf('<error>The command terminated with an error status (%s)</error>', $ret));
91+
}
92+
}
93+
}
94+
95+
/**
96+
* Returns the shell header.
97+
*
98+
* @return string The header string
99+
*/
100+
protected function getHeader()
101+
{
102+
return <<<EOF
103+
104+
Welcome to the <info>{$this->application->getName()}</info> shell (<comment>{$this->application->getVersion()}</comment>).
105+
106+
At the prompt, type <comment>help</comment> for some help,
107+
or <comment>list</comment> to get a list of available commands.
108+
109+
To exit the shell, type <comment>^D</comment>.
110+
111+
EOF;
112+
}
113+
114+
/**
115+
* Tries to return autocompletion for the current entered text.
116+
*
117+
* @param string $text The last segment of the entered text
118+
*
119+
* @return Boolean|array A list of guessed strings or true
120+
*/
121+
private function autocompleter($text)
122+
{
123+
$info = readline_info();
124+
$text = substr($info['line_buffer'], 0, $info['end']);
125+
126+
if ($info['point'] !== $info['end']) {
127+
return true;
128+
}
129+
130+
// task name?
131+
if (false === strpos($text, ' ') || !$text) {
132+
return array_keys($this->application->all());
133+
}
134+
135+
// options and arguments?
136+
try {
137+
$command = $this->application->find(substr($text, 0, strpos($text, ' ')));
138+
} catch (\Exception $e) {
139+
return true;
140+
}
141+
142+
$list = array('--help');
143+
foreach ($command->getDefinition()->getOptions() as $option) {
144+
$list[] = '--'.$option->getName();
145+
}
146+
147+
return $list;
148+
}
149+
150+
/**
151+
* Reads a single line from standard input.
152+
*
153+
* @return string The single line from standard input
154+
*/
155+
private function readline()
156+
{
157+
if ($this->hasReadline) {
158+
$line = readline($this->prompt);
159+
} else {
160+
$this->output->write($this->prompt);
161+
$line = fgets(STDIN, 1024);
162+
$line = (!$line && strlen($line) == 0) ? false : rtrim($line);
163+
}
164+
165+
return $line;
166+
}
167+
168+
public function getProcessIsolation()
169+
{
170+
return $this->processIsolation;
171+
}
172+
173+
public function setProcessIsolation($processIsolation)
174+
{
175+
$this->processIsolation = (Boolean) $processIsolation;
176+
}
177+
}
178+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace PHPCR\Shell\Console\Application;
4+
5+
use Symfony\Component\Console\Application;
6+
use PHPCR\SessionInterface;
7+
use Symfony\Component\Console\Input\InputInterface;
8+
use Symfony\Component\Console\Output\OutputInterface;
9+
use PHPCR\Shell\Console\Command\SelectCommand;
10+
use PHPCR\Shell\Console\Command\AbstractSessionCommand;
11+
12+
class ShellApplication extends Application
13+
{
14+
public function __construct(SessionInterface $session)
15+
{
16+
parent::__construct('PHPCR', '1.0');
17+
18+
$this->add(
19+
new SelectCommand()
20+
);
21+
22+
foreach ($this->all() as $command) {
23+
if ($command instanceof AbstractSessionCommand) {
24+
$command->setSession($session);
25+
}
26+
}
27+
}
28+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace PHPCR\Shell\Console\Command;
4+
5+
use PHPCR\SessionInterface;
6+
use Symfony\Component\Console\Command\Command;
7+
8+
class AbstractSessionCommand extends Command
9+
{
10+
protected $session;
11+
12+
public function setSession(SessionInterface $session)
13+
{
14+
$this->session = $session;
15+
}
16+
17+
public function getSession()
18+
{
19+
if (!$this->session) {
20+
throw new \InvalidArgumentException('Session has not been set.');
21+
}
22+
return $this->session;
23+
}
24+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace PHPCR\Shell\Console\Command;
4+
5+
use Jackalope\Tools\Console\Command\InitDoctrineDbalCommand;
6+
7+
class DoctrineDbalInitCommand extends InitDoctrineDbalCommand
8+
{
9+
public function configure()
10+
{
11+
parent::configure();
12+
$this->setName('dbal-init');
13+
}
14+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace PHPCR\Shell\Console\Command;
4+
5+
use PHPCR\Shell\Console\ShellQueryCommand;
6+
use Symfony\Component\Console\Input\InputInterface;
7+
use Symfony\Component\Console\Output\OutputInterface;
8+
use Symfony\Component\Console\Input\InputOption;
9+
10+
class SelectCommand extends AbstractSessionCommand
11+
{
12+
protected function configure()
13+
{
14+
$this->setName('select');
15+
$this->addArgument('query');
16+
$this->addOption('language', 'l', InputOption::VALUE_OPTIONAL, 'The query language (e.g. jcr-sql2', 'jcr-sql2');
17+
$this->addOption('limit', null, InputOption::VALUE_OPTIONAL, 'The query limit', 0);
18+
$this->addOption('offset', null, InputOption::VALUE_OPTIONAL, 'The query offset', 0);
19+
}
20+
21+
public function execute(InputInterface $input, OutputInterface $output)
22+
{
23+
$sql = $input->getArgument('query');
24+
$language = strtoupper($input->getOption('language'));
25+
$limit = $input->getOption('limit');
26+
$offset = $input->getOption('offset');
27+
28+
$session = $this->getSession();
29+
$qm = $session->getWorkspace()->getQueryManager();
30+
31+
$query = $qm->createQuery($sql, $language);
32+
33+
if ($limit) {
34+
$query->setLimit($limit);
35+
}
36+
37+
if ($offset) {
38+
$query->setOffset($offset);
39+
}
40+
41+
$start = microtime(true);
42+
$result = $query->execute();
43+
$elapsed = microtime(true) - $start;
44+
var_dump($result);die();
45+
}
46+
}

0 commit comments

Comments
 (0)