Merge pull request #322 from woocommerce/pcp-271-use-psalm

Add psalm
This commit is contained in:
Emili Castells 2021-10-13 16:48:49 +02:00 committed by GitHub
commit 79e2dd2ef5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 2784 additions and 73 deletions

View file

@ -30,5 +30,8 @@ jobs:
- name: Run PHPUnit
run: vendor/bin/phpunit
- name: Psalm
run: ./vendor/bin/psalm --show-info=false --threads=8 --diff
- name: Run PHPCS
run: ./vendor/bin/phpcs --runtime-set ignore_warnings_on_exit 1 src modules woocommerce-paypal-payments.php --extensions=php

View file

@ -21,6 +21,7 @@ PayPal's latest complete payments processing solution. Accept PayPal, Pay Later,
1. `$ composer install`
2. `$ ./vendor/bin/phpunit`
3. `$ ./vendor/bin/phpcs`
4. `$ ./vendor/bin/psalm`
### Docker

View file

@ -17,7 +17,10 @@
"require-dev": {
"woocommerce/woocommerce-sniffs": "^0.1.0",
"phpunit/phpunit": "^7.0 | ^8.0 | ^9.0",
"brain/monkey": "^2.4"
"brain/monkey": "^2.4",
"php-stubs/wordpress-stubs": "^5.0@stable",
"php-stubs/woocommerce-stubs": "^5.0@stable",
"vimeo/psalm": "^4.0"
},
"autoload": {
"psr-4": {

1546
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -219,10 +219,10 @@ return array(
'api.factory.webhook' => static function ( ContainerInterface $container ): WebhookFactory {
return new WebhookFactory();
},
'api.factory.webhook-event' => static function ( $container ): WebhookEventFactory {
'api.factory.webhook-event' => static function ( ContainerInterface $container ): WebhookEventFactory {
return new WebhookEventFactory();
},
'api.factory.capture' => static function ( $container ): CaptureFactory {
'api.factory.capture' => static function ( ContainerInterface $container ): CaptureFactory {
$amount_factory = $container->get( 'api.factory.amount' );
return new CaptureFactory( $amount_factory );

View file

@ -173,8 +173,9 @@ class Capture {
'invoice_id' => $this->invoice_id(),
'custom_id' => $this->custom_id(),
);
if ( $this->status()->details() ) {
$data['status_details'] = array( 'reason' => $this->status()->details()->reason() );
$details = $this->status()->details();
if ( $details ) {
$data['status_details'] = array( 'reason' => $details->reason() );
}
return $data;
}

View file

@ -41,7 +41,7 @@ class Webhook {
* Webhook constructor.
*
* @param string $url The URL of the webhook.
* @param string[] $event_types The associated event types.
* @param stdClass[] $event_types The associated event types.
* @param string $id The id of the webhook.
*/
public function __construct( string $url, array $event_types, string $id = '' ) {

View file

@ -9,6 +9,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
use stdClass;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Webhook;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
@ -21,14 +22,14 @@ class WebhookFactory {
* Returns a webhook for a URL with an array of event types associated to this URL.
*
* @param string $url The URL.
* @param array $event_types The event types to which this URL listens to.
* @param string[] $event_types The event types to which this URL listens to.
*
* @return Webhook
*/
public function for_url_and_events( string $url, array $event_types ): Webhook {
$event_types = array_map(
static function ( string $type ): array {
return array( 'name' => $type );
static function ( string $type ): stdClass {
return (object) array( 'name' => $type );
},
$event_types
);
@ -52,12 +53,12 @@ class WebhookFactory {
/**
* Returns a Webhook based of a PayPal JSON response.
*
* @param \stdClass $data The JSON object.
* @param stdClass $data The JSON object.
*
* @return Webhook
* @throws RuntimeException When JSON object is malformed.
*/
public function from_paypal_response( \stdClass $data ): Webhook {
public function from_paypal_response( stdClass $data ): Webhook {
if ( ! isset( $data->id ) ) {
throw new RuntimeException(
__( 'No id for webhook given.', 'woocommerce-paypal-payments' )

View file

@ -9,6 +9,8 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Compat;
use Psr\Container\ContainerInterface;
return array(
'compat.ppec.mock-gateway' => static function( $container ) {
@ -23,7 +25,7 @@ return array(
return new PPEC\MockGateway( $title );
},
'compat.ppec.subscriptions-handler' => static function ( $container ) {
'compat.ppec.subscriptions-handler' => static function ( ContainerInterface $container ) {
$ppcp_renewal_handler = $container->get( 'subscription.renewal-handler' );
$gateway = $container->get( 'compat.ppec.mock-gateway' );

View file

@ -10,7 +10,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\StatusReport;
return array(
'status-report.renderer' => static function ( $container ): Renderer {
'status-report.renderer' => static function (): Renderer {
return new Renderer();
},
);

View file

@ -9,17 +9,18 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Vaulting;
use Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\Vaulting\Assets\MyAccountPaymentsAssets;
use WooCommerce\PayPalCommerce\Vaulting\Endpoint\DeletePaymentTokenEndpoint;
return array(
'vaulting.module-url' => static function ( $container ): string {
'vaulting.module-url' => static function ( ContainerInterface $container ): string {
return plugins_url(
'/modules/ppcp-vaulting/',
dirname( __FILE__, 3 ) . '/woocommerce-paypal-payments.php'
);
},
'vaulting.assets.myaccount-payments' => function( $container ) : MyAccountPaymentsAssets {
'vaulting.assets.myaccount-payments' => function( ContainerInterface $container ) : MyAccountPaymentsAssets {
return new MyAccountPaymentsAssets(
$container->get( 'vaulting.module-url' )
);
@ -27,12 +28,12 @@ return array(
'vaulting.payment-tokens-renderer' => static function (): PaymentTokensRenderer {
return new PaymentTokensRenderer();
},
'vaulting.repository.payment-token' => static function ( $container ): PaymentTokenRepository {
'vaulting.repository.payment-token' => static function ( ContainerInterface $container ): PaymentTokenRepository {
$factory = $container->get( 'api.factory.payment-token' );
$endpoint = $container->get( 'api.endpoint.payment-token' );
return new PaymentTokenRepository( $factory, $endpoint );
},
'vaulting.endpoint.delete' => function( $container ) : DeletePaymentTokenEndpoint {
'vaulting.endpoint.delete' => function( ContainerInterface $container ) : DeletePaymentTokenEndpoint {
return new DeletePaymentTokenEndpoint(
$container->get( 'vaulting.repository.payment-token' ),
$container->get( 'button.request-data' ),

View file

@ -20,15 +20,15 @@ use Psr\Log\LoggerInterface;
return array(
'api.merchant_email' => static function ( $container ): string {
'api.merchant_email' => static function ( ContainerInterface $container ): string {
$settings = $container->get( 'wcgateway.settings' );
return $settings->has( 'merchant_email' ) ? (string) $settings->get( 'merchant_email' ) : '';
},
'api.merchant_id' => static function ( $container ): string {
'api.merchant_id' => static function ( ContainerInterface $container ): string {
$settings = $container->get( 'wcgateway.settings' );
return $settings->has( 'merchant_id' ) ? (string) $settings->get( 'merchant_id' ) : '';
},
'api.partner_merchant_id' => static function ( $container ): string {
'api.partner_merchant_id' => static function ( ContainerInterface $container ): string {
$environment = $container->get( 'onboarding.environment' );
/**
@ -39,20 +39,20 @@ return array(
return $environment->current_environment_is( Environment::SANDBOX ) ?
(string) $container->get( 'api.partner_merchant_id-sandbox' ) : (string) $container->get( 'api.partner_merchant_id-production' );
},
'api.key' => static function ( $container ): string {
'api.key' => static function ( ContainerInterface $container ): string {
$settings = $container->get( 'wcgateway.settings' );
$key = $settings->has( 'client_id' ) ? (string) $settings->get( 'client_id' ) : '';
return $key;
},
'api.secret' => static function ( $container ): string {
'api.secret' => static function ( ContainerInterface $container ): string {
$settings = $container->get( 'wcgateway.settings' );
return $settings->has( 'client_secret' ) ? (string) $settings->get( 'client_secret' ) : '';
},
'api.prefix' => static function ( $container ): string {
'api.prefix' => static function ( ContainerInterface $container ): string {
$settings = $container->get( 'wcgateway.settings' );
return $settings->has( 'prefix' ) ? (string) $settings->get( 'prefix' ) : 'WC-';
},
'api.endpoint.order' => static function ( $container ): OrderEndpoint {
'api.endpoint.order' => static function ( ContainerInterface $container ): OrderEndpoint {
$order_factory = $container->get( 'api.factory.order' );
$patch_collection_factory = $container->get( 'api.factory.patch-collection-factory' );
$logger = $container->get( 'woocommerce.logger.woocommerce' );
@ -85,7 +85,7 @@ return array(
$bn_code
);
},
'woocommerce.logger.woocommerce' => function ( $container ): LoggerInterface {
'woocommerce.logger.woocommerce' => function ( ContainerInterface $container ): LoggerInterface {
$settings = $container->get( 'wcgateway.settings' );
if ( ! function_exists( 'wc_get_logger' ) || ! $settings->has( 'logging_enabled' ) || ! $settings->get( 'logging_enabled' ) ) {
return new NullLogger();

View file

@ -113,13 +113,13 @@ return array(
$settings = $container->get( 'wcgateway.settings' );
return new DisableGateways( $session_handler, $settings );
},
'wcgateway.is-wc-payments-page' => static function ( $container ): bool {
'wcgateway.is-wc-payments-page' => static function ( ContainerInterface $container ): bool {
$page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '';
$tab = isset( $_GET['tab'] ) ? sanitize_text_field( wp_unslash( $_GET['tab'] ) ) : '';
return 'wc-settings' === $page && 'checkout' === $tab;
},
'wcgateway.is-ppcp-settings-page' => static function ( $container ): bool {
'wcgateway.is-ppcp-settings-page' => static function ( ContainerInterface $container ): bool {
if ( ! $container->get( 'wcgateway.is-wc-payments-page' ) ) {
return false;
}
@ -128,7 +128,7 @@ return array(
return in_array( $section, array( PayPalGateway::ID, CreditCardGateway::ID, WebhooksStatusPage::ID ), true );
},
'wcgateway.current-ppcp-settings-page-id' => static function ( $container ): string {
'wcgateway.current-ppcp-settings-page-id' => static function ( ContainerInterface $container ): string {
if ( ! $container->get( 'wcgateway.is-ppcp-settings-page' ) ) {
return '';
}
@ -155,7 +155,7 @@ return array(
return new DccWithoutPayPalAdminNotice( $state, $settings, $is_payments_page, $is_ppcp_settings_page );
},
'wcgateway.notice.authorize-order-action' =>
static function ( $container ): AuthorizeOrderActionNotice {
static function ( ContainerInterface $container ): AuthorizeOrderActionNotice {
return new AuthorizeOrderActionNotice();
},
'wcgateway.settings.sections-renderer' => static function ( ContainerInterface $container ): SectionsRenderer {
@ -1973,10 +1973,10 @@ return array(
dirname( __FILE__, 3 ) . '/woocommerce-paypal-payments.php'
);
},
'wcgateway.relative-path' => static function( $container ): string {
'wcgateway.relative-path' => static function( ContainerInterface $container ): string {
return 'modules/ppcp-wc-gateway/';
},
'wcgateway.absolute-path' => static function( $container ): string {
'wcgateway.absolute-path' => static function( ContainerInterface $container ): string {
return plugin_dir_path(
dirname( __FILE__, 3 ) . '/woocommerce-paypal-payments.php'
) .
@ -1993,15 +1993,15 @@ return array(
);
},
'wcgateway.transaction-url-sandbox' => static function ( $container ): string {
'wcgateway.transaction-url-sandbox' => static function ( ContainerInterface $container ): string {
return 'https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_view-a-trans&id=%s';
},
'wcgateway.transaction-url-live' => static function ( $container ): string {
'wcgateway.transaction-url-live' => static function ( ContainerInterface $container ): string {
return 'https://www.paypal.com/cgi-bin/webscr?cmd=_view-a-trans&id=%s';
},
'wcgateway.transaction-url-provider' => static function ( $container ): TransactionUrlProvider {
'wcgateway.transaction-url-provider' => static function ( ContainerInterface $container ): TransactionUrlProvider {
$sandbox_url_base = $container->get( 'wcgateway.transaction-url-sandbox' );
$live_url_base = $container->get( 'wcgateway.transaction-url-live' );
@ -2015,7 +2015,7 @@ return array(
return new DCCProductStatus( $settings, $partner_endpoint );
},
'button.helper.messages-disclaimers' => static function ( $container ): MessagesDisclaimers {
'button.helper.messages-disclaimers' => static function ( ContainerInterface $container ): MessagesDisclaimers {
return new MessagesDisclaimers();
},
);

View file

@ -55,8 +55,9 @@ trait PaymentsStatusHandlingTrait {
): void {
$status = $capture->status();
if ( $status->details() ) {
$this->add_status_details_note( $wc_order, $status->name(), $status->details()->text() );
$details = $status->details();
if ( $details ) {
$this->add_status_details_note( $wc_order, $status->name(), $details->text() );
}
switch ( $status->name() ) {
@ -95,8 +96,9 @@ trait PaymentsStatusHandlingTrait {
): void {
$status = $authorization->status();
if ( $status->details() ) {
$this->add_status_details_note( $wc_order, $status->name(), $status->details()->text() );
$details = $status->details();
if ( $details ) {
$this->add_status_details_note( $wc_order, $status->name(), $details->text() );
}
switch ( $status->name() ) {

View file

@ -148,9 +148,9 @@ class WCGatewayModule implements ModuleInterface {
/**
* Registers the payment gateways.
*
* @param ContainerInterface|null $container The container.
* @param ContainerInterface $container The container.
*/
private function register_payment_gateways( ContainerInterface $container = null ) {
private function register_payment_gateways( ContainerInterface $container ) {
add_filter(
'woocommerce_payment_gateways',

View file

@ -72,7 +72,7 @@ return array(
);
},
'webhook.current' => function( $container ) : ?Webhook {
'webhook.current' => function( ContainerInterface $container ) : ?Webhook {
$data = (array) get_option( WebhookRegistrar::KEY, array() );
if ( empty( $data ) ) {
return null;
@ -91,18 +91,18 @@ return array(
}
},
'webhook.is-registered' => function( $container ) : bool {
'webhook.is-registered' => function( ContainerInterface $container ) : bool {
return $container->get( 'webhook.current' ) !== null;
},
'webhook.status.registered-webhooks' => function( $container ) : array {
'webhook.status.registered-webhooks' => function( ContainerInterface $container ) : array {
$endpoint = $container->get( 'api.endpoint.webhook' );
assert( $endpoint instanceof WebhookEndpoint );
return $endpoint->list();
},
'webhook.status.registered-webhooks-data' => function( $container ) : array {
'webhook.status.registered-webhooks-data' => function( ContainerInterface $container ) : array {
$empty_placeholder = __( 'No webhooks found.', 'woocommerce-paypal-payments' );
$webhooks = array();
@ -139,7 +139,7 @@ return array(
);
},
'webhook.status.simulation' => function( $container ) : WebhookSimulation {
'webhook.status.simulation' => function( ContainerInterface $container ) : WebhookSimulation {
$webhook_endpoint = $container->get( 'api.endpoint.webhook' );
$webhook = $container->get( 'webhook.current' );
return new WebhookSimulation(
@ -150,13 +150,13 @@ return array(
);
},
'webhook.status.assets' => function( $container ) : WebhooksStatusPageAssets {
'webhook.status.assets' => function( ContainerInterface $container ) : WebhooksStatusPageAssets {
return new WebhooksStatusPageAssets(
$container->get( 'webhook.module-url' )
);
},
'webhook.endpoint.resubscribe' => static function ( $container ) : ResubscribeEndpoint {
'webhook.endpoint.resubscribe' => static function ( ContainerInterface $container ) : ResubscribeEndpoint {
$registrar = $container->get( 'webhook.registrar' );
$request_data = $container->get( 'button.request-data' );
@ -166,7 +166,7 @@ return array(
);
},
'webhook.endpoint.simulate' => static function ( $container ) : SimulateEndpoint {
'webhook.endpoint.simulate' => static function ( ContainerInterface $container ) : SimulateEndpoint {
$simulation = $container->get( 'webhook.status.simulation' );
$request_data = $container->get( 'button.request-data' );
@ -175,7 +175,7 @@ return array(
$request_data
);
},
'webhook.endpoint.simulation-state' => static function ( $container ) : SimulationStateEndpoint {
'webhook.endpoint.simulation-state' => static function ( ContainerInterface $container ) : SimulationStateEndpoint {
$simulation = $container->get( 'webhook.status.simulation' );
return new SimulationStateEndpoint(
@ -183,7 +183,7 @@ return array(
);
},
'webhook.module-url' => static function ( $container ): string {
'webhook.module-url' => static function ( ContainerInterface $container ): string {
return plugins_url(
'/modules/ppcp-webhooks/',
dirname( __FILE__, 3 ) . '/woocommerce-paypal-payments.php'

View file

@ -50,7 +50,7 @@ class CheckoutOrderApproved implements RequestHandler {
/**
* The event types a handler handles.
*
* @return array
* @return string[]
*/
public function event_types(): array {
return array(

View file

@ -39,7 +39,7 @@ class CheckoutOrderCompleted implements RequestHandler {
/**
* The event types a handler handles.
*
* @return array
* @return string[]
*/
public function event_types(): array {
return array(

View file

@ -40,7 +40,7 @@ class PaymentCaptureCompleted implements RequestHandler {
/**
* The event types a handler handles.
*
* @return array
* @return string[]
*/
public function event_types(): array {
return array( 'PAYMENT.CAPTURE.COMPLETED' );

View file

@ -39,7 +39,7 @@ class PaymentCaptureRefunded implements RequestHandler {
/**
* The event types a handler handles.
*
* @return array
* @return string[]
*/
public function event_types(): array {
return array( 'PAYMENT.CAPTURE.REFUNDED' );

View file

@ -42,7 +42,7 @@ class PaymentCaptureReversed implements RequestHandler {
/**
* The event types a handler handles.
*
* @return array
* @return string[]
*/
public function event_types(): array {
return array(

View file

@ -17,7 +17,7 @@ interface RequestHandler {
/**
* The event types a handler handles.
*
* @return array
* @return string[]
*/
public function event_types(): array;

View file

@ -248,7 +248,7 @@ class IncomingWebhookEndpoint {
/**
* Returns the event types, which are handled by the endpoint.
*
* @return array
* @return string[]
*/
public function handled_event_types(): array {
$event_types = array();

View file

@ -47,14 +47,14 @@ class WebhooksStatusPageAssets {
'ppcp-webhooks-status-page-style',
$this->module_url . '/assets/css/status-page.css',
array(),
1
'1'
);
wp_register_script(
'ppcp-webhooks-status-page',
$this->module_url . '/assets/js/status-page.js',
array(),
1,
'1',
true
);

View file

@ -78,7 +78,7 @@ class WebhookSimulation {
*
* @throws Exception If failed to start simulation.
*/
public function start() {
public function start(): void {
if ( ! $this->webhook ) {
throw new Exception( 'Webhooks not registered' );
}

View file

@ -28,7 +28,7 @@
"docker:build-js": "docker-compose run --rm build yarn run build:dev",
"docker:composer-update": "docker-compose run --rm composer composer update && docker-compose run --rm composer composer update --lock",
"docker:test": "docker-compose run --rm test vendor/bin/phpunit",
"docker:lint": "docker-compose run --rm test vendor/bin/phpcs --parallel=8 -s",
"docker:lint": "docker-compose run --rm test sh -c 'vendor/bin/phpcs --parallel=8 -s && vendor/bin/psalm --show-info=false --threads=8 --diff'",
"docker:fix-lint": "docker-compose run --rm test vendor/bin/phpcbf",

1012
psalm-baseline.xml Normal file

File diff suppressed because it is too large Load diff

153
psalm.xml.dist Normal file
View file

@ -0,0 +1,153 @@
<?xml version="1.0"?>
<psalm
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
xmlns:xi="http://www.w3.org/2001/XInclude"
totallyTyped="false"
useDocblockTypes="true"
usePhpDocMethodsWithoutMagicCall="false"
strictBinaryOperands="true"
rememberPropertyAssignmentsAfterCall="true"
allowPhpStormGenerics="true"
allowStringToStandInForClass="false"
memoizeMethodCallResults="false"
hoistConstants="false"
addParamDefaultToDocblockType="false"
checkForThrowsDocblock="true"
checkForThrowsInGlobalScope="false"
ignoreInternalFunctionFalseReturn="false"
ignoreInternalFunctionNullReturn="false"
throwExceptionOnError="false"
hideExternalErrors="true"
allowFileIncludes="true"
errorBaseline="psalm-baseline.xml"
>
<projectFiles>
<directory name="src"/>
<directory name="modules" />
</projectFiles>
<stubs>
<file name="vendor/php-stubs/wordpress-stubs/wordpress-stubs.php"/>
<file name="vendor/php-stubs/woocommerce-stubs/woocommerce-stubs.php"/>
</stubs>
<issueHandlers>
<ConflictingReferenceConstraint errorLevel="error"/>
<ContinueOutsideLoop errorLevel="error"/>
<DuplicateArrayKey errorLevel="error"/>
<DuplicateClass errorLevel="error"/>
<DuplicateFunction errorLevel="error"/>
<DuplicateMethod errorLevel="error"/>
<DuplicateParam errorLevel="error"/>
<EmptyArrayAccess errorLevel="error"/>
<FalsableReturnStatement errorLevel="error"/>
<FalseOperand errorLevel="error"/>
<ForbiddenCode errorLevel="error"/>
<ForbiddenEcho errorLevel="error"/>
<InaccessibleClassConstant errorLevel="error"/>
<InaccessibleMethod errorLevel="error"/>
<InterfaceInstantiation errorLevel="error"/>
<InaccessibleProperty errorLevel="error"/>
<InternalClass errorLevel="error"/>
<InternalMethod errorLevel="error"/>
<InternalProperty errorLevel="error"/>
<InvalidArgument errorLevel="error"/>
<InvalidArrayAccess errorLevel="error"/>
<InvalidArrayAssignment errorLevel="error"/>
<InvalidArrayOffset errorLevel="error"/>
<InvalidCast errorLevel="error"/>
<InvalidCatch errorLevel="error"/>
<InvalidClass errorLevel="error"/>
<InvalidClone errorLevel="error"/>
<InvalidFalsableReturnType errorLevel="error"/>
<InvalidThrow errorLevel="error"/>
<InvalidToString errorLevel="error"/>
<LoopInvalidation errorLevel="error"/>
<InvalidNullableReturnType errorLevel="error"/>
<LessSpecificReturnType errorLevel="error"/>
<InvalidGlobal errorLevel="error"/>
<InvalidIterator errorLevel="error"/>
<InvalidMethodCall errorLevel="error"/>
<InvalidFunctionCall errorLevel="error"/>
<ImplicitToStringCast errorLevel="error"/>
<ImplementedReturnTypeMismatch errorLevel="error"/>
<InvalidParamDefault errorLevel="error"/>
<InvalidPassByReference errorLevel="error"/>
<InvalidPropertyAssignment errorLevel="error"/>
<InvalidPropertyAssignmentValue errorLevel="error"/>
<InvalidPropertyFetch errorLevel="error"/>
<InvalidReturnStatement errorLevel="error"/>
<InvalidReturnType errorLevel="error"/>
<InvalidScalarArgument errorLevel="error"/>
<InvalidScope errorLevel="error"/>
<InvalidStaticInvocation errorLevel="error"/>
<MissingConstructor errorLevel="error"/>
<MissingDependency errorLevel="error"/>
<MissingFile errorLevel="error"/>
<MixedArgument errorLevel="error"/>
<MoreSpecificImplementedParamType errorLevel="error"/>
<MoreSpecificReturnType errorLevel="error"/>
<NoValue errorLevel="error"/>
<NoInterfaceProperties errorLevel="error"/>
<NonStaticSelfCall errorLevel="error"/>
<NullableReturnStatement errorLevel="error"/>
<NullArgument errorLevel="error"/>
<NullArrayAccess errorLevel="error"/>
<NullArrayOffset errorLevel="error"/>
<NullFunctionCall errorLevel="error"/>
<NullIterator errorLevel="error"/>
<NullOperand errorLevel="error"/>
<NullPropertyAssignment errorLevel="error"/>
<NullPropertyFetch errorLevel="error"/>
<NullReference errorLevel="error"/>
<OverriddenMethodAccess errorLevel="error"/>
<OverriddenPropertyAccess errorLevel="error"/>
<ParadoxicalCondition errorLevel="error"/>
<ParentNotFound errorLevel="error"/>
<LessSpecificImplementedReturnType errorLevel="error"/>
<MissingParamType errorLevel="error"/>
<MissingClosureParamType errorLevel="error"/>
<MissingClosureReturnType errorLevel="error"/>
<MissingPropertyType errorLevel="error"/>
<UndefinedConstant errorLevel="error"/>
<AssignmentToVoid errorLevel="info"/>
<DeprecatedClass errorLevel="info"/>
<DeprecatedConstant errorLevel="info"/>
<DeprecatedTrait errorLevel="info"/>
<DocblockTypeContradiction errorLevel="info"/>
<InvalidDocblock errorLevel="info"/>
<InvalidDocblockParamName errorLevel="info"/>
<InvalidTemplateParam errorLevel="info"/>
<DeprecatedInterface errorLevel="info"/>
<DeprecatedMethod errorLevel="info"/>
<DeprecatedProperty errorLevel="info"/>
<MethodSignatureMustOmitReturnType errorLevel="info"/>
<MismatchingDocblockParamType errorLevel="info"/>
<MismatchingDocblockReturnType errorLevel="info"/>
<MissingDocblockType errorLevel="info"/>
<MissingParamType errorLevel="info"/>
<MissingTemplateParam errorLevel="info"/>
<MissingThrowsDocblock errorLevel="info"/>
<MixedArgumentTypeCoercion errorLevel="info"/>
<MixedArrayAccess errorLevel="info"/>
<MixedArrayAssignment errorLevel="info"/>
<MixedArrayOffset errorLevel="info"/>
<MixedArrayTypeCoercion errorLevel="info"/>
<MixedAssignment errorLevel="info"/>
<MixedFunctionCall errorLevel="info"/>
<MixedInferredReturnType errorLevel="info"/>
<MixedMethodCall errorLevel="info"/>
<MixedOperand errorLevel="info"/>
<MixedPropertyAssignment errorLevel="info"/>
<MixedPropertyFetch errorLevel="info"/>
<MixedPropertyTypeCoercion errorLevel="info"/>
<MixedReturnStatement errorLevel="info"/>
<MixedReturnTypeCoercion errorLevel="info"/>
<MixedStringOffsetAssignment errorLevel="info"/>
<ParamNameMismatch errorLevel="info"/>
</issueHandlers>
</psalm>