summaryrefslogtreecommitdiff
path: root/app/Core
diff options
context:
space:
mode:
Diffstat (limited to 'app/Core')
-rw-r--r--app/Core/Action/ActionManager.php2
-rw-r--r--app/Core/Base.php30
-rw-r--r--app/Core/ExternalLink/ExternalLinkManager.php2
-rw-r--r--app/Core/Filter/CriteriaInterface.php40
-rw-r--r--app/Core/Filter/FilterInterface.php56
-rw-r--r--app/Core/Filter/FormatterInterface.php31
-rw-r--r--app/Core/Filter/Lexer.php153
-rw-r--r--app/Core/Filter/LexerBuilder.php151
-rw-r--r--app/Core/Filter/OrCriteria.php68
-rw-r--r--app/Core/Filter/QueryBuilder.php103
-rw-r--r--app/Core/Helper.php2
-rw-r--r--app/Core/Http/Response.php14
-rw-r--r--app/Core/Lexer.php161
13 files changed, 640 insertions, 173 deletions
diff --git a/app/Core/Action/ActionManager.php b/app/Core/Action/ActionManager.php
index f1ea8abe..dfa5a140 100644
--- a/app/Core/Action/ActionManager.php
+++ b/app/Core/Action/ActionManager.php
@@ -18,7 +18,7 @@ class ActionManager extends Base
* List of automatic actions
*
* @access private
- * @var array
+ * @var ActionBase[]
*/
private $actions = array();
diff --git a/app/Core/Base.php b/app/Core/Base.php
index 74573e94..8c6b7620 100644
--- a/app/Core/Base.php
+++ b/app/Core/Base.php
@@ -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;
+ }
}
diff --git a/app/Core/ExternalLink/ExternalLinkManager.php b/app/Core/ExternalLink/ExternalLinkManager.php
index 1fa423c2..804e6b34 100644
--- a/app/Core/ExternalLink/ExternalLinkManager.php
+++ b/app/Core/ExternalLink/ExternalLinkManager.php
@@ -23,7 +23,7 @@ class ExternalLinkManager extends Base
* Registered providers
*
* @access private
- * @var array
+ * @var ExternalLinkProviderInterface[]
*/
private $providers = array();
diff --git a/app/Core/Filter/CriteriaInterface.php b/app/Core/Filter/CriteriaInterface.php
new file mode 100644
index 00000000..009c4bd3
--- /dev/null
+++ b/app/Core/Filter/CriteriaInterface.php
@@ -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();
+}
diff --git a/app/Core/Filter/FilterInterface.php b/app/Core/Filter/FilterInterface.php
new file mode 100644
index 00000000..7b66ec28
--- /dev/null
+++ b/app/Core/Filter/FilterInterface.php
@@ -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();
+}
diff --git a/app/Core/Filter/FormatterInterface.php b/app/Core/Filter/FormatterInterface.php
new file mode 100644
index 00000000..b7c04c51
--- /dev/null
+++ b/app/Core/Filter/FormatterInterface.php
@@ -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();
+}
diff --git a/app/Core/Filter/Lexer.php b/app/Core/Filter/Lexer.php
new file mode 100644
index 00000000..041b58d3
--- /dev/null
+++ b/app/Core/Filter/Lexer.php
@@ -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;
+ }
+}
diff --git a/app/Core/Filter/LexerBuilder.php b/app/Core/Filter/LexerBuilder.php
new file mode 100644
index 00000000..7a9a714f
--- /dev/null
+++ b/app/Core/Filter/LexerBuilder.php
@@ -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;
+ }
+}
diff --git a/app/Core/Filter/OrCriteria.php b/app/Core/Filter/OrCriteria.php
new file mode 100644
index 00000000..174b8458
--- /dev/null
+++ b/app/Core/Filter/OrCriteria.php
@@ -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;
+ }
+}
diff --git a/app/Core/Filter/QueryBuilder.php b/app/Core/Filter/QueryBuilder.php
new file mode 100644
index 00000000..3de82b63
--- /dev/null
+++ b/app/Core/Filter/QueryBuilder.php
@@ -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;
+ }
+}
diff --git a/app/Core/Helper.php b/app/Core/Helper.php
index 3a66fbd0..ab1f8f76 100644
--- a/app/Core/Helper.php
+++ b/app/Core/Helper.php
@@ -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
diff --git a/app/Core/Http/Response.php b/app/Core/Http/Response.php
index 37349ca5..996fc58d 100644
--- a/app/Core/Http/Response.php
+++ b/app/Core/Http/Response.php
@@ -232,6 +232,20 @@ class Response extends Base
}
/**
+ * 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
*
* @access public
diff --git a/app/Core/Lexer.php b/app/Core/Lexer.php
deleted file mode 100644
index df2d90ae..00000000
--- a/app/Core/Lexer.php
+++ /dev/null
@@ -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;
- }
-}