From fed2242a56103bd39e4220dc2f7cacc706a3b1ce Mon Sep 17 00:00:00 2001 From: Hugo Sales Date: Wed, 19 Oct 2022 22:38:49 +0100 Subject: [PATCH] [TOOLS] Raise PHPStan level to 5 and fix associated error, fixing some bugs in the process --- components/LeftPanel/Controller/EditFeeds.php | 1 - phpstan.neon | 2 +- plugins/Cover/Controller/Cover.php | 8 +- plugins/Oomox/Controller/Oomox.php | 4 +- plugins/Pinboard/Controller/APIv1.php | 2 +- .../Controller/EditBlocked.php | 19 +-- src/Core/Cache.php | 124 +++++++++--------- src/Core/DB.php | 19 ++- src/Core/Entity.php | 15 ++- src/Twig/Extension.php | 1 - src/Util/Formatting.php | 7 +- src/Util/TemporaryFile.php | 8 +- tests/Core/CacheTest.php | 27 ++-- tests/Util/Form/ArrayTransformerTest.php | 4 +- tests/Util/FormattingTest.php | 4 +- tests/Util/HTMLTest.php | 2 +- 16 files changed, 139 insertions(+), 108 deletions(-) diff --git a/components/LeftPanel/Controller/EditFeeds.php b/components/LeftPanel/Controller/EditFeeds.php index cb75271a43..0e49307bc4 100644 --- a/components/LeftPanel/Controller/EditFeeds.php +++ b/components/LeftPanel/Controller/EditFeeds.php @@ -104,7 +104,6 @@ class EditFeeds extends Controller $feed->setUrl($fd[$md5 . '-url']); $feed->setOrdering($fd[$md5 . '-order']); $feed->setTitle($fd[$md5 . '-title']); - DB::merge($feed); } DB::flush(); Cache::delete($key); diff --git a/phpstan.neon b/phpstan.neon index 951f4f702e..12711b7264 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,5 @@ parameters: - level: 4 + level: 5 bootstrapFiles: - config/bootstrap.php paths: diff --git a/plugins/Cover/Controller/Cover.php b/plugins/Cover/Controller/Cover.php index 9cdaa14f58..16e18493e6 100644 --- a/plugins/Cover/Controller/Cover.php +++ b/plugins/Cover/Controller/Cover.php @@ -112,7 +112,9 @@ class Cover DB::flush(); // Only delete files if the commit went through if ($old_file != null) { - @unlink($old_file); + foreach ($old_file as $f) { + @unlink($f); + } } throw new RedirectException(); } @@ -128,7 +130,9 @@ class Cover $old_file = $cover->delete(); DB::remove($cover); DB::flush(); - @unlink($old_file); + foreach ($old_file as $f) { + @unlink($f); + } throw new RedirectException(); } $removeForm = $form2->createView(); diff --git a/plugins/Oomox/Controller/Oomox.php b/plugins/Oomox/Controller/Oomox.php index ac5d925472..c2496a62a1 100644 --- a/plugins/Oomox/Controller/Oomox.php +++ b/plugins/Oomox/Controller/Oomox.php @@ -170,7 +170,7 @@ class Oomox if ($reset_button->isClicked()) { DB::remove(EntityOomox::getByPK($actor_id)); } else { - DB::merge($current_oomox_settings); + DB::persist($current_oomox_settings); } DB::flush(); } @@ -219,7 +219,7 @@ class Oomox if ($reset_button->isClicked()) { DB::remove(EntityOomox::getByPK($actor_id)); } else { - DB::merge($current_oomox_settings); + DB::persist($current_oomox_settings); } DB::flush(); } diff --git a/plugins/Pinboard/Controller/APIv1.php b/plugins/Pinboard/Controller/APIv1.php index 38bc9389fd..4a3df717ef 100644 --- a/plugins/Pinboard/Controller/APIv1.php +++ b/plugins/Pinboard/Controller/APIv1.php @@ -73,7 +73,7 @@ class APIv1 extends Controller // dd($tag_names, $keys, $result, $xml, (string) $xml); // $xml->addChild(); // dd($xml); - return new Response(content: $xml, status: $status); + return new Response(content: (string) $xml, status: $status); } else { throw new InvalidRequestException; } diff --git a/plugins/TagBasedFiltering/Controller/EditBlocked.php b/plugins/TagBasedFiltering/Controller/EditBlocked.php index e72b5d8eaf..cb383bc85a 100644 --- a/plugins/TagBasedFiltering/Controller/EditBlocked.php +++ b/plugins/TagBasedFiltering/Controller/EditBlocked.php @@ -77,15 +77,16 @@ class EditBlocked extends Controller Cache::delete($block_class::cacheKey($user->getId())); Cache::delete(TagFilerPlugin::cacheKeys($user->getId())[$type_name]); switch ($type) { - case 'toggle-canon': - $ntb = DB::getReference($block_class, ['blocker' => $user->getId(), 'canonical' => $canon]); - $ntb->setUseCanonical(!$ntb->getUseCanonical()); - DB::flush(); - throw new RedirectException; - case 'remove': - $old_tag = $data[$canon . ':old-tag']; - DB::removeBy($block_class, ['blocker' => $user->getId(), 'canonical' => $canon]); - throw new RedirectException; + case 'toggle-canon': + /** @var NoteTagBlock $ntb */ + $ntb = DB::getReference($block_class, ['blocker' => $user->getId(), 'canonical' => $canon]); + $ntb->setUseCanonical(!$ntb->getUseCanonical()); + DB::flush(); + throw new RedirectException; + case 'remove': + $old_tag = $data[$canon . ':old-tag']; + DB::removeBy($block_class, ['blocker' => $user->getId(), 'canonical' => $canon]); + throw new RedirectException; } } } diff --git a/src/Core/Cache.php b/src/Core/Cache.php index 1267d1c08d..19b71cf0d5 100644 --- a/src/Core/Cache.php +++ b/src/Core/Cache.php @@ -23,7 +23,6 @@ declare(strict_types = 1); namespace App\Core; -use App\Core\DB; use App\Entity\Actor; use App\Entity\LocalUser; use App\Entity\Note; @@ -32,6 +31,7 @@ use App\Util\Exception\ConfigurationException; use App\Util\Exception\NotImplementedException; use Functional as F; use InvalidArgumentException; +use Memcached; use Redis; use RedisCluster; use Symfony\Component\Cache\Adapter; @@ -65,61 +65,66 @@ abstract class Cache $rest = ''; } switch ($scheme) { - case 'redis': - // Redis can have multiple servers, but we want to take proper advantage of - // redis, not just as a key value store, but using it's datastructures - $dsns = explode(';', $dsn); - if (\count($dsns) === 1) { - $class = Redis::class; - $r = new Redis(); - $r->pconnect($rest); - } else { - // @codeCoverageIgnoreStart - // This requires extra server configuration, but the code was tested - // manually and works, so it'll be excluded from automatic tests, for now, at least - if (F\Every($dsns, function ($str) { [$scheme, $rest] = explode('://', $str); return str_contains($rest, ':'); }) == false) { - throw new ConfigurationException('The configuration of a redis cluster requires specifying the ports to use'); + case 'redis': + // Redis can have multiple servers, but we want to take proper advantage of + // redis, not just as a key value store, but using it's datastructures + $dsns = explode(';', $dsn); + if (\count($dsns) === 1) { + $class = Redis::class; + $r = new Redis(); + $r->pconnect($rest); + } else { + // @codeCoverageIgnoreStart + // This requires extra server configuration, but the code was tested + // manually and works, so it'll be excluded from automatic tests, for now, at least + if (F\Every($dsns, function ($str) { [$scheme, $rest] = explode('://', $str); + return str_contains($rest, ':'); }) == false) { + throw new ConfigurationException('The configuration of a redis cluster requires specifying the ports to use'); + } + $class = RedisCluster::class; // true for persistent connection + $seeds = F\Map($dsns, fn ($str) => explode('://', $str)[1]); + $r = new RedisCluster(name: null, seeds: $seeds, timeout: 0.0, readTimeout: 0.0, persistent: true); + // Distribute reads randomly + $r->setOption($class::OPT_SLAVE_FAILOVER, $class::FAILOVER_DISTRIBUTE); + // @codeCoverageIgnoreEnd } - $class = RedisCluster::class; // true for persistent connection - $seeds = F\Map($dsns, fn ($str) => explode('://', $str)[1]); - $r = new RedisCluster(name: null, seeds: $seeds, timeout: null, readTimeout: null, persistent: true); - // Distribute reads randomly - $r->setOption($class::OPT_SLAVE_FAILOVER, $class::FAILOVER_DISTRIBUTE); + // Improved serializer + $r->setOption($class::OPT_SERIALIZER, $class::SERIALIZER_MSGPACK); + // Persistent connection + $r->setOption($class::OPT_TCP_KEEPALIVE, true); + // Use LZ4 for the improved decompression speed (while keeping an okay compression ratio) + $r->setOption($class::OPT_COMPRESSION, $class::COMPRESSION_LZ4); + self::$redis[$pool] = $r; + $adapters[$pool][] = new Adapter\RedisAdapter($r); + break; + case 'memcached': + // @codeCoverageIgnoreStart + // These all are excluded from automatic testing, as they require an unreasonable amount + // of configuration in the testing environment. The code is really simple, so it should work + // memcached can also have multiple servers + // host:port:weight? + $dsns = explode(';', $dsn); + $servers = F\map($dsns, fn ($dsn) => explode(':', $dsn)); + $memcached = new Memcached(); + $memcached->addServers($servers); + $adapters[$pool][] = new Adapter\MemcachedAdapter($memcached); + break; + case 'filesystem': + $adapters[$pool][] = new Adapter\FilesystemAdapter($rest); + break; + case 'apcu': + $adapters[$pool][] = new Adapter\ApcuAdapter(); + break; + case 'opcache': + $adapters[$pool][] = new Adapter\PhpArrayAdapter($rest, new Adapter\FilesystemAdapter($rest . '.fallback')); + break; + case 'doctrine': + $adapters[$pool][] = new Adapter\PdoAdapter($dsn); + break; + default: + Log::error("Unknown or discouraged cache scheme '{$scheme}'"); + return; // @codeCoverageIgnoreEnd - } - // Improved serializer - $r->setOption($class::OPT_SERIALIZER, $class::SERIALIZER_MSGPACK); - // Persistent connection - $r->setOption($class::OPT_TCP_KEEPALIVE, true); - // Use LZ4 for the improved decompression speed (while keeping an okay compression ratio) - $r->setOption($class::OPT_COMPRESSION, $class::COMPRESSION_LZ4); - self::$redis[$pool] = $r; - $adapters[$pool][] = new Adapter\RedisAdapter($r); - break; - case 'memcached': - // @codeCoverageIgnoreStart - // These all are excluded from automatic testing, as they require an unreasonable amount - // of configuration in the testing environment. The code is really simple, so it should work - // memcached can also have multiple servers - $dsns = explode(';', $dsn); - $adapters[$pool][] = new Adapter\MemcachedAdapter($dsns); - break; - case 'filesystem': - $adapters[$pool][] = new Adapter\FilesystemAdapter($rest); - break; - case 'apcu': - $adapters[$pool][] = new Adapter\ApcuAdapter(); - break; - case 'opcache': - $adapters[$pool][] = new Adapter\PhpArrayAdapter($rest, new Adapter\FilesystemAdapter($rest . '.fallback')); - break; - case 'doctrine': - $adapters[$pool][] = new Adapter\PdoAdapter($dsn); - break; - default: - Log::error("Unknown or discouraged cache scheme '{$scheme}'"); - return; - // @codeCoverageIgnoreEnd } } @@ -198,7 +203,7 @@ abstract class Cache $key, recompute: function () use ($key, $calculate, $pool) { $save = true; // Pass by reference - $res = $calculate(null, $save); + $res = $calculate(null, $save); if ($save) { self::set($key, $res, $pool); } @@ -255,7 +260,7 @@ abstract class Cache */ function () use ($key, $calculate, $pool, $max_count, $left, $right, $beta) { $save = true; // Pass by reference - $res = $calculate(null, $save); + $res = $calculate(null, $save); if ($save) { self::setList($key, $res, $pool, $max_count, $beta); } @@ -291,7 +296,7 @@ abstract class Cache */ function () use ($calculate, $max_count) { $save = true; - $res = $calculate(null, $save); + $res = $calculate(null, $save); if ($max_count != -1) { $res = \array_slice($res, 0, $max_count); } @@ -434,6 +439,7 @@ abstract class Cache * for redis and others. Different from lists, works with string map_keys * * @param callable(?CacheItem $item, bool &$save): (string|object|array) $calculate + * * @TODO cleanup */ public static function getHashMap(string $map_key, callable $calculate, string $pool = 'default', float $beta = 1.0): array @@ -443,7 +449,7 @@ abstract class Cache $map_key, recompute: function () use ($map_key, $calculate, $pool) { $save = true; // Pass by reference - $res = $calculate(null, $save); + $res = $calculate(null, $save); if ($save) { self::setHashMap($map_key, $res, $pool); } @@ -531,7 +537,7 @@ abstract class Cache $getter = fn (int $offset, int $length) => DB::dql($query, $query_args, options: ['offset' => $offset, 'limit' => $length]); - $requested_left = $offset = $per_page * ($page - 1); + $requested_left = $offset = $per_page * ($page - 1); $requested_right = $requested_left + $per_page; [$stored_left, $stored_right] = F\map( explode(':', self::get("{$key}-bounds", fn () => "{$requested_left}:{$requested_right}")), diff --git a/src/Core/DB.php b/src/Core/DB.php index 523d8f2d28..ff611efabc 100644 --- a/src/Core/DB.php +++ b/src/Core/DB.php @@ -40,17 +40,18 @@ use App\Util\Formatting; use Closure; use Doctrine\Common\Collections\Criteria; use Doctrine\Common\Collections\ExpressionBuilder; +use Doctrine\DBAL\Connection; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query; use Doctrine\ORM\Query\ResultSetMappingBuilder; +use Doctrine\ORM\QueryBuilder; use Exception; use Functional as F; /** - * @mixin EntityManager - * * @template T of Entity * * Finds an Entity by its identifier. You probably want to use DB::findBy instead. @@ -77,10 +78,14 @@ use Functional as F; * @method static void flush() * * Executes a function in a transaction. Warning: suppresses exceptions. Returns the result of the callable. - * @method mixed wrapInTransaction(callable $func) + * @method static mixed wrapInTransaction(callable $func) * * Refetch the given entity - * @method static T refetch(T $entity) + * @method static T refetch(T $entity) + * @method static QueryBuilder createQueryBuilder() + * @method static Connection getConnection() + * @method static EntityRepository getRepository(string $table) + * @method static ClassMetadata getClassMetadata(string $table) */ class DB { @@ -317,8 +322,10 @@ class DB $seqName = $metadata->getSequenceName($conn->getDatabasePlatform()); self::persist($owner); $id = (int) $conn->lastInsertId($seqName); - F\map(\is_array($others) ? $others : [$others], function ($o) use ($id) { $o->setId($id); - self::persist($o); }); + F\map(\is_array($others) ? $others : [$others], function ($o) use ($id) { + $o->setId($id); + self::persist($o); + }); if (!\is_null($extra)) { $extra($id); } diff --git a/src/Core/Entity.php b/src/Core/Entity.php index c09878d958..005e2aa200 100644 --- a/src/Core/Entity.php +++ b/src/Core/Entity.php @@ -37,7 +37,7 @@ use Functional as F; /** * Base class to all entities, with some utilities * - * @method int getId() // Not strictly true + * @method int getId() // Not strictly true FIXME */ abstract class Entity { @@ -67,12 +67,14 @@ abstract class Entity * Create an instance of the called class or fill in the * properties of $obj with the associative array $args. Doesn't * persist the result + * + * @return static */ - public static function create(array $args, bool $_delegated_call = false): static + public static function create(array $args, bool $_delegated_call = false): self { $date = new DateTime(); $class = static::class; - $obj = new $class(); + $obj = new $class; foreach (['created', 'modified'] as $prop) { if (property_exists($class, $prop) && !isset($args[$prop])) { $args[$prop] = $date; @@ -87,9 +89,9 @@ abstract class Entity } /** - * @param ?static $obj + * @return static */ - public static function createOrUpdate(?self $obj, array $args, bool $_delegated_call = false): static + public static function createOrUpdate(?self $obj, array $args, bool $_delegated_call = false): self { $date = new DateTime(); $class = static::class; @@ -100,7 +102,7 @@ abstract class Entity } if (\is_null($obj)) { - $obj = static::create($args, _delegated_call: true); + $obj = $class::create($args, _delegated_call: true); } foreach ($args as $prop => $val) { @@ -112,6 +114,7 @@ abstract class Entity } } + // @phpstan-ignore-next-line return $obj; } diff --git a/src/Twig/Extension.php b/src/Twig/Extension.php index e184eae9eb..e9c98165ec 100644 --- a/src/Twig/Extension.php +++ b/src/Twig/Extension.php @@ -64,7 +64,6 @@ class Extension extends AbstractExtension new TwigFunction('active', [Runtime::class, 'isCurrentRouteActive']), new TwigFunction('config', [Runtime::class, 'getConfig']), new TwigFunction('dd', 'dd'), - new TwigFunction('die', 'die'), new TwigFunction('get_profile_actions', [Runtime::class, 'getProfileActions']), new TwigFunction('get_extra_note_actions', [Runtime::class, 'getExtraNoteActions']), new TwigFunction('get_feeds', [Runtime::class, 'getFeeds']), diff --git a/src/Util/Formatting.php b/src/Util/Formatting.php index 8bc0002edc..5995922b7c 100644 --- a/src/Util/Formatting.php +++ b/src/Util/Formatting.php @@ -206,12 +206,11 @@ abstract class Formatting /** * Convert a user supplied string to array and return whether the conversion was successful + * + * @param static::SPLIT_BY_BOTH|static::SPLIT_BY_COMMA|static::SPLIT_BY_SPACE $split_type */ public static function toArray(string $input, &$output, string $split_type = self::SPLIT_BY_COMMA): bool { - if (!\in_array($split_type, [static::SPLIT_BY_SPACE, static::SPLIT_BY_COMMA, static::SPLIT_BY_BOTH])) { - throw new Exception('Formatting::toArray received invalid split option'); - } if ($input == '') { $output = []; return true; @@ -226,7 +225,7 @@ abstract class Formatting $arr = preg_split('/, ?/', $matches[1]); break; default: - $arr = explode($split_type[0], $matches[1]); + $arr = explode(self::SPLIT_BY_SPACE, $matches[1]); } $output = str_replace([' \'', '\'', ' "', '"'], '', $arr); $output = F\map($output, F\ary('trim', 1)); diff --git a/src/Util/TemporaryFile.php b/src/Util/TemporaryFile.php index 9893f14118..6e7f1bb8c4 100644 --- a/src/Util/TemporaryFile.php +++ b/src/Util/TemporaryFile.php @@ -207,7 +207,13 @@ class TemporaryFile extends SplFileInfo // @codeCoverageIgnoreEnd } - set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); + set_error_handler( + function (int $errno, string $msg, string $errfile, int $errline, array $errcontext = []) use (&$error): bool { + $error = $msg; + return $msg === ''; + }, + ); + $renamed = rename($this->getPathname(), $destpath); $chmoded = chmod($destpath, $filemode); restore_error_handler(); diff --git a/tests/Core/CacheTest.php b/tests/Core/CacheTest.php index 92037efbdc..c37c5a4ec7 100644 --- a/tests/Core/CacheTest.php +++ b/tests/Core/CacheTest.php @@ -31,6 +31,13 @@ use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface; class CacheTest extends GNUsocialTestCase { + // Needs to return something to conform to the interface + private function notCalled(mixed $i): array + { + static::assertFalse('should not be called'); + return []; + } + private function doTest(array $adapters, $result_pool, $throws = null, $recompute = \INF) { static::bootKernel(); @@ -105,15 +112,15 @@ class CacheTest extends GNUsocialTestCase static::assertSame([], Cache::getList($key . '0', fn ($i) => [])); static::assertSame(['foo'], Cache::getList($key . '1', fn ($i) => ['foo'])); static::assertSame(['foo', 'bar'], Cache::getList($key, fn ($i) => ['foo', 'bar'])); - static::assertSame(['foo', 'bar'], Cache::getList($key, function () { $this->assertFalse('should not be called'); })); // Repeat to test no recompute lrange + static::assertSame(['foo', 'bar'], Cache::getList($key, [$this, 'notCalled'])); // Repeat to test no recompute lrange Cache::listPushLeft($key, 'quux'); - static::assertSame(['quux', 'foo', 'bar'], Cache::getList($key, function ($i) { $this->assertFalse('should not be called'); })); + static::assertSame(['quux', 'foo', 'bar'], Cache::getList($key, [$this, 'notCalled'])); Cache::listPushLeft($key, 'foobar', max_count: 2); - static::assertSame(['foobar', 'quux'], Cache::getList($key, function ($i) { $this->assertFalse('should not be called'); })); + static::assertSame(['foobar', 'quux'], Cache::getList($key, [$this, 'notCalled'])); Cache::listPushRight($key, 'foo'); - static::assertSame(['foobar', 'quux', 'foo'], Cache::getList($key, function ($i) { $this->assertFalse('should not be called'); })); + static::assertSame(['foobar', 'quux', 'foo'], Cache::getList($key, [$this, 'notCalled'])); Cache::listPushRight($key, 'bar', max_count: 2); - static::assertSame(['foo', 'bar'], Cache::getList($key, function ($i) { $this->assertFalse('should not be called'); })); + static::assertSame(['foo', 'bar'], Cache::getList($key, [$this, 'notCalled'])); static::assertTrue(Cache::deleteList($key)); } @@ -135,15 +142,15 @@ class CacheTest extends GNUsocialTestCase static::assertSame([], Cache::getList($key . '0', fn ($i) => [], pool: 'file')); static::assertSame(['foo'], Cache::getList($key . '1', fn ($i) => ['foo'], pool: 'file')); static::assertSame(['foo', 'bar'], Cache::getList($key, fn ($i) => ['foo', 'bar'], pool: 'file')); - static::assertSame(['foo', 'bar'], Cache::getList($key, function () { $this->assertFalse('should not be called'); }, pool: 'file')); // Repeat to test no recompute lrange + static::assertSame(['foo', 'bar'], Cache::getList($key, [$this, 'notCalled'], pool: 'file')); // Repeat to test no recompute lrange Cache::listPushLeft($key, 'quux', pool: 'file'); - static::assertSame(['quux', 'foo', 'bar'], Cache::getList($key, function ($i) { $this->assertFalse('should not be called'); }, pool: 'file')); + static::assertSame(['quux', 'foo', 'bar'], Cache::getList($key, [$this, 'notCalled'], pool: 'file')); Cache::listPushLeft($key, 'foobar', max_count: 2, pool: 'file'); - static::assertSame(['foobar', 'quux'], Cache::getList($key, function ($i) { $this->assertFalse('should not be called'); }, pool: 'file')); + static::assertSame(['foobar', 'quux'], Cache::getList($key, [$this, 'notCalled'], pool: 'file')); Cache::listPushRight($key, 'foo', pool: 'file'); - static::assertSame(['foobar', 'quux', 'foo'], Cache::getList($key, function ($i) { $this->assertFalse('should not be called'); }, pool: 'file')); + static::assertSame(['foobar', 'quux', 'foo'], Cache::getList($key, [$this, 'notCalled'], pool: 'file')); Cache::listPushRight($key, 'bar', max_count: 2, pool: 'file'); - static::assertSame(['foo', 'bar'], Cache::getList($key, function ($i) { $this->assertFalse('should not be called'); }, pool: 'file')); + static::assertSame(['foo', 'bar'], Cache::getList($key, [$this, 'notCalled'], pool: 'file')); static::assertTrue(Cache::deleteList($key, pool: 'file')); } } diff --git a/tests/Util/Form/ArrayTransformerTest.php b/tests/Util/Form/ArrayTransformerTest.php index cf1a8c2297..2632cb3971 100644 --- a/tests/Util/Form/ArrayTransformerTest.php +++ b/tests/Util/Form/ArrayTransformerTest.php @@ -34,13 +34,13 @@ class ArrayTransformerTest extends WebTestCase { static::assertSame('', (new ArrayTransformer)->transform([])); static::assertSame('foo bar quux', (new ArrayTransformer)->transform(['foo', 'bar', 'quux'])); - static::assertThrows(TransformationFailedException::class, fn () => (new ArrayTransformer)->transform('')); + static::assertThrows(TransformationFailedException::class, fn () => (new ArrayTransformer)->transform('')); // @phpstan-ignore-line } public function testReverseTransform() { static::assertSame([], (new ArrayTransformer)->reverseTransform('')); static::assertSame(['foo', 'bar', 'quux'], (new ArrayTransformer)->reverseTransform('foo bar quux')); - static::assertThrows(TransformationFailedException::class, fn () => (new ArrayTransformer)->reverseTransform(1)); + static::assertThrows(TransformationFailedException::class, fn () => (new ArrayTransformer)->reverseTransform(1)); // @phpstan-ignore-line } } diff --git a/tests/Util/FormattingTest.php b/tests/Util/FormattingTest.php index 6ef7a23514..2e15d13fb2 100644 --- a/tests/Util/FormattingTest.php +++ b/tests/Util/FormattingTest.php @@ -170,7 +170,7 @@ class FormattingTest extends WebTestCase static::assertSame(' foo', Formatting::indent('foo', level: 1, count: 2)); static::assertSame(" foo\n bar", Formatting::indent("foo\nbar")); static::assertSame(" foo\n bar", Formatting::indent(['foo', 'bar'])); - static::assertThrows(InvalidArgumentException::class, fn () => Formatting::indent(1)); + static::assertThrows(InvalidArgumentException::class, fn () => Formatting::indent(1)); // @phpstan-ignore-line } public function testToString() @@ -185,7 +185,7 @@ class FormattingTest extends WebTestCase public function testToArray() { - static::assertThrows(Exception::class, fn () => Formatting::toArray('foo', $a, '')); + static::assertThrows(Exception::class, fn () => Formatting::toArray('foo', $a, '')); // @phpstan-ignore-line static::assertTrue(Formatting::toArray('', $a)); static::assertSame([], $a); diff --git a/tests/Util/HTMLTest.php b/tests/Util/HTMLTest.php index ec4c084a2a..68d72003bc 100644 --- a/tests/Util/HTMLTest.php +++ b/tests/Util/HTMLTest.php @@ -41,7 +41,7 @@ class HTMLTest extends WebTestCase static::assertSame('

foo


', HTML::html(['a' => ['p' => 'foo', 'br' => 'empty']])); static::assertSame("
\n

foo


\n
", HTML::html(['div' => ['a' => ['p' => 'foo', 'br' => 'empty']]])); static::assertSame('

foo


', HTML::html(['div' => ['a' => ['p' => 'foo', 'br' => 'empty']]], options: ['indent' => false])); - static::assertThrows(TypeError::class, fn () => HTML::html(1)); + static::assertThrows(TypeError::class, fn () => HTML::html(1)); // @phpstan-ignore-line static::assertSame('foo', HTML::tag('a', ['href' => 'test'], content: 'foo', options: ['empty' => false])); static::assertSame('
', HTML::tag('br', attrs: null, content: null, options: ['empty' => true])); }