diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b97487dc91..388f618b3b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -178,6 +178,8 @@ jobs: name: "Solr integration tests" runs-on: "ubuntu-22.04" timeout-minutes: 30 + env: + COMPOSER_ROOT_VERSION: dev-ibx-8470-symfony-6 permissions: packages: read contents: read @@ -215,7 +217,9 @@ jobs: - name: Add solr dependency run: | - VERSION=$(jq -r '.extra | ."branch-alias" | ."dev-main"' < composer.json) + export COMPOSER_ROOT_VERSION=dev-ibx-8470-symfony-6 + VERSION="$COMPOSER_ROOT_VERSION as 5.0.x-dev" + echo "COMPOSER_ROOT_VERSION: $COMPOSER_ROOT_VERSION" composer require --no-update "ibexa/solr:$VERSION" - uses: ramsey/composer-install@v3 diff --git a/composer.json b/composer.json index 2dabeab595..a37b1b5106 100644 --- a/composer.json +++ b/composer.json @@ -25,38 +25,38 @@ "doctrine/orm": "^2.7", "friendsofphp/proxy-manager-lts": "^1.0", "friendsofsymfony/http-cache-bundle": "^2.8", - "friendsofsymfony/jsrouting-bundle": "^2.5", - "ibexa/doctrine-schema": "~5.0.x-dev", - "jms/translation-bundle": "^1.5", + "friendsofsymfony/jsrouting-bundle": "^3.5", + "ibexa/doctrine-schema": "dev-ibx-8470-symfony-6 as 5.0.x-dev", + "jms/translation-bundle": "^2.4", "league/flysystem-memory": "^2.0.6", "liip/imagine-bundle": "^2.3", "nelmio/cors-bundle": "^2.0", "oneup/flysystem-bundle": "^4.4.2", - "pagerfanta/pagerfanta": "^2.1", + "pagerfanta/pagerfanta": "^3.6.2", "psr/event-dispatcher": "^1.0", "sensio/framework-extra-bundle": "^6.1", - "symfony-cmf/routing": "^2.3", - "symfony/cache": "^5.3.0", - "symfony/console": "^5.3.0", - "symfony/dependency-injection": "^5.3.0", - "symfony/event-dispatcher": "^5.3.0", - "symfony/expression-language": "^5.3.0", - "symfony/framework-bundle": "^5.3.0", - "symfony/http-client": "^5.3.0", - "symfony/http-foundation": "^5.3.0", - "symfony/http-kernel": "^5.3.0", - "symfony/mime": "^5.3.0", + "symfony-cmf/routing": "^3.0", + "symfony/cache": "^6.4.0", + "symfony/console": "^6.4.0", + "symfony/dependency-injection": "^6.4.0", + "symfony/event-dispatcher": "^6.4.0", + "symfony/expression-language": "^6.4.0", + "symfony/framework-bundle": "^6.4.0", + "symfony/http-client": "^6.4.0", + "symfony/http-foundation": "^6.4.0", + "symfony/http-kernel": "^6.4.0", + "symfony/mime": "^6.4.0", "symfony/polyfill-php80": "^1.27", - "symfony/process": "^5.3.0", - "symfony/security-bundle": "^5.3.0", - "symfony/security-core": "^5.3.0", - "symfony/security-http": "^5.3.0", - "symfony/serializer": "^5.3.0", - "symfony/templating": "^5.3.0", - "symfony/translation": "^5.3.0", - "symfony/validator": "^5.3.0", - "symfony/var-dumper": "^5.3.0", - "symfony/yaml": "^5.3.0", + "symfony/process": "^6.4.0", + "symfony/security-bundle": "^6.4.0", + "symfony/security-core": "^6.4.0", + "symfony/security-http": "^6.4.0", + "symfony/serializer": "^6.4.0", + "symfony/templating": "^6.4.0", + "symfony/translation": "^6.4.0", + "symfony/validator": "^6.4.0", + "symfony/var-dumper": "^6.4.0", + "symfony/yaml": "^6.4.0", "twig/extra-bundle": "^3.0", "twig/twig": "^3.0" }, @@ -71,8 +71,8 @@ "phpstan/phpstan-symfony": "^1.3", "phpunit/phpunit": "^9.6", "symfony/phpunit-bridge": "^5.4", - "symfony/proxy-manager-bridge": "^5.3", - "symfony/runtime": "^5.3.0" + "symfony/proxy-manager-bridge": "^6.4", + "symfony/runtime": "^6.4.0" }, "conflict": { "doctrine/dbal": "2.7.0", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 7dd0093d52..9a103ed8af 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -25,21 +25,11 @@ parameters: count: 1 path: src/bundle/Core/ApiLoader/SearchEngineIndexerFactory.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Cache\\\\Warmer\\\\ProxyCacheWarmer\\:\\:warmUp\\(\\) has parameter \\$cacheDir with no type specified\\.$#" - count: 1 - path: src/bundle/Core/Cache/Warmer/ProxyCacheWarmer.php - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\CheckURLsCommand\\:\\:getTotalCount\\(\\) should return int but returns int\\|null\\.$#" count: 1 path: src/bundle/Core/Command/CheckURLsCommand.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\CleanupVersionsCommand\\:\\:configure\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Command/CleanupVersionsCommand.php - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\CleanupVersionsCommand\\:\\:getObjectsIds\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -50,31 +40,6 @@ parameters: count: 1 path: src/bundle/Core/Command/CopySubtreeCommand.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\CopySubtreeCommand\\:\\:configure\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Command/CopySubtreeCommand.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\CopySubtreeCommand\\:\\:getAllChildrenCount\\(\\) should return int but returns int\\|null\\.$#" - count: 1 - path: src/bundle/Core/Command/CopySubtreeCommand.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\CopySubtreeCommand\\:\\:initialize\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Command/CopySubtreeCommand.php - - - - message: "#^PHPDoc tag @return with type int\\|null is not subtype of native type int\\.$#" - count: 1 - path: src/bundle/Core/Command/CopySubtreeCommand.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\DebugConfigResolverCommand\\:\\:configure\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Command/DebugConfigResolverCommand.php - - message: "#^Parameter \\#1 \\$messages of method Symfony\\\\Component\\\\Console\\\\Output\\\\OutputInterface\\:\\:write\\(\\) expects iterable\\\\|string, string\\|false given\\.$#" count: 1 @@ -85,16 +50,6 @@ parameters: count: 1 path: src/bundle/Core/Command/DebugConfigResolverCommand.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\DeleteContentTranslationCommand\\:\\:configure\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Command/DeleteContentTranslationCommand.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\DeleteContentTranslationCommand\\:\\:initialize\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Command/DeleteContentTranslationCommand.php - - message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Command\\\\DeleteContentTranslationCommand\\:\\:\\$questionHelper \\(Symfony\\\\Component\\\\Console\\\\Helper\\\\QuestionHelper\\) does not accept Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\.$#" count: 1 @@ -105,11 +60,6 @@ parameters: count: 1 path: src/bundle/Core/Command/ExpireUserPasswordsCommand.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\NormalizeImagesPathsCommand\\:\\:configure\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Command/NormalizeImagesPathsCommand.php - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\NormalizeImagesPathsCommand\\:\\:getFinalNormalizedPath\\(\\) has parameter \\$imagePathsToNormalize with no value type specified in iterable type array\\.$#" count: 1 @@ -155,11 +105,6 @@ parameters: count: 1 path: src/bundle/Core/Command/RegenerateUrlAliasesCommand.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\RegenerateUrlAliasesCommand\\:\\:configure\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Command/RegenerateUrlAliasesCommand.php - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\RegenerateUrlAliasesCommand\\:\\:loadSpecificLocations\\(\\) should return array\\ but returns iterable\\\\.$#" count: 1 @@ -185,16 +130,6 @@ parameters: count: 2 path: src/bundle/Core/Command/ReindexCommand.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\ReindexCommand\\:\\:configure\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Command/ReindexCommand.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\ReindexCommand\\:\\:initialize\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Command/ReindexCommand.php - - message: "#^Parameter \\#1 \\$max of method Symfony\\\\Component\\\\Console\\\\Helper\\\\ProgressBar\\:\\:start\\(\\) expects int\\|null, float given\\.$#" count: 1 @@ -225,31 +160,11 @@ parameters: count: 1 path: src/bundle/Core/Command/ReindexCommand.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$fields\\.$#" - count: 1 - path: src/bundle/Core/Command/ResizeOriginalImagesCommand.php - - - - message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:getVersionInfo\\(\\)\\.$#" - count: 2 - path: src/bundle/Core/Command/ResizeOriginalImagesCommand.php - - message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:ask\\(\\)\\.$#" count: 1 path: src/bundle/Core/Command/ResizeOriginalImagesCommand.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\ResizeOriginalImagesCommand\\:\\:configure\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Command/ResizeOriginalImagesCommand.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\ResizeOriginalImagesCommand\\:\\:initialize\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Command/ResizeOriginalImagesCommand.php - - message: "#^Parameter \\#1 \\$mimeType of method Symfony\\\\Component\\\\Mime\\\\MimeTypesInterface\\:\\:getExtensions\\(\\) expects string, string\\|null given\\.$#" count: 1 @@ -265,11 +180,6 @@ parameters: count: 1 path: src/bundle/Core/Command/ResizeOriginalImagesCommand.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\SetSystemContentTypeGroupCommand\\:\\:configure\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Command/SetSystemContentTypeGroupCommand.php - - message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:ask\\(\\)\\.$#" count: 1 @@ -280,11 +190,6 @@ parameters: count: 1 path: src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\UpdateTimestampsToUTCCommand\\:\\:configure\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\UpdateTimestampsToUTCCommand\\:\\:dateStringToTimestamp\\(\\) has parameter \\$dateString with no type specified\\.$#" count: 1 @@ -380,31 +285,6 @@ parameters: count: 1 path: src/bundle/Core/DependencyInjection/Compiler/BinaryContentDownloadPass.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\BinaryContentDownloadPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/BinaryContentDownloadPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ChainConfigResolverPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/ChainConfigResolverPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ChainRoutingPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/ChainRoutingPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ConsoleCacheWarmupPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/ConsoleCacheWarmupPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ConsoleCommandPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/ConsoleCommandPass.php - - message: "#^Argument of an invalid type array\\|bool\\|float\\|int\\|string\\|null supplied for foreach, only iterables are supported\\.$#" count: 1 @@ -415,21 +295,6 @@ parameters: count: 1 path: src/bundle/Core/DependencyInjection/Compiler/EntityManagerFactoryServiceLocatorPass.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\FieldTypeParameterProviderRegistryPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/FieldTypeParameterProviderRegistryPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\FragmentPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/FragmentPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ImaginePass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/ImaginePass.php - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ImaginePass\\:\\:processReduceNoiseFilter\\(\\) has no return type specified\\.$#" count: 1 @@ -510,51 +375,11 @@ parameters: count: 1 path: src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php - - - message: "#^Parameter \\#1 \\$haystack of function strpos expects string, string\\|null given\\.$#" - count: 2 - path: src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php - - message: "#^Parameter \\#1 \\$path of function dirname expects string, string\\|false given\\.$#" count: 1 path: src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\NotificationRendererPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/NotificationRendererPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\PlaceholderProviderPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/PlaceholderProviderPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\RegisterSearchEngineIndexerPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/RegisterSearchEngineIndexerPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\RegisterSearchEnginePass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/RegisterSearchEnginePass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\RegisterStorageEnginePass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/RegisterStorageEnginePass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\RouterPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/RouterPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\SessionConfigurationPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/SessionConfigurationPass.php - - message: "#^Parameter \\#2 \\$id of method Symfony\\\\Component\\\\DependencyInjection\\\\ContainerBuilder\\:\\:setAlias\\(\\) expects string\\|Symfony\\\\Component\\\\DependencyInjection\\\\Alias, array\\|bool\\|float\\|int\\|string given\\.$#" count: 1 @@ -565,41 +390,16 @@ parameters: count: 1 path: src/bundle/Core/DependencyInjection/Compiler/SessionConfigurationPass.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\SlugConverterConfigurationPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/SlugConverterConfigurationPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\StorageConnectionPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/StorageConnectionPass.php - - message: "#^Constant Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\TranslationCollectorPass\\:\\:LOCALES_MAP type has no value type specified in iterable type array\\.$#" count: 1 path: src/bundle/Core/DependencyInjection/Compiler/TranslationCollectorPass.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\TranslationCollectorPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/TranslationCollectorPass.php - - message: "#^Parameter \\#1 \\$kernelRootDir of class Ibexa\\\\Bundle\\\\Core\\\\Translation\\\\GlobCollector constructor expects string, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" count: 1 path: src/bundle/Core/DependencyInjection/Compiler/TranslationCollectorPass.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\URLHandlerPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/URLHandlerPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ViewProvidersPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Compiler/ViewProvidersPass.php - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\:\\:addHttpCacheSection\\(\\) has no return type specified\\.$#" count: 1 @@ -915,11 +715,6 @@ parameters: count: 1 path: src/bundle/Core/DependencyInjection/Configuration/Parser/AbstractFieldTypeParser.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Common\\:\\:addSemanticConfig\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/Configuration/Parser/Common.php - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Common\\:\\:mapConfig\\(\\) has no return type specified\\.$#" count: 1 @@ -1745,16 +1540,6 @@ parameters: count: 1 path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:load\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:prepend\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:prependTranslatorConfiguration\\(\\) has no return type specified\\.$#" count: 1 @@ -2825,11 +2610,6 @@ parameters: count: 1 path: src/bundle/Core/Features/Context/RoleContext.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentInfo\\.$#" - count: 4 - path: src/bundle/Core/Features/Context/UserContext.php - - message: "#^Dead catch \\- Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Exceptions\\\\NotFoundException is never thrown in the try block\\.$#" count: 1 @@ -3065,21 +2845,11 @@ parameters: count: 1 path: src/bundle/Core/Features/Context/UserContext.php - - - message: "#^Parameter \\#1 \\$value of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\ParentLocationId constructor expects array\\\\|int, string given\\.$#" - count: 1 - path: src/bundle/Core/Features/Context/UserContext.php - - message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\ given\\.$#" count: 1 path: src/bundle/Core/Features/Context/UserContext.php - - - message: "#^Parameter \\#2 \\$parentLocationId of method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:searchUserGroups\\(\\) expects string\\|null, int given\\.$#" - count: 1 - path: src/bundle/Core/Features/Context/UserContext.php - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\YamlConfigurationContext\\:\\:addConfiguration\\(\\) has no return type specified\\.$#" count: 1 @@ -3110,31 +2880,6 @@ parameters: count: 1 path: src/bundle/Core/Features/Context/YamlConfigurationContext.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRenderer\\:\\:render\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" - count: 1 - path: src/bundle/Core/Fragment/DecoratedFragmentRenderer.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRenderer\\:\\:setFragmentPath\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Fragment/DecoratedFragmentRenderer.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRenderer\\:\\:setFragmentPath\\(\\) has parameter \\$path with no type specified\\.$#" - count: 1 - path: src/bundle/Core/Fragment/DecoratedFragmentRenderer.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRenderer\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Fragment/DecoratedFragmentRenderer.php - - - - message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRenderer\\:\\:\\$siteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" - count: 1 - path: src/bundle/Core/Fragment/DecoratedFragmentRenderer.php - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\DirectFragmentRenderer\\:\\:getArguments\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -3145,11 +2890,6 @@ parameters: count: 1 path: src/bundle/Core/Fragment/DirectFragmentRenderer.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\DirectFragmentRenderer\\:\\:render\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" - count: 1 - path: src/bundle/Core/Fragment/DirectFragmentRenderer.php - - message: "#^Parameter \\#1 \\$controller of method Symfony\\\\Component\\\\HttpKernel\\\\ControllerMetadata\\\\ArgumentMetadataFactoryInterface\\:\\:createArgumentMetadata\\(\\) expects array\\|object\\|string, callable given\\.$#" count: 1 @@ -3180,26 +2920,11 @@ parameters: count: 1 path: src/bundle/Core/Fragment/FragmentListenerFactory.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\InlineFragmentRenderer\\:\\:render\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" - count: 1 - path: src/bundle/Core/Fragment/InlineFragmentRenderer.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\InlineFragmentRenderer\\:\\:setFragmentPath\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Fragment/InlineFragmentRenderer.php - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\InlineFragmentRenderer\\:\\:setFragmentPath\\(\\) has parameter \\$path with no type specified\\.$#" count: 1 path: src/bundle/Core/Fragment/InlineFragmentRenderer.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\InlineFragmentRenderer\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Fragment/InlineFragmentRenderer.php - - message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\InlineFragmentRenderer\\:\\:\\$siteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" count: 1 @@ -3685,81 +3410,6 @@ parameters: count: 1 path: src/bundle/Core/Matcher/ServiceAwareMatcherFactory.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:generate\\(\\) has parameter \\$name with no type specified\\.$#" - count: 1 - path: src/bundle/Core/Routing/DefaultRouter.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:generate\\(\\) has parameter \\$parameters with no type specified\\.$#" - count: 1 - path: src/bundle/Core/Routing/DefaultRouter.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:generate\\(\\) has parameter \\$referenceType with no type specified\\.$#" - count: 1 - path: src/bundle/Core/Routing/DefaultRouter.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:isSiteAccessAwareRoute\\(\\) has parameter \\$routeName with no type specified\\.$#" - count: 1 - path: src/bundle/Core/Routing/DefaultRouter.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:matchRequest\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/bundle/Core/Routing/DefaultRouter.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:setConfigResolver\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Routing/DefaultRouter.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:setNonSiteAccessAwareRoutes\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Routing/DefaultRouter.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:setNonSiteAccessAwareRoutes\\(\\) has parameter \\$routes with no value type specified in iterable type array\\.$#" - count: 1 - path: src/bundle/Core/Routing/DefaultRouter.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Routing/DefaultRouter.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:setSiteAccessRouter\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Routing/DefaultRouter.php - - - - message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:\\$nonSiteAccessAwareRoutes has no type specified\\.$#" - count: 1 - path: src/bundle/Core/Routing/DefaultRouter.php - - - - message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:\\$siteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" - count: 1 - path: src/bundle/Core/Routing/DefaultRouter.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\UrlAliasRouter\\:\\:matchRequest\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/bundle/Core/Routing/UrlAliasRouter.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\UrlAliasRouter\\:\\:setConfigResolver\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Core/Routing/UrlAliasRouter.php - - - - message: "#^Parameter \\#1 \\$rootLocationId of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:getPathPrefixByRootLocationId\\(\\) expects int, int\\|string given\\.$#" - count: 1 - path: src/bundle/Core/Routing/UrlAliasRouter.php - - message: "#^Cannot access property \\$name on Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" count: 2 @@ -3950,11 +3600,6 @@ parameters: count: 1 path: src/bundle/Debug/Collector/IbexaCoreCollector.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollector\\:\\:collect\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Debug/Collector/IbexaCoreCollector.php - - message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollector\\:\\:getPanelTemplate\\(\\) should return string but returns null\\.$#" count: 1 @@ -3965,16 +3610,6 @@ parameters: count: 1 path: src/bundle/Debug/Collector/IbexaCoreCollector.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollector\\:\\:reset\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Debug/Collector/IbexaCoreCollector.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\Collector\\\\PersistenceCacheCollector\\:\\:collect\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Debug/Collector/PersistenceCacheCollector.php - - message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\Collector\\\\PersistenceCacheCollector\\:\\:getCalls\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -3985,21 +3620,6 @@ parameters: count: 1 path: src/bundle/Debug/Collector/PersistenceCacheCollector.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\Collector\\\\SiteAccessCollector\\:\\:collect\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Debug/Collector/SiteAccessCollector.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\DependencyInjection\\\\Compiler\\\\DataCollectorPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Debug/DependencyInjection/Compiler/DataCollectorPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\IbexaDebugBundle\\:\\:build\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Debug/IbexaDebugBundle.php - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\ApiLoader\\\\HandlerRegistry\\:\\:setHandlersMap\\(\\) has no return type specified\\.$#" count: 1 @@ -4015,103 +3635,13 @@ parameters: count: 1 path: src/bundle/IO/ApiLoader/HandlerRegistry.php - - - message: "#^Cannot call method getTimestamp\\(\\) on int\\.$#" - count: 1 - path: src/bundle/IO/BinaryStreamResponse.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:__construct\\(\\) has parameter \\$headers with no value type specified in iterable type array\\.$#" - count: 1 - path: src/bundle/IO/BinaryStreamResponse.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:getContent\\(\\) should return string\\|false but returns null\\.$#" - count: 1 - path: src/bundle/IO/BinaryStreamResponse.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:sendContent\\(\\) should return \\$this\\(Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\) but empty return statement found\\.$#" - count: 2 - path: src/bundle/IO/BinaryStreamResponse.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:sendContent\\(\\) should return \\$this\\(Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\) but return statement is missing\\.$#" - count: 1 - path: src/bundle/IO/BinaryStreamResponse.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:setAutoLastModified\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/IO/BinaryStreamResponse.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:setContent\\(\\) has parameter \\$content with no type specified\\.$#" - count: 1 - path: src/bundle/IO/BinaryStreamResponse.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:setContent\\(\\) should return \\$this\\(Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\) but return statement is missing\\.$#" - count: 1 - path: src/bundle/IO/BinaryStreamResponse.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:setFile\\(\\) should return Symfony\\\\Component\\\\HttpFoundation\\\\BinaryFileResponse but returns \\$this\\(Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\)\\.$#" - count: 1 - path: src/bundle/IO/BinaryStreamResponse.php - - - - message: "#^PHPDoc tag @param references unknown parameter\\: \\$autoEtag$#" - count: 2 - path: src/bundle/IO/BinaryStreamResponse.php - - - - message: "#^Parameter \\#1 \\$date of method Symfony\\\\Component\\\\HttpFoundation\\\\Response\\:\\:setLastModified\\(\\) expects DateTimeInterface\\|null, DateTime\\|false given\\.$#" - count: 1 - path: src/bundle/IO/BinaryStreamResponse.php - - - - message: "#^Parameter \\#1 \\$file of method Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:setFile\\(\\) expects SplFileInfo\\|string, Ibexa\\\\Core\\\\IO\\\\Values\\\\BinaryFile given\\.$#" - count: 1 - path: src/bundle/IO/BinaryStreamResponse.php - - - - message: "#^Parameter \\#1 \\$stream of function fclose expects resource, resource\\|false given\\.$#" - count: 1 - path: src/bundle/IO/BinaryStreamResponse.php - - message: "#^Parameter \\#1 \\$string of function substr expects string, string\\|null given\\.$#" count: 1 path: src/bundle/IO/BinaryStreamResponse.php - - - message: "#^Parameter \\#2 \\$to of function stream_copy_to_stream expects resource, resource\\|false given\\.$#" - count: 1 - path: src/bundle/IO/BinaryStreamResponse.php - - message: "#^Parameter \\#2 \\$values of method Symfony\\\\Component\\\\HttpFoundation\\\\ResponseHeaderBag\\:\\:set\\(\\) expects array\\\\|string\\|null, int given\\.$#" - count: 2 - path: src/bundle/IO/BinaryStreamResponse.php - - - - message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:\\$file \\(Ibexa\\\\Core\\\\IO\\\\Values\\\\BinaryFile\\) does not accept SplFileInfo\\|string\\.$#" - count: 1 - path: src/bundle/IO/BinaryStreamResponse.php - - - - message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:\\$maxlen has no type specified\\.$#" - count: 1 - path: src/bundle/IO/BinaryStreamResponse.php - - - - message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:\\$offset has no type specified\\.$#" - count: 1 - path: src/bundle/IO/BinaryStreamResponse.php - - - - message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:\\$trustXSendfileTypeHeader has no type specified\\.$#" count: 1 path: src/bundle/IO/BinaryStreamResponse.php @@ -4130,11 +3660,6 @@ parameters: count: 1 path: src/bundle/IO/Command/MigrateFilesCommand.php - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\Command\\\\MigrateFilesCommand\\:\\:configure\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/IO/Command/MigrateFilesCommand.php - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\Command\\\\MigrateFilesCommand\\:\\:migrateFiles\\(\\) has no return type specified\\.$#" count: 1 @@ -4180,11 +3705,6 @@ parameters: count: 1 path: src/bundle/IO/DependencyInjection/Compiler/IOConfigurationPass.php - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/IO/DependencyInjection/Compiler/IOConfigurationPass.php - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPass\\:\\:processHandlers\\(\\) has no return type specified\\.$#" count: 1 @@ -4225,11 +3745,6 @@ parameters: count: 1 path: src/bundle/IO/DependencyInjection/Compiler/IOConfigurationPass.php - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\MigrationFileListerPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/IO/DependencyInjection/Compiler/MigrationFileListerPass.php - - message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeDefinition\\:\\:children\\(\\)\\.$#" count: 1 @@ -4335,86 +3850,11 @@ parameters: count: 1 path: src/bundle/IO/DependencyInjection/ConfigurationFactory/MetadataHandler/LegacyDFSCluster.php - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:addBinarydataHandlerFactory\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:addMetadataHandlerFactory\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:getBinarydataHandlerFactories\\(\\) return type with generic class ArrayObject does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:getConfiguration\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" - count: 1 - path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:getMetadataHandlerFactories\\(\\) return type with generic class ArrayObject does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:load\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:processHandlers\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:processHandlers\\(\\) has parameter \\$config with no type specified\\.$#" - count: 1 - path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php - - - - message: "#^Parameter \\#1 \\$configuration of method Symfony\\\\Component\\\\DependencyInjection\\\\Extension\\\\Extension\\:\\:processConfiguration\\(\\) expects Symfony\\\\Component\\\\Config\\\\Definition\\\\ConfigurationInterface, Symfony\\\\Component\\\\Config\\\\Definition\\\\ConfigurationInterface\\|null given\\.$#" - count: 1 - path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php - - - - message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:\\$binarydataHandlerFactories with generic class ArrayObject does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php - - - - message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:\\$metadataHandlerFactories with generic class ArrayObject does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListener\\:\\:onKernelRequest\\(\\) has no return type specified\\.$#" count: 1 path: src/bundle/IO/EventListener/StreamFileListener.php - - - message: "#^Cannot call method getBinarydataHandlerFactories\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\Extension\\\\ExtensionInterface\\|null\\.$#" - count: 1 - path: src/bundle/IO/IbexaIOBundle.php - - - - message: "#^Cannot call method getMetadataHandlerFactories\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\Extension\\\\ExtensionInterface\\|null\\.$#" - count: 1 - path: src/bundle/IO/IbexaIOBundle.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\IbexaIOBundle\\:\\:build\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/IO/IbexaIOBundle.php - - - - message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\IbexaIOBundle\\:\\:\\$extension \\(Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\) in isset\\(\\) is not nullable\\.$#" - count: 1 - path: src/bundle/IO/IbexaIOBundle.php - - message: "#^Parameter \\#2 \\$offset of class LimitIterator constructor expects int, int\\|null given\\.$#" count: 1 @@ -4585,16 +4025,6 @@ parameters: count: 1 path: src/bundle/LegacySearchEngine/ApiLoader/ConnectionFactory.php - - - message: "#^Method Ibexa\\\\Bundle\\\\LegacySearchEngine\\\\DependencyInjection\\\\IbexaLegacySearchEngineExtension\\:\\:load\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/LegacySearchEngine/DependencyInjection/IbexaLegacySearchEngineExtension.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\LegacySearchEngine\\\\IbexaLegacySearchEngineBundle\\:\\:build\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/LegacySearchEngine/IbexaLegacySearchEngineBundle.php - - message: "#^Call to an undefined method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Installer\\\\Installer\\:\\:setOutput\\(\\)\\.$#" count: 1 @@ -4625,11 +4055,6 @@ parameters: count: 1 path: src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php - - - message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Command\\\\InstallPlatformCommand\\:\\:configure\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php - - message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Command\\\\InstallPlatformCommand\\:\\:executeCommand\\(\\) has no return type specified\\.$#" count: 1 @@ -4660,26 +4085,11 @@ parameters: count: 1 path: src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php - - - message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\DependencyInjection\\\\Compiler\\\\InstallerTagPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/RepositoryInstaller/DependencyInjection/Compiler/InstallerTagPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\DependencyInjection\\\\IbexaRepositoryInstallerExtension\\:\\:load\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/RepositoryInstaller/DependencyInjection/IbexaRepositoryInstallerExtension.php - - message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Event\\\\Subscriber\\\\BuildSchemaSubscriber\\:\\:getSubscribedEvents\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: src/bundle/RepositoryInstaller/Event/Subscriber/BuildSchemaSubscriber.php - - - message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\IbexaRepositoryInstallerBundle\\:\\:build\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundle.php - - message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Installer\\\\CoreInstaller\\:\\:importBinaries\\(\\) has no return type specified\\.$#" count: 1 @@ -5390,26 +4800,6 @@ parameters: count: 1 path: src/contracts/Repository/Decorator/LocationServiceDecorator.php - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecorator\\:\\:findContent\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/Decorator/SearchServiceDecorator.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecorator\\:\\:findContentInfo\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/Decorator/SearchServiceDecorator.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecorator\\:\\:findLocations\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/Decorator/SearchServiceDecorator.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecorator\\:\\:findSingle\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/Decorator/SearchServiceDecorator.php - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecorator\\:\\:suggest\\(\\) has no return type specified\\.$#" count: 1 @@ -6600,11 +5990,6 @@ parameters: count: 1 path: src/contracts/Repository/Iterator/BatchIterator.php - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\AbstractSearchAdapter\\:\\:__construct\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapter.php - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\ContentFilteringAdapter\\:\\:__construct\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" count: 1 @@ -6620,16 +6005,6 @@ parameters: count: 1 path: src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationFilteringAdapter.php - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\LocationSearchAdapter\\:\\:__construct\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php - - - - message: "#^Parameter \\#1 \\$query of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService\\:\\:findLocations\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\LocationQuery, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query given\\.$#" - count: 1 - path: src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LanguageResolver\\:\\:getPrioritizedLanguages\\(\\) has parameter \\$forcedLanguages with no value type specified in iterable type array\\.$#" count: 1 @@ -6680,26 +6055,6 @@ parameters: count: 1 path: src/contracts/Repository/PermissionCriterionResolver.php - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService\\:\\:findContent\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/SearchService.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService\\:\\:findContentInfo\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/SearchService.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService\\:\\:findLocations\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/SearchService.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService\\:\\:findSingle\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/SearchService.php - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService\\:\\:suggest\\(\\) has no return type specified\\.$#" count: 1 @@ -6750,11 +6105,6 @@ parameters: count: 1 path: src/contracts/Repository/Values/Content/ContentDraftList.php - - - message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentList implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/contracts/Repository/Values/Content/ContentList.php - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\DraftList\\\\Item\\\\UnauthorizedContentDraftListItem\\:\\:__construct\\(\\) has parameter \\$payload with no value type specified in iterable type array\\.$#" count: 1 @@ -7025,11 +6375,6 @@ parameters: count: 1 path: src/contracts/Repository/Values/Content/Search/Facet/UserFacet.php - - - message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/contracts/Repository/Values/Content/Search/SearchResult.php - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\:\\:__construct\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" count: 1 @@ -7215,21 +6560,6 @@ parameters: count: 1 path: src/contracts/Search/Handler.php - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:findContent\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Search/Handler.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:findLocations\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Search/Handler.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:findSingle\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Search/Handler.php - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:indexContent\\(\\) has no return type specified\\.$#" count: 1 @@ -7425,16 +6755,6 @@ parameters: count: 1 path: src/lib/Base/Container/Compiler/AbstractFieldTypeBasedPass.php - - - message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\AbstractFieldTypeBasedPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/Base/Container/Compiler/AbstractFieldTypeBasedPass.php - - - - message: "#^Parameter \\#1 \\$object_or_class of function is_a expects object\\|string, string\\|null given\\.$#" - count: 1 - path: src/lib/Base/Container/Compiler/FieldTypeRegistryPass.php - - message: "#^Cannot call method isSubclassOf\\(\\) on ReflectionClass\\|null\\.$#" count: 1 @@ -7450,16 +6770,6 @@ parameters: count: 1 path: src/lib/Base/Container/Compiler/GenericFieldTypeConverterPass.php - - - message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Persistence\\\\FieldTypeRegistryPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/Base/Container/Compiler/Persistence/FieldTypeRegistryPass.php - - - - message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\FieldRegistryPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/Base/Container/Compiler/Search/FieldRegistryPass.php - - message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\CriteriaConverterPass\\:\\:addHandlers\\(\\) has no return type specified\\.$#" count: 1 @@ -7470,16 +6780,6 @@ parameters: count: 1 path: src/lib/Base/Container/Compiler/Search/Legacy/CriteriaConverterPass.php - - - message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\CriteriaConverterPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/Base/Container/Compiler/Search/Legacy/CriteriaConverterPass.php - - - - message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\CriterionFieldValueHandlerRegistryPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/Base/Container/Compiler/Search/Legacy/CriterionFieldValueHandlerRegistryPass.php - - message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\SortClauseConverterPass\\:\\:addHandlers\\(\\) has no return type specified\\.$#" count: 1 @@ -7490,21 +6790,6 @@ parameters: count: 1 path: src/lib/Base/Container/Compiler/Search/Legacy/SortClauseConverterPass.php - - - message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\SortClauseConverterPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/Base/Container/Compiler/Search/Legacy/SortClauseConverterPass.php - - - - message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Storage\\\\ExternalStorageRegistryPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/Base/Container/Compiler/Storage/ExternalStorageRegistryPass.php - - - - message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Storage\\\\Legacy\\\\RoleLimitationConverterPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/Base/Container/Compiler/Storage/Legacy/RoleLimitationConverterPass.php - - message: "#^Class Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\TaggedServiceIdsIterator\\\\BackwardCompatibleIterator implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" count: 1 @@ -10345,11 +9630,6 @@ parameters: count: 1 path: src/lib/IO/IOService.php - - - message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOService\\:\\:loadBinaryFile\\(\\) should return Ibexa\\\\Core\\\\IO\\\\Values\\\\BinaryFile but returns false\\.$#" - count: 1 - path: src/lib/IO/IOService.php - - message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOService\\:\\:newBinaryCreateStructFromUploadedFile\\(\\) has parameter \\$uploadedFile with no value type specified in iterable type array\\.$#" count: 1 @@ -10690,6 +9970,21 @@ parameters: count: 1 path: src/lib/MVC/RepositoryAwareInterface.php + - + message: "#^Class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\AbstractPropertyWhitelistNormalizer extends @final class Symfony\\\\Component\\\\Serializer\\\\Normalizer\\\\PropertyNormalizer\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Component/Serializer/AbstractPropertyWhitelistNormalizer.php + + - + message: "#^Class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\MapNormalizer extends @final class Symfony\\\\Component\\\\Serializer\\\\Normalizer\\\\PropertyNormalizer\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Component/Serializer/MapNormalizer.php + + - + message: "#^Class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\SimplifiedRequestNormalizer extends @final class Symfony\\\\Component\\\\Serializer\\\\Normalizer\\\\PropertyNormalizer\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Component/Serializer/SimplifiedRequestNormalizer.php + - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\SimplifiedRequestNormalizer\\:\\:supportsNormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" count: 1 @@ -10715,16 +10010,6 @@ parameters: count: 1 path: src/lib/MVC/Symfony/Controller/Content/QueryController.php - - - message: "#^Parameter \\#1 \\$adapter of class Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\Pagerfanta constructor expects Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\SearchResultAdapter, Pagerfanta\\\\Adapter\\\\AdapterInterface given\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Controller/Content/QueryController.php - - - - message: "#^Parameter \\#1 \\$query of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Content\\\\QueryController\\:\\:getAdapter\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query, Ibexa\\\\Core\\\\QueryType\\\\QueryType given\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Controller/Content/QueryController.php - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Content\\\\ViewController\\:\\:handleViewException\\(\\) has no return type specified\\.$#" count: 1 @@ -10820,31 +10105,6 @@ parameters: count: 1 path: src/lib/MVC/Symfony/Controller/QueryRenderController.php - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderController\\:\\:getAdapter\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Controller/QueryRenderController.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderController\\:\\:getAdapter\\(\\) should return Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\SearchResultAdapter but returns Pagerfanta\\\\Adapter\\\\AdapterInterface\\.$#" - count: 2 - path: src/lib/MVC/Symfony/Controller/QueryRenderController.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderController\\:\\:renderQuery\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Controller/QueryRenderController.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderController\\:\\:resolveOptions\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Controller/QueryRenderController.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderController\\:\\:resolveOptions\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Controller/QueryRenderController.php - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\SecurityController\\:\\:loginAction\\(\\) has no return type specified\\.$#" count: 1 @@ -11160,46 +10420,6 @@ parameters: count: 1 path: src/lib/MVC/Symfony/Matcher/ViewMatcherInterface.php - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\:\\:doGenerate\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\:\\:generate\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\:\\:setLogger\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\:\\:setRequestContext\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\:\\:setSiteAccessRouter\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator.php - - - - message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) does not accept Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator.php - - - - message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\:\\:\\$siteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator.php - - message: "#^Cannot access property \\$attributes on Symfony\\\\Component\\\\HttpFoundation\\\\Request\\|null\\.$#" count: 2 @@ -11225,61 +10445,6 @@ parameters: count: 1 path: src/lib/MVC/Symfony/Routing/Generator/RouteReferenceGeneratorInterface.php - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:__construct\\(\\) has parameter \\$unsafeCharMap with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:createQueryString\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:doGenerate\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:loadLocation\\(\\) should return Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:setExcludedUriPrefixes\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:setExcludedUriPrefixes\\(\\) has parameter \\$excludedUriPrefixes with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:setRootLocationId\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php - - - - message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:\\$excludedUriPrefixes type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php - - - - message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:\\$pathPrefixMap type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php - - - - message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:\\$repository \\(Ibexa\\\\Core\\\\Repository\\\\Repository\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Repository\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php - - - - message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:\\$unsafeCharMap type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReference\\:\\:__construct\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" count: 1 @@ -11385,91 +10550,6 @@ parameters: count: 1 path: src/lib/MVC/Symfony/Routing/SimplifiedRequest.php - - - message: "#^Else branch is unreachable because ternary operator condition is always true\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:generate\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:getRouteDebugMessage\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:getUrlAlias\\(\\) has parameter \\$pathinfo with no type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:match\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:matchRequest\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:setContext\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:setRootLocationId\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:supportsObject\\(\\) has parameter \\$object with no type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php - - - - message: "#^Parameter \\#1 \\$rootLocationId of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:getPathPrefixByRootLocationId\\(\\) expects int, int\\|string given\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php - - - - message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) does not accept Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php - - - - message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) in isset\\(\\) is not nullable\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlWildcardRouter\\:\\:generate\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/UrlWildcardRouter.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlWildcardRouter\\:\\:getRouteDebugMessage\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/UrlWildcardRouter.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlWildcardRouter\\:\\:match\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/UrlWildcardRouter.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlWildcardRouter\\:\\:matchRequest\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/UrlWildcardRouter.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlWildcardRouter\\:\\:setLogger\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Routing/UrlWildcardRouter.php - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authorization\\\\Attribute\\:\\:__construct\\(\\) has parameter \\$function with no type specified\\.$#" count: 1 @@ -11515,66 +10595,11 @@ parameters: count: 1 path: src/lib/MVC/Symfony/Security/Exception/UnauthorizedSiteAccessException.php - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtils\\:\\:analyzeLink\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Security/HttpUtils.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtils\\:\\:analyzeLink\\(\\) has parameter \\$path with no type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Security/HttpUtils.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtils\\:\\:generateUri\\(\\) has parameter \\$request with no type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Security/HttpUtils.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtils\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Security/HttpUtils.php - - - - message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtils\\:\\:\\$siteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Security/HttpUtils.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\InteractiveLoginToken\\:\\:__construct\\(\\) has parameter \\$credentials with no type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Security/InteractiveLoginToken.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\InteractiveLoginToken\\:\\:__construct\\(\\) has parameter \\$originalTokenType with no type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Security/InteractiveLoginToken.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\InteractiveLoginToken\\:\\:__construct\\(\\) has parameter \\$providerKey with no type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Security/InteractiveLoginToken.php - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserInterface\\:\\:setAPIUser\\(\\) has no return type specified\\.$#" count: 1 path: src/lib/MVC/Symfony/Security/UserInterface.php - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserWrapped\\:\\:eraseCredentials\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Security/UserWrapped.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserWrapped\\:\\:setAPIUser\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Security/UserWrapped.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserWrapped\\:\\:setWrappedUser\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Security/UserWrapped.php - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\:\\:__construct\\(\\) has parameter \\$groups with no value type specified in iterable type array\\.$#" count: 1 @@ -12325,11 +11350,6 @@ parameters: count: 5 path: src/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php - - - message: "#^Cannot call method error\\(\\) on Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ExceptionMessageTemplateFileVisitor\\:\\:isIgnore\\(\\) has parameter \\$node with no type specified\\.$#" count: 1 @@ -12340,91 +11360,16 @@ parameters: count: 1 path: src/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php - - - message: "#^Return type \\(void\\) of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ExceptionMessageTemplateFileVisitor\\:\\:enterNode\\(\\) should be compatible with return type \\(int\\|PhpParser\\\\Node\\|null\\) of method PhpParser\\\\NodeVisitor\\:\\:enterNode\\(\\)$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\FieldTypesTranslationExtractor\\:\\:extract\\(\\) has no return type specified\\.$#" count: 1 path: src/lib/MVC/Symfony/Translation/FieldTypesTranslationExtractor.php - - - message: "#^Access to an undefined property PhpParser\\\\Node\\:\\:\\$args\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php - - message: "#^Access to an undefined property PhpParser\\\\Node\\\\Arg\\|PhpParser\\\\Node\\\\VariadicPlaceholder\\:\\:\\$value\\.$#" count: 1 path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php - - - message: "#^Access to an undefined property PhpParser\\\\Node\\\\Expr\\|PhpParser\\\\Node\\\\Name\\|PhpParser\\\\Node\\\\Stmt\\\\Class_\\:\\:\\$parts\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:afterTraverse\\(\\) has parameter \\$nodes with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:afterTraverse\\(\\) should return array\\\\|null but return statement is missing\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:beforeTraverse\\(\\) has parameter \\$nodes with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:beforeTraverse\\(\\) should return array\\\\|null but return statement is missing\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:enterNode\\(\\) should return int\\|PhpParser\\\\Node\\|null but empty return statement found\\.$#" - count: 4 - path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:enterNode\\(\\) should return int\\|PhpParser\\\\Node\\|null but return statement is missing\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:leaveNode\\(\\) should return array\\\\|int\\|PhpParser\\\\Node\\|null but return statement is missing\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:setLogger\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:visitFile\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:visitPhpFile\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:visitPhpFile\\(\\) has parameter \\$ast with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:visitTwigFile\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php - - message: "#^Parameter \\#1 \\$desc of method JMS\\\\TranslationBundle\\\\Model\\\\Message\\:\\:setDesc\\(\\) expects string, string\\|null given\\.$#" count: 1 @@ -12435,81 +11380,16 @@ parameters: count: 1 path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php - - - message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:\\$exceptionsToExtractFrom type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php - - - - message: "#^Access to an undefined property PhpParser\\\\Node\\:\\:\\$args\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php - - message: "#^Access to an undefined property PhpParser\\\\Node\\\\Arg\\|PhpParser\\\\Node\\\\VariadicPlaceholder\\:\\:\\$value\\.$#" count: 2 path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:afterTraverse\\(\\) has parameter \\$nodes with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:afterTraverse\\(\\) should return array\\\\|null but return statement is missing\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:beforeTraverse\\(\\) has parameter \\$nodes with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:beforeTraverse\\(\\) should return array\\\\|null but return statement is missing\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:enterNode\\(\\) should return int\\|PhpParser\\\\Node\\|null but empty return statement found\\.$#" - count: 3 - path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:enterNode\\(\\) should return int\\|PhpParser\\\\Node\\|null but return statement is missing\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:leaveNode\\(\\) should return array\\\\|int\\|PhpParser\\\\Node\\|null but return statement is missing\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:setLogger\\(\\) has no return type specified\\.$#" count: 1 path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:visitFile\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:visitPhpFile\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:visitPhpFile\\(\\) has parameter \\$ast with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:visitTwigFile\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php - - message: "#^Parameter \\#1 \\$desc of method JMS\\\\TranslationBundle\\\\Model\\\\Message\\:\\:setDesc\\(\\) expects string, string\\|null given\\.$#" count: 2 @@ -12520,11 +11400,6 @@ parameters: count: 2 path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php - - - message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:\\$classToExtractFrom type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:__construct\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 @@ -12970,71 +11845,6 @@ parameters: count: 1 path: src/lib/MVC/Symfony/View/ViewManagerInterface.php - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AbstractSearchResultAdapter\\:\\:__construct\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AbstractSearchResultAdapter\\:\\:executeQuery\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AbstractSearchResultAdapter\\:\\:getNbResults\\(\\) should return int but returns int\\|null\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php - - - - message: "#^Property Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AbstractSearchResultAdapter\\:\\:\\$languageFilter type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AdapterFactory\\\\SearchHitAdapterFactory\\:\\:createAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AdapterFactory\\\\SearchHitAdapterFactory\\:\\:createFixedAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AdapterFactory\\\\SearchHitAdapterFactoryInterface\\:\\:createAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AdapterFactory\\\\SearchHitAdapterFactoryInterface\\:\\:createFixedAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\ContentFilteringAdapter\\:\\:__construct\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php - - - - message: "#^Property Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\ContentFilteringAdapter\\:\\:\\$languageFilter type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\ContentSearchAdapter\\:\\:getSlice\\(\\) should return array\\ but returns array\\, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\>\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php - - - - message: "#^Return type \\(array\\\\) of method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\ContentSearchAdapter\\:\\:getSlice\\(\\) should be compatible with return type \\(array\\\\) of method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AbstractSearchResultAdapter\\:\\:getSlice\\(\\)$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\ContentSearchHitAdapter\\:\\:executeQuery\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/ContentSearchHitAdapter.php - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\FixedSearchResultHitAdapter\\:\\:getAggregations\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResultCollection but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResultCollection\\|null\\.$#" count: 1 @@ -13045,56 +11855,6 @@ parameters: count: 1 path: src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php - - - message: "#^Property Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationFilteringAdapter\\:\\:\\$languageFilter type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationSearchAdapter\\:\\:getSlice\\(\\) should return array\\ but returns array\\, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\>\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php - - - - message: "#^Return type \\(array\\\\) of method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationSearchAdapter\\:\\:getSlice\\(\\) should be compatible with return type \\(array\\\\) of method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AbstractSearchResultAdapter\\:\\:getSlice\\(\\)$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationSearchHitAdapter\\:\\:__construct\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationSearchHitAdapter\\:\\:executeQuery\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php - - - - message: "#^Call to an undefined method Pagerfanta\\\\Adapter\\\\AdapterInterface\\:\\:getAggregations\\(\\)\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/Pagerfanta.php - - - - message: "#^Call to an undefined method Pagerfanta\\\\Adapter\\\\AdapterInterface\\:\\:getMaxScore\\(\\)\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/Pagerfanta.php - - - - message: "#^Call to an undefined method Pagerfanta\\\\Adapter\\\\AdapterInterface\\:\\:getTime\\(\\)\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/Pagerfanta.php - - - - message: "#^Call to an undefined method Pagerfanta\\\\Adapter\\\\AdapterInterface\\:\\:getTimedOut\\(\\)\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/Pagerfanta.php - - - - message: "#^Class Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\Pagerfanta extends generic class Pagerfanta\\\\Pagerfanta but does not specify its types\\: T$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/Pagerfanta.php - - message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\AbstractHandler\\:\\:getMultipleCacheItems\\(\\) has parameter \\$ids with no value type specified in iterable type array\\.$#" count: 1 @@ -13150,41 +11910,6 @@ parameters: count: 1 path: src/lib/Persistence/Cache/AbstractInMemoryHandler.php - - - message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\TransactionalInMemoryCacheAdapter\\:\\:__construct\\(\\) has parameter \\$deferredItemsDeletion with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\TransactionalInMemoryCacheAdapter\\:\\:__construct\\(\\) has parameter \\$deferredTagsInvalidation with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\TransactionalInMemoryCacheAdapter\\:\\:getItems\\(\\) should return Traversable\\ but returns array\\\\.$#" - count: 1 - path: src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\TransactionalInMemoryCacheAdapter\\:\\:markItemsAsDeferredMissIfNeeded\\(\\) should return array\\ but returns iterable\\\\.$#" - count: 1 - path: src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php - - - - message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\TransactionalInMemoryCacheAdapter\\:\\:\\$deferredItemsDeletion type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php - - - - message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\TransactionalInMemoryCacheAdapter\\:\\:\\$deferredTagsInvalidation type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php - - - - message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\TransactionalInMemoryCacheAdapter\\:\\:\\$inMemoryPools \\(array\\\\) does not accept iterable\\\\.$#" - count: 1 - path: src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php - - message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\BookmarkHandler\\:\\:loadByUserIdAndLocationId\\(\\) has parameter \\$locationIds with no value type specified in iterable type array\\.$#" count: 1 @@ -18615,16 +17340,6 @@ parameters: count: 1 path: src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php - - - message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\QueryParameterContentViewQueryTypeMapper\\:\\:map\\(\\) should return Ibexa\\\\Core\\\\QueryType\\\\QueryType but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\.$#" - count: 1 - path: src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php - - - - message: "#^Parameter \\#2 \\$code of class InvalidArgumentException constructor expects int, string given\\.$#" - count: 1 - path: src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php - - message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\QueryType\\:\\:getQuery\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 @@ -19580,11 +18295,6 @@ parameters: count: 1 path: src/lib/Repository/SearchService.php - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:findContent\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/SearchService.php - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:findContentInfo\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" count: 1 @@ -19700,26 +18410,6 @@ parameters: count: 1 path: src/lib/Repository/SiteAccessAware/Repository.php - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchService\\:\\:findContent\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/SiteAccessAware/SearchService.php - - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchService\\:\\:findContentInfo\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/SiteAccessAware/SearchService.php - - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchService\\:\\:findLocations\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/SiteAccessAware/SearchService.php - - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchService\\:\\:findSingle\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/SiteAccessAware/SearchService.php - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchService\\:\\:suggest\\(\\) has no return type specified\\.$#" count: 1 @@ -19940,16 +18630,6 @@ parameters: count: 2 path: src/lib/Repository/URLService.php - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\UsageSearchResult\\:\\:\\$items \\(array\\\\) does not accept array\\\\.$#" - count: 1 - path: src/lib/Repository/URLService.php - - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\UsageSearchResult\\:\\:\\$totalCount \\(int\\) does not accept int\\|null\\.$#" - count: 1 - path: src/lib/Repository/URLService.php - - message: "#^Property Ibexa\\\\Core\\\\Repository\\\\URLService\\:\\:\\$repository \\(Ibexa\\\\Core\\\\Repository\\\\Repository\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Repository\\.$#" count: 1 @@ -19980,11 +18660,6 @@ parameters: count: 1 path: src/lib/Repository/UserPreferenceService.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentInfo\\.$#" - count: 4 - path: src/lib/Repository/UserService.php - - message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:internalLoadContentById\\(\\)\\.$#" count: 5 @@ -20110,11 +18785,6 @@ parameters: count: 1 path: src/lib/Repository/Values/Content/Content.php - - - message: "#^Cannot access offset mixed on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\.$#" - count: 1 - path: src/lib/Repository/Values/Content/Content.php - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\:\\:__construct\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" count: 1 @@ -21375,26 +20045,6 @@ parameters: count: 1 path: src/lib/Search/Legacy/Content/Handler.php - - - message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:findContent\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Search/Legacy/Content/Handler.php - - - - message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:findLocations\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Search/Legacy/Content/Handler.php - - - - message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:findSingle\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Search/Legacy/Content/Handler.php - - - - message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:findSingle\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\ContentInfo but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\.$#" - count: 1 - path: src/lib/Search/Legacy/Content/Handler.php - - message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:indexContent\\(\\) has no return type specified\\.$#" count: 1 @@ -21420,16 +20070,6 @@ parameters: count: 1 path: src/lib/Search/Legacy/Content/Handler.php - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\:\\:\\$time \\(int\\) does not accept float\\.$#" - count: 2 - path: src/lib/Search/Legacy/Content/Handler.php - - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\:\\:\\$totalCount \\(int\\|null\\) does not accept array\\.$#" - count: 2 - path: src/lib/Search/Legacy/Content/Handler.php - - message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Indexer\\:\\:purge\\(\\) has no return type specified\\.$#" count: 1 @@ -23190,11 +21830,6 @@ parameters: count: 1 path: tests/bundle/Core/DependencyInjection/Stub/StubYamlPolicyProvider.php - - - message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Entity\\\\EntityManagerFactoryTest\\:\\:\\$serviceLocator is never read, only written\\.$#" - count: 1 - path: tests/bundle/Core/Entity/EntityManagerFactoryTest.php - - message: "#^Call to an undefined method PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\|Psr\\\\Log\\\\LoggerInterface\\:\\:expects\\(\\)\\.$#" count: 2 @@ -23655,11 +22290,6 @@ parameters: count: 1 path: tests/bundle/Core/EventListener/Stubs/TestOutput.php - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\TestOutput\\:\\:doWrite\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/bundle/Core/EventListener/Stubs/TestOutput.php - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\TestOutput\\:\\:doWrite\\(\\) has parameter \\$message with no type specified\\.$#" count: 1 @@ -23780,31 +22410,6 @@ parameters: count: 1 path: tests/bundle/Core/EventSubscriber/TrustedHeaderClientIpEventSubscriberTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRendererTest\\:\\:testGetName\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRendererTest\\:\\:testRendererAbsoluteUrl\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRendererTest\\:\\:testRendererControllerReference\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRendererTest\\:\\:testSetFragmentPath\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRendererTest\\:\\:testSetFragmentPathNotRoutableRenderer\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php - - message: "#^Binary operation \"\\.\" between 'rendered_' and \\(Closure\\(array\\\\)\\: string\\)\\|string results in an error\\.$#" count: 1 @@ -23835,31 +22440,11 @@ parameters: count: 1 path: tests/bundle/Core/Fragment/FragmentListenerFactoryTest.php - - - message: "#^Access to an undefined property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\FragmentRendererBaseTest\\:\\:\\$innerRenderer\\.$#" - count: 1 - path: tests/bundle/Core/Fragment/FragmentRendererBaseTest.php - - - - message: "#^Call to an undefined method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\:\\:getSubMatchers\\(\\)\\.$#" - count: 1 - path: tests/bundle/Core/Fragment/FragmentRendererBaseTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\InlineFragmentRendererTest\\:\\:testRendererControllerReference\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/bundle/Core/Fragment/InlineFragmentRendererTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\AliasCleanerTest\\:\\:testRemoveAliases\\(\\) has no return type specified\\.$#" count: 1 path: tests/bundle/Core/Imagine/AliasCleanerTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\AliasGeneratorTest\\:\\:assertImageVariationIsCorrect\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/bundle/Core/Imagine/AliasGeneratorTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\AliasGeneratorTest\\:\\:supportsValueProvider\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -24955,61 +23540,6 @@ parameters: count: 1 path: tests/bundle/IO/DependencyInjection/IbexaIOExtensionTest.php - - - message: "#^Cannot call method getDate\\(\\) on Symfony\\\\Component\\\\HttpFoundation\\\\Response\\|null\\.$#" - count: 2 - path: tests/bundle/IO/EventListener/StreamFileListenerTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListenerTest\\:\\:configureIoUrlPrefix\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/bundle/IO/EventListener/StreamFileListenerTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListenerTest\\:\\:configureIoUrlPrefix\\(\\) has parameter \\$urlPrefix with no type specified\\.$#" - count: 1 - path: tests/bundle/IO/EventListener/StreamFileListenerTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListenerTest\\:\\:createEvent\\(\\) has parameter \\$request with no type specified\\.$#" - count: 1 - path: tests/bundle/IO/EventListener/StreamFileListenerTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListenerTest\\:\\:createRequest\\(\\) has parameter \\$host with no type specified\\.$#" - count: 1 - path: tests/bundle/IO/EventListener/StreamFileListenerTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListenerTest\\:\\:createRequest\\(\\) has parameter \\$semanticPath with no type specified\\.$#" - count: 1 - path: tests/bundle/IO/EventListener/StreamFileListenerTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListenerTest\\:\\:testDoesNotRespondToNoIoRequest\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/bundle/IO/EventListener/StreamFileListenerTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListenerTest\\:\\:testDoesNotRespondToNonIoUri\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/bundle/IO/EventListener/StreamFileListenerTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListenerTest\\:\\:testRespondsToIoRequest\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/bundle/IO/EventListener/StreamFileListenerTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListenerTest\\:\\:testRespondsToIoUri\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/bundle/IO/EventListener/StreamFileListenerTest.php - - - - message: "#^Parameter \\#1 \\$date of method Symfony\\\\Component\\\\HttpFoundation\\\\Response\\:\\:setDate\\(\\) expects DateTimeInterface, DateTimeInterface\\|null given\\.$#" - count: 2 - path: tests/bundle/IO/EventListener/StreamFileListenerTest.php - - message: "#^Call to an undefined method Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\\\Flysystem\\:\\:expects\\(\\)\\.$#" count: 2 @@ -32110,11 +30640,6 @@ parameters: count: 2 path: tests/integration/Core/Repository/Filtering/ContentFilteringTest.php - - - message: "#^Parameter \\#1 \\$totalCount of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentList constructor expects int, int\\|null given\\.$#" - count: 1 - path: tests/integration/Core/Repository/Filtering/ContentFilteringTest.php - - message: "#^Parameter \\#1 \\$value of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\ParentLocationId constructor expects array\\\\|int, int\\|null given\\.$#" count: 1 @@ -32415,11 +30940,6 @@ parameters: count: 1 path: tests/integration/Core/Repository/LocationServiceAuthorizationTest.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" - count: 1 - path: tests/integration/Core/Repository/LocationServiceTest.php - - message: "#^Cannot access property \\$id on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\.$#" count: 11 @@ -33515,11 +32035,6 @@ parameters: count: 1 path: tests/integration/Core/Repository/Regression/EZP20018ObjectStateTest.php - - - message: "#^Parameter \\#1 \\$expectedCount of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertCount\\(\\) expects int, int\\|null given\\.$#" - count: 2 - path: tests/integration/Core/Repository/Regression/EZP20018ObjectStateTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP20018VisibilityTest\\:\\:testSearchForHiddenContent\\(\\) has no return type specified\\.$#" count: 1 @@ -34405,31 +32920,6 @@ parameters: count: 1 path: tests/integration/Core/Repository/RoleServiceTest.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentInfo\\.$#" - count: 4 - path: tests/integration/Core/Repository/SearchEngineIndexingTest.php - - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$hidden\\.$#" - count: 2 - path: tests/integration/Core/Repository/SearchEngineIndexingTest.php - - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" - count: 17 - path: tests/integration/Core/Repository/SearchEngineIndexingTest.php - - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$invisible\\.$#" - count: 1 - path: tests/integration/Core/Repository/SearchEngineIndexingTest.php - - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$versionInfo\\.$#" - count: 2 - path: tests/integration/Core/Repository/SearchEngineIndexingTest.php - - message: "#^Access to an undefined property Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\:\\:\\$mainLocationId\\.$#" count: 1 @@ -34890,16 +33380,6 @@ parameters: count: 1 path: tests/integration/Core/Repository/SearchService/MultilingualContentSearchIndexingTest.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentInfo\\.$#" - count: 1 - path: tests/integration/Core/Repository/SearchService/RemoteIdIndexingTest.php - - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" - count: 1 - path: tests/integration/Core/Repository/SearchService/RemoteIdIndexingTest.php - - message: "#^Cannot access offset 0 on array\\\\|bool\\|float\\|int\\|string\\.$#" count: 1 @@ -34995,11 +33475,6 @@ parameters: count: 1 path: tests/integration/Core/Repository/SearchService/SortClause/ScoreTest.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentInfo\\.$#" - count: 2 - path: tests/integration/Core/Repository/SearchServiceAuthorizationTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceAuthorizationTest\\:\\:testFindContent\\(\\) has no return type specified\\.$#" count: 1 @@ -35132,7 +33607,7 @@ parameters: - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" - count: 19 + count: 1 path: tests/integration/Core/Repository/SearchServiceLocationTest.php - @@ -35290,19 +33765,9 @@ parameters: count: 2 path: tests/integration/Core/Repository/SearchServiceLocationTest.php - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\:\\:\\$valueObject \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\) does not accept array\\\\.$#" - count: 1 - path: tests/integration/Core/Repository/SearchServiceLocationTest.php - - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentId\\.$#" - count: 4 - path: tests/integration/Core/Repository/SearchServiceTest.php - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" - count: 36 + count: 3 path: tests/integration/Core/Repository/SearchServiceTest.php - @@ -35320,11 +33785,6 @@ parameters: count: 1 path: tests/integration/Core/Repository/SearchServiceTest.php - - - message: "#^Cannot access property \\$valueObject on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\|null\\.$#" - count: 3 - path: tests/integration/Core/Repository/SearchServiceTest.php - - message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\ContentTypeIdentifier constructor invoked with 2 parameters, 1 required\\.$#" count: 2 @@ -36140,11 +34600,6 @@ parameters: count: 3 path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:assertIndexName\\(\\) has parameter \\$indexMap with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:createContent\\(\\) has parameter \\$parentLocationIds with no value type specified in iterable type array\\.$#" count: 1 @@ -36165,16 +34620,6 @@ parameters: count: 1 path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:getIndexName\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:getIndexName\\(\\) has parameter \\$indexMap with no type specified\\.$#" - count: 1 - path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:getIndexesToMatchData\\(\\) has parameter \\$inputContentData with no value type specified in iterable type array\\.$#" count: 1 @@ -42255,81 +40700,6 @@ parameters: count: 1 path: tests/lib/IO/IOMetadataHandler/LegacyDFSClusterTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:getPrefixedUri\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/IO/IOServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:getPrefixedUri\\(\\) has parameter \\$uri with no type specified\\.$#" - count: 1 - path: tests/lib/IO/IOServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testCreateBinaryFile\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/IO/IOServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testDeleteBinaryFile\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/IO/IOServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testDeleteDirectory\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/IO/IOServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testExists\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/IO/IOServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testExistsNot\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/IO/IOServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testGetFileContents\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/IO/IOServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testGetFileInputStream\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/IO/IOServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testGetMimeType\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/IO/IOServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testLoadBinaryFile\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/IO/IOServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testLoadBinaryFileByUri\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/IO/IOServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testLoadBinaryFileNoMetadataUri\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/IO/IOServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testNewBinaryCreateStructFromLocalFile\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/IO/IOServiceTest.php - - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\IO\\\\BinaryFile\\:\\:\\$size \\(int\\) does not accept int\\<0, max\\>\\|false\\.$#" - count: 1 - path: tests/lib/IO/IOServiceTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\MimeTypeDetector\\\\FileInfoTest\\:\\:testGetFromBuffer\\(\\) has no return type specified\\.$#" count: 1 @@ -42345,11 +40715,6 @@ parameters: count: 1 path: tests/lib/IO/MimeTypeDetector/FileInfoTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\TolerantIOServiceTest\\:\\:testCreateMissingBinaryFile\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/IO/TolerantIOServiceTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\UrlDecorator\\\\AbsolutePrefixTest\\:\\:provideData\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -43550,26 +41915,6 @@ parameters: count: 1 path: tests/lib/MVC/Symfony/Component/Serializer/Stubs/MatcherStub.php - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\Stubs\\\\SerializerStub\\:\\:deserialize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Component/Serializer/Stubs/SerializerStub.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\Stubs\\\\SerializerStub\\:\\:deserialize\\(\\) has parameter \\$format with no type specified\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Component/Serializer/Stubs/SerializerStub.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\Stubs\\\\SerializerStub\\:\\:deserialize\\(\\) has parameter \\$type with no type specified\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Component/Serializer/Stubs/SerializerStub.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\Stubs\\\\SerializerStub\\:\\:serialize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Component/Serializer/Stubs/SerializerStub.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\ControllerTest\\:\\:testRender\\(\\) has no return type specified\\.$#" count: 1 @@ -43580,21 +41925,6 @@ parameters: count: 1 path: tests/lib/MVC/Symfony/Controller/ControllerTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderControllerTest\\:\\:assertRenderQueryResult\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderControllerTest\\:\\:configureMocks\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php - - - - message: "#^Parameter \\#1 \\$adapter of class Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\Pagerfanta constructor expects Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\SearchResultAdapter, Pagerfanta\\\\Adapter\\\\AdapterInterface given\\.$#" - count: 2 - path: tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\RouteReferenceGenerationEventTest\\:\\:testConstruct\\(\\) has no return type specified\\.$#" count: 1 @@ -44700,16 +43030,6 @@ parameters: count: 2 path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\InteractiveLoginTokenTest\\:\\:testConstruct\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Security/InteractiveLoginTokenTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\InteractiveLoginTokenTest\\:\\:testSerialize\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Security/InteractiveLoginTokenTest.php - - message: "#^Parameter \\#1 \\$expirationDate of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PasswordInfo constructor expects DateTimeImmutable\\|null, DateTimeImmutable\\|false given\\.$#" count: 1 @@ -44740,36 +43060,11 @@ parameters: count: 1 path: tests/lib/MVC/Symfony/Security/UserTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserWrappedTest\\:\\:testGetSetAPIUser\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Security/UserWrappedTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserWrappedTest\\:\\:testGetSetWrappedUser\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Security/UserWrappedTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserWrappedTest\\:\\:testIsEqualTo\\(\\) has no return type specified\\.$#" count: 1 path: tests/lib/MVC/Symfony/Security/UserWrappedTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserWrappedTest\\:\\:testRegularUser\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Security/UserWrappedTest.php - - - - message: "#^Parameter \\#1 \\$string of function md5 expects string, float given\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Security/UserWrappedTest.php - - - - message: "#^Parameter \\#2 \\$apiUser of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserWrapped constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" - count: 5 - path: tests/lib/MVC/Symfony/Security/UserWrappedTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\CoreVoterTest\\:\\:supportsAttributeProvider\\(\\) has no return type specified\\.$#" count: 1 @@ -45430,31 +43725,6 @@ parameters: count: 1 path: tests/lib/MVC/Symfony/SiteAccess/SiteAccessServiceTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\BaseRenderStrategyTest\\:\\:createRenderStrategy\\(\\) has parameter \\$fragmentRenderers with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\BaseRenderStrategyTest\\:\\:createRenderStrategy\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\MVC\\\\Templating\\\\RenderStrategy but returns object\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest.php - - - - message: "#^Method class@anonymous/tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest\\.php\\:51\\:\\:render\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest.php - - - - message: "#^Property class@anonymous/tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest\\.php\\:51\\:\\:\\$rendered \\(string\\) does not accept string\\|null\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest.php - - - - message: "#^Property class@anonymous/tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest\\.php\\:51\\:\\:\\$rendered \\(string\\) on left side of \\?\\? is not nullable\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelperTest\\:\\:testGetAvailableLanguages\\(\\) has no return type specified\\.$#" count: 1 @@ -45735,16 +44005,6 @@ parameters: count: 1 path: tests/lib/MVC/Symfony/Templating/Twig/Extension/RoutingExtensionTest.php - - - message: "#^Parameter \\#1 \\$code of method PhpParser\\\\Parser\\:\\:parse\\(\\) expects string, string\\|false given\\.$#" - count: 3 - path: tests/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitorTest.php - - - - message: "#^Parameter \\#3 \\$ast of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ExceptionMessageTemplateFileVisitor\\:\\:visitPhpFile\\(\\) expects array, array\\\\|null given\\.$#" - count: 3 - path: tests/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitorTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\NoTranslationToExtract\\:\\:addParameter\\(\\) has no return type specified\\.$#" count: 1 @@ -46065,66 +44325,6 @@ parameters: count: 1 path: tests/lib/Pagination/AdapterFactory/SearchHitAdapterFactoryTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\ContentSearchAdapterTest\\:\\:getAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Pagination/ContentSearchAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\ContentSearchAdapterTest\\:\\:getExpectedFinalResultFromHits\\(\\) has parameter \\$hits with no type specified\\.$#" - count: 1 - path: tests/lib/Pagination/ContentSearchAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\ContentSearchHitAdapterTest\\:\\:getAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Pagination/ContentSearchHitAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\ContentSearchHitAdapterTest\\:\\:getExpectedFinalResultFromHits\\(\\) has parameter \\$hits with no type specified\\.$#" - count: 1 - path: tests/lib/Pagination/ContentSearchHitAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\ContentSearchHitAdapterTest\\:\\:testGetNbResults\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Pagination/ContentSearchHitAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\ContentSearchHitAdapterTest\\:\\:testGetSlice\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Pagination/ContentSearchHitAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\LocationSearchAdapterTest\\:\\:getAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Pagination/LocationSearchAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\LocationSearchAdapterTest\\:\\:getExpectedFinalResultFromHits\\(\\) has parameter \\$hits with no type specified\\.$#" - count: 1 - path: tests/lib/Pagination/LocationSearchAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\LocationSearchHitAdapterTest\\:\\:getAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Pagination/LocationSearchHitAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\LocationSearchHitAdapterTest\\:\\:getExpectedFinalResultFromHits\\(\\) has parameter \\$hits with no type specified\\.$#" - count: 1 - path: tests/lib/Pagination/LocationSearchHitAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\LocationSearchHitAdapterTest\\:\\:testGetNbResults\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Pagination/LocationSearchHitAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\LocationSearchHitAdapterTest\\:\\:testGetSlice\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Pagination/LocationSearchHitAdapterTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractBaseHandlerTest\\:\\:getCacheItem\\(\\) has parameter \\$key with no type specified\\.$#" count: 1 @@ -54350,31 +52550,6 @@ parameters: count: 1 path: tests/lib/Repository/Helper/FieldTypeRegistryTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\AbstractSearchAdapterTest\\:\\:createAdapterUnderTest\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\ContentInfoSearchAdapterTest\\:\\:createAdapterUnderTest\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\ContentSearchAdapterTest\\:\\:createAdapterUnderTest\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\LocationSearchAdapterTest\\:\\:createAdapterUnderTest\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php - - - - message: "#^Parameter \\#2 \\$query of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\LocationSearchAdapter constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\LocationQuery, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query given\\.$#" - count: 1 - path: tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorTestAdapter\\:\\:__construct\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" count: 1 @@ -54815,16 +52990,6 @@ parameters: count: 3 path: tests/lib/Repository/Service/Mock/ContentTest.php - - - message: "#^Cannot access offset mixed on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\.$#" - count: 1 - path: tests/lib/Repository/Service/Mock/ContentTest.php - - - - message: "#^Cannot access offset string on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\.$#" - count: 1 - path: tests/lib/Repository/Service/Mock/ContentTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForCreateContentContentValidationException\\(\\) has no return type specified\\.$#" count: 1 @@ -56212,7 +54377,7 @@ parameters: - message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:expects\\(\\)\\.$#" - count: 8 + count: 7 path: tests/lib/Repository/Service/Mock/SearchTest.php - @@ -56300,11 +54465,6 @@ parameters: count: 1 path: tests/lib/Repository/Service/Mock/SearchTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindSingle\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Repository/Service/Mock/SearchTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindSingleThrowsHandlerException\\(\\) has no return type specified\\.$#" count: 1 @@ -58450,11 +56610,6 @@ parameters: count: 1 path: tests/lib/Search/Legacy/Content/AbstractTestCase.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" - count: 8 - path: tests/lib/Search/Legacy/Content/HandlerContentSortTest.php - - message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\SortClause\\\\Field constructor invoked with 4 parameters, 2\\-3 required\\.$#" count: 2 @@ -58515,11 +56670,6 @@ parameters: count: 1 path: tests/lib/Search/Legacy/Content/HandlerContentSortTest.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" - count: 2 - path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:getContentSearchHandler\\(\\) has parameter \\$fullTextSearchConfiguration with no value type specified in iterable type array\\.$#" count: 1 @@ -58860,11 +57010,6 @@ parameters: count: 1 path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" - count: 3 - path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\SortClause\\\\Field constructor invoked with 4 parameters, 2\\-3 required\\.$#" count: 2 @@ -58975,11 +57120,6 @@ parameters: count: 1 path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" - count: 1 - path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:getContentSearchHandler\\(\\) has parameter \\$fullTextSearchConfiguration with no value type specified in iterable type array\\.$#" count: 1 diff --git a/phpstan-baseline.pagerfanta.neon b/phpstan-baseline.pagerfanta.neon new file mode 100644 index 0000000000..d4b009bae5 --- /dev/null +++ b/phpstan-baseline.pagerfanta.neon @@ -0,0 +1,241 @@ +parameters: + ignoreErrors: + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderController\\:\\:getAdapter\\(\\) return type with generic interface Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\SearchResultAdapter does not specify its types\\: T$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/QueryRenderController.php + + - + message: "#^Parameter \\#1 \\$searchResult of class Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\FixedSearchResultHitAdapter constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\\\|Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\ given\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\FixedSearchResultHitAdapter\\:\\:getNbResults\\(\\) should return int\\<0, max\\> but returns int\\<\\-1, max\\>\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationFilteringAdapter\\:\\:getNbResults\\(\\) should return int\\<0, max\\> but returns int\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationFilteringAdapter\\:\\:\\$totalCount \\(int\\<0, max\\>\\|null\\) does not accept int\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ContentService\\:\\:count\\(\\) should return int\\<0, max\\> but returns int\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Parameter \\#1 \\$totalCount of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentList constructor expects int\\<0, max\\>, int given\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildContentDomainObjectsOnSearchResult\\(\\) has parameter \\$result with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildLocationDomainObjectsOnSearchResult\\(\\) has parameter \\$result with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\\\:\\:\\$totalCount \\(int\\<0, max\\>\\|null\\) does not accept int\\<\\-1, max\\>\\.$#" + count: 2 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:findContentInfo\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:findLocations\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:internalFindContentInfo\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\UsageSearchResult\\:\\:\\$totalCount \\(int\\) does not accept int\\<0, max\\>\\|null\\.$#" + count: 1 + path: src/lib/Repository/URLService.php + + - + message: "#^Parameter \\#1 \\$totalCount of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentList constructor expects int\\<0, max\\>, int given\\.$#" + count: 1 + path: tests/bundle/Core/Command/Indexer/ContentIdList/ContentTypeInputGeneratorStrategyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:assertSortResult\\(\\) has parameter \\$searchResult with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:findContent\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:findLocations\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:getResultContentIdList\\(\\) has parameter \\$searchResult with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:sortContent\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:sortLocations\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$totalCount of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentList constructor expects int\\<0, max\\>, int\\<0, max\\>\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/ContentFilteringTest.php + + - + message: "#^Parameter \\#1 \\$expectedCount of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertCount\\(\\) expects int, int\\<0, max\\>\\|null given\\.$#" + count: 2 + path: tests/integration/Core/Repository/Regression/EZP20018ObjectStateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\DeleteTranslationTest\\:\\:findContent\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/DeleteTranslationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\SortClause\\\\AbstractSortClauseTest\\:\\:assertSearchResultOrderByRemoteId\\(\\) has parameter \\$actualSearchResults with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/SortClause/AbstractSortClauseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceBookmarkTest\\:\\:extractRemoteIds\\(\\) has parameter \\$result with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceBookmarkTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:assertFulltextSearch\\(\\) has parameter \\$searchResult with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:mapSearchResultToIds\\(\\) has parameter \\$searchResult with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:mapResultLocationIds\\(\\) has parameter \\$result with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:printResult\\(\\) has parameter \\$result with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:simplifySearchResult\\(\\) has parameter \\$result with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\\\:\\:\\$valueObject \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\) does not accept array\\\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Cannot access property \\$valueObject on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Cannot access property \\$valueObject on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\\\|null\\.$#" + count: 2 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:assertSearchResultMatchTranslations\\(\\) has parameter \\$searchResult with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:find\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:mapResultContentIds\\(\\) has parameter \\$result with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:printResult\\(\\) has parameter \\$result with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:simplifySearchResult\\(\\) has parameter \\$result with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\\\:\\:\\$totalCount \\(int\\<0, max\\>\\|null\\) does not accept int\\<\\-1, max\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\\\:\\:\\$totalCount \\(int\\<0, max\\>\\|null\\) does not accept int\\<\\-2, max\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Parameter \\#2 \\$searchHit of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\\\:\\:assertIndexName\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\ given\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Parameter \\#2 \\$searchHit of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\\\:\\:assertIndexName\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\ given\\.$#" + count: 3 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Type mixed in generic type Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\ in PHPDoc tag @param for parameter \\$searchHit is not subtype of template type T of Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Template type TItem of method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderControllerTest\\:\\:configureMocks\\(\\) is not referenced in a parameter\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php + + - + message: "#^Parameter \\#1 \\$totalCount of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentList constructor expects int\\<0, max\\>, int given\\.$#" + count: 1 + path: tests/lib/Pagination/ContentFilteringAdapterTest.php + + - + message: "#^Parameter \\#3 \\$languageFilter of class Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\ContentFilteringAdapter constructor expects array\\\\|null, array\\\\|true\\> given\\.$#" + count: 2 + path: tests/lib/Pagination/ContentFilteringAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\FixedSearchResultHitAdapterTest\\:\\:createExampleSearchResult\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/lib/Pagination/FixedSearchResultHitAdapterTest.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 8c4caf4f90..8c09fcf747 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -2,6 +2,7 @@ includes: - vendor/phpstan/phpstan-phpunit/extension.neon - vendor/phpstan/phpstan-symfony/extension.neon - phpstan-baseline.neon + - phpstan-baseline.pagerfanta.neon parameters: level: 8 diff --git a/phpunit.xml b/phpunit.xml index 8776b68e66..b186f33fd5 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -10,7 +10,7 @@ > - + diff --git a/src/bundle/Core/ApiLoader/StorageConnectionFactory.php b/src/bundle/Core/ApiLoader/StorageConnectionFactory.php index a7a83db22b..5340ffd1cf 100644 --- a/src/bundle/Core/ApiLoader/StorageConnectionFactory.php +++ b/src/bundle/Core/ApiLoader/StorageConnectionFactory.php @@ -17,6 +17,9 @@ */ final readonly class StorageConnectionFactory { + /** + * @phpstan-param \Symfony\Contracts\Service\ServiceProviderInterface<\Doctrine\DBAL\Connection> $serviceLocator + */ public function __construct( private RepositoryConfigurationProviderInterface $repositoryConfigurationProvider, private ServiceProviderInterface $serviceLocator, @@ -28,8 +31,6 @@ public function __construct( * * @throws \InvalidArgumentException * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException - * @throws \Psr\Container\ContainerExceptionInterface - * @throws \Psr\Container\NotFoundExceptionInterface */ public function getConnection(): Connection { diff --git a/src/bundle/Core/Command/CopySubtreeCommand.php b/src/bundle/Core/Command/CopySubtreeCommand.php index a8270b1d55..d5728566c8 100644 --- a/src/bundle/Core/Command/CopySubtreeCommand.php +++ b/src/bundle/Core/Command/CopySubtreeCommand.php @@ -32,20 +32,15 @@ class CopySubtreeCommand extends Command protected static $defaultDescription = 'Copies a subtree from one Location to another'; - /** @var \Ibexa\Contracts\Core\Repository\LocationService */ - private $locationService; + private LocationService $locationService; - /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ - private $permissionResolver; + private PermissionResolver $permissionResolver; - /** @var \Ibexa\Contracts\Core\Repository\UserService */ - private $userService; + private UserService $userService; - /** @var \Ibexa\Contracts\Core\Repository\ContentTypeService */ - private $contentTypeService; + private ContentTypeService $contentTypeService; - /** @var \Ibexa\Contracts\Core\Repository\SearchService */ - private $searchService; + private SearchService $searchService; /** * @param \Ibexa\Contracts\Core\Repository\LocationService $locationService @@ -69,7 +64,7 @@ public function __construct( $this->searchService = $searchService; } - protected function configure() + protected function configure(): void { $this ->addArgument( @@ -95,9 +90,10 @@ protected function configure() * @param \Symfony\Component\Console\Input\InputInterface $input * @param \Symfony\Component\Console\Output\OutputInterface $output * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ - protected function initialize(InputInterface $input, OutputInterface $output) + protected function initialize(InputInterface $input, OutputInterface $output): void { parent::initialize($input, $output); $this->permissionResolver->setCurrentUserReference( @@ -106,11 +102,6 @@ protected function initialize(InputInterface $input, OutputInterface $output) } /** - * @param \Symfony\Component\Console\Input\InputInterface $input - * @param \Symfony\Component\Console\Output\OutputInterface $output - * - * @return int|null - * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException @@ -144,9 +135,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int $questionHelper = $this->getHelper('question'); $question = new ConfirmationQuestion( sprintf( - 'Are you sure you want to copy `%s` subtree (no. of children: %d) into `%s`? This may take a while for a big number of nested children [Y/n]? ', + 'Are you sure you want to copy `%s` subtree (no. of children: %s) into `%s`? This may take a while for a big number of nested children [Y/n]? ', $sourceLocation->contentInfo->name, - $this->getAllChildrenCount($sourceLocation), + $this->getAllChildrenCountExpr($sourceLocation), $targetLocation->contentInfo->name ) ); @@ -168,20 +159,16 @@ protected function execute(InputInterface $input, OutputInterface $output): int } /** - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location - * - * @return int - * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ - protected function getAllChildrenCount(Location $location): int + protected function getAllChildrenCountExpr(Location $location): string { $query = new LocationQuery([ 'filter' => new Criterion\Subtree($location->pathString), ]); - $searchResults = $this->searchService->findLocations($query); + $totalCount = $this->searchService->findLocations($query)->totalCount; - return $searchResults->totalCount; + return $totalCount !== null ? (string) $totalCount : '~'; } } diff --git a/src/bundle/Core/Command/ResizeOriginalImagesCommand.php b/src/bundle/Core/Command/ResizeOriginalImagesCommand.php index 649beac29c..4c219a26ac 100644 --- a/src/bundle/Core/Command/ResizeOriginalImagesCommand.php +++ b/src/bundle/Core/Command/ResizeOriginalImagesCommand.php @@ -210,7 +210,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int while ($query->offset <= $totalCount) { $results = $this->searchService->findContent($query); - /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit $hit */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit<\Ibexa\Contracts\Core\Repository\Values\Content\Content> $hit */ foreach ($results->searchHits as $hit) { $this->resize($output, $hit, $imageFieldIdentifier, $filter); $progressBar->advance(); @@ -232,10 +232,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } /** - * @param \Symfony\Component\Console\Output\OutputInterface $output - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit $hit - * @param string $imageFieldIdentifier - * @param string $filter + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit<\Ibexa\Contracts\Core\Repository\Values\Content\Content> $hit */ private function resize(OutputInterface $output, SearchHit $hit, string $imageFieldIdentifier, string $filter): void { diff --git a/src/bundle/Core/DependencyInjection/Compiler/FragmentPass.php b/src/bundle/Core/DependencyInjection/Compiler/FragmentPass.php index 7c52a81830..2ddea81531 100644 --- a/src/bundle/Core/DependencyInjection/Compiler/FragmentPass.php +++ b/src/bundle/Core/DependencyInjection/Compiler/FragmentPass.php @@ -22,7 +22,7 @@ */ class FragmentPass implements CompilerPassInterface { - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if ( !( @@ -30,7 +30,7 @@ public function process(ContainerBuilder $container) && $container->hasDefinition(DecoratedFragmentRenderer::class) ) ) { - return null; + return; } $fragmentListenerDef = $container->findDefinition('fragment.listener'); diff --git a/src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php b/src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php index 94d187a18a..9044a3cde7 100644 --- a/src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php +++ b/src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php @@ -27,7 +27,7 @@ public function process(ContainerBuilder $container): void $mappingDriverConfig = $this->prepareMappingDriverConfig($entityMappings, $container); foreach ($entityManagers as $entityManagerName => $serviceName) { - if (strpos($entityManagerName, 'ibexa_') !== 0) { + if (!str_starts_with($entityManagerName, 'ibexa_')) { continue; } @@ -46,10 +46,8 @@ public function process(ContainerBuilder $container): void $metadataDriverServiceName = "doctrine.orm.{$entityManagerName}_{$driverType}_metadata_driver"; $metadataDriverDefinition = $this->createMetadataDriverDefinition($driverType, $driverPaths); - if ( - false !== strpos($metadataDriverDefinition->getClass(), 'yml') - || false !== strpos($metadataDriverDefinition->getClass(), 'xml') - ) { + $class = $metadataDriverDefinition->getClass(); + if (null !== $class && (str_contains($class, 'yml') || str_contains($class, 'xml'))) { $metadataDriverDefinition->setArguments([array_flip($driverPaths)]); $metadataDriverDefinition->addMethodCall('setGlobalBasename', ['mapping']); } diff --git a/src/bundle/Core/DependencyInjection/Configuration/Parser/Common.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/Common.php index 13e4695be9..39466ee876 100644 --- a/src/bundle/Core/DependencyInjection/Configuration/Parser/Common.php +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/Common.php @@ -21,7 +21,7 @@ class Common extends AbstractParser * * @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $nodeBuilder Node just under ibexa.system. */ - public function addSemanticConfig(NodeBuilder $nodeBuilder) + public function addSemanticConfig(NodeBuilder $nodeBuilder): void { $nodeBuilder ->scalarNode('repository')->info('The repository to use. Choose among ibexa.repositories.')->end() diff --git a/src/bundle/Core/DependencyInjection/Configuration/Parser/IO.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/IO.php index 7a02a2cdb1..8c1c422b48 100644 --- a/src/bundle/Core/DependencyInjection/Configuration/Parser/IO.php +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/IO.php @@ -45,10 +45,10 @@ public function addSemanticConfig(NodeBuilder $nodeBuilder) ->info('Permissions applied by the Local flysystem adapter when creating content files and directories in storage.') ->children() ->scalarNode('files') - ->defaultValue('0644') + ->defaultValue(0644) ->end() ->scalarNode('directories') - ->defaultValue('0755') + ->defaultValue(0755) ->end() ->end() ->end() diff --git a/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php b/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php index 2a99e00011..9a152a16c1 100644 --- a/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php +++ b/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php @@ -40,8 +40,8 @@ class IbexaCoreExtension extends Extension implements PrependExtensionInterface { - public const EXTENSION_NAME = 'ibexa'; - private const ENTITY_MANAGER_TEMPLATE = [ + public const string EXTENSION_NAME = 'ibexa'; + private const array ENTITY_MANAGER_TEMPLATE = [ 'connection' => null, 'mappings' => [], ]; @@ -97,11 +97,12 @@ public function getAlias(): string * @param mixed[] $configs An array of configuration values * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container A ContainerBuilder instance * + * @throws \Exception * @throws \InvalidArgumentException When provided tag is not defined in this extension * * @api */ - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $loader = new Loader\YamlFileLoader( $container, @@ -183,10 +184,7 @@ public function getConfiguration(array $config, ContainerBuilder $container): Co return $configuration; } - /** - * {@inheritdoc} - */ - public function prepend(ContainerBuilder $container) + public function prepend(ContainerBuilder $container): void { $this->prependTranslatorConfiguration($container); $this->prependDoctrineConfiguration($container); diff --git a/src/bundle/Core/Entity/EntityManagerFactory.php b/src/bundle/Core/Entity/EntityManagerFactory.php index 205cd9c895..1ee90755df 100644 --- a/src/bundle/Core/Entity/EntityManagerFactory.php +++ b/src/bundle/Core/Entity/EntityManagerFactory.php @@ -17,15 +17,18 @@ */ class EntityManagerFactory { - /** @var \Symfony\Component\DependencyInjection\ServiceLocator */ - private $serviceLocator; + /** @phpstan-var \Symfony\Component\DependencyInjection\ServiceLocator<\Doctrine\ORM\EntityManagerInterface> */ + private ServiceLocator $serviceLocator; /** @var string */ - private $defaultConnection; + private string $defaultConnection; /** @var array */ - private $entityManagers; + private array $entityManagers; + /** + * @phpstan-param \Symfony\Component\DependencyInjection\ServiceLocator<\Doctrine\ORM\EntityManagerInterface> $serviceLocator + */ public function __construct( private readonly RepositoryConfigurationProviderInterface $repositoryConfigurationProvider, ServiceLocator $serviceLocator, diff --git a/src/bundle/Core/Features/Context/UserContext.php b/src/bundle/Core/Features/Context/UserContext.php index 3d2bc860a6..7146119724 100644 --- a/src/bundle/Core/Features/Context/UserContext.php +++ b/src/bundle/Core/Features/Context/UserContext.php @@ -82,26 +82,27 @@ public function searchUserByLogin($username, $parentGroupId = null) * Search User Groups with given name. * * @param string $name name of User Group to search for - * @param string $parentLocationId (optional) parent location id to search in + * @param int|null $parentLocationId (optional) parent location id to search in * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit[] search results + * @phpstan-return list<\Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit<\Ibexa\Contracts\Core\Repository\Values\Content\Content>> + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ - public function searchUserGroups($name, $parentLocationId = null) + public function searchUserGroups(string $name, ?int $parentLocationId = null): array { $criterionArray = [ new Criterion\Subtree(self::USERGROUP_ROOT_SUBTREE), new Criterion\ContentTypeIdentifier(self::USERGROUP_CONTENT_IDENTIFIER), new Criterion\Field('name', Criterion\Operator::EQ, $name), ]; - if ($parentLocationId) { + if (null !== $parentLocationId) { $criterionArray[] = new Criterion\ParentLocationId($parentLocationId); } $query = new Query(); $query->filter = new Criterion\LogicalAnd($criterionArray); - $result = $this->searchService->findContent($query, [], false); - - return $result->searchHits; + return $this->searchService->findContent($query, [], false)->searchHits; } /** diff --git a/src/bundle/Core/Fragment/DecoratedFragmentRenderer.php b/src/bundle/Core/Fragment/DecoratedFragmentRenderer.php index 2b0155a88f..846f4e7630 100644 --- a/src/bundle/Core/Fragment/DecoratedFragmentRenderer.php +++ b/src/bundle/Core/Fragment/DecoratedFragmentRenderer.php @@ -10,17 +10,16 @@ use Ibexa\Core\MVC\Symfony\SiteAccess; use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Controller\ControllerReference; use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; use Symfony\Component\HttpKernel\Fragment\RoutableFragmentRenderer; class DecoratedFragmentRenderer implements FragmentRendererInterface, SiteAccessAware { - /** @var \Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface */ - private $innerRenderer; + private FragmentRendererInterface $innerRenderer; - /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ - private $siteAccess; + private ?SiteAccess $siteAccess; private SiteAccessSerializerInterface $siteAccessSerializer; @@ -32,22 +31,20 @@ public function __construct( $this->siteAccessSerializer = $siteAccessSerializer; } - /** - * @param \Ibexa\Core\MVC\Symfony\SiteAccess|null $siteAccess - */ - public function setSiteAccess(SiteAccess $siteAccess = null) + public function setSiteAccess(?SiteAccess $siteAccess = null): void { $this->siteAccess = $siteAccess; } - public function setFragmentPath($path) + public function setFragmentPath(string $path): void { if (!$this->innerRenderer instanceof RoutableFragmentRenderer) { - return null; + return; } - if ($this->siteAccess && $this->siteAccess->matcher instanceof SiteAccess\URILexer) { - $path = $this->siteAccess->matcher->analyseLink($path); + $matcher = $this->siteAccess?->matcher; + if ($matcher instanceof SiteAccess\URILexer) { + $path = $matcher->analyseLink($path); } $this->innerRenderer->setFragmentPath($path); @@ -56,13 +53,9 @@ public function setFragmentPath($path) /** * Renders a URI and returns the Response content. * - * @param string|\Symfony\Component\HttpKernel\Controller\ControllerReference $uri A URI as a string or a ControllerReference instance - * @param \Symfony\Component\HttpFoundation\Request $request A Request instance - * @param array $options An array of options - * - * @return \Symfony\Component\HttpFoundation\Response A Response instance + * @param array $options An array of options */ - public function render($uri, Request $request, array $options = []) + public function render(string|ControllerReference $uri, Request $request, array $options = []): Response { if ($uri instanceof ControllerReference && $request->attributes->has('siteaccess')) { // Serialize a SiteAccess to get it back after. @@ -76,10 +69,8 @@ public function render($uri, Request $request, array $options = []) /** * Gets the name of the strategy. - * - * @return string The strategy name */ - public function getName() + public function getName(): string { return $this->innerRenderer->getName(); } diff --git a/src/bundle/Core/Fragment/InlineFragmentRenderer.php b/src/bundle/Core/Fragment/InlineFragmentRenderer.php index 22c9994492..e9a44652fa 100644 --- a/src/bundle/Core/Fragment/InlineFragmentRenderer.php +++ b/src/bundle/Core/Fragment/InlineFragmentRenderer.php @@ -10,6 +10,7 @@ use Ibexa\Core\MVC\Symfony\SiteAccess; use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Controller\ControllerReference; use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; use Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer as BaseRenderer; @@ -17,11 +18,9 @@ class InlineFragmentRenderer extends BaseRenderer implements SiteAccessAware { - /** @var \Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface */ - private $innerRenderer; + private FragmentRendererInterface $innerRenderer; - /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ - private $siteAccess; + private SiteAccess $siteAccess; private SiteAccessSerializerInterface $siteAccessSerializer; @@ -33,19 +32,22 @@ public function __construct( $this->siteAccessSerializer = $siteAccessSerializer; } - public function setFragmentPath($path) + public function setFragmentPath($path): void { if ($this->innerRenderer instanceof RoutableFragmentRenderer) { $this->innerRenderer->setFragmentPath($path); } } - public function setSiteAccess(SiteAccess $siteAccess = null) + public function setSiteAccess(SiteAccess $siteAccess = null): void { $this->siteAccess = $siteAccess; } - public function render($uri, Request $request, array $options = []) + /** + * @param array $options + */ + public function render(string|ControllerReference $uri, Request $request, array $options = []): Response { if ($uri instanceof ControllerReference) { if ($request->attributes->has('siteaccess')) { @@ -64,7 +66,7 @@ public function render($uri, Request $request, array $options = []) return $this->innerRenderer->render($uri, $request, $options); } - public function getName() + public function getName(): string { return $this->innerRenderer->getName(); } diff --git a/src/bundle/Core/Resources/config/default_settings.yml b/src/bundle/Core/Resources/config/default_settings.yml index e3ff4307e6..ed8d74c620 100644 --- a/src/bundle/Core/Resources/config/default_settings.yml +++ b/src/bundle/Core/Resources/config/default_settings.yml @@ -104,8 +104,8 @@ parameters: ibexa.site_access.config.default.io.url_prefix: "$var_dir$/$storage_dir$" ibexa.site_access.config.default.io.legacy_url_prefix: "$var_dir$/$storage_dir$" ibexa.site_access.config.default.io.root_dir: "%webroot_dir%/$var_dir$/$storage_dir$" - ibexa.site_access.config.default.io.permissions.files: 0644 - ibexa.site_access.config.default.io.permissions.directories: 0755 + ibexa.site_access.config.default.io.permissions.files: 0o644 + ibexa.site_access.config.default.io.permissions.directories: 0o755 # Blacklist against storing certain file types, validation will be refused for these ibexa.site_access.config.default.io.file_storage.file_type_blacklist: - php diff --git a/src/bundle/Core/Routing/DefaultRouter.php b/src/bundle/Core/Routing/DefaultRouter.php index 936e5a3688..b8999d2460 100644 --- a/src/bundle/Core/Routing/DefaultRouter.php +++ b/src/bundle/Core/Routing/DefaultRouter.php @@ -8,6 +8,7 @@ namespace Ibexa\Bundle\Core\Routing; use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Routing\RequestContextFactory; use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; use Ibexa\Core\MVC\Symfony\SiteAccess; use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware; @@ -16,30 +17,28 @@ use Symfony\Bundle\FrameworkBundle\Routing\Router; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Exception\RouteNotFoundException; -use Symfony\Component\Routing\Matcher\RequestMatcherInterface; +use Symfony\Component\Routing\RequestContext; /** * Extension of Symfony default router implementing RequestMatcherInterface. */ -class DefaultRouter extends Router implements RequestMatcherInterface, SiteAccessAware +class DefaultRouter extends Router implements SiteAccessAware { - /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ - protected $siteAccess; + protected ?SiteAccess $siteAccess = null; - protected $nonSiteAccessAwareRoutes = []; + /** @var string[] */ + protected array $nonSiteAccessAwareRoutes = []; - /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ - protected $configResolver; + protected ConfigResolverInterface $configResolver; - /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface */ - protected $siteAccessRouter; + protected SiteAccessRouterInterface $siteAccessRouter; - public function setConfigResolver(ConfigResolverInterface $configResolver) + public function setConfigResolver(ConfigResolverInterface $configResolver): void { $this->configResolver = $configResolver; } - public function setSiteAccess(SiteAccess $siteAccess = null) + public function setSiteAccess(?SiteAccess $siteAccess = null): void { $this->siteAccess = $siteAccess; } @@ -48,22 +47,22 @@ public function setSiteAccess(SiteAccess $siteAccess = null) * Injects route names that are not supposed to be SiteAccess aware. * i.e. Routes pointing to asset generation (like assetic). * - * @param array $routes + * @param string[] $routes */ - public function setNonSiteAccessAwareRoutes(array $routes) + public function setNonSiteAccessAwareRoutes(array $routes): void { $this->nonSiteAccessAwareRoutes = $routes; } - /** - * @param \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface $siteAccessRouter - */ - public function setSiteAccessRouter(SiteAccessRouterInterface $siteAccessRouter) + public function setSiteAccessRouter(SiteAccessRouterInterface $siteAccessRouter): void { $this->siteAccessRouter = $siteAccessRouter; } - public function matchRequest(Request $request) + /** + * @return array An array of parameters + */ + public function matchRequest(Request $request): array { if ($request->attributes->has('semanticPathinfo')) { $request = $request->duplicate(); @@ -76,7 +75,10 @@ public function matchRequest(Request $request) return parent::matchRequest($request); } - public function generate($name, $parameters = [], $referenceType = self::ABSOLUTE_PATH) + /** + * @param array $parameters + */ + public function generate(string $name, array $parameters = [], int $referenceType = self::ABSOLUTE_PATH): string { $siteAccess = $this->siteAccess; $originalContext = $context = $this->getContext(); @@ -110,9 +112,9 @@ public function generate($name, $parameters = [], $referenceType = self::ABSOLUT if ($referenceType === self::ABSOLUTE_URL || $referenceType === self::NETWORK_PATH) { $scheme = $context->getScheme(); $port = ''; - if ($scheme === 'http' && $this->context->getHttpPort() != 80) { + if ($scheme === 'http' && $this->context->getHttpPort() !== 80) { $port = ':' . $this->context->getHttpPort(); - } elseif ($scheme === 'https' && $this->context->getHttpsPort() != 443) { + } elseif ($scheme === 'https' && $this->context->getHttpsPort() !== 443) { $port = ':' . $this->context->getHttpsPort(); } @@ -134,15 +136,11 @@ public function generate($name, $parameters = [], $referenceType = self::ABSOLUT /** * Checks if $routeName is a siteAccess aware route, and thus needs to have siteAccess URI prepended. * Will be used for link generation, only in the case of URI SiteAccess matching. - * - * @param $routeName - * - * @return bool */ - protected function isSiteAccessAwareRoute($routeName): bool + protected function isSiteAccessAwareRoute(string $routeName): bool { foreach ($this->nonSiteAccessAwareRoutes as $ignoredPrefix) { - if (strpos($routeName, $ignoredPrefix) === 0) { + if (str_starts_with($routeName, $ignoredPrefix)) { return false; } } @@ -152,37 +150,10 @@ protected function isSiteAccessAwareRoute($routeName): bool /** * Merges context from $simplifiedRequest into a clone of the current context. - * - * @param \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest $simplifiedRequest - * - * @return \Symfony\Component\Routing\RequestContext */ - public function getContextBySimplifiedRequest(SimplifiedRequest $simplifiedRequest) + public function getContextBySimplifiedRequest(SimplifiedRequest $simplifiedRequest): RequestContext { - $context = clone $this->context; - if ($simplifiedRequest->getScheme()) { - $context->setScheme($simplifiedRequest->getScheme()); - } - - if ($simplifiedRequest->getPort()) { - switch ($simplifiedRequest->getScheme()) { - case 'https': - $context->setHttpsPort($simplifiedRequest->getPort()); - break; - default: - $context->setHttpPort($simplifiedRequest->getPort()); - break; - } - } - - if ($simplifiedRequest->getHost()) { - $context->setHost($simplifiedRequest->getHost()); - } - - if ($simplifiedRequest->getPathInfo()) { - $context->setPathInfo($simplifiedRequest->getPathInfo()); - } - - return $context; + // inline-instantiated on purpose as it's lightweight and injecting it here through DI can be complicated + return (new RequestContextFactory($this->context))->getContextBySimplifiedRequest($simplifiedRequest); } } diff --git a/src/bundle/Core/Routing/JsRouting/ExposedRoutesExtractor.php b/src/bundle/Core/Routing/JsRouting/ExposedRoutesExtractor.php index 64d01dfcbd..db4efec3d7 100644 --- a/src/bundle/Core/Routing/JsRouting/ExposedRoutesExtractor.php +++ b/src/bundle/Core/Routing/JsRouting/ExposedRoutesExtractor.php @@ -21,11 +21,9 @@ */ class ExposedRoutesExtractor implements ExposedRoutesExtractorInterface { - /** @var \FOS\JsRoutingBundle\Extractor\ExposedRoutesExtractorInterface */ - private $innerExtractor; + private ExposedRoutesExtractorInterface $innerExtractor; - /** @var \Symfony\Component\HttpFoundation\RequestStack */ - private $requestStack; + private RequestStack $requestStack; public function __construct(ExposedRoutesExtractorInterface $innerExtractor, RequestStack $requestStack) { @@ -39,11 +37,7 @@ public function getRoutes(): RouteCollection } /** - * {@inheritdoc} - * * Will add the SiteAccess if configured in the URI. - * - * @return string */ public function getBaseUrl(): string { @@ -61,7 +55,7 @@ public function getBaseUrl(): string return $baseUrl; } - public function getPrefix($locale): string + public function getPrefix(string $locale): string { return $this->innerExtractor->getPrefix($locale); } @@ -88,10 +82,10 @@ public function getResources(): array public function getPort(): string { - return $this->innerExtractor->getPort(); + return $this->innerExtractor->getPort() ?? ''; } - public function isRouteExposed(Route $route, $name): bool + public function isRouteExposed(Route $route, string $name): bool { return $this->innerExtractor->isRouteExposed($route, $name); } diff --git a/src/bundle/Core/Routing/UrlAliasRouter.php b/src/bundle/Core/Routing/UrlAliasRouter.php index aadfcea15d..f3b688bc3d 100644 --- a/src/bundle/Core/Routing/UrlAliasRouter.php +++ b/src/bundle/Core/Routing/UrlAliasRouter.php @@ -7,6 +7,7 @@ namespace Ibexa\Bundle\Core\Routing; +use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use Ibexa\Core\MVC\Symfony\Routing\UrlAliasRouter as BaseUrlAliasRouter; use Symfony\Component\HttpFoundation\Request; @@ -14,15 +15,19 @@ class UrlAliasRouter extends BaseUrlAliasRouter { - /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ - protected $configResolver; + protected ConfigResolverInterface $configResolver; - public function setConfigResolver(ConfigResolverInterface $configResolver) + public function setConfigResolver(ConfigResolverInterface $configResolver): void { $this->configResolver = $configResolver; } - public function matchRequest(Request $request) + /** + * @return array an array of parameters + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function matchRequest(Request $request): array { // UrlAliasRouter might be disabled from configuration. // An example is for running the admin interface: it needs to be entirely run through the legacy kernel. @@ -34,26 +39,25 @@ public function matchRequest(Request $request) } /** - * Will return the right UrlAlias in regards to configured root location. - * - * @param string $pathinfo - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the path does not exist or is not valid for the given language + * Will return the right UrlAlias with respect to configured root location. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ - protected function getUrlAlias($pathinfo) + protected function getUrlAlias(string $pathInfo): URLAlias { - $pathPrefix = $this->generator->getPathPrefixByRootLocationId($this->rootLocationId); + $pathPrefix = $this->rootLocationId !== null + ? $this->generator->getPathPrefixByRootLocationId($this->rootLocationId) + : '/'; if ( $this->rootLocationId === null || - $this->generator->isUriPrefixExcluded($pathinfo) || - $pathPrefix === '/' + $pathPrefix === '/' || + $this->generator->isUriPrefixExcluded($pathInfo) ) { - return parent::getUrlAlias($pathinfo); + return parent::getUrlAlias($pathInfo); } - return $this->urlAliasService->lookup($pathPrefix . $pathinfo); + return $this->urlAliasService->lookup($pathPrefix . $pathInfo); } } diff --git a/src/bundle/Debug/IbexaDebugBundle.php b/src/bundle/Debug/IbexaDebugBundle.php index 5b93da4ff2..60b1f834b6 100644 --- a/src/bundle/Debug/IbexaDebugBundle.php +++ b/src/bundle/Debug/IbexaDebugBundle.php @@ -13,7 +13,7 @@ class IbexaDebugBundle extends Bundle { - public function build(ContainerBuilder $container) + public function build(ContainerBuilder $container): void { parent::build($container); $container->addCompilerPass(new DataCollectorPass()); diff --git a/src/bundle/IO/BinaryStreamResponse.php b/src/bundle/IO/BinaryStreamResponse.php index cf1fc82bbe..2b0725435c 100644 --- a/src/bundle/IO/BinaryStreamResponse.php +++ b/src/bundle/IO/BinaryStreamResponse.php @@ -7,11 +7,9 @@ namespace Ibexa\Bundle\IO; -use DateTime; use Ibexa\Core\IO\IOServiceInterface; use Ibexa\Core\IO\Values\BinaryFile; use LogicException; -use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -20,32 +18,31 @@ */ class BinaryStreamResponse extends Response { - protected static $trustXSendfileTypeHeader = false; + protected BinaryFile $file; - /** @var \Ibexa\Core\IO\Values\BinaryFile */ - protected $file; + protected IOServiceInterface $ioService; - /** @var \Ibexa\Core\IO\IOServiceInterface */ - protected $ioService; + protected int $offset; - protected $offset; - - protected $maxlen; + protected int $maxlen; /** - * Constructor. + * @param array> $headers An array of response headers + * @param bool $public Files are public by default + * + * @phpstan-param \Symfony\Component\HttpFoundation\ResponseHeaderBag::DISPOSITION_*|null $contentDisposition The type of Content-Disposition to set automatically with the filename * - * @param \Ibexa\Core\IO\Values\BinaryFile $binaryFile The name of the file to stream - * @param \Ibexa\Core\IO\IOServiceInterface $ioService The name of the file to stream - * @param int $status The response status code - * @param array $headers An array of response headers - * @param bool $public Files are public by default - * @param string|null $contentDisposition The type of Content-Disposition to set automatically with the filename - * @param bool $autoEtag Whether the ETag header should be automatically set - * @param bool $autoLastModified Whether the Last-Modified header should be automatically set + * @param bool $autoLastModified Whether the Last-Modified header should be automatically set */ - public function __construct(BinaryFile $binaryFile, IOServiceInterface $ioService, $status = 200, $headers = [], $public = true, $contentDisposition = null, $autoLastModified = true) - { + public function __construct( + BinaryFile $binaryFile, + IOServiceInterface $ioService, + int $status = 200, + array $headers = [], + bool $public = true, + ?string $contentDisposition = null, + bool $autoLastModified = true + ) { $this->ioService = $ioService; parent::__construct(null, $status, $headers); @@ -58,16 +55,11 @@ public function __construct(BinaryFile $binaryFile, IOServiceInterface $ioServic } /** - * Sets the file to stream. + * @phpstan-param \Symfony\Component\HttpFoundation\ResponseHeaderBag::DISPOSITION_*|null $contentDisposition * - * @param \SplFileInfo|string $file The file to stream - * @param string $contentDisposition - * @param bool $autoEtag - * @param bool $autoLastModified - * - * @return \Symfony\Component\HttpFoundation\BinaryFileResponse + * @return $this */ - public function setFile($file, $contentDisposition = null, $autoLastModified = true) + public function setFile(BinaryFile $file, ?string $contentDisposition = null, bool $autoLastModified = true): static { $this->file = $file; @@ -75,29 +67,26 @@ public function setFile($file, $contentDisposition = null, $autoLastModified = t $this->setAutoLastModified(); } - if ($contentDisposition) { + if (!empty($contentDisposition)) { $this->setContentDisposition($contentDisposition); } return $this; } - /** - * Gets the file. - * - * @return \Ibexa\Core\IO\Values\BinaryFile The file to stream - */ - public function getFile() + public function getFile(): BinaryFile { return $this->file; } /** * Automatically sets the Last-Modified header according the file modification date. + * + * @return $this */ - public function setAutoLastModified() + public function setAutoLastModified(): static { - $this->setLastModified(DateTime::createFromFormat('U', $this->file->mtime->getTimestamp())); + $this->setLastModified($this->file->mtime); return $this; } @@ -105,13 +94,14 @@ public function setAutoLastModified() /** * Sets the Content-Disposition header with the given filename. * - * @param string $disposition ResponseHeaderBag::DISPOSITION_INLINE or ResponseHeaderBag::DISPOSITION_ATTACHMENT + * @phpstan-param \Symfony\Component\HttpFoundation\ResponseHeaderBag::DISPOSITION_* $disposition + * * @param string $filename Optionally use this filename instead of the real name of the file * @param string $filenameFallback A fallback filename, containing only ASCII characters. Defaults to an automatically encoded filename * - * @return BinaryStreamResponse + * @return $this */ - public function setContentDisposition($disposition, $filename = '', $filenameFallback = '') + public function setContentDisposition(string $disposition, string $filename = '', string $filenameFallback = ''): BinaryStreamResponse { if ($filename === '') { $filename = $this->file->id; @@ -128,11 +118,11 @@ public function setContentDisposition($disposition, $filename = '', $filenameFal } /** - * {@inheritdoc} + * @return $this */ - public function prepare(Request $request) + public function prepare(Request $request): static { - $this->headers->set('Content-Length', $this->file->size); + $this->headers->set('Content-Length', (string)$this->file->size); $this->headers->set('Accept-Ranges', 'bytes'); $this->headers->set('Content-Transfer-Encoding', 'binary'); @@ -143,7 +133,7 @@ public function prepare(Request $request) ); } - if ('HTTP/1.0' != $request->server->get('SERVER_PROTOCOL')) { + if ('HTTP/1.0' !== $request->server->get('SERVER_PROTOCOL')) { $this->setProtocolVersion('1.1'); } @@ -152,36 +142,8 @@ public function prepare(Request $request) $this->offset = 0; $this->maxlen = -1; - if ($request->headers->has('Range')) { - // Process the range headers. - if (!$request->headers->has('If-Range') || $this->getEtag() == $request->headers->get('If-Range')) { - $range = $request->headers->get('Range'); - $fileSize = $this->file->size; - - list($start, $end) = explode('-', substr($range, 6), 2) + [0]; - - $end = ('' === $end) ? $fileSize - 1 : (int)$end; - - if ('' === $start) { - $start = $fileSize - $end; - $end = $fileSize - 1; - } else { - $start = (int)$start; - } - - if ($start <= $end) { - if ($start < 0 || $end > $fileSize - 1) { - $this->setStatusCode(Response::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE); // HTTP_REQUESTED_RANGE_NOT_SATISFIABLE - } elseif ($start !== 0 || $end !== $fileSize - 1) { - $this->maxlen = $end < $fileSize ? $end - $start + 1 : -1; - $this->offset = $start; - - $this->setStatusCode(Response::HTTP_PARTIAL_CONTENT); // HTTP_PARTIAL_CONTENT - $this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize)); - $this->headers->set('Content-Length', $end - $start + 1); - } - } - } + if ($this->isRangeRequest($request)) { + $this->processRangeRequest($request); } return $this; @@ -189,40 +151,93 @@ public function prepare(Request $request) /** * Sends the file. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @return $this */ - public function sendContent() + public function sendContent(): static { if (!$this->isSuccessful()) { parent::sendContent(); - return; + return $this; } - if (0 === $this->maxlen) { - return; - } + if ($this->maxlen > 0) { + $out = fopen('php://output', 'wb'); + if ($out === false) { + throw new LogicException('Failed to create binary output stream'); + } - $out = fopen('php://output', 'wb'); - $in = $this->ioService->getFileInputStream($this->file); - stream_copy_to_stream($in, $out, $this->maxlen, $this->offset); + $in = $this->ioService->getFileInputStream($this->file); + stream_copy_to_stream($in, $out, $this->maxlen, $this->offset); - fclose($out); + fclose($out); + } + + return $this; } /** - * {@inheritdoc} - * * @throws \LogicException when the content is not null + * + * @return $this */ - public function setContent($content) + public function setContent(?string $content): static { if (null !== $content) { throw new LogicException('The content cannot be set on a BinaryStreamResponse instance.'); } + + return $this; + } + + public function getContent(): false + { + return false; + } + + /** + * @phpstan-assert-if-true !null $request->headers->get('Range') + */ + private function isRangeRequest(Request $request): bool + { + return $request->headers->has('Range') + && ( + !$request->headers->has('If-Range') || $this->getEtag() === $request->headers->get('If-Range') + ); } - public function getContent() + private function processRangeRequest(Request $request): void { - return null; + $range = $request->headers->get('Range'); + $fileSize = $this->file->size; + + [$start, $end] = explode('-', substr($range, 6), 2) + [0]; + + $end = ('' === $end) ? $fileSize - 1 : (int)$end; + + if ('' === $start) { + $start = $fileSize - $end; + $end = $fileSize - 1; + } else { + $start = (int)$start; + } + + if ($start <= $end) { + if ($start < 0 || $end > $fileSize - 1) { + $this->setStatusCode( + Response::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE + ); + } elseif ($start !== 0 || $end !== $fileSize - 1) { + $this->maxlen = $end < $fileSize ? $end - $start + 1 : -1; + $this->offset = $start; + + $this->setStatusCode(Response::HTTP_PARTIAL_CONTENT); + $this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize)); + $this->headers->set('Content-Length', $end - $start + 1); + } + } } } diff --git a/src/bundle/IO/DependencyInjection/IbexaIOExtension.php b/src/bundle/IO/DependencyInjection/IbexaIOExtension.php index 27031f8230..6ef48e3724 100644 --- a/src/bundle/IO/DependencyInjection/IbexaIOExtension.php +++ b/src/bundle/IO/DependencyInjection/IbexaIOExtension.php @@ -22,13 +22,13 @@ */ class IbexaIOExtension extends Extension { - public const EXTENSION_NAME = 'ibexa_io'; + public const string EXTENSION_NAME = 'ibexa_io'; - /** @var \Ibexa\Bundle\IO\DependencyInjection\ConfigurationFactory[]|\ArrayObject */ - private $metadataHandlerFactories; + /** @var \ArrayObject */ + private ArrayObject $metadataHandlerFactories; - /** @var \Ibexa\Bundle\IO\DependencyInjection\ConfigurationFactory[]|\ArrayObject */ - private $binarydataHandlerFactories; + /** @var \ArrayObject */ + private ArrayObject $binarydataHandlerFactories; public function __construct() { @@ -38,38 +38,32 @@ public function __construct() /** * Registers a metadata handler configuration $factory for handler with $alias. - * - * @param string $alias - * @param \Ibexa\Bundle\IO\DependencyInjection\ConfigurationFactory $factory */ - public function addMetadataHandlerFactory($alias, ConfigurationFactory $factory) + public function addMetadataHandlerFactory(string $alias, ConfigurationFactory $factory): void { $this->metadataHandlerFactories[$alias] = $factory; } /** - * Registers a binarydata handler configuration $factory for handler with $alias. - * - * @param string $alias - * @param \Ibexa\Bundle\IO\DependencyInjection\ConfigurationFactory $factory + * Registers a binary data handler configuration $factory for handler with $alias. */ - public function addBinarydataHandlerFactory($alias, ConfigurationFactory $factory) + public function addBinarydataHandlerFactory(string $alias, ConfigurationFactory $factory): void { $this->binarydataHandlerFactories[$alias] = $factory; } /** - * @return \Ibexa\Bundle\IO\DependencyInjection\ConfigurationFactory[]|\ArrayObject + * @return \ArrayObject */ - public function getMetadataHandlerFactories() + public function getMetadataHandlerFactories(): ArrayObject { return $this->metadataHandlerFactories; } /** - * @return \Ibexa\Bundle\IO\DependencyInjection\ConfigurationFactory[]|\ArrayObject + * @return \ArrayObject */ - public function getBinarydataHandlerFactories() + public function getBinarydataHandlerFactories(): ArrayObject { return $this->binarydataHandlerFactories; } @@ -80,12 +74,13 @@ public function getAlias(): string } /** - * {@inheritdoc} + * @throws \Exception */ - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); + /** @var \Ibexa\Bundle\IO\DependencyInjection\Configuration $configuration */ $configuration = $this->getConfiguration($configs, $container); $config = $this->processConfiguration($configuration, $configs); @@ -100,10 +95,10 @@ public function load(array $configs, ContainerBuilder $container) /** * Processes the config key $key, and registers the result in ez_io.$key. * - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * @param string $key Configuration key, either binarydata or metadata + * @param array $config + * @param string $key Configuration key, either binary data or metadata */ - private function processHandlers(ContainerBuilder $container, $config, $key) + private function processHandlers(ContainerBuilder $container, array $config, string $key): void { $handlers = []; if (isset($config[$key])) { @@ -120,6 +115,9 @@ private function processHandlers(ContainerBuilder $container, $config, $key) $container->setParameter("ibexa.io.{$key}", $handlers); } + /** + * @param array $config + */ public function getConfiguration(array $config, ContainerBuilder $container): ?ConfigurationInterface { $configuration = new Configuration(); diff --git a/src/bundle/IO/IbexaIOBundle.php b/src/bundle/IO/IbexaIOBundle.php index cba5911556..5cb0049493 100644 --- a/src/bundle/IO/IbexaIOBundle.php +++ b/src/bundle/IO/IbexaIOBundle.php @@ -16,11 +16,12 @@ class IbexaIOBundle extends Bundle { - /** @var \Ibexa\Bundle\IO\DependencyInjection\IbexaIOExtension */ + /** @var \Ibexa\Bundle\IO\DependencyInjection\IbexaIOExtension|null */ protected $extension; - public function build(ContainerBuilder $container) + public function build(ContainerBuilder $container): void { + /** @var \Ibexa\Bundle\IO\DependencyInjection\IbexaIOExtension $extension */ $extension = $this->getContainerExtension(); $container->addCompilerPass( new Compiler\IOConfigurationPass( diff --git a/src/bundle/LegacySearchEngine/DependencyInjection/IbexaLegacySearchEngineExtension.php b/src/bundle/LegacySearchEngine/DependencyInjection/IbexaLegacySearchEngineExtension.php index 8369d672ab..a74486cca8 100644 --- a/src/bundle/LegacySearchEngine/DependencyInjection/IbexaLegacySearchEngineExtension.php +++ b/src/bundle/LegacySearchEngine/DependencyInjection/IbexaLegacySearchEngineExtension.php @@ -19,7 +19,10 @@ public function getAlias(): string return 'ibexa_legacy_search_engine'; } - public function load(array $configs, ContainerBuilder $container) + /** + * @throws \Exception + */ + public function load(array $configs, ContainerBuilder $container): void { // Loading configuration from ./src/lib/Resources/settings/policies.yml $loader = new YamlFileLoader( diff --git a/src/bundle/LegacySearchEngine/IbexaLegacySearchEngineBundle.php b/src/bundle/LegacySearchEngine/IbexaLegacySearchEngineBundle.php index dd452de200..36b995bd60 100644 --- a/src/bundle/LegacySearchEngine/IbexaLegacySearchEngineBundle.php +++ b/src/bundle/LegacySearchEngine/IbexaLegacySearchEngineBundle.php @@ -17,7 +17,7 @@ class IbexaLegacySearchEngineBundle extends Bundle { - public function build(ContainerBuilder $container) + public function build(ContainerBuilder $container): void { parent::build($container); diff --git a/src/bundle/RepositoryInstaller/DependencyInjection/IbexaRepositoryInstallerExtension.php b/src/bundle/RepositoryInstaller/DependencyInjection/IbexaRepositoryInstallerExtension.php index a5c61cf0f8..19e6d007db 100644 --- a/src/bundle/RepositoryInstaller/DependencyInjection/IbexaRepositoryInstallerExtension.php +++ b/src/bundle/RepositoryInstaller/DependencyInjection/IbexaRepositoryInstallerExtension.php @@ -14,7 +14,10 @@ class IbexaRepositoryInstallerExtension extends Extension { - public function load(array $configs, ContainerBuilder $container) + /** + * @throws \Exception + */ + public function load(array $configs, ContainerBuilder $container): void { $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('services.yml'); diff --git a/src/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundle.php b/src/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundle.php index 3e8a553fd9..c3448681c3 100644 --- a/src/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundle.php +++ b/src/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundle.php @@ -18,7 +18,7 @@ class IbexaRepositoryInstallerBundle extends Bundle /** * @throws \RuntimeException */ - public function build(ContainerBuilder $container) + public function build(ContainerBuilder $container): void { if (!$container->hasExtension('ibexa_doctrine_schema')) { throw new RuntimeException( diff --git a/src/contracts/IO/BinaryFile.php b/src/contracts/IO/BinaryFile.php index d65f2ac209..ef78cb68b9 100644 --- a/src/contracts/IO/BinaryFile.php +++ b/src/contracts/IO/BinaryFile.php @@ -8,7 +8,7 @@ namespace Ibexa\Contracts\Core\IO; /** - * This class provides an abstract access to binary files. + * This class provides abstract access to binary files. * * It allows reading & writing of files in a unified way */ diff --git a/src/contracts/Repository/ContentService.php b/src/contracts/Repository/ContentService.php index be8943d8a3..dea1594193 100644 --- a/src/contracts/Repository/ContentService.php +++ b/src/contracts/Repository/ContentService.php @@ -28,6 +28,8 @@ /** * This class provides service methods for managing content. + * + * @phpstan-type TFilteringLanguageFilter array */ interface ContentService { @@ -511,7 +513,7 @@ public function validate(ValueObject $object, array $context, ?array $fieldIdent /** * Fetches Content items from the Repository filtered by the given conditions. * - * @param array $languages A list of language codes to be added as additional constraints. + * @phpstan-param TFilteringLanguageFilter|null $languages A list of language codes to be added as additional constraints. * If skipped, by default, unless SiteAccessAware layer has been disabled, languages set * for a SiteAccess in a current context will be used. */ @@ -522,9 +524,11 @@ public function find(Filter $filter, ?array $languages = null): ContentList; * * Counts total number of items returned by {@see ContentService::find()} with the same parameters. * - * @param array $languages A list of language codes to be added as additional constraints. + * @phpstan-param TFilteringLanguageFilter|null $languages $languages A list of language codes to be added as additional constraints. * If skipped, by default, unless SiteAccessAware layer has been disabled, languages set * for a SiteAccess in a current context will be used. + * + * @phpstan-return int<0, max> */ public function count(Filter $filter, ?array $languages = null): int; } diff --git a/src/contracts/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapter.php b/src/contracts/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapter.php index 6d95b6ac63..ff0ea38aa2 100644 --- a/src/contracts/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapter.php +++ b/src/contracts/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapter.php @@ -14,20 +14,26 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; use Iterator; +/** + * @template TSearchHitValueObject of \Ibexa\Contracts\Core\Repository\Values\ValueObject + * + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService + */ abstract class AbstractSearchAdapter implements BatchIteratorAdapter { - /** @var \Ibexa\Contracts\Core\Repository\SearchService */ - protected $searchService; + protected SearchService $searchService; - /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query */ - protected $query; + protected Query $query; - /** @var string[] */ - protected $languageFilter; + /** @phpstan-var TSearchLanguageFilter */ + protected array $languageFilter; /** @var bool */ - protected $filterOnUserPermissions; + protected bool $filterOnUserPermissions; + /** + * @phpstan-param TSearchLanguageFilter $languageFilter + */ public function __construct( SearchService $searchService, Query $query, @@ -49,5 +55,10 @@ final public function fetch(int $offset, int $limit): Iterator return $this->executeSearch($query)->getIterator(); } + /** + * @phpstan-return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ abstract protected function executeSearch(Query $query): SearchResult; } diff --git a/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapter.php b/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapter.php index 8d7b4c318c..d19be293e4 100644 --- a/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapter.php +++ b/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapter.php @@ -11,8 +11,14 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Query; use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +/** + * @extends \Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo> + */ final class ContentInfoSearchAdapter extends AbstractSearchAdapter { + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ protected function executeSearch(Query $query): SearchResult { return $this->searchService->findContentInfo( diff --git a/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapter.php b/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapter.php index 7216153afc..7e25c3ddd9 100644 --- a/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapter.php +++ b/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapter.php @@ -11,6 +11,9 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Query; use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +/** + * @extends \Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\Content> + */ final class ContentSearchAdapter extends AbstractSearchAdapter { protected function executeSearch(Query $query): SearchResult diff --git a/src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php b/src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php index f5401e01c9..263675e277 100644 --- a/src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php +++ b/src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php @@ -8,11 +8,15 @@ namespace Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter; +use Ibexa\Contracts\Core\Exception\InvalidArgumentException; use Ibexa\Contracts\Core\Repository\SearchService; use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; use Ibexa\Contracts\Core\Repository\Values\Content\Query; use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +/** + * @extends \Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\Location> + */ final class LocationSearchAdapter extends AbstractSearchAdapter { public function __construct( @@ -26,6 +30,17 @@ public function __construct( protected function executeSearch(Query $query): SearchResult { + if (!$query instanceof LocationQuery) { + throw new InvalidArgumentException( + '$query', + sprintf( + 'Expected an instance of %s, got %s', + LocationQuery::class, + get_class($query) + ) + ); + } + return $this->searchService->findLocations( $query, $this->languageFilter, diff --git a/src/contracts/Repository/LocationService.php b/src/contracts/Repository/LocationService.php index 392306a00f..7f1f43615b 100644 --- a/src/contracts/Repository/LocationService.php +++ b/src/contracts/Repository/LocationService.php @@ -18,6 +18,8 @@ /** * Location service, used for complex subtree operations. + * + * @phpstan-type TFilteringLanguageFilter array */ interface LocationService { @@ -260,7 +262,7 @@ public function loadAllLocations(int $offset = 0, int $limit = 25): array; /** * Fetch a LocationList from the Repository filtered by the given conditions. * - * @param string[] $languages a list of language codes to be added as additional constraints. + * @phpstan-param TFilteringLanguageFilter|null $languages a list of language codes to be added as additional constraints. * If skipped, by default, unless SiteAccessAware layer has been disabled, languages set * for a SiteAccess in a current context will be used. */ @@ -269,7 +271,7 @@ public function find(Filter $filter, ?array $languages = null): LocationList; /** * Count total number of items returned by {@see find} method. * - * @param string[] $languages a list of language codes to be added as additional constraints. + * @phpstan-param TFilteringLanguageFilter|null $languages a list of language codes to be added as additional constraints. * If skipped, by default, unless SiteAccessAware layer has been disabled, languages set * for a SiteAccess in a current context will be used. */ diff --git a/src/contracts/Repository/SearchService.php b/src/contracts/Repository/SearchService.php index 6b4b501f92..0d8a77dfe6 100644 --- a/src/contracts/Repository/SearchService.php +++ b/src/contracts/Repository/SearchService.php @@ -15,7 +15,7 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; /** - * Search service. + * @phpstan-type TSearchLanguageFilter array{languages?: string[], useAlwaysAvailable?: bool} */ interface SearchService { @@ -25,8 +25,6 @@ interface SearchService * Scoring, a search feature telling you how well one search hit scores compared to other items in the search result. * When this is supported you can expect search engine to populate SearchHit->score and SearchResult->maxScore * properties as well as sort by this if no sort clauses are specified. - * - * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) */ public const CAPABILITY_SCORING = 1; @@ -39,8 +37,6 @@ interface SearchService * - It might not support all facets, by design it will only return facets for facet builders the search engine supports. * - Some of the faceting features are still work in progress in API and won't be further matured before in 7 .x * releases - * - * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) */ public const CAPABILITY_FACETS = 2; @@ -51,8 +47,6 @@ interface SearchService * generate custom fields, like a different representation (format, ...) of an existing field or similar. And 2. * allow you on some search criteria to specify this custom field to rather query on that instead of the default * field generated by the system. - * - * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) */ public const CAPABILITY_CUSTOM_FIELDS = 4; @@ -64,8 +58,6 @@ interface SearchService * WARNING: This feature is considered experimental given it is not completely designed yet in terms of how it should * interact with FullText criterion (singular) which is the most relevant here. Also given how FullText can be part of a more complex criteria it * might imply a need to more strictly define where users are supposed to place FullText vs other criteria. - * - * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) */ public const CAPABILITY_SPELLCHECK = 8; @@ -82,8 +74,6 @@ interface SearchService * @internal Maybe it should rather give just matched text and hint of which field (several: one with best score) * was matched and leave it to field type to render result with that info taking into account. But for now it is * designed as simple string field, so should be string with for instance `` around matched text. - * - * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) */ public const CAPABILITY_HIGHLIGHT = 16; @@ -92,8 +82,6 @@ interface SearchService * * WARNING: This feature is considered experimental given it is not completely clear what it is supposed to do. Feature * might be deprecated in the future. - * - * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) */ public const CAPABILITY_SUGGEST = 32; @@ -102,7 +90,6 @@ interface SearchService * * Advance full text is a feature making to possible by current engine to parse advance full text expressions. * - * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) * @see \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\FullText */ public const CAPABILITY_ADVANCED_FULLTEXT = 64; @@ -119,14 +106,14 @@ interface SearchService * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query - * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. - * Also used to define which field languages are loaded for the returned content. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations + * @phpstan-param TSearchLanguageFilter $languageFilter Configuration for specifying prioritized languages the query will be performed on. + * Also used to define which field languages are loaded for the returned content. + * Currently, it supports: ['languages' => [, ...], 'useAlwaysAvailable' => bool]. + * `useAlwaysAvailable` defaults to true to avoid exceptions on missing translations. + * * @param bool $filterOnUserPermissions if true only the objects which the user is allowed to read are returned. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Repository\Values\Content\Content> */ public function findContent(Query $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult; @@ -137,17 +124,16 @@ public function findContent(Query $query, array $languageFilter = [], bool $filt * it can be more efficient for use cases where you don't need the full Content. Also including use cases * where content will be loaded by separate code, like an ESI based sub requests that takes content ID as input. * - * @since 0.5.4.5 eZ Publish 5.4 (ezpublish-kernel 5.4) - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid + * @phpstan-param TSearchLanguageFilter $languageFilter Configuration for specifying prioritized languages the query will be performed on. + * Also used to define which field languages are loaded for the returned content. + * Currently, it supports: ['languages' => [, ...], 'useAlwaysAvailable' => bool]. + * `useAlwaysAvailable` defaults to true to avoid exceptions on missing translations. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query - * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations * @param bool $filterOnUserPermissions if true (default) only the objects which is the user allowed to read are returned. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo> + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid */ public function findContentInfo(Query $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult; @@ -158,13 +144,12 @@ public function findContentInfo(Query $query, array $languageFilter = [], bool $ * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if criterion is not valid * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is more than than one result matching the criterions * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $filter - * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations - * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned. + * @phpstan-param TSearchLanguageFilter $languageFilter Configuration for specifying prioritized languages the query will be performed on. + * Also used to define which field languages are loaded for the returned content. + * Currently, it supports: ['languages' => [, ...], 'useAlwaysAvailable' => bool]. + * `useAlwaysAvailable` defaults to true to avoid exceptions on missing translations. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned. */ public function findSingle(CriterionInterface $filter, array $languageFilter = [], bool $filterOnUserPermissions = true): Content; @@ -178,15 +163,16 @@ public function suggest(string $prefix, array $fieldPaths = [], int $limit = 10, /** * Finds Locations for the given query. * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid + * @phpstan-param TSearchLanguageFilter $languageFilter Configuration for specifying prioritized languages the query will be performed on. + * Also used to define which field languages are loaded for the returned content. + * Currently, it supports: ['languages' => [, ...], 'useAlwaysAvailable' => bool]. + * `useAlwaysAvailable` defaults to true to avoid exceptions on missing translations. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery $query - * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Repository\Values\Content\Location> + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid */ public function findLocations(LocationQuery $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult; @@ -195,8 +181,6 @@ public function findLocations(LocationQuery $query, array $languageFilter = [], * * Will return false if search engine does not implement {@see \Ibexa\Contracts\Core\Search\Capable}. * - * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) - * * @param int $capabilityFlag One of CAPABILITY_* constants. * * @return bool diff --git a/src/contracts/Repository/Values/Content/Content.php b/src/contracts/Repository/Values/Content/Content.php index e0469589a5..6507e61690 100644 --- a/src/contracts/Repository/Values/Content/Content.php +++ b/src/contracts/Repository/Values/Content/Content.php @@ -18,7 +18,7 @@ * @property-read \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo @deprecated 4.6.7 accessing magic getter is deprecated and will be removed in 5.0.0. Use {@see Content::getContentInfo()} instead. * @property-read int $id @deprecated 4.6.7 accessing magic getter is deprecated and will be removed in 5.0.0. Use {@see Content::getId()} instead. * @property-read \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo calls getVersionInfo() - * @property-read \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $fields access fields, calls getFields() + * @property-read array> $fields an array of [field definition identifier => [language code => field value]] * @property-read \Ibexa\Contracts\Core\Repository\Values\Content\Thumbnail|null $thumbnail calls getThumbnail() */ abstract class Content extends ValueObject diff --git a/src/contracts/Repository/Values/Content/ContentList.php b/src/contracts/Repository/Values/Content/ContentList.php index cef8cc4e73..9fd7b03792 100644 --- a/src/contracts/Repository/Values/Content/ContentList.php +++ b/src/contracts/Repository/Values/Content/ContentList.php @@ -14,10 +14,12 @@ /** * A filtered Content items list iterator. + * + * @implements \IteratorAggregate<\Ibexa\Contracts\Core\Repository\Values\Content\Content> */ final class ContentList implements IteratorAggregate, TotalCountAwareInterface { - /** @var int */ + /** @phpstan-var int<0, max> */ private int $totalCount; /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content[] */ @@ -26,6 +28,8 @@ final class ContentList implements IteratorAggregate, TotalCountAwareInterface /** * @internal for internal use by Repository * + * @phpstan-param int<0, max> $totalCount + * * @param array<\Ibexa\Contracts\Core\Repository\Values\Content\Content> $contentItems */ public function __construct(int $totalCount, array $contentItems) @@ -34,6 +38,9 @@ public function __construct(int $totalCount, array $contentItems) $this->contentItems = $contentItems; } + /** + * @phpstan-return int<0, max> + */ public function getTotalCount(): int { return $this->totalCount; diff --git a/src/contracts/Repository/Values/Content/LocationList.php b/src/contracts/Repository/Values/Content/LocationList.php index 02b5081768..f32d876352 100644 --- a/src/contracts/Repository/Values/Content/LocationList.php +++ b/src/contracts/Repository/Values/Content/LocationList.php @@ -18,7 +18,7 @@ * This class represents a queried location list holding a totalCount and a partial list of locations * (by offset/limit parameters and permission filters). * - * @property-read int $totalCount - the total count of found locations (filtered by permissions) + * @property-read int<0, max> $totalCount - the total count of found locations (filtered by permissions) * @property-read \Ibexa\Contracts\Core\Repository\Values\Content\Location[] $locations - the partial list of * Locations controlled by offset/limit. **/ @@ -29,7 +29,7 @@ class LocationList extends ValueObject implements IteratorAggregate, TotalCountA * * Use {@see getTotalCount} to fetch it. * - * @var int + * @phpstan-var int<0, max> */ protected $totalCount = 0; @@ -48,6 +48,9 @@ public function getIterator(): Traversable return new ArrayIterator($this->locations); } + /** + * @phpstan-return int<0, max> + */ public function getTotalCount(): int { return $this->totalCount; diff --git a/src/contracts/Repository/Values/Content/Search/SearchHit.php b/src/contracts/Repository/Values/Content/Search/SearchHit.php index cf7004ed17..f41431ebdb 100644 --- a/src/contracts/Repository/Values/Content/Search/SearchHit.php +++ b/src/contracts/Repository/Values/Content/Search/SearchHit.php @@ -12,13 +12,15 @@ /** * This class represents a SearchHit matching the query. + * + * @template-covariant T of \Ibexa\Contracts\Core\Repository\Values\ValueObject */ class SearchHit extends ValueObject { /** * The value found by the search. * - * @var \Ibexa\Contracts\Core\Repository\Values\ValueObject + * @phpstan-var T */ public $valueObject; diff --git a/src/contracts/Repository/Values/Content/Search/SearchResult.php b/src/contracts/Repository/Values/Content/Search/SearchResult.php index e495eed7eb..c122cea691 100644 --- a/src/contracts/Repository/Values/Content/Search/SearchResult.php +++ b/src/contracts/Repository/Values/Content/Search/SearchResult.php @@ -15,6 +15,10 @@ /** * This class represents a search result. + * + * @template TSearchHitValueObject of \Ibexa\Contracts\Core\Repository\Values\ValueObject + * + * @phpstan-implements \IteratorAggregate */ class SearchResult extends ValueObject implements IteratorAggregate, AggregationResultAwareInterface { @@ -35,9 +39,9 @@ class SearchResult extends ValueObject implements IteratorAggregate, Aggregation /** * The value objects found for the query. * - * @var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit[] + * @phpstan-var list<\Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit> */ - public $searchHits = []; + public array $searchHits = []; /** * If spellcheck is on this field contains a collated query suggestion where in the appropriate @@ -77,9 +81,9 @@ class SearchResult extends ValueObject implements IteratorAggregate, Aggregation * * `null` if Query->performCount was set to false and search engine avoids search lookup. * - * @var int|null + * @phpstan-var int<0, max>|null */ - public $totalCount; + public ?int $totalCount; public function __construct(array $properties = []) { diff --git a/src/contracts/Search/Handler.php b/src/contracts/Search/Handler.php index dc3c184a44..46e50ff3a8 100644 --- a/src/contracts/Search/Handler.php +++ b/src/contracts/Search/Handler.php @@ -13,10 +13,13 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Query; use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; use Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; /** * The Search handler retrieves sets of of Content objects, based on a * set of criteria. + * + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService */ interface Handler { @@ -25,45 +28,31 @@ interface Handler * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if Query criterion is not applicable to its target * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query - * @param array $languageFilter a map of language related filters specifying languages query will be performed on. - * Also used to define which field languages are loaded for the returned content. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations + * @phpstan-param TSearchLanguageFilter $languageFilter {@see \Ibexa\Contracts\Core\Repository\SearchService::findContent} * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult With ContentInfo as SearchHit->valueObject + * @phpstan-return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Persistence\Content\ContentInfo> */ - public function findContent(Query $query, array $languageFilter = []); + public function findContent(Query $query, array $languageFilter = []): SearchResult; /** * Performs a query for a single content object. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface $filter - * @param array $languageFilter a map of language related filters specifying languages query will be performed on. - * Also used to define which field languages are loaded for the returned content. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations - * - * @return \Ibexa\Contracts\Core\Persistence\Content\ContentInfo - * - *@throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the object was not found by the query or due to permissions + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the object was not found by the query or due to permissions * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if Criterion is not applicable to its target - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is more than than one result matching the criterions + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is more than one result matching the criteria + * + * @phpstan-param TSearchLanguageFilter $languageFilter {@see \Ibexa\Contracts\Core\Repository\SearchService::findSingle()} */ - public function findSingle(CriterionInterface $filter, array $languageFilter = []); + public function findSingle(CriterionInterface $filter, array $languageFilter = []): Content\ContentInfo; /** * Finds locations for the given $query. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery $query - * @param array $languageFilter a map of language related filters specifying languages query will be performed on. - * Also used to define which field languages are loaded for the returned content. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations + * @phpstan-param TSearchLanguageFilter $languageFilter {@see \Ibexa\Contracts\Core\Repository\SearchService::findSingle()} * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult With Location as SearchHit->valueObject + * @phpstan-return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Persistence\Content\Location> */ - public function findLocations(LocationQuery $query, array $languageFilter = []); + public function findLocations(LocationQuery $query, array $languageFilter = []): SearchResult; /** * Suggests a list of values for the given prefix. diff --git a/src/contracts/Validation/StructWrapperValidator.php b/src/contracts/Validation/StructWrapperValidator.php index 5a80f5d5fa..5cbef86b38 100644 --- a/src/contracts/Validation/StructWrapperValidator.php +++ b/src/contracts/Validation/StructWrapperValidator.php @@ -8,6 +8,7 @@ namespace Ibexa\Contracts\Core\Validation; +use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\ConstraintViolationList; use Symfony\Component\Validator\ConstraintViolationListInterface; @@ -25,17 +26,17 @@ public function __construct(ValidatorInterface $inner) $this->inner = $inner; } - public function getMetadataFor($value): MetadataInterface + public function getMetadataFor(mixed $value): MetadataInterface { return $this->inner->getMetadataFor($value); } - public function hasMetadataFor($value): bool + public function hasMetadataFor(mixed $value): bool { return $this->inner->hasMetadataFor($value); } - public function validate($value, $constraints = null, $groups = null): ConstraintViolationListInterface + public function validate(mixed $value, Constraint|array|null $constraints = null, $groups = null): ConstraintViolationListInterface { $result = $this->inner->validate($value, $constraints, $groups); diff --git a/src/lib/Base/Container/Compiler/FieldTypeRegistryPass.php b/src/lib/Base/Container/Compiler/FieldTypeRegistryPass.php index d6adcdbd13..f14a099d09 100644 --- a/src/lib/Base/Container/Compiler/FieldTypeRegistryPass.php +++ b/src/lib/Base/Container/Compiler/FieldTypeRegistryPass.php @@ -41,7 +41,8 @@ public function process(ContainerBuilder $container): void ); // Add FieldType to the "concrete" list if it's not a fake. - if (!is_a($container->findDefinition($id)->getClass(), Type::class, true)) { + $class = $container->findDefinition($id)->getClass(); + if ($class === null || !is_a($class, Type::class, true)) { $fieldTypeRegistryDefinition->addMethodCall( 'registerConcreteFieldTypeIdentifier', [$attribute['alias']] diff --git a/src/lib/IO/Flysystem/VisibilityConverter/SiteAccessAwareVisibilityConverter.php b/src/lib/IO/Flysystem/VisibilityConverter/SiteAccessAwareVisibilityConverter.php index 7c94201627..894ab21ff3 100644 --- a/src/lib/IO/Flysystem/VisibilityConverter/SiteAccessAwareVisibilityConverter.php +++ b/src/lib/IO/Flysystem/VisibilityConverter/SiteAccessAwareVisibilityConverter.php @@ -24,8 +24,8 @@ */ final class SiteAccessAwareVisibilityConverter extends BaseVisibilityConverter { - public const SITE_CONFIG_IO_FILE_PERMISSIONS_PARAM_NAME = 'io.permissions.files'; - public const SITE_CONFIG_IO_DIR_PERMISSIONS_PARAM_NAME = 'io.permissions.directories'; + public const string SITE_CONFIG_IO_FILE_PERMISSIONS_PARAM_NAME = 'io.permissions.files'; + public const string SITE_CONFIG_IO_DIR_PERMISSIONS_PARAM_NAME = 'io.permissions.directories'; private ConfigResolverInterface $configResolver; diff --git a/src/lib/IO/IOService.php b/src/lib/IO/IOService.php index 57087e789b..c80a2822fd 100644 --- a/src/lib/IO/IOService.php +++ b/src/lib/IO/IOService.php @@ -157,9 +157,8 @@ public function loadBinaryFile($binaryFileId) { $this->checkBinaryFileId($binaryFileId); - // @todo An absolute path can in no case be loaded, but throwing an exception is too much (why ?) if ($this->isAbsolutePath($binaryFileId)) { - return false; + throw new InvalidArgumentValue('$binaryFileId', "$binaryFileId is an absolute path"); } $spiBinaryFile = $this->metadataHandler->load($this->getPrefixedUri($binaryFileId)); diff --git a/src/lib/IO/Values/BinaryFile.php b/src/lib/IO/Values/BinaryFile.php index e698fd4cd6..ad52a7181f 100644 --- a/src/lib/IO/Values/BinaryFile.php +++ b/src/lib/IO/Values/BinaryFile.php @@ -7,15 +7,16 @@ namespace Ibexa\Core\IO\Values; +use DateTime; use Ibexa\Contracts\Core\Repository\Values\ValueObject; /** - * This class provides an abstract access to binary files. + * This class provides abstract access to binary files. * * It allows reading & writing of files in a unified way * * @property-read string $id The id of the binary file - * @property-read int $mtime File modification time + * @property-read \DateTime $mtime File modification time * @property-read string $uri HTTP URI to the binary file * @property-read int $size File size */ @@ -24,29 +25,21 @@ class BinaryFile extends ValueObject /** * Unique ID * Ex: media/images/ibexa-logo/209-1-eng-GB/Ibexa-Logo.gif, or application/2b042138835bb5f48beb9c9df6e86de4.pdf. - * - * @var mixed */ - protected $id; + protected string $id; /** * File size, in bytes. - * - * @var int */ - protected $size; + protected ?int $size = null; /** * File modification time. - * - * @var \DateTime */ - protected $mtime; + protected ?DateTime $mtime = null; /** * URI to the binary file. - * - * @var string */ - protected $uri; + protected string $uri; } diff --git a/src/lib/MVC/Exception/HiddenLocationException.php b/src/lib/MVC/Exception/HiddenLocationException.php index 1c8fabd454..1117bcea76 100644 --- a/src/lib/MVC/Exception/HiddenLocationException.php +++ b/src/lib/MVC/Exception/HiddenLocationException.php @@ -4,27 +4,25 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ +declare(strict_types=1); namespace Ibexa\Core\MVC\Exception; use Ibexa\Contracts\Core\Repository\Values\Content\Location; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Throwable; class HiddenLocationException extends NotFoundHttpException { - /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location */ - private $location; + private Location $location; - public function __construct(Location $location, $message = null, \Exception $previous = null, $code = 0) + public function __construct(Location $location, ?string $message = null, ?Throwable $previous = null, int $code = 0) { $this->location = $location; - parent::__construct($message, $previous, $code); + parent::__construct($message ?? 'HTTP Not Found', $previous, $code); } - /** - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location - */ - public function getLocation() + public function getLocation(): Location { return $this->location; } diff --git a/src/lib/MVC/Symfony/Controller/Content/QueryController.php b/src/lib/MVC/Symfony/Controller/Content/QueryController.php index 4ee23943cd..35fdafd6bb 100644 --- a/src/lib/MVC/Symfony/Controller/Content/QueryController.php +++ b/src/lib/MVC/Symfony/Controller/Content/QueryController.php @@ -14,8 +14,8 @@ use Ibexa\Core\Pagination\Pagerfanta\ContentSearchHitAdapter; use Ibexa\Core\Pagination\Pagerfanta\LocationSearchHitAdapter; use Ibexa\Core\Pagination\Pagerfanta\Pagerfanta; +use Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter; use Ibexa\Core\QueryType\ContentViewQueryTypeMapper; -use Pagerfanta\Adapter\AdapterInterface; use Symfony\Component\HttpFoundation\Request; /** @@ -132,11 +132,9 @@ private function runPagingQuery(ContentView $view, Request $request) } /** - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query - * - * @return \Pagerfanta\Adapter\AdapterInterface + * @phpstan-return \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\ValueObject> */ - private function getAdapter(Query $query): AdapterInterface + private function getAdapter(Query $query): SearchResultAdapter { if ($query instanceof LocationQuery) { return new LocationSearchHitAdapter($query, $this->searchService); diff --git a/src/lib/MVC/Symfony/Controller/QueryRenderController.php b/src/lib/MVC/Symfony/Controller/QueryRenderController.php index af4f8d2dd7..062190b6fa 100644 --- a/src/lib/MVC/Symfony/Controller/QueryRenderController.php +++ b/src/lib/MVC/Symfony/Controller/QueryRenderController.php @@ -20,14 +20,14 @@ * Controller used internally by ez_query_*_render and ez_query_*_render_* functions. * * @internal + * + * @phpstan-type TOptionsArray array */ final class QueryRenderController { - /** @var \Ibexa\Core\Query\QueryFactoryInterface */ - private $queryFactory; + private QueryFactoryInterface $queryFactory; - /** @var \Ibexa\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactoryInterface */ - private $searchHitAdapterFactory; + private SearchHitAdapterFactoryInterface $searchHitAdapterFactory; public function __construct( QueryFactoryInterface $queryFactory, @@ -37,6 +37,11 @@ public function __construct( $this->searchHitAdapterFactory = $searchHitAdapterFactory; } + /** + * @phpstan-param TOptionsArray $options + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ public function renderQuery(Request $request, array $options): QueryView { $options = $this->resolveOptions($options); @@ -57,6 +62,11 @@ public function renderQuery(Request $request, array $options): QueryView ); } + /** + * @phpstan-param TOptionsArray $options + * + * @phpstan-return TOptionsArray + */ private function resolveOptions(array $options): array { $resolver = new OptionsResolver(); @@ -92,6 +102,11 @@ private function resolveOptions(array $options): array return $resolver->resolve($options); } + /** + * @phpstan-param TOptionsArray $options + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ private function getAdapter(array $options): SearchResultAdapter { $query = $this->queryFactory->create( diff --git a/src/lib/MVC/Symfony/Routing/Generator.php b/src/lib/MVC/Symfony/Routing/Generator.php index a559547d6c..9729d0b90a 100644 --- a/src/lib/MVC/Symfony/Routing/Generator.php +++ b/src/lib/MVC/Symfony/Routing/Generator.php @@ -19,46 +19,30 @@ */ abstract class Generator implements SiteAccessAware { - /** @var \Symfony\Component\Routing\RequestContext */ - protected $requestContext; + protected RequestContext $requestContext; - /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface */ - protected $siteAccessRouter; + protected SiteAccessRouterInterface $siteAccessRouter; - /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ - protected $siteAccess; + protected ?SiteAccess $siteAccess; - /** @var \Psr\Log\LoggerInterface */ - protected $logger; + protected ?LoggerInterface $logger; - /** - * @param \Symfony\Component\Routing\RequestContext $requestContext - */ - public function setRequestContext(RequestContext $requestContext) + public function setRequestContext(RequestContext $requestContext): void { $this->requestContext = $requestContext; } - /** - * @param \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface $siteAccessRouter - */ - public function setSiteAccessRouter(SiteAccessRouterInterface $siteAccessRouter) + public function setSiteAccessRouter(SiteAccessRouterInterface $siteAccessRouter): void { $this->siteAccessRouter = $siteAccessRouter; } - /** - * @param \Ibexa\Core\MVC\Symfony\SiteAccess|null $siteAccess - */ - public function setSiteAccess(SiteAccess $siteAccess = null) + public function setSiteAccess(?SiteAccess $siteAccess = null): void { $this->siteAccess = $siteAccess; } - /** - * @param \Psr\Log\LoggerInterface $logger - */ - public function setLogger(LoggerInterface $logger = null) + public function setLogger(?LoggerInterface $logger = null): void { $this->logger = $logger; } @@ -67,13 +51,11 @@ public function setLogger(LoggerInterface $logger = null) * Triggers URL generation for $urlResource and $parameters. * * @param mixed $urlResource Type can be anything, depending on the context. It's up to the router to pass the appropriate value to the implementor. - * @param array $parameters Arbitrary hash of parameters to generate a link. + * @param array $parameters An arbitrary hash of parameters to generate a link. * SiteAccess name can be provided as 'siteaccess' to generate a link to it (cross siteaccess link). * @param int $referenceType The type of reference to be generated (one of the constants) - * - * @return string */ - public function generate($urlResource, array $parameters, $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) + public function generate(mixed $urlResource, array $parameters, int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string { $siteAccess = $this->siteAccess; $requestContext = $this->requestContext; @@ -82,8 +64,11 @@ public function generate($urlResource, array $parameters, $referenceType = UrlGe if (isset($parameters['siteaccess'])) { $siteAccess = $this->siteAccessRouter->matchByName($parameters['siteaccess']); if ($siteAccess instanceof SiteAccess && $siteAccess->matcher instanceof SiteAccess\VersatileMatcher) { - $requestContext = $this->getContextBySimplifiedRequest($siteAccess->matcher->getRequest()); - } elseif ($this->logger) { + // inline-instantiated on purpose, as it's lightweight and difficult to inject into DefaultRouter which also uses it + $requestContext = (new RequestContextFactory($this->requestContext))->getContextBySimplifiedRequest( + $siteAccess->matcher->getRequest() + ); + } elseif (isset($this->logger)) { $siteAccess = $this->siteAccess; $this->logger->notice("Could not generate a link using provided 'siteaccess' parameter: {$parameters['siteaccess']}. Generating using current context."); unset($parameters['siteaccess']); @@ -93,7 +78,7 @@ public function generate($urlResource, array $parameters, $referenceType = UrlGe $url = $this->doGenerate($urlResource, $parameters); // Add the SiteAccess URI back if needed. - if ($siteAccess && $siteAccess->matcher instanceof SiteAccess\URILexer) { + if (null !== $siteAccess && $siteAccess->matcher instanceof SiteAccess\URILexer) { $url = $siteAccess->matcher->analyseLink($url); } @@ -109,67 +94,20 @@ public function generate($urlResource, array $parameters, $referenceType = UrlGe /** * Generates the URL from $urlResource and $parameters. * - * @param mixed $urlResource - * @param array $parameters - * - * @return string + * @param array $parameters */ - abstract public function doGenerate($urlResource, array $parameters); + abstract public function doGenerate(mixed $urlResource, array $parameters): string; - /** - * Generates an absolute URL from $uri and the request context. - * - * @param string $uri - * @param \Symfony\Component\Routing\RequestContext $requestContext - * - * @return string - */ - protected function generateAbsoluteUrl($uri, RequestContext $requestContext) + protected function generateAbsoluteUrl(string $uri, RequestContext $requestContext): string { $scheme = $requestContext->getScheme(); $port = ''; - if ($scheme === 'http' && $requestContext->getHttpPort() != 80) { + if ($scheme === 'http' && $requestContext->getHttpPort() !== 80) { $port = ':' . $requestContext->getHttpPort(); - } elseif ($scheme === 'https' && $requestContext->getHttpsPort() != 443) { + } elseif ($scheme === 'https' && $requestContext->getHttpsPort() !== 443) { $port = ':' . $requestContext->getHttpsPort(); } return $scheme . '://' . $requestContext->getHost() . $port . $uri; } - - /** - * Merges context from $simplifiedRequest into a clone of the current context. - * - * @param SimplifiedRequest $simplifiedRequest - * - * @return \Symfony\Component\Routing\RequestContext - */ - private function getContextBySimplifiedRequest(SimplifiedRequest $simplifiedRequest) - { - $context = clone $this->requestContext; - if ($simplifiedRequest->getScheme()) { - $context->setScheme($simplifiedRequest->getScheme()); - } - - if ($simplifiedRequest->getPort()) { - switch ($simplifiedRequest->getScheme()) { - case 'https': - $context->setHttpsPort($simplifiedRequest->getPort()); - break; - default: - $context->setHttpPort($simplifiedRequest->getPort()); - break; - } - } - - if ($simplifiedRequest->getHost()) { - $context->setHost($simplifiedRequest->getHost()); - } - - if ($simplifiedRequest->getPathInfo()) { - $context->setPathInfo($simplifiedRequest->getPathInfo()); - } - - return $context; - } } diff --git a/src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php b/src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php index 41abb9a31d..de2c8b66f2 100644 --- a/src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php +++ b/src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php @@ -20,38 +20,33 @@ */ class UrlAliasGenerator extends Generator { - public const INTERNAL_CONTENT_VIEW_ROUTE = 'ibexa.content.view'; + public const string INTERNAL_CONTENT_VIEW_ROUTE = 'ibexa.content.view'; - /** @var \Ibexa\Core\Repository\Repository */ - private $repository; + private Repository $repository; - /** - * The default router (that works with declared routes). - * - * @var \Symfony\Component\Routing\RouterInterface - */ - private $defaultRouter; + private RouterInterface $defaultRouter; - /** @var int */ - private $rootLocationId; + private int $rootLocationId; - /** @var array */ - private $excludedUriPrefixes = []; + /** @var string[] */ + private array $excludedUriPrefixes = []; - /** @var array */ - private $pathPrefixMap = []; + /** @var array> */ + private array $pathPrefixMap = []; - /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ - private $configResolver; + private ConfigResolverInterface $configResolver; /** * Array of characters that are potentially unsafe for output for (x)html, json, etc, * and respective url-encoded value. * - * @var array + * @var array */ - private $unsafeCharMap; + private array $unsafeCharMap; + /** + * @param array $unsafeCharMap + */ public function __construct(Repository $repository, RouterInterface $defaultRouter, ConfigResolverInterface $configResolver, array $unsafeCharMap = []) { $this->repository = $repository; @@ -64,18 +59,15 @@ public function __construct(Repository $repository, RouterInterface $defaultRout * Generates the URL from $urlResource and $parameters. * Entries in $parameters will be added in the query string. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location - * @param array $parameters - * - * @return string + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $urlResource */ - public function doGenerate($location, array $parameters): string + public function doGenerate(mixed $urlResource, array $parameters): string { $siteAccess = $parameters['siteaccess'] ?? null; unset($parameters['language'], $parameters['contentId'], $parameters['siteaccess']); - $pathString = $this->createPathString($location, $siteAccess); + $pathString = $this->createPathString($urlResource, $siteAccess); $queryString = $this->createQueryString($parameters); $url = $pathString . $queryString; @@ -84,18 +76,16 @@ public function doGenerate($location, array $parameters): string /** * Injects current root locationId that will be used for link generation. - * - * @param int $rootLocationId */ - public function setRootLocationId($rootLocationId) + public function setRootLocationId(int $rootLocationId): void { $this->rootLocationId = $rootLocationId; } /** - * @param array $excludedUriPrefixes + * @param string[] $excludedUriPrefixes */ - public function setExcludedUriPrefixes(array $excludedUriPrefixes) + public function setExcludedUriPrefixes(array $excludedUriPrefixes): void { $this->excludedUriPrefixes = $excludedUriPrefixes; } @@ -103,26 +93,22 @@ public function setExcludedUriPrefixes(array $excludedUriPrefixes) /** * Returns path corresponding to $rootLocationId. * - * @param int $rootLocationId * @param array|null $languages - * @param string $siteaccess - * - * @return string * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ - public function getPathPrefixByRootLocationId($rootLocationId, $languages = null, $siteaccess = null) + public function getPathPrefixByRootLocationId(?int $rootLocationId, ?array $languages = null, ?string $siteAccess = null): string { - if (!$rootLocationId) { + if ($rootLocationId === null || $rootLocationId === 0) { return ''; } - if (!isset($this->pathPrefixMap[$siteaccess])) { - $this->pathPrefixMap[$siteaccess] = []; + if (!isset($this->pathPrefixMap[$siteAccess])) { + $this->pathPrefixMap[$siteAccess] = []; } - if (!isset($this->pathPrefixMap[$siteaccess][$rootLocationId])) { - $this->pathPrefixMap[$siteaccess][$rootLocationId] = $this->repository + if (!isset($this->pathPrefixMap[$siteAccess][$rootLocationId])) { + $this->pathPrefixMap[$siteAccess][$rootLocationId] = $this->repository ->getURLAliasService() ->reverseLookup( $this->loadLocation($rootLocationId, $languages), @@ -133,17 +119,13 @@ public function getPathPrefixByRootLocationId($rootLocationId, $languages = null ->path; } - return $this->pathPrefixMap[$siteaccess][$rootLocationId]; + return $this->pathPrefixMap[$siteAccess][$rootLocationId]; } /** * Checks if passed URI has an excluded prefix, when a root location is defined. - * - * @param string $uri - * - * @return bool */ - public function isUriPrefixExcluded($uri): bool + public function isUriPrefixExcluded(string $uri): bool { foreach ($this->excludedUriPrefixes as $excludedPrefix) { $excludedPrefix = '/' . ltrim($excludedPrefix, '/'); @@ -159,32 +141,23 @@ public function isUriPrefixExcluded($uri): bool * Loads a location by its locationId, regardless to user limitations since the router is invoked BEFORE security (no user authenticated yet). * Not to be used for link generation. * - * @param int $locationId * @param array|null $languages - * - * @return \Ibexa\Core\Repository\Values\Content\Location */ - public function loadLocation($locationId, ?array $languages = null) + public function loadLocation(int $locationId, ?array $languages = null): Location { return $this->repository->sudo( - static function (Repository $repository) use ($locationId, $languages) { + static function (Repository $repository) use ($locationId, $languages): Location { /* @var $repository \Ibexa\Core\Repository\Repository */ return $repository->getLocationService()->loadLocation($locationId, $languages); } ); } - /** - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location - * @param string|null $siteAccess - * - * @return string - */ private function createPathString(Location $location, ?string $siteAccess = null): string { $urlAliasService = $this->repository->getURLAliasService(); - if ($siteAccess) { + if (!empty($siteAccess)) { // We generate for a different SiteAccess, so potentially in a different language. $languages = $this->configResolver->getParameter('languages', null, $siteAccess); $urlAliases = iterator_to_array( @@ -195,7 +168,7 @@ private function createPathString(Location $location, ?string $siteAccess = null } else { $languages = null; $urlAliases = iterator_to_array($urlAliasService->listLocationAliases($location, false)); - $rootLocationId = $this->rootLocationId; + $rootLocationId = $this->rootLocationId ?? null; } if (!empty($urlAliases)) { @@ -226,9 +199,7 @@ private function createPathString(Location $location, ?string $siteAccess = null * Creates query string from parameters. If `_fragment` parameter is provided then * fragment identifier is added at the end of the URL. * - * @param array $parameters - * - * @return string + * @param array $parameters */ private function createQueryString(array $parameters): string { @@ -253,10 +224,6 @@ private function createQueryString(array $parameters): string /** * Replace potentially unsafe characters with url-encoded counterpart. - * - * @param string $url - * - * @return string */ private function filterCharactersOfURL(string $url): string { diff --git a/src/lib/MVC/Symfony/Routing/RequestContextFactory.php b/src/lib/MVC/Symfony/Routing/RequestContextFactory.php new file mode 100644 index 0000000000..9ea1fd2e6b --- /dev/null +++ b/src/lib/MVC/Symfony/Routing/RequestContextFactory.php @@ -0,0 +1,57 @@ +requestContext = clone $requestContext; + } + + /** + * Merges context from $simplifiedRequest into a clone of the current context. + */ + public function getContextBySimplifiedRequest(SimplifiedRequest $simplifiedRequest): RequestContext + { + if ($simplifiedRequest->getScheme()) { + $this->requestContext->setScheme($simplifiedRequest->getScheme()); + } + + if ($simplifiedRequest->getPort()) { + if ($simplifiedRequest->getScheme() === 'https') { + $this->requestContext->setHttpsPort((int)$simplifiedRequest->getPort()); + } else { + $this->requestContext->setHttpPort((int)$simplifiedRequest->getPort()); + } + } + + if ($simplifiedRequest->getHost()) { + $this->requestContext->setHost($simplifiedRequest->getHost()); + } + + if ($simplifiedRequest->getPathInfo()) { + $this->requestContext->setPathInfo($simplifiedRequest->getPathInfo()); + } + + return $this->requestContext; + } +} diff --git a/src/lib/MVC/Symfony/Routing/UrlAliasRouter.php b/src/lib/MVC/Symfony/Routing/UrlAliasRouter.php index dc62d9f509..ff6b2c8667 100644 --- a/src/lib/MVC/Symfony/Routing/UrlAliasRouter.php +++ b/src/lib/MVC/Symfony/Routing/UrlAliasRouter.php @@ -14,10 +14,12 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Location; use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; use Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator; -use Ibexa\Core\MVC\Symfony\View\Manager as ViewManager; +use Ibexa\Core\MVC\Symfony\View\ViewManagerInterface; use InvalidArgumentException; use LogicException; use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use RuntimeException; use Symfony\Cmf\Component\Routing\ChainedRouterInterface; use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\HttpFoundation\Request; @@ -26,188 +28,77 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\Matcher\RequestMatcherInterface; use Symfony\Component\Routing\RequestContext; -use Symfony\Component\Routing\Route as SymfonyRoute; use Symfony\Component\Routing\RouteCollection; class UrlAliasRouter implements ChainedRouterInterface, RequestMatcherInterface { - public const URL_ALIAS_ROUTE_NAME = 'ibexa.url.alias'; + public const string URL_ALIAS_ROUTE_NAME = 'ibexa.url.alias'; - public const VIEW_ACTION = 'ibexa_content::viewAction'; + public const string VIEW_ACTION = 'ibexa_content::viewAction'; - /** @var \Symfony\Component\Routing\RequestContext */ - protected $requestContext; + protected RequestContext $requestContext; - /** @var \Ibexa\Contracts\Core\Repository\LocationService */ - protected $locationService; + protected LocationService $locationService; - /** @var \Ibexa\Contracts\Core\Repository\URLAliasService */ - protected $urlAliasService; + protected URLAliasService $urlAliasService; - /** @var \Ibexa\Contracts\Core\Repository\ContentService */ - protected $contentService; + protected ContentService $contentService; - /** @var \Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator */ - protected $generator; + protected UrlAliasGenerator $generator; - /** - * Holds current root Location id. - * - * @var int|string - */ - protected $rootLocationId; + protected ?int $rootLocationId; - /** @var \Psr\Log\LoggerInterface */ - protected $logger; + protected LoggerInterface $logger; public function __construct( LocationService $locationService, URLAliasService $urlAliasService, ContentService $contentService, UrlAliasGenerator $generator, - RequestContext $requestContext, - LoggerInterface $logger = null + ?RequestContext $requestContext = null, + ?LoggerInterface $logger = null ) { $this->locationService = $locationService; $this->urlAliasService = $urlAliasService; $this->contentService = $contentService; $this->generator = $generator; - $this->requestContext = $requestContext !== null ? $requestContext : new RequestContext(); - $this->logger = $logger; + $this->requestContext = $requestContext ?? new RequestContext(); + $this->logger = $logger ?? new NullLogger(); + $this->rootLocationId = null; } - /** - * Injects current root Location id. - * - * @param int|string $rootLocationId - */ - public function setRootLocationId($rootLocationId) + public function setRootLocationId(?int $rootLocationId): void { $this->rootLocationId = $rootLocationId; } /** - * Tries to match a request with a set of routes. - * - * If the matcher can not find information, it must throw one of the exceptions documented - * below. - * - * @param \Symfony\Component\HttpFoundation\Request $request The request to match + * @return array An array of parameters * - * @return array An array of parameters - * - * @throws \Symfony\Component\Routing\Exception\ResourceNotFoundException If no matching resource could be found + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ - public function matchRequest(Request $request) + public function matchRequest(Request $request): array { try { - $requestedPath = $request->attributes->get('semanticPathinfo', $request->getPathInfo()); + $requestedPath = $request->attributes->getString('semanticPathinfo', $request->getPathInfo()); $urlAlias = $this->getUrlAlias($requestedPath); if ($this->rootLocationId === null) { $pathPrefix = '/'; } else { - $pathPrefix = $this->generator->getPathPrefixByRootLocationId($this->rootLocationId); + $pathPrefix = $this->generator->getPathPrefixByRootLocationId((int)$this->rootLocationId); } $params = [ '_route' => static::URL_ALIAS_ROUTE_NAME, ]; - switch ($urlAlias->type) { - case URLAlias::LOCATION: - $location = $this->generator->loadLocation($urlAlias->destination); - $params += [ - '_controller' => static::VIEW_ACTION, - 'contentId' => $location->contentId, - 'locationId' => $urlAlias->destination, - 'viewType' => ViewManager::VIEW_TYPE_FULL, - 'layout' => true, - ]; - - // For Location alias setup 301 redirect to Location's current URL when: - // 1. alias is history - // 2. alias is custom with forward flag true - // 3. requested URL is not case-sensitive equal with the one loaded - if ($urlAlias->isHistory === true || ($urlAlias->isCustom === true && $urlAlias->forward === true)) { - $params += [ - 'semanticPathinfo' => $this->generator->generate($location, []), - 'needsRedirect' => true, - // Specify not to prepend siteaccess while redirecting when applicable since it would be already present (see UrlAliasGenerator::doGenerate()) - 'prependSiteaccessOnRedirect' => false, - ]; - } elseif ($this->needsCaseRedirect($urlAlias, $requestedPath, $pathPrefix)) { - $params += [ - 'semanticPathinfo' => $this->removePathPrefix($urlAlias->path, $pathPrefix), - 'needsRedirect' => true, - ]; - - if ($urlAlias->destination instanceof Location) { - $params += ['locationId' => $urlAlias->destination->id]; - } - } - - if (isset($this->logger)) { - $this->logger->info("UrlAlias matched location #{$urlAlias->destination}. Forwarding to ViewController"); - } - - break; - - case URLAlias::RESOURCE: - // In URLAlias terms, "forward" means "redirect". - if ($urlAlias->forward) { - $params += [ - 'semanticPathinfo' => '/' . trim($urlAlias->destination, '/'), - 'needsRedirect' => true, - ]; - } elseif ($this->needsCaseRedirect($urlAlias, $requestedPath, $pathPrefix)) { - // Handle case-correction redirect - $params += [ - 'semanticPathinfo' => $this->removePathPrefix($urlAlias->path, $pathPrefix), - 'needsRedirect' => true, - ]; - } else { - $params += [ - 'semanticPathinfo' => '/' . trim($urlAlias->destination, '/'), - 'needsForward' => true, - ]; - } - - break; - - case URLAlias::VIRTUAL: - // Handle case-correction redirect - if ($this->needsCaseRedirect($urlAlias, $requestedPath, $pathPrefix)) { - $params += [ - 'semanticPathinfo' => $this->removePathPrefix($urlAlias->path, $pathPrefix), - 'needsRedirect' => true, - ]; - } else { - // Virtual aliases should load the Content at homepage URL - $params += [ - 'semanticPathinfo' => '/', - 'needsForward' => true, - ]; - } - - break; - } - return $params; + return $this->buildParametersByUrlAliasType($urlAlias, $params, $requestedPath, $pathPrefix); } catch (NotFoundException $e) { throw new ResourceNotFoundException($e->getMessage(), $e->getCode(), $e); } } - /** - * Removes prefix from path. - * - * Checks for presence of $prefix and removes it from $path if found. - * - * @param string $path - * @param string $prefix - * - * @return string - */ - protected function removePathPrefix($path, $prefix) + protected function removePathPrefix(string $path, string $prefix): string { if ($prefix !== '/' && mb_stripos($path, $prefix) === 0) { $path = mb_substr($path, mb_strlen($prefix)); @@ -221,14 +112,8 @@ protected function removePathPrefix($path, $prefix) * * Used to determine if redirect is needed because requested path is case-different * from the stored one. - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias $loadedUrlAlias - * @param string $requestedPath - * @param string $pathPrefix - * - * @return bool */ - protected function needsCaseRedirect(URLAlias $loadedUrlAlias, $requestedPath, $pathPrefix) + protected function needsCaseRedirect(URLAlias $loadedUrlAlias, string $requestedPath, string $pathPrefix): bool { // If requested path is excluded from tree root jail, compare it to loaded UrlAlias directly. if ($this->generator->isUriPrefixExcluded($requestedPath)) { @@ -247,15 +132,12 @@ protected function needsCaseRedirect(URLAlias $loadedUrlAlias, $requestedPath, $ /** * Returns the UrlAlias object to use, starting from the request. * - * @param $pathinfo - * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the path does not exist or is not valid for the given language - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ - protected function getUrlAlias($pathinfo) + protected function getUrlAlias(string $pathInfo): UrlAlias { - return $this->urlAliasService->lookup($pathinfo); + return $this->urlAliasService->lookup($pathInfo); } /** @@ -269,32 +151,23 @@ public function getRouteCollection(): RouteCollection } /** - * Generates a URL for a location, from the given parameters. - * - * It is possible to directly pass a Location object as the route name, as the ChainRouter allows it through ChainedRouterInterface. - * - * If $name is a route name, the "location" key in $parameters must be set to a valid {@see \Ibexa\Contracts\Core\Repository\Values\Content\Location} object. - * "locationId" can also be provided. - * - * If the generator is not able to generate the url, it must throw the RouteNotFoundException - * as documented below. - * - * @see UrlAliasRouter::supports() + * {@inheritDoc} * - * @param string $name The name of the route or a Location instance - * @param array $parameters An array of parameters - * @param int $referenceType The type of reference to be generated (one of the constants) + * The "location" key in $parameters must be set to a valid {@see \Ibexa\Contracts\Core\Repository\Values\Content\Location} object. + * "locationId" parameter can also be provided. * - * @throws \LogicException - * @throws \Symfony\Component\Routing\Exception\RouteNotFoundException - * @throws \InvalidArgumentException + * @param array $parameters An array of parameters * - * @return string The generated URL + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException * * @api */ - public function generate(string $name, array $parameters = [], int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string - { + public function generate( + string $name, + array $parameters = [], + int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH + ): string { if ($name === '' && array_key_exists(RouteObjectInterface::ROUTE_OBJECT, $parameters) && $this->supportsObject($parameters[RouteObjectInterface::ROUTE_OBJECT]) @@ -315,7 +188,7 @@ public function generate(string $name, array $parameters = [], int $referenceTyp ); } - $location = isset($parameters['location']) ? $parameters['location'] : $this->locationService->loadLocation($parameters['locationId']); + $location = $parameters['location'] ?? $this->locationService->loadLocation($parameters['locationId']); unset($parameters['location'], $parameters['locationId'], $parameters['viewType'], $parameters['layout']); return $this->generator->generate($location, $parameters, $referenceType); @@ -344,13 +217,13 @@ public function generate(string $name, array $parameters = [], int $referenceTyp throw new RouteNotFoundException('Could not match route'); } - public function setContext(RequestContext $context) + public function setContext(RequestContext $context): void { $this->requestContext = $context; $this->generator->setRequestContext($context); } - public function getContext() + public function getContext(): RequestContext { return $this->requestContext; } @@ -358,13 +231,15 @@ public function getContext() /** * Not supported. Please use matchRequest() instead. * - * @param $pathinfo + * @return array * * @throws \RuntimeException */ - public function match($pathinfo) + public function match(string $pathinfo): array { - throw new \RuntimeException("The UrlAliasRouter doesn't support the match() method. Use matchRequest() instead."); + throw new RuntimeException( + "The UrlAliasRouter doesn't support the match() method. Use matchRequest() instead." + ); } /** @@ -374,33 +249,169 @@ public function match($pathinfo) * resolved to a route, only whether the router can generate routes from * objects of this class. * - * @param mixed $name The route name or route object - * - * @return bool + * @param string|object $name The route name or route object */ - public function supports($name) + public function supports(string|object $name): bool { - return $name === static::URL_ALIAS_ROUTE_NAME || $this->supportsObject($name); + return $name === static::URL_ALIAS_ROUTE_NAME || (is_object($name) && $this->supportsObject($name)); } - private function supportsObject($object): bool + private function supportsObject(object $object): bool { return $object instanceof Location; } + public function getRouteDebugMessage(string $name, array $parameters = []): string + { + return $name; + } + + /** + * @param array $params An array of parameters + * + * @return array An array of parameters + * + * @throws \Ibexa\Contracts\Core\Exception\InvalidArgumentException + */ + private function buildParametersByUrlAliasType( + URLAlias $urlAlias, + array $params, + string $requestedPath, + string $pathPrefix + ): array { + return match ($urlAlias->type) { + URLAlias::LOCATION => $this->buildParametersForLocationUrlAlias( + $urlAlias, + $params, + $requestedPath, + $pathPrefix + ), + URLAlias::RESOURCE => $this->buildParametersForResourceUrlAlias( + $urlAlias, + $params, + $requestedPath, + $pathPrefix + ), + URLAlias::VIRTUAL => $this->buildParametersForVirtualUrlAlias( + $urlAlias, + $params, + $requestedPath, + $pathPrefix + ), + default => throw new \Ibexa\Contracts\Core\Exception\InvalidArgumentException( + '$urlAlias->type', + "Unknown URLAlias type: $urlAlias->type" + ) + }; + } + /** - * @see \Symfony\Cmf\Component\Routing\VersatileGeneratorInterface::getRouteDebugMessage() + * @param array $params An array of parameters + * + * @return array An array of parameters */ - public function getRouteDebugMessage($name, array $parameters = []) - { - if ($name instanceof RouteObjectInterface) { - return 'Route with key ' . $name->getRouteKey(); + private function buildParametersForLocationUrlAlias( + URLAlias $urlAlias, + array $params, + string $requestedPath, + string $pathPrefix + ): array { + $location = $this->generator->loadLocation($urlAlias->destination); + $params += [ + '_controller' => static::VIEW_ACTION, + 'contentId' => $location->contentId, + 'locationId' => $urlAlias->destination, + 'viewType' => ViewManagerInterface::VIEW_TYPE_FULL, + 'layout' => true, + ]; + + // For Location alias setup 301 redirect to Location's current URL when: + // 1. alias is history + // 2. alias is custom with forward flag true + // 3. requested URL is not case-sensitive equal with the one loaded + if ($urlAlias->isHistory === true || ($urlAlias->isCustom === true && $urlAlias->forward === true)) { + $params += [ + 'semanticPathinfo' => $this->generator->generate($location, []), + 'needsRedirect' => true, + // Specify not to prepend siteaccess while redirecting when applicable since it would be already present (see UrlAliasGenerator::doGenerate()) + 'prependSiteaccessOnRedirect' => false, + ]; + } elseif ($this->needsCaseRedirect($urlAlias, $requestedPath, $pathPrefix)) { + $params += [ + 'semanticPathinfo' => $this->removePathPrefix($urlAlias->path, $pathPrefix), + 'needsRedirect' => true, + ]; + + if ($urlAlias->destination instanceof Location) { + $params += ['locationId' => $urlAlias->destination->id]; + } + } + + $this->logger->info( + "UrlAlias matched location #{$urlAlias->destination}. Forwarding to ViewController" + ); + + return $params; + } + + /** + * @param array $params An array of parameters + * + * @return array An array of parameters + */ + private function buildParametersForResourceUrlAlias( + URLAlias $urlAlias, + array $params, + string $requestedPath, + string $pathPrefix + ): array { + // In URLAlias terms, "forward" means "redirect". + if ($urlAlias->forward) { + $params += [ + 'semanticPathinfo' => '/' . trim($urlAlias->destination, '/'), + 'needsRedirect' => true, + ]; + } elseif ($this->needsCaseRedirect($urlAlias, $requestedPath, $pathPrefix)) { + // Handle case-correction redirect + $params += [ + 'semanticPathinfo' => $this->removePathPrefix($urlAlias->path, $pathPrefix), + 'needsRedirect' => true, + ]; + } else { + $params += [ + 'semanticPathinfo' => '/' . trim($urlAlias->destination, '/'), + 'needsForward' => true, + ]; } - if ($name instanceof SymfonyRoute) { - return 'Route with pattern ' . $name->getPath(); + return $params; + } + + /** + * @param array $params An array of parameters + * + * @return array An array of parameters + */ + private function buildParametersForVirtualUrlAlias( + URLAlias $urlAlias, + array $params, + string $requestedPath, + string $pathPrefix + ): array { + // Handle case-correction redirect + if ($this->needsCaseRedirect($urlAlias, $requestedPath, $pathPrefix)) { + $params += [ + 'semanticPathinfo' => $this->removePathPrefix($urlAlias->path, $pathPrefix), + 'needsRedirect' => true, + ]; + } else { + // Virtual aliases should load the Content at homepage URL + $params += [ + 'semanticPathinfo' => '/', + 'needsForward' => true, + ]; } - return $name; + return $params; } } diff --git a/src/lib/MVC/Symfony/Routing/UrlWildcardRouter.php b/src/lib/MVC/Symfony/Routing/UrlWildcardRouter.php index dae115076e..6bf4300f80 100644 --- a/src/lib/MVC/Symfony/Routing/UrlWildcardRouter.php +++ b/src/lib/MVC/Symfony/Routing/UrlWildcardRouter.php @@ -10,37 +10,28 @@ use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; use Ibexa\Contracts\Core\Repository\URLWildcardService; use Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use Symfony\Cmf\Component\Routing\ChainedRouterInterface; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Exception\RouteNotFoundException; use Symfony\Component\Routing\Matcher\RequestMatcherInterface; use Symfony\Component\Routing\RequestContext; -use Symfony\Component\Routing\Route as SymfonyRoute; use Symfony\Component\Routing\RouteCollection; class UrlWildcardRouter implements ChainedRouterInterface, RequestMatcherInterface { - public const URL_ALIAS_ROUTE_NAME = 'ibexa.url.alias'; + public const string URL_ALIAS_ROUTE_NAME = 'ibexa.url.alias'; - /** @var \Ibexa\Contracts\Core\Repository\URLWildcardService */ - private $wildcardService; + private URLWildcardService $wildcardService; - /** @var \Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator */ - private $generator; + private UrlAliasGenerator $generator; - /** @var \Symfony\Component\Routing\RequestContext */ - private $requestContext; + private RequestContext $requestContext; - /** @var \Psr\Log\LoggerInterface */ - private $logger; + private LoggerInterface $logger; - /** - * @param \Ibexa\Contracts\Core\Repository\URLWildcardService $wildcardService - * @param \Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator $generator - * @param \Symfony\Component\Routing\RequestContext $requestContext - */ public function __construct( URLWildcardService $wildcardService, UrlAliasGenerator $generator, @@ -49,20 +40,16 @@ public function __construct( $this->wildcardService = $wildcardService; $this->generator = $generator; $this->requestContext = $requestContext; + $this->logger = new NullLogger(); } - /** - * @param \Psr\Log\LoggerInterface $logger - */ - public function setLogger($logger) + public function setLogger(LoggerInterface $logger): void { $this->logger = $logger; } /** - * @param \Symfony\Component\HttpFoundation\Request $request - * - * @return array An array of parameters + * @return array An array of parameters * * @throws \Symfony\Component\Routing\Exception\ResourceNotFoundException */ @@ -76,9 +63,7 @@ public function matchRequest(Request $request): array throw new ResourceNotFoundException($e->getMessage(), $e->getCode(), $e); } - if ($this->logger !== null) { - $this->logger->info("UrlWildcard matched. Destination URL: {$urlWildcardTranslationResult->uri}"); - } + $this->logger->info("UrlWildcard matched. Destination URL: $urlWildcardTranslationResult->uri"); // set translated path for the next router $request->attributes->set('semanticPathinfo', $urlWildcardTranslationResult->uri); @@ -97,29 +82,19 @@ public function getRouteCollection(): RouteCollection } /** - * @param string $name - * @param array $parameters - * @param int $referenceType - * - * @return string|void + * @param array $parameters */ - public function generate($name, $parameters = [], $referenceType = self::ABSOLUTE_PATH) + public function generate(string $name, array $parameters = [], int $referenceType = self::ABSOLUTE_PATH): string { throw new RouteNotFoundException('Could not match route'); } - /** - * @param \Symfony\Component\Routing\RequestContext $context - */ public function setContext(RequestContext $context): void { $this->requestContext = $context; $this->generator->setRequestContext($context); } - /** - * @return \Symfony\Component\Routing\RequestContext - */ public function getContext(): RequestContext { return $this->requestContext; @@ -128,13 +103,11 @@ public function getContext(): RequestContext /** * Not supported. Please use matchRequest() instead. * - * @param string $pathinfo - * - * @return array + * @return array * * @throws \RuntimeException */ - public function match($pathinfo): array + public function match(string $pathinfo): array { throw new \RuntimeException("The UrlWildcardRouter doesn't support the match() method. Use matchRequest() instead."); } @@ -145,29 +118,14 @@ public function match($pathinfo): array * This check does not need to look if the specific instance can be * resolved to a route, only whether the router can generate routes from * objects of this class. - * - * @param mixed $name The route name or route object - * - * @return bool */ - public function supports($name): bool + public function supports(string $name): bool { return $name === static::URL_ALIAS_ROUTE_NAME; } - /** - * @see Symfony\Cmf\Component\Routing\VersatileGeneratorInterface::getRouteDebugMessage() - */ - public function getRouteDebugMessage($name, array $parameters = []): string + public function getRouteDebugMessage(string $name, array $parameters = []): string { - if ($name instanceof RouteObjectInterface) { - return 'Route with key ' . $name->getRouteKey(); - } - - if ($name instanceof SymfonyRoute) { - return 'Route with pattern ' . $name->getPath(); - } - return $name; } } diff --git a/src/lib/MVC/Symfony/Security/Authentication/EventSubscriber/RepositoryUserAuthenticationSubscriber.php b/src/lib/MVC/Symfony/Security/Authentication/EventSubscriber/RepositoryUserAuthenticationSubscriber.php index cbfe3a4c8e..707578fbe3 100644 --- a/src/lib/MVC/Symfony/Security/Authentication/EventSubscriber/RepositoryUserAuthenticationSubscriber.php +++ b/src/lib/MVC/Symfony/Security/Authentication/EventSubscriber/RepositoryUserAuthenticationSubscriber.php @@ -18,6 +18,7 @@ use Psr\Log\NullLogger; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport; use Symfony\Component\Security\Http\Event\CheckPassportEvent; @@ -66,7 +67,7 @@ public function validateRepositoryUser(CheckPassportEvent $event): void } $user = $badge->getUser(); - if (!$user instanceof IbexaUserInterface) { + if (!$user instanceof IbexaUserInterface || !$user instanceof PasswordAuthenticatedUserInterface) { return; } diff --git a/src/lib/MVC/Symfony/Security/HttpUtils.php b/src/lib/MVC/Symfony/Security/HttpUtils.php index 24190d6087..4e1e8c725b 100644 --- a/src/lib/MVC/Symfony/Security/HttpUtils.php +++ b/src/lib/MVC/Symfony/Security/HttpUtils.php @@ -14,37 +14,34 @@ class HttpUtils extends BaseHttpUtils implements SiteAccessAware { - /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ - private $siteAccess; + private ?SiteAccess $siteAccess; - /** - * @param \Ibexa\Core\MVC\Symfony\SiteAccess|null $siteAccess - */ - public function setSiteAccess(SiteAccess $siteAccess = null) + public function setSiteAccess(?SiteAccess $siteAccess = null): void { $this->siteAccess = $siteAccess; } - private function analyzeLink($path) + private function analyzeLink(string $path): string { - if ($path[0] === '/' && $this->siteAccess->matcher instanceof SiteAccess\URILexer) { - $path = $this->siteAccess->matcher->analyseLink($path); + $matcher = $this->siteAccess?->matcher; + if ($path[0] === '/' && $matcher instanceof SiteAccess\URILexer) { + $path = $matcher->analyseLink($path); } return $path; } - public function generateUri($request, $path) + public function generateUri(Request $request, string $path): string { if ($this->isRouteName($path)) { - // Remove siteaccess attribute to avoid triggering reverse siteaccess lookup during link generation. + // Remove SiteAccess attribute to avoid triggering reverse SiteAccess lookup during link generation. $request->attributes->remove('siteaccess'); } return parent::generateUri($request, $this->analyzeLink($path)); } - public function checkRequestPath(Request $request, $path) + public function checkRequestPath(Request $request, string $path): bool { return parent::checkRequestPath($request, $this->analyzeLink($path)); } @@ -54,8 +51,8 @@ public function checkRequestPath(Request $request, $path) * * @return bool */ - private function isRouteName($path): bool + private function isRouteName(string $path): bool { - return $path && strpos($path, 'http') !== 0 && strpos($path, '/') !== 0; + return !empty($path) && !str_starts_with($path, 'http') && !str_starts_with($path, '/'); } } diff --git a/src/lib/MVC/Symfony/Security/InteractiveLoginToken.php b/src/lib/MVC/Symfony/Security/InteractiveLoginToken.php deleted file mode 100644 index a8e4417b6b..0000000000 --- a/src/lib/MVC/Symfony/Security/InteractiveLoginToken.php +++ /dev/null @@ -1,87 +0,0 @@ -originalTokenType = $originalTokenType; - } - - public function getOriginalTokenType(): string - { - return $this->originalTokenType; - } - - /** - * @return array{ - * string, - * mixed, - * null|\Symfony\Component\Security\Core\Authentication\Token\TokenInterface - * } $data - */ - public function __serialize(): array - { - return [ - $this->originalTokenType, - parent::__serialize(), - $this->originalToken, - ]; - } - - /** - * @param array{ - * string, - * mixed, - * 2?: \Symfony\Component\Security\Core\Authentication\Token\TokenInterface - * } $data - */ - public function __unserialize(array $data): void - { - if (isset($data[2])) { - [$this->originalTokenType, $parentData, $this->originalToken] = $data; - } else { - [$this->originalTokenType, $parentData] = $data; - } - - parent::__unserialize($parentData); - } - - public function setOriginalToken(TokenInterface $token): void - { - $this->originalToken = $token; - } - - public function getOriginalToken(): ?TokenInterface - { - return $this->originalToken; - } - - public function isAuthenticated(): bool - { - if (null !== $this->originalToken) { - return $this->originalToken->isAuthenticated(); - } - - return parent::isAuthenticated(); - } -} diff --git a/src/lib/MVC/Symfony/Security/User/APIUserProviderInterface.php b/src/lib/MVC/Symfony/Security/User/APIUserProviderInterface.php index 4d4a2a534f..dfd57ff395 100644 --- a/src/lib/MVC/Symfony/Security/User/APIUserProviderInterface.php +++ b/src/lib/MVC/Symfony/Security/User/APIUserProviderInterface.php @@ -12,6 +12,8 @@ /** * Interface adding Ibexa API specific methods to Symfony UserProviderInterface. + * + * @extends \Symfony\Component\Security\Core\User\UserProviderInterface<\Ibexa\Core\MVC\Symfony\Security\UserInterface> */ interface APIUserProviderInterface extends UserProviderInterface { diff --git a/src/lib/MVC/Symfony/Security/UserWrapped.php b/src/lib/MVC/Symfony/Security/UserWrapped.php index a417445ac6..8ef7b213e4 100644 --- a/src/lib/MVC/Symfony/Security/UserWrapped.php +++ b/src/lib/MVC/Symfony/Security/UserWrapped.php @@ -11,8 +11,9 @@ use Ibexa\Contracts\Core\Repository\Values\User\UserReference as APIUserReference; use Ibexa\Core\Repository\Values\User\UserReference; use InvalidArgumentException; +use LogicException; use Symfony\Component\Security\Core\User\EquatableInterface; -use Symfony\Component\Security\Core\User\UserInterface as CoreUserInterface; +use Symfony\Component\Security\Core\User\UserInterface; /** * This class represents a UserWrapped object. @@ -25,43 +26,34 @@ */ class UserWrapped implements ReferenceUserInterface, EquatableInterface { - /** @var \Symfony\Component\Security\Core\User\UserInterface */ - private $wrappedUser; + private UserInterface $wrappedUser; - /** @var \Ibexa\Contracts\Core\Repository\Values\User\User */ - private $apiUser; + private APIUser $apiUser; - /** @var \Ibexa\Contracts\Core\Repository\Values\User\UserReference */ - private $apiUserReference; + private APIUserReference $apiUserReference; - public function __construct(CoreUserInterface $wrappedUser, APIUser $apiUser) + public function __construct(UserInterface $wrappedUser, APIUser $apiUser) { $this->setWrappedUser($wrappedUser); $this->apiUser = $apiUser; $this->apiUserReference = new UserReference($apiUser->getUserId()); } - public function __toString() + public function __toString(): string { - return $this->wrappedUser->getUsername(); + return $this->wrappedUser->getUserIdentifier(); } - /** - * @param \Ibexa\Contracts\Core\Repository\Values\User\User $apiUser - */ - public function setAPIUser(APIUser $apiUser) + public function setAPIUser(APIUser $apiUser): void { $this->apiUser = $apiUser; $this->apiUserReference = new UserReference($apiUser->getUserId()); } - /** - * @return \Ibexa\Contracts\Core\Repository\Values\User\User - */ - public function getAPIUser() + public function getAPIUser(): APIUser { - if (!$this->apiUser instanceof APIUser) { - throw new \LogicException( + if (!isset($this->apiUser)) { + throw new LogicException( 'Attempted to get APIUser before it has been set by UserProvider, APIUser is not serialized to session' ); } @@ -69,35 +61,29 @@ public function getAPIUser() return $this->apiUser; } - /** - * @return \Ibexa\Contracts\Core\Repository\Values\User\UserReference - */ public function getAPIUserReference(): APIUserReference { return $this->apiUserReference; } /** - * @param \Symfony\Component\Security\Core\User\UserInterface $wrappedUser - * * @throws \InvalidArgumentException If $wrappedUser is instance of self or User to avoid duplicated APIUser in * session. */ - public function setWrappedUser(CoreUserInterface $wrappedUser) + public function setWrappedUser(UserInterface $wrappedUser): void { if ($wrappedUser instanceof self) { throw new InvalidArgumentException('Injecting UserWrapped in itself is not allowed to avoid recursion'); - } elseif ($wrappedUser instanceof User) { + } + + if ($wrappedUser instanceof User) { throw new InvalidArgumentException('Injecting a User into UserWrapped causes duplication of APIUser, which should be avoided for session serialization'); } $this->wrappedUser = $wrappedUser; } - /** - * @return \Symfony\Component\Security\Core\User\UserInterface - */ - public function getWrappedUser() + public function getWrappedUser(): UserInterface { return $this->wrappedUser; } @@ -107,27 +93,12 @@ public function getRoles(): array return $this->wrappedUser->getRoles(); } - public function getPassword() - { - return $this->wrappedUser->getPassword(); - } - - public function getSalt() - { - return $this->wrappedUser->getSalt(); - } - - public function getUsername() - { - return $this->wrappedUser->getUsername(); - } - - public function eraseCredentials() + public function eraseCredentials(): void { $this->wrappedUser->eraseCredentials(); } - public function isEqualTo(CoreUserInterface $user) + public function isEqualTo(UserInterface $user): bool { if ($user instanceof self) { $user = $user->wrappedUser; @@ -143,4 +114,9 @@ public function __sleep(): array { return ['wrappedUser', 'apiUserReference']; } + + public function getUserIdentifier(): string + { + return $this->wrappedUser->getUserIdentifier(); + } } diff --git a/src/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php b/src/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php index 2f4e163ce0..ac6715402c 100644 --- a/src/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php +++ b/src/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php @@ -49,7 +49,7 @@ public function __construct(DocParser $docParser, FileSourceFactory $fileSourceF $this->traverser->addVisitor($this); } - public function enterNode(Node $node): void + public function enterNode(Node $node): null { $methodCallNodeName = null; if ($node instanceof Node\Expr\MethodCall) { @@ -61,21 +61,24 @@ public function enterNode(Node $node): void ) { $this->previousNode = $node; - return; + return null; } $ignore = $this->isIgnore($node); if (!$node->args[0]->value instanceof String_) { - if ($ignore) { - return; + if (!$ignore) { + $message = sprintf( + 'Can only extract the translation id from a scalar string, but got "%s". Please refactor your code to make it extractable, or add the doc comment /** @Ignore */ to this code element (in %s on line %d).', + get_class($node->args[0]->value), + $this->file, + $node->args[0]->value->getLine() + ); + + $this->logger?->error($message); } - $message = sprintf('Can only extract the translation id from a scalar string, but got "%s". Please refactor your code to make it extractable, or add the doc comment /** @Ignore */ to this code element (in %s on line %d).', get_class($node->args[0]->value), $this->file, $node->args[0]->value->getLine()); - - $this->logger->error($message); - - return; + return null; } $id = $node->args[0]->value->value; @@ -83,6 +86,8 @@ public function enterNode(Node $node): void $message = new Message($id, $this->defaultDomain); $message->addSource($this->fileSourceFactory->create($this->file, $node->getLine())); $this->catalogue->add($message); + + return null; } public function visitPhpFile(SplFileInfo $file, MessageCatalogue $catalogue, array $ast): void diff --git a/src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php b/src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php index 37ab1998fa..39916c24bd 100644 --- a/src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php +++ b/src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php @@ -17,12 +17,12 @@ use JMS\TranslationBundle\Model\MessageCatalogue; use JMS\TranslationBundle\Translation\Extractor\FileVisitorInterface; use JMS\TranslationBundle\Translation\FileSourceFactory; -use PhpParser\Comment\Doc; use PhpParser\Node; use PhpParser\Node\Scalar\String_; use PhpParser\NodeTraverser; use PhpParser\NodeVisitor; use Psr\Log\LoggerInterface; +use SplFileInfo; use Twig\Node\Node as TwigNode; /** @@ -30,46 +30,28 @@ */ class TranslatableExceptionsFileVisitor implements LoggerAwareInterface, FileVisitorInterface, NodeVisitor { - /** @var \JMS\TranslationBundle\Translation\FileSourceFactory */ - private $fileSourceFactory; + private FileSourceFactory $fileSourceFactory; - /** @var \PhpParser\NodeTraverser */ - private $traverser; + private NodeTraverser $traverser; - /** @var \JMS\TranslationBundle\Model\MessageCatalogue */ - private $catalogue; + private MessageCatalogue $catalogue; - /** @var \SplFileInfo */ - private $file; + private SplFileInfo $file; - /** @var \Doctrine\Common\Annotations\DocParser */ - private $docParser; + private DocParser $docParser; - /** @var \Psr\Log\LoggerInterface */ - private $logger; + private ?LoggerInterface $logger = null; - /** @var \PhpParser\Node */ - private $previousNode; + private Node $previousNode; - /** @var string */ - protected $defaultDomain = 'ibexa_repository_exceptions'; + protected string $defaultDomain = 'ibexa_repository_exceptions'; - /** - * Methods and "domain" parameter offset to extract from PHP code. - * - * @var array method => position of the "domain" parameter - */ - protected $exceptionsToExtractFrom = [ + /** @var string[] exception class names, lowercase */ + protected array $exceptionsToExtractFrom = [ 'contentvalidationexception', 'forbiddenexception', ]; - /** - * DefaultPhpFileExtractor constructor. - * - * @param \Doctrine\Common\Annotations\DocParser $docParser - * @param \JMS\TranslationBundle\Translation\FileSourceFactory $fileSourceFactory - */ public function __construct(DocParser $docParser, FileSourceFactory $fileSourceFactory) { $this->docParser = $docParser; @@ -78,40 +60,33 @@ public function __construct(DocParser $docParser, FileSourceFactory $fileSourceF $this->traverser->addVisitor($this); } - /** - * @param \Psr\Log\LoggerInterface $logger - */ - public function setLogger(LoggerInterface $logger) + public function setLogger(LoggerInterface $logger): void { $this->logger = $logger; } - /** - * @param \PhpParser\Node $node - */ - public function enterNode(Node $node) + public function enterNode(Node $node): null { - if (!$node instanceof Node\Stmt\Throw_ - || !$node->expr instanceof Node\Expr\New_) { + if (!$node instanceof Node\Expr\Throw_ + || !$node->expr instanceof Node\Expr\New_ + || !$node->expr->class instanceof Node\Name + ) { $this->previousNode = $node; - return; + return null; } - $exceptionClass = $node->expr->class->parts[0]; - if (!in_array(strtolower($exceptionClass), array_map('strtolower', $this->exceptionsToExtractFrom))) { + $exceptionClass = $node->expr->class->getParts()[0]; + if (!in_array(strtolower($exceptionClass), $this->exceptionsToExtractFrom, true)) { $this->previousNode = $node; - return; + return null; } $node = $node->expr; $ignore = false; $desc = $meaning = null; if (null !== $docComment = $this->getDocCommentForNode($node)) { - if ($docComment instanceof Doc) { - $docComment = $docComment->getText(); - } foreach ($this->docParser->parse($docComment, 'file ' . $this->file . ' near line ' . $node->getLine()) as $annot) { if ($annot instanceof Ignore) { $ignore = true; @@ -125,15 +100,15 @@ public function enterNode(Node $node) if (!$node->args[0]->value instanceof String_) { if ($ignore) { - return; + return null; } $message = sprintf('Can only extract the translation ID from a scalar string, but got "%s". Refactor your code to make it extractable, or add the doc comment /** @Ignore */ to this code element (in %s on line %d).', get_class($node->args[0]->value), $this->file, $node->args[0]->value->getLine()); - if ($this->logger) { + if (null !== $this->logger) { $this->logger->error($message); - return; + return null; } throw new RuntimeException($message); @@ -146,46 +121,42 @@ public function enterNode(Node $node) $message->setMeaning($meaning); $message->addSource($this->fileSourceFactory->create($this->file, $node->getLine())); $this->catalogue->add($message); + + return null; } /** * @param \SplFileInfo $file * @param \JMS\TranslationBundle\Model\MessageCatalogue $catalogue - * @param array $ast + * @param array<\PhpParser\Node> $ast */ - public function visitPhpFile(\SplFileInfo $file, MessageCatalogue $catalogue, array $ast) + public function visitPhpFile(SplFileInfo $file, MessageCatalogue $catalogue, array $ast): void { $this->file = $file; $this->catalogue = $catalogue; $this->traverser->traverse($ast); } - /** - * @param array $nodes - */ - public function beforeTraverse(array $nodes) + public function beforeTraverse(array $nodes): null { + return null; } - /** - * @param \PhpParser\Node $node - */ - public function leaveNode(Node $node) + public function leaveNode(Node $node): null { + return null; } - /** - * @param array $nodes - */ - public function afterTraverse(array $nodes) + public function afterTraverse(array $nodes): null { + return null; } /** * @param \SplFileInfo $file * @param \JMS\TranslationBundle\Model\MessageCatalogue $catalogue */ - public function visitFile(\SplFileInfo $file, MessageCatalogue $catalogue) + public function visitFile(SplFileInfo $file, MessageCatalogue $catalogue): void { } @@ -194,16 +165,14 @@ public function visitFile(\SplFileInfo $file, MessageCatalogue $catalogue) * @param \JMS\TranslationBundle\Model\MessageCatalogue $catalogue * @param \Twig\Node\Node $ast */ - public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, TwigNode $ast) + public function visitTwigFile(SplFileInfo $file, MessageCatalogue $catalogue, TwigNode $ast): void { } /** - * @param \PhpParser\Node $node - * - * @return string|null + * @param \PhpParser\Node\Expr\New_ $node */ - private function getDocCommentForNode(Node $node) + private function getDocCommentForNode(Node $node): ?string { // check if there is a doc comment for the ID argument // ->trans(/** @Desc("FOO") */ 'my.id') @@ -217,12 +186,10 @@ private function getDocCommentForNode(Node $node) // /** @Desc("FOO") */ $translator->trans('my.id') if (null !== $comment = $node->getDocComment()) { return $comment->getText(); - } elseif (null !== $this->previousNode && $this->previousNode->getDocComment() !== null) { - $comment = $this->previousNode->getDocComment(); - - return is_object($comment) ? $comment->getText() : $comment; } - return null; + return isset($this->previousNode) && ($comment = $this->previousNode->getDocComment()) !== null + ? $comment->getText() + : null; } } diff --git a/src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php b/src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php index 076157b360..40884bc9e8 100644 --- a/src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php +++ b/src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php @@ -30,39 +30,21 @@ */ class ValidationErrorFileVisitor implements LoggerAwareInterface, FileVisitorInterface, NodeVisitor { - /** @var \JMS\TranslationBundle\Translation\FileSourceFactory */ - private $fileSourceFactory; + private FileSourceFactory $fileSourceFactory; - /** @var \PhpParser\NodeTraverser */ - private $traverser; + private NodeTraverser $traverser; - /** @var \JMS\TranslationBundle\Model\MessageCatalogue */ - private $catalogue; + private MessageCatalogue $catalogue; - /** @var \SplFileInfo */ - private $file; + private \SplFileInfo $file; - /** @var \Doctrine\Common\Annotations\DocParser */ - private $docParser; + private DocParser $docParser; - /** @var \Psr\Log\LoggerInterface */ - private $logger; + private LoggerInterface $logger; - /** @var \PhpParser\Node */ - private $previousNode; + private Node $previousNode; - /** @var string */ - protected $defaultDomain = 'ibexa_repository_exceptions'; - - /** - * Methods and "domain" parameter offset to extract from PHP code. - * - * @var array method => position of the "domain" parameter - */ - protected $classToExtractFrom = [ - 'contentvalidationexception', - 'forbiddenexception', - ]; + protected string $defaultDomain = 'ibexa_repository_exceptions'; /** * DefaultPhpFileExtractor constructor. @@ -86,25 +68,20 @@ public function setLogger(LoggerInterface $logger) $this->logger = $logger; } - /** - * @param \PhpParser\Node $node - */ - public function enterNode(Node $node) + public function enterNode(Node $node): null { if (!$node instanceof Node\Expr\New_ - || !is_string($node->class) - || strtolower($node->class) !== 'validationerror') { + || !$node->class instanceof Node\Name + || strtolower((string)$node->class) !== 'validationerror' + ) { $this->previousNode = $node; - return; + return null; } $ignore = false; $desc = $meaning = null; if (null !== $docComment = $this->getDocCommentForNode($node)) { - if ($docComment instanceof Doc) { - $docComment = $docComment->getText(); - } foreach ($this->docParser->parse($docComment, 'file ' . $this->file . ' near line ' . $node->getLine()) as $annot) { if ($annot instanceof Ignore) { $ignore = true; @@ -118,15 +95,15 @@ public function enterNode(Node $node) if (!$node->args[0]->value instanceof String_) { if ($ignore) { - return; + return null; } $message = sprintf('Can only extract the translation ID from a scalar string, but got "%s". Refactor your code to make it extractable, or add the doc comment /** @Ignore */ to this code element (in %s on line %d).', get_class($node->args[0]->value), $this->file, $node->args[0]->value->getLine()); - if ($this->logger) { + if (isset($this->logger)) { $this->logger->error($message); - return; + return null; } throw new RuntimeException($message); @@ -139,53 +116,49 @@ public function enterNode(Node $node) $this->catalogue->add($message); // plural - if ($node->args[1]->value instanceof String_) { + if (isset($node->args[1]) && $node->args[1]->value instanceof String_) { $message = new Message($node->args[1]->value->value, $this->defaultDomain); $message->setDesc($desc); $message->setMeaning($meaning); $message->addSource($this->fileSourceFactory->create($this->file, $node->getLine())); $this->catalogue->add($message); } + + return null; } /** * @param \SplFileInfo $file * @param \JMS\TranslationBundle\Model\MessageCatalogue $catalogue - * @param array $ast + * @param \PhpParser\Node\Stmt[] $ast */ - public function visitPhpFile(\SplFileInfo $file, MessageCatalogue $catalogue, array $ast) + public function visitPhpFile(\SplFileInfo $file, MessageCatalogue $catalogue, array $ast): void { $this->file = $file; $this->catalogue = $catalogue; $this->traverser->traverse($ast); } - /** - * @param array $nodes - */ - public function beforeTraverse(array $nodes) + public function beforeTraverse(array $nodes): null { + return null; } - /** - * @param \PhpParser\Node $node - */ - public function leaveNode(Node $node) + public function leaveNode(Node $node): null { + return null; } - /** - * @param array $nodes - */ - public function afterTraverse(array $nodes) + public function afterTraverse(array $nodes): null { + return null; } /** * @param \SplFileInfo $file * @param \JMS\TranslationBundle\Model\MessageCatalogue $catalogue */ - public function visitFile(\SplFileInfo $file, MessageCatalogue $catalogue) + public function visitFile(\SplFileInfo $file, MessageCatalogue $catalogue): void { } @@ -194,16 +167,16 @@ public function visitFile(\SplFileInfo $file, MessageCatalogue $catalogue) * @param \JMS\TranslationBundle\Model\MessageCatalogue $catalogue * @param \Twig\Node\Node $ast */ - public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, TwigNode $ast) + public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, TwigNode $ast): void { } /** - * @param \PhpParser\Node $node + * @param \PhpParser\Node\Expr\New_ $node * * @return string|null */ - private function getDocCommentForNode(Node $node) + private function getDocCommentForNode(Node $node): ?string { // check if there is a doc comment for the ID argument // ->trans(/** @Desc("FOO") */ 'my.id') @@ -217,12 +190,11 @@ private function getDocCommentForNode(Node $node) // /** @Desc("FOO") */ $translator->trans('my.id') if (null !== $comment = $node->getDocComment()) { return $comment->getText(); - } elseif (null !== $this->previousNode && $this->previousNode->getDocComment() !== null) { - $comment = $this->previousNode->getDocComment(); - - return is_object($comment) ? $comment->getText() : $comment; } - return null; + return + ($comment = $this->previousNode->getDocComment()) !== null + ? $comment->getText() + : null; } } diff --git a/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php b/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php index 2817deba58..19aef03e1c 100644 --- a/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php +++ b/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php @@ -15,34 +15,39 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Search\SpellcheckResult; use Pagerfanta\Adapter\AdapterInterface; +/** + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService + * + * @template T of \Ibexa\Contracts\Core\Repository\Values\ValueObject + * + * @phpstan-implements \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit> + * @phpstan-implements \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit> + */ abstract class AbstractSearchResultAdapter implements AdapterInterface, SearchResultAdapter { - /** @var \Ibexa\Contracts\Core\Repository\SearchService */ - private $searchService; + private SearchService $searchService; - /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query */ - private $query; + private Query $query; - /** @var array */ - private $languageFilter; + /** @phpstan-var TSearchLanguageFilter */ + private array $languageFilter; - /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection|null */ - private $aggregations; + private ?AggregationResultCollection $aggregations = null; - /** @var float|null */ - private $time; + private ?float $time; - /** @var bool|null */ - private $timedOut; + private ?bool $timedOut; - /** @var float|null */ - private $maxScore; + private ?float $maxScore; - /** @var int|null */ - private $totalCount; + /** @phpstan-var int<0, max>|null */ + private ?int $totalCount; private ?SpellcheckResult $spellcheck = null; + /** + * @phpstan-param TSearchLanguageFilter $languageFilter + */ public function __construct(Query $query, SearchService $searchService, array $languageFilter = []) { $this->query = $query; @@ -50,12 +55,7 @@ public function __construct(Query $query, SearchService $searchService, array $l $this->languageFilter = $languageFilter; } - /** - * Returns the number of results. - * - * @return int The number of results. - */ - public function getNbResults() + public function getNbResults(): int { if (isset($this->totalCount)) { return $this->totalCount; @@ -74,18 +74,13 @@ public function getNbResults() $this->languageFilter ); - return $this->totalCount = $searchResults->totalCount; + return $this->totalCount = $searchResults->totalCount ?? 0; } /** * Returns a slice of the results, as SearchHit objects. - * - * @param int $offset The offset. - * @param int $length The length. - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit[] */ - public function getSlice($offset, $length) + public function getSlice(int $offset, int $length): iterable { $query = clone $this->query; $query->offset = $offset; @@ -167,6 +162,11 @@ public function getMaxScore(): ?float return $this->maxScore; } + /** + * @phpstan-param TSearchLanguageFilter $languageFilter + * + * @phpstan-return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + */ abstract protected function executeQuery( SearchService $searchService, Query $query, diff --git a/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php b/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php index 97684cd854..9bc7e2e668 100644 --- a/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php +++ b/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php @@ -14,22 +14,24 @@ use Ibexa\Core\Pagination\Pagerfanta\ContentSearchHitAdapter; use Ibexa\Core\Pagination\Pagerfanta\FixedSearchResultHitAdapter; use Ibexa\Core\Pagination\Pagerfanta\LocationSearchHitAdapter; -use Pagerfanta\Adapter\AdapterInterface; +use Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter; /** * @internal + * + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService */ final class SearchHitAdapterFactory implements SearchHitAdapterFactoryInterface { /** @var \Ibexa\Contracts\Core\Repository\SearchService */ - private $searchService; + private SearchService $searchService; public function __construct(SearchService $searchService) { $this->searchService = $searchService; } - public function createAdapter(Query $query, array $languageFilter = []): AdapterInterface + public function createAdapter(Query $query, array $languageFilter = []): SearchResultAdapter { if ($query instanceof LocationQuery) { return new LocationSearchHitAdapter($query, $this->searchService, $languageFilter); @@ -38,7 +40,7 @@ public function createAdapter(Query $query, array $languageFilter = []): Adapter return new ContentSearchHitAdapter($query, $this->searchService, $languageFilter); } - public function createFixedAdapter(Query $query, array $languageFilter = []): AdapterInterface + public function createFixedAdapter(Query $query, array $languageFilter = []): SearchResultAdapter { if ($query instanceof LocationQuery) { $searchResults = $this->searchService->findLocations($query, $languageFilter); diff --git a/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php b/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php index 7717cfc618..98d69a24b1 100644 --- a/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php +++ b/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php @@ -9,14 +9,28 @@ namespace Ibexa\Core\Pagination\Pagerfanta\AdapterFactory; use Ibexa\Contracts\Core\Repository\Values\Content\Query; -use Pagerfanta\Adapter\AdapterInterface; +use Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter; /** * @internal + * + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService */ interface SearchHitAdapterFactoryInterface { - public function createAdapter(Query $query, array $languageFilter = []): AdapterInterface; + /** + * @phpstan-param TSearchLanguageFilter $languageFilter + * + * @phpstan-return \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\ValueObject> + */ + public function createAdapter(Query $query, array $languageFilter = []): SearchResultAdapter; - public function createFixedAdapter(Query $query, array $languageFilter = []): AdapterInterface; + /** + * @phpstan-return \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\ValueObject> + * + * @phpstan-param TSearchLanguageFilter $languageFilter + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function createFixedAdapter(Query $query, array $languageFilter = []): SearchResultAdapter; } diff --git a/src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php b/src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php index 6a4f6f413e..739af7b21a 100644 --- a/src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php +++ b/src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php @@ -14,21 +14,26 @@ /** * Pagerfanta adapter for content filtering. + * + * @implements \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\Content\Content> + * + * @phpstan-import-type TFilteringLanguageFilter from \Ibexa\Contracts\Core\Repository\ContentService */ final class ContentFilteringAdapter implements AdapterInterface { - /** @var \Ibexa\Contracts\Core\Repository\ContentService */ - private $contentService; + private ContentService $contentService; - /** @var \Ibexa\Contracts\Core\Repository\Values\Filter\Filter */ - private $filter; + private Filter $filter; - /** @var array|null */ - private $languageFilter; + /** @var TFilteringLanguageFilter|null */ + private ?array $languageFilter; - /** @var int|null */ - private $totalCount; + /** @phpstan-var int<0, max>|null */ + private ?int $totalCount = null; + /** + * @param array|null $languageFilter + */ public function __construct( ContentService $contentService, Filter $filter, @@ -48,6 +53,11 @@ public function getNbResults(): int return $this->totalCount; } + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentList + */ public function getSlice($offset, $length): iterable { $selectFilter = clone $this->filter; diff --git a/src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php b/src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php index 2f50492f11..d159a4e15d 100644 --- a/src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php +++ b/src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php @@ -7,27 +7,67 @@ namespace Ibexa\Core\Pagination\Pagerfanta; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; +use Pagerfanta\Adapter\AdapterInterface; + /** * Pagerfanta adapter for Ibexa content search. * Will return results as Content objects. + * + * @implements \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\Content\Content> + * @implements \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\Content> + * + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService */ -class ContentSearchAdapter extends ContentSearchHitAdapter +class ContentSearchAdapter implements AdapterInterface, SearchResultAdapter { + private ContentSearchHitAdapter $contentSearchHitAdapter; + + /** + * @phpstan-param TSearchLanguageFilter $languageFilter + */ + public function __construct(Query $query, SearchService $searchService, array $languageFilter = []) + { + $this->contentSearchHitAdapter = new ContentSearchHitAdapter($query, $searchService, $languageFilter); + } + /** * Returns a slice of the results as Content objects. - * - * @param int $offset The offset. - * @param int $length The length. - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content[] */ - public function getSlice($offset, $length) + public function getSlice(int $offset, int $length): iterable { $list = []; - foreach (parent::getSlice($offset, $length) as $hit) { + foreach ($this->contentSearchHitAdapter->getSlice($offset, $length) as $hit) { $list[] = $hit->valueObject; } return $list; } + + public function getNbResults(): int + { + return $this->contentSearchHitAdapter->getNbResults(); + } + + public function getAggregations(): AggregationResultCollection + { + return $this->contentSearchHitAdapter->getAggregations(); + } + + public function getTime(): ?float + { + return $this->contentSearchHitAdapter->getTime(); + } + + public function getTimedOut(): ?bool + { + return $this->contentSearchHitAdapter->getTimedOut(); + } + + public function getMaxScore(): ?float + { + return $this->contentSearchHitAdapter->getMaxScore(); + } } diff --git a/src/lib/Pagination/Pagerfanta/ContentSearchHitAdapter.php b/src/lib/Pagination/Pagerfanta/ContentSearchHitAdapter.php index 00e5b02b83..ab06fa34c0 100644 --- a/src/lib/Pagination/Pagerfanta/ContentSearchHitAdapter.php +++ b/src/lib/Pagination/Pagerfanta/ContentSearchHitAdapter.php @@ -14,9 +14,14 @@ /** * Pagerfanta adapter for Ibexa content search. * Will return results as SearchHit objects. + * + * @extends \Ibexa\Core\Pagination\Pagerfanta\AbstractSearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\Content> */ class ContentSearchHitAdapter extends AbstractSearchResultAdapter { + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ protected function executeQuery( SearchService $searchService, Query $query, diff --git a/src/lib/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php b/src/lib/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php index 343f463b11..34df32a609 100644 --- a/src/lib/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php +++ b/src/lib/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php @@ -11,11 +11,19 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +/** + * @template TSearchHitValueObject of \Ibexa\Contracts\Core\Repository\Values\ValueObject + * + * @implements \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit> + */ final class FixedSearchResultHitAdapter implements SearchResultAdapter { - /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult */ - private $searchResult; + /** @phpstan-var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult */ + private SearchResult $searchResult; + /** + * @phpstan-param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult $searchResult + */ public function __construct(SearchResult $searchResult) { $this->searchResult = $searchResult; @@ -26,7 +34,7 @@ public function getNbResults(): int return $this->searchResult->totalCount ?? -1; } - public function getSlice($offset, $length) + public function getSlice(int $offset, int $length): iterable { return $this->searchResult->searchHits; } diff --git a/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php b/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php index ef4a0cea3e..dc3bc9fc6e 100644 --- a/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php +++ b/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php @@ -12,19 +12,23 @@ use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; use Pagerfanta\Adapter\AdapterInterface; +/** + * @implements \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\Content\Location> + * + * @phpstan-import-type TFilteringLanguageFilter from \Ibexa\Contracts\Core\Repository\LocationService + */ final class LocationFilteringAdapter implements AdapterInterface { - /** @var \Ibexa\Contracts\Core\Repository\LocationService */ - private $locationService; + private LocationService $locationService; /** @var \Ibexa\Contracts\Core\Repository\Values\Filter\Filter */ - private $filter; + private Filter $filter; - /** @var array|null */ - private $languageFilter; + /** @phpstan-var TFilteringLanguageFilter|null */ + private ?array $languageFilter; - /** @var int|null */ - private $totalCount; + /** @phpstan-var int<0, max>|null */ + private ?int $totalCount = null; public function __construct( LocationService $locationService, diff --git a/src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php b/src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php index 1b35248a74..160c76e9fb 100644 --- a/src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php +++ b/src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php @@ -7,27 +7,69 @@ namespace Ibexa\Core\Pagination\Pagerfanta; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; +use Pagerfanta\Adapter\AdapterInterface; + /** * Pagerfanta adapter for Ibexa content search. * Will return results as Location objects. + * + * @implements \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\Content\Location> + * @implements \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\Location> + * + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService */ -class LocationSearchAdapter extends LocationSearchHitAdapter +class LocationSearchAdapter implements AdapterInterface, SearchResultAdapter { + private LocationSearchHitAdapter $locationSearchHitAdapter; + + /** + * @phpstan-param TSearchLanguageFilter $languageFilter + */ + public function __construct(LocationQuery $query, SearchService $searchService, array $languageFilter = []) + { + $this->locationSearchHitAdapter = new LocationSearchHitAdapter($query, $searchService, $languageFilter); + } + /** * Returns a slice of the results as Location objects. * - * @param int $offset The offset. - * @param int $length The length. - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location[] + * @phpstan-return iterable, \Ibexa\Contracts\Core\Repository\Values\Content\Location> */ - public function getSlice($offset, $length) + public function getSlice(int $offset, int $length): iterable { $list = []; - foreach (parent::getSlice($offset, $length) as $hit) { + foreach ($this->locationSearchHitAdapter->getSlice($offset, $length) as $hit) { $list[] = $hit->valueObject; } return $list; } + + public function getNbResults(): int + { + return $this->locationSearchHitAdapter->getNbResults(); + } + + public function getAggregations(): AggregationResultCollection + { + return $this->locationSearchHitAdapter->getAggregations(); + } + + public function getTime(): ?float + { + return $this->locationSearchHitAdapter->getTime(); + } + + public function getTimedOut(): ?bool + { + return $this->locationSearchHitAdapter->getTimedOut(); + } + + public function getMaxScore(): ?float + { + return $this->locationSearchHitAdapter->getMaxScore(); + } } diff --git a/src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php b/src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php index 636a1f402c..fad3f40a59 100644 --- a/src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php +++ b/src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php @@ -15,6 +15,8 @@ /** * Pagerfanta adapter for Ibexa location search. * Will return results as SearchHit objects. + * + * @extends \Ibexa\Core\Pagination\Pagerfanta\AbstractSearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\Location> */ class LocationSearchHitAdapter extends AbstractSearchResultAdapter { @@ -24,10 +26,12 @@ public function __construct(LocationQuery $query, SearchService $searchService, } /** - * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery $query + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ protected function executeQuery(SearchService $searchService, Query $query, array $languageFilter): SearchResult { + assert($query instanceof LocationQuery); + return $searchService->findLocations($query, $languageFilter); } } diff --git a/src/lib/Pagination/Pagerfanta/Pagerfanta.php b/src/lib/Pagination/Pagerfanta/Pagerfanta.php index e3b5b9a5f2..ac84e931fb 100644 --- a/src/lib/Pagination/Pagerfanta/Pagerfanta.php +++ b/src/lib/Pagination/Pagerfanta/Pagerfanta.php @@ -11,30 +11,38 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; use Pagerfanta\Pagerfanta as BasePagerfanta; +/** + * @template TSearchResultAdapter + * + * @extends \Pagerfanta\Pagerfanta + */ final class Pagerfanta extends BasePagerfanta { - public function __construct(SearchResultAdapter $adapter) + /** + * @phpstan-param \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter $searchResultAdapter + */ + public function __construct(private readonly SearchResultAdapter $searchResultAdapter) { - parent::__construct($adapter); + parent::__construct($this->searchResultAdapter); } public function getAggregations(): AggregationResultCollection { - return $this->getAdapter()->getAggregations(); + return $this->searchResultAdapter->getAggregations(); } public function getTime(): ?float { - return $this->getAdapter()->getTime(); + return $this->searchResultAdapter->getTime(); } public function getTimedOut(): ?bool { - return $this->getAdapter()->getTimedOut(); + return $this->searchResultAdapter->getTimedOut(); } public function getMaxScore(): ?float { - return $this->getAdapter()->getMaxScore(); + return $this->searchResultAdapter->getMaxScore(); } } diff --git a/src/lib/Pagination/Pagerfanta/SearchResultAdapter.php b/src/lib/Pagination/Pagerfanta/SearchResultAdapter.php index 5b0acd0acf..42603a01e6 100644 --- a/src/lib/Pagination/Pagerfanta/SearchResultAdapter.php +++ b/src/lib/Pagination/Pagerfanta/SearchResultAdapter.php @@ -15,6 +15,10 @@ * Contract for SearchService based adapters. * * @see \Ibexa\Contracts\Core\Repository\SearchService + * + * @template-covariant T + * + * @extends \Pagerfanta\Adapter\AdapterInterface */ interface SearchResultAdapter extends AdapterInterface { diff --git a/src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php b/src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php index 6ac7994ce1..c3960ef0ba 100644 --- a/src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php +++ b/src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php @@ -8,6 +8,7 @@ namespace Ibexa\Core\Persistence\Cache\Adapter; +use Ibexa\Contracts\Core\Exception\InvalidArgumentException; use Psr\Cache\CacheItemInterface; use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface; use Symfony\Component\Cache\CacheItem; @@ -16,34 +17,34 @@ /** * Internal proxy adapter invalidating our isolated in-memory cache, and defer shared pool changes during transactions. * - * @internal For type hinting inside {@see \Ibexa\Core\Persistence\Cache\}. For external, type hint on TagAwareAdapterInterface. + * @internal For type hinting inside {@see \Ibexa\Core\Persistence\Cache\}. For external, type hint on + * {@see \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface}. */ class TransactionalInMemoryCacheAdapter implements TransactionAwareAdapterInterface { - /** @var \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface */ - protected $sharedPool; + protected TagAwareAdapterInterface $sharedPool; - /** @var \Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache[] */ - private $inMemoryPools; + /** @var iterable<\Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache> */ + private iterable $inMemoryPools; /** @var int */ - protected $transactionDepth; + protected int $transactionDepth; - /** @var array To be unique and simplify lookup hash key is cache tag, value is only true value */ - protected $deferredTagsInvalidation; + /** @var array To be unique and simplify lookup hash key is cache tag, value is only true value */ + protected array $deferredTagsInvalidation; - /** @var array To be unique and simplify lookup hash key is cache key, value is only true value */ - protected $deferredItemsDeletion; + /** @var array To be unique and simplify lookup hash key is cache key, value is only true value */ + protected array $deferredItemsDeletion; /** @var \Closure Callback for use by {@see markItemsAsDeferredMissIfNeeded()} when items are misses by deferred action */ - protected $setCacheItemAsMiss; + protected \Closure $setCacheItemAsMiss; /** * @param \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface $sharedPool * @param \Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache[] $inMemoryPools * @param int $transactionDepth - * @param array $deferredTagsInvalidation - * @param array $deferredItemsDeletion + * @param array $deferredTagsInvalidation + * @param array $deferredItemsDeletion */ public function __construct( TagAwareAdapterInterface $sharedPool, @@ -69,29 +70,31 @@ static function (CacheItem $item) { } /** - * {@inheritdoc} + * @throws \Ibexa\Contracts\Core\Exception\InvalidArgumentException + * @throws \Psr\Cache\InvalidArgumentException */ - public function getItem($key) + public function getItem($key): CacheItem { - return $this->markItemsAsDeferredMissIfNeeded( - [$this->sharedPool->getItem($key)] - )[0]; + $item = current( + $this->markItemsAsDeferredMissIfNeeded( + [$key => $this->sharedPool->getItem($key)] + ) + ); + if ($item === false) { + throw new InvalidArgumentException('$key', "Unknown cache key: $key"); + } + + return $item; } - /** - * {@inheritdoc} - */ - public function getItems(array $keys = []) + public function getItems(array $keys = []): iterable { return $this->markItemsAsDeferredMissIfNeeded( $this->sharedPool->getItems($keys) ); } - /** - * {@inheritdoc} - */ - public function hasItem($key) + public function hasItem(string $key): bool { if (isset($this->deferredItemsDeletion[$key])) { return false; @@ -100,10 +103,7 @@ public function hasItem($key) return $this->sharedPool->hasItem($key); } - /** - * {@inheritdoc} - */ - public function deleteItem($key) + public function deleteItem(string $key): bool { foreach ($this->inMemoryPools as $inMemory) { $inMemory->deleteMulti([$key]); @@ -118,10 +118,7 @@ public function deleteItem($key) return $this->sharedPool->deleteItem($key); } - /** - * {@inheritdoc} - */ - public function deleteItems(array $keys) + public function deleteItems(array $keys): bool { foreach ($this->inMemoryPools as $inMemory) { $inMemory->deleteMulti($keys); @@ -136,10 +133,7 @@ public function deleteItems(array $keys) return $this->sharedPool->deleteItems($keys); } - /** - * {@inheritdoc} - */ - public function invalidateTags(array $tags) + public function invalidateTags(array $tags): bool { // No tracking of tags in in-memory, as it's anyway meant to only optimize for reads (GETs) and not writes. $this->clearInMemoryPools(); @@ -153,10 +147,7 @@ public function invalidateTags(array $tags) return $this->sharedPool->invalidateTags($tags); } - /** - * {@inheritdoc} - */ - public function clear(string $prefix = '') + public function clear(string $prefix = ''): bool { $this->clearInMemoryPools(); @@ -168,10 +159,7 @@ public function clear(string $prefix = '') return $this->sharedPool->clear($prefix); } - /** - * {@inheritdoc} - */ - public function save(CacheItemInterface $item) + public function save(CacheItemInterface $item): bool { if ($this->transactionDepth > 0) { $this->deferredItemsDeletion[$item->getKey()] = true; @@ -182,16 +170,13 @@ public function save(CacheItemInterface $item) return $this->sharedPool->save($item); } - /** - * {@inheritdoc} - */ public function beginTransaction(): void { ++$this->transactionDepth; } /** - * {@inheritdoc} + * @throws \Psr\Cache\InvalidArgumentException */ public function commitTransaction(): void { @@ -216,9 +201,6 @@ public function commitTransaction(): void } } - /** - * {@inheritdoc} - */ public function rollbackTransaction(): void { $this->transactionDepth = 0; @@ -229,21 +211,17 @@ public function rollbackTransaction(): void } /** - * {@inheritdoc} - * * Symfony cache feature for deferring saves, not used by Ibexa & not related to transaction handling here. */ - public function saveDeferred(CacheItemInterface $item) + public function saveDeferred(CacheItemInterface $item): bool { return $this->sharedPool->saveDeferred($item); } /** - * {@inheritdoc} - * * Symfony cache feature for committing deferred saves, not used by Ibexa & not related to transaction handling here. */ - public function commit() + public function commit(): bool { return $this->sharedPool->commit(); } @@ -251,11 +229,11 @@ public function commit() /** * For use by getItem(s) to mark items as a miss if it's going to be cleared on transaction commit. * - * @param \Symfony\Component\Cache\CacheItem[] $items + * @param iterable $items * - * @return \Symfony\Component\Cache\CacheItem[] + * @return iterable */ - private function markItemsAsDeferredMissIfNeeded(iterable $items) + private function markItemsAsDeferredMissIfNeeded(iterable $items): iterable { if ($this->transactionDepth === 0) { return $items; @@ -279,11 +257,6 @@ private function markItemsAsDeferredMissIfNeeded(iterable $items) return $iteratedItems; } - /** - * @param \Symfony\Component\Cache\CacheItem $item - * - * @return bool - */ private function itemIsDeferredMiss(CacheItem $item): bool { if (isset($this->deferredItemsDeletion[$item->getKey()])) { diff --git a/src/lib/QueryType/ContentViewQueryTypeMapper.php b/src/lib/QueryType/ContentViewQueryTypeMapper.php index c9a37b39c5..e88852907a 100644 --- a/src/lib/QueryType/ContentViewQueryTypeMapper.php +++ b/src/lib/QueryType/ContentViewQueryTypeMapper.php @@ -7,6 +7,7 @@ namespace Ibexa\Core\QueryType; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; use Ibexa\Core\MVC\Symfony\View\ContentView; /** @@ -14,10 +15,5 @@ */ interface ContentViewQueryTypeMapper { - /** - * @param \Ibexa\Core\MVC\Symfony\View\ContentView $contentView - * - * @return \Ibexa\Core\QueryType\QueryType - */ - public function map(ContentView $contentView); + public function map(ContentView $contentView): Query; } diff --git a/src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php b/src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php index 9023d58770..676bf942b0 100644 --- a/src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php +++ b/src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php @@ -7,8 +7,9 @@ namespace Ibexa\Core\QueryType; +use Ibexa\Contracts\Core\Exception\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; use Ibexa\Core\MVC\Symfony\View\ContentView; -use InvalidArgumentException; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; /** @@ -16,28 +17,27 @@ */ class QueryParameterContentViewQueryTypeMapper implements ContentViewQueryTypeMapper { - /** @var QueryTypeRegistry */ - private $queryTypeRegistry; + private QueryTypeRegistry $queryTypeRegistry; public function __construct(QueryTypeRegistry $queryTypeRegistry) { $this->queryTypeRegistry = $queryTypeRegistry; } - public function map(ContentView $contentView) + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function map(ContentView $contentView): Query { - if (!$contentView instanceof ContentView) { - throw new InvalidArgumentException('ContentView expected'); - } - if (!$contentView->hasParameter('query')) { throw new InvalidArgumentException('query', "Required 'query' view parameter is missing"); } $queryOptions = $contentView->getParameter('query'); - $queryType = $this->queryTypeRegistry->getQueryType($queryOptions['query_type']); - return $queryType->getQuery($this->extractParametersFromContentView($contentView)); + return $this->queryTypeRegistry + ->getQueryType($queryOptions['query_type']) + ->getQuery($this->extractParametersFromContentView($contentView)); } /** diff --git a/src/lib/Repository/SearchService.php b/src/lib/Repository/SearchService.php index 76555a3929..625ca37c82 100644 --- a/src/lib/Repository/SearchService.php +++ b/src/lib/Repository/SearchService.php @@ -81,19 +81,6 @@ public function __construct( $this->backgroundIndexer = $backgroundIndexer; } - /** - * Finds content objects for the given query. - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query - * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations. - * @param bool $filterOnUserPermissions if true only the objects which the user is allowed to read are returned. - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult - */ public function findContent(Query $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult { $result = $this->internalFindContentInfo($query, $languageFilter, $filterOnUserPermissions); diff --git a/src/lib/Repository/UserService.php b/src/lib/Repository/UserService.php index 091ee4df0c..7a97184b0d 100644 --- a/src/lib/Repository/UserService.php +++ b/src/lib/Repository/UserService.php @@ -251,11 +251,11 @@ public function loadSubUserGroups(APIUserGroup $userGroup, int $offset = 0, int /** * Returns (searches) subgroups of a user group described by its main location. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location - * @param int $offset - * @param int $limit + * @phpstan-return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Repository\Values\Content\Location> * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ protected function searchSubGroups(Location $location, int $offset = 0, int $limit = 25): SearchResult { diff --git a/src/lib/Repository/Values/Content/Content.php b/src/lib/Repository/Values/Content/Content.php index 0cc1e2d534..83a362ca29 100644 --- a/src/lib/Repository/Values/Content/Content.php +++ b/src/lib/Repository/Values/Content/Content.php @@ -22,7 +22,7 @@ * @property-read \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType convenience getter for $versionInfo->contentInfo->contentType * @property-read int $id convenience getter for retrieving the contentId: $versionInfo->content->id * @property-read \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo calls getVersionInfo() - * @property-read \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $fields Access fields, calls getFields() + * @property-read array> $fields an array of [field definition identifier => [language code => field value]] * * @internal Meant for internal use by Repository, type hint against API object instead. */ @@ -31,8 +31,8 @@ class Content extends APIContent /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Thumbnail|null */ protected $thumbnail; - /** @var mixed[][] An array of array of field values like[$fieldDefIdentifier][$languageCode] */ - protected $fields; + /** @var array> An array of field values like [field definition identifier => [language code => field value]] => */ + protected array $fields = []; /** @var \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo */ protected $versionInfo; diff --git a/src/lib/Resources/settings/settings.yml b/src/lib/Resources/settings/settings.yml index 1ea2293059..cf27f43870 100644 --- a/src/lib/Resources/settings/settings.yml +++ b/src/lib/Resources/settings/settings.yml @@ -8,8 +8,8 @@ parameters: ibexa.io.binary_file.storage.prefix: original ibexa.site_access.list: [test] ibexa.site_access.config.default.anonymous_user_id: 10 - ibexa.site_access.config.default.io.permissions.files: 0644 - ibexa.site_access.config.default.io.permissions.directories: 0755 + ibexa.site_access.config.default.io.permissions.files: 0o644 + ibexa.site_access.config.default.io.permissions.directories: 0o755 services: ibexa.api.persistence_handler: diff --git a/src/lib/Search/Legacy/Content/Handler.php b/src/lib/Search/Legacy/Content/Handler.php index 20c8890f0d..ccf685cce0 100644 --- a/src/lib/Search/Legacy/Content/Handler.php +++ b/src/lib/Search/Legacy/Content/Handler.php @@ -46,6 +46,8 @@ * 4) Additionally we might need a post-query filtering step, which filters * content objects based on criteria, which could not be converted in to * database statements. + * + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService */ class Handler implements SearchHandlerInterface { @@ -116,28 +118,9 @@ public function __construct( $this->mapper = $mapper; } - /** - * Finds content objects for the given query. - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if Query criterion is not applicable to its target - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query - * @param array $languageFilter - a map of language related filters specifying languages query will be performed on. - * Also used to define which field languages are loaded for the returned content. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult - */ - public function findContent(Query $query, array $languageFilter = []) + public function findContent(Query $query, array $languageFilter = []): SearchResult { - if (!isset($languageFilter['languages'])) { - $languageFilter['languages'] = []; - } - - if (!isset($languageFilter['useAlwaysAvailable'])) { - $languageFilter['useAlwaysAvailable'] = true; - } + $languageFilter = $this->setLanguageFilterDefaults($languageFilter); $start = microtime(true); $query->filter = $query->filter ?: new Criterion\MatchAll(); @@ -156,9 +139,10 @@ public function findContent(Query $query, array $languageFilter = []) $query->performCount ); + /** @phpstan-var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Persistence\Content\ContentInfo> $result */ $result = new SearchResult(); - $result->time = microtime(true) - $start; - $result->totalCount = $data['count']; + $result->time = (int) (microtime(true) - $start) * 1000; // time expressed in ms + $result->totalCount = $data['count'] !== null ? (int)$data['count'] : null; $contentInfoList = $this->contentMapper->extractContentInfoFromRows( $data['rows'], '', @@ -166,6 +150,7 @@ public function findContent(Query $query, array $languageFilter = []) ); foreach ($contentInfoList as $index => $contentInfo) { + /** @phpstan-var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit<\Ibexa\Contracts\Core\Persistence\Content\ContentInfo> $searchHit */ $searchHit = new SearchHit(); $searchHit->valueObject = $contentInfo; $searchHit->matchedTranslation = $this->extractMatchedLanguage( @@ -199,30 +184,9 @@ protected function extractMatchedLanguage($languageMask, $mainLanguageId, $langu return null; } - /** - * Performs a query for a single content object. - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface $filter - * @param array $languageFilter - a map of language related filters specifying languages query will be performed on. - * Also used to define which field languages are loaded for the returned content. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations - * - * @return \Ibexa\Contracts\Core\Persistence\Content\ContentInfo - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the object was not found by the query or due to permissions - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if Criterion is not applicable to its target - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is more than than one result matching the criterions - */ - public function findSingle(CriterionInterface $filter, array $languageFilter = []) + public function findSingle(CriterionInterface $filter, array $languageFilter = []): Content\ContentInfo { - if (!isset($languageFilter['languages'])) { - $languageFilter['languages'] = []; - } - - if (!isset($languageFilter['useAlwaysAvailable'])) { - $languageFilter['useAlwaysAvailable'] = true; - } + $languageFilter = $this->setLanguageFilterDefaults($languageFilter); $searchQuery = new Query(); $searchQuery->filter = $filter; @@ -239,20 +203,12 @@ public function findSingle(CriterionInterface $filter, array $languageFilter = [ throw new InvalidArgumentException('totalCount', 'findSingle() found more then one item for the given $criterion'); } - $first = reset($result->searchHits); - - return $first->valueObject; + return reset($result->searchHits)->valueObject; } - public function findLocations(LocationQuery $query, array $languageFilter = []) + public function findLocations(LocationQuery $query, array $languageFilter = []): SearchResult { - if (!isset($languageFilter['languages'])) { - $languageFilter['languages'] = []; - } - - if (!isset($languageFilter['useAlwaysAvailable'])) { - $languageFilter['useAlwaysAvailable'] = true; - } + $languageFilter = $this->setLanguageFilterDefaults($languageFilter); $start = microtime(true); $query->filter = $query->filter ?: new Criterion\MatchAll(); @@ -269,12 +225,14 @@ public function findLocations(LocationQuery $query, array $languageFilter = []) $query->performCount ); + /** @phpstan-var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Persistence\Content\Location> $result */ $result = new SearchResult(); - $result->time = microtime(true) - $start; - $result->totalCount = $data['count']; + $result->time = (int) (microtime(true) - $start) * 1000; // time expressed in ms + $result->totalCount = $data['count'] !== null ? (int)$data['count'] : null; $locationList = $this->locationMapper->createLocationsFromRows($data['rows']); foreach ($locationList as $index => $location) { + /** @phpstan-var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit<\Ibexa\Contracts\Core\Persistence\Content\Location> $searchHit */ $searchHit = new SearchHit(); $searchHit->valueObject = $location; $searchHit->matchedTranslation = $this->extractMatchedLanguage( @@ -393,4 +351,22 @@ public function supports(int $capabilityFlag): bool { return false; } + + /** + * @phpstan-param TSearchLanguageFilter $languageFilter + * + * @phpstan-return TSearchLanguageFilter + */ + private function setLanguageFilterDefaults(array $languageFilter): array + { + if (!isset($languageFilter['languages'])) { + $languageFilter['languages'] = []; + } + + if (!isset($languageFilter['useAlwaysAvailable'])) { + $languageFilter['useAlwaysAvailable'] = true; + } + + return $languageFilter; + } } diff --git a/tests/bundle/Core/Entity/EntityManagerFactoryTest.php b/tests/bundle/Core/Entity/EntityManagerFactoryTest.php index aa0c446e74..98292fa89b 100644 --- a/tests/bundle/Core/Entity/EntityManagerFactoryTest.php +++ b/tests/bundle/Core/Entity/EntityManagerFactoryTest.php @@ -16,23 +16,25 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ServiceLocator; -class EntityManagerFactoryTest extends TestCase +/** + * @covers \Ibexa\Bundle\Core\Entity\EntityManagerFactory + */ +final class EntityManagerFactoryTest extends TestCase { - private const DEFAULT_ENTITY_MANAGER = 'doctrine.orm.ibexa_default_entity_manager'; - private const INVALID_ENTITY_MANAGER = 'doctrine.orm.ibexa_invalid_entity_manager'; - private const DEFAULT_CONNECTION = 'default'; - private const ENTITY_MANAGERS = [ + private const string DEFAULT_ENTITY_MANAGER = 'doctrine.orm.ibexa_default_entity_manager'; + private const string INVALID_ENTITY_MANAGER = 'doctrine.orm.ibexa_invalid_entity_manager'; + private const string DEFAULT_CONNECTION = 'default'; + private const array ENTITY_MANAGERS = [ 'ibexa_default' => self::DEFAULT_ENTITY_MANAGER, 'ibexa_invalid' => self::INVALID_ENTITY_MANAGER, ]; private RepositoryConfigurationProviderInterface & MockObject $repositoryConfigurationProvider; - /** @var \Doctrine\ORM\EntityManagerInterface */ - private $entityManager; + private EntityManagerInterface & MockObject $entityManager; - /** @var \Symfony\Component\DependencyInjection\ServiceLocator */ - private $serviceLocator; + /** @phpstan-var \Symfony\Component\DependencyInjection\ServiceLocator<\Doctrine\ORM\EntityManagerInterface> & \PHPUnit\Framework\MockObject\MockObject */ + private ServiceLocator & MockObject $serviceLocator; public function setUp(): void { @@ -43,12 +45,11 @@ public function setUp(): void public function testGetEntityManager(): void { - $serviceLocator = $this->getServiceLocator(); - $serviceLocator + $this->serviceLocator ->method('has') ->with(self::DEFAULT_ENTITY_MANAGER) ->willReturn(true); - $serviceLocator + $this->serviceLocator ->method('get') ->with(self::DEFAULT_ENTITY_MANAGER) ->willReturn($this->getEntityManager()); @@ -64,7 +65,7 @@ public function testGetEntityManager(): void $entityManagerFactory = new EntityManagerFactory( $this->repositoryConfigurationProvider, - $serviceLocator, + $this->serviceLocator, self::DEFAULT_CONNECTION, self::ENTITY_MANAGERS ); @@ -138,17 +139,14 @@ protected function getRepositoryConfigurationProvider(): RepositoryConfiguration } /** - * @return \Symfony\Component\DependencyInjection\ServiceLocator|\PHPUnit\Framework\MockObject\MockObject + * @phpstan-return \Symfony\Component\DependencyInjection\ServiceLocator<\Doctrine\ORM\EntityManagerInterface> & \PHPUnit\Framework\MockObject\MockObject */ - protected function getServiceLocator(): ServiceLocator + protected function getServiceLocator(): ServiceLocator & MockObject { return $this->createMock(ServiceLocator::class); } - /** - * @return \Doctrine\ORM\EntityManagerInterface|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getEntityManager(): EntityManagerInterface + protected function getEntityManager(): EntityManagerInterface & MockObject { return $this->createMock(EntityManagerInterface::class); } diff --git a/tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php b/tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php index ff8e73594e..eda709b056 100644 --- a/tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php +++ b/tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php @@ -9,9 +9,10 @@ use Ibexa\Bundle\Core\Fragment\DecoratedFragmentRenderer; use Ibexa\Bundle\Core\Fragment\SiteAccessSerializer; -use Ibexa\Core\MVC\Symfony\Component\Serializer\SerializerTrait; use Ibexa\Core\MVC\Symfony\SiteAccess; +use PHPUnit\Framework\MockObject\MockObject; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Controller\ControllerReference; use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; use Symfony\Component\HttpKernel\Fragment\RoutableFragmentRenderer; @@ -20,12 +21,9 @@ /** * @covers \Ibexa\Bundle\Core\Fragment\DecoratedFragmentRenderer */ -class DecoratedFragmentRendererTest extends FragmentRendererBaseTest +class DecoratedFragmentRendererTest extends FragmentRendererBaseTestCase { - use SerializerTrait; - - /** @var \Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface&\PHPUnit\Framework\MockObject\MockObject */ - protected FragmentRendererInterface $innerRenderer; + protected FragmentRendererInterface & MockObject $innerRenderer; protected function setUp(): void { @@ -33,7 +31,7 @@ protected function setUp(): void $this->innerRenderer = $this->createMock(FragmentRendererInterface::class); } - public function testSetFragmentPathNotRoutableRenderer() + public function testSetFragmentPathNotRoutableRenderer(): void { $matcher = $this->createMock(SiteAccess\URILexer::class); $siteAccess = new SiteAccess('test', 'test', $matcher); @@ -48,7 +46,7 @@ public function testSetFragmentPathNotRoutableRenderer() } } - public function testSetFragmentPath() + public function testSetFragmentPath(): void { $matcher = $this->createMock(SiteAccess\URILexer::class); $siteAccess = new SiteAccess('test', 'test', $matcher); @@ -56,7 +54,7 @@ public function testSetFragmentPath() ->expects(self::once()) ->method('analyseLink') ->with('/foo') - ->will(self::returnValue('/bar/foo')); + ->willReturn('/bar/foo'); $innerRenderer = $this->createMock(RoutableFragmentRenderer::class); $innerRenderer @@ -68,35 +66,35 @@ public function testSetFragmentPath() $renderer->setFragmentPath('/foo'); } - public function testGetName() + public function testGetName(): void { $name = 'test'; $this->innerRenderer ->expects(self::once()) ->method('getName') - ->will(self::returnValue($name)); + ->willReturn($name); $renderer = $this->getRenderer(); self::assertSame($name, $renderer->getName()); } - public function testRendererAbsoluteUrl() + public function testRendererAbsoluteUrl(): void { $url = 'http://phoenix-rises.fm/foo/bar'; $request = new Request(); $options = ['foo' => 'bar']; - $expectedReturn = '/_fragment?foo=bar'; + $expectedReturn = new Response('/_fragment?foo=bar'); $this->innerRenderer ->expects(self::once()) ->method('render') ->with($url, $request, $options) - ->will(self::returnValue($expectedReturn)); + ->willReturn($expectedReturn); $renderer = $this->getRenderer(); - self::assertSame($expectedReturn, $renderer->render($url, $request, $options)); + self::assertEquals($expectedReturn, $renderer->render($url, $request, $options)); } - public function testRendererControllerReference() + public function testRendererControllerReference(): void { $reference = new ControllerReference('FooBundle:bar:baz'); $matcher = new SiteAccess\Matcher\URIElement(1); @@ -108,12 +106,12 @@ public function testRendererControllerReference() $request = new Request(); $request->attributes->set('siteaccess', $siteAccess); $options = ['foo' => 'bar']; - $expectedReturn = '/_fragment?foo=bar'; + $expectedReturn = new Response('/_fragment?foo=bar'); $this->innerRenderer ->expects(self::once()) ->method('render') ->with($reference, $request, $options) - ->will(self::returnValue($expectedReturn)); + ->willReturn($expectedReturn); $renderer = $this->getRenderer(); self::assertSame($expectedReturn, $renderer->render($reference, $request, $options)); diff --git a/tests/bundle/Core/Fragment/FragmentRendererBaseTest.php b/tests/bundle/Core/Fragment/FragmentRendererBaseTestCase.php similarity index 79% rename from tests/bundle/Core/Fragment/FragmentRendererBaseTest.php rename to tests/bundle/Core/Fragment/FragmentRendererBaseTestCase.php index 37fb61a6f0..b7286a0eac 100644 --- a/tests/bundle/Core/Fragment/FragmentRendererBaseTest.php +++ b/tests/bundle/Core/Fragment/FragmentRendererBaseTestCase.php @@ -9,16 +9,20 @@ use Ibexa\Core\MVC\Symfony\Component\Serializer\SerializerTrait; use Ibexa\Core\MVC\Symfony\SiteAccess; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Controller\ControllerReference; use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; -abstract class FragmentRendererBaseTest extends TestCase +abstract class FragmentRendererBaseTestCase extends TestCase { use SerializerTrait; + protected FragmentRendererInterface & MockObject $innerRenderer; + public function testRendererControllerReferenceWithCompoundMatcher(): ControllerReference { $reference = new ControllerReference('FooBundle:bar:baz'); @@ -36,12 +40,13 @@ public function testRendererControllerReferenceWithCompoundMatcher(): Controller $request = $this->getRequest($siteAccess); $options = ['foo' => 'bar']; - $expectedReturn = '/_fragment?foo=bar'; + $expectedReturn = new Response('/_fragment?foo=bar'); $this->innerRenderer ->expects(self::once()) ->method('render') ->with($reference, $request, $options) - ->will(self::returnValue($expectedReturn)); + ->willReturn($expectedReturn) + ; $renderer = $this->getRenderer(); self::assertSame($expectedReturn, $renderer->render($reference, $request, $options)); @@ -58,7 +63,22 @@ public function testRendererControllerReferenceWithCompoundMatcher(): Controller $reference->attributes['serialized_siteaccess_matcher'] ); self::assertArrayHasKey('serialized_siteaccess_sub_matchers', $reference->attributes); - foreach ($siteAccess->matcher->getSubMatchers() as $subMatcher) { + if ($siteAccess->matcher instanceof SiteAccess\Matcher\CompoundInterface) { + $this->assertSubMatchers($reference, $siteAccess->matcher); + } + + return $reference; + } + + abstract public function getRequest(SiteAccess $siteAccess): Request; + + abstract public function getRenderer(): FragmentRendererInterface; + + private function assertSubMatchers( + ControllerReference $reference, + SiteAccess\Matcher\CompoundInterface $compoundMatcher + ): void { + foreach ($compoundMatcher->getSubMatchers() as $subMatcher) { self::assertSame( $this->getSerializer()->serialize( $subMatcher, @@ -68,11 +88,5 @@ public function testRendererControllerReferenceWithCompoundMatcher(): Controller $reference->attributes['serialized_siteaccess_sub_matchers'][get_class($subMatcher)] ); } - - return $reference; } - - abstract public function getRequest(SiteAccess $siteAccess): Request; - - abstract public function getRenderer(): FragmentRendererInterface; } diff --git a/tests/bundle/Core/Fragment/InlineFragmentRendererTest.php b/tests/bundle/Core/Fragment/InlineFragmentRendererTest.php index 65cf3e4960..1a2a6d71e9 100644 --- a/tests/bundle/Core/Fragment/InlineFragmentRendererTest.php +++ b/tests/bundle/Core/Fragment/InlineFragmentRendererTest.php @@ -9,9 +9,9 @@ use Ibexa\Bundle\Core\Fragment\InlineFragmentRenderer; use Ibexa\Bundle\Core\Fragment\SiteAccessSerializer; -use Ibexa\Core\MVC\Symfony\Component\Serializer\SerializerTrait; use Ibexa\Core\MVC\Symfony\SiteAccess; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Controller\ControllerReference; use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; @@ -21,9 +21,10 @@ */ class InlineFragmentRendererTest extends DecoratedFragmentRendererTest { - use SerializerTrait; + private const string FOO_BAR_SEMANTIC_PATH = '/foo/bar'; + private const string FOO_BAR_VIEW_PARAM_STRING = '/(foo)/bar'; - public function testRendererControllerReference() + public function testRendererControllerReference(): void { $reference = new ControllerReference('FooBundle:bar:baz'); $matcher = new SiteAccess\Matcher\HostElement(1); @@ -34,15 +35,15 @@ public function testRendererControllerReference() ); $request = new Request(); $request->attributes->set('siteaccess', $siteAccess); - $request->attributes->set('semanticPathinfo', '/foo/bar'); - $request->attributes->set('viewParametersString', '/(foo)/bar'); + $request->attributes->set('semanticPathinfo', self::FOO_BAR_SEMANTIC_PATH); + $request->attributes->set('viewParametersString', self::FOO_BAR_VIEW_PARAM_STRING); $options = ['foo' => 'bar']; - $expectedReturn = '/_fragment?foo=bar'; + $expectedReturn = new Response('/_fragment?foo=bar'); $this->innerRenderer ->expects(self::once()) ->method('render') ->with($reference, $request, $options) - ->will(self::returnValue($expectedReturn)); + ->willReturn($expectedReturn); $renderer = $this->getRenderer(); self::assertSame($expectedReturn, $renderer->render($reference, $request, $options)); @@ -59,9 +60,9 @@ public function testRendererControllerReference() $reference->attributes['serialized_siteaccess_matcher'] ); self::assertTrue(isset($reference->attributes['semanticPathinfo'])); - self::assertSame('/foo/bar', $reference->attributes['semanticPathinfo']); + self::assertSame(self::FOO_BAR_SEMANTIC_PATH, $reference->attributes['semanticPathinfo']); self::assertTrue(isset($reference->attributes['viewParametersString'])); - self::assertSame('/(foo)/bar', $reference->attributes['viewParametersString']); + self::assertSame(self::FOO_BAR_VIEW_PARAM_STRING, $reference->attributes['viewParametersString']); } public function testRendererControllerReferenceWithCompoundMatcher(): ControllerReference @@ -69,9 +70,9 @@ public function testRendererControllerReferenceWithCompoundMatcher(): Controller $reference = parent::testRendererControllerReferenceWithCompoundMatcher(); self::assertArrayHasKey('semanticPathinfo', $reference->attributes); - self::assertSame('/foo/bar', $reference->attributes['semanticPathinfo']); + self::assertSame(self::FOO_BAR_SEMANTIC_PATH, $reference->attributes['semanticPathinfo']); self::assertArrayHasKey('viewParametersString', $reference->attributes); - self::assertSame('/(foo)/bar', $reference->attributes['viewParametersString']); + self::assertSame(self::FOO_BAR_VIEW_PARAM_STRING, $reference->attributes['viewParametersString']); return $reference; } @@ -80,8 +81,8 @@ public function getRequest(SiteAccess $siteAccess): Request { $request = new Request(); $request->attributes->set('siteaccess', $siteAccess); - $request->attributes->set('semanticPathinfo', '/foo/bar'); - $request->attributes->set('viewParametersString', '/(foo)/bar'); + $request->attributes->set('semanticPathinfo', self::FOO_BAR_SEMANTIC_PATH); + $request->attributes->set('viewParametersString', self::FOO_BAR_VIEW_PARAM_STRING); return $request; } diff --git a/tests/bundle/Core/Imagine/AliasGeneratorTest.php b/tests/bundle/Core/Imagine/AliasGeneratorTest.php index 379871d7e2..f022df510a 100644 --- a/tests/bundle/Core/Imagine/AliasGeneratorTest.php +++ b/tests/bundle/Core/Imagine/AliasGeneratorTest.php @@ -410,29 +410,26 @@ public function testGetVariationInvalidVariation() /** * Prepare required Imagine-related mocks and assert that the Image Variation is as expected. * - * @param string $expectedUrl - * @param string $variationName - * @param string $imageId - * @param string $originalPath - * @param int $imageWidth - * @param int $imageHeight - * - * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ protected function assertImageVariationIsCorrect( - $expectedUrl, - $variationName, - $imageId, - $originalPath, - $imageWidth, - $imageHeight - ) { + string $expectedUrl, + string $variationName, + string $imageId, + string $originalPath, + int $imageWidth, + int $imageHeight + ): void { $imageValue = new ImageValue(['id' => $originalPath, 'imageId' => $imageId]); $field = new Field(['value' => $imageValue]); $binaryFile = new BinaryFile( [ + 'id' => 'foo/bar/image.jpg', 'uri' => "_aliases/{$variationName}/foo/bar/image.jpg", + 'mtime' => null, + 'size' => 123, ] ); @@ -440,7 +437,7 @@ protected function assertImageVariationIsCorrect( ->expects(self::once()) ->method('resolve') ->with($originalPath, $variationName) - ->will(self::returnValue($expectedUrl)); + ->willReturn($expectedUrl); $this->variationPathGenerator ->expects(self::once()) @@ -464,25 +461,26 @@ protected function assertImageVariationIsCorrect( ->expects(self::once()) ->method('load') ->with('file contents mock') - ->will(self::returnValue($this->image)); + ->willReturn($this->image); $this->image ->expects(self::once()) ->method('getSize') - ->will(self::returnValue($this->box)); + ->willReturn($this->box); $this->box ->expects(self::once()) ->method('getWidth') - ->will(self::returnValue($imageWidth)); + ->willReturn($imageWidth); $this->box ->expects(self::once()) ->method('getHeight') - ->will(self::returnValue($imageHeight)); + ->willReturn($imageHeight); $expected = new ImageVariation( [ 'name' => $variationName, 'fileName' => "image_$variationName.jpg", + 'fileSize' => 123, 'dirPath' => 'http://localhost/foo/bar', 'uri' => $expectedUrl, 'imageId' => $imageId, diff --git a/tests/bundle/Core/Resources/config/framework.yaml b/tests/bundle/Core/Resources/config/framework.yaml index f8b8e99feb..348740bc30 100644 --- a/tests/bundle/Core/Resources/config/framework.yaml +++ b/tests/bundle/Core/Resources/config/framework.yaml @@ -5,7 +5,7 @@ parameters: framework: test: true session: - storage_id: session.storage.mock_file + storage_factory_id: session.storage.factory.mock_file cache: app: cache.adapter.array router: diff --git a/tests/bundle/Core/Resources/config/security.yaml b/tests/bundle/Core/Resources/config/security.yaml index abb4fa3d91..9f7a878085 100644 --- a/tests/bundle/Core/Resources/config/security.yaml +++ b/tests/bundle/Core/Resources/config/security.yaml @@ -5,4 +5,3 @@ security: firewalls: main: - anonymous: ~ diff --git a/tests/bundle/IO/EventListener/StreamFileListenerTest.php b/tests/bundle/IO/EventListener/StreamFileListenerTest.php index cce315842e..11e35fece6 100644 --- a/tests/bundle/IO/EventListener/StreamFileListenerTest.php +++ b/tests/bundle/IO/EventListener/StreamFileListenerTest.php @@ -13,6 +13,7 @@ use Ibexa\Core\IO\IOConfigProvider; use Ibexa\Core\IO\IOServiceInterface; use Ibexa\Core\IO\Values\BinaryFile; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\RequestEvent; @@ -20,14 +21,11 @@ class StreamFileListenerTest extends TestCase { - /** @var \Ibexa\Bundle\IO\EventListener\StreamFileListener */ - private $eventListener; + private StreamFileListener $eventListener; - /** @var \Ibexa\Core\IO\IOServiceInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $ioServiceMock; + private IOServiceInterface & MockObject $ioServiceMock; - /** @var \Ibexa\Core\IO\IOConfigProvider|\PHPUnit\Framework\MockObject\MockObject */ - private $ioConfigResolverMock; + private IOConfigProvider & MockObject $ioConfigResolverMock; protected function setUp(): void { @@ -38,7 +36,7 @@ protected function setUp(): void $this->eventListener = new StreamFileListener($this->ioServiceMock, $this->ioConfigResolverMock); } - public function testDoesNotRespondToNonIoUri() + public function testDoesNotRespondToNonIoUri(): void { $request = $this->createRequest('/Not-an-image'); $event = $this->createEvent($request); @@ -53,7 +51,7 @@ public function testDoesNotRespondToNonIoUri() self::assertNull($event->getResponse()); } - public function testDoesNotRespondToNoIoRequest() + public function testDoesNotRespondToNoIoRequest(): void { $request = $this->createRequest('/Not-an-image', 'bar.fr'); $event = $this->createEvent($request); @@ -68,79 +66,33 @@ public function testDoesNotRespondToNoIoRequest() self::assertNull($event->getResponse()); } - public function testRespondsToIoUri() + public function testRespondsToIoUri(): void { - $uri = '/var/test/storage/images/image.png'; - $this->configureIoUrlPrefix(ltrim($uri, '/')); + $uri = $binaryFileUri = '/var/test/storage/images/image.png'; + $urlPrefix = ltrim($uri, '/'); $request = $this->createRequest($uri); - $event = $this->createEvent($request); - - $binaryFile = new BinaryFile(['mtime' => new DateTime()]); - - $this->ioServiceMock - ->expects(self::once()) - ->method('loadBinaryFileByUri') - ->with($uri) - ->will(self::returnValue($binaryFile)); - - $this->eventListener->onKernelRequest($event); - - self::assertTrue($event->hasResponse()); - $expectedResponse = new BinaryStreamResponse($binaryFile, $this->ioServiceMock); - $response = $event->getResponse(); - // since symfony/symfony v3.2.7 Response sets Date header if not explicitly set - // @see https://github.com/symfony/symfony/commit/e3d90db74773406fb8fdf07f36cb8ced4d187f62 - $expectedResponse->setDate($response->getDate()); - self::assertEquals( - $expectedResponse, - $response - ); + $this->assertOnKernelRequestResponse($request, $urlPrefix, $binaryFileUri); } - public function testRespondsToIoRequest() + public function testRespondsToIoRequest(): void { $uri = '/var/test/storage/images/image.png'; $host = 'phoenix-rises.fm'; $urlPrefix = "http://$host/var/test/storage"; - $this->configureIoUrlPrefix($urlPrefix); $request = $this->createRequest($uri, $host); - $event = $this->createEvent($request); - - $binaryFile = new BinaryFile(['mtime' => new DateTime()]); - - $this->ioServiceMock - ->expects(self::once()) - ->method('loadBinaryFileByUri') - ->with(sprintf('http://%s%s', $host, $uri)) - ->will(self::returnValue($binaryFile)); - - $this->eventListener->onKernelRequest($event); - - self::assertTrue($event->hasResponse()); - $expectedResponse = new BinaryStreamResponse($binaryFile, $this->ioServiceMock); - $response = $event->getResponse(); - // since symfony/symfony v3.2.7 Response sets Date header if not explicitly set - // @see https://github.com/symfony/symfony/commit/e3d90db74773406fb8fdf07f36cb8ced4d187f62 - $expectedResponse->setDate($response->getDate()); - self::assertEquals( - $expectedResponse, - $response - ); + $this->assertOnKernelRequestResponse($request, $urlPrefix, sprintf('http://%s%s', $host, $uri)); } - private function configureIoUrlPrefix($urlPrefix) + private function configureIoUrlPrefix(string $urlPrefix): void { $this->ioConfigResolverMock ->method('getUrlPrefix') ->willReturn($urlPrefix); } - /** - * @return \Symfony\Component\HttpFoundation\Request - */ - protected function createRequest($semanticPath, $host = 'localhost') + protected function createRequest(string $semanticPath, string $host = 'localhost'): Request { $request = Request::create(sprintf('http://%s%s', $host, $semanticPath)); $request->attributes->set('semanticPathinfo', $semanticPath); @@ -148,19 +100,43 @@ protected function createRequest($semanticPath, $host = 'localhost') return $request; } - /** - * @param $request - * - * @return \Symfony\Component\HttpKernel\Event\RequestEvent - */ - protected function createEvent($request) + protected function createEvent(Request $request): RequestEvent { - $event = new RequestEvent( + return new RequestEvent( $this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST ); + } + + private function assertOnKernelRequestResponse(Request $request, string $urlPrefix, string $binaryFileUri): void + { + $this->configureIoUrlPrefix($urlPrefix); + + $event = $this->createEvent($request); + + $binaryFile = new BinaryFile(['mtime' => new DateTime()]); - return $event; + $this->ioServiceMock + ->expects(self::once()) + ->method('loadBinaryFileByUri') + ->with($binaryFileUri) + ->willReturn($binaryFile) + ; + + $this->eventListener->onKernelRequest($event); + + self::assertTrue($event->hasResponse()); + $expectedResponse = new BinaryStreamResponse($binaryFile, $this->ioServiceMock); + $response = $event->getResponse(); + $date = $response?->getDate(); + self::assertNotNull($date); + // since symfony/symfony v3.2.7 Response sets Date header if not explicitly set + // @see https://github.com/symfony/symfony/commit/e3d90db74773406fb8fdf07f36cb8ced4d187f62 + $expectedResponse->setDate($date); + self::assertEquals( + $expectedResponse, + $response + ); } } diff --git a/tests/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundleTest.php b/tests/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundleTest.php index 16b396b1b7..a67042e6ba 100644 --- a/tests/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundleTest.php +++ b/tests/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundleTest.php @@ -16,19 +16,18 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\RuntimeException; +/** + * @covers \Ibexa\Bundle\RepositoryInstaller\IbexaRepositoryInstallerBundle + */ class IbexaRepositoryInstallerBundleTest extends TestCase { - /** @var \Ibexa\Bundle\RepositoryInstaller\IbexaRepositoryInstallerBundle */ - private $bundle; + private IbexaRepositoryInstallerBundle $bundle; public function setUp(): void { $this->bundle = new IbexaRepositoryInstallerBundle(); } - /** - * @covers \Ibexa\Bundle\RepositoryInstaller\IbexaRepositoryInstallerBundle::build - */ public function testBuild(): void { $container = new ContainerBuilder(); @@ -46,9 +45,6 @@ static function (CompilerPassInterface $compilerPass): bool { ); } - /** - * @covers \Ibexa\Bundle\RepositoryInstaller\IbexaRepositoryInstallerBundle::build - */ public function testBuildFailsWithoutDoctrineSchemaBundle(): void { $container = new ContainerBuilder(); diff --git a/tests/integration/Core/BinaryBase/BinaryBaseStorage/BinaryBaseStorageTest.php b/tests/integration/Core/BinaryBase/BinaryBaseStorage/BinaryBaseStorageTest.php index ff1e891e99..2386599759 100644 --- a/tests/integration/Core/BinaryBase/BinaryBaseStorage/BinaryBaseStorageTest.php +++ b/tests/integration/Core/BinaryBase/BinaryBaseStorage/BinaryBaseStorageTest.php @@ -79,8 +79,9 @@ public function testHasFieldData(): void */ public function testStoreFieldData(VersionInfo $versionInfo, Field $field): void { + $binaryFileIdentifier = 'qwerty12345'; $binaryFileCreateStruct = new BinaryFileCreateStruct([ - 'id' => 'qwerty12345', + 'id' => $binaryFileIdentifier, 'size' => '372949', 'mimeType' => 'image/jpeg', ]); @@ -88,7 +89,7 @@ public function testStoreFieldData(VersionInfo $versionInfo, Field $field): void $this->ioServiceMock ->expects(self::once()) ->method('newBinaryCreateStructFromLocalFile') - ->will(self::returnValue($binaryFileCreateStruct)); + ->willReturn($binaryFileCreateStruct); $this->pathGeneratorMock ->expects(self::once()) @@ -100,7 +101,7 @@ public function testStoreFieldData(VersionInfo $versionInfo, Field $field): void ->expects(self::once()) ->method('createBinaryFile') ->with($binaryFileCreateStruct) - ->willReturn(new BinaryFile()); + ->willReturn(new BinaryFile(['id' => $binaryFileIdentifier, 'uri' => '/foo'])); $this->storage->storeFieldData($versionInfo, $field); diff --git a/tests/integration/Core/Repository/SearchServiceLocationTest.php b/tests/integration/Core/Repository/SearchServiceLocationTest.php index 9381c5fc10..6f5fed74b5 100644 --- a/tests/integration/Core/Repository/SearchServiceLocationTest.php +++ b/tests/integration/Core/Repository/SearchServiceLocationTest.php @@ -191,7 +191,7 @@ protected function createFolderWithNonPrintableUtf8Characters(): Content $createStruct->alwaysAvailable = false; $createStruct->setField( 'name', - utf8_decode("Non\x09Printable\x0EFolder") + mb_convert_encoding("Non\x09Printable\x0EFolder", 'ISO-8859-1', 'UTF-8') ); $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(2); @@ -338,7 +338,7 @@ public function testNonPrintableUtf8Characters(): void 'query' => new Criterion\Field( 'name', Criterion\Operator::EQ, - utf8_decode("Non\x09Printable\x0EFolder") + mb_convert_encoding("Non\x09Printable\x0EFolder", 'ISO-8859-1', 'UTF-8') ), ] ); diff --git a/tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php b/tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php index 8e15ed4e0c..09c200b469 100644 --- a/tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php +++ b/tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php @@ -24,6 +24,10 @@ * @group integration * @group search * @group language_fallback + * + * @template TSearchHitValueObject + * + * @phpstan-type TIndexMap array{dedicated: string, shared: string, single: string, cloud: string} */ class SearchServiceTranslationLanguageFallbackTest extends BaseTest { @@ -1694,7 +1698,12 @@ protected function getSetupType() throw new RuntimeException("Backend cores setup '{$coresSetup}' is not handled"); } - protected function getIndexName($indexMap) + /** + * @phpstan-param TIndexMap $indexMap + * + * @throws \ErrorException + */ + protected function getIndexName(array $indexMap): ?string { $setupFactory = $this->getSetupFactory(); @@ -1882,6 +1891,10 @@ public function testFindLocationsMultiple( } } + /** + * @phpstan-param TIndexMap $indexMap + * @phpstan-param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit $searchHit + */ private function assertIndexName(array $indexMap, SearchHit $searchHit): void { $indexName = $this->getIndexName($indexMap); diff --git a/tests/lib/IO/Flysystem/VisibilityConverter/SiteAccessAwareVisibilityConverterTest.php b/tests/lib/IO/Flysystem/VisibilityConverter/SiteAccessAwareVisibilityConverterTest.php index 3c9da4733b..7316fbeb0d 100644 --- a/tests/lib/IO/Flysystem/VisibilityConverter/SiteAccessAwareVisibilityConverterTest.php +++ b/tests/lib/IO/Flysystem/VisibilityConverter/SiteAccessAwareVisibilityConverterTest.php @@ -18,8 +18,8 @@ */ final class SiteAccessAwareVisibilityConverterTest extends BaseVisibilityConverterTest { - private const SITE_FILE_FLAGS = 0644; - private const SITE_DIRECTORY_FLAGS = 0755; + private const int SITE_FILE_FLAGS = 0644; + private const int SITE_DIRECTORY_FLAGS = 0755; protected function buildVisibilityConverter(): BaseVisibilityConverter { diff --git a/tests/lib/IO/IOServiceTest.php b/tests/lib/IO/IOServiceTest.php index 5d865b19b7..bcff3d4c54 100644 --- a/tests/lib/IO/IOServiceTest.php +++ b/tests/lib/IO/IOServiceTest.php @@ -7,6 +7,7 @@ namespace Ibexa\Tests\Core\IO; +use Closure; use Ibexa\Contracts\Core\IO\BinaryFile as SPIBinaryFile; use Ibexa\Contracts\Core\IO\BinaryFileCreateStruct as SPIBinaryFileCreateStruct; use Ibexa\Contracts\Core\IO\MimeTypeDetector; @@ -16,6 +17,7 @@ use Ibexa\Core\IO\IOService; use Ibexa\Core\IO\Values\BinaryFile; use Ibexa\Core\IO\Values\BinaryFileCreateStruct; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; /** @@ -23,19 +25,17 @@ */ class IOServiceTest extends TestCase { - public const PREFIX = 'test-prefix'; + public const string PREFIX = 'test-prefix'; + protected const string BINARY_FILE_ID_MY_PATH = 'my/path.png'; + private const string MIME_TYPE_TEXT_PHP = 'text/x-php'; - /** @var \Ibexa\Core\IO\IOService */ - protected $IOService; + protected IOService $ioService; - /** @var \Ibexa\Core\IO\IOMetadataHandler|\PHPUnit\Framework\MockObject\MockObject */ - protected $metadataHandlerMock; + protected IOMetadataHandler & MockObject $metadataHandlerMock; - /** @var \Ibexa\Core\IO\IOBinarydataHandler|\PHPUnit\Framework\MockObject\MockObject */ - protected $binarydataHandlerMock; + protected IOBinarydataHandler & MockObject $binarydataHandlerMock; - /** @var \Ibexa\Contracts\Core\IO\MimeTypeDetector|\PHPUnit\Framework\MockObject\MockObject */ - protected $mimeTypeDetectorMock; + protected MimeTypeDetector & MockObject $mimeTypeDetectorMock; protected function setUp(): void { @@ -45,7 +45,7 @@ protected function setUp(): void $this->metadataHandlerMock = $this->createMock(IOMetadataHandler::class); $this->mimeTypeDetectorMock = $this->createMock(MimeTypeDetector::class); - $this->IOService = new IOService( + $this->ioService = new IOService( $this->metadataHandlerMock, $this->binarydataHandlerMock, $this->mimeTypeDetectorMock, @@ -53,7 +53,7 @@ protected function setUp(): void ); } - public function testNewBinaryCreateStructFromLocalFile() + public function testNewBinaryCreateStructFromLocalFile(): BinaryFileCreateStruct { $file = __FILE__; @@ -61,9 +61,9 @@ public function testNewBinaryCreateStructFromLocalFile() ->expects(self::once()) ->method('getFromPath') ->with(self::equalTo($file)) - ->will(self::returnValue('text/x-php')); + ->willReturn(self::MIME_TYPE_TEXT_PHP); - $binaryCreateStruct = $this->getIOService()->newBinaryCreateStructFromLocalFile( + $binaryCreateStruct = $this->getIoService()->newBinaryCreateStructFromLocalFile( $file ); @@ -71,7 +71,7 @@ public function testNewBinaryCreateStructFromLocalFile() self::assertNull($binaryCreateStruct->id); self::assertIsResource($binaryCreateStruct->inputStream); self::assertEquals(filesize(__FILE__), $binaryCreateStruct->size); - self::assertEquals('text/x-php', $binaryCreateStruct->mimeType); + self::assertEquals(self::MIME_TYPE_TEXT_PHP, $binaryCreateStruct->mimeType); return $binaryCreateStruct; } @@ -79,7 +79,7 @@ public function testNewBinaryCreateStructFromLocalFile() /** * @depends testNewBinaryCreateStructFromLocalFile */ - public function testCreateBinaryFile(BinaryFileCreateStruct $createStruct) + public function testCreateBinaryFile(BinaryFileCreateStruct $createStruct): BinaryFile { $createStruct->id = 'my/path.php'; $id = $this->getPrefixedUri($createStruct->id); @@ -87,7 +87,9 @@ public function testCreateBinaryFile(BinaryFileCreateStruct $createStruct) $spiBinaryFile = new SPIBinaryFile(); $spiBinaryFile->id = $id; $spiBinaryFile->uri = $id; - $spiBinaryFile->size = filesize(__FILE__); + $filesize = filesize(__FILE__); + self::assertNotFalse($filesize); + $spiBinaryFile->size = $filesize; $this->binarydataHandlerMock ->expects(self::once()) @@ -99,7 +101,7 @@ static function ($subject) use ($id): bool { return false; } - return $subject->id == $id; + return $subject->id === $id; } ) ); @@ -108,9 +110,9 @@ static function ($subject) use ($id): bool { ->expects(self::once()) ->method('create') ->with(self::callback($this->getSPIBinaryFileCreateStructCallback($id))) - ->will(self::returnValue($spiBinaryFile)); + ->willReturn($spiBinaryFile); - $binaryFile = $this->IOService->createBinaryFile($createStruct); + $binaryFile = $this->ioService->createBinaryFile($createStruct); self::assertInstanceOf(BinaryFile::class, $binaryFile); self::assertEquals($createStruct->id, $binaryFile->id); self::assertEquals($createStruct->size, $binaryFile->size); @@ -118,9 +120,9 @@ static function ($subject) use ($id): bool { return $binaryFile; } - public function testLoadBinaryFile() + public function testLoadBinaryFile(): BinaryFile { - $id = 'my/path.png'; + $id = self::BINARY_FILE_ID_MY_PATH; $spiId = $this->getPrefixedUri($id); $spiBinaryFile = new SPIBinaryFile(); $spiBinaryFile->id = $spiId; @@ -131,18 +133,22 @@ public function testLoadBinaryFile() ->expects(self::once()) ->method('load') ->with($spiId) - ->will(self::returnValue($spiBinaryFile)); + ->willReturn($spiBinaryFile); - $binaryFile = $this->getIOService()->loadBinaryFile($id); + $binaryFile = $this->getIoService()->loadBinaryFile($id); self::assertEquals($id, $binaryFile->id); return $binaryFile; } - public function testLoadBinaryFileNoMetadataUri() + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ + public function testLoadBinaryFileNoMetadataUri(): BinaryFile { - $id = 'my/path.png'; + $id = self::BINARY_FILE_ID_MY_PATH; $spiId = $this->getPrefixedUri($id); + $prefixedId = $this->mockGettingPrefixedUriFromDataHandler($id); $spiBinaryFile = new SPIBinaryFile(); $spiBinaryFile->id = $spiId; $spiBinaryFile->size = 12345; @@ -151,17 +157,16 @@ public function testLoadBinaryFileNoMetadataUri() ->expects(self::once()) ->method('load') ->with($spiId) - ->will(self::returnValue($spiBinaryFile)); - - $this->binarydataHandlerMock - ->expects(self::once()) - ->method('getUri') - ->with($spiId) - ->will(self::returnValue("/$spiId")); + ->willReturn($spiBinaryFile); - $binaryFile = $this->getIOService()->loadBinaryFile($id); + $binaryFile = $this->getIoService()->loadBinaryFile($id); - $expectedBinaryFile = new BinaryFile(['id' => $id, 'size' => 12345, 'uri' => "/$spiId"]); + $expectedBinaryFile = new BinaryFile( + [ + 'id' => $id, + 'size' => 12345, 'uri' => $prefixedId, 'mtime' => null, + ] + ); self::assertEquals($expectedBinaryFile, $binaryFile); @@ -169,18 +174,19 @@ public function testLoadBinaryFileNoMetadataUri() } /** - * @return mixed Whatever loadBinaryFile returns + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ - public function testLoadBinaryFileNotFound() + public function testLoadBinaryFileNotFound(): BinaryFile { $this->expectException(BinaryFileNotFoundException::class); return $this->loadBinaryFileNotFound(); } - public function testLoadBinaryFileByUri() + public function testLoadBinaryFileByUri(): BinaryFile { - $id = 'my/path.png'; + $id = self::BINARY_FILE_ID_MY_PATH; $spiId = $this->getPrefixedUri($id); $spiBinaryFile = new SPIBinaryFile(); $spiBinaryFile->id = $spiId; @@ -191,42 +197,35 @@ public function testLoadBinaryFileByUri() ->expects(self::once()) ->method('getIdFromUri') ->with($spiId) - ->will(self::returnValue($spiId)); + ->willReturn($spiId); $this->metadataHandlerMock ->expects(self::once()) ->method('load') ->with($spiId) - ->will(self::returnValue($spiBinaryFile)); + ->willReturn($spiBinaryFile); - $binaryFile = $this->getIOService()->loadBinaryFileByUri($spiId); + $binaryFile = $this->getIoService()->loadBinaryFileByUri($spiId); self::assertEquals($id, $binaryFile->id); return $binaryFile; } /** - * @return mixed Whatever loadBinaryFileByUri returns + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ - public function testLoadBinaryFileByUriNotFound() + public function testLoadBinaryFileByUriNotFound(): BinaryFile { $this->expectException(BinaryFileNotFoundException::class); return $this->loadBinaryFileByUriNotFound(); } - /** - * @depends testCreateBinaryFile - */ - public function testGetFileInputStream(BinaryFile $binaryFile) - { - self::markTestSkipped('Not implemented'); - } - /** * @depends testLoadBinaryFile */ - public function testGetFileContents(BinaryFile $binaryFile) + public function testGetFileContents(BinaryFile $binaryFile): void { $expectedContents = file_get_contents(__FILE__); @@ -234,42 +233,42 @@ public function testGetFileContents(BinaryFile $binaryFile) ->expects(self::once()) ->method('getContents') ->with(self::equalTo($this->getPrefixedUri($binaryFile->id))) - ->will(self::returnValue($expectedContents)); + ->willReturn($expectedContents); self::assertEquals( $expectedContents, - $this->getIOService()->getFileContents($binaryFile) + $this->getIoService()->getFileContents($binaryFile) ); } /** * @depends testCreateBinaryFile */ - public function testExists(BinaryFile $binaryFile) + public function testExists(BinaryFile $binaryFile): void { $this->metadataHandlerMock ->expects(self::once()) ->method('exists') ->with(self::equalTo($this->getPrefixedUri($binaryFile->id))) - ->will(self::returnValue(true)); + ->willReturn(true); self::assertTrue( - $this->getIOService()->exists( + $this->getIoService()->exists( $binaryFile->id ) ); } - public function testExistsNot() + public function testExistsNot(): void { $this->metadataHandlerMock ->expects(self::once()) ->method('exists') ->with(self::equalTo($this->getPrefixedUri(__METHOD__))) - ->will(self::returnValue(false)); + ->willReturn(false); self::assertFalse( - $this->getIOService()->exists( + $this->getIoService()->exists( __METHOD__ ) ); @@ -278,17 +277,17 @@ public function testExistsNot() /** * @depends testCreateBinaryFile */ - public function testGetMimeType(BinaryFile $binaryFile) + public function testGetMimeType(BinaryFile $binaryFile): void { $this->metadataHandlerMock ->expects(self::once()) ->method('getMimeType') ->with(self::equalTo($this->getPrefixedUri($binaryFile->id))) - ->willReturn('text/x-php'); + ->willReturn(self::MIME_TYPE_TEXT_PHP); self::assertEquals( 'text/x-php', - $this->getIOService()->getMimeType( + $this->getIoService()->getMimeType( $binaryFile->id ) ); @@ -297,7 +296,7 @@ public function testGetMimeType(BinaryFile $binaryFile) /** * @depends testCreateBinaryFile */ - public function testDeleteBinaryFile(BinaryFile $binaryFile) + public function testDeleteBinaryFile(BinaryFile $binaryFile): void { $this->metadataHandlerMock ->expects(self::once()) @@ -309,10 +308,10 @@ public function testDeleteBinaryFile(BinaryFile $binaryFile) ->method('delete') ->with(self::equalTo($this->getPrefixedUri($binaryFile->id))); - $this->getIOService()->deleteBinaryFile($binaryFile); + $this->getIoService()->deleteBinaryFile($binaryFile); } - public function testDeleteDirectory() + public function testDeleteDirectory(): void { $id = 'some/directory'; $spiId = $this->getPrefixedUri($id); @@ -327,9 +326,12 @@ public function testDeleteDirectory() ->method('deleteDirectory') ->with($spiId); - $this->getIOService()->deleteDirectory('some/directory'); + $this->getIoService()->deleteDirectory('some/directory'); } + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ public function testDeleteBinaryFileNotFound(): void { $this->expectException(BinaryFileNotFoundException::class); @@ -337,58 +339,55 @@ public function testDeleteBinaryFileNotFound(): void $this->deleteBinaryFileNotFound(); } - public function getPrefixedUri($uri) + public function getPrefixedUri(string $uri): string { return self::PREFIX . '/' . $uri; } - /** - * @return \Ibexa\Core\IO\IOService - */ - protected function getIOService() + protected function getIOService(): IOService { - return $this->IOService; + return $this->ioService; } /** * Asserts that the given $ioCreateStruct is of the right type and that id matches the expected value. - * - * @param int $spiId */ - private function getSPIBinaryFileCreateStructCallback($spiId): \Closure + private function getSPIBinaryFileCreateStructCallback(string $spiId): Closure { return static function ($subject) use ($spiId): bool { if (!$subject instanceof SPIBinaryFileCreateStruct) { return false; } - return $subject->id == $spiId; + return $subject->id === $spiId; }; } /** - * @return bool|\Ibexa\Core\IO\Values\BinaryFile - * - * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentValue - * @throws \Ibexa\Core\Base\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ - protected function loadBinaryFileNotFound() + protected function loadBinaryFileNotFound(): BinaryFile { $id = 'id.ext'; $prefixedUri = $this->getPrefixedUri($id); + $this->metadataHandlerMock ->expects(self::once()) ->method('load') ->with($prefixedUri) - ->will(self::throwException(new BinaryFileNotFoundException($prefixedUri))); + ->willThrowException(new BinaryFileNotFoundException($prefixedUri)); - return $this->getIOService()->loadBinaryFile($id); + return $this->getIoService()->loadBinaryFile($id); } + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ protected function deleteBinaryFileNotFound(): void { $binaryFile = new BinaryFile( - ['id' => __METHOD__] + ['id' => __METHOD__, 'uri' => '/test-prefix/' . __METHOD__] ); $prefixedId = $this->getPrefixedUri($binaryFile->id); @@ -396,34 +395,44 @@ protected function deleteBinaryFileNotFound(): void ->expects(self::once()) ->method('delete') ->with(self::equalTo($prefixedId)) - ->will(self::throwException(new BinaryFileNotFoundException($prefixedId))); + ->willThrowException(new BinaryFileNotFoundException($prefixedId)); - $this->getIOService()->deleteBinaryFile($binaryFile); + $this->getIoService()->deleteBinaryFile($binaryFile); } /** - * @return bool|\Ibexa\Core\IO\Values\BinaryFile - * - * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentValue - * @throws \Ibexa\Core\Base\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ - protected function loadBinaryFileByUriNotFound() + protected function loadBinaryFileByUriNotFound(): BinaryFile { - $id = 'my/path.png'; + $id = self::BINARY_FILE_ID_MY_PATH; $spiId = $this->getPrefixedUri($id); $this->binarydataHandlerMock ->expects(self::once()) ->method('getIdFromUri') ->with($spiId) - ->will(self::returnValue($spiId)); + ->willReturn($spiId); $this->metadataHandlerMock ->expects(self::once()) ->method('load') ->with($spiId) - ->will(self::throwException(new BinaryFileNotFoundException($spiId))); + ->willThrowException(new BinaryFileNotFoundException($spiId)); + + return $this->getIoService()->loadBinaryFileByUri($spiId); + } + + protected function mockGettingPrefixedUriFromDataHandler(string $uri): string + { + $prefixedUri = $this->getPrefixedUri($uri); + $this->binarydataHandlerMock + ->expects(self::once()) + ->method('getUri') + ->with($prefixedUri) + ->willReturn($prefixedUri); - return $this->getIOService()->loadBinaryFileByUri($spiId); + return $prefixedUri; } } diff --git a/tests/lib/IO/TolerantIOServiceTest.php b/tests/lib/IO/TolerantIOServiceTest.php index 7c248f2f71..5c0098f232 100644 --- a/tests/lib/IO/TolerantIOServiceTest.php +++ b/tests/lib/IO/TolerantIOServiceTest.php @@ -8,7 +8,9 @@ namespace Ibexa\Tests\Core\IO; use Ibexa\Core\IO\TolerantIOService; +use Ibexa\Core\IO\Values\BinaryFile; use Ibexa\Core\IO\Values\MissingBinaryFile; +use Override; /** * @covers \Ibexa\Core\IO\IOService @@ -19,7 +21,7 @@ protected function setUp(): void { parent::setUp(); - $this->IOService = new TolerantIOService( + $this->ioService = new TolerantIOService( $this->metadataHandlerMock, $this->binarydataHandlerMock, $this->mimeTypeDetectorMock, @@ -27,47 +29,56 @@ protected function setUp(): void ); } - public function testLoadBinaryFileNotFound() + public function testLoadBinaryFileNotFound(): BinaryFile { - $binaryFile = parent::loadBinaryFileNotFound(); + $prefixedUri = $this->mockGettingPrefixedUriFromDataHandler('id.ext'); + + $binaryFile = $this->loadBinaryFileNotFound(); self::assertEquals( - new MissingBinaryFile(['id' => 'id.ext']), + new MissingBinaryFile(['id' => 'id.ext', 'uri' => $prefixedUri]), $binaryFile ); + + return $binaryFile; } - public function testCreateMissingBinaryFile() + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testCreateMissingBinaryFile(): void { $id = 'id.ext'; - $prefixedUri = $this->getPrefixedUri($id); + $prefixedUri = $this->mockGettingPrefixedUriFromDataHandler($id); - $this->binarydataHandlerMock - ->expects(self::once()) - ->method('getUri') - ->with($prefixedUri) - ->will(self::returnValue("/$prefixedUri")); - - $binaryFile = parent::loadBinaryFileNotFound(); + $binaryFile = $this->loadBinaryFileNotFound(); self::assertEquals( - new MissingBinaryFile(['id' => 'id.ext', 'uri' => "/$prefixedUri"]), + new MissingBinaryFile(['id' => 'id.ext', 'uri' => $prefixedUri]), $binaryFile ); } /** - * Overridden to change the expected exception (none). + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ + #[Override] public function testDeleteBinaryFileNotFound(): void { + $this->expectNotToPerformAssertions(); $this->deleteBinaryFileNotFound(); } - public function testLoadBinaryFileByUriNotFound() + public function testLoadBinaryFileByUriNotFound(): BinaryFile { + $prefixedUri = $this->mockGettingPrefixedUriFromDataHandler(self::BINARY_FILE_ID_MY_PATH); + + $binaryFile = $this->loadBinaryFileByUriNotFound(); self::assertEquals( - new MissingBinaryFile(['id' => 'my/path.png']), - $this->loadBinaryFileByUriNotFound() + new MissingBinaryFile(['id' => 'my/path.png', 'uri' => $prefixedUri]), + $binaryFile ); + + return $binaryFile; } } diff --git a/tests/lib/MVC/Symfony/Component/Serializer/Stubs/SerializerStub.php b/tests/lib/MVC/Symfony/Component/Serializer/Stubs/SerializerStub.php index 0da03526c4..3a2828469e 100644 --- a/tests/lib/MVC/Symfony/Component/Serializer/Stubs/SerializerStub.php +++ b/tests/lib/MVC/Symfony/Component/Serializer/Stubs/SerializerStub.php @@ -13,12 +13,18 @@ final class SerializerStub implements SerializerInterface, NormalizerInterface { - public function serialize($data, $format, array $context = []) + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + public function serialize(mixed $data, string $format, array $context = []): string { throw new NotImplementedException(__METHOD__); } - public function deserialize($data, $type, $format, array $context = []) + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + public function deserialize(mixed $data, string $type, string $format, array $context = []): mixed { throw new NotImplementedException(__METHOD__); } diff --git a/tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php b/tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php index 1c75dd39e1..18394243ef 100644 --- a/tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php +++ b/tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php @@ -15,23 +15,32 @@ use Ibexa\Core\Pagination\Pagerfanta\Pagerfanta; use Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter; use Ibexa\Core\Query\QueryFactoryInterface; -use Pagerfanta\Adapter\AdapterInterface; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; +/** + * @covers \Ibexa\Core\MVC\Symfony\Controller\QueryRenderController + * + * @phpstan-import-type TOptionsArray from \Ibexa\Core\MVC\Symfony\Controller\QueryRenderController + * + * @template TSearchHitValueObject of \Ibexa\Contracts\Core\Repository\Values\ValueObject + */ final class QueryRenderControllerTest extends TestCase { - private const EXAMPLE_CURRENT_PAGE = 3; - private const EXAMPLE_MAX_PER_PAGE = 100; + private const int EXAMPLE_CURRENT_PAGE = 3; + private const int EXAMPLE_MAX_PER_PAGE = 100; - private const MIN_OPTIONS = [ + /** @phpstan-var TOptionsArray */ + private const array MIN_OPTIONS = [ 'query' => [ 'query_type' => 'ExampleQuery', ], 'template' => 'example.html.twig', ]; - private const ALL_OPTIONS = [ + /** @phpstan-var TOptionsArray */ + private const array ALL_OPTIONS = [ 'query' => [ 'query_type' => 'ExampleQuery', 'parameters' => [ @@ -49,14 +58,11 @@ final class QueryRenderControllerTest extends TestCase ], ]; - /** @var \Ibexa\Core\Query\QueryFactoryInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $queryFactory; + private QueryFactoryInterface & MockObject $queryFactory; - /** @var \Ibexa\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactoryInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $searchHitAdapterFactory; + private SearchHitAdapterFactoryInterface & MockObject $searchHitAdapterFactory; - /** @var \Ibexa\Core\MVC\Symfony\Controller\QueryRenderController */ - private $controller; + private QueryRenderController $controller; protected function setUp(): void { @@ -84,6 +90,9 @@ public function testRenderQueryWithMinOptions(): void ); } + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ public function testRenderQueryWithAllOptions(): void { $adapter = $this->configureMocks(self::ALL_OPTIONS); @@ -102,7 +111,14 @@ public function testRenderQueryWithAllOptions(): void ); } - private function configureMocks(array $options): AdapterInterface + /** + * @phpstan-param TOptionsArray $options + * + * @template TItem + * + * @phpstan-return \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter + */ + private function configureMocks(array $options): SearchResultAdapter { $query = new Query(); @@ -124,6 +140,9 @@ private function configureMocks(array $options): AdapterInterface return $adapter; } + /** + * @phpstan-param TOptionsArray $options + */ private function assertRenderQueryResult( QueryView $expectedView, array $options, diff --git a/tests/lib/MVC/Symfony/Routing/RequestContextFactoryTest.php b/tests/lib/MVC/Symfony/Routing/RequestContextFactoryTest.php new file mode 100644 index 0000000000..85afc6c314 --- /dev/null +++ b/tests/lib/MVC/Symfony/Routing/RequestContextFactoryTest.php @@ -0,0 +1,75 @@ +getContextBySimplifiedRequest($simplifiedRequest); + + // expect cloned object + self::assertNotSame($requestContext, $context); + + self::assertEquals($expectedRequestContext, $context); + } + + /** + * @return iterable< + * string, + * array{ + * \Symfony\Component\Routing\RequestContext, + * \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest, + * \Symfony\Component\Routing\RequestContext + * }> + */ + public static function getDataForTestGetContextBySimplifiedRequest(): iterable + { + yield 'fully populated HTTP SimplifiedRequest' => [ + new RequestContext(), + new SimplifiedRequest( + [ + 'scheme' => 'http', + 'host' => 'localhost', + 'port' => '8080', + 'pathinfo' => 'foo', + ] + ), + new RequestContext('', 'GET', 'localhost', 'http', 8080, 443, 'foo'), + ]; + + yield 'fully populated HTTPS SimplifiedRequest' => [ + new RequestContext(), + new SimplifiedRequest( + [ + 'scheme' => 'https', + 'host' => 'localhost', + 'port' => '8443', + 'pathinfo' => 'foo', + ] + ), + new RequestContext('', 'GET', 'localhost', 'https', 80, 8443, 'foo'), + ]; + } +} diff --git a/tests/lib/MVC/Symfony/Security/Authentication/EventSubscriber/RepositoryUserAuthenticationSubscriberTest.php b/tests/lib/MVC/Symfony/Security/Authentication/EventSubscriber/RepositoryUserAuthenticationSubscriberTest.php index cbcc38092b..c305df6cbb 100644 --- a/tests/lib/MVC/Symfony/Security/Authentication/EventSubscriber/RepositoryUserAuthenticationSubscriberTest.php +++ b/tests/lib/MVC/Symfony/Security/Authentication/EventSubscriber/RepositoryUserAuthenticationSubscriberTest.php @@ -13,13 +13,14 @@ use Ibexa\Contracts\Core\Repository\UserService; use Ibexa\Core\MVC\Symfony\Security\Authentication\EventSubscriber\RepositoryUserAuthenticationSubscriber; use Ibexa\Core\MVC\Symfony\Security\User; +use Ibexa\Core\MVC\Symfony\Security\UserInterface as IbexaUserInterface; use Ibexa\Core\Repository\User\Exception\UnsupportedPasswordHashType; use Ibexa\Core\Repository\Values\User\User as APIUser; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials; @@ -148,7 +149,7 @@ private function getSubscriber( } private function getCheckPassportEvent( - ?UserInterface $user = null, + (User & MockObject)|null $user = null, ?Passport $passport = null, ): CheckPassportEvent { $authenticator = $this->createMock(AuthenticatorInterface::class); @@ -158,12 +159,15 @@ private function getCheckPassportEvent( $userProvider = $this->createMock(User\APIUserProviderInterface::class); $userProvider ->expects(self::once()) - ->method('loadUserByUsername') + ->method('loadUserByIdentifier') ->willReturn($user); $passport = new Passport( - new UserBadge($user->getUsername(), [$userProvider, 'loadUserByUsername']), - new PasswordCredentials($user->getPassword() ?? '') + new UserBadge( + $user->getUserIdentifier(), + static fn (string $userIdentifier): IbexaUserInterface => $userProvider->loadUserByIdentifier($userIdentifier) + ), + new PasswordCredentials($user->getPassword()) ); } diff --git a/tests/lib/MVC/Symfony/Security/InteractiveLoginTokenTest.php b/tests/lib/MVC/Symfony/Security/InteractiveLoginTokenTest.php deleted file mode 100644 index e029d0f3c1..0000000000 --- a/tests/lib/MVC/Symfony/Security/InteractiveLoginTokenTest.php +++ /dev/null @@ -1,54 +0,0 @@ -createMock(UserInterface::class); - $originalTokenType = 'FooBar'; - $credentials = 'my_credentials'; - $providerKey = 'key'; - $roles = ['ROLE_USER', 'ROLE_TEST', 'ROLE_FOO']; - $expectedRoles = []; - foreach ($roles as $role) { - if (is_string($role)) { - $expectedRoles[] = $role; - } else { - $expectedRoles[] = $role; - } - } - - $token = new InteractiveLoginToken($user, $originalTokenType, $credentials, $providerKey, $roles); - self::assertSame($user, $token->getUser()); - self::assertTrue($token->isAuthenticated()); - self::assertSame($originalTokenType, $token->getOriginalTokenType()); - self::assertSame($credentials, $token->getCredentials()); - self::assertSame($providerKey, $token->getFirewallName()); - self::assertEquals($expectedRoles, $token->getRoleNames()); - } - - public function testSerialize() - { - $user = $this->createMock(UserInterface::class); - $originalTokenType = 'FooBar'; - $credentials = 'my_credentials'; - $providerKey = 'key'; - $roles = ['ROLE_USER', 'ROLE_TEST', 'ROLE_FOO']; - - $token = new InteractiveLoginToken($user, $originalTokenType, $credentials, $providerKey, $roles); - $serialized = serialize($token); - $unserializedToken = unserialize($serialized); - self::assertEquals($token, $unserializedToken); - } -} diff --git a/tests/lib/MVC/Symfony/Security/UserWrappedTest.php b/tests/lib/MVC/Symfony/Security/UserWrappedTest.php index 3eb541a47e..b4ac50d84b 100644 --- a/tests/lib/MVC/Symfony/Security/UserWrappedTest.php +++ b/tests/lib/MVC/Symfony/Security/UserWrappedTest.php @@ -4,20 +4,21 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ +declare(strict_types=1); namespace Ibexa\Tests\Core\MVC\Symfony\Security; use Ibexa\Contracts\Core\Repository\Values\User\User as APIUser; use Ibexa\Core\MVC\Symfony\Security\UserInterface; use Ibexa\Core\MVC\Symfony\Security\UserWrapped; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\User\EquatableInterface; use Symfony\Component\Security\Core\User\UserInterface as SymfonyUserInterface; -class UserWrappedTest extends TestCase +final class UserWrappedTest extends TestCase { - /** @var \PHPUnit\Framework\MockObject\MockObject */ - private $apiUser; + private APIUser & MockObject $apiUser; protected function setUp(): void { @@ -25,7 +26,7 @@ protected function setUp(): void $this->apiUser = $this->createMock(APIUser::class); } - public function testGetSetAPIUser() + public function testGetSetAPIUser(): void { $originalUser = $this->createMock(SymfonyUserInterface::class); $userWrapped = new UserWrapped($originalUser, $this->apiUser); @@ -36,7 +37,7 @@ public function testGetSetAPIUser() self::assertSame($newApiUser, $userWrapped->getAPIUser()); } - public function testGetSetWrappedUser() + public function testGetSetWrappedUser(): void { $originalUser = $this->createMock(SymfonyUserInterface::class); $userWrapped = new UserWrapped($originalUser, $this->apiUser); @@ -47,7 +48,7 @@ public function testGetSetWrappedUser() self::assertSame($newWrappedUser, $userWrapped->getWrappedUser()); } - public function testRegularUser() + public function testRegularUser(): void { $originalUser = $this->createMock(SymfonyUserInterface::class); $user = new UserWrapped($originalUser, $this->apiUser); @@ -60,31 +61,19 @@ public function testRegularUser() $user->eraseCredentials(); $username = 'lolautruche'; - $password = 'NoThisIsNotMyRealPassword'; $roles = ['ROLE_USER', 'ROLE_TEST']; - $salt = md5(microtime(true)); $originalUser ->expects(self::exactly(2)) - ->method('getUsername') - ->will(self::returnValue($username)); - $originalUser - ->expects(self::once()) - ->method('getPassword') - ->will(self::returnValue($password)); + ->method('getUserIdentifier') + ->willReturn($username); $originalUser ->expects(self::once()) ->method('getRoles') - ->will(self::returnValue($roles)); - $originalUser - ->expects(self::once()) - ->method('getSalt') - ->will(self::returnValue($salt)); + ->willReturn($roles); - self::assertSame($username, $user->getUsername()); + self::assertSame($username, $user->getUserIdentifier()); self::assertSame($username, (string)$user); - self::assertSame($password, $user->getPassword()); self::assertSame($roles, $user->getRoles()); - self::assertSame($salt, $user->getSalt()); self::assertSame($originalUser, $user->getWrappedUser()); } diff --git a/tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest.php b/tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest.php index 75c206b855..6475e76f06 100644 --- a/tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest.php +++ b/tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest.php @@ -20,10 +20,16 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ControllerReference; use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; abstract class BaseRenderStrategyTest extends TestCase { + /** + * @phpstan-param class-string<\Ibexa\Contracts\Core\MVC\Templating\BaseRenderStrategy> $typeClass + * + * @param \Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface[] $fragmentRenderers + */ public function createRenderStrategy( string $typeClass, array $fragmentRenderers, @@ -48,19 +54,11 @@ public function createFragmentRenderer( string $name = 'inline', string $rendered = null ): FragmentRendererInterface { - return new class($name, $rendered) implements FragmentRendererInterface { - /** @var string */ - private $name; - - /** @var string */ - private $rendered; - + return new readonly class($name, $rendered) implements FragmentRendererInterface { public function __construct( - string $name, - ?string $rendered + private string $name, + private ?string $rendered ) { - $this->name = $name; - $this->rendered = $rendered; } public function getName(): string @@ -68,8 +66,11 @@ public function getName(): string return $this->name; } + /** + * @param array $options + */ public function render( - $uri, + string|ControllerReference $uri, Request $request, array $options = [] ): Response { diff --git a/tests/lib/MVC/Symfony/Translation/BaseMessageExtractorPhpFileVisitorTestCase.php b/tests/lib/MVC/Symfony/Translation/BaseMessageExtractorPhpFileVisitorTestCase.php new file mode 100644 index 0000000000..f2543a0fc0 --- /dev/null +++ b/tests/lib/MVC/Symfony/Translation/BaseMessageExtractorPhpFileVisitorTestCase.php @@ -0,0 +1,103 @@ +}> + */ + abstract public static function getDataForTestExtractTranslation(): iterable; + + abstract protected function buildVisitor( + DocParser $docParser, + FileSourceFactory $fileSourceFactory + ): FileVisitorInterface; + + protected function setUp(): void + { + $docParser = new DocParser(); + $fileSourceFactory = new FileSourceFactory(self::FIXTURES_DIR); + $factory = new ParserFactory(); + $this->phpParser = $factory->createForHostVersion(); + $this->visitor = $this->buildVisitor($docParser, $fileSourceFactory); + } + + /** + * @dataProvider getDataForTestExtractTranslation + * + * @param array<\JMS\TranslationBundle\Model\Message> $expectedMessages + */ + public function testExtractTranslation(string $phpFileName, array $expectedMessages): void + { + $messageCatalogue = new MessageCatalogue(); + $file = self::FIXTURES_DIR . $phpFileName; + $fileInfo = new SplFileInfo($file); + + $ast = $this->getASTFromFile($file); + $this->visitor->visitPhpFile( + $fileInfo, + $messageCatalogue, + $ast + ); + + foreach ($expectedMessages as $expectedMessage) { + self::assertTrue( + $messageCatalogue->has($expectedMessage), + 'Message catalogue does not have the expected message: ' . var_export($expectedMessage, true) . PHP_EOL + . 'Current message catalogue structure: ' . var_export($messageCatalogue, true) + ); + } + } + + public function testNoTranslationToExtract(): void + { + $messageCatalogue = new MessageCatalogue(); + $file = self::FIXTURES_DIR . 'NoTranslationToExtract.php'; + $fileInfo = new SplFileInfo($file); + + $ast = $this->getASTFromFile($file); + $this->visitor->visitPhpFile( + $fileInfo, + $messageCatalogue, + $ast + ); + + self::assertEmpty($messageCatalogue->getDomains()); + } + + /** + * @return \PhpParser\Node\Stmt[] + */ + protected function getASTFromFile(string $filePath): array + { + $fileContents = file_get_contents($filePath); + assert($fileContents !== false, "Failed to read $filePath"); + + $ast = $this->phpParser->parse($fileContents); + assert($ast !== null, "Failed to parse AST of $filePath"); + + return $ast; + } +} diff --git a/tests/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitorTest.php b/tests/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitorTest.php index 9dd7874e92..460618f849 100644 --- a/tests/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitorTest.php +++ b/tests/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitorTest.php @@ -10,93 +10,58 @@ use Doctrine\Common\Annotations\DocParser; use Ibexa\Core\MVC\Symfony\Translation\ExceptionMessageTemplateFileVisitor; +use JMS\TranslationBundle\Logger\LoggerAwareInterface; use JMS\TranslationBundle\Model\Message; use JMS\TranslationBundle\Model\MessageCatalogue; +use JMS\TranslationBundle\Translation\Extractor\FileVisitorInterface; use JMS\TranslationBundle\Translation\FileSourceFactory; -use PhpParser\Lexer; -use PhpParser\Parser; -use PhpParser\ParserFactory; -use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use SplFileInfo; -final class ExceptionMessageTemplateFileVisitorTest extends TestCase +/** + * @covers \Ibexa\Core\MVC\Symfony\Translation\ExceptionMessageTemplateFileVisitor + */ +final class ExceptionMessageTemplateFileVisitorTest extends BaseMessageExtractorPhpFileVisitorTestCase { - private const FIXTURES_DIR = __DIR__ . '/fixtures/'; - - private Parser $phpParser; - - private ExceptionMessageTemplateFileVisitor $exceptionMessageTemplateFileVisitor; - - protected function setUp(): void + public static function getDataForTestExtractTranslation(): iterable { - $docParser = new DocParser(); - $fileSourceFactory = new FileSourceFactory( - self::FIXTURES_DIR, - ); - $lexer = new Lexer(); - $factory = new ParserFactory(); - $this->phpParser = $factory->create(ParserFactory::PREFER_PHP7, $lexer); - $this->exceptionMessageTemplateFileVisitor = new ExceptionMessageTemplateFileVisitor( - $docParser, - $fileSourceFactory - ); + yield 'TranslatableBase::setMessageTemplate()' => [ + 'SetMessageTemplate.php', + [ + new Message('Foo exception', 'ibexa_repository_exceptions'), + ], + ]; } - public function testExtractTranslation(): void + public function testWrongTranslationId(): void { $messageCatalogue = new MessageCatalogue(); - $file = self::FIXTURES_DIR . 'SetMessageTemplate.php'; + $file = self::FIXTURES_DIR . 'WrongTranslationId.php'; $fileInfo = new SplFileInfo($file); - $ast = $this->phpParser->parse(file_get_contents($file)); - $this->exceptionMessageTemplateFileVisitor->visitPhpFile( - $fileInfo, - $messageCatalogue, - $ast - ); - - $expectedMessage = new Message('Foo exception', 'ibexa_repository_exceptions'); + $ast = $this->getASTFromFile($file); - self::assertTrue( - $messageCatalogue->has($expectedMessage) - ); - } + if ($this->visitor instanceof LoggerAwareInterface) { + $logger = $this->createMock(LoggerInterface::class); + $logger + ->expects(self::once()) + ->method('error'); - public function testNoTranslationToExtract(): void - { - $messageCatalogue = new MessageCatalogue(); - $file = self::FIXTURES_DIR . 'NoTranslationToExtract.php'; - $fileInfo = new SplFileInfo($file); + $this->visitor->setLogger($logger); + } - $ast = $this->phpParser->parse(file_get_contents($file)); - $this->exceptionMessageTemplateFileVisitor->visitPhpFile( + $this->visitor->visitPhpFile( $fileInfo, $messageCatalogue, $ast ); - - self::assertEmpty($messageCatalogue->getDomains()); } - public function testWrongTranslationId(): void + protected function buildVisitor(DocParser $docParser, FileSourceFactory $fileSourceFactory): FileVisitorInterface { - $messageCatalogue = new MessageCatalogue(); - $file = self::FIXTURES_DIR . 'WrongTranslationId.php'; - $fileInfo = new SplFileInfo($file); - - $ast = $this->phpParser->parse(file_get_contents($file)); - - $logger = $this->createMock(LoggerInterface::class); - $logger - ->expects(self::once()) - ->method('error'); - - $this->exceptionMessageTemplateFileVisitor->setLogger($logger); - $this->exceptionMessageTemplateFileVisitor->visitPhpFile( - $fileInfo, - $messageCatalogue, - $ast + return new ExceptionMessageTemplateFileVisitor( + $docParser, + $fileSourceFactory ); } } diff --git a/tests/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitorTest.php b/tests/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitorTest.php new file mode 100644 index 0000000000..88fa94ac16 --- /dev/null +++ b/tests/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitorTest.php @@ -0,0 +1,43 @@ + [ + 'ContentValidationExceptionUsageStub.php', + [ + new Message('Content with ID %contentId% could not be found', 'ibexa_repository_exceptions'), + ], + ]; + + yield 'throw new ForbiddenException()' => [ + 'ForbiddenExceptionUsageStub.php', + [ + new Message('Forbidden exception', 'ibexa_repository_exceptions'), + ], + ]; + } + + protected function buildVisitor(DocParser $docParser, FileSourceFactory $fileSourceFactory): FileVisitorInterface + { + return new TranslatableExceptionsFileVisitor($docParser, $fileSourceFactory); + } +} diff --git a/tests/lib/MVC/Symfony/Translation/ValidationErrorFileVisitorTest.php b/tests/lib/MVC/Symfony/Translation/ValidationErrorFileVisitorTest.php new file mode 100644 index 0000000000..9dbc44e9a3 --- /dev/null +++ b/tests/lib/MVC/Symfony/Translation/ValidationErrorFileVisitorTest.php @@ -0,0 +1,44 @@ +}> + */ + public static function getDataForTestExtractTranslation(): iterable + { + yield 'new ValidationError()' => [ + 'ValidationErrorUsageStub.php', + [ + new Message('error_1.singular_only', 'ibexa_repository_exceptions'), + new Message('error_2.singular', 'ibexa_repository_exceptions'), + new Message('error_2.plural', 'ibexa_repository_exceptions'), + ], + ]; + } + + protected function buildVisitor(DocParser $docParser, FileSourceFactory $fileSourceFactory): FileVisitorInterface + { + return new ValidationErrorFileVisitor( + $docParser, + $fileSourceFactory + ); + } +} diff --git a/tests/lib/MVC/Symfony/Translation/fixtures/ContentValidationExceptionUsageStub.php b/tests/lib/MVC/Symfony/Translation/fixtures/ContentValidationExceptionUsageStub.php new file mode 100644 index 0000000000..6235c3b8a0 --- /dev/null +++ b/tests/lib/MVC/Symfony/Translation/fixtures/ContentValidationExceptionUsageStub.php @@ -0,0 +1,28 @@ + 123] + ); + } +} diff --git a/tests/lib/MVC/Symfony/Translation/fixtures/ForbiddenExceptionUsageStub.php b/tests/lib/MVC/Symfony/Translation/fixtures/ForbiddenExceptionUsageStub.php new file mode 100644 index 0000000000..e40290d0bb --- /dev/null +++ b/tests/lib/MVC/Symfony/Translation/fixtures/ForbiddenExceptionUsageStub.php @@ -0,0 +1,25 @@ + + */ +abstract class BaseContentSearchResultAdapterTestCase extends BaseSearchResultAdapterTestCase +{ + protected function mockQueryForGetNbResults(int $nbResults): Query + { + $query = $this->createTestQuery(); + + // Count query will necessarily have a 0 limit and empty aggregations/facet builders. + $countQuery = clone $query; + $countQuery->limit = 0; + $countQuery->aggregations = []; + + $searchResult = new SearchResult(['totalCount' => $nbResults]); + $this->searchService + ->expects(self::once()) + ->method('findContent') + ->with($countQuery, self::EXAMPLE_LANGUAGE_FILTER) + ->willReturn($searchResult) + ; + + return $query; + } + + /** + * @phpstan-return list<\Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit<\Ibexa\Contracts\Core\Repository\Values\ValueObject>> + */ + protected function mockSearchHitsForGetSlice( + Query $query, + int $nbResults, + AggregationResultCollection $aggregationsResults + ): array { + // Injected query is being cloned to modify offset/limit, + // so we need to do the same here for our assertions. + $searchQuery = clone $query; + $searchQuery->offset = self::EXAMPLE_OFFSET; + $searchQuery->limit = self::EXAMPLE_LIMIT; + $searchQuery->performCount = false; + + $hits = []; + for ($i = 0; $i < self::EXAMPLE_LIMIT; ++$i) { + $hits[] = new SearchHit( + [ + 'valueObject' => $this->createMock(APIContent::class), + ] + ); + } + + $searchResult = new SearchResult( + [ + 'searchHits' => $hits, + 'totalCount' => $nbResults, + 'aggregations' => $aggregationsResults, + 'maxScore' => self::EXAMPLE_RESULT_MAX_SCORE, + 'timedOut' => true, + 'time' => self::EXAMPLE_RESULT_TIME, + ] + ); + + $this + ->searchService + ->expects(self::once()) + ->method('findContent') + ->with($searchQuery, self::EXAMPLE_LANGUAGE_FILTER) + ->willReturn($searchResult) + ; + + return $hits; + } + + protected function mockQueryForGetAggregations(AggregationResultCollection $exceptedAggregationsResults): Query + { + $query = $this->createTestQuery(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT); + + // Injected query is being cloned to modify offset/limit, + // so we need to do the same here for our assertions. + $aggregationQuery = clone $query; + $aggregationQuery->offset = 0; + $aggregationQuery->limit = 0; + + $searchResult = new SearchResult( + [ + 'searchHits' => [], + 'totalCount' => 0, + 'aggregations' => $exceptedAggregationsResults, + ] + ); + + $this + ->searchService + ->expects(self::once()) + ->method('findContent') + ->with($aggregationQuery, self::EXAMPLE_LANGUAGE_FILTER) + ->willReturn($searchResult) + ; + + return $query; + } + + protected function createTestQuery(int $limit = 25, int $offset = 0): Query + { + $query = new Query(); + $query->query = $this->createMock(CriterionInterface::class); + $query->aggregations[] = $this->createMock(Aggregation::class); + $query->sortClauses[] = $this->createMock(SortClause::class); + $query->offset = $offset; + $query->limit = $limit; + + return $query; + } +} diff --git a/tests/lib/Pagination/BaseLocationSearchResultAdapterTestCase.php b/tests/lib/Pagination/BaseLocationSearchResultAdapterTestCase.php new file mode 100644 index 0000000000..3453dc8791 --- /dev/null +++ b/tests/lib/Pagination/BaseLocationSearchResultAdapterTestCase.php @@ -0,0 +1,139 @@ + + */ +abstract class BaseLocationSearchResultAdapterTestCase extends BaseSearchResultAdapterTestCase +{ + protected function mockQueryForGetNbResults(int $nbResults): LocationQuery + { + $query = $this->createTestQuery(); + + // Count query will necessarily have a 0 limit. + $countQuery = clone $query; + $countQuery->aggregations = []; + $countQuery->limit = 0; + + $searchResult = new SearchResult( + [ + 'totalCount' => $nbResults, + ] + ); + + $this->searchService + ->expects(self::once()) + ->method('findLocations') + ->with($countQuery, self::EXAMPLE_LANGUAGE_FILTER) + ->willReturn($searchResult) + ; + + return $query; + } + + /** + * @phpstan-return list<\Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit<\Ibexa\Contracts\Core\Repository\Values\ValueObject>> + */ + protected function mockSearchHitsForGetSlice( + LocationQuery $query, + int $nbResults, + AggregationResultCollection $aggregationsResults + ): array { + // Injected query is being cloned to modify offset/limit, + // so we need to do the same here for our assertions. + $searchQuery = clone $query; + $searchQuery->offset = self::EXAMPLE_OFFSET; + $searchQuery->limit = self::EXAMPLE_LIMIT; + $searchQuery->performCount = false; + + $hits = []; + for ($i = 0; $i < self::EXAMPLE_LIMIT; ++$i) { + $hits[] = new SearchHit( + [ + 'valueObject' => $this->createMock(APILocation::class), + ] + ); + } + + $searchResult = new SearchResult( + [ + 'searchHits' => $hits, + 'totalCount' => $nbResults, + 'aggregations' => $aggregationsResults, + 'maxScore' => self::EXAMPLE_RESULT_MAX_SCORE, + 'timedOut' => true, + 'time' => self::EXAMPLE_RESULT_TIME, + ] + ); + + $this + ->searchService + ->expects(self::once()) + ->method('findLocations') + ->with($searchQuery, self::EXAMPLE_LANGUAGE_FILTER) + ->willReturn($searchResult) + ; + + return $hits; + } + + protected function mockQueryForGetAggregations( + AggregationResultCollection $expectedAggregationsResults + ): LocationQuery { + $query = $this->createTestQuery(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT); + + // Injected query is being cloned to modify offset/limit, + // so we need to do the same here for our assertions. + $aggregationQuery = clone $query; + $aggregationQuery->offset = 0; + $aggregationQuery->limit = 0; + + $searchResult = new SearchResult( + [ + 'searchHits' => [], + 'totalCount' => 0, + 'aggregations' => $expectedAggregationsResults, + ] + ); + + $this + ->searchService + ->expects(self::once()) + ->method('findLocations') + ->with($aggregationQuery, self::EXAMPLE_LANGUAGE_FILTER) + ->willReturn($searchResult) + ; + + return $query; + } + + protected function createTestQuery(int $limit = 25, int $offset = 0): LocationQuery + { + $query = new LocationQuery(); + $query->query = $this->createMock(CriterionInterface::class); + $query->aggregations[] = $this->createMock(Aggregation::class); + $query->sortClauses[] = $this->createMock(SortClause::class); + $query->offset = $offset; + $query->limit = $limit; + + return $query; + } +} diff --git a/tests/lib/Pagination/BaseSearchResultAdapterTestCase.php b/tests/lib/Pagination/BaseSearchResultAdapterTestCase.php new file mode 100644 index 0000000000..cb98cd5c1e --- /dev/null +++ b/tests/lib/Pagination/BaseSearchResultAdapterTestCase.php @@ -0,0 +1,61 @@ + ['eng-GB', 'pol-PL'], + 'useAlwaysAvailable' => true, + ]; + + protected const float EXAMPLE_RESULT_MAX_SCORE = 5.123; + protected const float EXAMPLE_RESULT_TIME = 30.0; + + protected SearchService & MockObject $searchService; + + protected function setUp(): void + { + parent::setUp(); + $this->searchService = $this->createMock(SearchService::class); + } + + abstract public function testGetNbResults(): void; + + abstract public function testGetSlice(): void; + + abstract public function testGetAggregations(): void; + + /** + * @phpstan-param TSearchResultAdapter $adapter + */ + protected function assertSearchResult( + int $nbResults, + SearchResultAdapter $adapter, + AggregationResultCollection $aggregationsResults + ): void { + self::assertSame($nbResults, $adapter->getNbResults()); + self::assertSame($aggregationsResults, $adapter->getAggregations()); + self::assertSame(self::EXAMPLE_RESULT_MAX_SCORE, $adapter->getMaxScore()); + self::assertTrue($adapter->getTimedOut()); + self::assertSame(self::EXAMPLE_RESULT_TIME, $adapter->getTime()); + } +} diff --git a/tests/lib/Pagination/ContentSearchAdapterTest.php b/tests/lib/Pagination/ContentSearchAdapterTest.php index ecd6475fb3..9ff0b6d8a4 100644 --- a/tests/lib/Pagination/ContentSearchAdapterTest.php +++ b/tests/lib/Pagination/ContentSearchAdapterTest.php @@ -4,43 +4,71 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ +declare(strict_types=1); namespace Ibexa\Tests\Core\Pagination; use Ibexa\Contracts\Core\Repository\SearchService; use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; use Ibexa\Core\Pagination\Pagerfanta\ContentSearchAdapter; -class ContentSearchAdapterTest extends ContentSearchHitAdapterTest +/** + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService + * + * @extends \Ibexa\Tests\Core\Pagination\BaseContentSearchResultAdapterTestCase<\Ibexa\Core\Pagination\Pagerfanta\ContentSearchAdapter> + */ +final class ContentSearchAdapterTest extends BaseContentSearchResultAdapterTestCase { /** - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query - * @param \Ibexa\Contracts\Core\Repository\SearchService $searchService - * @param array $languageFilter - * - * @return \Ibexa\Core\Pagination\Pagerfanta\ContentSearchAdapter + * @phpstan-param TSearchLanguageFilter $languageFilter */ - protected function getAdapter(Query $query, SearchService $searchService, array $languageFilter = []) + protected function getAdapter(Query $query, SearchService $searchService, array $languageFilter = []): ContentSearchAdapter { return new ContentSearchAdapter($query, $searchService, $languageFilter); } - /** - * Returns expected result from adapter from search hits. - * - * @param $hits - * - * @return mixed - */ - protected function getExpectedFinalResultFromHits($hits) + public function testGetNbResults(): void { - $expectedResult = []; + $nbResults = 123; + + $query = $this->mockQueryForGetNbResults($nbResults); + + $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); + + self::assertSame($nbResults, $adapter->getNbResults()); + // Running a 2nd time to ensure SearchService::findContent() is called only once. + self::assertSame($nbResults, $adapter->getNbResults()); + } + + public function testGetSlice(): void + { + $aggregationsResults = new AggregationResultCollection(); + $nbResults = 123; + + $query = $this->createTestQuery(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT); + $hits = $this->mockSearchHitsForGetSlice($query, $nbResults, $aggregationsResults); + + $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); + + self::assertSame( + array_map(static fn (SearchHit $hit) => $hit->valueObject, $hits), + $adapter->getSlice(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT) + ); + $this->assertSearchResult($nbResults, $adapter, $aggregationsResults); + } + + public function testGetAggregations(): void + { + $exceptedAggregationsResults = new AggregationResultCollection(); + + $query = $this->mockQueryForGetAggregations($exceptedAggregationsResults); - /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit[] $hits */ - foreach ($hits as $hit) { - $expectedResult[] = $hit->valueObject; - } + $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); - return $expectedResult; + self::assertSame($exceptedAggregationsResults, $adapter->getAggregations()); + // Running a 2nd time to ensure SearchService::findContent() is called only once. + self::assertSame($exceptedAggregationsResults, $adapter->getAggregations()); } } diff --git a/tests/lib/Pagination/ContentSearchHitAdapterTest.php b/tests/lib/Pagination/ContentSearchHitAdapterTest.php index 9abb45c145..8e549a9acf 100644 --- a/tests/lib/Pagination/ContentSearchHitAdapterTest.php +++ b/tests/lib/Pagination/ContentSearchHitAdapterTest.php @@ -4,35 +4,24 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ +declare(strict_types=1); namespace Ibexa\Tests\Core\Pagination; use Ibexa\Contracts\Core\Repository\SearchService; -use Ibexa\Contracts\Core\Repository\Values\Content\Content as APIContent; use Ibexa\Contracts\Core\Repository\Values\Content\Query; -use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; -use Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface; -use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; -use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; -use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; use Ibexa\Core\Pagination\Pagerfanta\ContentSearchHitAdapter; -use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; -class ContentSearchHitAdapterTest extends TestCase +/** + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService + * + * @extends \Ibexa\Tests\Core\Pagination\BaseContentSearchResultAdapterTestCase<\Ibexa\Core\Pagination\Pagerfanta\ContentSearchHitAdapter> + */ +final class ContentSearchHitAdapterTest extends BaseContentSearchResultAdapterTestCase { - private const EXAMPLE_LIMIT = 40; - private const EXAMPLE_OFFSET = 10; - private const EXAMPLE_LANGUAGE_FILTER = [ - 'languages' => ['eng-GB', 'pol-PL'], - 'useAlwaysAvailable' => true, - ]; - - private const EXAMPLE_RESULT_MAX_SCORE = 5.123; - private const EXAMPLE_RESULT_TIME = 30.0; - - /** @var \Ibexa\Contracts\Core\Repository\SearchService|\PHPUnit\Framework\MockObject\MockObject */ - protected $searchService; + protected SearchService & MockObject $searchService; protected function setUp(): void { @@ -41,36 +30,21 @@ protected function setUp(): void } /** - * Returns the adapter to test. - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query - * @param \Ibexa\Contracts\Core\Repository\SearchService $searchService - * @param array $languageFilter - * - * @return \Ibexa\Core\Pagination\Pagerfanta\ContentSearchHitAdapter + * @phpstan-param TSearchLanguageFilter $languageFilter */ - protected function getAdapter(Query $query, SearchService $searchService, array $languageFilter = []) - { + protected function getAdapter( + Query $query, + SearchService $searchService, + array $languageFilter = [] + ): ContentSearchHitAdapter { return new ContentSearchHitAdapter($query, $searchService, $languageFilter); } - public function testGetNbResults() + public function testGetNbResults(): void { $nbResults = 123; - $query = $this->createTestQuery(); - - // Count query will necessarily have a 0 limit and empty aggregations/facet builders. - $countQuery = clone $query; - $countQuery->limit = 0; - $countQuery->aggregations = []; - - $searchResult = new SearchResult(['totalCount' => $nbResults]); - $this->searchService - ->expects(self::once()) - ->method('findContent') - ->with($countQuery, self::EXAMPLE_LANGUAGE_FILTER) - ->willReturn($searchResult); + $query = $this->mockQueryForGetNbResults($nbResults); $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); @@ -79,80 +53,28 @@ public function testGetNbResults() self::assertSame($nbResults, $adapter->getNbResults()); } - public function testGetSlice() + public function testGetSlice(): void { - $nbResults = 123; $aggregationsResults = new AggregationResultCollection(); + $nbResults = 123; $query = $this->createTestQuery(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT); - - // Injected query is being cloned to modify offset/limit, - // so we need to do the same here for our assertions. - $searchQuery = clone $query; - $searchQuery->offset = self::EXAMPLE_OFFSET; - $searchQuery->limit = self::EXAMPLE_LIMIT; - $searchQuery->performCount = false; - - $hits = []; - for ($i = 0; $i < self::EXAMPLE_LIMIT; ++$i) { - $hits[] = new SearchHit([ - 'valueObject' => $this->createMock(APIContent::class), - ]); - } - - $searchResult = new SearchResult([ - 'searchHits' => $hits, - 'totalCount' => $nbResults, - 'aggregations' => $aggregationsResults, - 'maxScore' => self::EXAMPLE_RESULT_MAX_SCORE, - 'timedOut' => true, - 'time' => self::EXAMPLE_RESULT_TIME, - ]); - - $this - ->searchService - ->expects(self::once()) - ->method('findContent') - ->with($searchQuery, self::EXAMPLE_LANGUAGE_FILTER) - ->willReturn($searchResult); + $hits = $this->mockSearchHitsForGetSlice($query, $nbResults, $aggregationsResults); $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); self::assertSame( - $this->getExpectedFinalResultFromHits($hits), + $hits, $adapter->getSlice(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT) ); - self::assertSame($nbResults, $adapter->getNbResults()); - self::assertSame($aggregationsResults, $adapter->getAggregations()); - self::assertSame(self::EXAMPLE_RESULT_MAX_SCORE, $adapter->getMaxScore()); - self::assertTrue($adapter->getTimedOut()); - self::assertSame(self::EXAMPLE_RESULT_TIME, $adapter->getTime()); + $this->assertSearchResult($nbResults, $adapter, $aggregationsResults); } public function testGetAggregations(): void { $exceptedAggregationsResults = new AggregationResultCollection(); - $query = $this->createTestQuery(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT); - - // Injected query is being cloned to modify offset/limit, - // so we need to do the same here for our assertions. - $aggregationQuery = clone $query; - $aggregationQuery->offset = 0; - $aggregationQuery->limit = 0; - - $searchResult = new SearchResult([ - 'searchHits' => [], - 'totalCount' => 0, - 'aggregations' => $exceptedAggregationsResults, - ]); - - $this - ->searchService - ->expects(self::once()) - ->method('findContent') - ->with($aggregationQuery, self::EXAMPLE_LANGUAGE_FILTER) - ->willReturn($searchResult); + $query = $this->mockQueryForGetAggregations($exceptedAggregationsResults); $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); @@ -160,28 +82,4 @@ public function testGetAggregations(): void // Running a 2nd time to ensure SearchService::findContent() is called only once. self::assertSame($exceptedAggregationsResults, $adapter->getAggregations()); } - - /** - * Returns expected result from adapter from search hits. - * - * @param $hits - * - * @return mixed - */ - protected function getExpectedFinalResultFromHits($hits) - { - return $hits; - } - - private function createTestQuery(int $limit = 25, int $offset = 0): Query - { - $query = new Query(); - $query->query = $this->createMock(CriterionInterface::class); - $query->aggregations[] = $this->createMock(Aggregation::class); - $query->sortClauses[] = $this->createMock(SortClause::class); - $query->offset = $offset; - $query->limit = $limit; - - return $query; - } } diff --git a/tests/lib/Pagination/LocationSearchAdapterTest.php b/tests/lib/Pagination/LocationSearchAdapterTest.php index eea38eb32c..86cd980bd5 100644 --- a/tests/lib/Pagination/LocationSearchAdapterTest.php +++ b/tests/lib/Pagination/LocationSearchAdapterTest.php @@ -4,42 +4,74 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ +declare(strict_types=1); namespace Ibexa\Tests\Core\Pagination; use Ibexa\Contracts\Core\Repository\SearchService; use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; use Ibexa\Core\Pagination\Pagerfanta\LocationSearchAdapter; -class LocationSearchAdapterTest extends LocationSearchHitAdapterTest +/** + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService + * + * @extends \Ibexa\Tests\Core\Pagination\BaseLocationSearchResultAdapterTestCase<\Ibexa\Core\Pagination\Pagerfanta\LocationSearchAdapter> + */ +final class LocationSearchAdapterTest extends BaseLocationSearchResultAdapterTestCase { /** - * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery $query - * @param \Ibexa\Contracts\Core\Repository\SearchService $searchService - * - * @return \Ibexa\Core\Pagination\Pagerfanta\LocationSearchAdapter + * @phpstan-param TSearchLanguageFilter $languageFilter */ - protected function getAdapter(LocationQuery $query, SearchService $searchService, array $languageFilter = []) + protected function getAdapter(LocationQuery $query, SearchService $searchService, array $languageFilter = []): LocationSearchAdapter { return new LocationSearchAdapter($query, $searchService, $languageFilter); } - /** - * Returns expected result from adapter from search hits. - * - * @param $hits - * - * @return mixed - */ - protected function getExpectedFinalResultFromHits($hits) + public function testGetNbResults(): void + { + $nbResults = 123; + $query = $this->mockQueryForGetNbResults($nbResults); + + $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); + + self::assertSame($nbResults, $adapter->getNbResults()); + // Running a 2nd time to ensure SearchService::findContent() is called only once. + self::assertSame($nbResults, $adapter->getNbResults()); + } + + public function testGetSlice(): void + { + $nbResults = 123; + $aggregationsResults = new AggregationResultCollection(); + $query = $this->createTestQuery(); + $hits = $this->mockSearchHitsForGetSlice($query, $nbResults, $aggregationsResults); + + $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); + + self::assertSame( + array_map(static fn (SearchHit $hit) => $hit->valueObject, $hits), + $adapter->getSlice(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT) + ); + + self::assertSame($nbResults, $adapter->getNbResults()); + self::assertSame($aggregationsResults, $adapter->getAggregations()); + self::assertSame(self::EXAMPLE_RESULT_MAX_SCORE, $adapter->getMaxScore()); + self::assertTrue($adapter->getTimedOut()); + self::assertSame(self::EXAMPLE_RESULT_TIME, $adapter->getTime()); + } + + public function testGetAggregations(): void { - $expectedResult = []; + $expectedAggregationsResults = new AggregationResultCollection(); + + $query = $this->mockQueryForGetAggregations($expectedAggregationsResults); - /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit[] $hits */ - foreach ($hits as $hit) { - $expectedResult[] = $hit->valueObject; - } + $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); - return $expectedResult; + self::assertSame($expectedAggregationsResults, $adapter->getAggregations()); + // Running a 2nd time to ensure SearchService::findContent() is called only once. + self::assertSame($expectedAggregationsResults, $adapter->getAggregations()); } } diff --git a/tests/lib/Pagination/LocationSearchHitAdapterTest.php b/tests/lib/Pagination/LocationSearchHitAdapterTest.php index 6298ec5b44..60d8eaa098 100644 --- a/tests/lib/Pagination/LocationSearchHitAdapterTest.php +++ b/tests/lib/Pagination/LocationSearchHitAdapterTest.php @@ -4,76 +4,36 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ +declare(strict_types=1); namespace Ibexa\Tests\Core\Pagination; use Ibexa\Contracts\Core\Repository\SearchService; -use Ibexa\Contracts\Core\Repository\Values\Content\Location as APILocation; use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; -use Ibexa\Contracts\Core\Repository\Values\Content\Query; -use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; -use Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface; -use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; -use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; -use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; use Ibexa\Core\Pagination\Pagerfanta\LocationSearchHitAdapter; -use PHPUnit\Framework\TestCase; -class LocationSearchHitAdapterTest extends TestCase +/** + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService + * + * @extends \Ibexa\Tests\Core\Pagination\BaseLocationSearchResultAdapterTestCase<\Ibexa\Core\Pagination\Pagerfanta\LocationSearchHitAdapter> + */ +final class LocationSearchHitAdapterTest extends BaseLocationSearchResultAdapterTestCase { - private const EXAMPLE_LIMIT = 40; - private const EXAMPLE_OFFSET = 10; - private const EXAMPLE_LANGUAGE_FILTER = [ - 'languages' => ['eng-GB', 'pol-PL'], - 'useAlwaysAvailable' => true, - ]; - - private const EXAMPLE_RESULT_MAX_SCORE = 5.123; - private const EXAMPLE_RESULT_TIME = 30.0; - - /** @var \Ibexa\Contracts\Core\Repository\SearchService|\PHPUnit\Framework\MockObject\MockObject */ - protected $searchService; - - protected function setUp(): void - { - parent::setUp(); - $this->searchService = $this->createMock(SearchService::class); - } - /** * Returns the adapter to test. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery $query - * @param \Ibexa\Contracts\Core\Repository\SearchService $searchService - * @param array $languageFilter - * - * @return \Ibexa\Core\Pagination\Pagerfanta\LocationSearchHitAdapter + * @phpstan-param TSearchLanguageFilter $languageFilter */ - protected function getAdapter(LocationQuery $query, SearchService $searchService, array $languageFilter = []) + protected function getAdapter(LocationQuery $query, SearchService $searchService, array $languageFilter = []): LocationSearchHitAdapter { return new LocationSearchHitAdapter($query, $searchService, $languageFilter); } - public function testGetNbResults() + public function testGetNbResults(): void { $nbResults = 123; - $query = $this->createTestQuery(); - - // Count query will necessarily have a 0 limit. - $countQuery = clone $query; - $countQuery->aggregations = []; - $countQuery->limit = 0; - - $searchResult = new SearchResult([ - 'totalCount' => $nbResults, - ]); - - $this->searchService - ->expects(self::once()) - ->method('findLocations') - ->with($countQuery, self::EXAMPLE_LANGUAGE_FILTER) - ->willReturn($searchResult); + $query = $this->mockQueryForGetNbResults($nbResults); $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); @@ -82,110 +42,33 @@ public function testGetNbResults() self::assertSame($nbResults, $adapter->getNbResults()); } - public function testGetSlice() + public function testGetSlice(): void { $nbResults = 123; $aggregationsResults = new AggregationResultCollection(); - $query = $this->createTestQuery(); - - // Injected query is being cloned to modify offset/limit, - // so we need to do the same here for our assertions. - $searchQuery = clone $query; - $searchQuery->offset = self::EXAMPLE_OFFSET; - $searchQuery->limit = self::EXAMPLE_LIMIT; - $searchQuery->performCount = false; - - $hits = []; - for ($i = 0; $i < self::EXAMPLE_LIMIT; ++$i) { - $hits[] = new SearchHit([ - 'valueObject' => $this->createMock(APILocation::class), - ]); - } - - $searchResult = new SearchResult([ - 'searchHits' => $hits, - 'totalCount' => $nbResults, - 'aggregations' => $aggregationsResults, - 'maxScore' => self::EXAMPLE_RESULT_MAX_SCORE, - 'timedOut' => true, - 'time' => self::EXAMPLE_RESULT_TIME, - ]); - - $this - ->searchService - ->expects(self::once()) - ->method('findLocations') - ->with($searchQuery, self::EXAMPLE_LANGUAGE_FILTER) - ->willReturn($searchResult); + $hits = $this->mockSearchHitsForGetSlice($query, $nbResults, $aggregationsResults); $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); self::assertSame( - $this->getExpectedFinalResultFromHits($hits), + $hits, $adapter->getSlice(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT) ); - self::assertSame($nbResults, $adapter->getNbResults()); - self::assertSame($aggregationsResults, $adapter->getAggregations()); - self::assertSame(self::EXAMPLE_RESULT_MAX_SCORE, $adapter->getMaxScore()); - self::assertTrue($adapter->getTimedOut()); - self::assertSame(self::EXAMPLE_RESULT_TIME, $adapter->getTime()); + $this->assertSearchResult($nbResults, $adapter, $aggregationsResults); } public function testGetAggregations(): void { - $exceptedAggregationsResults = new AggregationResultCollection(); - - $query = $this->createTestQuery(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT); - - // Injected query is being cloned to modify offset/limit, - // so we need to do the same here for our assertions. - $aggregationQuery = clone $query; - $aggregationQuery->offset = 0; - $aggregationQuery->limit = 0; + $expectedAggregationsResults = new AggregationResultCollection(); - $searchResult = new SearchResult([ - 'searchHits' => [], - 'totalCount' => 0, - 'aggregations' => $exceptedAggregationsResults, - ]); - - $this - ->searchService - ->expects(self::once()) - ->method('findLocations') - ->with($aggregationQuery, self::EXAMPLE_LANGUAGE_FILTER) - ->willReturn($searchResult); + $query = $this->mockQueryForGetAggregations($expectedAggregationsResults); $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); - self::assertSame($exceptedAggregationsResults, $adapter->getAggregations()); + self::assertSame($expectedAggregationsResults, $adapter->getAggregations()); // Running a 2nd time to ensure SearchService::findContent() is called only once. - self::assertSame($exceptedAggregationsResults, $adapter->getAggregations()); - } - - /** - * Returns expected result from adapter from search hits. - * - * @param $hits - * - * @return mixed - */ - protected function getExpectedFinalResultFromHits($hits) - { - return $hits; - } - - private function createTestQuery(int $limit = 25, int $offset = 0): LocationQuery - { - $query = new LocationQuery(); - $query->query = $this->createMock(CriterionInterface::class); - $query->aggregations[] = $this->createMock(Aggregation::class); - $query->sortClauses[] = $this->createMock(SortClause::class); - $query->offset = $offset; - $query->limit = $limit; - - return $query; + self::assertSame($expectedAggregationsResults, $adapter->getAggregations()); } } diff --git a/tests/lib/Pagination/PagerfantaTest.php b/tests/lib/Pagination/PagerfantaTest.php index 5611a851a8..9563fba5ae 100644 --- a/tests/lib/Pagination/PagerfantaTest.php +++ b/tests/lib/Pagination/PagerfantaTest.php @@ -11,18 +11,22 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; use Ibexa\Core\Pagination\Pagerfanta\Pagerfanta; use Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +/** + * @template TSearchResultAdapter + */ final class PagerfantaTest extends TestCase { - private const EXAMPLE_TIME_RESULT = 30.0; - private const EXAMPLE_MAX_SCORE_RESULT = 5.12354; + private const float EXAMPLE_TIME_RESULT = 30.0; + private const float EXAMPLE_MAX_SCORE_RESULT = 5.12354; - /** @var \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter|\PHPUnit\Framework\MockObject\MockObject */ - private $adapter; + /** @phpstan-var \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter & \PHPUnit\Framework\MockObject\MockObject */ + private SearchResultAdapter & MockObject $adapter; - /** @var \Ibexa\Core\Pagination\Pagerfanta\Pagerfanta */ - private $pagerfanta; + /** @var \Ibexa\Core\Pagination\Pagerfanta\Pagerfanta */ + private Pagerfanta $pagerfanta; protected function setUp(): void { diff --git a/tests/lib/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapterTest.php b/tests/lib/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapterTest.php index 2203aa8f35..14b5186ac5 100644 --- a/tests/lib/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapterTest.php +++ b/tests/lib/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapterTest.php @@ -16,6 +16,11 @@ use Iterator; use PHPUnit\Framework\TestCase; +/** + * @template TSearchHitValueObject of \Ibexa\Contracts\Core\Repository\Values\ValueObject + * + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService + */ abstract class AbstractSearchAdapterTest extends TestCase { protected const EXAMPLE_LANGUAGE_FILTER = [ @@ -59,6 +64,11 @@ final public function testFetch(): void self::assertEquals(25, $originalQuery->limit); } + /** + * @phpstan-param TSearchLanguageFilter $languageFilter + * + * @phpstan-return \Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapter + */ abstract protected function createAdapterUnderTest( SearchService $searchService, Query $query, diff --git a/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapterTest.php b/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapterTest.php index 2fa8ab7ad2..cf761fd26e 100644 --- a/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapterTest.php +++ b/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapterTest.php @@ -13,6 +13,9 @@ use Ibexa\Contracts\Core\Repository\SearchService; use Ibexa\Contracts\Core\Repository\Values\Content\Query; +/** + * @extends \Ibexa\Tests\Core\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapterTest<\Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo> + */ final class ContentInfoSearchAdapterTest extends AbstractSearchAdapterTest { protected function createAdapterUnderTest( diff --git a/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapterTest.php b/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapterTest.php index 63691ac19b..6319549771 100644 --- a/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapterTest.php +++ b/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapterTest.php @@ -13,6 +13,11 @@ use Ibexa\Contracts\Core\Repository\SearchService; use Ibexa\Contracts\Core\Repository\Values\Content\Query; +/** + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService + * + * @extends \Ibexa\Tests\Core\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapterTest<\Ibexa\Contracts\Core\Repository\Values\Content\Content> + */ final class ContentSearchAdapterTest extends AbstractSearchAdapterTest { protected function createAdapterUnderTest( diff --git a/tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php b/tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php index 9d330f4be7..8b083a1121 100644 --- a/tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php +++ b/tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php @@ -14,6 +14,9 @@ use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; use Ibexa\Contracts\Core\Repository\Values\Content\Query; +/** + * @extends \Ibexa\Tests\Core\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapterTest<\Ibexa\Contracts\Core\Repository\Values\Content\Location> + */ final class LocationSearchAdapterTest extends AbstractSearchAdapterTest { protected function createAdapterUnderTest( @@ -22,6 +25,8 @@ protected function createAdapterUnderTest( array $languageFilter, bool $filterOnPermissions ): AbstractSearchAdapter { + self::assertInstanceOf(LocationQuery::class, $query); + return new LocationSearchAdapter( $searchService, $query, diff --git a/tests/lib/Repository/Service/Mock/SearchTest.php b/tests/lib/Repository/Service/Mock/SearchTest.php index 987cdbcda4..fdd14abe7b 100644 --- a/tests/lib/Repository/Service/Mock/SearchTest.php +++ b/tests/lib/Repository/Service/Mock/SearchTest.php @@ -245,7 +245,7 @@ public function testFindContentWhenDomainMapperThrowsException() ->with(self::equalTo($result), self::equalTo([])) ->willReturnCallback(static function (SearchResult $spiResult) use ($info) { unset($spiResult->searchHits[0]); - --$spiResult->totalCount; + $spiResult->totalCount = $spiResult->totalCount > 0 ? --$spiResult->totalCount : 0; return [$info]; }); @@ -583,16 +583,10 @@ public function testFindSingleThrowsHandlerException() $service->findSingle($criterionMock, [], true); } - /** - * Test for the findSingle() method. - * - * @covers \Ibexa\Contracts\Core\Repository\SearchService::addPermissionsCriterion - * @covers \Ibexa\Contracts\Core\Repository\SearchService::findSingle - */ - public function testFindSingle() + public function testFindSingle(): void { $repositoryMock = $this->getRepositoryMock(); - /** @var \Ibexa\Contracts\Core\Search\Handler $searchHandlerMock */ + /** @var \Ibexa\Contracts\Core\Search\Handler&\PHPUnit\Framework\MockObject\MockObject $searchHandlerMock */ $searchHandlerMock = $this->getSPIMockHandler('Search\\Handler'); $domainMapperMock = $this->getContentDomainMapperMock(); $permissionsCriterionResolverMock = $this->getPermissionCriterionResolverMock(); @@ -608,13 +602,11 @@ public function testFindSingle() $repositoryMock ->expects(self::once()) ->method('getContentService') - ->will( - self::returnValue( - $contentServiceMock = $this - ->getMockBuilder(ContentService::class) - ->disableOriginalConstructor() - ->getMock() - ) + ->willReturn( + $contentServiceMock = $this + ->getMockBuilder(ContentService::class) + ->disableOriginalConstructor() + ->getMock() ); /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterionMock */ @@ -626,18 +618,17 @@ public function testFindSingle() $permissionsCriterionResolverMock->expects(self::once()) ->method('getPermissionsCriterion') ->with('content', 'read') - ->will(self::returnValue(true)); + ->willReturn(true); $languageFilter = []; $spiContentInfo = new SPIContentInfo(['id' => 123]); $contentMock = $this->getMockForAbstractClass(Content::class); - /* @var \PHPUnit\Framework\MockObject\MockObject $searchHandlerMock */ $searchHandlerMock ->expects(self::once()) ->method('findSingle') ->with(self::equalTo($criterionMock), self::equalTo($languageFilter)) - ->will(self::returnValue($spiContentInfo)); + ->willReturn($spiContentInfo); $domainMapperMock->expects(self::never()) ->method(self::anything()); @@ -645,7 +636,7 @@ public function testFindSingle() $contentServiceMock ->expects(self::once()) ->method('internalLoadContentById') - ->will(self::returnValue($contentMock)); + ->willReturn($contentMock); $result = $service->findSingle($criterionMock, $languageFilter, true); @@ -829,7 +820,7 @@ public function testFindLocationsBackgroundIndexerWhenDomainMapperThrowsExceptio ->with(self::equalTo($result)) ->willReturnCallback(static function (SearchResult $spiResult) use ($location) { unset($spiResult->searchHits[0]); - --$spiResult->totalCount; + $spiResult->totalCount = $spiResult->totalCount > 0 ? --$spiResult->totalCount : 0; return [$location]; });