diff --git a/lib/private/DB/QueryBuilder/Events/AfterQueryExecuted.php b/lib/private/DB/QueryBuilder/Events/AfterQueryExecuted.php new file mode 100644 index 0000000000000..32200dcd59ce6 --- /dev/null +++ b/lib/private/DB/QueryBuilder/Events/AfterQueryExecuted.php @@ -0,0 +1,65 @@ +. + * + */ + +namespace OC\DB\QueryBuilder\Events; + +use OCP\EventDispatcher\Event; +use OCP\DB\QueryBuilder\IQueryBuilder; + +/** + * This event is used by apps to intercept, inspect, and potentially modify + * the results of database queries after execution. This can be used for deep + * integrations, restricting user access to certain data, redacting information, + * etc. + * + * @see https://docs.nextcloud.com/server/latest/developer_manual/digging_deeper/projects.html + * @since 26.0.0 + */ +class AfterQueryExecuted extends Event { + private IQueryBuilder $queryBuilder; + private $result; + + public function __construct(IQueryBuilder $queryBuilder, $result) { + $this->queryBuilder = $queryBuilder; + $this->result = $result; + } + + public function getQueryBuilder() { + return $this->queryBuilder; + } + + public function setQueryBuilder() { + return $this->queryBuilder; + } + + public function getResult() { + return $this->result; + } + + public function setResult() { + return $this->result; + } +} diff --git a/lib/private/DB/QueryBuilder/Events/BeforeQueryExecuted.php b/lib/private/DB/QueryBuilder/Events/BeforeQueryExecuted.php new file mode 100644 index 0000000000000..57eff74bdd332 --- /dev/null +++ b/lib/private/DB/QueryBuilder/Events/BeforeQueryExecuted.php @@ -0,0 +1,69 @@ +. + * + */ + +namespace OC\DB\QueryBuilder\Events; + +use OCP\EventDispatcher\Event; +use OCP\DB\QueryBuilder\IQueryBuilder; + +/** + * This event is used by apps to intercept, inspect, and potentially modify + * database queries prior to execution. This can be used for deep integrations, + * restricting user access to certain data, redacting information, etc. + * + * The result field can optionally be set by the event listener by executing the + * query in the event handler logic. This allows apps to inspect the results, + * use try/catch blocks around the query execution, and/or modify/re-execute the + * query if necessary. If the result field is not set, the QueryBuilder class + * will execute the query as expected by default. + * + * @see https://docs.nextcloud.com/server/latest/developer_manual/digging_deeper/projects.html + * @since 26.0.0 + */ +class BeforeQueryExecuted extends Event { + private IQueryBuilder $queryBuilder; + private $result = null; + + public function __construct(IQueryBuilder $queryBuilder) { + $this->queryBuilder = $queryBuilder; + } + + public function getQueryBuilder() { + return $this->queryBuilder; + } + + public function setQueryBuilder() { + return $this->queryBuilder; + } + + public function getResult() { + return $this->result; + } + + public function setResult() { + return $this->result; + } +} diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index 43ed68f5616ac..7d4197a777bb0 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -36,6 +36,8 @@ use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Query\QueryException; use OC\DB\ConnectionAdapter; +use OC\DB\QueryBuilder\Events\BeforeQueryExecuted; +use OC\DB\QueryBuilder\Events\AfterQueryExecuted; use OC\DB\QueryBuilder\ExpressionBuilder\ExpressionBuilder; use OC\DB\QueryBuilder\ExpressionBuilder\MySqlExpressionBuilder; use OC\DB\QueryBuilder\ExpressionBuilder\OCIExpressionBuilder; @@ -53,6 +55,7 @@ use OCP\DB\QueryBuilder\IParameter; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryFunction; +use OCP\EventDispatcher\IEventDispatcher; use Psr\Log\LoggerInterface; class QueryBuilder implements IQueryBuilder { @@ -76,6 +79,9 @@ class QueryBuilder implements IQueryBuilder { /** @var string */ protected $lastInsertedTable; + /** @var IEventDispatcher */ + private $dispatcher; + /** * Initializes a new QueryBuilder. * @@ -88,6 +94,7 @@ public function __construct(ConnectionAdapter $connection, SystemConfig $systemC $this->logger = $logger; $this->queryBuilder = new \Doctrine\DBAL\Query\QueryBuilder($this->connection->getInner()); $this->helper = new QuoteHelper(); + $this->dispatcher = \OC::$server->get(IEventDispatcher::class); } /** @@ -277,7 +284,18 @@ public function execute() { ]); } - $result = $this->queryBuilder->execute(); + $event = new BeforeQueryExecuted($this); + $this->dispatcher->dispatchTyped($event); + $result = $event->getResult(); + + if ($result === null) { + $result = $this->queryBuilder->execute(); + } + + $event = new AfterQueryExecuted($this, $result); + $this->dispatcher->dispatchTyped($event); + $result = $event->getResult(); + if (is_int($result)) { return $result; }