Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/fulltextsearch.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile6
-rw-r--r--composer.json25
-rw-r--r--composer.lock59
-rw-r--r--lib/AppInfo/Application.php12
-rw-r--r--lib/Command/Index.php2
-rw-r--r--lib/Command/Live.php2
-rw-r--r--lib/Controller/ApiController.php26
-rw-r--r--lib/Db/CoreQueryBuilder.php48
-rw-r--r--lib/Db/CoreRequestBuilder.php5
-rw-r--r--lib/Db/IndexesRequestBuilder.php5
-rw-r--r--lib/Db/TickRequestBuilder.php2
-rw-r--r--lib/Model/Index.php2
-rw-r--r--lib/Model/IndexOptions.php2
-rw-r--r--lib/Model/Runner.php2
-rw-r--r--lib/Model/SearchRequest.php2
-rw-r--r--lib/Model/Tick.php2
-rw-r--r--lib/Search/UnifiedSearchProvider.php2
-rw-r--r--lib/Service/SearchService.php5
-rw-r--r--lib/Tools/Db/ExtendedQueryBuilder.php1135
-rw-r--r--lib/Tools/Db/IQueryRow.php49
-rw-r--r--lib/Tools/Exceptions/ArrayNotFoundException.php42
-rw-r--r--lib/Tools/Exceptions/DateTimeException.php42
-rw-r--r--lib/Tools/Exceptions/InvalidItemException.php42
-rw-r--r--lib/Tools/Exceptions/ItemNotFoundException.php42
-rw-r--r--lib/Tools/Exceptions/MalformedArrayException.php42
-rw-r--r--lib/Tools/Exceptions/RowNotFoundException.php42
-rw-r--r--lib/Tools/Exceptions/UnknownTypeException.php42
-rw-r--r--lib/Tools/IDeserializable.php42
-rw-r--r--lib/Tools/Traits/TArrayTools.php432
-rw-r--r--lib/Tools/Traits/TDeserialize.php114
-rw-r--r--lib/Tools/Traits/TNCLogger.php201
-rw-r--r--lib/Tools/Traits/TNCSetup.php107
-rw-r--r--lib/Tools/Traits/TStringTools.php258
33 files changed, 2718 insertions, 123 deletions
diff --git a/Makefile b/Makefile
index 5d49ebc..364db74 100644
--- a/Makefile
+++ b/Makefile
@@ -69,12 +69,8 @@ clean:
rm -rf $(build_dir)
rm -rf node_modules
-# composer packages
-composer:
- composer install --prefer-dist
- composer upgrade --prefer-dist
-appstore: clean composer
+appstore: clean
mkdir -p $(sign_dir)
rsync -a \
--exclude=/build \
diff --git a/composer.json b/composer.json
deleted file mode 100644
index 8253a43..0000000
--- a/composer.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "name": "nextcloud/fulltextsearch",
- "description": "Full text search framework for Nextcloud",
- "minimum-stability": "stable",
- "license": "agpl",
- "config": {
- "optimize-autoloader": true,
- "classmap-authoritative": true,
- "autoloader-suffix": "FullTextSearch"
- },
- "authors": [
- {
- "name": "Maxence Lange",
- "email": "maxence@artificial-owl.com"
- }
- ],
- "autoload": {
- "psr-4": {
- "OCA\\FullTextSearch\\": "lib/"
- }
- },
- "require": {
- "artificial-owl/my-small-php-tools": "~23"
- }
-}
diff --git a/composer.lock b/composer.lock
deleted file mode 100644
index 16163b4..0000000
--- a/composer.lock
+++ /dev/null
@@ -1,59 +0,0 @@
-{
- "_readme": [
- "This file locks the dependencies of your project to a known state",
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
- "This file is @generated automatically"
- ],
- "content-hash": "b52d6a5c9fb9be09368e44333c380806",
- "packages": [
- {
- "name": "artificial-owl/my-small-php-tools",
- "version": "v23.0.2",
- "source": {
- "type": "git",
- "url": "https://github.com/ArtificialOwl/my-small-php-tools.git",
- "reference": "0246b20ebffabcb1e929c71d3b0f221086f72db1"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/ArtificialOwl/my-small-php-tools/zipball/0246b20ebffabcb1e929c71d3b0f221086f72db1",
- "reference": "0246b20ebffabcb1e929c71d3b0f221086f72db1",
- "shasum": ""
- },
- "require": {
- "php": ">=7.0"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "ArtificialOwl\\MySmallPhpTools\\": "lib/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "AGPL-3.0-or-later"
- ],
- "authors": [
- {
- "name": "Maxence Lange",
- "email": "maxence@artificial-owl.com"
- }
- ],
- "description": "My small PHP Tools",
- "support": {
- "issues": "https://github.com/ArtificialOwl/my-small-php-tools/issues",
- "source": "https://github.com/ArtificialOwl/my-small-php-tools/tree/v23.0.2"
- },
- "time": "2021-07-26T12:32:51+00:00"
- }
- ],
- "packages-dev": [],
- "aliases": [],
- "minimum-stability": "stable",
- "stability-flags": [],
- "prefer-stable": false,
- "prefer-lowest": false,
- "platform": [],
- "platform-dev": [],
- "plugin-api-version": "2.0.0"
-}
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index bcbb084..e54dd6a 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -47,12 +47,10 @@ use OCP\FullTextSearch\IFullTextSearchManager;
use OCP\INavigationManager;
use OCP\IServerContainer;
use OCP\IURLGenerator;
-use Symfony\Component\Routing\Exception\RouteNotFoundException;
use Psr\Container\ContainerInterface;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
use Throwable;
-require_once __DIR__ . '/../../vendor/autoload.php';
-
class Application extends App implements IBootstrap {
@@ -137,11 +135,11 @@ class Application extends App implements IBootstrap {
$urlGen = OC::$server->get(IURLGenerator::class);
return [
- 'id' => self::APP_ID,
+ 'id' => self::APP_ID,
'order' => 5,
- 'href' => $urlGen->linkToRoute(self::APP_ID . '.Navigation.navigate'),
- 'icon' => $urlGen->imagePath(self::APP_ID, 'fulltextsearch.svg'),
- 'name' => 'Search'
+ 'href' => $urlGen->linkToRoute(self::APP_ID . '.Navigation.navigate'),
+ 'icon' => $urlGen->imagePath(self::APP_ID, 'fulltextsearch.svg'),
+ 'name' => 'Search'
];
}
diff --git a/lib/Command/Index.php b/lib/Command/Index.php
index 1508769..b8fece8 100644
--- a/lib/Command/Index.php
+++ b/lib/Command/Index.php
@@ -31,7 +31,7 @@ declare(strict_types=1);
namespace OCA\FullTextSearch\Command;
-use ArtificialOwl\MySmallPhpTools\Traits\TArrayTools;
+use OCA\FullTextSearch\Tools\Traits\TArrayTools;
use Exception;
use OC\Core\Command\InterruptedException;
use OCA\FullTextSearch\ACommandBase;
diff --git a/lib/Command/Live.php b/lib/Command/Live.php
index 79684ca..1897de8 100644
--- a/lib/Command/Live.php
+++ b/lib/Command/Live.php
@@ -31,7 +31,7 @@ declare(strict_types=1);
namespace OCA\FullTextSearch\Command;
-use ArtificialOwl\MySmallPhpTools\Traits\TArrayTools;
+use OCA\FullTextSearch\Tools\Traits\TArrayTools;
use Exception;
use OC\Core\Command\InterruptedException;
use OCA\FullTextSearch\ACommandBase;
diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php
index 989a7bf..07947cc 100644
--- a/lib/Controller/ApiController.php
+++ b/lib/Controller/ApiController.php
@@ -31,13 +31,14 @@ declare(strict_types=1);
namespace OCA\FullTextSearch\Controller;
-use ArtificialOwl\MySmallPhpTools\Traits\Nextcloud\TNCDataResponse;
use Exception;
+use OC\AppFramework\Http;
use OCA\FullTextSearch\AppInfo\Application;
use OCA\FullTextSearch\Model\SearchRequest;
use OCA\FullTextSearch\Service\ConfigService;
use OCA\FullTextSearch\Service\MiscService;
use OCA\FullTextSearch\Service\SearchService;
+use OCA\FullTextSearch\Tools\Traits\TDeserialize;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
@@ -50,10 +51,7 @@ use OCP\IUserSession;
* @package OCA\FullTextSearch\Controller
*/
class ApiController extends Controller {
-
-
- use TNCDataResponse;
-
+ use TDeserialize;
/** @var IUserSession */
private $userSession;
@@ -128,23 +126,25 @@ class ApiController extends Controller {
$user = $this->userSession->getUser();
$result = $this->searchService->search($user->getUID(), $request);
- return $this->success(
- $result,
+ return new DataResponse(
[
'request' => $request,
- 'version' => $this->configService->getAppValue('installed_version')
+ 'version' => $this->configService->getAppValue('installed_version'),
+ 'result' => $result,
+ 'status' => 1
]
);
} catch (Exception $e) {
- return $this->fail(
- $e,
+ return new DataResponse(
[
'request' => $request,
- 'version' => $this->configService->getAppValue('installed_version')
- ]
+ 'version' => $this->configService->getAppValue('installed_version'),
+ 'status' => -1,
+ 'exception' => get_class($e),
+ 'message' => $e->getMessage()
+ ], Http::STATUS_INTERNAL_SERVER_ERROR
);
}
}
-
}
diff --git a/lib/Db/CoreQueryBuilder.php b/lib/Db/CoreQueryBuilder.php
new file mode 100644
index 0000000..a7b7d8d
--- /dev/null
+++ b/lib/Db/CoreQueryBuilder.php
@@ -0,0 +1,48 @@
+<?php
+
+declare(strict_types=1);
+
+
+/**
+ * FullTextSearch - Full text search framework for Nextcloud
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Maxence Lange <maxence@artificial-owl.com>
+ * @copyright 2018
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\FullTextSearch\Db;
+
+
+
+use OCA\FullTextSearch\Tools\Db\ExtendedQueryBuilder;
+
+/**
+ * Class CoreQueryBuilder
+ *
+ * @package OCA\Files_FullTextSearch\Db
+ */
+class CoreQueryBuilder extends ExtendedQueryBuilder {
+
+
+
+}
+
diff --git a/lib/Db/CoreRequestBuilder.php b/lib/Db/CoreRequestBuilder.php
index 47018cc..54ffc76 100644
--- a/lib/Db/CoreRequestBuilder.php
+++ b/lib/Db/CoreRequestBuilder.php
@@ -85,6 +85,11 @@ class CoreRequestBuilder {
}
+ public function getQueryBuilder(): CoreQueryBuilder {
+ return new CoreQueryBuilder();
+ }
+
+
/**
* Limit the request to the Id
*
diff --git a/lib/Db/IndexesRequestBuilder.php b/lib/Db/IndexesRequestBuilder.php
index 7634ec6..0fd87ee 100644
--- a/lib/Db/IndexesRequestBuilder.php
+++ b/lib/Db/IndexesRequestBuilder.php
@@ -31,8 +31,8 @@ declare(strict_types=1);
namespace OCA\FullTextSearch\Db;
-use ArtificialOwl\MySmallPhpTools\Traits\TArrayTools;
use OCA\FullTextSearch\Model\Index;
+use OCA\FullTextSearch\Tools\Traits\TArrayTools;
use OCP\DB\QueryBuilder\IQueryBuilder;
@@ -43,7 +43,6 @@ use OCP\DB\QueryBuilder\IQueryBuilder;
*/
class IndexesRequestBuilder extends CoreRequestBuilder {
-
use TArrayTools;
@@ -53,7 +52,7 @@ class IndexesRequestBuilder extends CoreRequestBuilder {
* @return IQueryBuilder
*/
protected function getIndexesInsertSql(): IQueryBuilder {
- $qb = $this->dbConnection->getQueryBuilder();
+ $qb = $this->getQueryBuilder();
$qb->insert(self::TABLE_INDEXES);
return $qb;
diff --git a/lib/Db/TickRequestBuilder.php b/lib/Db/TickRequestBuilder.php
index 81cdd0d..a262bc1 100644
--- a/lib/Db/TickRequestBuilder.php
+++ b/lib/Db/TickRequestBuilder.php
@@ -31,7 +31,7 @@ declare(strict_types=1);
namespace OCA\FullTextSearch\Db;
-use ArtificialOwl\MySmallPhpTools\Traits\TArrayTools;
+use OCA\FullTextSearch\Tools\Traits\TArrayTools;
use OCA\FullTextSearch\Model\Tick;
use OCP\DB\QueryBuilder\IQueryBuilder;
diff --git a/lib/Model/Index.php b/lib/Model/Index.php
index 31d74ec..669456b 100644
--- a/lib/Model/Index.php
+++ b/lib/Model/Index.php
@@ -31,7 +31,7 @@ declare(strict_types=1);
namespace OCA\FullTextSearch\Model;
-use ArtificialOwl\MySmallPhpTools\Traits\TArrayTools;
+use OCA\FullTextSearch\Tools\Traits\TArrayTools;
use JsonSerializable;
use OCP\FullTextSearch\Model\IIndex;
use OCP\IURLGenerator;
diff --git a/lib/Model/IndexOptions.php b/lib/Model/IndexOptions.php
index 6d54876..a4e1831 100644
--- a/lib/Model/IndexOptions.php
+++ b/lib/Model/IndexOptions.php
@@ -31,7 +31,7 @@ declare(strict_types=1);
namespace OCA\FullTextSearch\Model;
-use ArtificialOwl\MySmallPhpTools\Traits\TArrayTools;
+use OCA\FullTextSearch\Tools\Traits\TArrayTools;
use JsonSerializable;
use OCP\FullTextSearch\Model\IIndexOptions;
diff --git a/lib/Model/Runner.php b/lib/Model/Runner.php
index 0861861..96ef545 100644
--- a/lib/Model/Runner.php
+++ b/lib/Model/Runner.php
@@ -31,7 +31,7 @@ declare(strict_types=1);
namespace OCA\FullTextSearch\Model;
-use ArtificialOwl\MySmallPhpTools\Traits\TArrayTools;
+use OCA\FullTextSearch\Tools\Traits\TArrayTools;
use Exception;
use OCA\FullTextSearch\ACommandBase;
use OCA\FullTextSearch\Exceptions\RunnerAlreadyUpException;
diff --git a/lib/Model/SearchRequest.php b/lib/Model/SearchRequest.php
index c56a5e2..991939e 100644
--- a/lib/Model/SearchRequest.php
+++ b/lib/Model/SearchRequest.php
@@ -31,7 +31,7 @@ declare(strict_types=1);
namespace OCA\FullTextSearch\Model;
-use ArtificialOwl\MySmallPhpTools\Traits\TArrayTools;
+use OCA\FullTextSearch\Tools\Traits\TArrayTools;
use JsonSerializable;
use OCP\FullTextSearch\Model\ISearchRequest;
use OCP\FullTextSearch\Model\ISearchRequestSimpleQuery;
diff --git a/lib/Model/Tick.php b/lib/Model/Tick.php
index fada00a..baf888c 100644
--- a/lib/Model/Tick.php
+++ b/lib/Model/Tick.php
@@ -31,7 +31,7 @@ declare(strict_types=1);
namespace OCA\FullTextSearch\Model;
-use ArtificialOwl\MySmallPhpTools\Traits\TArrayTools;
+use OCA\FullTextSearch\Tools\Traits\TArrayTools;
/**
* Class Tick
diff --git a/lib/Search/UnifiedSearchProvider.php b/lib/Search/UnifiedSearchProvider.php
index 70cf72e..e6598a5 100644
--- a/lib/Search/UnifiedSearchProvider.php
+++ b/lib/Search/UnifiedSearchProvider.php
@@ -31,7 +31,7 @@ declare(strict_types=1);
namespace OCA\FullTextSearch\Search;
-use ArtificialOwl\MySmallPhpTools\Traits\TArrayTools;
+use OCA\FullTextSearch\Tools\Traits\TArrayTools;
use Exception;
use OCA\FullTextSearch\Model\SearchRequest;
use OCA\FullTextSearch\Service\ConfigService;
diff --git a/lib/Service/SearchService.php b/lib/Service/SearchService.php
index 2dd8958..f41e738 100644
--- a/lib/Service/SearchService.php
+++ b/lib/Service/SearchService.php
@@ -31,7 +31,7 @@ declare(strict_types=1);
namespace OCA\FullTextSearch\Service;
-use ArtificialOwl\MySmallPhpTools\Traits\Nextcloud\nc22\TNC22Logger;
+use OCA\FullTextSearch\Tools\Traits\Nextcloud\nc22\TNC22Logger;
use Exception;
use OC;
use OC\App\AppManager;
@@ -44,6 +44,7 @@ use OCA\FullTextSearch\Exceptions\EmptySearchException;
use OCA\FullTextSearch\Exceptions\ProviderDoesNotExistException;
use OCA\FullTextSearch\Model\SearchRequest;
use OCA\FullTextSearch\Model\SearchResult;
+use OCA\FullTextSearch\Tools\Traits\TNCLogger;
use OCP\FullTextSearch\IFullTextSearchPlatform;
use OCP\FullTextSearch\IFullTextSearchProvider;
use OCP\FullTextSearch\Model\IDocumentAccess;
@@ -63,7 +64,7 @@ use OCP\IUserManager;
class SearchService implements ISearchService {
- use TNC22Logger;
+ use TNCLogger;
/** @var string */
diff --git a/lib/Tools/Db/ExtendedQueryBuilder.php b/lib/Tools/Db/ExtendedQueryBuilder.php
new file mode 100644
index 0000000..a019148
--- /dev/null
+++ b/lib/Tools/Db/ExtendedQueryBuilder.php
@@ -0,0 +1,1135 @@
+<?php
+
+declare(strict_types=1);
+
+
+/**
+ * FullTextSearch - Full text search framework for Nextcloud
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Maxence Lange <maxence@artificial-owl.com>
+ * @copyright 2022
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\FullTextSearch\Tools\Db;
+
+use DateInterval;
+use DateTime;
+use Doctrine\DBAL\Query\QueryBuilder as DBALQueryBuilder;
+use Exception;
+use OC;
+use OC\DB\QueryBuilder\QueryBuilder;
+use OC\SystemConfig;
+use OCA\FullTextSearch\Tools\Exceptions\DateTimeException;
+use OCA\FullTextSearch\Tools\Exceptions\InvalidItemException;
+use OCA\FullTextSearch\Tools\Exceptions\RowNotFoundException;
+use OCA\FullTextSearch\Tools\Traits\TArrayTools;
+use OCP\DB\QueryBuilder\ICompositeExpression;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+use Psr\Log\LoggerInterface;
+
+class ExtendedQueryBuilder extends QueryBuilder {
+ use TArrayTools;
+
+
+ /** @var string */
+ private $defaultSelectAlias = '';
+
+ /** @var array */
+ private $defaultValues = [];
+
+
+ public function __construct() {
+ parent::__construct(
+ OC::$server->get(IDBConnection::class),
+ OC::$server->get(SystemConfig::class),
+ OC::$server->get(LoggerInterface::class)
+ );
+ }
+
+
+ /**
+ * @param string $alias
+ *
+ * @return self
+ */
+ public function setDefaultSelectAlias(string $alias): self {
+ $this->defaultSelectAlias = $alias;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDefaultSelectAlias(): string {
+ return $this->defaultSelectAlias;
+ }
+
+
+ /**
+ * @return array
+ */
+ public function getDefaultValues(): array {
+ return $this->defaultValues;
+ }
+
+ /**
+ * @param string $key
+ * @param string $value
+ *
+ * @return $this
+ */
+ public function addDefaultValue(string $key, string $value): self {
+ $this->defaultValues[$key] = $value;
+
+ return $this;
+ }
+
+ /**
+ * @param int $size
+ * @param int $page
+ */
+ public function paginate(int $size, int $page = 0): void {
+ if ($page < 0) {
+ $page = 0;
+ }
+
+ $this->chunk($page * $size, $size);
+ }
+
+ /**
+ * @param int $offset
+ * @param int $limit
+ */
+ public function chunk(int $offset, int $limit): void {
+ if ($offset > -1) {
+ $this->setFirstResult($offset);
+ }
+
+ if ($limit > 0) {
+ $this->setMaxResults($limit);
+ }
+ }
+
+
+ /**
+ * Limit the request to the Id
+ *
+ * @param int $id
+ */
+ public function limitToId(int $id): void {
+ $this->limitInt('id', $id);
+ }
+
+ /**
+ * @param array $ids
+ */
+ public function limitToIds(array $ids): void {
+ $this->limitArray('id', $ids);
+ }
+
+ /**
+ * @param string $id
+ */
+ public function limitToIdString(string $id): void {
+ $this->limit('id', $id);
+ }
+
+ /**
+ * @param string $userId
+ */
+ public function limitToUserId(string $userId): void {
+ $this->limit('user_id', $userId);
+ }
+
+ /**
+ * @param string $uniqueId
+ */
+ public function limitToUniqueId(string $uniqueId): void {
+ $this->limit('unique_id', $uniqueId);
+ }
+
+ /**
+ * @param string $memberId
+ */
+ public function limitToMemberId(string $memberId): void {
+ $this->limit('member_id', $memberId);
+ }
+
+ /**
+ * @param string $status
+ */
+ public function limitToStatus(string $status): void {
+ $this->limit('status', $status, '', false);
+ }
+
+ /**
+ * @param int $type
+ */
+ public function limitToType(int $type): void {
+ $this->limitInt('type', $type);
+ }
+
+ /**
+ * @param string $type
+ */
+ public function limitToTypeString(string $type): void {
+ $this->limit('type', $type, '', false);
+ }
+
+ /**
+ * @param string $token
+ */
+ public function limitToToken(string $token): void {
+ $this->limit('token', $token);
+ }
+
+
+ /**
+ * Limit the request to the creation
+ *
+ * @param int $delay
+ *
+ * @return self
+ * @throws Exception
+ */
+ public function limitToCreation(int $delay = 0): self {
+ $date = new DateTime('now');
+ $date->sub(new DateInterval('PT' . $delay . 'M'));
+
+ $this->limitToDBFieldDateTime('creation', $date, true);
+
+ return $this;
+ }
+
+
+ /**
+ * @param string $field
+ * @param DateTime $date
+ * @param bool $orNull
+ */
+ public function limitToDBFieldDateTime(string $field, DateTime $date, bool $orNull = false): void {
+ $expr = $this->expr();
+ $pf = ($this->getType() === DBALQueryBuilder::SELECT) ? $this->getDefaultSelectAlias()
+ . '.' : '';
+ $field = $pf . $field;
+
+ $orX = $expr->orX();
+ $orX->add(
+ $expr->lte($field, $this->createNamedParameter($date, IQueryBuilder::PARAM_DATE))
+ );
+
+ if ($orNull === true) {
+ $orX->add($expr->isNull($field));
+ }
+
+ $this->andWhere($orX);
+ }
+
+
+ /**
+ * @param int $timestamp
+ * @param string $field
+ *
+ * @throws DateTimeException
+ */
+ public function limitToSince(int $timestamp, string $field): void {
+ try {
+ $dTime = new DateTime();
+ $dTime->setTimestamp($timestamp);
+ } catch (Exception $e) {
+ throw new DateTimeException($e->getMessage());
+ }
+
+ $expr = $this->expr();
+ $pf = ($this->getType() === DBALQueryBuilder::SELECT) ? $this->getDefaultSelectAlias() . '.' : '';
+ $field = $pf . $field;
+
+ $orX = $expr->orX();
+ $orX->add(
+ $expr->gte($field, $this->createNamedParameter($dTime, IQueryBuilder::PARAM_DATE))
+ );
+
+ $this->andWhere($orX);
+ }
+
+
+ /**
+ * @param string $field
+ * @param string $value
+ */
+ public function searchInDBField(string $field, string $value): void {
+ $expr = $this->expr();
+
+ $pf = ($this->getType() === DBALQueryBuilder::SELECT) ? $this->getDefaultSelectAlias() . '.' : '';
+ $field = $pf . $field;
+
+ $this->andWhere($expr->iLike($field, $this->createNamedParameter($value)));
+ }
+
+
+ /**
+ * @param string $field
+ * @param string $value
+ * @param string $alias
+ * @param bool $cs
+ */
+ public function like(string $field, string $value, string $alias = '', bool $cs = true): void {
+ $this->andWhere($this->exprLike($field, $value, $alias, $cs));
+ }
+
+
+ /**
+ * @param string $field
+ * @param string $value
+ * @param string $alias
+ * @param bool $cs
+ */
+ public function limit(string $field, string $value, string $alias = '', bool $cs = true): void {
+ $this->andWhere($this->exprLimit($field, $value, $alias, $cs));
+ }
+
+ /**
+ * @param string $field
+ * @param int $value
+ * @param string $alias
+ */
+ public function limitInt(string $field, int $value, string $alias = ''): void {
+ $this->andWhere($this->exprLimitInt($field, $value, $alias));
+ }
+
+ /**
+ * @param string $field
+ * @param bool $value
+ * @param string $alias
+ */
+ public function limitBool(string $field, bool $value, string $alias = ''): void {
+ $this->andWhere($this->exprLimitBool($field, $value, $alias));
+ }
+
+ /**
+ * @param string $field
+ * @param bool $orNull
+ * @param string $alias
+ */
+ public function limitEmpty(string $field, bool $orNull = false, string $alias = ''): void {
+ $this->andWhere($this->exprLimitEmpty($field, $orNull, $alias));
+ }
+
+ /**
+ * @param string $field
+ * @param bool $orEmpty
+ * @param string $alias
+ */
+ public function limitNull(string $field, bool $orEmpty = false, string $alias = ''): void {
+ $this->andWhere($this->exprLimitNull($field, $orEmpty, $alias));
+ }
+
+ /**
+ * @param string $field
+ * @param array $value
+ * @param string $alias
+ * @param bool $cs
+ */
+ public function limitArray(string $field, array $value, string $alias = '', bool $cs = true): void {
+ $this->andWhere($this->exprLimitArray($field, $value, $alias, $cs));
+ }
+
+ /**
+ * @param string $field
+ * @param array $value
+ * @param string $alias
+ */
+ public function limitInArray(string $field, array $value, string $alias = ''): void {
+ $this->andWhere($this->exprLimitInArray($field, $value, $alias));
+ }
+
+ /**
+ * @param string $field
+ * @param int $flag
+ * @param string $alias
+ */
+ public function limitBitwise(string $field, int $flag, string $alias = ''): void {
+ $this->andWhere($this->exprLimitBitwise($field, $flag, $alias));
+ }
+
+ /**
+ * @param string $field
+ * @param int $value
+ * @param bool $gte
+ * @param string $alias
+ */
+ public function gt(string $field, int $value, bool $gte = false, string $alias = ''): void {
+ $this->andWhere($this->exprGt($field, $value, $gte, $alias));
+ }
+
+ /**
+ * @param string $field
+ * @param int $value
+ * @param bool $lte
+ * @param string $alias
+ */
+ public function lt(string $field, int $value, bool $lte = false, string $alias = ''): void {
+ $this->andWhere($this->exprLt($field, $value, $lte, $alias));
+ }
+
+
+ /**
+ * @param string $field
+ * @param string $value
+ * @param string $alias
+ * @param bool $cs
+ *
+ * @return string
+ */
+ public function exprLike(string $field, string $value, string $alias = '', bool $cs = true): string {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $expr = $this->expr();
+ if ($cs) {
+ return $expr->like($field, $this->createNamedParameter($value));
+ } else {
+ return $expr->iLike($field, $this->createNamedParameter($value));
+ }
+ }
+
+
+ /**
+ * @param string $field
+ * @param string $value
+ * @param string $alias
+ * @param bool $cs
+ *
+ * @return string
+ */
+ public function exprLimit(string $field, string $value, string $alias = '', bool $cs = true): string {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $expr = $this->expr();
+ if ($value === '') {
+ return $expr->emptyString($field);
+ }
+ if ($cs) {
+ return $expr->eq($field, $this->createNamedParameter($value));
+ } else {
+ $func = $this->func();
+
+ return $expr->eq($func->lower($field), $func->lower($this->createNamedParameter($value)));
+ }
+ }
+
+
+ /**
+ * @param string $field
+ * @param int $value
+ * @param string $alias
+ *
+ * @return string
+ */
+ public function exprLimitInt(string $field, int $value, string $alias = ''): string {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $expr = $this->expr();
+
+ return $expr->eq($field, $this->createNamedParameter($value, IQueryBuilder::PARAM_INT));
+ }
+
+
+ /**
+ * @param string $field
+ * @param bool $value
+ * @param string $alias
+ *
+ * @return string
+ */
+ public function exprLimitBool(string $field, bool $value, string $alias = ''): string {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $expr = $this->expr();
+
+ return $expr->eq($field, $this->createNamedParameter($value, IQueryBuilder::PARAM_BOOL));
+ }
+
+ /**
+ * @param string $field
+ * @param bool $orNull
+ * @param string $alias
+ *
+ * @return ICompositeExpression
+ */
+ public function exprLimitEmpty(
+ string $field,
+ bool $orNull = false,
+ string $alias = ''
+ ): ICompositeExpression {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $expr = $this->expr();
+ $orX = $expr->orX();
+ $orX->add($expr->emptyString($field));
+ if ($orNull) {
+ $orX->add($expr->isNull($field));
+ }
+
+ return $orX;
+ }
+
+ /**
+ * @param string $field
+ * @param bool $orEmpty
+ * @param string $alias
+ *
+ * @return ICompositeExpression
+ */
+ public function exprLimitNull(
+ string $field,
+ bool $orEmpty = false,
+ string $alias = ''
+ ): ICompositeExpression {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $expr = $this->expr();
+ $orX = $expr->orX();
+ $orX->add($expr->isNull($field));
+ if ($orEmpty) {
+ $orX->add($expr->emptyString($field));
+ }
+
+ return $orX;
+ }
+
+
+ /**
+ * @param string $field
+ * @param array $values
+ * @param string $alias
+ * @param bool $cs
+ *
+ * @return ICompositeExpression
+ */
+ public function exprLimitArray(
+ string $field,
+ array $values,
+ string $alias = '',
+ bool $cs = true
+ ): ICompositeExpression {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $andX = $this->expr()->andX();
+ foreach ($values as $value) {
+ if (is_integer($value)) {
+ $andX->add($this->exprLimitInt($field, $value, $alias));
+ } else {
+ $andX->add($this->exprLimit($field, $value, $alias, $cs));
+ }
+ }
+
+ return $andX;
+ }
+
+
+ /**
+ * @param string $field
+ * @param array $values
+ * @param string $alias
+ *
+ * @return string
+ */
+ public function exprLimitInArray(string $field, array $values, string $alias = ''): string {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $expr = $this->expr();
+
+ return $expr->in($field, $this->createNamedParameter($values, IQueryBuilder::PARAM_STR_ARRAY));
+ }
+
+
+ /**
+ * @param string $field
+ * @param int $flag
+ * @param string $alias
+ *
+ * @return string
+ */
+ public function exprLimitBitwise(string $field, int $flag, string $alias = ''): string {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $expr = $this->expr();
+
+ return $expr->gt(
+ $expr->bitwiseAnd($field, $flag),
+ $this->createNamedParameter(0, IQueryBuilder::PARAM_INT)
+ );
+ }
+
+
+ /**
+ * @param string $field
+ * @param int $value
+ * @param bool $lte
+ * @param string $alias
+ *
+ * @return string
+ */
+ public function exprLt(string $field, int $value, bool $lte = false, string $alias = ''): string {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $expr = $this->expr();
+
+ if ($lte) {
+ return $expr->lte($field, $this->createNamedParameter($value, IQueryBuilder::PARAM_INT));
+ } else {
+ return $expr->lt($field, $this->createNamedParameter($value, IQueryBuilder::PARAM_INT));
+ }
+ }
+
+ /**
+ * @param string $field
+ * @param int $value
+ * @param bool $gte
+ * @param string $alias
+ *
+ * @return string
+ */
+ public function exprGt(string $field, int $value, bool $gte = false, string $alias = ''): string {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $expr = $this->expr();
+
+ if ($gte) {
+ return $expr->gte($field, $this->createNamedParameter($value, IQueryBuilder::PARAM_INT));
+ } else {
+ return $expr->gt($field, $this->createNamedParameter($value, IQueryBuilder::PARAM_INT));
+ }
+ }
+
+
+ /**
+ * @param string $field
+ * @param string $value
+ * @param string $alias
+ * @param bool $cs
+ */
+ public function unlike(string $field, string $value, string $alias = '', bool $cs = true): void {
+ $this->andWhere($this->exprUnlike($field, $value, $alias, $cs));
+ }
+
+
+ /**
+ * @param string $field
+ * @param string $value
+ * @param string $alias
+ * @param bool $cs
+ */
+ public function filter(string $field, string $value, string $alias = '', bool $cs = true): void {
+ $this->andWhere($this->exprFilter($field, $value, $alias, $cs));
+ }
+
+ /**
+ * @param string $field
+ * @param int $value
+ * @param string $alias
+ */
+ public function filterInt(string $field, int $value, string $alias = ''): void {
+ $this->andWhere($this->exprFilterInt($field, $value, $alias));
+ }
+
+ /**
+ * @param string $field
+ * @param bool $value
+ * @param string $alias
+ */
+ public function filterBool(string $field, bool $value, string $alias = ''): void {
+ $this->andWhere($this->exprFilterBool($field, $value, $alias));
+ }
+
+ /**
+ * @param string $field
+ * @param bool $norNull
+ * @param string $alias
+ */
+ public function filterEmpty(string $field, bool $norNull = false, string $alias = ''): void {
+ $this->andWhere($this->exprFilterEmpty($field, $norNull, $alias));
+ }
+
+ /**
+ * @param string $field
+ * @param bool $norEmpty
+ * @param string $alias
+ */
+ public function filterNull(string $field, bool $norEmpty = false, string $alias = ''): void {
+ $this->andWhere($this->exprFilterNull($field, $norEmpty, $alias));
+ }
+
+ /**
+ * @param string $field
+ * @param array $value
+ * @param string $alias
+ * @param bool $cs
+ */
+ public function filterArray(string $field, array $value, string $alias = '', bool $cs = true): void {
+ $this->andWhere($this->exprFilterArray($field, $value, $alias, $cs));
+ }
+
+ /**
+ * @param string $field
+ * @param array $value
+ * @param string $alias
+ */
+ public function filterInArray(string $field, array $value, string $alias = ''): void {
+ $this->andWhere($this->exprFilterInArray($field, $value, $alias));
+ }
+
+ /**
+ * @param string $field
+ * @param int $flag
+ * @param string $alias
+ */
+ public function filterBitwise(string $field, int $flag, string $alias = ''): void {
+ $this->andWhere($this->exprFilterBitwise($field, $flag, $alias));
+ }
+
+
+ /**
+ * @param string $field
+ * @param string $value
+ * @param string $alias
+ * @param bool $cs
+ *
+ * @return string
+ */
+ public function exprUnlike(string $field, string $value, string $alias = '', bool $cs = true): string {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $expr = $this->expr();
+ if ($cs) {
+ return $expr->notLike($field, $this->createNamedParameter($value));
+ } else {
+ $func = $this->func();
+
+ return $expr->notLike($func->lower($field), $func->lower($this->createNamedParameter($value)));
+ }
+ }
+
+
+ /**
+ * @param string $field
+ * @param string $value
+ * @param string $alias
+ * @param bool $cs
+ *
+ * @return string
+ */
+ public function exprFilter(string $field, string $value, string $alias = '', bool $cs = true): string {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $expr = $this->expr();
+ if ($value === '') {
+ return $expr->nonEmptyString($field);
+ }
+ if ($cs) {
+ return $expr->neq($field, $this->createNamedParameter($value));
+ } else {
+ $func = $this->func();
+
+ return $expr->neq($func->lower($field), $func->lower($this->createNamedParameter($value)));
+ }
+ }
+
+
+ /**
+ * @param string $field
+ * @param int $value
+ * @param string $alias
+ *
+ * @return string
+ */
+ public function exprFilterInt(string $field, int $value, string $alias = ''): string {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $expr = $this->expr();
+
+ return $expr->neq($field, $this->createNamedParameter($value, IQueryBuilder::PARAM_INT));
+ }
+
+
+ /**
+ * @param string $field
+ * @param bool $value
+ * @param string $alias
+ *
+ * @return string
+ */
+ public function exprFilterBool(string $field, bool $value, string $alias = ''): string {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $expr = $this->expr();
+
+ return $expr->neq($field, $this->createNamedParameter($value, IQueryBuilder::PARAM_BOOL));
+ }
+
+ /**
+ * @param string $field
+ * @param bool $norNull
+ * @param string $alias
+ *
+ * @return ICompositeExpression
+ */
+ public function exprFilterEmpty(
+ string $field,
+ bool $norNull = false,
+ string $alias = ''
+ ): ICompositeExpression {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $expr = $this->expr();
+ $andX = $expr->andX();
+ $andX->add($expr->nonEmptyString($field));
+ if ($norNull) {
+ $andX->add($expr->isNotNull($field));
+ }
+
+ return $andX;
+ }
+
+ /**
+ * @param string $field
+ * @param bool $norEmpty
+ * @param string $alias
+ *
+ * @return ICompositeExpression
+ */
+ public function exprFilterNull(
+ string $field,
+ bool $norEmpty = false,
+ string $alias = ''
+ ): ICompositeExpression {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $expr = $this->expr();
+ $andX = $expr->andX();
+ $andX->add($expr->isNotNull($field));
+ if ($norEmpty) {
+ $andX->add($expr->nonEmptyString($field));
+ }
+
+ return $andX;
+ }
+
+
+ /**
+ * @param string $field
+ * @param array $values
+ * @param string $alias
+ * @param bool $cs
+ *
+ * @return ICompositeExpression
+ */
+ public function exprFilterArray(
+ string $field,
+ array $values,
+ string $alias = '',
+ bool $cs = true
+ ): ICompositeExpression {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $orX = $this->expr()->orX();
+ foreach ($values as $value) {
+ if (is_integer($value)) {
+ $orX->add($this->exprFilterInt($field, $value, $alias));
+ } else {
+ $orX->add($this->exprFilter($field, $value, $alias, $cs));
+ }
+ }
+
+ return $orX;
+ }
+
+
+ /**
+ * @param string $field
+ * @param array $values
+ * @param string $alias
+ *
+ * @return string
+ */
+ public function exprFilterInArray(string $field, array $values, string $alias = ''): string {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $expr = $this->expr();
+
+ return $expr->notIn($field, $this->createNamedParameter($values, IQueryBuilder::PARAM_STR_ARRAY));
+ }
+
+
+ /**
+ * @param string $field
+ * @param int $flag
+ * @param string $alias
+ *
+ * @return string
+ */
+ public function exprFilterBitwise(string $field, int $flag, string $alias = ''): string {
+ if ($this->getType() === DBALQueryBuilder::SELECT) {
+ $field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
+ }
+
+ $expr = $this->expr();
+
+ return $expr->eq(
+ $expr->bitwiseAnd($field, $flag),
+ $this->createNamedParameter(0, IQueryBuilder::PARAM_INT)
+ );
+ }
+
+
+ /**
+ * @param string $object
+ * @param array $params
+ *
+ * @return IQueryRow
+ * @throws RowNotFoundException
+ * @throws InvalidItemException
+ */
+ public function asItem(string $object, array $params = []): IQueryRow {
+ return $this->getRow([$this, 'parseSimpleSelectSql'], $object, $params);
+ }
+
+ /**
+ * @param string $object
+ * @param array $params
+ *
+ * @return IQueryRow[]
+ */
+ public function asItems(string $object, array $params = []): array {
+ return $this->getRows([$this, 'parseSimpleSelectSql'], $object, $params);
+ }
+
+
+ /**
+ * @param string $field
+ * @param array $params
+ *
+ * @return IQueryRow
+ * @throws InvalidItemException
+ * @throws RowNotFoundException
+ */
+ public function asItemFromField(string $field, array $params = []): IQueryRow {
+ $param['modelFromField'] = $field;
+
+ return $this->getRow([$this, 'parseSimpleSelectSql'], '', $params);
+ }
+
+ /**
+ * @param string $field
+ * @param array $params
+ *
+ * @return IQueryRow[]
+ */
+ public function asItemsFromField(string $field, array $params = []): array {
+ $param['modelFromField'] = $field;
+
+ return $this->getRows([$this, 'parseSimpleSelectSql'], $field, $params);
+ }
+
+
+ /**
+ * @param array $data
+ * @param ExtendedQueryBuilder $qb
+ * @param string $object
+ * @param array $params
+ *
+ * @return IQueryRow
+ * @throws InvalidItemException
+ */
+ private function parseSimpleSelectSql(
+ array $data,
+ ExtendedQueryBuilder $qb,
+ string $object,
+ array $params
+ ): IQueryRow {
+ $fromField = $this->get('modelFromField', $params);
+ if ($fromField !== '') {
+ $object = $fromField;
+ }
+
+ $item = new $object();
+ if (!($item instanceof IQueryRow)) {
+ throw new InvalidItemException();
+ }
+
+ if (!empty($params)) {
+ $data['_params'] = $params;
+ }
+
+ foreach ($qb->getDefaultValues() as $k => $v) {
+ if ($this->get($k, $data) === '') {
+ $data[$k] = $v;
+ }
+ }
+
+ $data = array_merge($qb->getDefaultValues(), $data);
+
+ $item->importFromDatabase($data);
+
+ return $item;
+ }
+
+
+ /**
+ * @param callable $method
+ * @param string $object
+ * @param array $params
+ *
+ * @return IQueryRow
+ * @throws RowNotFoundException
+ */
+ public function getRow(callable $method, string $object = '', array $params = []): IQueryRow {
+ $cursor = $this->execute();
+ $data = $cursor->fetch();
+ $cursor->closeCursor();
+
+ if ($data === false) {
+ throw new RowNotFoundException();
+ }
+
+ return $method($data, $this, $object, $params);
+ }
+
+
+ /**
+ * @param callable $method
+ * @param string $object
+ * @param array $params
+ *
+ * @return IQueryRow[]
+ */
+ public function getRows(callable $method, string $object = '', array $params = []): array {
+ $rows = [];
+ $cursor = $this->execute();
+ while ($data = $cursor->fetch()) {
+ try {
+ $rows[] = $method($data, $this, $object, $params);
+ } catch (Exception $e) {
+ }
+ }
+ $cursor->closeCursor();
+
+ return $rows;
+ }
+
+
+ /**
+ * @param string $table
+ * @param array $fields
+ * @param string $alias
+ *
+ * @return $this
+ */
+ public function generateSelect(
+ string $table,
+ array $fields,
+ string $alias = ''
+ ): self {
+ $selectFields = array_map(
+ function (string $item) use ($alias) {
+ if ($alias === '') {
+ return $item;
+ }
+
+ return $alias . '.' . $item;
+ }, $fields
+ );
+
+ $this->select($selectFields)
+ ->from($table, $alias)
+ ->setDefaultSelectAlias($alias);
+
+ return $this;
+ }
+
+
+ /**
+ * @param array $fields
+ * @param string $alias
+ * @param string $prefix
+ * @param array $default
+ *
+ * @return $this
+ */
+ public function generateSelectAlias(
+ array $fields,
+ string $alias,
+ string $prefix,
+ array $default = []
+ ): self {
+ $prefix = trim($prefix) . '_';
+ foreach ($default as $k => $v) {
+ $this->addDefaultValue($prefix . $k, (string)$v);
+ }
+
+ foreach ($fields as $field) {
+ $this->selectAlias($alias . '.' . $field, $prefix . $field);
+ }
+
+ return $this;
+ }
+}
diff --git a/lib/Tools/Db/IQueryRow.php b/lib/Tools/Db/IQueryRow.php
new file mode 100644
index 0000000..180537d
--- /dev/null
+++ b/lib/Tools/Db/IQueryRow.php
@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types=1);
+
+
+/**
+ * FullTextSearch - Full text search framework for Nextcloud
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Maxence Lange <maxence@artificial-owl.com>
+ * @copyright 2022
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\FullTextSearch\Tools\Db;
+
+/**
+ * Interface IQueryRow
+ *
+ * @package OCA\FullTextSearch\Tools\Db
+ */
+interface IQueryRow {
+
+ /**
+ * import data to feed the model.
+ *
+ * @param array $data
+ *
+ * @return IQueryRow
+ */
+ public function importFromDatabase(array $data): self;
+}
diff --git a/lib/Tools/Exceptions/ArrayNotFoundException.php b/lib/Tools/Exceptions/ArrayNotFoundException.php
new file mode 100644
index 0000000..aee3280
--- /dev/null
+++ b/lib/Tools/Exceptions/ArrayNotFoundException.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+
+/**
+ * FullTextSearch - Full text search framework for Nextcloud
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Maxence Lange <maxence@artificial-owl.com>
+ * @copyright 2022
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\FullTextSearch\Tools\Exceptions;
+
+use Exception;
+
+/**
+ * Class ArrayNotFoundException
+ *
+ * @package OCA\FullTextSearch\Tools\Exceptions
+ */
+class ArrayNotFoundException extends Exception {
+}
diff --git a/lib/Tools/Exceptions/DateTimeException.php b/lib/Tools/Exceptions/DateTimeException.php
new file mode 100644
index 0000000..066b6bf
--- /dev/null
+++ b/lib/Tools/Exceptions/DateTimeException.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+
+/**
+ * FullTextSearch - Full text search framework for Nextcloud
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Maxence Lange <maxence@artificial-owl.com>
+ * @copyright 2022
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\FullTextSearch\Tools\Exceptions;
+
+use Exception;
+
+/**
+ * Class DateTimeException
+ *
+ * @package OCA\FullTextSearch\Tools\Exceptions
+ */
+class DateTimeException extends Exception {
+}
diff --git a/lib/Tools/Exceptions/InvalidItemException.php b/lib/Tools/Exceptions/InvalidItemException.php
new file mode 100644
index 0000000..ff4e89c
--- /dev/null
+++ b/lib/Tools/Exceptions/InvalidItemException.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+
+/**
+ * FullTextSearch - Full text search framework for Nextcloud
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Maxence Lange <maxence@artificial-owl.com>
+ * @copyright 2022
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\FullTextSearch\Tools\Exceptions;
+
+use Exception;
+
+/**
+ * Class InvalidItemException
+ *
+ * @package OCA\FullTextSearch\Tools\Exceptions
+ */
+class InvalidItemException extends Exception {
+}
diff --git a/lib/Tools/Exceptions/ItemNotFoundException.php b/lib/Tools/Exceptions/ItemNotFoundException.php
new file mode 100644
index 0000000..ad28b0a
--- /dev/null
+++ b/lib/Tools/Exceptions/ItemNotFoundException.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+
+/**
+ * FullTextSearch - Full text search framework for Nextcloud
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Maxence Lange <maxence@artificial-owl.com>
+ * @copyright 2022
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\FullTextSearch\Tools\Exceptions;
+
+use Exception;
+
+/**
+ * Class ItemNotFoundException
+ *
+ * @package OCA\FullTextSearch\Tools\Exceptions
+ */
+class ItemNotFoundException extends Exception {
+}
diff --git a/lib/Tools/Exceptions/MalformedArrayException.php b/lib/Tools/Exceptions/MalformedArrayException.php
new file mode 100644
index 0000000..a6a3304
--- /dev/null
+++ b/lib/Tools/Exceptions/MalformedArrayException.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+
+/**
+ * FullTextSearch - Full text search framework for Nextcloud
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Maxence Lange <maxence@artificial-owl.com>
+ * @copyright 2022
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\FullTextSearch\Tools\Exceptions;
+
+use Exception;
+
+/**
+ * Class MalformedArrayException
+ *
+ * @package OCA\FullTextSearch\Tools\Exceptions
+ */
+class MalformedArrayException extends Exception {
+}
diff --git a/lib/Tools/Exceptions/RowNotFoundException.php b/lib/Tools/Exceptions/RowNotFoundException.php
new file mode 100644
index 0000000..b91dd01
--- /dev/null
+++ b/lib/Tools/Exceptions/RowNotFoundException.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+
+/**
+ * FullTextSearch - Full text search framework for Nextcloud
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Maxence Lange <maxence@artificial-owl.com>
+ * @copyright 2022
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\FullTextSearch\Tools\Exceptions;
+
+use Exception;
+
+/**
+ * Class RowNotFoundException
+ *
+ * @package OCA\FullTextSearch\Tools\Exceptions
+ */
+class RowNotFoundException extends Exception {
+}
diff --git a/lib/Tools/Exceptions/UnknownTypeException.php b/lib/Tools/Exceptions/UnknownTypeException.php
new file mode 100644
index 0000000..cba7337
--- /dev/null
+++ b/lib/Tools/Exceptions/UnknownTypeException.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+
+/**
+ * FullTextSearch - Full text search framework for Nextcloud
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Maxence Lange <maxence@artificial-owl.com>
+ * @copyright 2022
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\FullTextSearch\Tools\Exceptions;
+
+use Exception;
+
+/**
+ * Class UnknownTypeException
+ *
+ * @package OCA\FullTextSearch\Tools\Exceptions
+ */
+class UnknownTypeException extends Exception {
+}
diff --git a/lib/Tools/IDeserializable.php b/lib/Tools/IDeserializable.php
new file mode 100644
index 0000000..ab0fb4b
--- /dev/null
+++ b/lib/Tools/IDeserializable.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+
+/**
+ * FullTextSearch - Full text search framework for Nextcloud
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Maxence Lange <maxence@artificial-owl.com>
+ * @copyright 2022
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\FullTextSearch\Tools;
+
+interface IDeserializable {
+
+ /**
+ * @param array $data
+ *
+ * @return self
+ */
+ public function import(array $data): self;
+}
diff --git a/lib/Tools/Traits/TArrayTools.php b/lib/Tools/Traits/TArrayTools.php
new file mode 100644
index 0000000..cb9feae
--- /dev/null
+++ b/lib/Tools/Traits/TArrayTools.php
@@ -0,0 +1,432 @@
+<?php
+
+declare(strict_types=1);
+
+
+/**
+ * FullTextSearch - Full text search framework for Nextcloud
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Maxence Lange <maxence@artificial-owl.com>
+ * @copyright 2022
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\FullTextSearch\Tools\Traits;
+
+use Exception;
+use JsonSerializable;
+use OCA\FullTextSearch\Tools\Exceptions\ArrayNotFoundException;
+use OCA\FullTextSearch\Tools\Exceptions\ItemNotFoundException;
+use OCA\FullTextSearch\Tools\Exceptions\MalformedArrayException;
+use OCA\FullTextSearch\Tools\Exceptions\UnknownTypeException;
+
+trait TArrayTools {
+ public static $TYPE_NULL = 'Null';
+ public static $TYPE_STRING = 'String';
+ public static $TYPE_ARRAY = 'Array';
+ public static $TYPE_BOOLEAN = 'Boolean';
+ public static $TYPE_INTEGER = 'Integer';
+ public static $TYPE_SERIALIZABLE = 'Serializable';
+
+
+ /**
+ * @param string $k
+ * @param array $arr
+ * @param string $default
+ *
+ * @return string
+ */
+ protected function get(string $k, array $arr, string $default = ''): string {
+ if (!array_key_exists($k, $arr)) {
+ $subs = explode('.', $k, 2);
+ if (sizeof($subs) > 1) {
+ if (!array_key_exists($subs[0], $arr)) {
+ return $default;
+ }
+
+ $r = $arr[$subs[0]];
+ if (!is_array($r)) {
+ return $default;
+ }
+
+ return $this->get($subs[1], $r, $default);
+ } else {
+ return $default;
+ }
+ }
+
+ if ($arr[$k] === null || !is_string($arr[$k]) && (!is_int($arr[$k]))) {
+ return $default;
+ }
+
+ return (string)$arr[$k];
+ }
+
+
+ /**
+ * @param string $k
+ * @param array $arr
+ * @param int $default
+ *
+ * @return int
+ */
+ protected function getInt(string $k, array $arr, int $default = 0): int {
+ if (!array_key_exists($k, $arr)) {
+ $subs = explode('.', $k, 2);
+ if (sizeof($subs) > 1) {
+ if (!array_key_exists($subs[0], $arr)) {
+ return $default;
+ }
+
+ $r = $arr[$subs[0]];
+ if (!is_array($r)) {
+ return $default;
+ }
+
+ return $this->getInt($subs[1], $r, $default);
+ } else {
+ return $default;
+ }
+ }
+
+ if ($arr[$k] === null) {
+ return $default;
+ }
+
+ return intval($arr[$k]);
+ }
+
+
+ /**
+ * @param string $k
+ * @param array $arr
+ * @param float $default
+ *
+ * @return float
+ */
+ protected function getFloat(string $k, array $arr, float $default = 0): float {
+ if (!array_key_exists($k, $arr)) {
+ $subs = explode('.', $k, 2);
+ if (sizeof($subs) > 1) {
+ if (!array_key_exists($subs[0], $arr)) {
+ return $default;
+ }
+
+ $r = $arr[$subs[0]];
+ if (!is_array($r)) {
+ return $default;
+ }
+
+ return $this->getFloat($subs[1], $r, $default);
+ } else {
+ return $default;
+ }
+ }
+
+ if ($arr[$k] === null) {
+ return $default;
+ }
+
+ return intval($arr[$k]);
+ }
+
+
+ /**
+ * @param string $k
+ * @param array $arr
+ * @param bool $default
+ *
+ * @return bool
+ */
+ protected function getBool(string $k, array $arr, bool $default = false): bool {
+ if (!array_key_exists($k, $arr)) {
+ $subs = explode('.', $k, 2);
+ if (sizeof($subs) > 1) {
+ if (!array_key_exists($subs[0], $arr)) {
+ return $default;
+ }
+
+ return $this->getBool($subs[1], $arr[$subs[0]], $default);
+ } else {
+ return $default;
+ }
+ }
+
+ if ($arr[$k] === null) {
+ return $default;
+ }
+
+ if (is_bool($arr[$k])) {
+ return $arr[$k];
+ }
+
+ $sk = (string)$arr[$k];
+ if ($sk === '1' || strtolower($sk) === 'true') {
+ return true;
+ }
+
+ if ($sk === '0' || strtolower($sk) === 'false') {
+ return false;
+ }
+
+ return $default;
+ }
+
+
+ /**
+ * @param string $k
+ * @param array $arr
+ * @param JsonSerializable|null $default
+ *
+ * @return mixed
+ */
+ protected function getObj(string $k, array $arr, ?JsonSerializable $default = null): ?JsonSerializable {
+ if (!array_key_exists($k, $arr)) {
+ $subs = explode('.', $k, 2);
+ if (sizeof($subs) > 1) {
+ if (!array_key_exists($subs[0], $arr)) {
+ return $default;
+ }
+
+ return $this->getObj($subs[1], $arr[$subs[0]], $default);
+ } else {
+ return $default;
+ }
+ }
+
+ return $arr[$k];
+ }
+
+
+ /**
+ * @param string $k
+ * @param array $arr
+ * @param array $default
+ *
+ * @return array
+ */
+ protected function getArray(string $k, array $arr, array $default = []): array {
+ if (!array_key_exists($k, $arr)) {
+ $subs = explode('.', $k, 2);
+ if (sizeof($subs) > 1) {
+ if (!array_key_exists($subs[0], $arr)) {
+ return $default;
+ }
+
+ $r = $arr[$subs[0]];
+ if (!is_array($r)) {
+ return $default;
+ }
+
+ return $this->getArray($subs[1], $r, $default);
+ } else {
+ return $default;
+ }
+ }
+
+ $r = $arr[$k];
+ if (!is_array($r) && !is_string($r)) {
+ return $default;
+ }
+
+ if (is_string($r)) {
+ $r = json_decode($r, true);
+ }
+
+ if (!is_array($r)) {
+ return $default;
+ }
+
+ return $r;
+ }
+
+
+ /**
+ * @param string $k
+ * @param array $arr
+ *
+ * @return bool
+ */
+ public function validKey(string $k, array $arr): bool {
+ if (array_key_exists($k, $arr)) {
+ return true;
+ }
+
+ $subs = explode('.', $k, 2);
+ if (sizeof($subs) > 1) {
+ if (!array_key_exists($subs[0], $arr)) {
+ return false;
+ }
+
+ $r = $arr[$subs[0]];
+ if (!is_array($r)) {
+ return false;
+ }
+
+ return $this->validKey($subs[1], $r);
+ }
+
+ return false;
+ }
+
+
+ /**
+ * @param string $k
+ * @param array $arr
+ * @param array $import
+ * @param array $default
+ *
+ * @return array
+ */
+ protected function getList(string $k, array $arr, array $import, array $default = []): array {
+ $list = $this->getArray($k, $arr, $default);
+
+ $r = [];
+ [$obj, $method] = $import;
+ foreach ($list as $item) {
+ try {
+ $o = new $obj();
+ $o->$method($item);
+
+ $r[] = $o;
+ } catch (Exception $e) {
+ }
+ }
+
+ return $r;
+ }
+
+
+ /**
+ * @param string $k
+ * @param string $value
+ * @param array $list
+ *
+ * @return mixed
+ * @throws ArrayNotFoundException
+ */
+ protected function extractArray(string $k, string $value, array $list) {
+ foreach ($list as $arr) {
+ if (!array_key_exists($k, $arr)) {
+ continue;
+ }
+
+ if ($arr[$k] === $value) {
+ return $arr;
+ }
+ }
+
+ throw new ArrayNotFoundException();
+ }
+
+
+ /**
+ * @param string $key
+ * @param array $arr
+ * @param bool $root
+ *
+ * @return string
+ * @throws ItemNotFoundException
+ * @throws UnknownTypeException
+ */
+ public function typeOf(string $key, array $arr, bool $root = true): string {
+ if (array_key_exists($key, $arr)) {
+ $item = $arr[$key];
+
+ if (is_null($item)) {
+ return self::$TYPE_NULL;
+ }
+
+ if (is_string($item)) {
+ return self::$TYPE_STRING;
+ }
+
+ if (is_array($item)) {
+ return self::$TYPE_ARRAY;
+ }
+
+ if (is_bool($item)) {
+ return self::$TYPE_BOOLEAN;
+ }
+
+ if (is_int($item)) {
+ return self::$TYPE_INTEGER;
+ }
+
+ if ($item instanceof JsonSerializable) {
+ return self::$TYPE_SERIALIZABLE;
+ }
+
+ throw new UnknownTypeException();
+ }
+
+ $subs = explode('.', $key, 2);
+ if (sizeof($subs) > 1) {
+ if (!array_key_exists($subs[0], $arr)) {
+ throw new ItemNotFoundException();
+ }
+
+ $r = $arr[$subs[0]];
+ if (is_array($r)) {
+ return $this->typeOf($subs[1], $r);
+ }
+ }
+
+ throw new ItemNotFoundException();
+ }
+
+
+ /**
+ * @param array $keys
+ * @param array $arr
+ *
+ * @throws MalformedArrayException
+ */
+ protected function mustContains(array $keys, array $arr) {
+ foreach ($keys as $key) {
+ if (!array_key_exists($key, $arr)) {
+ throw new MalformedArrayException(
+ 'source: ' . json_encode($arr) . ' - missing key: ' . $key
+ );
+ }
+ }
+ }
+
+
+ /**
+ * @param array $arr
+ */
+ protected function cleanArray(array &$arr) {
+ $arr = array_filter(
+ $arr,
+ function ($v) {
+ if (is_string($v)) {
+ return ($v !== '');
+ }
+ if (is_array($v)) {
+ return !empty($v);
+ }
+
+ return true;
+ }
+ );
+ }
+}
diff --git a/lib/Tools/Traits/TDeserialize.php b/lib/Tools/Traits/TDeserialize.php
new file mode 100644
index 0000000..0836477
--- /dev/null
+++ b/lib/Tools/Traits/TDeserialize.php
@@ -0,0 +1,114 @@
+<?php
+
+declare(strict_types=1);
+
+
+/**
+ * FullTextSearch - Full text search framework for Nextcloud
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Maxence Lange <maxence@artificial-owl.com>
+ * @copyright 2022
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\FullTextSearch\Tools\Traits;
+
+use JsonSerializable;
+use OCA\FullTextSearch\Tools\Exceptions\InvalidItemException;
+use OCA\FullTextSearch\Tools\IDeserializable;
+
+trait TDeserialize {
+
+
+ /**
+ * @param JsonSerializable $model
+ *
+ * @return array
+ */
+ public function serialize(JsonSerializable $model): array {
+ return json_decode(json_encode($model), true);
+ }
+
+ /**
+ * @param array $data
+ *
+ * @return array
+ */
+ public function serializeArray(array $data): array {
+ return json_decode(json_encode($data), true);
+ }
+
+
+ /**
+ * @param array $data
+ * @param string $class
+ *
+ * @return IDeserializable
+ * @throws InvalidItemException
+ */
+ public function deserialize(array $data, string $class): IDeserializable {
+ if ($class instanceof IDeserializable) {
+ throw new InvalidItemException(get_class($class) . ' does not implement IDeserializable');
+ }
+
+ /** @var IDeserializable $item */
+ $item = new $class;
+ $item->import($data);
+
+ return $item;
+ }
+
+
+ /**
+ * @param string $json
+ * @param string $class
+ *
+ * @return IDeserializable[]
+ * @throws InvalidItemException
+ */
+ public function deserializeArray(string $json, string $class): array {
+ $arr = [];
+ $data = json_decode($json, true);
+ if (!is_array($data)) {
+ return $arr;
+ }
+
+ foreach ($data as $entry) {
+ $arr[] = $this->deserialize($entry, $class);
+ }
+
+ return $arr;
+ }
+
+
+ /**
+ * @param string $json
+ * @param string $class
+ *
+ * @return IDeserializable
+ * @throws InvalidItemException
+ */
+ public function deserializeJson(string $json, string $class): IDeserializable {
+ $data = json_decode($json, true);
+
+ return $this->deserialize($data, $class);
+ }
+}
diff --git a/lib/Tools/Traits/TNCLogger.php b/lib/Tools/Traits/TNCLogger.php
new file mode 100644
index 0000000..4479e3f
--- /dev/null
+++ b/lib/Tools/Traits/TNCLogger.php
@@ -0,0 +1,201 @@
+<?php
+
+declare(strict_types=1);
+
+
+/**
+ * FullTextSearch - Full text search framework for Nextcloud
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Maxence Lange <maxence@artificial-owl.com>
+ * @copyright 2022
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\FullTextSearch\Tools\Traits;
+
+use Exception;
+use OC;
+use OC\HintException;
+use Psr\Log\LoggerInterface;
+use Throwable;
+
+trait TNCLogger {
+ use TNCSetup;
+
+
+ public static $EMERGENCY = 4;
+ public static $ALERT = 3;
+ public static $CRITICAL = 3;
+ public static $ERROR = 3;
+ public static $WARNING = 2;
+ public static $NOTICE = 1;
+ public static $INFO = 1;
+ public static $DEBUG = 0;
+
+
+ /**
+ * @param Throwable $t
+ * @param array $serializable
+ */
+ public function t(Throwable $t, array $serializable = []): void {
+ $this->throwable($t, self::$ERROR, $serializable);
+ }
+
+ /**
+ * @param Throwable $t
+ * @param int $level
+ * @param array $serializable
+ */
+ public function throwable(Throwable $t, int $level = 3, array $serializable = []): void {
+ $message = '';
+ if (!empty($serializable)) {
+ $message = json_encode($serializable);
+ }
+
+ $this->logger()
+ ->log(
+ $level,
+ $message,
+ [
+ 'app' => $this->setup('app'),
+ 'exception' => $t
+ ]
+ );
+ }
+
+
+ /**
+ * @param Exception $e
+ * @param array $serializable
+ */
+ public function e(Exception $e, array $serializable = []): void {
+ $this->exception($e, self::$ERROR, $serializable);
+ }
+
+ /**
+ * @param Exception $e
+ * @param int|array $level
+ * @param array $serializable
+ */
+ public function exception(Exception $e, $level = 3, array $serializable = []): void {
+ if (is_array($level) && empty($serializable)) {
+ $serializable = $level;
+ $level = 3;
+ }
+
+ $message = '';
+ if (!empty($serializable)) {
+ $message = json_encode($serializable);
+ }
+
+ if ($level === self::$DEBUG) {
+ $level = (int)$this->appConfig('debug_level');
+ }
+
+ $this->logger()
+ ->log(
+ $level,
+ $message,
+ [
+ 'app' => $this->setup('app'),
+ 'exception' => $e
+ ]
+ );
+ }
+
+
+ /**
+ * @param string $message
+ * @param bool $trace
+ * @param array $serializable
+ */
+ public function emergency(string $message, bool $trace = false, array $serializable = []): void {
+ $this->log(self::$EMERGENCY, '[emergency] ' . $message, $trace, $serializable);
+ }
+
+ /**
+ * @param string $message
+ * @param bool $trace
+ * @param array $serializable
+ */
+ public function alert(string $message, bool $trace = false, array $serializable = []): void {
+ $this->log(self::$ALERT, '[alert] ' . $message, $trace, $serializable);
+ }
+
+ /**
+ * @param string $message
+ * @param bool $trace
+ * @param array $serializable
+ */
+ public function warning(string $message, bool $trace = false, array $serializable = []): void {
+ $this->log(self::$WARNING, '[warning] ' . $message, $trace, $serializable);
+ }
+
+ /**
+ * @param string $message
+ * @param bool $trace
+ * @param array $serializable
+ */
+ public function notice(string $message, bool $trace = false, array $serializable = []): void {
+ $this->log(self::$NOTICE, '[notice] ' . $message, $trace, $serializable);
+ }
+
+ /**
+ * @param string $message
+ * @param array $serializable
+ */
+ public function debug(string $message, array $serializable = []): void {
+ $message = '[debug] ' . $message;
+ $debugLevel = (int)$this->appConfig('debug_level');
+ $this->log($debugLevel, $message, ($this->appConfig('debug_trace') === '1'), $serializable);
+ }
+
+
+ /**
+ * @param int $level
+ * @param string $message
+ * @param bool $trace
+ * @param array $serializable
+ */
+ public function log(int $level, string $message, bool $trace = false, array $serializable = []): void {
+ $opts = ['app' => $this->setup('app')];
+ if ($trace) {
+ $opts['exception'] = new HintException($message, json_encode($serializable));
+ } elseif (!empty($serializable)) {
+ $message .= ' -- ' . json_encode($serializable);
+ }
+
+ $this->logger()
+ ->log($level, $message, $opts);
+ }
+
+
+ /**
+ * @return LoggerInterface
+ */
+ public function logger(): LoggerInterface {
+ if (isset($this->logger) && $this->logger instanceof LoggerInterface) {
+ return $this->logger;
+ } else {
+ return OC::$server->get(LoggerInterface::class);
+ }
+ }
+}
diff --git a/lib/Tools/Traits/TNCSetup.php b/lib/Tools/Traits/TNCSetup.php
new file mode 100644
index 0000000..e8f4bb7
--- /dev/null
+++ b/lib/Tools/Traits/TNCSetup.php
@@ -0,0 +1,107 @@
+<?php
+
+declare(strict_types=1);
+
+
+/**
+ * FullTextSearch - Full text search framework for Nextcloud
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Maxence Lange <maxence@artificial-owl.com>
+ * @copyright 2022
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\FullTextSearch\Tools\Traits;
+
+use OC;
+use OCP\IConfig;
+
+trait TNCSetup {
+ use TArrayTools;
+
+
+ /** @var array */
+ private $_setup = [];
+
+
+ /**
+ * @param string $key
+ * @param string $value
+ *
+ * @param string $default
+ *
+ * @return string
+ */
+ public function setup(string $key, string $value = '', string $default = ''): string {
+ if ($value !== '') {
+ $this->_setup[$key] = $value;
+ }
+
+ return $this->get($key, $this->_setup, $default);
+ }
+
+ /**
+ * @param string $key
+ * @param array $value
+ * @param array $default
+ *
+ * @return array
+ */
+ public function setupArray(string $key, array $value = [], array $default = []): array {
+ if (!empty($value)) {
+ $this->_setup[$key] = $value;
+ }
+
+ return $this->getArray($key, $this->_setup, $default);
+ }
+
+ /**
+ * @param string $key
+ * @param int $value
+ * @param int $default
+ *
+ * @return int
+ */
+ public function setupInt(string $key, int $value = -999, int $default = 0): int {
+ if ($value !== -999) {
+ $this->_setup[$key] = $value;
+ }
+
+ return $this->getInt($key, $this->_setup, $default);
+ }
+
+ /**
+ * @param string $key
+ *
+ * @return string
+ */
+ public function appConfig(string $key): string {
+ $app = $this->setup('app');
+ if ($app === '') {
+ return '';
+ }
+
+ /** @var IConfig $config */
+ $config = OC::$server->get(IConfig::class);
+
+ return $config->getAppValue($app, $key, '');
+ }
+}
diff --git a/lib/Tools/Traits/TStringTools.php b/lib/Tools/Traits/TStringTools.php
new file mode 100644
index 0000000..2a35a1d
--- /dev/null
+++ b/lib/Tools/Traits/TStringTools.php
@@ -0,0 +1,258 @@
+<?php
+
+declare(strict_types=1);
+
+
+/**
+ * FullTextSearch - Full text search framework for Nextcloud
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Maxence Lange <maxence@artificial-owl.com>
+ * @copyright 2022
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\FullTextSearch\Tools\Traits;
+
+use DateTime;
+use Exception;
+
+trait TStringTools {
+ use TArrayTools;
+
+
+ /**
+ * @param int $length
+ *
+ * @return string
+ */
+ protected function token(int $length = 15): string {
+ $chars = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890';
+
+ $str = '';
+ $max = strlen($chars);
+ for ($i = 0; $i < $length; $i++) {
+ try {
+ $str .= $chars[random_int(0, $max - 2)];
+ } catch (Exception $e) {
+ }
+ }
+
+ return $str;
+ }
+
+
+ /**
+ * Generate uuid: 2b5a7a87-8db1-445f-a17b-405790f91c80
+ *
+ * @param int $length
+ *
+ * @return string
+ */
+ protected function uuid(int $length = 0): string {
+ $uuid = sprintf(
+ '%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff),
+ mt_rand(0, 0xffff), mt_rand(0, 0xfff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000,
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
+ );
+
+ if ($length > 0) {
+ if ($length <= 16) {
+ $uuid = str_replace('-', '', $uuid);
+ }
+
+ $uuid = substr($uuid, 0, $length);
+ }
+
+ return $uuid;
+ }
+
+
+ /**
+ * @param string $line
+ * @param int $length
+ *
+ * @return string
+ */
+ protected function cut(string $line, int $length): string {
+ if (strlen($line) < $length) {
+ return $line;
+ }
+
+ return substr($line, 0, $length - 5) . ' (..)';
+ }
+
+ /**
+ * @param string $str1
+ * @param string $str2
+ * @param bool $cs case sensitive ?
+ *
+ * @return string
+ */
+ protected function commonPart(string $str1, string $str2, bool $cs = true): string {
+ for ($i = 0; $i < strlen($str1) && $i < strlen($str2); $i++) {
+ $chr1 = $str1[$i];
+ $chr2 = $str2[$i];
+
+ if (!$cs) {
+ $chr1 = strtolower($chr1);
+ $chr2 = strtolower($chr2);
+ }
+
+ if ($chr1 !== $chr2) {
+ break;
+ }
+ }
+
+ return substr($str1, 0, $i);
+ }
+
+
+ /**
+ * @param string $line
+ * @param array $params
+ *
+ * @return string
+ */
+ protected function feedStringWithParams(string $line, array $params): string {
+ $ak = array_keys($params);
+ foreach ($ak as $k) {
+ $line = str_replace('{' . $k . '}', (string)$params[$k], $line);
+ }
+
+ return $line;
+ }
+
+
+ /**
+ * @param int $words
+ *
+ * @return string
+ */
+ public function generateRandomSentence(int $words = 5): string {
+ $sentence = [];
+ for ($i = 0; $i < $words; $i++) {
+ $sentence[] = $this->generateRandomWord(rand(2, 12));
+ }
+
+ return implode(' ', $sentence);
+ }
+
+
+ /**
+ * @param int $length
+ *
+ * @return string
+ */
+ public function generateRandomWord(int $length = 8): string {
+ $c = ['b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'r', 's', 't', 'v'];
+ $v = ['a', 'e', 'i', 'o', 'u', 'y'];
+
+ $word = [];
+ for ($i = 0; $i <= ($length / 2); $i++) {
+ $word[] = $c[array_rand($c)];
+ $word[] = $v[array_rand($v)];
+ }
+
+ return implode('', $word);
+ }
+
+
+ /**
+ * @param int $bytes
+ *
+ * @return string
+ */
+ public function humanReadable(int $bytes): string {
+ if ($bytes == 0) {
+ return '0.00 B';
+ }
+
+ $s = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
+ $e = floor(log($bytes, 1024));
+
+ return round($bytes / pow(1024, $e), 2) . ' ' . $s[$e];
+ }
+
+
+ /**
+ * @param int $first
+ * @param int $second
+ * @param bool $short
+ *
+ * @return string
+ * @throws Exception
+ */
+ public function getDateDiff(
+ int $first,
+ int $second = 0,
+ bool $short = false,
+ array $words = []
+ ): string {
+ if ($second === 0) {
+ $first = time() - $first;
+ $second = time();
+ }
+
+ $f = new DateTime('@' . $first);
+ $s = new DateTime('@' . $second);
+ $duration = $second - $first;
+ if ($short) {
+ $minutes = $this->get('minutes', $words, 'M');
+ $hours = $this->get('hours', $words, 'H');
+ $days = $this->get('days', $words, 'D');
+
+ if ($duration < 60) {
+ return $f->diff($s)->format('<1' . $minutes);
+ }
+ if ($duration < 3600) {
+ return $f->diff($s)->format('%i' . $minutes);
+ }
+ if ($duration < 86400) {
+ return $f->diff($s)->format('%h' . $hours . ', %i' . $minutes);
+ }
+
+ return $f->diff($s)->format('%a' . $days . ', %h' . $hours . ', %i' . $minutes);
+ }
+
+ $seconds = $this->get('seconds', $words, 'seconds');
+ $minutes = $this->get('minutes', $words, 'minutes');
+ $hours = $this->get('hours', $words, 'hours');
+ $days = $this->get('days', $words, 'days');
+ if ($duration < 60) {
+ return $f->diff($s)->format('%s ' . $seconds);
+ }
+
+ if ($duration < 3600) {
+ return $f->diff($s)->format('%i ' . $minutes . ' and %s ' . $seconds);
+ }
+
+ if ($duration < 86400) {
+ return $f->diff($s)->format('%h ' . $hours . ', %i ' . $minutes . ' and %s ' . $seconds);
+ }
+
+ return $f->diff($s)->format(
+ '%a ' . $days .
+ ', %h ' . $hours .
+ ', %i ' . $minutes .
+ ' and %s ' . $seconds
+ );
+ }
+}