fix: propfind query monitor breaking removeListener and removeAllListeners

Signed-off-by: Salvatore Martire <4652631+salmart-dev@users.noreply.github.com>
pull/54507/head
Salvatore Martire 2 months ago
parent d5417d63e0
commit 5d028cfaf8
  1. 56
      apps/dav/lib/Connector/Sabre/Server.php
  2. 68
      apps/dav/tests/unit/Connector/ServerTest.php

@ -34,6 +34,16 @@ class Server extends \Sabre\DAV\Server {
public bool $debugEnabled = false;
/**
* @var array<string, array<int, callable>>
*/
private array $originalListeners = [];
/**
* @var array<string, array<int, callable>>
*/
private array $wrappedListeners = [];
/**
* @see \Sabre\DAV\Server
*/
@ -86,15 +96,49 @@ class Server extends \Sabre\DAV\Server {
$parentFn($eventName, $callBack, $priority);
return;
}
// The \Sabre\DAVACL\Plugin needs to excluded as it relies on removeListener()
if ($pluginName === \Sabre\DAVACL\Plugin::class) {
$parentFn($eventName, $callBack, $priority);
return;
$wrappedCallback
= $this->getMonitoredCallback($callBack, $pluginName, $eventName);
$this->originalListeners[$eventName][] = $callBack;
$this->wrappedListeners[$eventName][] = $wrappedCallback;
$parentFn($eventName, $wrappedCallback, $priority);
}
public function removeListener(
string $eventName,
callable $listener,
): bool {
$listenerIndex = null;
if (isset($this->wrappedListeners[$eventName], $this->originalListeners[$eventName])) {
$key = array_search(
$listener,
$this->originalListeners[$eventName],
true
);
if ($key !== false) {
$listenerIndex = $key;
$listener = $this->wrappedListeners[$eventName][$listenerIndex];
}
}
$removed = parent::removeListener($eventName, $listener);
$callback = $this->getMonitoredCallback($callBack, $pluginName, $eventName);
if ($removed && $listenerIndex !== null) {
unset($this->originalListeners[$eventName][$listenerIndex], $this->wrappedListeners[$eventName][$listenerIndex]);
}
return $removed;
}
$parentFn($eventName, $callback, $priority);
public function removeAllListeners(?string $eventName = null): void {
parent::removeAllListeners($eventName);
if ($eventName === null) {
$this->originalListeners = [];
$this->wrappedListeners = [];
} else {
unset($this->wrappedListeners[$eventName], $this->originalListeners[$eventName]);
}
}
/**

@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\Tests\unit\Connector\Sabre;
use OCA\DAV\Connector\Sabre\Server;
use PHPUnit\Framework\Attributes\DataProvider;
use Sabre\DAV\INode;
use Sabre\DAV\PropFind;
use Test\TestCase;
class ServerTest extends TestCase {
private Server $server;
protected function setUp(): void {
parent::setUp();
$this->server = new Server();
$this->server->debugEnabled = true;
}
public function testRemoveListener(): void {
$listener = static function () {
return false;
};
$this->server->on('propFind', $listener);
$this->server->removeListener('propFind', $listener);
$propFind = $this->createMock(PropFind::class);
$iNode = $this->createMock(INode::class);
$return = $this->server->emit('propFind', [$propFind, $iNode]);
$this->assertTrue($return);
}
public static function removeAllListenersData(): array {
return [
'all listeners' => [null], 'propFind listeners' => ['propFind'],
];
}
#[DataProvider('removeAllListenersData')]
public function testRemoveAllListeners(?string $removeEventName): void {
$listener = static function () {
return false;
};
$this->server->on('propFind', $listener);
$this->server->on('otherEvent', $listener);
$this->server->removeAllListeners($removeEventName);
$propFind = $this->createMock(PropFind::class);
$iNode = $this->createMock(INode::class);
$propFindReturn = $this->server->emit('propFind', [$propFind, $iNode]);
$this->assertTrue($propFindReturn);
$otherEventReturn = $this->server->emit('otherEvent', [$propFind,
$iNode]);
// if listeners are not removed when they should, emit will return false
$this->assertEquals($removeEventName === null, $otherEventReturn);
}
}
Loading…
Cancel
Save