[TOOLS] Continue raising PHPStan level to 6
This commit is contained in:
parent
c31f3d4997
commit
2fd46ca886
@ -178,7 +178,7 @@ return $config
|
||||
// There MUST NOT be a space after the opening parenthesis. There MUST NOT be a space before the closing parenthesis.
|
||||
'no_spaces_inside_parenthesis' => true,
|
||||
// Removes `@param`, `@return` and `@var` tags that don't provide any useful information.
|
||||
'no_superfluous_phpdoc_tags' => true,
|
||||
'no_superfluous_phpdoc_tags' => false,
|
||||
// Remove trailing commas in list function calls.
|
||||
'no_trailing_comma_in_list_call' => true,
|
||||
// PHP single-line arrays should not have trailing comma.
|
||||
|
@ -54,7 +54,7 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
*/
|
||||
class Circle extends Component
|
||||
{
|
||||
/** @phpstan-use MetaCollectionTrait<Circle> */
|
||||
/** @phpstan-use MetaCollectionTrait<ActorCircle> */
|
||||
use MetaCollectionTrait;
|
||||
public const TAG_CIRCLE_REGEX = '/' . Nickname::BEFORE_MENTIONS . '@#([\pL\pN_\-\.]{1,64})/';
|
||||
protected const SLUG = 'circle';
|
||||
@ -228,7 +228,7 @@ class Circle extends Component
|
||||
* @param null|array<string, mixed> $vars Page vars sent by AppendRightPanelBlock event
|
||||
* @param bool $ids_only true if only the Collections ids are to be returned
|
||||
*
|
||||
* @return Circle[]|int[]
|
||||
* @return ($ids_only is true ? int[] : ActorCircle[])
|
||||
*/
|
||||
protected function getCollectionsBy(Actor $owner, ?array $vars = null, bool $ids_only = false): array
|
||||
{
|
||||
|
@ -90,14 +90,12 @@ class Circles extends MetaCollectionController
|
||||
return $this->getCollectionItems($tagger_id, $circle_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ActorCircle[]
|
||||
*/
|
||||
public function getCollectionsByActorId(int $owner_id): array
|
||||
{
|
||||
return DB::findBy(ActorCircle::class, ['tagger' => $owner_id], order_by: ['id' => 'desc']);
|
||||
}
|
||||
public function getCollectionBy(int $owner_id, int $collection_id): ActorCircle
|
||||
|
||||
public function getCollectionBy(int $owner_id, int $collection_id): self
|
||||
{
|
||||
return DB::findOneBy(ActorCircle::class, ['id' => $collection_id, 'actor_id' => $owner_id]);
|
||||
}
|
||||
|
@ -26,7 +26,8 @@ class Collection extends Component
|
||||
*
|
||||
* @param array<string, OrderByType> $note_order_by
|
||||
* @param array<string, OrderByType> $actor_order_by
|
||||
* @return array{notes: Note[], actors: Actor[]}
|
||||
*
|
||||
* @return array{notes: null|Note[], actors: null|Actor[]}
|
||||
*/
|
||||
public static function query(string $query, int $page, ?string $locale = null, ?Actor $actor = null, array $note_order_by = [], array $actor_order_by = []): array
|
||||
{
|
||||
@ -84,6 +85,9 @@ class Collection extends Component
|
||||
/**
|
||||
* Convert $term to $note_expr and $actor_expr, search criteria. Handles searching for text
|
||||
* notes, for different types of actors and for the content of text notes
|
||||
*
|
||||
* @param mixed $note_expr
|
||||
* @param mixed $actor_expr
|
||||
*/
|
||||
public function onCollectionQueryCreateExpression(ExpressionBuilder $eb, string $term, ?string $locale, ?Actor $actor, &$note_expr, &$actor_expr): EventResult
|
||||
{
|
||||
|
@ -4,6 +4,9 @@ declare(strict_types = 1);
|
||||
|
||||
namespace Component\Collection\Util\Controller;
|
||||
|
||||
/**
|
||||
* @extends OrderedCollection<\Component\Circle\Entity\ActorCircle>
|
||||
*/
|
||||
class CircleController extends OrderedCollection
|
||||
{
|
||||
}
|
||||
|
@ -6,18 +6,20 @@ namespace Component\Collection\Util\Controller;
|
||||
|
||||
use App\Core\Controller;
|
||||
use App\Entity\Actor;
|
||||
use App\Entity\Note;
|
||||
use App\Util\Common;
|
||||
use Component\Collection\Collection as CollectionComponent;
|
||||
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
class Collection extends Controller
|
||||
abstract class Collection extends Controller
|
||||
{
|
||||
/**
|
||||
* @param array<string, OrderByType> $note_order_by
|
||||
* @param array<string, OrderByType> $actor_order_by
|
||||
* @return array<T>
|
||||
*
|
||||
* @return array{notes: null|Note[], actors: null|Actor[]}
|
||||
*/
|
||||
public function query(string $query, ?string $locale = null, ?Actor $actor = null, array $note_order_by = [], array $actor_order_by = []): array
|
||||
{
|
||||
|
@ -38,6 +38,11 @@ use App\Entity\Note;
|
||||
use App\Util\Common;
|
||||
use Functional as F;
|
||||
|
||||
/**
|
||||
* @template T
|
||||
*
|
||||
* @extends OrderedCollection<T>
|
||||
*/
|
||||
abstract class FeedController extends OrderedCollection
|
||||
{
|
||||
/**
|
||||
@ -45,9 +50,11 @@ abstract class FeedController extends OrderedCollection
|
||||
* notes or actors the user specified, as well as format the raw
|
||||
* list of notes into a usable format
|
||||
*
|
||||
* @template T of Note|Actor
|
||||
* @param T[] $result
|
||||
* @return T[]
|
||||
* @template NA of Note|Actor
|
||||
*
|
||||
* @param NA[] $result
|
||||
*
|
||||
* @return NA[]
|
||||
*/
|
||||
protected function postProcess(array $result): array
|
||||
{
|
||||
|
@ -39,10 +39,13 @@ use App\Util\Common;
|
||||
use App\Util\Exception\RedirectException;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template T of object
|
||||
*
|
||||
* @extends FeedController<T>
|
||||
*/
|
||||
abstract class MetaCollectionController extends FeedController
|
||||
{
|
||||
@ -70,7 +73,7 @@ abstract class MetaCollectionController extends FeedController
|
||||
abstract public function createCollection(int $owner_id, string $name): bool;
|
||||
|
||||
/**
|
||||
* @return T[]
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function collectionsViewByActorNickname(Request $request, string $nickname): array
|
||||
{
|
||||
@ -79,7 +82,7 @@ abstract class MetaCollectionController extends FeedController
|
||||
}
|
||||
|
||||
/**
|
||||
* @return T[]
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function collectionsViewByActorId(Request $request, int $id): array
|
||||
{
|
||||
@ -135,34 +138,23 @@ abstract class MetaCollectionController extends FeedController
|
||||
// the functions and passing that class to the template.
|
||||
// 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;
|
||||
private $parent;
|
||||
private $slug;
|
||||
|
||||
public function __construct($id, $nickname, $request, $parent, $slug)
|
||||
public function __construct(private int $id, private string $nickname, private Request $request, private object $parent, private string $slug)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->nick = $nickname;
|
||||
$this->request = $request;
|
||||
$this->parent = $parent;
|
||||
$this->slug = $slug;
|
||||
}
|
||||
// there's already an injected function called path,
|
||||
// that maps to Router::url(name, args), but since
|
||||
// I want to preserve nicknames, I think it's better
|
||||
// to use that getUrl function
|
||||
public function getUrl($cid)
|
||||
public function getUrl(int $cid): string
|
||||
{
|
||||
return $this->parent->getCollectionUrl($this->id, $this->nick, $cid);
|
||||
return $this->parent->getCollectionUrl($this->id, $this->nickname, $cid);
|
||||
}
|
||||
// There are many collections in this page and we need two
|
||||
// forms for each one of them: one form to edit the collection's
|
||||
// name and another to remove the collection.
|
||||
|
||||
// creating the edit form
|
||||
public function editForm($collection)
|
||||
public function editForm(object $collection): FormView
|
||||
{
|
||||
$edit = Form::create([
|
||||
['name', TextType::class, [
|
||||
@ -181,7 +173,7 @@ abstract class MetaCollectionController extends FeedController
|
||||
]);
|
||||
$edit->handleRequest($this->request);
|
||||
if ($edit->isSubmitted() && $edit->isValid()) {
|
||||
$this->parent->setCollectionName($this->id, $this->nick, $collection, $edit->getData()['name']);
|
||||
$this->parent->setCollectionName($this->id, $this->nickname, $collection, $edit->getData()['name']);
|
||||
DB::flush();
|
||||
throw new RedirectException();
|
||||
}
|
||||
@ -189,7 +181,7 @@ abstract class MetaCollectionController extends FeedController
|
||||
}
|
||||
|
||||
// creating the remove form
|
||||
public function rmForm($collection)
|
||||
public function rmForm(object $collection): FormView
|
||||
{
|
||||
$rm = Form::create([
|
||||
['remove_' . $collection->getId(), SubmitType::class, [
|
||||
@ -202,7 +194,7 @@ abstract class MetaCollectionController extends FeedController
|
||||
]);
|
||||
$rm->handleRequest($this->request);
|
||||
if ($rm->isSubmitted()) {
|
||||
$this->parent->removeCollection($this->id, $this->nick, $collection);
|
||||
$this->parent->removeCollection($this->id, $this->nickname, $collection);
|
||||
DB::flush();
|
||||
throw new RedirectException();
|
||||
}
|
||||
@ -220,12 +212,18 @@ abstract class MetaCollectionController extends FeedController
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function collectionsEntryViewNotesByNickname(Request $request, string $nickname, int $cid): array
|
||||
{
|
||||
$user = DB::findOneBy(LocalUser::class, ['nickname' => $nickname]);
|
||||
return self::collectionsEntryViewNotesByActorId($request, $user->getId(), $cid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function collectionsEntryViewNotesByActorId(Request $request, int $id, int $cid): array
|
||||
{
|
||||
$collection = $this->getCollectionBy($id, $cid);
|
||||
|
@ -4,6 +4,11 @@ declare(strict_types = 1);
|
||||
|
||||
namespace Component\Collection\Util\Controller;
|
||||
|
||||
class OrderedCollection extends Collection
|
||||
/**
|
||||
* @template T
|
||||
*
|
||||
* @extends Collection<T>
|
||||
*/
|
||||
abstract class OrderedCollection extends Collection
|
||||
{
|
||||
}
|
||||
|
@ -82,6 +82,7 @@ trait MetaCollectionTrait
|
||||
|
||||
/**
|
||||
* Check the route to determine whether the widget should be added
|
||||
*
|
||||
* @param array<string, mixed> $vars
|
||||
*/
|
||||
abstract protected function shouldAddToRightPanel(Actor $user, array $vars, Request $request): bool;
|
||||
@ -91,7 +92,8 @@ trait MetaCollectionTrait
|
||||
* @param Actor $owner Collection's owner
|
||||
* @param null|array<string, mixed> $vars Page vars sent by AppendRightPanelBlock event
|
||||
* @param bool $ids_only if true, the function must return only the primary key or each collections
|
||||
* @return T[]|int[]
|
||||
*
|
||||
* @return int[]|T[]
|
||||
*/
|
||||
abstract protected function getCollectionsBy(Actor $owner, ?array $vars = null, bool $ids_only = false): array;
|
||||
|
||||
@ -196,7 +198,7 @@ trait MetaCollectionTrait
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[]
|
||||
* @param string[] $styles
|
||||
*/
|
||||
public function onEndShowStyles(array &$styles, string $route): EventResult
|
||||
{
|
||||
|
@ -32,6 +32,9 @@ abstract class Parser
|
||||
{
|
||||
/**
|
||||
* Merge $parts into $criteria_arr
|
||||
*
|
||||
* @param mixed[] $parts
|
||||
* @param Criteria[] $criteria_arr
|
||||
*/
|
||||
private static function connectParts(array &$parts, array &$criteria_arr, string $last_op, mixed $eb, bool $force = false): void
|
||||
{
|
||||
|
@ -46,6 +46,9 @@ use Component\Conversation\Entity\ConversationMute;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* @extends FeedController<\App\Entity\Note>
|
||||
*/
|
||||
class Conversation extends FeedController
|
||||
{
|
||||
/**
|
||||
@ -55,7 +58,10 @@ class Conversation extends FeedController
|
||||
*
|
||||
* @throws \App\Util\Exception\ServerException
|
||||
*
|
||||
* @return array Array containing keys: 'notes' (all known notes in the given Conversation), 'should_format' (boolean, stating if onFormatNoteList events may or not format given notes), 'page_title' (used as the title header)
|
||||
* @return ControllerResultType Array containing keys: 'notes' (all known
|
||||
* notes in the given Conversation), 'should_format' (boolean, stating if
|
||||
* onFormatNoteList events may or not format given notes), 'page_title'
|
||||
* (used as the title header)
|
||||
*/
|
||||
public function showConversation(Request $request, int $conversation_id): array
|
||||
{
|
||||
@ -83,7 +89,7 @@ class Conversation extends FeedController
|
||||
* @throws NoSuchNoteException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return array
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function addReply(Request $request)
|
||||
{
|
||||
@ -103,7 +109,7 @@ class Conversation extends FeedController
|
||||
* @throws \App\Util\Exception\RedirectException
|
||||
* @throws \App\Util\Exception\ServerException
|
||||
*
|
||||
* @return array Array containing templating where the form is to be rendered, and the form itself
|
||||
* @return ControllerResultType Array containing templating where the form is to be rendered, and the form itself
|
||||
*/
|
||||
public function muteConversation(Request $request, int $conversation_id)
|
||||
{
|
||||
|
@ -96,10 +96,12 @@ class Conversation extends Component
|
||||
* action, if a user is logged in.
|
||||
*
|
||||
* @param \App\Entity\Note $note The Note being rendered
|
||||
* @param array $actions Contains keys 'url' (linking 'conversation_reply_to'
|
||||
* route), 'title' (used as title for aforementioned url),
|
||||
* 'classes' (CSS styling classes used to visually inform the user of action context),
|
||||
* 'id' (HTML markup id used to redirect user to this anchor upon performing the action)
|
||||
* @param array{url: string, title: string, classes: string, id: string} $actions
|
||||
* Contains keys 'url' (linking 'conversation_reply_to' route),
|
||||
* 'title' (used as title for aforementioned url), 'classes' (CSS styling
|
||||
* classes used to visually inform the user of action context), 'id' (HTML
|
||||
* markup id used to redirect user to this anchor upon performing the
|
||||
* action)
|
||||
*
|
||||
* @throws \App\Util\Exception\ServerException
|
||||
*/
|
||||
@ -138,8 +140,11 @@ class Conversation extends Component
|
||||
/**
|
||||
* Append on note information about user actions.
|
||||
*
|
||||
* @param array $vars Contains information related to Note currently being rendered
|
||||
* @param array $result Contains keys 'actors', and 'action'. Needed to construct a string, stating who ($result['actors']), has already performed a reply ($result['action']), in the given Note (vars['note'])
|
||||
* @param array<string, mixed> $vars Contains information related to Note currently being rendered
|
||||
* @param array{actors: Actor[], action: string} $result
|
||||
*cContains keys 'actors', and 'action'. Needed to construct a string,
|
||||
* stating who ($result['actors']), has already performed a reply
|
||||
* ($result['action']), in the given Note (vars['note'])
|
||||
*/
|
||||
public function onAppendCardNote(array $vars, array &$result): EventResult
|
||||
{
|
||||
@ -206,7 +211,7 @@ class Conversation extends Component
|
||||
/**
|
||||
* Posting event to add extra information to Component\Posting form data
|
||||
*
|
||||
* @param array $data Transport data to be filled with reply_to_id
|
||||
* @param array{reply_to_id: int} $data Transport data to be filled with reply_to_id
|
||||
*
|
||||
* @throws \App\Util\Exception\ClientException
|
||||
* @throws \App\Util\Exception\NoSuchNoteException
|
||||
@ -224,6 +229,8 @@ class Conversation extends Component
|
||||
|
||||
/**
|
||||
* Add minimal Note card to RightPanel template
|
||||
*
|
||||
* @param string[] $elements
|
||||
*/
|
||||
public function onPrependPostingForm(Request $request, array &$elements): EventResult
|
||||
{
|
||||
@ -251,7 +258,9 @@ class Conversation extends Component
|
||||
* Adds extra actions related to Conversation Component, that act upon/from the given Note.
|
||||
*
|
||||
* @param \App\Entity\Note $note Current Note being rendered
|
||||
* @param array $actions Containing 'url' (Controller connected route), 'title' (used in anchor link containing the url), ?'classes' (CSS classes required for styling, if needed)
|
||||
* @param array{url: string, title: string, classes?: string} $actions Containing 'url' (Controller connected
|
||||
* route), 'title' (used in anchor link containing the url), ?'classes' (CSS classes required for styling, if
|
||||
* needed)
|
||||
*
|
||||
* @throws \App\Util\Exception\ServerException
|
||||
*/
|
||||
|
@ -41,10 +41,15 @@ use App\Util\HTML\Heading;
|
||||
use Component\Collection\Util\Controller\FeedController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* @extends FeedController<\App\Entity\Note>
|
||||
*/
|
||||
class Feeds extends FeedController
|
||||
{
|
||||
/**
|
||||
* The Planet feed represents every local post. Which is what this instance has to share with the universe.
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function public(Request $request): array
|
||||
{
|
||||
@ -60,6 +65,8 @@ class Feeds extends FeedController
|
||||
|
||||
/**
|
||||
* The Home feed represents everything that concerns a certain actor (its subscriptions)
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function home(Request $request): array
|
||||
{
|
||||
|
@ -244,6 +244,8 @@ class FreeNetwork extends Component
|
||||
|
||||
/**
|
||||
* Add a link header for LRDD Discovery
|
||||
*
|
||||
* @param mixed $action
|
||||
*/
|
||||
public function onStartShowHTML($action): EventResult
|
||||
{
|
||||
@ -344,6 +346,7 @@ class FreeNetwork extends Component
|
||||
* @param string $preMention Character(s) that signals a mention ('@', '!'...)
|
||||
*
|
||||
* @return array the matching URLs (without @ or acct:) and each respective position in the given string
|
||||
*
|
||||
* @example.com/mublog/user
|
||||
*/
|
||||
public static function extractUrlMentions(string $text, string $preMention = '@'): array
|
||||
@ -375,6 +378,7 @@ class FreeNetwork extends Component
|
||||
* @param $mentions
|
||||
*
|
||||
* @return bool hook return value
|
||||
*
|
||||
* @example.com/mublog/user
|
||||
*/
|
||||
public function onEndFindMentions(Actor $sender, string $text, array &$mentions): EventResult
|
||||
@ -496,6 +500,9 @@ class FreeNetwork extends Component
|
||||
return Event::next;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Actor[] $targets
|
||||
*/
|
||||
public static function notify(Actor $sender, Activity $activity, array $targets, ?string $reason = null): bool
|
||||
{
|
||||
foreach (self::$protocols as $protocol) {
|
||||
@ -517,6 +524,9 @@ class FreeNetwork extends Component
|
||||
/**
|
||||
* Add fediverse: query expression
|
||||
* // TODO: adding WebFinger would probably be nice
|
||||
*
|
||||
* @param mixed $note_expr
|
||||
* @param mixed $actor_expr
|
||||
*/
|
||||
public function onCollectionQueryCreateExpression(ExpressionBuilder $eb, string $term, ?string $locale, ?Actor $actor, &$note_expr, &$actor_expr): EventResult
|
||||
{
|
||||
|
@ -61,6 +61,8 @@ class Group extends Controller
|
||||
*
|
||||
* @throws RedirectException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function groupCreate(Request $request): array
|
||||
{
|
||||
@ -89,6 +91,8 @@ class Group extends Controller
|
||||
* @throws NicknameTooLongException
|
||||
* @throws NotFoundException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function groupSettings(Request $request, int $id): array
|
||||
{
|
||||
|
@ -40,10 +40,15 @@ use Component\Subscription\Entity\ActorSubscription;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* @extends FeedController<\App\Entity\Note>
|
||||
*/
|
||||
class GroupFeed extends FeedController
|
||||
{
|
||||
/**
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function groupView(Request $request, Actor $group): array
|
||||
{
|
||||
@ -96,6 +101,8 @@ class GroupFeed extends FeedController
|
||||
/**
|
||||
* @throws ClientException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function groupViewId(Request $request, int $id): array
|
||||
{
|
||||
@ -119,6 +126,8 @@ class GroupFeed extends FeedController
|
||||
*
|
||||
* @throws ClientException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function groupViewNickname(Request $request, string $nickname): array
|
||||
{
|
||||
|
@ -50,6 +50,8 @@ class Group extends Component
|
||||
/**
|
||||
* Enqueues a notification for an Actor (such as person or group) which means
|
||||
* it shows up in their home feed and such.
|
||||
*
|
||||
* @param Actor[] $targets
|
||||
*/
|
||||
public function onNewNotificationStart(Actor $sender, Activity $activity, array $targets = [], ?string $reason = null): EventResult
|
||||
{
|
||||
@ -70,6 +72,9 @@ class Group extends Component
|
||||
|
||||
/**
|
||||
* Add an <a href=group_actor_settings> to the profile card for groups, if the current actor can access them
|
||||
*
|
||||
* @param array<string, mixed> $vars
|
||||
* @param string[] $res
|
||||
*/
|
||||
public function onAppendCardProfile(array $vars, array &$res): EventResult
|
||||
{
|
||||
@ -109,6 +114,9 @@ class Group extends Component
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Actor[] $targets
|
||||
*/
|
||||
public function onPostingFillTargetChoices(Request $request, Actor $actor, array &$targets): EventResult
|
||||
{
|
||||
$group = $this->getGroupFromContext($request);
|
||||
|
@ -100,6 +100,8 @@ class Language extends Controller
|
||||
* @throws NoLoggedInUser
|
||||
* @throws RedirectException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function sortLanguages(Request $request): array
|
||||
{
|
||||
|
@ -119,6 +119,9 @@ class ActorLanguage extends Entity
|
||||
) ?: [Language::getByLocale(Common::config('site', 'language'))];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public static function getActorRelatedLanguagesIds(Actor $actor): array
|
||||
{
|
||||
return Cache::getList(
|
||||
|
@ -134,6 +134,9 @@ class Language extends Entity
|
||||
return self::getById($note->getLanguageId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public static function getLanguageChoices(): array
|
||||
{
|
||||
$langs = Cache::getHashMap(
|
||||
@ -144,6 +147,8 @@ class Language extends Entity
|
||||
return array_merge(...F\map(array_values($langs), fn ($l) => $l->toChoiceFormat()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string> */
|
||||
public function toChoiceFormat(): array
|
||||
{
|
||||
return [_m($this->getLongDisplay()) => $this->getLocale()];
|
||||
@ -152,6 +157,8 @@ class Language extends Entity
|
||||
/**
|
||||
* Get all the available languages as well as the languages $actor
|
||||
* prefers and are appropriate for posting in/to $context_actor
|
||||
*
|
||||
* @return array{array<string, string>, array<string, string>}
|
||||
*/
|
||||
public static function getSortedLanguageChoices(?Actor $actor, ?Actor $context_actor, ?bool $use_short_display): array
|
||||
{
|
||||
|
@ -45,6 +45,9 @@ class Language extends Component
|
||||
return Event::next;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Note[] $notes
|
||||
*/
|
||||
public function onFilterNoteList(?Actor $actor, array &$notes, Request $request): EventResult
|
||||
{
|
||||
if (\is_null($actor)) {
|
||||
@ -60,6 +63,9 @@ class Language extends Component
|
||||
|
||||
/**
|
||||
* Populate $note_expr or $actor_expr with an expression to match a language
|
||||
*
|
||||
* @param mixed $note_expr
|
||||
* @param mixed $actor_expr
|
||||
*/
|
||||
public function onCollectionQueryCreateExpression(ExpressionBuilder $eb, string $term, ?string $locale, ?Actor $actor, &$note_expr, &$actor_expr): EventResult
|
||||
{
|
||||
|
@ -43,6 +43,8 @@ class LeftPanel extends Component
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $route_params
|
||||
*
|
||||
* @throws \App\Util\Exception\DuplicateFoundException
|
||||
* @throws \App\Util\Exception\ServerException
|
||||
* @throws ClientException
|
||||
|
@ -84,6 +84,8 @@ class NoteToLink extends Entity
|
||||
* Create an instance of NoteToLink or fill in the
|
||||
* properties of $obj with the associative array $args. Doesn't
|
||||
* persist the result
|
||||
*
|
||||
* @param (array{link_id: int, note_id: int} & array<string, mixed>) $args
|
||||
*/
|
||||
public static function create(array $args, bool $_delegated_call = false): static
|
||||
{
|
||||
|
@ -54,6 +54,8 @@ class Link extends Component
|
||||
|
||||
/**
|
||||
* Extract URLs from $content and create the appropriate Link and NoteToLink entities
|
||||
*
|
||||
* @param array{ignoreLinks?: string[]} $process_note_content_extra_args
|
||||
*/
|
||||
public function onProcessNoteContent(Note $note, string $content, string $content_type, array $process_note_content_extra_args = []): EventResult
|
||||
{
|
||||
@ -150,7 +152,12 @@ class Link extends Component
|
||||
public const URL_SCHEME_NO_DOMAIN = 4;
|
||||
public const URL_SCHEME_COLON_COORDINATES = 8;
|
||||
|
||||
public function URLSchemes($filter = null)
|
||||
/**
|
||||
* @param self::URL_SCHEME_COLON_COORDINATES|self::URL_SCHEME_COLON_DOUBLE_SLASH|self::URL_SCHEME_NO_DOMAIN|self::URL_SCHEME_SINGLE_COLON $filter
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function URLSchemes(?int $filter = null): array
|
||||
{
|
||||
// TODO: move these to config
|
||||
$schemes = [
|
||||
@ -197,6 +204,7 @@ class Link extends Component
|
||||
* Intermediate callback for `replaceURLs()`, which helps resolve some
|
||||
* ambiguous link forms before passing on to the final callback.
|
||||
*
|
||||
* @param string[] $matches
|
||||
* @param callable(string $text): string $callback: return replacement text
|
||||
*/
|
||||
private function callbackHelper(array $matches, callable $callback): string
|
||||
|
@ -44,6 +44,8 @@ class Feed extends Controller
|
||||
{
|
||||
/**
|
||||
* Everything with attention to current user
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function notifications(Request $request): array
|
||||
{
|
||||
|
@ -117,7 +117,7 @@ class Notification extends Entity
|
||||
/**
|
||||
* Pull the complete list of known activity context notifications for this activity.
|
||||
*
|
||||
* @return array of integer actor ids (also group profiles)
|
||||
* @return int[] actor ids (also group profiles)
|
||||
*/
|
||||
public static function getNotificationTargetIdsByActivity(int|Activity $activity_id): array
|
||||
{
|
||||
@ -129,11 +129,17 @@ class Notification extends Entity
|
||||
return $targets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getNotificationTargetsByActivity(int|Activity $activity_id): array
|
||||
{
|
||||
return DB::findBy(Actor::class, ['id' => $this->getNotificationTargetIdsByActivity($activity_id)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public static function getAllActivitiesTargetedAtActor(Actor $actor): array
|
||||
{
|
||||
return DB::dql(<<<'EOF'
|
||||
|
@ -73,7 +73,7 @@ class Notification extends Component
|
||||
*
|
||||
* @param Actor $sender The one responsible for this activity, take care not to include it in targets
|
||||
* @param Activity $activity The activity responsible for the object being given to known to targets
|
||||
* @param array $targets Attentions, Mentions, any other source. Should never be empty, you usually want to register an attention to every $sender->getSubscribers()
|
||||
* @param non-empty-array<Actor|int> $targets Attentions, Mentions, any other source. Should never be empty, you usually want to register an attention to every $sender->getSubscribers()
|
||||
* @param null|string $reason An optional reason explaining why this notification exists
|
||||
*/
|
||||
public function onNewNotification(Actor $sender, Activity $activity, array $targets, ?string $reason = null): EventResult
|
||||
@ -103,12 +103,19 @@ class Notification extends Component
|
||||
return Event::next;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $retry_args
|
||||
*/
|
||||
public function onQueueNotificationLocal(Actor $sender, Activity $activity, Actor $target, ?string $reason, array &$retry_args): EventResult
|
||||
{
|
||||
// TODO: use https://symfony.com/doc/current/notifier.html
|
||||
return Event::stop;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Actor[] $targets
|
||||
* @param mixed[] $retry_args
|
||||
*/
|
||||
public function onQueueNotificationRemote(Actor $sender, Activity $activity, array $targets, ?string $reason, array &$retry_args): EventResult
|
||||
{
|
||||
if (FreeNetwork::notify($sender, $activity, $targets, $reason)) {
|
||||
@ -122,7 +129,9 @@ class Notification extends Component
|
||||
* Bring given Activity to Targets' knowledge.
|
||||
* This will flush a Notification to DB.
|
||||
*
|
||||
* @return true if successful, false otherwise
|
||||
* @param Actor[] $targets
|
||||
*
|
||||
* @return bool true if successful, false otherwise
|
||||
*/
|
||||
public static function notify(Actor $sender, Activity $activity, array $targets, ?string $reason = null): bool
|
||||
{
|
||||
|
@ -34,11 +34,16 @@ use App\Util\HTML\Heading;
|
||||
use Component\Collection\Util\Controller\FeedController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* @extends FeedController<\App\Entity\Note>
|
||||
*/
|
||||
class PersonFeed extends FeedController
|
||||
{
|
||||
/**
|
||||
* @throws ClientException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function personViewId(Request $request, int $id): array
|
||||
{
|
||||
@ -62,6 +67,8 @@ class PersonFeed extends FeedController
|
||||
*
|
||||
* @throws ClientException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function personViewNickname(Request $request, string $nickname): array
|
||||
{
|
||||
@ -73,6 +80,9 @@ class PersonFeed extends FeedController
|
||||
return $this->personView($request, $person);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function personView(Request $request, Actor $person): array
|
||||
{
|
||||
return [
|
||||
|
@ -88,6 +88,8 @@ class PersonSettings extends Controller
|
||||
* @throws NoLoggedInUser
|
||||
* @throws RedirectException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function allSettings(Request $request, LanguageController $language): array
|
||||
{
|
||||
@ -205,6 +207,8 @@ class PersonSettings extends Controller
|
||||
* @throws \Doctrine\DBAL\Exception
|
||||
* @throws NoLoggedInUser
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return ControllerResultType[]
|
||||
*/
|
||||
private static function notifications(Request $request): array
|
||||
{
|
||||
|
@ -44,11 +44,13 @@ use App\Util\Exception\ServerException;
|
||||
use App\Util\Formatting;
|
||||
use App\Util\HTML;
|
||||
use Component\Attachment\Entity\ActorToAttachment;
|
||||
use Component\Attachment\Entity\Attachment;
|
||||
use Component\Attachment\Entity\AttachmentToNote;
|
||||
use Component\Conversation\Conversation;
|
||||
use Component\Language\Entity\Language;
|
||||
use Component\Notification\Entity\Attention;
|
||||
use EventResult;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
@ -66,6 +68,8 @@ class Posting extends Component
|
||||
* HTML render event handler responsible for adding and handling
|
||||
* the result of adding the note submission form, only if a user is logged in
|
||||
*
|
||||
* @param array{post_form?: FormInterface} $res
|
||||
*
|
||||
* @throws BugFoundException
|
||||
* @throws ClientException
|
||||
* @throws DuplicateFoundException
|
||||
@ -84,9 +88,25 @@ class Posting extends Component
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Actor $actor The Actor responsible for the creation of this Note
|
||||
* @param null|string $content The raw text content
|
||||
* @param string $content_type Indicating one of the various supported content format (Plain Text, Markdown, LaTeX...)
|
||||
* @param null|string $locale Note's written text language, set by the default Actor language or upon filling
|
||||
* @param null|VisibilityScope $scope The visibility of this Note
|
||||
* @param Actor[]|int[] $attentions Actor|int[]: In Group/To Person or Bot, registers an attention between note and target
|
||||
* @param null|int|Note $reply_to The soon-to-be Note parent's id, if it's a Reply itself
|
||||
* @param UploadedFile[] $attachments UploadedFile[] to be stored as GSFiles associated to this note
|
||||
* @param array<array{Attachment, string}> $processed_attachments Array of [Attachment, Attachment's name][] to be associated to this $actor and Note
|
||||
* @param array{note?: Note, content?: string, content_type?: string, extra_args?: array<string, mixed>} $process_note_content_extra_args Extra arguments for the event ProcessNoteContent
|
||||
* @param bool $flush_and_notify True if the newly created Note activity should be passed on as a Notification
|
||||
* @param null|string $rendered The Note's content post RenderNoteContent event, which sanitizes and processes the raw content sent
|
||||
* @param string $source The source of this Note
|
||||
*
|
||||
* @throws ClientException
|
||||
* @throws DuplicateFoundException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return array{\App\Entity\Activity, \App\Entity\Note, array<int, \App\Entity\Actor>}
|
||||
*/
|
||||
public static function storeLocalArticle(
|
||||
Actor $actor,
|
||||
@ -149,11 +169,11 @@ class Posting extends Component
|
||||
* @param string $content_type Indicating one of the various supported content format (Plain Text, Markdown, LaTeX...)
|
||||
* @param null|string $locale Note's written text language, set by the default Actor language or upon filling
|
||||
* @param null|VisibilityScope $scope The visibility of this Note
|
||||
* @param array $attentions Actor|int[]: In Group/To Person or Bot, registers an attention between note and target
|
||||
* @param Actor[]|int[] $attentions Actor|int[]: In Group/To Person or Bot, registers an attention between note and targte
|
||||
* @param null|int|Note $reply_to The soon-to-be Note parent's id, if it's a Reply itself
|
||||
* @param array $attachments UploadedFile[] to be stored as GSFiles associated to this note
|
||||
* @param array $processed_attachments Array of [Attachment, Attachment's name][] to be associated to this $actor and Note
|
||||
* @param array $process_note_content_extra_args Extra arguments for the event ProcessNoteContent
|
||||
* @param UploadedFile[] $attachments UploadedFile[] to be stored as GSFiles associated to this note
|
||||
* @param array<array{Attachment, string}> $processed_attachments Array of [Attachment, Attachment's name][] to be associated to this $actor and Note
|
||||
* @param array{note?: Note, content?: string, content_type?: string, extra_args?: array<string, mixed>} $process_note_content_extra_args Extra arguments for the event ProcessNoteContent
|
||||
* @param bool $flush_and_notify True if the newly created Note activity should be passed on as a Notification
|
||||
* @param null|string $rendered The Note's content post RenderNoteContent event, which sanitizes and processes the raw content sent
|
||||
* @param string $source The source of this Note
|
||||
@ -162,7 +182,7 @@ class Posting extends Component
|
||||
* @throws DuplicateFoundException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return array [Activity, Note, Effective Attentions]
|
||||
* @return array{\App\Entity\Activity, \App\Entity\Note, array<int, \App\Entity\Actor>}
|
||||
*/
|
||||
public static function storeLocalNote(
|
||||
Actor $actor,
|
||||
@ -302,6 +322,9 @@ class Posting extends Component
|
||||
return [$activity, $note, $effective_attentions];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, \App\Entity\Actor> $mentions
|
||||
*/
|
||||
public function onRenderNoteContent(string $content, string $content_type, ?string &$rendered, Actor $author, ?string $language = null, array &$mentions = []): EventResult
|
||||
{
|
||||
switch ($content_type) {
|
||||
|
@ -38,12 +38,17 @@ use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* @extends FeedController<\App\Entity\Note>
|
||||
*/
|
||||
class Search extends FeedController
|
||||
{
|
||||
/**
|
||||
* Handle a search query
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function handle(Request $request)
|
||||
public function handle(Request $request): array
|
||||
{
|
||||
$actor = Common::actor();
|
||||
$language = !\is_null($actor) ? $actor->getTopLanguage()->getLocale() : null;
|
||||
|
@ -134,6 +134,8 @@ class Search extends Component
|
||||
/**
|
||||
* Add the search form to the site header
|
||||
*
|
||||
* @param string[] $elements
|
||||
*
|
||||
* @throws RedirectException
|
||||
*/
|
||||
public function onPrependRightPanelBlock(Request $request, array &$elements): EventResult
|
||||
@ -145,7 +147,7 @@ class Search extends Component
|
||||
/**
|
||||
* Output our dedicated stylesheet
|
||||
*
|
||||
* @param array $styles stylesheets path
|
||||
* @param string[] $styles stylesheets path
|
||||
*/
|
||||
public function onEndShowStyles(array &$styles, string $route): EventResult
|
||||
{
|
||||
|
@ -45,6 +45,8 @@ class Subscribers extends CircleController
|
||||
{
|
||||
/**
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function subscribersByActor(Request $request, Actor $actor): array
|
||||
{
|
||||
@ -61,6 +63,8 @@ class Subscribers extends CircleController
|
||||
/**
|
||||
* @throws ClientException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function subscribersByActorId(Request $request, int $id): array
|
||||
{
|
||||
@ -78,6 +82,8 @@ class Subscribers extends CircleController
|
||||
* @throws \App\Util\Exception\ServerException
|
||||
* @throws ClientException
|
||||
* @throws RedirectException
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function subscribersAdd(Request $request, int $object_id): array
|
||||
{
|
||||
@ -126,6 +132,8 @@ class Subscribers extends CircleController
|
||||
* @throws \App\Util\Exception\ServerException
|
||||
* @throws ClientException
|
||||
* @throws RedirectException
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function subscribersRemove(Request $request, int $object_id): array
|
||||
{
|
||||
|
@ -38,6 +38,8 @@ class Subscriptions extends CircleController
|
||||
/**
|
||||
* @throws ClientException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function subscriptionsByActorId(Request $request, int $id): array
|
||||
{
|
||||
@ -48,7 +50,10 @@ class Subscriptions extends CircleController
|
||||
return $this->subscriptionsByActor($request, $actor);
|
||||
}
|
||||
|
||||
public function subscriptionsByActor(Request $request, Actor $actor)
|
||||
/**
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function subscriptionsByActor(Request $request, Actor $actor): array
|
||||
{
|
||||
return [
|
||||
'_template' => 'collection/actors.html.twig',
|
||||
|
@ -58,6 +58,8 @@ class Subscription extends Component
|
||||
*
|
||||
* @param Actor|int|LocalUser $subject The Actor who subscribed or unsubscribed
|
||||
* @param Actor|int|LocalUser $object The Actor who was subscribed or unsubscribed from
|
||||
*
|
||||
* @return array{bool, bool}
|
||||
*/
|
||||
public static function refreshSubscriptionCount(int|Actor|LocalUser $subject, int|Actor|LocalUser $object): array
|
||||
{
|
||||
@ -178,7 +180,8 @@ class Subscription extends Component
|
||||
* **unsubscribe** a given **Actor**.
|
||||
*
|
||||
* @param Actor $object The Actor on which the action is to be performed
|
||||
* @param array $actions An array containing all actions added to the
|
||||
* @param array<array{url: string, title: string, classes: string, id: string}> $actions
|
||||
* An array containing all actions added to the
|
||||
* current profile, this event adds an action to it
|
||||
*
|
||||
* @throws DuplicateFoundException
|
||||
|
@ -15,7 +15,12 @@ class Tag extends Controller
|
||||
// TODO: Use Feed::query
|
||||
// TODO: If ?canonical=something, respect
|
||||
// TODO: Allow to set locale of tag being selected
|
||||
private function process(null|string|array $tag_single_or_multi, string $key, string $query, string $template, bool $include_locale = false)
|
||||
/**
|
||||
* @param (null|string|string[]) $tag_single_or_multi
|
||||
*
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
private function process(null|string|array $tag_single_or_multi, string $key, string $query, string $template, bool $include_locale = false): array
|
||||
{
|
||||
$actor = Common::actor();
|
||||
$page = $this->int('page') ?: 1;
|
||||
@ -46,7 +51,10 @@ class Tag extends Controller
|
||||
];
|
||||
}
|
||||
|
||||
public function single_note_tag(string $tag)
|
||||
/**
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function single_note_tag(string $tag): array
|
||||
{
|
||||
return $this->process(
|
||||
tag_single_or_multi: $tag,
|
||||
@ -57,7 +65,10 @@ class Tag extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
public function multi_note_tags(string $tags)
|
||||
/**
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public function multi_note_tags(string $tags): array
|
||||
{
|
||||
return $this->process(
|
||||
tag_single_or_multi: explode(',', $tags),
|
||||
|
@ -134,6 +134,9 @@ class NoteTag extends Entity
|
||||
return "note-tags-{$note_id}";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NoteTag[]
|
||||
*/
|
||||
public static function getByNoteId(int $note_id): array
|
||||
{
|
||||
return Cache::getList(self::cacheKey($note_id), fn () => DB::dql('SELECT nt FROM note_tag AS nt JOIN note AS n WITH n.id = nt.note_id WHERE n.id = :id', ['id' => $note_id]));
|
||||
|
@ -119,6 +119,8 @@ class NoteTagBlock extends Entity
|
||||
/**
|
||||
* Check whether $note_tag is considered blocked by one of
|
||||
* $note_tag_blocks
|
||||
*
|
||||
* @param NoteTagBlock[] $note_tag_blocks
|
||||
*/
|
||||
public static function checkBlocksNoteTag(NoteTag $note_tag, array $note_tag_blocks): bool
|
||||
{
|
||||
|
@ -61,13 +61,16 @@ class Tag extends Component
|
||||
public const TAG_REGEX = '/(^|\\s)(#[\\pL\\pN_\\-]{1,64})/u'; // Brion Vibber 2011-02-23 v2:classes/Notice.php:367 function saveTags
|
||||
public const TAG_SLUG_REGEX = '[A-Za-z0-9]{1,64}';
|
||||
|
||||
public function onAddRoute($r): EventResult
|
||||
public function onAddRoute(Router $r): EventResult
|
||||
{
|
||||
$r->connect('single_note_tag', '/note-tag/{tag<' . self::TAG_SLUG_REGEX . '>}', [Controller\Tag::class, 'single_note_tag']);
|
||||
$r->connect('multi_note_tags', '/note-tags/{tags<(' . self::TAG_SLUG_REGEX . ',)+' . self::TAG_SLUG_REGEX . '>}', [Controller\Tag::class, 'multi_note_tags']);
|
||||
return Event::next;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{tag_use_canonical?: bool} $extra_args
|
||||
*/
|
||||
public static function maybeCreateTag(string $tag, int $note_id, ?int $lang_id, array $extra_args = []): ?NoteTag
|
||||
{
|
||||
if (!self::validate($tag)) {
|
||||
@ -118,6 +121,8 @@ class Tag extends Component
|
||||
|
||||
/**
|
||||
* Process note by extracting any tags present
|
||||
*
|
||||
* @param array{TagProcessed?: bool} $extra_args
|
||||
*/
|
||||
public function onProcessNoteContent(Note $note, string $content, string $content_type, array $extra_args): EventResult
|
||||
{
|
||||
@ -213,6 +218,9 @@ class Tag extends Component
|
||||
* Populate $note_expr with an expression to match a tag, if the term looks like a tag
|
||||
*
|
||||
* $term /^(note|tag|people|actor)/ means we want to match only either a note or an actor
|
||||
*
|
||||
* @param mixed $note_expr
|
||||
* @param mixed $actor_expr
|
||||
*/
|
||||
public function onCollectionQueryCreateExpression(ExpressionBuilder $eb, string $term, ?string $locale, ?Actor $actor, &$note_expr, &$actor_expr): EventResult
|
||||
{
|
||||
@ -264,12 +272,19 @@ class Tag extends Component
|
||||
return Event::next;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{string, class-string, array<string, mixed>} $form_params
|
||||
*/
|
||||
public function onPostingAddFormEntries(Request $request, Actor $actor, array &$form_params): EventResult
|
||||
{
|
||||
$form_params[] = ['tag_use_canonical', CheckboxType::class, ['required' => false, 'data' => true, 'label' => _m('Make note tags canonical'), 'help' => _m('Canonical tags will be treated as a version of an existing tag with the same root/stem (e.g. \'#great_tag\' will be considered as a version of \'#great\', if it already exists)')]];
|
||||
return Event::next;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{tag_use_canonical?: bool} $data
|
||||
* @param array{tag_use_canonical?: bool} $extra_args
|
||||
*/
|
||||
public function onAddExtraArgsToNoteContent(Request $request, Actor $actor, array $data, array &$extra_args): EventResult
|
||||
{
|
||||
if (!isset($data['tag_use_canonical'])) {
|
||||
|
46
phpstan.neon
46
phpstan.neon
@ -1,5 +1,7 @@
|
||||
parameters:
|
||||
level: 5
|
||||
level: 6
|
||||
tmpDir: /var/www/social/var/cache/phpstan
|
||||
inferPrivatePropertyTypeFromConstructor: true
|
||||
bootstrapFiles:
|
||||
- config/bootstrap.php
|
||||
paths:
|
||||
@ -16,10 +18,11 @@ parameters:
|
||||
App\Core\Log:
|
||||
- unexpected_exception
|
||||
typeAliases:
|
||||
ControllerResultType: 'array{_template: string} & array<string, mixed>'
|
||||
ControllerResultType: '(array{_template: string} | array{_redirect: string}) & array<string, mixed>'
|
||||
CacheKeysType: 'array<string, string>'
|
||||
SettingsTabsType: 'array<array{title: string, desc: string, id: string, controller: ControllerResultType}>'
|
||||
OrderByType: "'ASC' | 'DESC' | 'asc' | 'desc'"
|
||||
ModuleVersionType: 'array{name: string, version: string, author: string, rawdescription: string}'
|
||||
|
||||
ignoreErrors:
|
||||
-
|
||||
@ -41,10 +44,41 @@ parameters:
|
||||
paths:
|
||||
- *
|
||||
|
||||
# -
|
||||
# message: '/has no return typehint specified/'
|
||||
# paths:
|
||||
# - tests/*
|
||||
-
|
||||
message: '/::onCollectionQueryCreateExpression\(\) has parameter \$(actor|note)_expr with no type specified\./'
|
||||
paths:
|
||||
- *
|
||||
|
||||
-
|
||||
message: '/::schemaDef\(\) return type has no value type specified in iterable type array\./'
|
||||
paths:
|
||||
- *
|
||||
|
||||
-
|
||||
message: '/has no return type specified\./'
|
||||
paths:
|
||||
- *
|
||||
|
||||
-
|
||||
message: '/with no value type specified in iterable type (array|iterable)\.|type has no value type specified in iterable type (array|iterable)\./'
|
||||
paths:
|
||||
- *
|
||||
|
||||
-
|
||||
message: '/never returns array{_redirect: string} so it can be removed from the return type\./'
|
||||
paths:
|
||||
- *
|
||||
|
||||
-
|
||||
message: '/but returns array<string, array<int, mixed>\|string>\./'
|
||||
paths:
|
||||
- plugins/AttachmentCollections/Controller/AttachmentCollections.php
|
||||
- plugins/Bundles/Controller/BundleCollection.php
|
||||
|
||||
-
|
||||
message: '/has parameter \$.+ with no type specified./'
|
||||
paths:
|
||||
- tests/*
|
||||
|
||||
services:
|
||||
-
|
||||
|
@ -49,9 +49,14 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class AttachmentCollections extends Plugin
|
||||
{
|
||||
/** @phpstan-use MetaCollectionTrait<AttachmentCollection> */
|
||||
use MetaCollectionTrait;
|
||||
protected const SLUG = 'collection';
|
||||
protected const PLURAL_SLUG = 'collections';
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $vars
|
||||
*/
|
||||
protected function createCollection(Actor $owner, array $vars, string $name): void
|
||||
{
|
||||
$col = AttachmentCollection::create([
|
||||
@ -65,6 +70,12 @@ class AttachmentCollections extends Plugin
|
||||
'attachment_collection_id' => $col->getId(),
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $vars
|
||||
* @param int[] $items
|
||||
* @param array<string, mixed> $collections
|
||||
*/
|
||||
protected function removeItem(Actor $owner, array $vars, array $items, array $collections): bool
|
||||
{
|
||||
return DB::dql(<<<'EOF'
|
||||
@ -83,6 +94,11 @@ class AttachmentCollections extends Plugin
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $vars
|
||||
* @param int[] $items
|
||||
* @param array<string, mixed> $collections
|
||||
*/
|
||||
protected function addItem(Actor $owner, array $vars, array $items, array $collections): void
|
||||
{
|
||||
foreach ($items as $id) {
|
||||
@ -97,14 +113,18 @@ class AttachmentCollections extends Plugin
|
||||
}
|
||||
}
|
||||
|
||||
protected function shouldAddToRightPanel(Actor $user, $vars, Request $request): bool
|
||||
/**
|
||||
* @param array<string, mixed> $vars
|
||||
*/
|
||||
protected function shouldAddToRightPanel(Actor $user, array $vars, Request $request): bool
|
||||
{
|
||||
return $vars['path'] === 'note_attachment_show';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $vars
|
||||
* @return int[]
|
||||
*
|
||||
* @return ($ids_only is true ? int[] : AttachmentCollection[])
|
||||
*/
|
||||
protected function getCollectionsBy(Actor $owner, ?array $vars = null, bool $ids_only = false): array
|
||||
{
|
||||
|
@ -28,6 +28,9 @@ use App\Core\Router;
|
||||
use Component\Collection\Util\Controller\MetaCollectionController;
|
||||
use Plugin\AttachmentCollections\Entity\AttachmentCollection;
|
||||
|
||||
/**
|
||||
* @extends MetaCollectionController<AttachmentCollection>
|
||||
*/
|
||||
class AttachmentCollections extends MetaCollectionController
|
||||
{
|
||||
public function createCollection(int $owner_id, string $name): bool
|
||||
@ -36,6 +39,8 @@ class AttachmentCollections extends MetaCollectionController
|
||||
'name' => $name,
|
||||
'actor_id' => $owner_id,
|
||||
]));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getCollectionUrl(int $owner_id, ?string $owner_nickname, int $collection_id): string
|
||||
@ -51,7 +56,12 @@ class AttachmentCollections extends MetaCollectionController
|
||||
['nickname' => $owner_nickname, 'cid' => $collection_id],
|
||||
);
|
||||
}
|
||||
public function getCollectionItems(int $owner_id, $collection_id): array
|
||||
|
||||
/**
|
||||
* FIXME return value not consistent with base class
|
||||
*/
|
||||
// @phpstan-disable-next-line
|
||||
public function getCollectionItems(int $owner_id, int $collection_id): array
|
||||
{
|
||||
[$attachs, $notes] = DB::dql(
|
||||
<<<'EOF'
|
||||
@ -64,28 +74,30 @@ class AttachmentCollections extends MetaCollectionController
|
||||
EOF,
|
||||
['cid' => $collection_id],
|
||||
);
|
||||
return [
|
||||
'_template' => 'AttachmentCollections/collection_entry_view.html.twig',
|
||||
'attachments' => array_values($attachs),
|
||||
'bare_notes' => array_values($notes),
|
||||
];
|
||||
|
||||
return ['_template' => 'AttachmentCollections/collection_entry_view.html.twig', 'attachments' => array_values($attachs), 'bare_notes' => array_values($notes)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AttachmentCollection[]
|
||||
*/
|
||||
public function getCollectionsByActorId(int $owner_id): array
|
||||
{
|
||||
return DB::findBy(AttachmentCollection::class, ['actor_id' => $owner_id], order_by: ['id' => 'desc']);
|
||||
}
|
||||
|
||||
public function getCollectionBy(int $owner_id, int $collection_id): AttachmentCollection
|
||||
{
|
||||
return DB::findOneBy(AttachmentCollection::class, ['id' => $collection_id]);
|
||||
}
|
||||
|
||||
public function setCollectionName(int $actor_id, string $actor_nickname, AttachmentCollection $collection, string $name)
|
||||
public function setCollectionName(int $actor_id, string $actor_nickname, AttachmentCollection $collection, string $name): void
|
||||
{
|
||||
$collection->setName($name);
|
||||
DB::persist($collection);
|
||||
}
|
||||
|
||||
public function removeCollection(int $actor_id, string $actor_nickname, AttachmentCollection $collection)
|
||||
public function removeCollection(int $actor_id, string $actor_nickname, AttachmentCollection $collection): void
|
||||
{
|
||||
DB::remove($collection);
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ class AttachmentShowRelated extends Plugin
|
||||
/**
|
||||
* Output our dedicated stylesheet
|
||||
*
|
||||
* @param array $styles stylesheets path
|
||||
* @param string[] $styles stylesheets path
|
||||
*/
|
||||
public function onEndShowStyles(array &$styles, string $path): EventResult
|
||||
{
|
||||
|
@ -54,6 +54,9 @@ class AudioEncoder extends Plugin
|
||||
return GSFile::mimetypeMajor($mimetype) === 'audio';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, callable> $event_map
|
||||
*/
|
||||
public function onFileMetaAvailable(array &$event_map, string $mimetype): EventResult
|
||||
{
|
||||
if (!self::shouldHandle($mimetype)) {
|
||||
@ -90,6 +93,9 @@ class AudioEncoder extends Plugin
|
||||
|
||||
/**
|
||||
* Generates the view for attachments of type Video
|
||||
*
|
||||
* @param (array{attachment: \Component\Attachment\Entity\Attachment, note: \App\Entity\Note, title: string} & array<string, mixed>) $vars
|
||||
* @param array<string> $res
|
||||
*/
|
||||
public function onViewAttachment(array $vars, array &$res): EventResult
|
||||
{
|
||||
@ -109,6 +115,8 @@ class AudioEncoder extends Plugin
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ModuleVersionType[] $versions
|
||||
*
|
||||
* @throws ServerException
|
||||
*/
|
||||
public function onPluginVersion(array &$versions): EventResult
|
||||
|
@ -39,6 +39,10 @@ class Blog extends Plugin
|
||||
return Event::next;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param (array{actor: \App\Entity\Actor} & array<string, mixed>) $vars
|
||||
* @param array<string> $res
|
||||
*/
|
||||
public function onAppendCardProfile(array $vars, array &$res): EventResult
|
||||
{
|
||||
$actor = Common::actor();
|
||||
|
@ -32,6 +32,9 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class Bundles extends Plugin
|
||||
{
|
||||
/**
|
||||
* @phpstan-use MetaCollectionTrait<BundleCollection>
|
||||
*/
|
||||
use MetaCollectionTrait;
|
||||
protected const SLUG = 'bundle';
|
||||
protected const PLURAL_SLUG = 'bundles';
|
||||
@ -55,6 +58,7 @@ class Bundles extends Plugin
|
||||
/**
|
||||
* @param array<string, mixed> $vars
|
||||
* @param array<int> $items
|
||||
* @param array<mixed> $collections
|
||||
*/
|
||||
protected function removeItem(Actor $owner, array $vars, array $items, array $collections): bool
|
||||
{
|
||||
@ -101,8 +105,11 @@ class Bundles extends Plugin
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $vars
|
||||
* @retrun int[]
|
||||
* FIXME incompatible return type
|
||||
*
|
||||
* @param null|array<string, mixed> $vars
|
||||
*
|
||||
* @return BundleCollection[]|int[]
|
||||
*/
|
||||
protected function getCollectionsBy(Actor $owner, ?array $vars = null, bool $ids_only = false): array
|
||||
{
|
||||
|
@ -26,9 +26,12 @@ namespace Plugin\Bundles\Controller;
|
||||
use App\Core\DB;
|
||||
use App\Core\Router;
|
||||
use Component\Collection\Util\Controller\MetaCollectionController;
|
||||
use Plugin\Bundles\Entity\BundleCollection;
|
||||
use Plugin\Bundles\Entity\BundleCollection as BundleCollectionEntity;
|
||||
|
||||
class BundleCollections extends MetaCollectionController
|
||||
/**
|
||||
* @extends MetaCollectionController<BundleCollectionEntity>
|
||||
*/
|
||||
class BundleCollection extends MetaCollectionController
|
||||
{
|
||||
public function getCollectionUrl(int $owner_id, ?string $owner_nickname, int $collection_id): string
|
||||
{
|
||||
@ -44,7 +47,8 @@ class BundleCollections extends MetaCollectionController
|
||||
);
|
||||
}
|
||||
|
||||
public function getCollectionItems(int $owner_id, $collection_id): array
|
||||
// FIXME
|
||||
public function getCollectionItems(int $owner_id, int $collection_id): array
|
||||
{
|
||||
[$notes] = DB::dql(
|
||||
<<<'EOF'
|
||||
@ -65,17 +69,17 @@ class BundleCollections extends MetaCollectionController
|
||||
|
||||
public function getCollectionsByActorId(int $owner_id): array
|
||||
{
|
||||
return DB::findBy(BundleCollection::class, ['actor_id' => $owner_id], order_by: ['id' => 'desc']);
|
||||
return DB::findBy(BundleCollectionEntity::class, ['actor_id' => $owner_id], order_by: ['id' => 'desc']);
|
||||
}
|
||||
|
||||
public function getCollectionBy(int $owner_id, int $collection_id): BundleCollection
|
||||
public function getCollectionBy(int $owner_id, int $collection_id): BundleCollectionEntity
|
||||
{
|
||||
return DB::findOneBy(BundleCollection::class, ['id' => $collection_id]);
|
||||
return DB::findOneBy(BundleCollectionEntity::class, ['id' => $collection_id]);
|
||||
}
|
||||
|
||||
public function createCollection(int $owner_id, string $name): bool
|
||||
{
|
||||
DB::persist(BundleCollection::create([
|
||||
DB::persist(BundleCollectionEntity::create([
|
||||
'name' => $name,
|
||||
'actor_id' => $owner_id,
|
||||
]));
|
@ -58,7 +58,7 @@ class Cover
|
||||
* @throws ClientException Invalid form
|
||||
* @throws ServerException Invalid file type
|
||||
*
|
||||
* @return array template
|
||||
* @return ControllerResultType
|
||||
*/
|
||||
public static function coverSettings(Request $request): array
|
||||
{
|
||||
@ -113,7 +113,7 @@ class Cover
|
||||
// Only delete files if the commit went through
|
||||
if ($old_file != null) {
|
||||
foreach ($old_file as $f) {
|
||||
@unlink($f);
|
||||
@unlink($f->getPath());
|
||||
}
|
||||
}
|
||||
throw new RedirectException();
|
||||
@ -131,7 +131,7 @@ class Cover
|
||||
DB::remove($cover);
|
||||
DB::flush();
|
||||
foreach ($old_file as $f) {
|
||||
@unlink($f);
|
||||
@unlink($f->getPath());
|
||||
}
|
||||
throw new RedirectException();
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ class Cover extends Plugin
|
||||
/**
|
||||
* Output our dedicated stylesheet
|
||||
*
|
||||
* @param array $styles stylesheets path
|
||||
* @param string[] $styles stylesheets path
|
||||
*/
|
||||
public function onStartShowStyles(array &$styles): EventResult
|
||||
{
|
||||
|
@ -117,7 +117,7 @@ class Cover extends Entity
|
||||
/**
|
||||
* Delete this cover and the corresponding attachment and thumbnails, which this owns
|
||||
*
|
||||
* @return array attachments deleted (if delete_attachments_now is true)
|
||||
* @return Attachment[] attachments deleted (if delete_attachments_now is true)
|
||||
*/
|
||||
public function delete(bool $flush = false, bool $delete_attachments_now = false, bool $cascading = false): array
|
||||
{
|
||||
|
@ -150,6 +150,8 @@ class DeleteNote extends NoteHandlerPlugin
|
||||
* @throws \App\Util\Exception\DuplicateFoundException
|
||||
* @throws \App\Util\Exception\NotFoundException
|
||||
* @throws \App\Util\Exception\ServerException
|
||||
*
|
||||
* @params string[] $actions
|
||||
*/
|
||||
public function onAddExtraNoteActions(Request $request, Note $note, array &$actions): EventResult
|
||||
{
|
||||
|
@ -40,7 +40,7 @@ use EventResult;
|
||||
|
||||
class EmailNotifications extends Plugin
|
||||
{
|
||||
public function onAddNotificationTransport(&$form_defs): EventResult
|
||||
public function onAddNotificationTransport(array &$form_defs): EventResult
|
||||
{
|
||||
$form_defs['Email'] = $form_defs['placeholder'];
|
||||
$form_defs['Email'][] = $form_defs['placeholder']['save']('Email', 'save_email');
|
||||
|
@ -216,7 +216,7 @@ class OEmbed extends Controller
|
||||
/**
|
||||
* Placeholder
|
||||
*/
|
||||
public function init_document($type)
|
||||
public function init_document(string $type): void
|
||||
{
|
||||
throw new NotImplementedException;
|
||||
// switch ($type) {
|
||||
@ -243,7 +243,7 @@ class OEmbed extends Controller
|
||||
/**
|
||||
* Placeholder
|
||||
*/
|
||||
public function end_document($type)
|
||||
public function end_document(string $type): void
|
||||
{
|
||||
throw new NotImplementedException;
|
||||
// switch ($type) {
|
||||
|
@ -26,6 +26,7 @@ namespace Plugin\Favourite\Controller;
|
||||
use App\Core\DB;
|
||||
use App\Core\Event;
|
||||
use App\Core\Form;
|
||||
use function App\Core\I18n\_m;
|
||||
use App\Core\Log;
|
||||
use App\Core\Router;
|
||||
use App\Entity\Activity;
|
||||
@ -41,10 +42,12 @@ use App\Util\Exception\RedirectException;
|
||||
use App\Util\Exception\ServerException;
|
||||
use Component\Collection\Util\Controller\FeedController;
|
||||
use Component\Notification\Entity\Attention;
|
||||
use function App\Core\I18n\_m;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* @extends FeedController<Note>
|
||||
*/
|
||||
class Favourite extends FeedController
|
||||
{
|
||||
/**
|
||||
|
@ -44,7 +44,8 @@ class LatexNotes extends Plugin
|
||||
$types['LaTeX'] = 'application/x-latex';
|
||||
return Event::next;
|
||||
}
|
||||
public function onRenderNoteContent($content, $content_type, &$rendered): EventResult
|
||||
|
||||
public function onRenderNoteContent(string $content, string $content_type, string &$rendered): EventResult
|
||||
{
|
||||
if ($content_type !== 'application/x-latex') {
|
||||
return Event::next;
|
||||
|
@ -43,7 +43,8 @@ class MarkdownNotes extends Plugin
|
||||
$types['Markdown'] = 'text/markdown';
|
||||
return Event::next;
|
||||
}
|
||||
public function onRenderNoteContent($content, $content_type, &$rendered): EventResult
|
||||
|
||||
public function onRenderNoteContent(string $content, string $content_type, string &$rendered): EventResult
|
||||
{
|
||||
if ($content_type !== 'text/markdown') {
|
||||
return Event::next;
|
||||
|
@ -140,7 +140,7 @@ class NoteTypeFeedFilter extends Plugin
|
||||
/**
|
||||
* Draw the media feed navigation.
|
||||
*/
|
||||
public function onAddFeedActions(Request $request, bool $is_not_empty, &$res): EventResult
|
||||
public function onAddFeedActions(Request $request, bool $is_not_empty, array &$res): EventResult
|
||||
{
|
||||
$qs = [];
|
||||
$query_string = $request->getQueryString();
|
||||
|
@ -36,6 +36,9 @@ use League\OAuth2\Server\CryptKey;
|
||||
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||
use Plugin\OAuth2\Util\Token;
|
||||
|
||||
/**
|
||||
* @extends Token<AccessToken>
|
||||
*/
|
||||
class AccessToken extends Token implements AccessTokenEntityInterface
|
||||
{
|
||||
// {{{ Autocode
|
||||
|
@ -36,6 +36,9 @@ use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
|
||||
use Plugin\OAuth2\Repository;
|
||||
use Plugin\OAuth2\Util\Token;
|
||||
|
||||
/**
|
||||
* @extends Token<AuthCode>
|
||||
*/
|
||||
class AuthCode extends Token implements AuthCodeEntityInterface
|
||||
{
|
||||
// {{{ Autocode
|
||||
|
@ -45,11 +45,17 @@ class RefreshToken implements RefreshTokenRepositoryInterface
|
||||
DB::persist($refreshTokenEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tokenId
|
||||
*/
|
||||
public function revokeRefreshToken($tokenId)
|
||||
{
|
||||
// Some logic to revoke the auth token in a database
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tokenId
|
||||
*/
|
||||
public function isRefreshtokenRevoked($tokenId): bool
|
||||
{
|
||||
return false; // The auth token has not been revoked
|
||||
|
@ -29,6 +29,7 @@ use function App\Core\I18n\_m;
|
||||
use App\Core\Router;
|
||||
use App\Entity\Actor;
|
||||
use App\Entity\LocalUser;
|
||||
use App\Entity\Note;
|
||||
use App\Util\Common;
|
||||
use App\Util\Exception\ClientException;
|
||||
use App\Util\Exception\NoSuchNoteException;
|
||||
@ -39,6 +40,9 @@ use Plugin\PinnedNotes\Entity as E;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* @extends FeedController<Note>
|
||||
*/
|
||||
class PinnedNotes extends FeedController
|
||||
{
|
||||
public function listPinsByNickname(Request $request, string $nickname)
|
||||
|
@ -16,7 +16,8 @@ class PinnedNotes extends Entity
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
public function setId($id)
|
||||
|
||||
public function setId(int $id): self
|
||||
{
|
||||
$this->id = $id;
|
||||
return $this;
|
||||
@ -26,7 +27,8 @@ class PinnedNotes extends Entity
|
||||
{
|
||||
return $this->actor_id;
|
||||
}
|
||||
public function setActorId($actor_id)
|
||||
|
||||
public function setActorId(int $actor_id): self
|
||||
{
|
||||
$this->actor_id = $actor_id;
|
||||
return $this;
|
||||
@ -36,7 +38,8 @@ class PinnedNotes extends Entity
|
||||
{
|
||||
return $this->note_id;
|
||||
}
|
||||
public function setNoteId($note_id)
|
||||
|
||||
public function setNoteId(int $note_id): self
|
||||
{
|
||||
$this->note_id = $note_id;
|
||||
return $this;
|
||||
|
@ -74,7 +74,7 @@ class PinnedNotes extends Plugin
|
||||
return Event::next;
|
||||
}
|
||||
|
||||
public function onBeforeFeed(Request $request, &$res): EventResult
|
||||
public function onBeforeFeed(Request $request, array &$res): EventResult
|
||||
{
|
||||
$path = $request->attributes->get('_route');
|
||||
if ($path === 'actor_view_nickname') {
|
||||
@ -160,7 +160,7 @@ class PinnedNotes extends Plugin
|
||||
return Event::next;
|
||||
}
|
||||
|
||||
public function onActivityPubAddActivityStreamsTwoData(string $type_name, &$type): EventResult
|
||||
public function onActivityPubAddActivityStreamsTwoData(string $type_name, object &$type): EventResult
|
||||
{
|
||||
if ($type_name === 'Person') {
|
||||
$actor = \Plugin\ActivityPub\Util\Explorer::getOneFromUri($type->get('id'));
|
||||
|
@ -83,7 +83,7 @@ class UnboundGroup extends Plugin
|
||||
return Event::next;
|
||||
}
|
||||
|
||||
public function onActivityPubAddActivityStreamsTwoData(string $type_name, &$type): EventResult
|
||||
public function onActivityPubAddActivityStreamsTwoData(string $type_name, object &$type): EventResult
|
||||
{
|
||||
if ($type_name === 'Group' || $type_name === 'Organization') {
|
||||
$actor = \Plugin\ActivityPub\Util\Explorer::getOneFromUri($type->getId());
|
||||
|
@ -63,9 +63,6 @@ class WebMonetization extends Plugin
|
||||
if (\is_null($user)) {
|
||||
return Event::next;
|
||||
}
|
||||
if (\is_null($vars)) {
|
||||
return Event::next;
|
||||
}
|
||||
|
||||
$is_self = null;
|
||||
$receiver_id = null;
|
||||
@ -214,7 +211,7 @@ class WebMonetization extends Plugin
|
||||
];
|
||||
}
|
||||
|
||||
public function onAppendToHead(Request $request, &$res): EventResult
|
||||
public function onAppendToHead(Request $request, array &$res): EventResult
|
||||
{
|
||||
$user = Common::user();
|
||||
if (\is_null($user)) {
|
||||
@ -257,7 +254,7 @@ class WebMonetization extends Plugin
|
||||
return Event::next;
|
||||
}
|
||||
|
||||
public function onActivityPubAddActivityStreamsTwoData(string $type_name, &$type): EventResult
|
||||
public function onActivityPubAddActivityStreamsTwoData(string $type_name, object &$type): EventResult
|
||||
{
|
||||
if ($type_name === 'Person') {
|
||||
$actor = \Plugin\ActivityPub\Util\Explorer::getOneFromUri($type->getId());
|
||||
|
@ -40,7 +40,7 @@ use EventResult;
|
||||
|
||||
class XMPPNotifications extends Plugin
|
||||
{
|
||||
public function onAddNotificationTransport(&$form_defs): EventResult
|
||||
public function onAddNotificationTransport(array &$form_defs): EventResult
|
||||
{
|
||||
$form_defs['XMPP'] = $form_defs['placeholder'];
|
||||
$form_defs['XMPP'][] = $form_defs['placeholder']['save']('XMPP', 'save_xmpp');
|
||||
|
@ -40,8 +40,12 @@ use Symfony\Component\Cache\CacheItem;
|
||||
|
||||
abstract class Cache
|
||||
{
|
||||
protected static $pools;
|
||||
protected static $redis;
|
||||
protected static array $pools;
|
||||
|
||||
/**
|
||||
* @property ?Redis $redis
|
||||
*/
|
||||
protected static mixed $redis;
|
||||
|
||||
/**
|
||||
* Configure a cache pool, with adapters taken from `ENV_VAR`.
|
||||
@ -77,8 +81,10 @@ abstract class Cache
|
||||
// @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) {
|
||||
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
|
||||
|
@ -153,7 +153,8 @@ abstract class Controller extends AbstractController implements EventSubscriberI
|
||||
$controller = $controller[0];
|
||||
}
|
||||
if (is_subclass_of($controller, FeedController::class)) {
|
||||
$this->vars = $controller->postProcess($this->vars);
|
||||
// XXX
|
||||
$this->vars = $controller->postProcess($this->vars); // @phpstan-ignore-line
|
||||
}
|
||||
|
||||
// Respond in the most preferred acceptable content type
|
||||
|
@ -45,6 +45,7 @@ use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
@ -89,8 +90,8 @@ use Functional as F;
|
||||
*/
|
||||
class DB
|
||||
{
|
||||
private static ?EntityManagerInterface $em;
|
||||
public static function setManager($m): void
|
||||
private static null|\Doctrine\ORM\EntityManagerInterface|\Doctrine\Persistence\ObjectManager $em;
|
||||
public static function setManager(EntityManagerInterface|\Doctrine\Persistence\ObjectManager $m): void
|
||||
{
|
||||
self::$em = $m;
|
||||
}
|
||||
@ -104,6 +105,7 @@ class DB
|
||||
private static ?array $dql_table_name_patterns = null;
|
||||
public static function initTableMap()
|
||||
{
|
||||
/** @var ClassMetadataInfo[] $all */ // @phpstan-ignore-next-line
|
||||
$all = self::$em->getMetadataFactory()->getAllMetadata();
|
||||
foreach ($all as $meta) {
|
||||
self::$table_map[$meta->getTableName()] = $meta->getMetadataValue('name');
|
||||
@ -130,7 +132,7 @@ class DB
|
||||
public static function dql(string $query, array $params = [], array $options = [])
|
||||
{
|
||||
$query = preg_replace(self::$dql_table_name_patterns, self::$table_map, $query);
|
||||
$q = new Query(self::$em);
|
||||
$q = new Query(self::$em); // @phpstan-ignore-line
|
||||
$q->setDQL($query);
|
||||
|
||||
if (isset($options['limit'])) {
|
||||
@ -192,6 +194,7 @@ class DB
|
||||
if ($_ENV['APP_ENV'] === 'dev' && str_starts_with($query, 'select *')) {
|
||||
throw new Exception('Cannot use `select *`, use `select {select}` (see ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT)');
|
||||
}
|
||||
// @phpstan-ignore-next-line
|
||||
$rsmb = new ResultSetMappingBuilder(self::$em, \is_null($entities) ? ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT : ResultSetMappingBuilder::COLUMN_RENAMING_NONE);
|
||||
if (\is_null($entities)) {
|
||||
$matches = [];
|
||||
@ -205,6 +208,7 @@ class DB
|
||||
$rsmb->addRootEntityFromClassMetadata($entity, $alias);
|
||||
}
|
||||
$query = preg_replace('/{select}/', $rsmb->generateSelectClause(), $query);
|
||||
// @phpstan-ignore-next-line
|
||||
$q = self::$em->createNativeQuery($query, $rsmb);
|
||||
foreach ($params as $k => $v) {
|
||||
$q->setParameter($k, $v);
|
||||
@ -263,7 +267,7 @@ class DB
|
||||
{
|
||||
$criteria = array_change_key_case($criteria, \CASE_LOWER);
|
||||
$ops = array_intersect(array_keys($criteria), self::$find_by_ops);
|
||||
/** @var EntityRepository */
|
||||
/** @var EntityRepository */ // @phpstan-ignore-next-line
|
||||
$repo = self::getRepository($table);
|
||||
if (empty($ops)) {
|
||||
return $repo->findBy($criteria, $order_by, $limit, $offset);
|
||||
@ -307,7 +311,7 @@ class DB
|
||||
|
||||
public static function count(string $table, array $criteria)
|
||||
{
|
||||
/** @var EntityRepository */
|
||||
/** @var EntityRepository */ // @phpstan-ignore-next-line
|
||||
$repo = self::getRepository($table);
|
||||
return $repo->count($criteria);
|
||||
}
|
||||
|
@ -32,9 +32,7 @@ declare(strict_types = 1);
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
use App\Core\DB;
|
||||
use function App\Core\I18n\_m;
|
||||
use App\Core\Router;
|
||||
use App\Util\Common;
|
||||
use App\Util\Exception\ClientException;
|
||||
use App\Util\Exception\ServerException;
|
||||
@ -82,7 +80,7 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
abstract class Form
|
||||
{
|
||||
private static ?FormFactoryInterface $form_factory;
|
||||
public static function setFactory($ff): void
|
||||
public static function setFactory(FormFactoryInterface $ff): void
|
||||
{
|
||||
self::$form_factory = $ff;
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ class GSFile
|
||||
* Throw a client exception if the cache key $id doesn't contain
|
||||
* exactly one entry
|
||||
*/
|
||||
public static function error($exception, $id, array $res)
|
||||
public static function error(string $exception, int $id, array $res): mixed
|
||||
{
|
||||
switch (\count($res)) {
|
||||
case 0:
|
||||
|
@ -66,7 +66,7 @@ abstract class I18n
|
||||
{
|
||||
public static ?TranslatorInterface $translator = null;
|
||||
|
||||
public static function setTranslator($trans): void
|
||||
public static function setTranslator(TranslatorInterface $trans): void
|
||||
{
|
||||
self::$translator = $trans;
|
||||
}
|
||||
@ -306,9 +306,11 @@ abstract class I18n
|
||||
* _m(string|string[] $msg, array $params) -- parameterized message
|
||||
* _m(string $ctx, string|string[] $msg, array $params) -- combination of the previous two
|
||||
*
|
||||
* @throws ServerException
|
||||
*
|
||||
* @todo add parameters
|
||||
*
|
||||
* @param array|int|string ...$args
|
||||
*
|
||||
* @throws ServerException
|
||||
*/
|
||||
function _m(...$args): string
|
||||
{
|
||||
|
@ -127,6 +127,8 @@ class TransExtractor extends AbstractFileExtractor implements ExtractorInterface
|
||||
|
||||
/**
|
||||
* Normalizes a token.
|
||||
*
|
||||
* @param string $token
|
||||
*/
|
||||
protected function normalizeToken($token): ?string
|
||||
{
|
||||
@ -186,6 +188,8 @@ class TransExtractor extends AbstractFileExtractor implements ExtractorInterface
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @param string $directory
|
||||
*/
|
||||
protected function extractFromDirectory($directory): iterable
|
||||
{
|
||||
|
@ -53,7 +53,7 @@ abstract class Log
|
||||
{
|
||||
private static ?LoggerInterface $logger;
|
||||
|
||||
public static function setLogger($l): void
|
||||
public static function setLogger(LoggerInterface $l): void
|
||||
{
|
||||
self::$logger = $l;
|
||||
}
|
||||
@ -62,6 +62,7 @@ abstract class Log
|
||||
* Log a critical error when a really unexpected exception occured. This indicates a bug in the software
|
||||
*
|
||||
* @throws ServerException
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function unexpected_exception(Exception $e)
|
||||
|
@ -52,11 +52,11 @@ use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
class ModuleManager
|
||||
{
|
||||
protected static $loader;
|
||||
protected static \Composer\Autoload\ClassLoader $loader;
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function setLoader($l)
|
||||
public static function setLoader(\Composer\Autoload\ClassLoader $l)
|
||||
{
|
||||
self::$loader = $l;
|
||||
}
|
||||
@ -161,7 +161,7 @@ class ModuleManager
|
||||
/**
|
||||
* Serialize this class, for dumping into the cache
|
||||
*/
|
||||
public static function __set_state($state)
|
||||
public static function __set_state(array $state): self
|
||||
{
|
||||
$obj = new self();
|
||||
$obj->modules = $state['modules'];
|
||||
|
@ -54,7 +54,7 @@ abstract class Module
|
||||
/**
|
||||
* Serialize the class to store in the cache
|
||||
*/
|
||||
public static function __set_state($state)
|
||||
public static function __set_state(array $state)
|
||||
{
|
||||
$obj = new (static::class);
|
||||
foreach ($state as $k => $v) {
|
||||
|
@ -40,7 +40,7 @@ abstract class Queue
|
||||
{
|
||||
private static ?MessageBusInterface $message_bus;
|
||||
|
||||
public static function setMessageBus($mb): void
|
||||
public static function setMessageBus(MessageBusInterface $mb): void
|
||||
{
|
||||
self::$message_bus = $mb;
|
||||
}
|
||||
@ -50,7 +50,7 @@ abstract class Queue
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function enqueue($payload, string $queue, bool $priority = false, array $stamps = [])
|
||||
public static function enqueue(mixed $payload, string $queue, bool $priority = false, array $stamps = []): void
|
||||
{
|
||||
if ($priority) {
|
||||
self::$message_bus->dispatch(new MessageHigh($payload, $queue), $stamps);
|
||||
|
@ -32,8 +32,6 @@ declare(strict_types = 1);
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
use App\Core\Event;
|
||||
use App\Core\Log;
|
||||
use App\Util\Common;
|
||||
use Symfony\Component\Config\Loader\Loader;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
@ -41,6 +39,7 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\Routing\Router as SymfonyRouter;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
|
||||
/**
|
||||
* @mixin SymfonyRouter
|
||||
@ -70,9 +69,9 @@ class Router extends Loader
|
||||
*/
|
||||
public const NETWORK_PATH = UrlGeneratorInterface::NETWORK_PATH;
|
||||
|
||||
public static ?SymfonyRouter $router = null;
|
||||
public static ?RouterInterface $router = null;
|
||||
|
||||
public static function setRouter($rtr): void
|
||||
public static function setRouter(RouterInterface $rtr): void
|
||||
{
|
||||
self::$router = $rtr;
|
||||
}
|
||||
@ -122,6 +121,7 @@ class Router extends Loader
|
||||
*
|
||||
* Must conform to symfony's interface, but the $resource is unused
|
||||
* and $type must not be null
|
||||
* @param string $resource
|
||||
*/
|
||||
public function load($resource, ?string $type = null): RouteCollection
|
||||
{
|
||||
|
@ -50,6 +50,8 @@ class RouteLoader extends Loader
|
||||
*
|
||||
* Must conform to symfony's interface, but the $resource is unused
|
||||
* and $type must not be null
|
||||
*
|
||||
* @param resource $resource
|
||||
*/
|
||||
public function load($resource, ?string $type = null): RouteCollection
|
||||
{
|
||||
|
@ -46,6 +46,7 @@ use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
|
||||
* HtmlSanitizer\SanitizerInterface, calling the first existing method, in that order
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @mixin SymfonySecurity
|
||||
*
|
||||
* @method static LocalUser getUser()
|
||||
@ -53,7 +54,7 @@ use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
|
||||
class Security implements EventSubscriberInterface //implements AuthenticatorInterface
|
||||
{
|
||||
private static ?SymfonySecurity $security;
|
||||
public static function setHelper($sec): void
|
||||
public static function setHelper(SymfonySecurity $sec): void
|
||||
{
|
||||
self::$security = $sec;
|
||||
}
|
||||
|
@ -94,6 +94,9 @@ class SchemaDefDriver extends StaticPHPDriver implements CompilerPassInterface
|
||||
* Fill in the database $metadata for $class_name
|
||||
*
|
||||
* @param class-string $class_name
|
||||
*
|
||||
* @phpstan-ignore-next-line
|
||||
*
|
||||
* @param ClassMetadataInfo $metadata ClassMetadataInfo is the real type, but we need to override the method
|
||||
*/
|
||||
public function loadMetadataForClass($class_name, ClassMetadata $metadata): void
|
||||
@ -213,6 +216,8 @@ class SchemaDefDriver extends StaticPHPDriver implements CompilerPassInterface
|
||||
* Override StaticPHPDriver's method,
|
||||
* we care about classes that have the method `schemaDef`,
|
||||
* instead of `loadMetadata`.
|
||||
*
|
||||
* @param mixed $class_name
|
||||
*/
|
||||
public function isTransient($class_name): bool
|
||||
{
|
||||
|
@ -550,6 +550,10 @@ class Actor extends Entity
|
||||
}
|
||||
}
|
||||
|
||||
public function hasBlocked(self $other): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function __call(string $name, array $arguments): mixed
|
||||
{
|
||||
if (Formatting::startsWith($name, 'is')) {
|
||||
|
@ -17,6 +17,8 @@ use SymfonyCasts\Bundle\ResetPassword\Persistence\ResetPasswordRequestRepository
|
||||
* @method null|ResetPasswordRequest findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method ResetPasswordRequest[] findAll()
|
||||
* @method ResetPasswordRequest[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*
|
||||
* @extends ServiceEntityRepository<ResetPasswordRequest>
|
||||
*/
|
||||
class ResetPasswordRequestRepository extends ServiceEntityRepository implements ResetPasswordRequestRepositoryInterface
|
||||
{
|
||||
|
@ -116,6 +116,8 @@ class Runtime implements RuntimeExtensionInterface, EventSubscriberInterface
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param null|string ...$args
|
||||
*/
|
||||
public function getConfig(...$args)
|
||||
{
|
||||
@ -125,10 +127,11 @@ class Runtime implements RuntimeExtensionInterface, EventSubscriberInterface
|
||||
/**
|
||||
* get stylesheets
|
||||
*
|
||||
* @return array|mixed
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getShowStylesheets($route)
|
||||
public function getShowStylesheets(string $route): array
|
||||
{
|
||||
$styles = [];
|
||||
Event::handle('EndShowStyles', [&$styles, $route]);
|
||||
@ -137,8 +140,11 @@ class Runtime implements RuntimeExtensionInterface, EventSubscriberInterface
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param mixed ...$args
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function handleEvent(string $event, ...$args)
|
||||
public function handleEvent(string $event, ...$args): array
|
||||
{
|
||||
$res = [];
|
||||
$args[] = &$res;
|
||||
@ -164,7 +170,7 @@ class Runtime implements RuntimeExtensionInterface, EventSubscriberInterface
|
||||
&& (preg_match(pattern: $re_has_gecko, subject: $this->request->headers->get('User-Agent')) === 1);
|
||||
}
|
||||
|
||||
public function isInstanceOf($value, string $type): bool
|
||||
public function isInstanceOf(mixed $value, string $type): bool
|
||||
{
|
||||
return (\function_exists($func = 'is_' . $type) && $func($value)) || $value instanceof $type;
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ abstract class Common
|
||||
*
|
||||
* @param bool $transient keep this setting in memory only
|
||||
*/
|
||||
public static function setConfig(string $section, string $setting, $value, bool $transient = false): void
|
||||
public static function setConfig(string $section, string $setting, mixed $value, bool $transient = false): void
|
||||
{
|
||||
self::$config[$section][$setting] = $value;
|
||||
if (!$transient) {
|
||||
@ -184,8 +184,13 @@ abstract class Common
|
||||
|
||||
/**
|
||||
* A recursive `array_diff`, while PHP itself doesn't provide one
|
||||
*
|
||||
* @param mixed[] $array1
|
||||
* @param mixed[] $array2
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function arrayDiffRecursive($array1, $array2): array
|
||||
public static function arrayDiffRecursive(array $array1, array $array2): array
|
||||
{
|
||||
$diff = [];
|
||||
foreach ($array1 as $key => $value) {
|
||||
|
@ -191,7 +191,7 @@ abstract class Formatting
|
||||
/**
|
||||
* Convert scalars, objects implementing __toString or arrays to strings
|
||||
*/
|
||||
public static function toString($value, string $join_type = self::JOIN_BY_COMMA): string
|
||||
public static function toString(mixed $value, string $join_type = self::JOIN_BY_COMMA): string
|
||||
{
|
||||
if (!\in_array($join_type, [static::JOIN_BY_SPACE, static::JOIN_BY_COMMA])) {
|
||||
throw new Exception('Formatting::toString received invalid join option');
|
||||
@ -209,7 +209,7 @@ abstract class Formatting
|
||||
*
|
||||
* @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
|
||||
public static function toArray(string $input, array &$output, string $split_type = self::SPLIT_BY_COMMA): bool
|
||||
{
|
||||
if ($input == '') {
|
||||
$output = [];
|
||||
|
@ -57,7 +57,7 @@ abstract class HTML
|
||||
public const SELF_CLOSING_TAG = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
|
||||
private static ?SanitizerInterface $sanitizer;
|
||||
|
||||
public static function setSanitizer($sanitizer): void
|
||||
public static function setSanitizer(SanitizerInterface $sanitizer): void
|
||||
{
|
||||
self::$sanitizer = $sanitizer;
|
||||
}
|
||||
|
@ -42,7 +42,9 @@ use Symfony\Component\Mime\MimeTypes;
|
||||
*/
|
||||
class TemporaryFile extends SplFileInfo
|
||||
{
|
||||
// Cannot type annotate currently. `resource` is the expected type, but it's not a builtin type
|
||||
/**
|
||||
* @var null|false|resource $resource
|
||||
*/
|
||||
protected $resource;
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user