First commit

This commit is contained in:
Theodotos Andreou 2018-01-14 13:10:16 +00:00
commit c6e2478c40
13918 changed files with 2303184 additions and 0 deletions

View file

@ -0,0 +1,112 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion;
use Psy\TabCompletion\Matcher\AbstractMatcher;
/**
* A readline tab completion service.
*
* @author Marc Garcia <markcial@gmail.com>
*/
class AutoCompleter
{
/** @var Matcher\AbstractMatcher[] */
protected $matchers;
/**
* Register a tab completion Matcher.
*
* @param AbstractMatcher $matcher
*/
public function addMatcher(AbstractMatcher $matcher)
{
$this->matchers[] = $matcher;
}
/**
* Activate readline tab completion.
*/
public function activate()
{
readline_completion_function(array(&$this, 'callback'));
}
/**
* Handle readline completion.
*
* @param string $input Readline current word
* @param int $index Current word index
* @param array $info readline_info() data
*
* @return array
*/
public function processCallback($input, $index, $info = array())
{
// Some (Windows?) systems provide incomplete `readline_info`, so let's
// try to work around it.
$line = $info['line_buffer'];
if (isset($info['end'])) {
$line = substr($line, 0, $info['end']);
}
if ($line === '' && $input !== '') {
$line = $input;
}
$tokens = token_get_all('<?php ' . $line);
// remove whitespaces
$tokens = array_filter($tokens, function ($token) {
return !AbstractMatcher::tokenIs($token, AbstractMatcher::T_WHITESPACE);
});
$matches = array();
foreach ($this->matchers as $matcher) {
if ($matcher->hasMatched($tokens)) {
$matches = array_merge($matcher->getMatches($tokens), $matches);
}
}
$matches = array_unique($matches);
return !empty($matches) ? $matches : array('');
}
/**
* The readline_completion_function callback handler.
*
* @see processCallback
*
* @param $input
* @param $index
*
* @return array
*/
public function callback($input, $index)
{
return $this->processCallback($input, $index, readline_info());
}
/**
* Remove readline callback handler on destruct.
*/
public function __destruct()
{
// PHP didn't implement the whole readline API when they first switched
// to libedit. And they still haven't.
//
// So this is a thing to make PsySH work on 5.3.x:
if (function_exists('readline_callback_handler_remove')) {
readline_callback_handler_remove();
}
}
}

View file

@ -0,0 +1,65 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion\Matcher;
use Psy\Context;
use Psy\ContextAware;
/**
* An abstract tab completion Matcher which implements ContextAware.
*
* The AutoCompleter service will inject a Context instance into all
* ContextAware Matchers.
*
* @author Marc Garcia <markcial@gmail.com>
*/
abstract class AbstractContextAwareMatcher extends AbstractMatcher implements ContextAware
{
/**
* Context instance (for ContextAware interface).
*
* @var Context
*/
protected $context;
/**
* ContextAware interface.
*
* @param Context $context
*/
public function setContext(Context $context)
{
$this->context = $context;
}
/**
* Get a Context variable by name.
*
* @param $var Variable name
*
* @return mixed
*/
protected function getVariable($var)
{
return $this->context->get($var);
}
/**
* Get all variables in the current Context.
*
* @return array
*/
protected function getVariables()
{
return $this->context->getAll();
}
}

View file

@ -0,0 +1,76 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion\Matcher;
abstract class AbstractDefaultParametersMatcher extends AbstractContextAwareMatcher
{
/**
* @param \ReflectionParameter[] $reflectionParameters
*
* @return array
*/
public function getDefaultParameterCompletion(array $reflectionParameters)
{
$parametersProcessed = array();
foreach ($reflectionParameters as $parameter) {
if (!$parameter->isDefaultValueAvailable()) {
return array();
}
$defaultValue = $this->valueToShortString($parameter->getDefaultValue());
$parametersProcessed[] = "\${$parameter->getName()} = $defaultValue";
}
if (empty($parametersProcessed)) {
return array();
}
return array(implode(', ', $parametersProcessed) . ')');
}
/**
* Takes in the default value of a parameter and turns it into a
* string representation that fits inline.
* This is not 100% true to the original (newlines are inlined, for example).
*
* @param mixed $value
*
* @return string
*/
private function valueToShortString($value)
{
if (!is_array($value)) {
return json_encode($value);
}
$chunks = array();
$chunksSequential = array();
$allSequential = true;
foreach ($value as $key => $item) {
$allSequential = $allSequential && is_numeric($key) && $key === count($chunksSequential);
$keyString = $this->valueToShortString($key);
$itemString = $this->valueToShortString($item);
$chunks[] = "{$keyString} => {$itemString}";
$chunksSequential[] = $itemString;
}
$chunksToImplode = $allSequential ? $chunksSequential : $chunks;
return '[' . implode(', ', $chunksToImplode) . ']';
}
}

View file

@ -0,0 +1,186 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion\Matcher;
/**
* Abstract tab completion Matcher.
*
* @author Marc Garcia <markcial@gmail.com>
*/
abstract class AbstractMatcher
{
/** Syntax types */
const CONSTANT_SYNTAX = '^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$';
const VAR_SYNTAX = '^\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$';
const MISC_OPERATORS = '+-*/^|&';
/** Token values */
const T_OPEN_TAG = 'T_OPEN_TAG';
const T_VARIABLE = 'T_VARIABLE';
const T_OBJECT_OPERATOR = 'T_OBJECT_OPERATOR';
const T_DOUBLE_COLON = 'T_DOUBLE_COLON';
const T_NEW = 'T_NEW';
const T_CLONE = 'T_CLONE';
const T_NS_SEPARATOR = 'T_NS_SEPARATOR';
const T_STRING = 'T_STRING';
const T_WHITESPACE = 'T_WHITESPACE';
const T_AND_EQUAL = 'T_AND_EQUAL';
const T_BOOLEAN_AND = 'T_BOOLEAN_AND';
const T_BOOLEAN_OR = 'T_BOOLEAN_OR';
const T_ENCAPSED_AND_WHITESPACE = 'T_ENCAPSED_AND_WHITESPACE';
const T_REQUIRE = 'T_REQUIRE';
const T_REQUIRE_ONCE = 'T_REQUIRE_ONCE';
const T_INCLUDE = 'T_INCLUDE';
const T_INCLUDE_ONCE = 'T_INCLUDE_ONCE';
/**
* Check whether this matcher can provide completions for $tokens.
*
* @param array $tokens Tokenized readline input
*
* @return bool
*/
public function hasMatched(array $tokens)
{
return false;
}
/**
* Get current readline input word.
*
* @param array $tokens Tokenized readline input (see token_get_all)
*
* @return string
*/
protected function getInput(array $tokens)
{
$var = '';
$firstToken = array_pop($tokens);
if (self::tokenIs($firstToken, self::T_STRING)) {
$var = $firstToken[1];
}
return $var;
}
/**
* Get current namespace and class (if any) from readline input.
*
* @param array $tokens Tokenized readline input (see token_get_all)
*
* @return string
*/
protected function getNamespaceAndClass($tokens)
{
$class = '';
while (self::hasToken(
array(self::T_NS_SEPARATOR, self::T_STRING),
$token = array_pop($tokens)
)) {
$class = $token[1] . $class;
}
return $class;
}
/**
* Provide tab completion matches for readline input.
*
* @param array $tokens information substracted with get_token_all
* @param array $info readline_info object
*
* @return array The matches resulting from the query
*/
abstract public function getMatches(array $tokens, array $info = array());
/**
* Check whether $word starts with $prefix.
*
* @param string $prefix
* @param string $word
*
* @return bool
*/
public static function startsWith($prefix, $word)
{
return preg_match(sprintf('#^%s#', $prefix), $word);
}
/**
* Check whether $token matches a given syntax pattern.
*
* @param mixed $token A PHP token (see token_get_all)
* @param string $syntax A syntax pattern (default: variable pattern)
*
* @return bool
*/
public static function hasSyntax($token, $syntax = self::VAR_SYNTAX)
{
if (!is_array($token)) {
return false;
}
$regexp = sprintf('#%s#', $syntax);
return (bool) preg_match($regexp, $token[1]);
}
/**
* Check whether $token type is $which.
*
* @param string $which A PHP token type
* @param mixed $token A PHP token (see token_get_all)
*
* @return bool
*/
public static function tokenIs($token, $which)
{
if (!is_array($token)) {
return false;
}
return token_name($token[0]) === $which;
}
/**
* Check whether $token is an operator.
*
* @param mixed $token A PHP token (see token_get_all)
*
* @return bool
*/
public static function isOperator($token)
{
if (!is_string($token)) {
return false;
}
return strpos(self::MISC_OPERATORS, $token) !== false;
}
/**
* Check whether $token type is present in $coll.
*
* @param array $coll A list of token types
* @param mixed $token A PHP token (see token_get_all)
*
* @return bool
*/
public static function hasToken(array $coll, $token)
{
if (!is_array($token)) {
return false;
}
return in_array(token_name($token[0]), $coll);
}
}

View file

@ -0,0 +1,84 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion\Matcher;
/**
* A class attribute tab completion Matcher.
*
* Given a namespace and class, this matcher provides completion for constants
* and static properties.
*
* @author Marc Garcia <markcial@gmail.com>
*/
class ClassAttributesMatcher extends AbstractMatcher
{
/**
* {@inheritdoc}
*/
public function getMatches(array $tokens, array $info = array())
{
$input = $this->getInput($tokens);
$firstToken = array_pop($tokens);
if (self::tokenIs($firstToken, self::T_STRING)) {
// second token is the nekudotayim operator
array_pop($tokens);
}
$class = $this->getNamespaceAndClass($tokens);
try {
$reflection = new \ReflectionClass($class);
} catch (\ReflectionException $re) {
return array();
}
$vars = array_merge(
array_map(
function ($var) {
return '$' . $var;
},
array_keys($reflection->getStaticProperties())
),
array_keys($reflection->getConstants())
);
return array_map(
function ($name) use ($class) {
return $class . '::' . $name;
},
array_filter(
$vars,
function ($var) use ($input) {
return AbstractMatcher::startsWith($input, $var);
}
)
);
}
/**
* {@inheritdoc}
*/
public function hasMatched(array $tokens)
{
$token = array_pop($tokens);
$prevToken = array_pop($tokens);
switch (true) {
case self::tokenIs($prevToken, self::T_DOUBLE_COLON) && self::tokenIs($token, self::T_STRING):
case self::tokenIs($token, self::T_DOUBLE_COLON):
return true;
}
return false;
}
}

View file

@ -0,0 +1,64 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion\Matcher;
class ClassMethodDefaultParametersMatcher extends AbstractDefaultParametersMatcher
{
public function getMatches(array $tokens, array $info = array())
{
$openBracket = array_pop($tokens);
$functionName = array_pop($tokens);
$methodOperator = array_pop($tokens);
$class = $this->getNamespaceAndClass($tokens);
try {
$reflection = new \ReflectionClass($class);
} catch (\ReflectionException $e) {
// In this case the class apparently does not exist, so we can do nothing
return array();
}
$methods = $reflection->getMethods(\ReflectionMethod::IS_STATIC);
foreach ($methods as $method) {
if ($method->getName() === $functionName[1]) {
return $this->getDefaultParameterCompletion($method->getParameters());
}
}
return array();
}
public function hasMatched(array $tokens)
{
$openBracket = array_pop($tokens);
if ($openBracket !== '(') {
return false;
}
$functionName = array_pop($tokens);
if (!self::tokenIs($functionName, self::T_STRING)) {
return false;
}
$operator = array_pop($tokens);
if (!self::tokenIs($operator, self::T_DOUBLE_COLON)) {
return false;
}
return true;
}
}

View file

@ -0,0 +1,76 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion\Matcher;
/**
* A class method tab completion Matcher.
*
* Given a namespace and class, this matcher provides completion for static
* methods.
*
* @author Marc Garcia <markcial@gmail.com>
*/
class ClassMethodsMatcher extends AbstractMatcher
{
/**
* {@inheritdoc}
*/
public function getMatches(array $tokens, array $info = array())
{
$input = $this->getInput($tokens);
$firstToken = array_pop($tokens);
if (self::tokenIs($firstToken, self::T_STRING)) {
// second token is the nekudotayim operator
array_pop($tokens);
}
$class = $this->getNamespaceAndClass($tokens);
try {
$reflection = new \ReflectionClass($class);
} catch (\ReflectionException $re) {
return array();
}
$methods = $reflection->getMethods(\ReflectionMethod::IS_STATIC);
$methods = array_map(function (\ReflectionMethod $method) {
return $method->getName();
}, $methods);
return array_map(
function ($name) use ($class) {
return $class . '::' . $name;
},
array_filter($methods, function ($method) use ($input) {
return AbstractMatcher::startsWith($input, $method);
})
);
}
/**
* {@inheritdoc}
*/
public function hasMatched(array $tokens)
{
$token = array_pop($tokens);
$prevToken = array_pop($tokens);
switch (true) {
case self::tokenIs($prevToken, self::T_DOUBLE_COLON) && self::tokenIs($token, self::T_STRING):
case self::tokenIs($token, self::T_DOUBLE_COLON):
return true;
}
return false;
}
}

View file

@ -0,0 +1,77 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion\Matcher;
/**
* A class name tab completion Matcher.
*
* This matcher provides completion for all declared classes.
*
* @author Marc Garcia <markcial@gmail.com>
*/
class ClassNamesMatcher extends AbstractMatcher
{
/**
* {@inheritdoc}
*/
public function getMatches(array $tokens, array $info = array())
{
$class = $this->getNamespaceAndClass($tokens);
if (strlen($class) > 0 && $class[0] === '\\') {
$class = substr($class, 1, strlen($class));
}
$quotedClass = preg_quote($class);
return array_map(
function ($className) use ($class) {
// get the number of namespace separators
$nsPos = substr_count($class, '\\');
$pieces = explode('\\', $className);
//$methods = Mirror::get($class);
return implode('\\', array_slice($pieces, $nsPos, count($pieces)));
},
array_filter(
get_declared_classes(),
function ($className) use ($quotedClass) {
return AbstractMatcher::startsWith($quotedClass, $className);
}
)
);
}
/**
* {@inheritdoc}
*/
public function hasMatched(array $tokens)
{
$token = array_pop($tokens);
$prevToken = array_pop($tokens);
$blacklistedTokens = array(
self::T_INCLUDE, self::T_INCLUDE_ONCE, self::T_REQUIRE, self::T_REQUIRE_ONCE,
);
switch (true) {
case self::hasToken(array($blacklistedTokens), $token):
case self::hasToken(array($blacklistedTokens), $prevToken):
case is_string($token) && $token === '$':
return false;
case self::hasToken(array(self::T_NEW, self::T_OPEN_TAG, self::T_NS_SEPARATOR), $prevToken):
case self::hasToken(array(self::T_NEW, self::T_OPEN_TAG, self::T_NS_SEPARATOR), $token):
case self::hasToken(array(self::T_OPEN_TAG, self::T_VARIABLE), $token):
case self::isOperator($token):
return true;
}
return false;
}
}

View file

@ -0,0 +1,114 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion\Matcher;
use Psy\Command\Command;
/**
* A Psy Command tab completion Matcher.
*
* This matcher provides completion for all registered Psy Command names and
* aliases.
*
* @author Marc Garcia <markcial@gmail.com>
*/
class CommandsMatcher extends AbstractMatcher
{
/** @var string[] */
protected $commands = array();
/**
* CommandsMatcher constructor.
*
* @param Command[] $commands
*/
public function __construct(array $commands)
{
$this->setCommands($commands);
}
/**
* Set Commands for completion.
*
* @param Command[] $commands
*/
public function setCommands(array $commands)
{
$names = array();
foreach ($commands as $command) {
$names = array_merge(array($command->getName()), $names);
$names = array_merge($command->getAliases(), $names);
}
$this->commands = $names;
}
/**
* Check whether a command $name is defined.
*
* @param string $name
*
* @return bool
*/
protected function isCommand($name)
{
return in_array($name, $this->commands);
}
/**
* Check whether input matches a defined command.
*
* @param string $name
*
* @return bool
*/
protected function matchCommand($name)
{
foreach ($this->commands as $cmd) {
if ($this->startsWith($name, $cmd)) {
return true;
}
}
return false;
}
/**
* {@inheritdoc}
*/
public function getMatches(array $tokens, array $info = array())
{
$input = $this->getInput($tokens);
return array_filter($this->commands, function ($command) use ($input) {
return AbstractMatcher::startsWith($input, $command);
});
}
/**
* {@inheritdoc}
*/
public function hasMatched(array $tokens)
{
/* $openTag */ array_shift($tokens);
$command = array_shift($tokens);
switch (true) {
case self::tokenIs($command, self::T_STRING) &&
!$this->isCommand($command[1]) &&
$this->matchCommand($command[1]) &&
empty($tokens):
return true;
}
return false;
}
}

View file

@ -0,0 +1,54 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion\Matcher;
/**
* A constant name tab completion Matcher.
*
* This matcher provides completion for all defined constants.
*
* @author Marc Garcia <markcial@gmail.com>
*/
class ConstantsMatcher extends AbstractMatcher
{
/**
* {@inheritdoc}
*/
public function getMatches(array $tokens, array $info = array())
{
$const = $this->getInput($tokens);
return array_filter(array_keys(get_defined_constants()), function ($constant) use ($const) {
return AbstractMatcher::startsWith($const, $constant);
});
}
/**
* {@inheritdoc}
*/
public function hasMatched(array $tokens)
{
$token = array_pop($tokens);
$prevToken = array_pop($tokens);
switch (true) {
case self::tokenIs($prevToken, self::T_NEW):
case self::tokenIs($prevToken, self::T_NS_SEPARATOR):
return false;
case self::hasToken(array(self::T_OPEN_TAG, self::T_STRING), $token):
case self::isOperator($token):
return true;
}
return false;
}
}

View file

@ -0,0 +1,53 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion\Matcher;
class FunctionDefaultParametersMatcher extends AbstractDefaultParametersMatcher
{
public function getMatches(array $tokens, array $info = array())
{
array_pop($tokens); // open bracket
$functionName = array_pop($tokens);
try {
$reflection = new \ReflectionFunction($functionName[1]);
} catch (\ReflectionException $e) {
return array();
}
$parameters = $reflection->getParameters();
return $this->getDefaultParameterCompletion($parameters);
}
public function hasMatched(array $tokens)
{
$openBracket = array_pop($tokens);
if ($openBracket !== '(') {
return false;
}
$functionName = array_pop($tokens);
if (!self::tokenIs($functionName, self::T_STRING)) {
return false;
}
if (!function_exists($functionName[1])) {
return false;
}
return true;
}
}

View file

@ -0,0 +1,56 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion\Matcher;
/**
* A function name tab completion Matcher.
*
* This matcher provides completion for all internal and user-defined functions.
*
* @author Marc Garcia <markcial@gmail.com>
*/
class FunctionsMatcher extends AbstractMatcher
{
/**
* {@inheritdoc}
*/
public function getMatches(array $tokens, array $info = array())
{
$func = $this->getInput($tokens);
$functions = get_defined_functions();
$allFunctions = array_merge($functions['user'], $functions['internal']);
return array_filter($allFunctions, function ($function) use ($func) {
return AbstractMatcher::startsWith($func, $function);
});
}
/**
* {@inheritdoc}
*/
public function hasMatched(array $tokens)
{
$token = array_pop($tokens);
$prevToken = array_pop($tokens);
switch (true) {
case self::tokenIs($prevToken, self::T_NEW):
return false;
case self::hasToken(array(self::T_OPEN_TAG, self::T_STRING), $token):
case self::isOperator($token):
return true;
}
return false;
}
}

View file

@ -0,0 +1,85 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion\Matcher;
/**
* A PHP keyword tab completion Matcher.
*
* This matcher provides completion for all function-like PHP keywords.
*
* @author Marc Garcia <markcial@gmail.com>
*/
class KeywordsMatcher extends AbstractMatcher
{
protected $keywords = array(
'array', 'clone', 'declare', 'die', 'echo', 'empty', 'eval', 'exit', 'include',
'include_once', 'isset', 'list', 'print', 'require', 'require_once', 'unset',
);
protected $mandatoryStartKeywords = array(
'die', 'echo', 'print', 'unset',
);
/**
* Get all (completable) PHP keywords.
*
* @return array
*/
public function getKeywords()
{
return $this->keywords;
}
/**
* Check whether $keyword is a (completable) PHP keyword.
*
* @param string $keyword
*
* @return bool
*/
public function isKeyword($keyword)
{
return in_array($keyword, $this->keywords);
}
/**
* {@inheritdoc}
*/
public function getMatches(array $tokens, array $info = array())
{
$input = $this->getInput($tokens);
return array_filter($this->keywords, function ($keyword) use ($input) {
return AbstractMatcher::startsWith($input, $keyword);
});
}
/**
* {@inheritdoc}
*/
public function hasMatched(array $tokens)
{
$token = array_pop($tokens);
$prevToken = array_pop($tokens);
switch (true) {
case self::hasToken(array(self::T_OPEN_TAG, self::T_VARIABLE), $token):
// case is_string($token) && $token === '$':
case self::hasToken(array(self::T_OPEN_TAG, self::T_VARIABLE), $prevToken) &&
self::tokenIs($token, self::T_STRING):
case self::isOperator($token):
return true;
}
return false;
}
}

View file

@ -0,0 +1,71 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion\Matcher;
/**
* A MongoDB Client tab completion Matcher.
*
* This matcher provides completion for MongoClient database names.
*
* @author Marc Garcia <markcial@gmail.com>
*/
class MongoClientMatcher extends AbstractContextAwareMatcher
{
/**
* {@inheritdoc}
*/
public function getMatches(array $tokens, array $info = array())
{
$input = $this->getInput($tokens);
$firstToken = array_pop($tokens);
if (self::tokenIs($firstToken, self::T_STRING)) {
// second token is the object operator
array_pop($tokens);
}
$objectToken = array_pop($tokens);
$objectName = str_replace('$', '', $objectToken[1]);
$object = $this->getVariable($objectName);
if (!$object instanceof \MongoClient) {
return array();
}
$list = $object->listDBs();
return array_filter(
array_map(function ($info) {
return $info['name'];
}, $list['databases']),
function ($var) use ($input) {
return AbstractMatcher::startsWith($input, $var);
}
);
}
/**
* {@inheritdoc}
*/
public function hasMatched(array $tokens)
{
$token = array_pop($tokens);
$prevToken = array_pop($tokens);
switch (true) {
case self::tokenIs($token, self::T_OBJECT_OPERATOR):
case self::tokenIs($prevToken, self::T_OBJECT_OPERATOR):
return true;
}
return false;
}
}

View file

@ -0,0 +1,67 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion\Matcher;
/**
* A MongoDB tab completion Matcher.
*
* This matcher provides completion for Mongo collection names.
*
* @author Marc Garcia <markcial@gmail.com>
*/
class MongoDatabaseMatcher extends AbstractContextAwareMatcher
{
/**
* {@inheritdoc}
*/
public function getMatches(array $tokens, array $info = array())
{
$input = $this->getInput($tokens);
$firstToken = array_pop($tokens);
if (self::tokenIs($firstToken, self::T_STRING)) {
// second token is the object operator
array_pop($tokens);
}
$objectToken = array_pop($tokens);
$objectName = str_replace('$', '', $objectToken[1]);
$object = $this->getVariable($objectName);
if (!$object instanceof \MongoDB) {
return array();
}
return array_filter(
$object->getCollectionNames(),
function ($var) use ($input) {
return AbstractMatcher::startsWith($input, $var);
}
);
}
/**
* {@inheritdoc}
*/
public function hasMatched(array $tokens)
{
$token = array_pop($tokens);
$prevToken = array_pop($tokens);
switch (true) {
case self::tokenIs($token, self::T_OBJECT_OPERATOR):
case self::tokenIs($prevToken, self::T_OBJECT_OPERATOR):
return true;
}
return false;
}
}

View file

@ -0,0 +1,78 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion\Matcher;
use InvalidArgumentException;
/**
* An object attribute tab completion Matcher.
*
* This matcher provides completion for properties of objects in the current
* Context.
*
* @author Marc Garcia <markcial@gmail.com>
*/
class ObjectAttributesMatcher extends AbstractContextAwareMatcher
{
/**
* {@inheritdoc}
*/
public function getMatches(array $tokens, array $info = array())
{
$input = $this->getInput($tokens);
$firstToken = array_pop($tokens);
if (self::tokenIs($firstToken, self::T_STRING)) {
// second token is the object operator
array_pop($tokens);
}
$objectToken = array_pop($tokens);
if (!is_array($objectToken)) {
return array();
}
$objectName = str_replace('$', '', $objectToken[1]);
try {
$object = $this->getVariable($objectName);
} catch (InvalidArgumentException $e) {
return array();
}
if (!is_object($object)) {
return array();
}
return array_filter(
array_keys(get_class_vars(get_class($object))),
function ($var) use ($input) {
return AbstractMatcher::startsWith($input, $var);
}
);
}
/**
* {@inheritdoc}
*/
public function hasMatched(array $tokens)
{
$token = array_pop($tokens);
$prevToken = array_pop($tokens);
switch (true) {
case self::tokenIs($token, self::T_OBJECT_OPERATOR):
case self::tokenIs($prevToken, self::T_OBJECT_OPERATOR):
return true;
}
return false;
}
}

View file

@ -0,0 +1,71 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion\Matcher;
class ObjectMethodDefaultParametersMatcher extends AbstractDefaultParametersMatcher
{
public function getMatches(array $tokens, array $info = array())
{
$openBracket = array_pop($tokens);
$functionName = array_pop($tokens);
$methodOperator = array_pop($tokens);
$objectToken = array_pop($tokens);
if (!is_array($objectToken)) {
return array();
}
$objectName = str_replace('$', '', $objectToken[1]);
try {
$object = $this->getVariable($objectName);
$reflection = new \ReflectionObject($object);
} catch (InvalidArgumentException $e) {
return array();
} catch (\ReflectionException $e) {
return array();
}
$methods = $reflection->getMethods();
foreach ($methods as $method) {
if ($method->getName() === $functionName[1]) {
return $this->getDefaultParameterCompletion($method->getParameters());
}
}
return array();
}
public function hasMatched(array $tokens)
{
$openBracket = array_pop($tokens);
if ($openBracket !== '(') {
return false;
}
$functionName = array_pop($tokens);
if (!self::tokenIs($functionName, self::T_STRING)) {
return false;
}
$operator = array_pop($tokens);
if (!self::tokenIs($operator, self::T_OBJECT_OPERATOR)) {
return false;
}
return true;
}
}

View file

@ -0,0 +1,80 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion\Matcher;
use InvalidArgumentException;
/**
* An object method tab completion Matcher.
*
* This matcher provides completion for methods of objects in the current
* Context.
*
* @author Marc Garcia <markcial@gmail.com>
*/
class ObjectMethodsMatcher extends AbstractContextAwareMatcher
{
/**
* {@inheritdoc}
*/
public function getMatches(array $tokens, array $info = array())
{
$input = $this->getInput($tokens);
$firstToken = array_pop($tokens);
if (self::tokenIs($firstToken, self::T_STRING)) {
// second token is the object operator
array_pop($tokens);
}
$objectToken = array_pop($tokens);
if (!is_array($objectToken)) {
return array();
}
$objectName = str_replace('$', '', $objectToken[1]);
try {
$object = $this->getVariable($objectName);
} catch (InvalidArgumentException $e) {
return array();
}
if (!is_object($object)) {
return array();
}
return array_filter(
get_class_methods($object),
function ($var) use ($input) {
return AbstractMatcher::startsWith($input, $var) &&
// also check that we do not suggest invoking a super method(__construct, __wakeup, …)
!AbstractMatcher::startsWith('__', $var);
}
);
}
/**
* {@inheritdoc}
*/
public function hasMatched(array $tokens)
{
$token = array_pop($tokens);
$prevToken = array_pop($tokens);
switch (true) {
case self::tokenIs($token, self::T_OBJECT_OPERATOR):
case self::tokenIs($prevToken, self::T_OBJECT_OPERATOR):
return true;
}
return false;
}
}

View file

@ -0,0 +1,51 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2017 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\TabCompletion\Matcher;
/**
* A variable name tab completion Matcher.
*
* This matcher provides completion for variable names in the current Context.
*
* @author Marc Garcia <markcial@gmail.com>
*/
class VariablesMatcher extends AbstractContextAwareMatcher
{
/**
* {@inheritdoc}
*/
public function getMatches(array $tokens, array $info = array())
{
$var = str_replace('$', '', $this->getInput($tokens));
return array_filter(array_keys($this->getVariables()), function ($variable) use ($var) {
return AbstractMatcher::startsWith($var, $variable);
});
}
/**
* {@inheritdoc}
*/
public function hasMatched(array $tokens)
{
$token = array_pop($tokens);
switch (true) {
case self::hasToken(array(self::T_OPEN_TAG, self::T_VARIABLE), $token):
case is_string($token) && $token === '$':
case self::isOperator($token):
return true;
}
return false;
}
}