diff --git a/components/Circle/Circle.php b/components/Circle/Circle.php index be44d87d76..591379cbc7 100644 --- a/components/Circle/Circle.php +++ b/components/Circle/Circle.php @@ -57,8 +57,8 @@ class Circle extends Component { use MetaCollectionTrait; public const TAG_CIRCLE_REGEX = '/' . Nickname::BEFORE_MENTIONS . '@#([\pL\pN_\-\.]{1,64})/'; - protected string $slug = 'circle'; - protected string $plural_slug = 'circles'; + protected const SLUG = 'circle'; + protected const PLURAL_SLUG = 'circles'; public function onAddRoute(RouteLoader $r): bool { diff --git a/components/Circle/Controller/Circles.php b/components/Circle/Controller/Circles.php index 6b316c49b8..5a1c8ba7eb 100644 --- a/components/Circle/Controller/Circles.php +++ b/components/Circle/Controller/Circles.php @@ -33,9 +33,9 @@ use Component\Collection\Util\Controller\MetaCollectionController; class Circles extends MetaCollectionController { - protected string $slug = 'circle'; - protected string $plural_slug = 'circles'; - protected string $page_title = 'Actor circles'; + protected const SLUG = 'circle'; + protected const PLURAL_SLUG = 'circles'; + protected string $page_title = 'Actor circles'; public function createCollection(int $owner_id, string $name) { diff --git a/components/Collection/Util/Controller/MetaCollectionController.php b/components/Collection/Util/Controller/MetaCollectionController.php index 4d8cc092fd..55e187263a 100644 --- a/components/Collection/Util/Controller/MetaCollectionController.php +++ b/components/Collection/Util/Controller/MetaCollectionController.php @@ -43,9 +43,9 @@ use Symfony\Component\HttpFoundation\Request; abstract class MetaCollectionController extends FeedController { - protected string $slug = 'collectionsEntry'; - protected string $plural_slug = 'collectionsList'; - protected string $page_title = 'Collections'; + protected const SLUG = 'collectionsEntry'; + protected const PLURAL_SLUG = 'collectionsList'; + protected string $page_title = 'Collections'; abstract public function getCollectionUrl(int $owner_id, string $owner_nickname, int $collection_id): string; abstract public function getCollectionItems(int $owner_id, $collection_id): array; @@ -76,8 +76,8 @@ abstract class MetaCollectionController extends FeedController { $collections = $this->getCollectionsByActorId($id); - $create_title = _m('Create a ' . mb_strtolower(preg_replace('/([a-z0-9])([A-Z])/', '$1 $2', $this->slug))); - $collections_title = _m('The ' . mb_strtolower(preg_replace('/([a-z0-9])([A-Z])/', '$1 $2', $this->plural_slug))); + $create_title = _m('Create a ' . mb_strtolower(preg_replace('/([a-z0-9])([A-Z])/', '$1 $2', static::SLUG))); + $collections_title = _m('The ' . mb_strtolower(preg_replace('/([a-z0-9])([A-Z])/', '$1 $2', static::PLURAL_SLUG))); // create collection form $create = null; if (Common::user()?->getId() === $id) { @@ -111,8 +111,8 @@ abstract class MetaCollectionController extends FeedController // // Instead, I'm using an anonymous class to encapsulate // the functions and passing that class to the template. - // This is suggested at https://stackoverflow.com/a/50364502. - $fn = new class($id, $nickname, $request, $this, $this->slug) { + // This is suggested at https://web.archive.org/web/20220226132328/https://stackoverflow.com/questions/3595727/twig-pass-function-into-template/50364502 + $fn = new class($id, $nickname, $request, $this, static::SLUG) { private $id; private $nick; private $request; diff --git a/components/Collection/Util/MetaCollectionTrait.php b/components/Collection/Util/MetaCollectionTrait.php index 531d37aeaa..1aec098457 100644 --- a/components/Collection/Util/MetaCollectionTrait.php +++ b/components/Collection/Util/MetaCollectionTrait.php @@ -46,8 +46,8 @@ use Symfony\Component\HttpFoundation\Request; trait MetaCollectionTrait { - //protected string $slug = 'collection'; - //protected string $plural_slug = 'collections'; + //protected const SLUG = 'collection'; + //protected const PLURAL_SLUG = 'collections'; /** * create a collection owned by Actor $owner. @@ -127,9 +127,9 @@ trait MetaCollectionTrait }, ]], ['add', SubmitType::class, [ - 'label' => _m('Add to ' . $this->plural_slug), + 'label' => _m('Add to ' . static::PLURAL_SLUG), 'attr' => [ - 'title' => _m('Add to ' . $this->plural_slug), + 'title' => _m('Add to ' . static::PLURAL_SLUG), ], ]], ]); @@ -151,17 +151,17 @@ trait MetaCollectionTrait // form: add to new collection $create_form = Form::create([ ['name', TextType::class, [ - 'label' => _m('Add to a new ' . $this->slug), + 'label' => _m('Add to a new ' . static::SLUG), 'attr' => [ - 'placeholder' => _m('New ' . $this->slug . ' name'), + 'placeholder' => _m('New ' . static::SLUG . ' name'), 'required' => 'required', ], 'data' => '', ]], ['create', SubmitType::class, [ - 'label' => _m('Create a new ' . $this->slug), + 'label' => _m('Create a new ' . static::SLUG), 'attr' => [ - 'title' => _m('Create a new ' . $this->slug), + 'title' => _m('Create a new ' . static::SLUG), ], ]], ]); @@ -176,7 +176,7 @@ trait MetaCollectionTrait $res[] = Formatting::twigRenderFile( 'collection/widget_add_to.html.twig', [ - 'ctitle' => _m('Add to ' . $this->plural_slug), + 'ctitle' => _m('Add to ' . static::PLURAL_SLUG), 'user' => $user, 'has_collections' => \count($collections) > 0, 'add_form' => $add_form->createView(), diff --git a/components/Collection/Util/Parser.php b/components/Collection/Util/Parser.php index a53d6a4107..799b203f0c 100644 --- a/components/Collection/Util/Parser.php +++ b/components/Collection/Util/Parser.php @@ -50,8 +50,9 @@ abstract class Parser * recognises either spaces (currently `or`, should be fuzzy match), `OR` or `|` (`or`) and `AND` or `&` (`and`) * * TODO: Better fuzzy match, implement exact match with quotes and nesting with parens + * TODO: Proper parser, tokenize better. Mostly a rewrite * - * @return Criteria[] + * @return array{?Criteria, ?Criteria} [?$note_criteria, ?$actor_criteria] */ public static function parse(string $input, ?string $locale = null, ?Actor $actor = null, int $level = 0): array { @@ -83,13 +84,13 @@ abstract class Parser //throw new ServerException("No one claimed responsibility for a match term: {$term}"); // It's okay if the term doesn't exist, just perform a regular search } - if (!empty($note_res)) { // @phpstan-ignore-line + if (!empty($note_res)) { // @phpstan-ignore-line currently an open bug. See https://web.archive.org/web/20220226131651/https://github.com/phpstan/phpstan/issues/6234 if (\is_array($note_res)) { $note_res = $eb->orX(...$note_res); } $note_parts[] = $note_res; } - if (!empty($actor_res)) { + if (!empty($actor_res)) { // @phpstan-ignore-line currently an open bug. See https://web.archive.org/web/20220226131651/https://github.com/phpstan/phpstan/issues/6234 if (\is_array($actor_res)) { $actor_res = $eb->orX(...$actor_res); } @@ -108,18 +109,18 @@ abstract class Parser } } // TODO - if (!$match) { // @phpstan-ignore-line + if (!$match) { ++$right; } } $note_criteria = null; $actor_criteria = null; - if (!empty($note_parts)) { // @phpstan-ignore-line + if (!empty($note_parts)) { self::connectParts($note_parts, $note_criteria_arr, $last_op, $eb, force: true); $note_criteria = new Criteria($eb->orX(...$note_criteria_arr)); } - if (!empty($actor_parts)) { // @phpstan-ignore-line + if (!empty($actor_parts)) { // @phpstan-ignore-line weird, but this whole thing needs a rewrite self::connectParts($actor_parts, $actor_criteria_arr, $last_op, $eb, force: true); $actor_criteria = new Criteria($eb->orX(...$actor_criteria_arr)); } diff --git a/components/Posting/Posting.php b/components/Posting/Posting.php index 6baad56b27..acd623ffb2 100644 --- a/components/Posting/Posting.php +++ b/components/Posting/Posting.php @@ -121,7 +121,7 @@ class Posting extends Component _m('Local') => VisibilityScope::LOCAL->value, _m('Addressee') => VisibilityScope::ADDRESSEE->value, ]; - if (!\is_null($context_actor) && $context_actor->isGroup()) { + if (!\is_null($context_actor) && $context_actor->isGroup()) { // @phpstan-ignore-line currently an open bug. See https://web.archive.org/web/20220226131651/https://github.com/phpstan/phpstan/issues/6234 if ($actor->canModerate($context_actor)) { if ($context_actor->getRoles() & ActorLocalRoles::PRIVATE_GROUP) { $visibility_options = array_merge([_m('Group') => VisibilityScope::GROUP->value], $visibility_options); diff --git a/plugins/AttachmentCollections/AttachmentCollections.php b/plugins/AttachmentCollections/AttachmentCollections.php index 7e187dd8d7..f3f9fa3db8 100644 --- a/plugins/AttachmentCollections/AttachmentCollections.php +++ b/plugins/AttachmentCollections/AttachmentCollections.php @@ -50,8 +50,8 @@ use Symfony\Component\HttpFoundation\Request; class AttachmentCollections extends Plugin { use MetaCollectionTrait; - protected string $slug = 'collection'; - protected string $plural_slug = 'collections'; + protected const SLUG = 'collection'; + protected const PLURAL_SLUG = 'collections'; protected function createCollection(Actor $owner, array $vars, string $name) { $col = AttachmentCollection::create([ diff --git a/plugins/Bundles/Bundles.php b/plugins/Bundles/Bundles.php index c411e1f02f..b489edfb6f 100644 --- a/plugins/Bundles/Bundles.php +++ b/plugins/Bundles/Bundles.php @@ -33,6 +33,8 @@ use Symfony\Component\HttpFoundation\Request; class Bundles extends Plugin { use MetaCollectionTrait; + protected const SLUG = 'bundle'; + protected const PLURAL_SLUG = 'bundles'; protected function createCollection(Actor $owner, array $vars, string $name) { diff --git a/plugins/OAuth2/Util/Token.php b/plugins/OAuth2/Util/Token.php index dd6005975e..479e54e1a3 100644 --- a/plugins/OAuth2/Util/Token.php +++ b/plugins/OAuth2/Util/Token.php @@ -33,14 +33,43 @@ namespace Plugin\OAuth2\Util; use App\Core\Entity; use DateTimeImmutable; +use DateTimeInterface; use Functional as F; use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; use League\OAuth2\Server\Entities\TokenInterface; use Plugin\OAuth2\Repository; +/** + * A type of token, needs to be extended. + * + * Since there's no way to specify an abstract method that returns a + * child of self, need to use method annotations + * + * @template T of self + * + * @method T setId(string $id) + * @method T setExpiry(\DateTimeInterface $expiry) + * @method T setUserId(?int $id) + * @method T setClientId(string $id) + * @method T setTokenScopes(string $scopes) + * + * From Entity: + * @method bool hasTokenScopes() + */ abstract class Token extends Entity implements TokenInterface { + abstract public function getId(): string; + // abstract public function setId(string $id): child; + abstract public function getExpiry(): DateTimeInterface; + // abstract public function setExpiry(\DateTimeInterface $expiry): child; + abstract public function getUserId(): ?int; + // abstract public function setUserId(?int $id): child; + abstract public function getClientId(): string; + // abstract public function setClientId(string $id): child; + abstract public function getTokenScopes(): string; + // abstract public function setTokenScopes(string $scopes): child; + public function getIdentifier(): string { return $this->getId(); @@ -56,7 +85,7 @@ abstract class Token extends Entity implements TokenInterface */ public function getExpiryDateTime(): DateTimeImmutable { - return $this->getExpiry(); + return DateTimeImmutable::createFromInterface($this->getExpiry()); } /** diff --git a/src/Core/Controller.php b/src/Core/Controller.php index e4be75b583..e3ea25cd5b 100644 --- a/src/Core/Controller.php +++ b/src/Core/Controller.php @@ -248,7 +248,7 @@ abstract class Controller extends AbstractController implements EventSubscriberI 'int' => $this->request->query->getInt($args[0]), 'bool' => $this->request->query->getBoolean($args[0]), 'string' => $this->request->query->get($args[0]), - default => throw new BugFoundException('Inconsistent switch/match spotted'), + default => throw new BugFoundException('Inconsistent switch/match spotted'), // @phpstan-ignore-line }; } diff --git a/src/Entity/Actor.php b/src/Entity/Actor.php index 70763a7cb6..42d39c24c7 100644 --- a/src/Entity/Actor.php +++ b/src/Entity/Actor.php @@ -56,6 +56,11 @@ use Functional as F; * @author Hugo Sales * @copyright 2020-2021 Free Software Foundation, Inc http://www.fsf.org * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later + * + * + * @method bool isPerson() + * @method bool isGroup() + * @method bool isBot() */ class Actor extends Entity { @@ -533,11 +538,6 @@ class Actor extends Entity } } - /** - * @method bool isPerson() - * @method bool isGroup() - * @method bool isBot() - */ public function __call(string $name, array $arguments): mixed { if (Formatting::startsWith($name, 'is')) { diff --git a/tests/Util/HTMLTest.php b/tests/Util/HTMLTest.php index 547c4260f3..ec4c084a2a 100644 --- a/tests/Util/HTMLTest.php +++ b/tests/Util/HTMLTest.php @@ -21,6 +21,7 @@ declare(strict_types = 1); namespace App\Tests\Util; +use App\Util\HTML; use Jchook\AssertThrows\AssertThrows; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use TypeError; @@ -31,17 +32,17 @@ class HTMLTest extends WebTestCase public function testHTML() { - static::assertSame('', HTML\HTML::html('')); - static::assertSame('', HTML\HTML::html(['a' => ''])); - static::assertSame("
\n

\n
", HTML\HTML::html(['div' => ['p' => '']])); - static::assertSame("
\n
\n

\n
\n
", HTML\HTML::html(['div' => ['div' => ['p' => '']]])); - static::assertSame("
\n
\n
\n

\n
\n
\n
", HTML\HTML::html(['div' => ['div' => ['div' => ['p' => '']]]])); - static::assertSame('

', HTML\HTML::html(['a' => ['attrs' => ['href' => 'test'], 'p' => '']])); - static::assertSame('

foo


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

foo


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

foo


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

\n
", HTML::html(['div' => ['p' => '']])); + static::assertSame("
\n
\n

\n
\n
", HTML::html(['div' => ['div' => ['p' => '']]])); + static::assertSame("
\n
\n
\n

\n
\n
\n
", HTML::html(['div' => ['div' => ['div' => ['p' => '']]]])); + static::assertSame('

', HTML::html(['a' => ['attrs' => ['href' => 'test'], 'p' => '']])); + 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::assertSame('foo', HTML::tag('a', ['href' => 'test'], content: 'foo', options: ['empty' => false])); + static::assertSame('
', HTML::tag('br', attrs: null, content: null, options: ['empty' => true])); } }