chore(phpstan): upgrade to be compatible with latest dependency updates (#3835)

This commit is contained in:
Sami Mazouz 2023-06-15 17:49:39 +01:00 committed by GitHub
parent 64b25b26c3
commit 493ffa0538
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 472 additions and 268 deletions

View File

@ -94,24 +94,10 @@ jobs:
prefix: flarum_
prefixStr: (prefix)
# @TODO: remove in 2.0
# Include testing PHP 8.2 with deprecation warnings disabled.
- php: 8.2
php_ini_values: error_reporting=E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED
# To reduce number of actions, we exclude some PHP versions from running with some DB versions.
exclude:
- php: ${{ fromJSON(inputs.php_versions)[1] }}
service: 'mysql:8.0.30'
- php: ${{ fromJSON(inputs.php_versions)[2] }}
service: 'mysql:8.0.30'
- php: ${{ fromJSON(inputs.php_versions)[3] }}
service: 'mysql:8.0.30'
# @TODO: remove in 2.0
# Exclude testing PHP 8.2 with deprecation warnings enabled.
- php: 8.2
php_ini_values: error_reporting=E_ALL
services:
mysql:
@ -137,14 +123,10 @@ jobs:
tools: phpunit, composer:v2
ini-values: ${{ matrix.php_ini_values }}
# The authentication alter is necessary because newer mysql versions use the `caching_sha2_password` driver,
# which isn't supported prior to PHP7.4
# When we drop support for PHP7.3, we should remove this from the setup.
- name: Create MySQL Database
run: |
sudo systemctl start mysql
mysql -uroot -proot -e 'CREATE DATABASE flarum_test;' --port 13306
mysql -uroot -proot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root';" --port 13306
- name: Install Composer dependencies
run: composer install
@ -180,6 +162,12 @@ jobs:
matrix:
php: ${{ fromJSON(inputs.php_versions) }}
services:
mysql:
image: mysql:8.0.30
ports:
- 33306:3306
name: 'PHPStan PHP ${{ matrix.php }}'
if: >-
@ -202,5 +190,15 @@ jobs:
run: composer install
working-directory: ${{ inputs.backend_directory }}
- name: Create MySQL Database
run: |
sudo systemctl start mysql
mysql -uroot -proot -e 'CREATE DATABASE flarum_test;' --port 33306
- name: Run PHPStan
run: composer analyse:phpstan
env:
DB_PORT: 33306
DB_PASSWORD: root
COMPOSER_PROCESS_TIMEOUT: 600
FLARUM_TEST_TMP_DIR_LOCAL: ./tmp

View File

@ -156,6 +156,8 @@
"symfony/console": "^6.3",
"symfony/event-dispatcher": "^6.3",
"symfony/http-client": "^6.3",
"symfony/mailer": "^6.3",
"symfony/mailgun-mailer": "^6.3",
"symfony/mime": "^6.3",
"symfony/polyfill-intl-messageformatter": "^1.27",
"symfony/postmark-mailer": "^6.3",
@ -164,7 +166,7 @@
"wikimedia/less.php": "^3.0"
},
"require-dev": {
"mockery/mockery": "^1.4",
"mockery/mockery": "^1.5",
"phpunit/phpunit": "^9.0",
"phpstan/phpstan": "^1.10.0",
"nunomaduro/larastan": "^2.6",

View File

@ -12,6 +12,7 @@ namespace Flarum\Mentions;
use Flarum\Extension\ExtensionManager;
use Flarum\Group\Group;
use Flarum\Http\UrlGenerator;
use Flarum\Post\Post;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\Tags\Tag;
use Flarum\User\User;
@ -80,10 +81,10 @@ class ConfigureMentions
/**
* @param FormatterTag $tag
* @param array<string, Collection> $mentions
* @param array{users: Collection<int, User>} $mentions
* @return bool|void
*/
public static function addUserId($tag, array $mentions)
public static function addUserId(FormatterTag $tag, array $mentions)
{
$allow_username_format = (bool) resolve(SettingsRepositoryInterface::class)->get('flarum-mentions.allow_username_format');
@ -139,10 +140,10 @@ class ConfigureMentions
/**
* @param FormatterTag $tag
* @param array<string, Collection> $mentions
* @param array{posts: Collection<int, Post>} $mentions
* @return bool|void
*/
public static function addPostId($tag, array $mentions)
public static function addPostId(FormatterTag $tag, array $mentions)
{
$post = $mentions['posts']->where('id', $tag->getAttribute('id'))->first();
@ -212,7 +213,7 @@ class ConfigureMentions
/**
* @param FormatterTag $tag
* @param User $actor
* @param array<string, Collection> $mentions
* @param array{groups: Collection<int, Group>} $mentions
* @return bool|void
*/
public static function addGroupId(FormatterTag $tag, User $actor, array $mentions)
@ -228,7 +229,7 @@ class ConfigureMentions
$group = $mentions['groups']->where('id', $id)->first();
if ($group) {
$tag->setAttribute('id', $group->id);
$tag->setAttribute('id', (string) $group->id);
$tag->setAttribute('groupname', $group->name_plural);
return true;
@ -301,7 +302,7 @@ class ConfigureMentions
/**
* @param FormatterTag $tag
* @param array<string, Collection> $mentions
* @param array{tags: Collection<int, Tag>} $mentions
* @return true|void
*/
public static function addTagId(FormatterTag $tag, array $mentions)

View File

@ -42,7 +42,6 @@ class ComposerAdapter
$exitCode = $this->application->run($input, $this->output);
chdir($currDir);
// @phpstan-ignore-next-line
$command = $input->__toString();
$output = $this->output->fetch();

View File

@ -20,7 +20,7 @@
],
"require": {
"flarum/core": "^2.0",
"pusher/pusher-php-server": "^2.2"
"pusher/pusher-php-server": "^7.2"
},
"require-dev": {
"flarum/tags": "^1.0"

View File

@ -17,7 +17,7 @@ use Laminas\Diactoros\Response\JsonResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Pusher;
use Pusher\Pusher;
class AuthController implements RequestHandlerInterface
{
@ -36,7 +36,6 @@ class AuthController implements RequestHandlerInterface
$this->settings->get('flarum-pusher.app_key'),
$this->settings->get('flarum-pusher.app_secret'),
$this->settings->get('flarum-pusher.app_id'),
// @phpstan-ignore-next-line
['cluster' => $this->settings->get('flarum-pusher.app_cluster')]
);

View File

@ -14,7 +14,7 @@ use Flarum\Post\Event\Posted;
use Flarum\User\Guest;
use Flarum\User\User;
use Illuminate\Support\Str;
use Pusher;
use Pusher\Pusher;
class PushNewPost
{
@ -32,15 +32,15 @@ class PushNewPost
$channels[] = 'public';
} else {
// Retrieve private channels, used for each user.
$response = $this->pusher->get_channels([
$response = $this->pusher->getChannels([
'filter_by_prefix' => 'private-user'
]);
// @phpstan-ignore-next-line
if (! $response) {
return;
}
// @phpstan-ignore-next-line
foreach ($response->channels as $name => $channel) {
$userId = Str::after($name, 'private-user');

View File

@ -11,7 +11,7 @@ namespace Flarum\Pusher\Provider;
use Flarum\Foundation\AbstractServiceProvider;
use Flarum\Settings\SettingsRepositoryInterface;
use Pusher;
use Pusher\Pusher;
class PusherProvider extends AbstractServiceProvider
{
@ -30,7 +30,6 @@ class PusherProvider extends AbstractServiceProvider
$settings->get('flarum-pusher.app_key'),
$settings->get('flarum-pusher.app_secret'),
$settings->get('flarum-pusher.app_id'),
// @phpstan-ignore-next-line
$options
);
});

View File

@ -12,7 +12,7 @@ namespace Flarum\Pusher;
use Flarum\Notification\Blueprint\BlueprintInterface;
use Flarum\Queue\AbstractJob;
use Flarum\User\User;
use Pusher;
use Pusher\Pusher;
class SendPusherNotificationsJob extends AbstractJob
{

View File

@ -96,6 +96,7 @@ class UpdateTagMetadata
}
/**
* @param Collection<int, Tag>|null $tags
* @param Post|null $post This is only used when a post has been hidden
*/
protected function updateTags(Discussion $discussion, int $delta = 0, ?Collection $tags = null, ?Post $post = null): void

View File

@ -43,8 +43,8 @@ use Illuminate\Database\Query\Builder as QueryBuilder;
*
* @property TagState $state
* @property Tag|null $parent
* @property-read Collection<Tag> $children
* @property-read Collection<Discussion> $discussions
* @property-read Collection<int, Tag> $children
* @property-read Collection<int, Discussion> $discussions
* @property Discussion|null $lastPostedDiscussion
* @property User|null $lastPostedUser
*/

View File

@ -71,7 +71,7 @@ class TagRepository
* Find all tags, optionally making sure they are visible to a
* certain user.
*
* @return Collection<Tag>
* @return Collection<int, Tag>
*/
public function all(User $user = null): Collection
{

View File

@ -21,13 +21,18 @@ use Illuminate\Support\Str;
* @property string|null $allowed_ips
* @property string|null $scopes
* @property int|null $user_id
* @property \Flarum\User\User|null $user
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon|null $last_activity_at
* @property-read \Flarum\User\User|null $user
*/
class ApiKey extends AbstractModel
{
protected $casts = ['last_activity_at' => 'datetime'];
protected $casts = [
'id' => 'integer',
'user_id' => 'integer',
'created_at' => 'datetime',
'last_activity_at' => 'datetime'
];
public static function generate(): static
{

View File

@ -137,7 +137,7 @@ class ShowDiscussionController extends AbstractShowController
/** @var Post $post */
foreach ($posts as $post) {
$post->discussion = $discussion;
$post->setRelation('discussion', $discussion);
}
$this->loadRelations($posts, $include, $request);

View File

@ -12,7 +12,7 @@ namespace Flarum\Api\Controller;
use Flarum\Http\RememberAccessToken;
use Flarum\Http\RequestUtil;
use Flarum\Http\SessionAccessToken;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Psr\Http\Message\ServerRequestInterface;
class TerminateAllOtherSessionsController extends AbstractDeleteController

View File

@ -14,6 +14,7 @@ use Flarum\Discussion\Command\EditDiscussion;
use Flarum\Discussion\Command\ReadDiscussion;
use Flarum\Discussion\Discussion;
use Flarum\Http\RequestUtil;
use Flarum\Post\Post;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Arr;
@ -35,6 +36,7 @@ class UpdateDiscussionController extends AbstractShowController
$discussionId = Arr::get($request->getQueryParams(), 'id');
$data = Arr::get($request->getParsedBody(), 'data', []);
/** @var Discussion $discussion */
$discussion = $this->bus->dispatch(
new EditDiscussion($discussionId, $actor, $data)
);
@ -50,6 +52,7 @@ class UpdateDiscussionController extends AbstractShowController
}
if ($posts = $discussion->getModifiedPosts()) {
/** @var Collection<int, Post> $posts */
$posts = (new Collection($posts))->load('discussion', 'user');
$discussionPosts = $discussion->posts()->whereVisibleTo($actor)->oldest()->pluck('id')->all();

View File

@ -164,11 +164,9 @@ abstract class AbstractSerializer extends BaseAbstractSerializer
{
if (is_object($model)) {
return $model->$relation;
} elseif (is_array($model)) {
return $model[$relation];
}
return null;
return $model[$relation];
}
/**

View File

@ -17,6 +17,7 @@ class Schedule extends LaravelSchedule
{
public function dueEvents($app)
{
/** @phpstan-ignore-next-line */
return (new Collection($this->events))->filter->isDue(new class($app) {
protected Config $config;

View File

@ -9,8 +9,15 @@
namespace Flarum\Database\Eloquent;
use Flarum\Database\AbstractModel;
use Illuminate\Database\Eloquent\Collection as BaseCollection;
/**
* @template TKey of array-key
* @template TModel of AbstractModel
*
* @extends BaseCollection<TKey, TModel>
*/
class Collection extends BaseCollection
{
/**

View File

@ -46,16 +46,16 @@ use Illuminate\Support\Str;
* @property int|null $last_post_number
* @property \Carbon\Carbon|null $hidden_at
* @property int|null $hidden_user_id
* @property UserState|null $state
* @property \Illuminate\Database\Eloquent\Collection $posts
* @property \Illuminate\Database\Eloquent\Collection $comments
* @property \Illuminate\Database\Eloquent\Collection $participants
* @property Post|null $firstPost
* @property User|null $user
* @property Post|null $lastPost
* @property User|null $lastPostedUser
* @property \Illuminate\Database\Eloquent\Collection $readers
* @property bool $is_private
* @property-read UserState|null $state
* @property-read \Illuminate\Database\Eloquent\Collection $posts
* @property-read \Illuminate\Database\Eloquent\Collection $comments
* @property-read \Illuminate\Database\Eloquent\Collection $participants
* @property-read Post|null $firstPost
* @property-read User|null $user
* @property-read Post|null $lastPost
* @property-read User|null $lastPostedUser
* @property-read \Illuminate\Database\Eloquent\Collection $readers
*/
class Discussion extends AbstractModel
{
@ -69,12 +69,16 @@ class Discussion extends AbstractModel
*/
protected array $modifiedPosts = [];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'id' => 'integer',
'comment_count' => 'integer',
'participant_count' => 'integer',
'user_id' => 'integer',
'first_post_id' => 'integer',
'last_posted_user_id' => 'integer',
'last_post_id' => 'integer',
'last_post_number' => 'integer',
'hidden_user_id' => 'integer',
'is_private' => 'boolean',
'created_at' => 'datetime',
'last_posted_at' => 'datetime',
@ -224,6 +228,8 @@ class Discussion extends AbstractModel
/**
* Get the posts that have been modified during this request.
*
* @return Post[]
*/
public function getModifiedPosts(): array
{

View File

@ -38,7 +38,7 @@ class DiscussionRepository
* Get the IDs of discussions which a user has read completely.
*
* @param User $user
* @return Collection<Discussion>
* @return Collection<int, Discussion>
* @deprecated 1.3 Use `getReadIdsQuery` instead
*/
public function getReadIds(User $user): Collection

View File

@ -28,8 +28,8 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property int $discussion_id
* @property Carbon|null $last_read_at
* @property int|null $last_read_post_number
* @property Discussion $discussion
* @property User $user
* @property-read Discussion $discussion
* @property-read User $user
*/
class UserState extends AbstractModel
{
@ -37,12 +37,12 @@ class UserState extends AbstractModel
protected $table = 'discussion_user';
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $casts = ['last_read_at' => 'datetime'];
protected $casts = [
'user_id' => 'integer',
'discussion_id' => 'integer',
'last_read_post_number' => 'integer',
'last_read_at' => 'datetime'
];
/**
* The attributes that are mass assignable.

View File

@ -95,7 +95,7 @@ class ExtensionManager
$extension = $extensions->get($enabledKey);
if (is_null($extension)) {
$needsReset = true;
} else {
} else { // @phpstan-ignore-line
$enabledExtensions[] = $extension;
}
}

View File

@ -236,4 +236,17 @@ class Application
}
}
}
public function version(): string
{
return static::VERSION;
}
public function terminating(): void
{
}
public function terminate(): void
{
}
}

View File

@ -9,13 +9,19 @@
namespace Flarum\Foundation;
class Container extends \Illuminate\Container\Container
{
public function terminating(): void
{
}
use Illuminate\Container\Container as LaravelContainer;
public function terminate(): void
class Container extends LaravelContainer
{
/**
* Laravel's application is the container itself.
* So as we upgrade Laravel versions, some of its internals that we rely on
* make calls to methods that don't exist in our container, but do in Laravel's Application.
*
* @TODO: Implement the Application contract and merge the container into it.
*/
public function __call(string $name, array $arguments)
{
return $this->get('flarum')->$name(...$arguments);
}
}

View File

@ -39,6 +39,7 @@ use Illuminate\Cache\Repository as CacheRepository;
use Illuminate\Config\Repository as ConfigRepository;
use Illuminate\Contracts\Cache\Repository;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Contracts\Container\Container as LaravelContainer;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Hashing\HashServiceProvider;
use Illuminate\Validation\ValidationServiceProvider;
@ -86,7 +87,7 @@ class InstalledSite implements SiteInterface
return $this;
}
protected function bootLaravel(): Container
protected function bootLaravel(): LaravelContainer
{
$container = new Container;
$laravel = new Application($container, $this->paths);
@ -95,7 +96,7 @@ class InstalledSite implements SiteInterface
$container->instance('flarum.config', $this->config);
$container->alias('flarum.config', Config::class);
$container->instance('flarum.debug', $this->config->inDebugMode());
$container->instance('config', $config = $this->getIlluminateConfig());
$container->instance('config', $this->getIlluminateConfig());
$container->instance('flarum.maintenance.handler', new MaintenanceModeHandler);
$this->registerLogger($container);

View File

@ -26,8 +26,8 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
* @property string|null $color
* @property string|null $icon
* @property bool $is_hidden
* @property \Illuminate\Database\Eloquent\Collection $users
* @property \Illuminate\Database\Eloquent\Collection $permissions
* @property-read \Illuminate\Database\Eloquent\Collection $users
* @property-read \Illuminate\Database\Eloquent\Collection $permissions
*/
class Group extends AbstractModel
{
@ -39,12 +39,9 @@ class Group extends AbstractModel
const MEMBER_ID = 3;
const MODERATOR_ID = 4;
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $casts = [
'id' => 'integer',
'is_hidden' => 'boolean',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];

View File

@ -21,12 +21,10 @@ class Permission extends AbstractModel
{
protected $table = 'group_permission';
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $casts = ['created_at' => 'datetime'];
protected $casts = [
'group_id' => 'integer',
'created_at' => 'datetime'
];
public function group(): BelongsTo
{

View File

@ -29,7 +29,7 @@ use Psr\Http\Message\ServerRequestInterface;
* @property string $title
* @property string|null $last_ip_address
* @property string|null $last_user_agent
* @property \Flarum\User\User|null $user
* @property-read \Flarum\User\User|null $user
*/
class AccessToken extends AbstractModel
{
@ -38,6 +38,8 @@ class AccessToken extends AbstractModel
protected $table = 'access_tokens';
protected $casts = [
'id' => 'integer',
'user_id' => 'integer',
'created_at' => 'datetime',
'last_activity_at' => 'datetime',
];

View File

@ -19,8 +19,8 @@ use Illuminate\Database\ConnectionInterface;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use League\Flysystem\Adapter\Local;
use League\Flysystem\Filesystem;
use League\Flysystem\Local\LocalFilesystemAdapter;
class EnableBundledExtensions implements Step
{
@ -68,8 +68,9 @@ class EnableBundledExtensions implements Step
foreach ($extensions as $extension) {
$extension->migrate($this->getMigrator());
$adapter = new LocalFilesystemAdapter($this->assetPath);
$extension->copyAssetsTo(
new FilesystemAdapter(new Filesystem(new Local($this->assetPath)))
new FilesystemAdapter(new Filesystem($adapter), $adapter)
);
}
@ -81,7 +82,7 @@ class EnableBundledExtensions implements Step
}
/**
* @return Collection<Extension>
* @return Collection<string, Extension>
*/
private function loadExtensions(): Collection
{

View File

@ -14,4 +14,5 @@ use Symfony\Contracts\Translation\TranslatorInterface as SymfonyTranslatorInterf
interface TranslatorInterface extends Translator, SymfonyTranslatorInterface
{
public function getLocale(): string;
}

View File

@ -37,22 +37,27 @@ use Illuminate\Support\Arr;
* @property int|null $from_user_id
* @property string $type
* @property int|null $subject_id
* @property mixed|null $data
* @property array|null $data
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $read_at
* @property \Carbon\Carbon $deleted_at
* @property \Flarum\User\User|null $user
* @property \Flarum\User\User|null $fromUser
* @property \Flarum\Database\AbstractModel|\Flarum\Post\Post|\Flarum\Discussion\Discussion|null $subject
* @property-read \Flarum\User\User|null $user
* @property-read \Flarum\User\User|null $fromUser
* @property-read \Flarum\Database\AbstractModel|\Flarum\Post\Post|\Flarum\Discussion\Discussion|null $subject
* @method static \Illuminate\Database\Eloquent\Builder<Notification> matchingBlueprint(BlueprintInterface $blueprint)
*/
class Notification extends AbstractModel
{
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $casts = ['created_at' => 'datetime', 'read_at' => 'datetime'];
protected $casts = [
'id' => 'integer',
'user_id' => 'integer',
'from_user_id' => 'integer',
'subject_id' => 'integer',
'data' => 'array',
'created_at' => 'datetime',
'read_at' => 'datetime',
'deleted_at' => 'datetime',
];
/**
* A map of notification types and the model classes to use for their
@ -69,26 +74,6 @@ class Notification extends AbstractModel
$this->read_at = Carbon::now();
}
/**
* When getting the data attribute, unserialize the JSON stored in the
* database into a plain array.
*/
public function getDataAttribute(?string $value): mixed
{
return $value !== null
? json_decode($value, true)
: null;
}
/**
* When setting the data attribute, serialize it into JSON for storage in
* the database.
*/
public function setDataAttribute(mixed $value): void
{
$this->attributes['data'] = json_encode($value);
}
/**
* Get the subject model for this notification record by looking up its
* type in our subject model map.

View File

@ -15,6 +15,9 @@ use Illuminate\Database\Eloquent\Collection;
class NotificationRepository
{
/**
* @return Collection<int, Notification>
*/
public function findByUser(User $user, ?int $limit = null, int $offset = 0): Collection
{
$primaries = Notification::query()

View File

@ -70,7 +70,8 @@ class NotificationSyncer
continue;
}
$existing = $toDelete->first(function ($notification) use ($user) {
/** @var Notification|null $existing */
$existing = $toDelete->first(function (Notification $notification) use ($user) {
return $notification->user_id === $user->id;
});

View File

@ -33,12 +33,12 @@ use Staudenmeir\EloquentEagerLimit\HasEagerLimit;
* @property int|null $edited_user_id
* @property \Carbon\Carbon|null $hidden_at
* @property int|null $hidden_user_id
* @property \Flarum\Discussion\Discussion|null $discussion
* @property User|null $user
* @property User|null $editedUser
* @property User|null $hiddenUser
* @property string $ip_address
* @property bool $is_private
* @property-read Discussion|null $discussion
* @property-read User|null $user
* @property-read User|null $editedUser
* @property-read User|null $hiddenUser
*/
class Post extends AbstractModel
{
@ -48,12 +48,13 @@ class Post extends AbstractModel
protected $table = 'posts';
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'id' => 'integer',
'discussion_id' => 'integer',
'number' => 'integer',
'user_id' => 'integer',
'edited_user_id' => 'integer',
'hidden_user_id' => 'integer',
'is_private' => 'boolean',
'created_at' => 'datetime',
'edited_at' => 'datetime',

View File

@ -24,12 +24,10 @@ use Illuminate\Support\Str;
*/
class EmailToken extends AbstractModel
{
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $casts = ['created_at' => 'datetime'];
protected $casts = [
'user_id' => 'integer',
'created_at' => 'datetime',
];
/**
* Use a custom primary key for this model.

View File

@ -21,18 +21,11 @@ use Illuminate\Support\Str;
*/
class PasswordToken extends AbstractModel
{
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $casts = ['created_at' => 'datetime'];
protected $casts = [
'created_at' => 'datetime',
'user_id' => 'integer',
];
/**
* Use a custom primary key for this model.
*
* @var bool
*/
public $incrementing = false;
protected $primaryKey = 'token';

View File

@ -63,6 +63,8 @@ class User extends AbstractModel
use HasEagerLimit;
protected $casts = [
'id' => 'integer',
'is_email_confirmed' => 'boolean',
'joined_at' => 'datetime',
'last_seen_at' => 'datetime',
'marked_all_as_read_at' => 'datetime',

View File

@ -0,0 +1,22 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
if (! defined('LARAVEL_VERSION')) {
define('LARAVEL_VERSION', '10.0');
}
if (! function_exists('database_path')) {
function database_path($path = ''): string
{
return __DIR__."/../../$path";
}
}
$site = (new \Flarum\Testing\integration\Setup\Bootstrapper())->run();
$site->bootApp();

View File

@ -4,8 +4,9 @@
"minimum-stability": "dev",
"license": "MIT",
"require": {
"phpstan/phpstan": ">=1.8.11 < 1.9.0",
"nunomaduro/larastan": "^1.0"
"flarum/testing": "^2.0",
"phpstan/phpstan": "^1.10.0",
"nunomaduro/larastan": "^2.6"
},
"autoload": {
"psr-4": {

View File

@ -1,38 +1,7 @@
parameters:
stubFiles:
- %rootDir%/../../nunomaduro/larastan/stubs/Enumerable.stub
- %rootDir%/../../nunomaduro/larastan/stubs/EloquentBuilder.stub
- %rootDir%/../../nunomaduro/larastan/stubs/Collection.stub
- %rootDir%/../../nunomaduro/larastan/stubs/EloquentCollection.stub
- %rootDir%/../../nunomaduro/larastan/stubs/Factory.stub
- %rootDir%/../../nunomaduro/larastan/stubs/Model.stub
- %rootDir%/../../nunomaduro/larastan/stubs/Gate.stub
- %rootDir%/../../nunomaduro/larastan/stubs/Relation.stub
- %rootDir%/../../nunomaduro/larastan/stubs/BelongsTo.stub
- %rootDir%/../../nunomaduro/larastan/stubs/BelongsToMany.stub
- %rootDir%/../../nunomaduro/larastan/stubs/HasOneOrMany.stub
- %rootDir%/../../nunomaduro/larastan/stubs/HasMany.stub
- %rootDir%/../../nunomaduro/larastan/stubs/HasOne.stub
- %rootDir%/../../nunomaduro/larastan/stubs/HasOneThrough.stub
- %rootDir%/../../nunomaduro/larastan/stubs/HasManyThrough.stub
- %rootDir%/../../nunomaduro/larastan/stubs/Mailable.stub
- %rootDir%/../../nunomaduro/larastan/stubs/MorphOne.stub
- %rootDir%/../../nunomaduro/larastan/stubs/MorphOneOrMany.stub
- %rootDir%/../../nunomaduro/larastan/stubs/MorphTo.stub
- %rootDir%/../../nunomaduro/larastan/stubs/MorphToMany.stub
- %rootDir%/../../nunomaduro/larastan/stubs/MorphMany.stub
- %rootDir%/../../nunomaduro/larastan/stubs/Helpers.stub
- %rootDir%/../../nunomaduro/larastan/stubs/HigherOrderProxies.stub
- %rootDir%/../../nunomaduro/larastan/stubs/QueryBuilder.stub
- %rootDir%/../../nunomaduro/larastan/stubs/Facades.stub
- %rootDir%/../../nunomaduro/larastan/stubs/Pagination.stub
- %rootDir%/../../nunomaduro/larastan/stubs/Contracts/Pagination.stub
- %rootDir%/../../nunomaduro/larastan/stubs/Contracts/Support.stub
- %rootDir%/../../nunomaduro/larastan/stubs/Redis/Connection.stub
- %rootDir%/../../nunomaduro/larastan/stubs/Logger.stub
- %rootDir%/../../nunomaduro/larastan/stubs/EnumeratesValues.stub
universalObjectCratesClasses:
- Illuminate\Http\Request
- Illuminate\Support\Optional
earlyTerminatingFunctionCalls:
- abort
- dd
@ -40,17 +9,21 @@ parameters:
- *.blade.php
mixinExcludeClasses:
- Eloquent
# bootstrapFiles:
# - bootstrap.php
checkGenericClassInNonGenericObjectType: false
bootstrapFiles:
- bootstrap.php
checkOctaneCompatibility: false
noModelMake: true
noUnnecessaryCollectionCall: true
noUnnecessaryCollectionCallOnly: []
noUnnecessaryCollectionCallExcept: []
squashedMigrationsPath: []
databaseMigrationsPath: []
disableMigrationScan: false
disableSchemaScan: false
viewDirectories: []
checkModelProperties: false
checkPhpDocMissingReturn: false
checkUnusedViews: false
parametersSchema:
checkOctaneCompatibility: bool()
@ -59,7 +32,12 @@ parametersSchema:
noUnnecessaryCollectionCallOnly: listOf(string())
noUnnecessaryCollectionCallExcept: listOf(string())
databaseMigrationsPath: listOf(string())
disableMigrationScan: bool()
viewDirectories: listOf(string())
squashedMigrationsPath: listOf(string())
disableSchemaScan: bool()
checkModelProperties: bool()
checkUnusedViews: bool()
conditionalTags:
NunoMaduro\Larastan\Rules\NoModelMakeRule:
@ -72,6 +50,8 @@ conditionalTags:
phpstan.rules.rule: %checkModelProperties%
NunoMaduro\Larastan\Rules\ModelProperties\ModelPropertyStaticCallRule:
phpstan.rules.rule: %checkModelProperties%
NunoMaduro\Larastan\Rules\UnusedViewsRule:
phpstan.rules.rule: %checkUnusedViews%
services:
-
@ -116,6 +96,14 @@ services:
class: NunoMaduro\Larastan\Methods\RedirectResponseMethodsClassReflectionExtension
tags:
- phpstan.broker.methodsClassReflectionExtension
-
class: NunoMaduro\Larastan\Methods\MacroMethodsClassReflectionExtension
tags:
- phpstan.broker.methodsClassReflectionExtension
-
class: NunoMaduro\Larastan\Methods\ViewWithMethodsClassReflectionExtension
tags:
- phpstan.broker.methodsClassReflectionExtension
-
class: NunoMaduro\Larastan\Properties\ModelAccessorExtension
@ -182,7 +170,12 @@ services:
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\ModelExtension
class: NunoMaduro\Larastan\ReturnTypes\ModelDynamicStaticMethodReturnTypeExtension
tags:
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\AppMakeDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
@ -201,13 +194,28 @@ services:
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\DateExtension
tags:
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\GuardExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\RequestExtension
class: NunoMaduro\Larastan\ReturnTypes\RequestFileExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\RequestRouteExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\RequestUserExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
@ -241,11 +249,6 @@ services:
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\CollectionMakeDynamicStaticMethodReturnTypeExtension
tags:
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\Support\CollectionHelper
@ -260,7 +263,7 @@ services:
- phpstan.broker.dynamicFunctionReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\Helpers\CookieExtension
class: NunoMaduro\Larastan\ReturnTypes\Helpers\NowAndTodayExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
@ -269,31 +272,6 @@ services:
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\Helpers\RequestExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\Helpers\RedirectExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\Helpers\UrlExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\Helpers\ViewExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\Helpers\TransExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\Helpers\ValidatorExtension
tags:
@ -303,6 +281,30 @@ services:
class: NunoMaduro\Larastan\ReturnTypes\CollectionFilterDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\CollectionWhereNotNullDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\CollectionGenericStaticMethodDynamicMethodReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\NewModelQueryDynamicMethodReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\FactoryDynamicMethodReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\CollectionGenericStaticMethodDynamicStaticMethodReturnTypeExtension
tags:
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\Types\AbortIfFunctionTypeSpecifyingExtension
@ -388,6 +390,10 @@ services:
class: NunoMaduro\Larastan\Types\GenericEloquentBuilderTypeNodeResolverExtension
tags:
- phpstan.phpDoc.typeNodeResolverExtension
-
class: NunoMaduro\Larastan\ReturnTypes\AppEnvironmentReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\Types\ModelProperty\ModelPropertyTypeNodeResolverExtension
@ -405,8 +411,18 @@ services:
class: NunoMaduro\Larastan\Properties\MigrationHelper
arguments:
databaseMigrationPath: %databaseMigrationsPath%
disableMigrationScan: %disableMigrationScan%
parser: @currentPhpVersionSimpleDirectParser
-
class: NunoMaduro\Larastan\Properties\SquashedMigrationHelper
arguments:
schemaPaths: %squashedMigrationsPath%
disableSchemaScan: %disableSchemaScan%
-
class: NunoMaduro\Larastan\Properties\ModelCastHelper
-
class: NunoMaduro\Larastan\Rules\ModelProperties\ModelPropertiesRuleHelper
@ -421,7 +437,7 @@ services:
-
class: NunoMaduro\Larastan\Rules\RelationExistenceRule
tags:
- phpstan.rule
- phpstan.rules.rule
-
class: NunoMaduro\Larastan\Rules\CheckDispatchArgumentTypesCompatibleWithClassConstructorRule
@ -435,5 +451,94 @@ services:
dispatchableClass: Illuminate\Foundation\Events\Dispatchable
tags:
- phpstan.rules.rule
- NunoMaduro\Larastan\Properties\Schema\PhpMyAdminDataTypeToPhpTypeConverter
-
class: NunoMaduro\Larastan\LarastanStubFilesExtension
tags: [phpstan.stubFilesExtension]
-
class: NunoMaduro\Larastan\Rules\UnusedViewsRule
-
class: NunoMaduro\Larastan\Collectors\UsedViewFunctionCollector
tags:
- phpstan.collector
-
class: NunoMaduro\Larastan\Collectors\UsedEmailViewCollector
tags:
- phpstan.collector
-
class: NunoMaduro\Larastan\Collectors\UsedViewMakeCollector
tags:
- phpstan.collector
-
class: NunoMaduro\Larastan\Collectors\UsedViewFacadeMakeCollector
tags:
- phpstan.collector
-
class: NunoMaduro\Larastan\Collectors\UsedRouteFacadeViewCollector
tags:
- phpstan.collector
-
class: NunoMaduro\Larastan\Collectors\UsedViewInAnotherViewCollector
arguments:
parser: @currentPhpVersionSimpleDirectParser
-
class: NunoMaduro\Larastan\Support\ViewFileHelper
arguments:
viewDirectories: %viewDirectories%
# -
# class: NunoMaduro\Larastan\ReturnTypes\ApplicationMakeDynamicReturnTypeExtension
# tags:
# - phpstan.broker.dynamicMethodReturnTypeExtension
#
# -
# class: NunoMaduro\Larastan\ReturnTypes\ContainerMakeDynamicReturnTypeExtension
# tags:
# - phpstan.broker.dynamicMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\ConsoleCommand\ArgumentDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\ConsoleCommand\HasArgumentDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\ConsoleCommand\OptionDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\ConsoleCommand\HasOptionDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\TranslatorGetReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\TransHelperReturnTypeExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
-
class: NunoMaduro\Larastan\ReturnTypes\DoubleUnderscoreHelperReturnTypeExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
- NunoMaduro\Larastan\ReturnTypes\AppMakeHelper
- NunoMaduro\Larastan\Internal\ConsoleApplicationResolver
- NunoMaduro\Larastan\Internal\ConsoleApplicationHelper
- NunoMaduro\Larastan\Support\HigherOrderCollectionProxyHelper
rules:
- NunoMaduro\Larastan\Rules\RelationExistenceRule
- NunoMaduro\Larastan\Rules\UselessConstructs\NoUselessWithFunctionCallsRule
- NunoMaduro\Larastan\Rules\UselessConstructs\NoUselessValueFunctionCallsRule
- NunoMaduro\Larastan\Rules\DeferrableServiceProviderMissingProvidesRule
- NunoMaduro\Larastan\Rules\ConsoleCommand\UndefinedArgumentOrOptionRule

View File

@ -16,6 +16,8 @@ parameters:
# @TODO: needs discussion.
- message: "#^Unsafe usage of new static[()]{2}.$#"
reportUnmatched: false
- message: "#^Static access to instance property .*#"
reportUnmatched: false
# ConnectionInterface lacks methods that exist in the implementation,
# yet we don't want to inject the implementation.

View File

@ -16,6 +16,7 @@ use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\PropertyReflection;
use PHPStan\TrinaryLogic;
use PHPStan\Type\Generic\GenericObjectType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
@ -59,18 +60,14 @@ class RelationProperty implements PropertyReflection
public function getReadableType(): Type
{
switch ($this->methodCall->methodName) {
case 'hasMany':
case 'belongsToMany':
return new GenericObjectType(Collection::class, [new ObjectType($this->methodCall->arguments[1]->class->toString())]);
case 'hasOne':
case 'belongsTo':
return new ObjectType($this->methodCall->arguments[1]->class->toString());
default:
throw new Exception('Unknown relationship type for relation: '.$this->methodCall->methodName);
}
return match ($this->methodCall->methodName) {
'hasMany', 'belongsToMany' => new GenericObjectType(Collection::class, [
new IntegerType(),
new ObjectType($this->methodCall->arguments[1]->class->toString())
]),
'hasOne', 'belongsTo' => new ObjectType($this->methodCall->arguments[1]->class->toString()),
default => throw new Exception('Unknown relationship type for relation: '.$this->methodCall->methodName),
};
}
public function getWritableType(): Type

View File

@ -0,0 +1,80 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Testing\integration\Setup;
use Flarum\Foundation\Config;
use Flarum\Foundation\InstalledSite;
use Flarum\Foundation\Paths;
use Flarum\Testing\integration\Extend\BeginTransactionAndSetDatabase;
use Flarum\Testing\integration\Extend\OverrideExtensionManagerForTests;
use Flarum\Testing\integration\Extend\SetSettingsBeforeBoot;
use Flarum\Testing\integration\UsesTmpDir;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Support\Arr;
class Bootstrapper
{
use UsesTmpDir;
public ?ConnectionInterface $database = null;
public function __construct(
protected array $config = [],
protected array $extensions = [],
protected array $settings = [],
protected array $extenders = []
) {
}
public function setupOnce(): void
{
$tmp = $this->tmpDir();
if (! file_exists("$tmp/config.php")) {
$setup = new SetupScript();
$setup->run();
}
}
public function run(): InstalledSite
{
$this->setupOnce();
$tmp = $this->tmpDir();
$config = include "$tmp/config.php";
foreach ($this->config as $key => $value) {
Arr::set($config, $key, $value);
}
$site = new InstalledSite(
new Paths([
'base' => $tmp,
'public' => "$tmp/public",
'storage' => "$tmp/storage",
'vendor' => getenv('FLARUM_TEST_VENDOR_PATH') ?: getcwd().'/vendor',
]),
new Config($config)
);
$extenders = array_merge([
new OverrideExtensionManagerForTests($this->extensions),
new BeginTransactionAndSetDatabase(function (ConnectionInterface $db) {
$this->database = $db;
}),
new SetSettingsBeforeBoot($this->settings),
], $this->extenders);
$site->extendWith($extenders);
return $site;
}
}

View File

@ -10,12 +10,7 @@
namespace Flarum\Testing\integration;
use Flarum\Extend\ExtenderInterface;
use Flarum\Foundation\Config;
use Flarum\Foundation\InstalledSite;
use Flarum\Foundation\Paths;
use Flarum\Testing\integration\Extend\BeginTransactionAndSetDatabase;
use Flarum\Testing\integration\Extend\OverrideExtensionManagerForTests;
use Flarum\Testing\integration\Extend\SetSettingsBeforeBoot;
use Flarum\Testing\integration\Setup\Bootstrapper;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Support\Arr;
@ -51,35 +46,16 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase
protected function app()
{
if (is_null($this->app)) {
$tmp = $this->tmpDir();
$config = include "$tmp/config.php";
foreach ($this->config as $key => $value) {
Arr::set($config, $key, $value);
}
$site = new InstalledSite(
new Paths([
'base' => $tmp,
'public' => "$tmp/public",
'storage' => "$tmp/storage",
'vendor' => getenv('FLARUM_TEST_VENDOR_PATH') ?: getcwd().'/vendor',
]),
new Config($config)
$bootstrapper = new Bootstrapper(
$this->config,
$this->extensions,
$this->settings,
$this->extenders
);
$extenders = array_merge([
new OverrideExtensionManagerForTests($this->extensions),
new BeginTransactionAndSetDatabase(function (ConnectionInterface $db) {
$this->database = $db;
}),
new SetSettingsBeforeBoot($this->settings),
], $this->extenders);
$this->app = $bootstrapper->run()->bootApp();
$site->extendWith($extenders);
$this->app = $site->bootApp();
$this->database = $bootstrapper->database;
$this->populateDatabase();
}