Filter refactoring
This commit is contained in:
@@ -18,7 +18,7 @@ class ActionManager extends Base
|
||||
* List of automatic actions
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
* @var ActionBase[]
|
||||
*/
|
||||
private $actions = array();
|
||||
|
||||
|
||||
@@ -48,16 +48,8 @@ use Pimple\Container;
|
||||
* @property \Kanboard\Core\User\UserSession $userSession
|
||||
* @property \Kanboard\Core\DateParser $dateParser
|
||||
* @property \Kanboard\Core\Helper $helper
|
||||
* @property \Kanboard\Core\Lexer $lexer
|
||||
* @property \Kanboard\Core\Paginator $paginator
|
||||
* @property \Kanboard\Core\Template $template
|
||||
* @property \Kanboard\Formatter\ProjectGanttFormatter $projectGanttFormatter
|
||||
* @property \Kanboard\Formatter\TaskFilterGanttFormatter $taskFilterGanttFormatter
|
||||
* @property \Kanboard\Formatter\TaskFilterAutoCompleteFormatter $taskFilterAutoCompleteFormatter
|
||||
* @property \Kanboard\Formatter\TaskFilterCalendarFormatter $taskFilterCalendarFormatter
|
||||
* @property \Kanboard\Formatter\TaskFilterICalendarFormatter $taskFilterICalendarFormatter
|
||||
* @property \Kanboard\Formatter\UserFilterAutoCompleteFormatter $userFilterAutoCompleteFormatter
|
||||
* @property \Kanboard\Formatter\GroupAutoCompleteFormatter $groupAutoCompleteFormatter
|
||||
* @property \Kanboard\Model\Action $action
|
||||
* @property \Kanboard\Model\ActionParameter $actionParameter
|
||||
* @property \Kanboard\Model\AvatarFile $avatarFile
|
||||
@@ -85,7 +77,6 @@ use Pimple\Container;
|
||||
* @property \Kanboard\Model\ProjectMetadata $projectMetadata
|
||||
* @property \Kanboard\Model\ProjectPermission $projectPermission
|
||||
* @property \Kanboard\Model\ProjectUserRole $projectUserRole
|
||||
* @property \Kanboard\Model\projectUserRoleFilter $projectUserRoleFilter
|
||||
* @property \Kanboard\Model\ProjectGroupRole $projectGroupRole
|
||||
* @property \Kanboard\Model\ProjectNotification $projectNotification
|
||||
* @property \Kanboard\Model\ProjectNotificationType $projectNotificationType
|
||||
@@ -99,7 +90,6 @@ use Pimple\Container;
|
||||
* @property \Kanboard\Model\TaskDuplication $taskDuplication
|
||||
* @property \Kanboard\Model\TaskExternalLink $taskExternalLink
|
||||
* @property \Kanboard\Model\TaskFinder $taskFinder
|
||||
* @property \Kanboard\Model\TaskFilter $taskFilter
|
||||
* @property \Kanboard\Model\TaskLink $taskLink
|
||||
* @property \Kanboard\Model\TaskModification $taskModification
|
||||
* @property \Kanboard\Model\TaskPermission $taskPermission
|
||||
@@ -137,6 +127,12 @@ use Pimple\Container;
|
||||
* @property \Kanboard\Export\SubtaskExport $subtaskExport
|
||||
* @property \Kanboard\Export\TaskExport $taskExport
|
||||
* @property \Kanboard\Export\TransitionExport $transitionExport
|
||||
* @property \Kanboard\Core\Filter\QueryBuilder $projectGroupRoleQuery
|
||||
* @property \Kanboard\Core\Filter\QueryBuilder $projectUserRoleQuery
|
||||
* @property \Kanboard\Core\Filter\QueryBuilder $userQuery
|
||||
* @property \Kanboard\Core\Filter\QueryBuilder $projectQuery
|
||||
* @property \Kanboard\Core\Filter\QueryBuilder $taskQuery
|
||||
* @property \Kanboard\Core\Filter\LexerBuilder $taskLexer
|
||||
* @property \Psr\Log\LoggerInterface $logger
|
||||
* @property \PicoDb\Database $db
|
||||
* @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher
|
||||
@@ -173,4 +169,18 @@ abstract class Base
|
||||
{
|
||||
return $this->container[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get object instance
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param Container $container
|
||||
* @return static
|
||||
*/
|
||||
public static function getInstance(Container $container)
|
||||
{
|
||||
$self = new static($container);
|
||||
return $self;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class ExternalLinkManager extends Base
|
||||
* Registered providers
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
* @var ExternalLinkProviderInterface[]
|
||||
*/
|
||||
private $providers = array();
|
||||
|
||||
|
||||
40
app/Core/Filter/CriteriaInterface.php
Normal file
40
app/Core/Filter/CriteriaInterface.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core\Filter;
|
||||
|
||||
use PicoDb\Table;
|
||||
|
||||
/**
|
||||
* Criteria Interface
|
||||
*
|
||||
* @package filter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
interface CriteriaInterface
|
||||
{
|
||||
/**
|
||||
* Set the Query
|
||||
*
|
||||
* @access public
|
||||
* @param Table $query
|
||||
* @return CriteriaInterface
|
||||
*/
|
||||
public function withQuery(Table $query);
|
||||
|
||||
/**
|
||||
* Set filter
|
||||
*
|
||||
* @access public
|
||||
* @param FilterInterface $filter
|
||||
* @return CriteriaInterface
|
||||
*/
|
||||
public function withFilter(FilterInterface $filter);
|
||||
|
||||
/**
|
||||
* Apply condition
|
||||
*
|
||||
* @access public
|
||||
* @return CriteriaInterface
|
||||
*/
|
||||
public function apply();
|
||||
}
|
||||
56
app/Core/Filter/FilterInterface.php
Normal file
56
app/Core/Filter/FilterInterface.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core\Filter;
|
||||
|
||||
use PicoDb\Table;
|
||||
|
||||
/**
|
||||
* Filter Interface
|
||||
*
|
||||
* @package filter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
interface FilterInterface
|
||||
{
|
||||
/**
|
||||
* BaseFilter constructor
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __construct($value = null);
|
||||
|
||||
/**
|
||||
* Set the value
|
||||
*
|
||||
* @access public
|
||||
* @param string $value
|
||||
* @return FilterInterface
|
||||
*/
|
||||
public function withValue($value);
|
||||
|
||||
/**
|
||||
* Set query
|
||||
*
|
||||
* @access public
|
||||
* @param Table $query
|
||||
* @return FilterInterface
|
||||
*/
|
||||
public function withQuery(Table $query);
|
||||
|
||||
/**
|
||||
* Get search attribute
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAttributes();
|
||||
|
||||
/**
|
||||
* Apply filter
|
||||
*
|
||||
* @access public
|
||||
* @return FilterInterface
|
||||
*/
|
||||
public function apply();
|
||||
}
|
||||
31
app/Core/Filter/FormatterInterface.php
Normal file
31
app/Core/Filter/FormatterInterface.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core\Filter;
|
||||
|
||||
use PicoDb\Table;
|
||||
|
||||
/**
|
||||
* Formatter interface
|
||||
*
|
||||
* @package filter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
interface FormatterInterface
|
||||
{
|
||||
/**
|
||||
* Set query
|
||||
*
|
||||
* @access public
|
||||
* @param Table $query
|
||||
* @return FormatterInterface
|
||||
*/
|
||||
public function withQuery(Table $query);
|
||||
|
||||
/**
|
||||
* Apply formatter
|
||||
*
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function format();
|
||||
}
|
||||
153
app/Core/Filter/Lexer.php
Normal file
153
app/Core/Filter/Lexer.php
Normal file
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core\Filter;
|
||||
|
||||
/**
|
||||
* Lexer
|
||||
*
|
||||
* @package filter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Lexer
|
||||
{
|
||||
/**
|
||||
* Current position
|
||||
*
|
||||
* @access private
|
||||
* @var integer
|
||||
*/
|
||||
private $offset = 0;
|
||||
|
||||
/**
|
||||
* Token map
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $tokenMap = array(
|
||||
"/^(\s+)/" => 'T_WHITESPACE',
|
||||
'/^([<=>]{0,2}[0-9]{4}-[0-9]{2}-[0-9]{2})/' => 'T_DATE',
|
||||
'/^(yesterday|tomorrow|today)/' => 'T_DATE',
|
||||
'/^("(.*?)")/' => 'T_STRING',
|
||||
"/^(\w+)/" => 'T_STRING',
|
||||
"/^(#\d+)/" => 'T_STRING',
|
||||
);
|
||||
|
||||
/**
|
||||
* Default token
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $defaultToken = '';
|
||||
|
||||
/**
|
||||
* Add token
|
||||
*
|
||||
* @access public
|
||||
* @param string $regex
|
||||
* @param string $token
|
||||
* @return $this
|
||||
*/
|
||||
public function addToken($regex, $token)
|
||||
{
|
||||
$this->tokenMap = array($regex => $token) + $this->tokenMap;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default token
|
||||
*
|
||||
* @access public
|
||||
* @param string $token
|
||||
* @return $this
|
||||
*/
|
||||
public function setDefaultToken($token)
|
||||
{
|
||||
$this->defaultToken = $token;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenize input string
|
||||
*
|
||||
* @access public
|
||||
* @param string $input
|
||||
* @return array
|
||||
*/
|
||||
public function tokenize($input)
|
||||
{
|
||||
$tokens = array();
|
||||
$this->offset = 0;
|
||||
|
||||
while (isset($input[$this->offset])) {
|
||||
$result = $this->match(substr($input, $this->offset));
|
||||
|
||||
if ($result === false) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$tokens[] = $result;
|
||||
}
|
||||
|
||||
return $this->map($tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a token that match and move the offset
|
||||
*
|
||||
* @access protected
|
||||
* @param string $string
|
||||
* @return array|boolean
|
||||
*/
|
||||
protected function match($string)
|
||||
{
|
||||
foreach ($this->tokenMap as $pattern => $name) {
|
||||
if (preg_match($pattern, $string, $matches)) {
|
||||
$this->offset += strlen($matches[1]);
|
||||
|
||||
return array(
|
||||
'match' => trim($matches[1], '"'),
|
||||
'token' => $name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build map of tokens and matches
|
||||
*
|
||||
* @access protected
|
||||
* @param array $tokens
|
||||
* @return array
|
||||
*/
|
||||
protected function map(array $tokens)
|
||||
{
|
||||
$map = array();
|
||||
$leftOver = '';
|
||||
|
||||
while (false !== ($token = current($tokens))) {
|
||||
if ($token['token'] === 'T_STRING' || $token['token'] === 'T_WHITESPACE') {
|
||||
$leftOver .= $token['match'];
|
||||
} else {
|
||||
$next = next($tokens);
|
||||
|
||||
if ($next !== false && in_array($next['token'], array('T_STRING', 'T_DATE'))) {
|
||||
$map[$token['token']][] = $next['match'];
|
||||
}
|
||||
}
|
||||
|
||||
next($tokens);
|
||||
}
|
||||
|
||||
$leftOver = trim($leftOver);
|
||||
|
||||
if ($this->defaultToken !== '' && $leftOver !== '') {
|
||||
$map[$this->defaultToken] = array($leftOver);
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
}
|
||||
151
app/Core/Filter/LexerBuilder.php
Normal file
151
app/Core/Filter/LexerBuilder.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core\Filter;
|
||||
|
||||
use PicoDb\Table;
|
||||
|
||||
/**
|
||||
* Lexer Builder
|
||||
*
|
||||
* @package filter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class LexerBuilder
|
||||
{
|
||||
/**
|
||||
* Lexer object
|
||||
*
|
||||
* @access protected
|
||||
* @var Lexer
|
||||
*/
|
||||
protected $lexer;
|
||||
|
||||
/**
|
||||
* Query object
|
||||
*
|
||||
* @access protected
|
||||
* @var Table
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* List of filters
|
||||
*
|
||||
* @access protected
|
||||
* @var FilterInterface[]
|
||||
*/
|
||||
protected $filters;
|
||||
|
||||
/**
|
||||
* QueryBuilder object
|
||||
*
|
||||
* @access protected
|
||||
* @var QueryBuilder
|
||||
*/
|
||||
protected $queryBuilder;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->lexer = new Lexer;
|
||||
$this->queryBuilder = new QueryBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a filter
|
||||
*
|
||||
* @access public
|
||||
* @param FilterInterface $filter
|
||||
* @param bool $default
|
||||
* @return LexerBuilder
|
||||
*/
|
||||
public function withFilter(FilterInterface $filter, $default = false)
|
||||
{
|
||||
$attributes = $filter->getAttributes();
|
||||
|
||||
foreach ($attributes as $attribute) {
|
||||
$this->filters[$attribute] = $filter;
|
||||
$this->lexer->addToken(sprintf("/^(%s:)/", $attribute), $attribute);
|
||||
|
||||
if ($default) {
|
||||
$this->lexer->setDefaultToken($attribute);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the query
|
||||
*
|
||||
* @access public
|
||||
* @param Table $query
|
||||
* @return LexerBuilder
|
||||
*/
|
||||
public function withQuery(Table $query)
|
||||
{
|
||||
$this->query = $query;
|
||||
$this->queryBuilder->withQuery($this->query);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the input and build the query
|
||||
*
|
||||
* @access public
|
||||
* @param string $input
|
||||
* @return QueryBuilder
|
||||
*/
|
||||
public function build($input)
|
||||
{
|
||||
$tokens = $this->lexer->tokenize($input);
|
||||
|
||||
foreach ($tokens as $token => $values) {
|
||||
if (isset($this->filters[$token])) {
|
||||
$this->applyFilters($this->filters[$token], $values);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->queryBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply filters to the query
|
||||
*
|
||||
* @access protected
|
||||
* @param FilterInterface $filter
|
||||
* @param array $values
|
||||
*/
|
||||
protected function applyFilters(FilterInterface $filter, array $values)
|
||||
{
|
||||
$len = count($values);
|
||||
|
||||
if ($len > 1) {
|
||||
$criteria = new OrCriteria();
|
||||
$criteria->withQuery($this->query);
|
||||
|
||||
foreach ($values as $value) {
|
||||
$currentFilter = clone($filter);
|
||||
$criteria->withFilter($currentFilter->withValue($value));
|
||||
}
|
||||
|
||||
$this->queryBuilder->withCriteria($criteria);
|
||||
} elseif ($len === 1) {
|
||||
$this->queryBuilder->withFilter($filter->withValue($values[0]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone object with deep copy
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
$this->lexer = clone $this->lexer;
|
||||
$this->query = clone $this->query;
|
||||
$this->queryBuilder = clone $this->queryBuilder;
|
||||
}
|
||||
}
|
||||
68
app/Core/Filter/OrCriteria.php
Normal file
68
app/Core/Filter/OrCriteria.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core\Filter;
|
||||
|
||||
use PicoDb\Table;
|
||||
|
||||
/**
|
||||
* OR criteria
|
||||
*
|
||||
* @package filter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class OrCriteria implements CriteriaInterface
|
||||
{
|
||||
/**
|
||||
* @var Table
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* @var FilterInterface[]
|
||||
*/
|
||||
protected $filters = array();
|
||||
|
||||
/**
|
||||
* Set the Query
|
||||
*
|
||||
* @access public
|
||||
* @param Table $query
|
||||
* @return CriteriaInterface
|
||||
*/
|
||||
public function withQuery(Table $query)
|
||||
{
|
||||
$this->query = $query;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set filter
|
||||
*
|
||||
* @access public
|
||||
* @param FilterInterface $filter
|
||||
* @return CriteriaInterface
|
||||
*/
|
||||
public function withFilter(FilterInterface $filter)
|
||||
{
|
||||
$this->filters[] = $filter;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply condition
|
||||
*
|
||||
* @access public
|
||||
* @return CriteriaInterface
|
||||
*/
|
||||
public function apply()
|
||||
{
|
||||
$this->query->beginOr();
|
||||
|
||||
foreach ($this->filters as $filter) {
|
||||
$filter->withQuery($this->query)->apply();
|
||||
}
|
||||
|
||||
$this->query->closeOr();
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
103
app/Core/Filter/QueryBuilder.php
Normal file
103
app/Core/Filter/QueryBuilder.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core\Filter;
|
||||
|
||||
use PicoDb\Table;
|
||||
|
||||
/**
|
||||
* Class QueryBuilder
|
||||
*
|
||||
* @package filter
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class QueryBuilder
|
||||
{
|
||||
/**
|
||||
* Query object
|
||||
*
|
||||
* @access protected
|
||||
* @var Table
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* Set the query
|
||||
*
|
||||
* @access public
|
||||
* @param Table $query
|
||||
* @return QueryBuilder
|
||||
*/
|
||||
public function withQuery(Table $query)
|
||||
{
|
||||
$this->query = $query;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a filter
|
||||
*
|
||||
* @access public
|
||||
* @param FilterInterface $filter
|
||||
* @return QueryBuilder
|
||||
*/
|
||||
public function withFilter(FilterInterface $filter)
|
||||
{
|
||||
$filter->withQuery($this->query)->apply();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a criteria
|
||||
*
|
||||
* @access public
|
||||
* @param CriteriaInterface $criteria
|
||||
* @return QueryBuilder
|
||||
*/
|
||||
public function withCriteria(CriteriaInterface $criteria)
|
||||
{
|
||||
$criteria->withQuery($this->query)->apply();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a formatter
|
||||
*
|
||||
* @access public
|
||||
* @param FormatterInterface $formatter
|
||||
* @return string|array
|
||||
*/
|
||||
public function format(FormatterInterface $formatter)
|
||||
{
|
||||
return $formatter->withQuery($this->query)->format();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query result as array
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->query->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Query object
|
||||
*
|
||||
* @access public
|
||||
* @return Table
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone object with deep copy
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
$this->query = clone $this->query;
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,12 @@ use Pimple\Container;
|
||||
*
|
||||
* @property \Kanboard\Helper\AppHelper $app
|
||||
* @property \Kanboard\Helper\AssetHelper $asset
|
||||
* @property \Kanboard\Helper\CalendarHelper $calendar
|
||||
* @property \Kanboard\Helper\DateHelper $dt
|
||||
* @property \Kanboard\Helper\FileHelper $file
|
||||
* @property \Kanboard\Helper\FormHelper $form
|
||||
* @property \Kanboard\Helper\HookHelper $hook
|
||||
* @property \Kanboard\Helper\ICalHelper $ical
|
||||
* @property \Kanboard\Helper\ModelHelper $model
|
||||
* @property \Kanboard\Helper\SubtaskHelper $subtask
|
||||
* @property \Kanboard\Helper\TaskHelper $task
|
||||
|
||||
@@ -231,6 +231,20 @@ class Response extends Base
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a iCal response
|
||||
*
|
||||
* @access public
|
||||
* @param string $data Raw data
|
||||
* @param integer $status_code HTTP status code
|
||||
*/
|
||||
public function ical($data, $status_code = 200)
|
||||
{
|
||||
$this->status($status_code);
|
||||
$this->contentType('text/calendar; charset=utf-8');
|
||||
echo $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the security header: Content-Security-Policy
|
||||
*
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core;
|
||||
|
||||
/**
|
||||
* Lexer
|
||||
*
|
||||
* @package core
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Lexer
|
||||
{
|
||||
/**
|
||||
* Current position
|
||||
*
|
||||
* @access private
|
||||
* @var integer
|
||||
*/
|
||||
private $offset = 0;
|
||||
|
||||
/**
|
||||
* Token map
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $tokenMap = array(
|
||||
"/^(assignee:)/" => 'T_ASSIGNEE',
|
||||
"/^(color:)/" => 'T_COLOR',
|
||||
"/^(due:)/" => 'T_DUE',
|
||||
"/^(updated:)/" => 'T_UPDATED',
|
||||
"/^(modified:)/" => 'T_UPDATED',
|
||||
"/^(created:)/" => 'T_CREATED',
|
||||
"/^(status:)/" => 'T_STATUS',
|
||||
"/^(description:)/" => 'T_DESCRIPTION',
|
||||
"/^(category:)/" => 'T_CATEGORY',
|
||||
"/^(column:)/" => 'T_COLUMN',
|
||||
"/^(project:)/" => 'T_PROJECT',
|
||||
"/^(swimlane:)/" => 'T_SWIMLANE',
|
||||
"/^(ref:)/" => 'T_REFERENCE',
|
||||
"/^(reference:)/" => 'T_REFERENCE',
|
||||
"/^(link:)/" => 'T_LINK',
|
||||
"/^(\s+)/" => 'T_WHITESPACE',
|
||||
'/^([<=>]{0,2}[0-9]{4}-[0-9]{2}-[0-9]{2})/' => 'T_DATE',
|
||||
'/^(yesterday|tomorrow|today)/' => 'T_DATE',
|
||||
'/^("(.*?)")/' => 'T_STRING',
|
||||
"/^(\w+)/" => 'T_STRING',
|
||||
"/^(#\d+)/" => 'T_STRING',
|
||||
);
|
||||
|
||||
/**
|
||||
* Tokenize input string
|
||||
*
|
||||
* @access public
|
||||
* @param string $input
|
||||
* @return array
|
||||
*/
|
||||
public function tokenize($input)
|
||||
{
|
||||
$tokens = array();
|
||||
$this->offset = 0;
|
||||
|
||||
while (isset($input[$this->offset])) {
|
||||
$result = $this->match(substr($input, $this->offset));
|
||||
|
||||
if ($result === false) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$tokens[] = $result;
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a token that match and move the offset
|
||||
*
|
||||
* @access public
|
||||
* @param string $string
|
||||
* @return array|boolean
|
||||
*/
|
||||
public function match($string)
|
||||
{
|
||||
foreach ($this->tokenMap as $pattern => $name) {
|
||||
if (preg_match($pattern, $string, $matches)) {
|
||||
$this->offset += strlen($matches[1]);
|
||||
|
||||
return array(
|
||||
'match' => trim($matches[1], '"'),
|
||||
'token' => $name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the output of tokenizer to be easily parsed by the database filter
|
||||
*
|
||||
* Example: ['T_ASSIGNEE' => ['user1', 'user2'], 'T_TITLE' => 'task title']
|
||||
*
|
||||
* @access public
|
||||
* @param array $tokens
|
||||
* @return array
|
||||
*/
|
||||
public function map(array $tokens)
|
||||
{
|
||||
$map = array(
|
||||
'T_TITLE' => '',
|
||||
);
|
||||
|
||||
while (false !== ($token = current($tokens))) {
|
||||
switch ($token['token']) {
|
||||
case 'T_ASSIGNEE':
|
||||
case 'T_COLOR':
|
||||
case 'T_CATEGORY':
|
||||
case 'T_COLUMN':
|
||||
case 'T_PROJECT':
|
||||
case 'T_SWIMLANE':
|
||||
case 'T_LINK':
|
||||
$next = next($tokens);
|
||||
|
||||
if ($next !== false && $next['token'] === 'T_STRING') {
|
||||
$map[$token['token']][] = $next['match'];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'T_STATUS':
|
||||
case 'T_DUE':
|
||||
case 'T_UPDATED':
|
||||
case 'T_CREATED':
|
||||
case 'T_DESCRIPTION':
|
||||
case 'T_REFERENCE':
|
||||
$next = next($tokens);
|
||||
|
||||
if ($next !== false && ($next['token'] === 'T_DATE' || $next['token'] === 'T_STRING')) {
|
||||
$map[$token['token']] = $next['match'];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
$map['T_TITLE'] .= $token['match'];
|
||||
break;
|
||||
}
|
||||
|
||||
next($tokens);
|
||||
}
|
||||
|
||||
$map['T_TITLE'] = trim($map['T_TITLE']);
|
||||
|
||||
if (empty($map['T_TITLE'])) {
|
||||
unset($map['T_TITLE']);
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user