field * * @return array */ public function getQuery() { if (!$this->field) { throw new \RuntimeException('Failed to build a query, no field specified'); } return ($this->field === 'pid') ? $this->getPages() : $this->getTags(); } /** * Processes all parts of the query for fetching tagged pages * * Returns SQL and query parameter values * * @return array */ public function getPages() { $this->groupby = 'pid'; $this->orderby = "cnt DESC, pid"; if ($this->tags && $this->logicalAnd) $this->having = ' HAVING cnt = ' . count($this->tags); return [$this->getSql(), $this->values]; } /** * Processes all parts of the query for fetching tags * * Returns SQL and query parameter values * * @return array */ public function getTags() { $this->groupby = 'CLEANTAG(tag)'; $this->orderby = 'CLEANTAG(tag)'; return [$this->getSql(), $this->values]; } /** * Tags to search for * @param array $tags */ public function setTags($tags) { $this->tags = $tags; } /** * Namespaces to limit search to * @param array $ns */ public function includeNS($ns) { $this->ns = $this->globNS($ns); } /** * Namespaces to exclude from search * @param array $ns */ public function excludeNS($ns) { $this->notns = $this->globNS($ns); } /** * Sets the logical operator used in tag search to AND * @param bool $and */ public function setLogicalAnd($and) { $this->logicalAnd = (bool)$and; } /** * Result limit * @param int $limit */ public function setLimit($limit) { $this->limit = $limit; } /** * Database field to select * @param string $field */ public function setField($field) { $this->field = $field; } /** * Limit search to this page id * @param string $pid */ public function setPid($pid) { $this->pid = $pid; } /** * Limit search to certain pages * * @param array $pids */ public function setPids($pids) { $this->pids = $pids; } /** * Limit results to this tagger * @param string $tagger */ public function setTagger($tagger) { $this->tagger = $tagger; } /** * Returns full query SQL * @return string */ protected function getSql() { $sql = "SELECT $this->field AS item, COUNT(*) AS cnt FROM taggings WHERE " . $this->getWhere() . " GROUP BY $this->groupby $this->having ORDER BY $this->orderby "; if ($this->limit) { $sql .= ' LIMIT ?'; $this->values[] = $this->limit; } return $sql; } /** * Builds the WHERE part of query string * @return string */ protected function getWhere() { $where = '1=1'; if ($this->pid) { $where .= ' AND pid'; $where .= $this->useLike($this->pid) ? ' GLOB' : ' ='; $where .= ' ?'; $this->values[] = $this->pid; } if ($this->pids) { $where .= ' AND pid'; $where .= ' IN('; foreach ($this->pids as $pid) { $where .= ' ?,'; $this->values[] = $pid; } $where = rtrim($where, ',') . ')'; } if ($this->tagger) { $where .= ' AND tagger = ?'; $this->values[] = $this->tagger; } if ($this->ns) { $where .= ' AND '; $nsCnt = count($this->ns); $i = 0; foreach ($this->ns as $ns) { $where .= ' pid'; $where .= ' GLOB'; $where .= ' ?'; if (++$i < $nsCnt) $where .= ' OR'; $this->values[] = $ns; } } if ($this->notns) { $where .= ' AND '; $nsCnt = count($this->notns); $i = 0; foreach ($this->notns as $notns) { $where .= ' pid'; $where .= ' NOT GLOB'; $where .= ' ?'; if (++$i < $nsCnt) $where .= ' AND'; $this->values[] = $notns; } } if ($this->tags) { $where .= ' AND '; $tagCnt = count($this->tags); $i = 0; foreach ($this->tags as $tag) { $where .= ' CLEANTAG(tag)'; $where .= $this->useLike($tag) ? ' GLOB' : ' ='; $where .= ' CLEANTAG(?)'; if (++$i < $tagCnt) $where .= ' OR'; $this->values[] = $tag; } } // bypass page access check when called by a command line tool if (php_sapi_name() !== 'cli' || defined('DOKU_UNITTEST')) { $where .= ' AND GETACCESSLEVEL(pid) >= ' . AUTH_READ; } return $where; } /** * Check if the given string is a LIKE statement * * @param string $value * @return bool */ protected function useLike($value) { return strpos($value, '*') === 0 || strrpos($value, '*') === strlen($value) - 1; } /** * Converts namespaces into a wildcard form suitable for SQL queries * * @param array $item * @return array */ protected function globNS(array $item) { return array_map(function($ns) { return cleanId($ns) . '*'; }, $item); } }