1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2025-12-12 08:24:04 +00:00

get phpstan errors down from 252 to 189

This commit is contained in:
Samuel Štancl 2022-08-30 05:44:23 +02:00
parent fb8b9c1614
commit 8af354c20e
40 changed files with 119 additions and 115 deletions

View file

@ -8,8 +8,24 @@ php-cs-fixer will fix code style violations in your pull requests.
Run `composer docker-up` to start the containers. Then run `composer test` to run the tests.
If you need to pass additional flags to phpunit, use `./test --foo` instead of `composer test --foo`. Composer scripts unfortunately don't pass CLI arguments.
When you're done testing, run `composer docker-down` to shut down the containers.
### Docker on M1
Run `composer docker-m1` to symlink `docker-compose-m1.override.yml` to `docker-compose.override.yml`. This will reconfigure a few services in the docker compose config to be compatible with M1.
### Coverage reports
To run tests and generate coverage reports, use `composer test-full`.
To view the coverage reports in your browser, use `composer coverage` (works on macOS; on other operating systems you can manually open `coverage/phpunit/html/index.html` in your browser).
### Rebuilding containers
If you need to rebuild the container for any reason (e.g. a change in `Dockerfile`), you can use `composer docker-rebuild`.
## PHPStan
Use `composer phpstan` to run our phpstan suite.

View file

@ -62,6 +62,7 @@
"docker-rebuild": "PHP_VERSION=8.1 docker-compose up -d --no-deps --build",
"docker-m1": "ln -s docker-compose-m1.override.yml docker-compose.override.yml",
"coverage": "open coverage/phpunit/html/index.html",
"phpstan": "vendor/bin/phpstan",
"test": "PHP_VERSION=8.1 ./test --no-coverage",
"test-full": "PHP_VERSION=8.1 ./test"
},

View file

@ -12,7 +12,14 @@ parameters:
- Illuminate\Routing\Route
ignoreErrors:
-
message: '#Cannot access offset (.*?) on Illuminate\\Contracts\\Foundation\\Application#'
paths:
- src/TenancyServiceProvider.php
-
message: '#invalid type Laravel\\Telescope\\IncomingEntry#'
paths:
- src/Features/TelescopeTags.php
checkMissingIterableValueType: false
treatPhpDocTypesAsCertain: false

View file

@ -8,11 +8,11 @@ interface Syncable
{
public function getGlobalIdentifierKeyName(): string;
public function getGlobalIdentifierKey();
public function getGlobalIdentifierKey(): string|int;
public function getCentralModelName(): string;
public function getSyncedAttributeNames(): array;
public function triggerSyncEvent();
public function triggerSyncEvent(): void;
}

View file

@ -11,5 +11,5 @@ interface TenantResolver
*
* @throws TenantCouldNotBeIdentifiedException
*/
public function resolve(...$args): Tenant;
public function resolve(mixed ...$args): Tenant;
}

View file

@ -17,7 +17,7 @@ class TenantAssetsController extends Controller
$this->middleware(static::$tenancyMiddleware);
}
public function asset($path = null)
public function asset(string $path = null)
{
abort_if($path === null, 404);

View file

@ -10,7 +10,7 @@ trait BelongsToPrimaryModel
{
abstract public function getRelationshipToPrimaryModel(): string;
public static function bootBelongsToPrimaryModel()
public static function bootBelongsToPrimaryModel(): void
{
static::addGlobalScope(new ParentModelScope);
}

View file

@ -19,7 +19,7 @@ trait BelongsToTenant
return $this->belongsTo(config('tenancy.tenant_model'), BelongsToTenant::$tenantIdColumn);
}
public static function bootBelongsToTenant()
public static function bootBelongsToTenant(): void
{
static::addGlobalScope(new TenantScope);

View file

@ -6,7 +6,7 @@ namespace Stancl\Tenancy\Database\Concerns;
trait ConvertsDomainsToLowercase
{
public static function bootConvertsDomainsToLowercase()
public static function bootConvertsDomainsToLowercase(): void
{
static::saving(function ($model) {
$model->domain = strtolower($model->domain);

View file

@ -8,7 +8,7 @@ use Stancl\Tenancy\Exceptions\DomainOccupiedByOtherTenantException;
trait EnsuresDomainIsNotOccupied
{
public static function bootEnsuresDomainIsNotOccupied()
public static function bootEnsuresDomainIsNotOccupied(): void
{
static::saving(function ($self) {
if ($domain = $self->newQuery()->where('domain', $self->domain)->first()) {

View file

@ -8,7 +8,7 @@ use Stancl\Tenancy\Contracts\UniqueIdentifierGenerator;
trait GeneratesIds
{
public static function bootGeneratesIds()
public static function bootGeneratesIds(): void
{
static::creating(function (self $model) {
if (! $model->getKey() && $model->shouldGenerateId()) {

View file

@ -16,7 +16,7 @@ trait InvalidatesResolverCache
Resolvers\RequestDataTenantResolver::class,
];
public static function bootInvalidatesResolverCache()
public static function bootInvalidatesResolverCache(): void
{
static::saved(function (Tenant $tenant) {
foreach (static::$resolvers as $resolver) {

View file

@ -19,7 +19,7 @@ trait InvalidatesTenantsResolverCache
Resolvers\RequestDataTenantResolver::class,
];
public static function bootInvalidatesTenantsResolverCache()
public static function bootInvalidatesTenantsResolverCache(): void
{
static::saved(function (Model $model) {
foreach (static::$resolvers as $resolver) {

View file

@ -10,7 +10,7 @@ use Stancl\Tenancy\Events\SyncedResourceSaved;
trait ResourceSyncing
{
public static function bootResourceSyncing()
public static function bootResourceSyncing(): void
{
static::saved(function (Syncable $model) {
/** @var ResourceSyncing $model */
@ -27,7 +27,7 @@ trait ResourceSyncing
});
}
public function triggerSyncEvent()
public function triggerSyncEvent(): void
{
/** @var Syncable $this */
event(new SyncedResourceSaved($this, tenant()));

View file

@ -34,7 +34,7 @@ class DatabaseManager
/**
* Connect to a tenant's database.
*/
public function connectToTenant(TenantWithDatabase $tenant)
public function connectToTenant(TenantWithDatabase $tenant): void
{
$this->purgeTenantConnection();
$this->createTenantConnection($tenant);
@ -44,7 +44,7 @@ class DatabaseManager
/**
* Reconnect to the default non-tenant connection.
*/
public function reconnectToCentral()
public function reconnectToCentral(): void
{
$this->purgeTenantConnection();
$this->setDefaultConnection($this->config->get('tenancy.database.central_connection'));
@ -53,7 +53,7 @@ class DatabaseManager
/**
* Change the default database connection config.
*/
public function setDefaultConnection(string $connection)
public function setDefaultConnection(string $connection): void
{
$this->config['database.default'] = $connection;
$this->database->setDefaultConnection($connection);
@ -62,7 +62,7 @@ class DatabaseManager
/**
* Create the tenant database connection.
*/
public function createTenantConnection(TenantWithDatabase $tenant)
public function createTenantConnection(TenantWithDatabase $tenant): void
{
$this->config['database.connections.tenant'] = $tenant->database()->connection();
}
@ -70,7 +70,7 @@ class DatabaseManager
/**
* Purge the tenant database connection.
*/
public function purgeTenantConnection()
public function purgeTenantConnection(): void
{
if (array_key_exists('tenant', $this->database->getConnections())) {
$this->database->purge('tenant');

View file

@ -8,7 +8,7 @@ use Exception;
class NoConnectionSetException extends Exception
{
public function __construct($manager)
public function __construct(string $manager)
{
parent::__construct("No connection was set on this $manager instance.");
}

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Database\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Stancl\Tenancy\Contracts;
use Stancl\Tenancy\Contracts\Tenant;
use Stancl\Tenancy\Database\Concerns;
@ -25,7 +26,7 @@ class Domain extends Model implements Contracts\Domain
protected $guarded = [];
public function tenant()
public function tenant(): BelongsTo
{
return $this->belongsTo(config('tenancy.tenant_model'));
}

View file

@ -10,12 +10,12 @@ use Illuminate\Support\Str;
use Stancl\Tenancy\Database\Concerns\CentralConnection;
/**
* @param string $token
* @param string $tenant_id
* @param string $user_id
* @param string $auth_guard
* @param string $redirect_url
* @param Carbon $created_at
* @property string $token
* @property string $tenant_id
* @property string $user_id
* @property string $auth_guard
* @property string $redirect_url
* @property Carbon $created_at
*/
class ImpersonationToken extends Model
{
@ -35,7 +35,7 @@ class ImpersonationToken extends Model
'created_at',
];
public static function booted()
public static function booted(): void
{
static::creating(function ($model) {
$model->created_at = $model->created_at ?? $model->freshTimestamp();

View file

@ -9,7 +9,7 @@ use Stancl\Tenancy\Contracts\Syncable;
class TenantPivot extends Pivot
{
public static function booted()
public static function booted(): void
{
static::saved(function (self $pivot) {
$parent = $pivot->pivotParent;

View file

@ -10,13 +10,12 @@ use Stancl\Tenancy\Database\Contracts\TenantWithDatabase;
class SyncedResourceSaved
{
/** @var Syncable|Model */
public $model;
public Syncable&Model $model;
/** @var TenantWithDatabase|Model|null */
public $tenant;
/** @var (TenantWithDatabase&Model)|null */
public TenantWithDatabase|null $tenant;
public function __construct(Syncable $model, ?TenantWithDatabase $tenant)
public function __construct(Syncable $model, TenantWithDatabase|null $tenant)
{
$this->model = $model;
$this->tenant = $tenant;

View file

@ -19,8 +19,7 @@ class TenantConfig implements Feature
/** @var Repository */
protected $config;
/** @var array */
public $originalConfig = [];
public array $originalConfig = [];
public static $storageToConfigMap = [
// 'paypal_api_key' => 'services.paypal.api_key',
@ -51,14 +50,14 @@ class TenantConfig implements Feature
if (! is_null($override)) {
if (is_array($configKey)) {
foreach ($configKey as $key) {
$this->originalConfig[$key] = $this->originalConfig[$key] ?? $this->config[$key];
$this->originalConfig[$key] = $this->originalConfig[$key] ?? $this->config->get($key);
$this->config[$key] = $override;
$this->config->set($key, $override);
}
} else {
$this->originalConfig[$configKey] = $this->originalConfig[$configKey] ?? $this->config[$configKey];
$this->originalConfig[$configKey] = $this->originalConfig[$configKey] ?? $this->config->get($configKey);
$this->config[$configKey] = $override;
$this->config->set($configKey, $override);
}
}
}
@ -67,7 +66,7 @@ class TenantConfig implements Feature
public function unsetTenantConfig(): void
{
foreach ($this->originalConfig as $key => $value) {
$this->config[$key] = $value;
$this->config->set($key, $value);
}
}
}

View file

@ -31,6 +31,7 @@ class UserImpersonation implements Feature
/** Impersonate a user and get an HTTP redirect response. */
public static function makeResponse(string|ImpersonationToken $token, int $ttl = null): RedirectResponse
{
/** @var ImpersonationToken $token */
$token = $token instanceof ImpersonationToken ? $token : ImpersonationToken::findOrFail($token);
$ttl ??= static::$ttl;

View file

@ -19,13 +19,9 @@ class CreateDatabase implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/** @var TenantWithDatabase|Model */
protected $tenant;
public function __construct(TenantWithDatabase $tenant)
{
$this->tenant = $tenant;
}
public function __construct(
protected TenantWithDatabase&Model $tenant,
) {}
public function handle(DatabaseManager $databaseManager)
{

View file

@ -6,6 +6,7 @@ namespace Stancl\Tenancy\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
@ -17,15 +18,11 @@ class DeleteDatabase implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/** @var TenantWithDatabase */
protected $tenant;
public function __construct(
protected TenantWithDatabase&Model $tenant,
) {}
public function __construct(TenantWithDatabase $tenant)
{
$this->tenant = $tenant;
}
public function handle()
public function handle(): void
{
event(new DeletingDatabase($this->tenant));

View file

@ -5,24 +5,26 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Stancl\Tenancy\Database\Contracts\TenantWithDatabase;
use Stancl\Tenancy\Database\Concerns\HasDomains;
class DeleteDomains
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/** @var TenantWithDatabase */
protected $tenant;
/** @var TenantWithDatabase&Model&HasDomains */ // todo unresolvable type for phpstan
protected TenantWithDatabase&Model $tenant;
public function __construct(TenantWithDatabase $tenant)
public function __construct(TenantWithDatabase&Model $tenant)
{
$this->tenant = $tenant;
}
public function handle()
public function handle(): void
{
$this->tenant->domains->each->delete();
}

View file

@ -6,6 +6,7 @@ namespace Stancl\Tenancy\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
@ -16,20 +17,11 @@ class MigrateDatabase implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/** @var TenantWithDatabase */
protected $tenant;
public function __construct(
protected TenantWithDatabase&Model $tenant,
) {}
public function __construct(TenantWithDatabase $tenant)
{
$this->tenant = $tenant;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
public function handle(): void
{
Artisan::call('tenants:migrate', [
'--tenants' => [$this->tenant->getTenantKey()],

View file

@ -6,6 +6,7 @@ namespace Stancl\Tenancy\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
@ -16,20 +17,11 @@ class SeedDatabase implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/** @var TenantWithDatabase */
protected $tenant;
public function __construct(
protected TenantWithDatabase&Model $tenant,
) {}
public function __construct(TenantWithDatabase $tenant)
{
$this->tenant = $tenant;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
public function handle(): void
{
Artisan::call('tenants:seed', [
'--tenants' => [$this->tenant->getTenantKey()],

View file

@ -10,7 +10,7 @@ use Stancl\Tenancy\Events\TenancyInitialized;
class BootstrapTenancy
{
public function handle(TenancyInitialized $event)
public function handle(TenancyInitialized $event): void
{
event(new BootstrappingTenancy($event->tenancy));

View file

@ -17,7 +17,7 @@ class CreateTenantConnection
$this->database = $database;
}
public function handle(TenantEvent $event)
public function handle(TenantEvent $event): void
{
$this->database->createTenantConnection($event->tenant);
}

View file

@ -11,9 +11,9 @@ use Illuminate\Contracts\Queue\ShouldQueue;
*/
abstract class QueueableListener implements ShouldQueue
{
public static $shouldQueue = false;
public static bool $shouldQueue = false;
public function shouldQueue($event)
public function shouldQueue($event): bool
{
if (static::$shouldQueue) {
return true;

View file

@ -10,7 +10,7 @@ use Stancl\Tenancy\Events\TenancyEnded;
class RevertToCentralContext
{
public function handle(TenancyEnded $event)
public function handle(TenancyEnded $event): void
{
event(new RevertingToCentralContext($event->tenancy));

View file

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Listeners;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Pivot;
use Stancl\Tenancy\Contracts\SyncMaster;
@ -13,9 +14,9 @@ use Stancl\Tenancy\Exceptions\ModelNotSyncMasterException;
class UpdateSyncedResource extends QueueableListener
{
public static $shouldQueue = false;
public static bool $shouldQueue = false;
public function handle(SyncedResourceSaved $event)
public function handle(SyncedResourceSaved $event): void
{
$syncedAttributes = $event->model->only($event->model->getSyncedAttributeNames());
@ -29,7 +30,7 @@ class UpdateSyncedResource extends QueueableListener
$this->updateResourceInTenantDatabases($tenants, $event, $syncedAttributes);
}
protected function getTenantsForCentralModel($centralModel)
protected function getTenantsForCentralModel($centralModel): EloquentCollection
{
if (! $centralModel instanceof SyncMaster) {
// If we're trying to use a tenant User model instead of the central User model, for example.
@ -45,7 +46,7 @@ class UpdateSyncedResource extends QueueableListener
return $centralModel->tenants;
}
protected function updateResourceInCentralDatabaseAndGetTenants($event, $syncedAttributes)
protected function updateResourceInCentralDatabaseAndGetTenants($event, $syncedAttributes): EloquentCollection
{
/** @var Model|SyncMaster $centralModel */
$centralModel = $event->model->getCentralModelName()::where($event->model->getGlobalIdentifierKeyName(), $event->model->getGlobalIdentifierKey())
@ -85,7 +86,7 @@ class UpdateSyncedResource extends QueueableListener
});
}
protected function updateResourceInTenantDatabases($tenants, $event, $syncedAttributes)
protected function updateResourceInTenantDatabases($tenants, $event, $syncedAttributes): void
{
tenancy()->runForMultiple($tenants, function ($tenant) use ($event, $syncedAttributes) {
// Forget instance state and find the model,

View file

@ -25,7 +25,7 @@ abstract class CachedTenantResolver implements TenantResolver
$this->cache = $cache->store(static::$cacheStore);
}
public function resolve(...$args): Tenant
public function resolve(mixed ...$args): Tenant
{
if (! static::$shouldCache) {
return $this->resolveWithoutCache(...$args);
@ -58,12 +58,12 @@ abstract class CachedTenantResolver implements TenantResolver
}
}
public function getCacheKey(...$args): string
public function getCacheKey(mixed ...$args): string
{
return '_tenancy_resolver:' . static::class . ':' . json_encode($args);
}
abstract public function resolveWithoutCache(...$args): Tenant;
abstract public function resolveWithoutCache(mixed ...$args): Tenant;
public function resolved(Tenant $tenant, ...$args): void
{

View file

@ -20,7 +20,7 @@ class DomainTenantResolver extends Contracts\CachedTenantResolver
public static string|null $cacheStore = null; // default
public function resolveWithoutCache(...$args): Tenant
public function resolveWithoutCache(mixed ...$args): Tenant
{
$domain = $args[0];

View file

@ -18,7 +18,7 @@ class PathTenantResolver extends Contracts\CachedTenantResolver
public static string|null $cacheStore = null; // default
public function resolveWithoutCache(...$args): Tenant
public function resolveWithoutCache(mixed ...$args): Tenant
{
/** @var Route $route */
$route = $args[0];

View file

@ -15,7 +15,7 @@ class RequestDataTenantResolver extends Contracts\CachedTenantResolver
public static string|null $cacheStore = null; // default
public function resolveWithoutCache(...$args): Tenant
public function resolveWithoutCache(mixed ...$args): Tenant
{
$payload = $args[0];

View file

@ -28,7 +28,7 @@ class Tenancy
public ?Closure $getBootstrappersUsing = null;
/** Is tenancy fully initialized? */
public $initialized = false; // todo document the difference between $tenant being set and $initialized being true (e.g. end of initialize() method)
public bool $initialized = false; // todo document the difference between $tenant being set and $initialized being true (e.g. end of initialize() method)
/** Initialize tenancy for the passed tenant. */
public function initialize(Tenant|int|string $tenant): void
@ -42,6 +42,7 @@ class Tenancy
}
}
// todo0 for phpstan this should be $this->tenant?, but first I want to clean up the $initialized logic and explore removing the property
if ($this->initialized && $this->tenant->getTenantKey() === $tenant->getTenantKey()) {
return;
}

View file

@ -15,15 +15,14 @@ if (! function_exists('tenancy')) {
if (! function_exists('tenant')) {
/**
* Get a key from the current tenant's storage.
* Get the current tenant or a key from the current tenant's properties.
*
* @param string|null $key
* @return Tenant|null|mixed
*/
function tenant($key = null)
function tenant(string $key = null): mixed
{
if (! app()->bound(Tenant::class)) {
return;
return null;
}
if (is_null($key)) {
@ -35,15 +34,15 @@ if (! function_exists('tenant')) {
}
if (! function_exists('tenant_asset')) {
/** @return string */
function tenant_asset($asset)
// todo docblock
function tenant_asset(string|null $asset): string
{
return route('stancl.tenancy.asset', ['path' => $asset]);
}
}
if (! function_exists('global_asset')) {
function global_asset($asset)
function global_asset(string $asset) // todo types, also inside the globalUrl implementation
{
return app('globalUrl')->asset($asset);
}
@ -57,9 +56,9 @@ if (! function_exists('global_cache')) {
}
if (! function_exists('tenant_route')) {
function tenant_route(string $domain, $route, $parameters = [], $absolute = true)
function tenant_route(string $domain, string $route, array $parameters = [], bool $absolute = true): string
{
// replace first occurance of hostname fragment with $domain
// replace the first occurrence of the hostname fragment with $domain
$url = route($route, $parameters, $absolute);
$hostname = parse_url($url, PHP_URL_HOST);
$position = strpos($url, $hostname);

View file

@ -181,7 +181,7 @@ test('database is not migrated if creation is disabled', function () {
class FooListener extends QueueableListener
{
public static $shouldQueue = false;
public static bool $shouldQueue = false;
public function handle()
{

View file

@ -575,7 +575,7 @@ class CentralUser extends Model implements SyncMaster
return ResourceUser::class;
}
public function getGlobalIdentifierKey()
public function getGlobalIdentifierKey(): string|int
{
return $this->getAttribute($this->getGlobalIdentifierKeyName());
}
@ -610,7 +610,7 @@ class ResourceUser extends Model implements Syncable
public $timestamps = false;
public function getGlobalIdentifierKey()
public function getGlobalIdentifierKey(): string|int
{
return $this->getAttribute($this->getGlobalIdentifierKeyName());
}