Quality of life things.
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Build and test / requirements (push) Failing after 3m48s
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Build and test / requirements (push) Failing after 3m48s
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,3 +7,4 @@ config.yml
|
|||||||
*.phar
|
*.phar
|
||||||
*.deb
|
*.deb
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
|
*cache/
|
51
.phpcs.xml
Normal file
51
.phpcs.xml
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="PHP_CodeSniffer" xsi:noNamespaceSchemaLocation="phpcs.xsd">
|
||||||
|
<description>Coding standard</description>
|
||||||
|
|
||||||
|
<file>src/</file>
|
||||||
|
<file>tests/</file>
|
||||||
|
|
||||||
|
<rule ref="PSR1">
|
||||||
|
<exclude name="Generic.Files.LineLength"/>
|
||||||
|
</rule>
|
||||||
|
<rule ref="PSR2"></rule>
|
||||||
|
<rule ref="PSR12"></rule>
|
||||||
|
|
||||||
|
<rule ref="Generic">
|
||||||
|
<exclude name="Generic.WhiteSpace.DisallowSpaceIndent.SpacesUsed"/>
|
||||||
|
<exclude name="Generic.Files.LowercasedFilename.NotFound"/>
|
||||||
|
<exclude name="Generic.PHP.ClosingPHPTag.NotFound"/>
|
||||||
|
<exclude name="Generic.Files.EndFileNoNewline.Found"/>
|
||||||
|
<exclude name="Generic.Files.EndFileNoNewline.Found"/>
|
||||||
|
<exclude name="Generic.Arrays.DisallowShortArraySyntax.Found"/>
|
||||||
|
<exclude name="Generic.Functions.OpeningFunctionBraceKernighanRitchie.BraceOnNewLine"/>
|
||||||
|
<exclude name="Generic.Classes.OpeningBraceSameLine.BraceOnNewLine"/>
|
||||||
|
<exclude name="Generic.PHP.LowerCaseConstant.Found"/>
|
||||||
|
<exclude name="Generic.Formatting.SpaceAfterCast"/>
|
||||||
|
<exclude name="Generic.Formatting.MultipleStatementAlignment.NotSameWarning"/>
|
||||||
|
<exclude name="Generic.Commenting.DocComment.MissingShort"/>
|
||||||
|
<exclude name="Generic.NamingConventions.AbstractClassNamePrefix.Missing"/>
|
||||||
|
<exclude name="Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed"/>
|
||||||
|
<exclude name="Generic.NamingConventions.InterfaceNameSuffix.Missing"/>
|
||||||
|
<exclude name="Generic.Commenting.Todo.TaskFound"/>
|
||||||
|
<exclude name="Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUse"/>
|
||||||
|
<exclude name="Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed"/>
|
||||||
|
<exclude name="Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed"/>
|
||||||
|
<exclude name="Generic.Formatting.SpaceBeforeCast.NoSpace"/>
|
||||||
|
<exclude name="Generic.CodeAnalysis.UselessOverridingMethod.Found"/>
|
||||||
|
<exclude name="Squiz.Functions.MultiLineFunctionDeclaration.NewlineBeforeOpenBrace"/>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<!-- Ban some functions -->
|
||||||
|
<rule ref="Generic.PHP.ForbiddenFunctions">
|
||||||
|
<properties>
|
||||||
|
<property name="forbiddenFunctions" type="array">
|
||||||
|
<element key="sizeof" value="count"/>
|
||||||
|
<element key="delete" value="unset"/>
|
||||||
|
<element key="print" value="echo"/>
|
||||||
|
<element key="is_null" value="null"/>
|
||||||
|
<element key="create_function" value="null"/>
|
||||||
|
</property>
|
||||||
|
</properties>
|
||||||
|
</rule>
|
||||||
|
</ruleset>
|
@ -23,10 +23,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"squizlabs/php_codesniffer": "*",
|
|
||||||
"phpstan/phpstan": "^1.10.37",
|
|
||||||
"vimeo/psalm": "^5.15",
|
|
||||||
"phpmd/phpmd": "^2.14.1",
|
|
||||||
"phpunit/phpunit": "^10.3.5",
|
"phpunit/phpunit": "^10.3.5",
|
||||||
"phpmetrics/phpmetrics": "^2.8.2"
|
"phpmetrics/phpmetrics": "^2.8.2"
|
||||||
},
|
},
|
||||||
@ -34,7 +30,7 @@
|
|||||||
"test": "vendor/bin/phpunit tests --display-warnings",
|
"test": "vendor/bin/phpunit tests --display-warnings",
|
||||||
"test-full": "vendor/bin/phpunit -c phpunit.full.xml",
|
"test-full": "vendor/bin/phpunit -c phpunit.full.xml",
|
||||||
"metrics": "vendor/bin/phpmetrics --report-html=output/metrics --junit=output/test.xml src/",
|
"metrics": "vendor/bin/phpmetrics --report-html=output/metrics --junit=output/test.xml src/",
|
||||||
"docs": "./phpDocumentor.phar --setting=graphs.enabled=true",
|
"docs": "phpDocumentor --setting=graphs.enabled=true",
|
||||||
"analyze": [
|
"analyze": [
|
||||||
"@analyze-yaml",
|
"@analyze-yaml",
|
||||||
"@analyze-phpmd",
|
"@analyze-phpmd",
|
||||||
@ -43,9 +39,9 @@
|
|||||||
"@analyze-phpcs"
|
"@analyze-phpcs"
|
||||||
],
|
],
|
||||||
"analyze-yaml": "vendor/bin/yaml-lint *.yml .*.yml *.json",
|
"analyze-yaml": "vendor/bin/yaml-lint *.yml .*.yml *.json",
|
||||||
"analyze-phpmd": "vendor/bin/phpmd src,tests text cleancode,codesize,controversial,design,naming,unusedcode",
|
"analyze-phpmd": "phpmd src,tests text cleancode,codesize,controversial,design,naming,unusedcode",
|
||||||
"analyze-phpstan":"vendor/bin/phpstan analyze --level=8 --error-format=raw src/ backup tests",
|
"analyze-phpstan":"phpstan",
|
||||||
"analyze-psalm": "vendor/bin/psalm --no-cache",
|
"analyze-psalm": "psalm --no-cache",
|
||||||
"analyze-phpcs": "vendor/bin/phpcs src backup tests --report=emacs --standard=PSR12"
|
"analyze-phpcs": "phpcs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1871
composer.lock
generated
1871
composer.lock
generated
File diff suppressed because it is too large
Load Diff
5
phpstan.neon
Normal file
5
phpstan.neon
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
parameters:
|
||||||
|
level: 8
|
||||||
|
paths:
|
||||||
|
- src
|
||||||
|
- tests
|
@ -1,36 +0,0 @@
|
|||||||
<phpunit
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/6.3/phpunit.xsd"
|
|
||||||
executionOrder="random"
|
|
||||||
testdox="true"
|
|
||||||
displayDetailsOnIncompleteTests="true"
|
|
||||||
displayDetailsOnSkippedTests="true"
|
|
||||||
displayDetailsOnTestsThatTriggerDeprecations="true"
|
|
||||||
displayDetailsOnTestsThatTriggerErrors="true"
|
|
||||||
displayDetailsOnTestsThatTriggerNotices="true"
|
|
||||||
displayDetailsOnTestsThatTriggerWarnings="true"
|
|
||||||
>
|
|
||||||
<testsuites>
|
|
||||||
<testsuite name="All">
|
|
||||||
<directory>tests</directory>
|
|
||||||
</testsuite>
|
|
||||||
</testsuites>
|
|
||||||
<source>
|
|
||||||
<include>
|
|
||||||
<directory suffix=".php">src</directory>
|
|
||||||
<file>backup</file>
|
|
||||||
</include>
|
|
||||||
</source>
|
|
||||||
<logging>
|
|
||||||
<junit outputFile="output/test.xml"/>
|
|
||||||
<testdoxHtml outputFile="output/test.html"/>
|
|
||||||
<testdoxText outputFile="output/test.txt"/>
|
|
||||||
</logging>
|
|
||||||
<coverage includeUncoveredFiles="true"
|
|
||||||
pathCoverage="true">
|
|
||||||
<report>
|
|
||||||
<html outputDirectory="output/coverage"/>
|
|
||||||
<text outputFile="output/coverage.txt" showUncoveredFiles="true" showOnlySummary="true"/>
|
|
||||||
</report>
|
|
||||||
</coverage>
|
|
||||||
</phpunit>
|
|
25
phpunit.xml
25
phpunit.xml
@ -1,11 +1,24 @@
|
|||||||
<phpunit
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" bootstrap="vendor/autoload.php" colors="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.2/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false" displayDetailsOnTestsThatTriggerWarnings="true">
|
||||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/6.3/phpunit.xsd"
|
|
||||||
>
|
|
||||||
<testsuites>
|
<testsuites>
|
||||||
<testsuite name="All">
|
<testsuite name="Backupscript Test Suite">
|
||||||
<directory>tests</directory>
|
<directory>./tests/</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
|
<source>
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">src/</directory>
|
||||||
|
</include>
|
||||||
|
</source>
|
||||||
|
<logging>
|
||||||
|
<junit outputFile="output/test/junit.xml"/>
|
||||||
|
<testdoxHtml outputFile="output/test/index.html"/>
|
||||||
|
</logging>
|
||||||
|
|
||||||
|
<coverage>
|
||||||
|
<report>
|
||||||
|
<html outputDirectory="output/coverage" />
|
||||||
|
<clover outputFile ="output/coverage/clover.xml" />
|
||||||
|
</report>
|
||||||
|
</coverage>
|
||||||
</phpunit>
|
</phpunit>
|
@ -6,7 +6,7 @@
|
|||||||
xmlns="https://getpsalm.org/schema/config"
|
xmlns="https://getpsalm.org/schema/config"
|
||||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||||
findUnusedBaselineEntry="true"
|
findUnusedBaselineEntry="true"
|
||||||
findUnusedCode="true"
|
findUnusedCode="false"
|
||||||
strictBinaryOperands="true"
|
strictBinaryOperands="true"
|
||||||
checkForThrowsInGlobalScope="true"
|
checkForThrowsInGlobalScope="true"
|
||||||
ignoreInternalFunctionFalseReturn="false"
|
ignoreInternalFunctionFalseReturn="false"
|
||||||
@ -18,5 +18,9 @@
|
|||||||
<projectFiles>
|
<projectFiles>
|
||||||
<directory name="src/" />
|
<directory name="src/" />
|
||||||
<file name="backup" />
|
<file name="backup" />
|
||||||
|
<directory name="tests" />
|
||||||
|
<ignoreFiles>
|
||||||
|
<directory name="vendor" />
|
||||||
|
</ignoreFiles>
|
||||||
</projectFiles>
|
</projectFiles>
|
||||||
</psalm>
|
</psalm>
|
||||||
|
10
src/App.php
10
src/App.php
@ -26,6 +26,7 @@ class App
|
|||||||
* Create a new instance providing a config file.
|
* Create a new instance providing a config file.
|
||||||
*
|
*
|
||||||
* @param string $configFile Relative or full path to YML config.
|
* @param string $configFile Relative or full path to YML config.
|
||||||
|
*
|
||||||
* @SuppressWarnings(PHPMD.StaticAccess)
|
* @SuppressWarnings(PHPMD.StaticAccess)
|
||||||
*/
|
*/
|
||||||
public function __construct(string $configFile)
|
public function __construct(string $configFile)
|
||||||
@ -60,7 +61,9 @@ class App
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$parser = new Yaml();
|
$parser = new Yaml();
|
||||||
/** @var array<string, mixed> */
|
/**
|
||||||
|
* @var array<string, mixed>
|
||||||
|
*/
|
||||||
$parsedConfig = $parser->parseFile($configFile);
|
$parsedConfig = $parser->parseFile($configFile);
|
||||||
|
|
||||||
// Merge those values into the configuration schema:
|
// Merge those values into the configuration schema:
|
||||||
@ -80,11 +83,14 @@ class App
|
|||||||
* Get configuration from key
|
* Get configuration from key
|
||||||
*
|
*
|
||||||
* @param non-empty-string $key Key to fetch
|
* @param non-empty-string $key Key to fetch
|
||||||
|
*
|
||||||
* @return mixed Configuration value
|
* @return mixed Configuration value
|
||||||
*/
|
*/
|
||||||
public function getConfig(string $key): mixed
|
public function getConfig(string $key): mixed
|
||||||
{
|
{
|
||||||
/** @var string|array<string, string> */
|
/**
|
||||||
|
* @var string|array<string, string>
|
||||||
|
*/
|
||||||
$ret = $this->config->get($key);
|
$ret = $this->config->get($key);
|
||||||
$this->logger->debug("Fetching configuration key", [$key, $ret]);
|
$this->logger->debug("Fetching configuration key", [$key, $ret]);
|
||||||
return $ret;
|
return $ret;
|
||||||
|
@ -65,19 +65,25 @@ class CommandBackup extends Command
|
|||||||
$rclone = new Rclone($app->getLogger()->withName('rclone'), (string)$app->getConfig('rclone.path'));
|
$rclone = new Rclone($app->getLogger()->withName('rclone'), (string)$app->getConfig('rclone.path'));
|
||||||
|
|
||||||
$notification = new Notification();
|
$notification = new Notification();
|
||||||
/** @var array<array-key,array<string,string>> */
|
/**
|
||||||
|
* @var array<array-key,array<string,string>>
|
||||||
|
*/
|
||||||
$notificationConfig = $app->getConfig('notification');
|
$notificationConfig = $app->getConfig('notification');
|
||||||
$notification->loadMany($notificationConfig);
|
$notification->loadMany($notificationConfig);
|
||||||
|
|
||||||
/** @var array<string,string> */
|
/**
|
||||||
|
* @var array<string,string>
|
||||||
|
*/
|
||||||
$templateConfig = $app->getConfig('templates');
|
$templateConfig = $app->getConfig('templates');
|
||||||
$render = new Twig($templateConfig);
|
$render = new Twig($templateConfig);
|
||||||
|
|
||||||
/** @var array{title: string, source: string, destination: string}[] */
|
/**
|
||||||
|
* @var array{title: string, source: string, destination: string}[]
|
||||||
|
*/
|
||||||
$backupElements = $app->getConfig('backup');
|
$backupElements = $app->getConfig('backup');
|
||||||
foreach ($sioProgressbar->progressIterate($backupElements) as $conf) {
|
foreach ($sioProgressbar->progressIterate($backupElements) as $conf) {
|
||||||
$title = $conf['title'];
|
$title = $conf['title'];
|
||||||
$template = array();
|
$template = [];
|
||||||
$template['config'] = $conf;
|
$template['config'] = $conf;
|
||||||
try {
|
try {
|
||||||
$template['start'] = new DateTime();
|
$template['start'] = new DateTime();
|
||||||
@ -85,7 +91,9 @@ class CommandBackup extends Command
|
|||||||
$template['rclone_version'] = $rclone->getVersion();
|
$template['rclone_version'] = $rclone->getVersion();
|
||||||
$template['destination_size_before'] = $rclone->getSize($conf['destination']);
|
$template['destination_size_before'] = $rclone->getSize($conf['destination']);
|
||||||
|
|
||||||
/** @var array<array-key, string> */
|
/**
|
||||||
|
* @var array<array-key, string>
|
||||||
|
*/
|
||||||
$rcloneOptions = $app->getConfig('rclone.options');
|
$rcloneOptions = $app->getConfig('rclone.options');
|
||||||
$rclone->copy($conf['source'], $conf['destination'], $rcloneOptions);
|
$rclone->copy($conf['source'], $conf['destination'], $rcloneOptions);
|
||||||
|
|
||||||
|
@ -42,7 +42,9 @@ class CommandShow extends Command
|
|||||||
$sio->error('Configuration error: ' . $e->getMessage());
|
$sio->error('Configuration error: ' . $e->getMessage());
|
||||||
return Command::FAILURE;
|
return Command::FAILURE;
|
||||||
}
|
}
|
||||||
/** @var array{title: string, source: string, destination: string}[] */
|
/**
|
||||||
|
* @var array{title: string, source: string, destination: string}[]
|
||||||
|
*/
|
||||||
$backupElements = $app->getConfig('backup');
|
$backupElements = $app->getConfig('backup');
|
||||||
$sio->table(
|
$sio->table(
|
||||||
['Description', 'Source', 'Destination'],
|
['Description', 'Source', 'Destination'],
|
||||||
|
@ -12,7 +12,7 @@ class Notification
|
|||||||
/**
|
/**
|
||||||
* @var NotificationInterface[] $notifiers
|
* @var NotificationInterface[] $notifiers
|
||||||
*/
|
*/
|
||||||
private array $notifiers = array();
|
private array $notifiers = [];
|
||||||
|
|
||||||
public function __construct(private NullLogger $logger = new NullLogger())
|
public function __construct(private NullLogger $logger = new NullLogger())
|
||||||
{
|
{
|
||||||
@ -35,6 +35,7 @@ class Notification
|
|||||||
*
|
*
|
||||||
* @param string $key Notification class
|
* @param string $key Notification class
|
||||||
* @param string[] $config Implementation specific configuration
|
* @param string[] $config Implementation specific configuration
|
||||||
|
*
|
||||||
* @SuppressWarnings(PHPMD)
|
* @SuppressWarnings(PHPMD)
|
||||||
*/
|
*/
|
||||||
public function loadSingle(string $key, array $config): void
|
public function loadSingle(string $key, array $config): void
|
||||||
|
@ -35,9 +35,6 @@ class Ntfy implements NotificationInterface
|
|||||||
return $instance;
|
return $instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo The constructor should be private but static code analysis complains.
|
|
||||||
*/
|
|
||||||
public function __construct(private Client $client)
|
public function __construct(private Client $client)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -70,8 +70,10 @@ class Rclone
|
|||||||
throw new Exception($process->getErrorOutput());
|
throw new Exception($process->getErrorOutput());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var array{bytes: int} */
|
/**
|
||||||
$output = json_decode($process->getOutput(), true);
|
* @var array{bytes: int}
|
||||||
|
*/
|
||||||
|
$output = json_decode($process->getOutput(), TRUE);
|
||||||
return $output['bytes'];
|
return $output['bytes'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,9 +84,9 @@ class Rclone
|
|||||||
* @param $dest Destination mount and path
|
* @param $dest Destination mount and path
|
||||||
* @param string[] $additionalOptions Additional options
|
* @param string[] $additionalOptions Additional options
|
||||||
*/
|
*/
|
||||||
public function copy(string $src, string $dest, array $additionalOptions = array()): void
|
public function copy(string $src, string $dest, array $additionalOptions = []): void
|
||||||
{
|
{
|
||||||
$options = array();
|
$options = [];
|
||||||
|
|
||||||
$options[] = $src;
|
$options[] = $src;
|
||||||
$options[] = $dest;
|
$options[] = $dest;
|
||||||
@ -108,7 +110,7 @@ class Rclone
|
|||||||
*
|
*
|
||||||
* @return Process Instance.
|
* @return Process Instance.
|
||||||
*/
|
*/
|
||||||
private function exec(string $command, array $options = array()): Process
|
private function exec(string $command, array $options = []): Process
|
||||||
{
|
{
|
||||||
$process = new Process(
|
$process = new Process(
|
||||||
array_merge(
|
array_merge(
|
||||||
|
@ -22,9 +22,9 @@ class TwigExtension extends AbstractExtension
|
|||||||
*/
|
*/
|
||||||
public function getFilters(): array
|
public function getFilters(): array
|
||||||
{
|
{
|
||||||
return array(
|
return [
|
||||||
new TwigFilter('formatBytes', array($this, 'formatBytes')),
|
new TwigFilter('formatBytes', [$this, 'formatBytes']),
|
||||||
);
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,7 +12,9 @@ use Exception;
|
|||||||
|
|
||||||
final class NotificationTest extends TestCase
|
final class NotificationTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var array<string, string> */
|
/**
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
private static array $config = ['type' => 'ntfy', 'domain' => 'https://test.com', 'topic' => 'testing'];
|
private static array $config = ['type' => 'ntfy', 'domain' => 'https://test.com', 'topic' => 'testing'];
|
||||||
|
|
||||||
public function testloadSingle(): void
|
public function testloadSingle(): void
|
||||||
|
@ -16,6 +16,12 @@ final class NtfyTest extends TestCase
|
|||||||
private Ntfy $instance;
|
private Ntfy $instance;
|
||||||
private MockObject $client;
|
private MockObject $client;
|
||||||
|
|
||||||
|
public function __construct($name)
|
||||||
|
{
|
||||||
|
parent::__construct($name);
|
||||||
|
$this->setUp();
|
||||||
|
}
|
||||||
|
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
$this->client = $this->createMock(Client::class);
|
$this->client = $this->createMock(Client::class);
|
||||||
@ -45,7 +51,9 @@ final class NtfyTest extends TestCase
|
|||||||
$this->instance->send(str_repeat("t", Ntfy::TITLE_MAX_LENGTH), str_repeat("t", Ntfy::MESSAGE_MAX_LENGTH));
|
$this->instance->send(str_repeat("t", Ntfy::TITLE_MAX_LENGTH), str_repeat("t", Ntfy::MESSAGE_MAX_LENGTH));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return array<int, array<int, string>> */
|
/**
|
||||||
|
* @return array<int, array<int, string>>
|
||||||
|
*/
|
||||||
public static function sendBadParameterProvider(): array
|
public static function sendBadParameterProvider(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@ -75,7 +83,9 @@ final class NtfyTest extends TestCase
|
|||||||
$this->assertEquals($topic, $this->instance->getTopic());
|
$this->assertEquals($topic, $this->instance->getTopic());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return array<int, array<int, string>> */
|
/**
|
||||||
|
* @return array<int, array<int, string>>
|
||||||
|
*/
|
||||||
public static function topicBadParameterProvider(): array
|
public static function topicBadParameterProvider(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
Reference in New Issue
Block a user